Home | History | Annotate | Download | only in timeline
      1 /*
      2  * Copyright 2014 The Chromium Authors. All rights reserved.
      3  * Use of this source code is governed by a BSD-style license that can be
      4  * found in the LICENSE file.
      5  */
      6 
      7 /**
      8  * @constructor
      9  * @extends {WebInspector.Object}
     10  * @param {!Element} element
     11  * @param {boolean=} disableRotate
     12  */
     13 WebInspector.TransformController = function(element, disableRotate)
     14 {
     15     this._shortcuts = {};
     16     this.element = element;
     17     this._registerShortcuts();
     18     element.addEventListener("keydown", this._onKeyDown.bind(this), false);
     19     element.addEventListener("keyup", this._onKeyUp.bind(this), false);
     20     element.addEventListener("mousemove", this._onMouseMove.bind(this), false);
     21     element.addEventListener("mousedown", this._onMouseDown.bind(this), false);
     22     element.addEventListener("mouseup", this._onMouseUp.bind(this), false);
     23     element.addEventListener("mousewheel", this._onMouseWheel.bind(this), false);
     24     this._disableRotate = disableRotate;
     25 
     26     this._controlPanelElement = document.createElement("div");
     27     this._controlPanelElement.classList.add("transform-control-panel");
     28 
     29     this._modeButtons = {};
     30     if (!disableRotate) {
     31         var panModeButton = new WebInspector.StatusBarButton(WebInspector.UIString("Pan mode (X)"), "transform-mode-pan");
     32         panModeButton.addEventListener("click", this._setMode.bind(this, WebInspector.TransformController.Modes.Pan));
     33         this._modeButtons[WebInspector.TransformController.Modes.Pan] = panModeButton;
     34         this._controlPanelElement.appendChild(panModeButton.element);
     35         var rotateModeButton = new WebInspector.StatusBarButton(WebInspector.UIString("Rotate mode (V)"), "transform-mode-rotate");
     36         rotateModeButton.addEventListener("click", this._setMode.bind(this, WebInspector.TransformController.Modes.Rotate));
     37         this._modeButtons[WebInspector.TransformController.Modes.Rotate] = rotateModeButton;
     38         this._controlPanelElement.appendChild(rotateModeButton.element);
     39     }
     40     this._setMode(WebInspector.TransformController.Modes.Pan);
     41 
     42     var resetButton = new WebInspector.StatusBarButton(WebInspector.UIString("Reset transform (0)"), "transform-reset");
     43     resetButton.addEventListener("click", this.resetAndNotify.bind(this, undefined));
     44     this._controlPanelElement.appendChild(resetButton.element);
     45 
     46     this._reset();
     47 }
     48 
     49 /**
     50  * @enum {string}
     51  */
     52 WebInspector.TransformController.Events = {
     53     TransformChanged: "TransformChanged"
     54 }
     55 
     56 /**
     57  * @enum {string}
     58  */
     59 WebInspector.TransformController.Modes = {
     60     Pan: "Pan",
     61     Rotate: "Rotate",
     62 }
     63 
     64 WebInspector.TransformController.prototype = {
     65     /**
     66      * @return {!Element}
     67      */
     68     controlPanelElement: function()
     69     {
     70         return this._controlPanelElement;
     71     },
     72 
     73     _onKeyDown: function(event)
     74     {
     75         if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Shift.code) {
     76             this._toggleMode();
     77             return;
     78         }
     79 
     80         var shortcutKey = WebInspector.KeyboardShortcut.makeKeyFromEventIgnoringModifiers(event);
     81         var handler = this._shortcuts[shortcutKey];
     82         if (handler && handler(event))
     83             event.consume();
     84     },
     85 
     86     _onKeyUp: function(event)
     87     {
     88         if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Shift.code)
     89             this._toggleMode();
     90     },
     91 
     92     _addShortcuts: function(keys, handler)
     93     {
     94         for (var i = 0; i < keys.length; ++i)
     95             this._shortcuts[keys[i].key] = handler;
     96     },
     97 
     98     _registerShortcuts: function()
     99     {
    100         this._addShortcuts(WebInspector.ShortcutsScreen.LayersPanelShortcuts.ResetView, this.resetAndNotify.bind(this));
    101         this._addShortcuts(WebInspector.ShortcutsScreen.LayersPanelShortcuts.PanMode, this._setMode.bind(this, WebInspector.TransformController.Modes.Pan));
    102         this._addShortcuts(WebInspector.ShortcutsScreen.LayersPanelShortcuts.RotateMode, this._setMode.bind(this, WebInspector.TransformController.Modes.Rotate));
    103         var zoomFactor = 1.1;
    104         this._addShortcuts(WebInspector.ShortcutsScreen.LayersPanelShortcuts.ZoomIn, this._onKeyboardZoom.bind(this, zoomFactor));
    105         this._addShortcuts(WebInspector.ShortcutsScreen.LayersPanelShortcuts.ZoomOut, this._onKeyboardZoom.bind(this, 1 / zoomFactor));
    106         this._addShortcuts(WebInspector.ShortcutsScreen.LayersPanelShortcuts.Up, this._onKeyboardPanOrRotate.bind(this, 0, -1));
    107         this._addShortcuts(WebInspector.ShortcutsScreen.LayersPanelShortcuts.Down, this._onKeyboardPanOrRotate.bind(this, 0, 1));
    108         this._addShortcuts(WebInspector.ShortcutsScreen.LayersPanelShortcuts.Left, this._onKeyboardPanOrRotate.bind(this, -1, 0));
    109         this._addShortcuts(WebInspector.ShortcutsScreen.LayersPanelShortcuts.Right, this._onKeyboardPanOrRotate.bind(this, 1, 0));
    110     },
    111 
    112     _postChangeEvent: function()
    113     {
    114         this.dispatchEventToListeners(WebInspector.TransformController.Events.TransformChanged);
    115     },
    116 
    117     _reset: function()
    118     {
    119         this._scale = 1;
    120         this._offsetX = 0;
    121         this._offsetY = 0;
    122         this._rotateX = 0;
    123         this._rotateY = 0;
    124     },
    125 
    126     _toggleMode: function()
    127     {
    128         this._setMode(this._mode === WebInspector.TransformController.Modes.Pan ? WebInspector.TransformController.Modes.Rotate : WebInspector.TransformController.Modes.Pan);
    129     },
    130 
    131     /**
    132      * @param {!WebInspector.TransformController.Modes} mode
    133      */
    134     _setMode: function(mode)
    135     {
    136         if (this._mode === mode)
    137             return;
    138         this._mode = mode;
    139         this._updateModeButtons();
    140         this.element.focus();
    141     },
    142 
    143     _updateModeButtons: function()
    144     {
    145         for (var mode in this._modeButtons)
    146             this._modeButtons[mode].toggled = (mode === this._mode);
    147     },
    148 
    149     /**
    150      * @param {!Event=} event
    151      */
    152     resetAndNotify: function(event)
    153     {
    154         this._reset();
    155         this._postChangeEvent();
    156         if (event)
    157             event.preventDefault();
    158         this.element.focus();
    159     },
    160 
    161     /**
    162      * @return {number}
    163      */
    164     scale: function()
    165     {
    166         return this._scale;
    167     },
    168 
    169     /**
    170      * @return {number}
    171      */
    172     offsetX: function()
    173     {
    174         return this._offsetX;
    175     },
    176 
    177     /**
    178      * @return {number}
    179      */
    180     offsetY: function()
    181     {
    182         return this._offsetY;
    183     },
    184 
    185     /**
    186      * @return {number}
    187      */
    188     rotateX: function()
    189     {
    190         return this._rotateX;
    191     },
    192 
    193     /**
    194      * @return {number}
    195      */
    196     rotateY: function()
    197     {
    198         return this._rotateY;
    199     },
    200 
    201     /**
    202      * @param {number} scaleFactor
    203      * @param {number} x
    204      * @param {number} y
    205      */
    206     _onScale: function(scaleFactor, x, y)
    207     {
    208         this._scale *= scaleFactor;
    209         this._offsetX -= (x - this._offsetX) * (scaleFactor - 1);
    210         this._offsetY -= (y - this._offsetY) * (scaleFactor - 1);
    211         this._postChangeEvent();
    212     },
    213 
    214     /**
    215      * @param {number} offsetX
    216      * @param {number} offsetY
    217      */
    218     _onPan: function(offsetX, offsetY)
    219     {
    220         this._offsetX += offsetX;
    221         this._offsetY += offsetY;
    222         this._postChangeEvent();
    223     },
    224 
    225     /**
    226      * @param {number} rotateX
    227      * @param {number} rotateY
    228      */
    229     _onRotate: function(rotateX, rotateY)
    230     {
    231         this._rotateX = rotateX;
    232         this._rotateY = rotateY;
    233         this._postChangeEvent();
    234     },
    235 
    236     /**
    237      * @param {number} zoomFactor
    238      */
    239     _onKeyboardZoom: function(zoomFactor)
    240     {
    241         this._onScale(zoomFactor, this.element.clientWidth / 2, this.element.clientHeight / 2);
    242     },
    243 
    244     /**
    245      * @param {number} xMultiplier
    246      * @param {number} yMultiplier
    247      */
    248     _onKeyboardPanOrRotate: function(xMultiplier, yMultiplier)
    249     {
    250         var panStepInPixels = 6;
    251         var rotateStepInDegrees = 5;
    252 
    253         if (this._mode === WebInspector.TransformController.Modes.Rotate) {
    254             // Sic! _onRotate treats X and Y as "rotate around X" and "rotate around Y", so swap X/Y multiplers.
    255             this._onRotate(this._rotateX + yMultiplier * rotateStepInDegrees, this._rotateY + xMultiplier * rotateStepInDegrees);
    256         } else {
    257             this._onPan(xMultiplier * panStepInPixels, yMultiplier * panStepInPixels);
    258         }
    259     },
    260 
    261     /**
    262      * @param {!Event} event
    263      */
    264     _onMouseWheel: function(event)
    265     {
    266         /** @const */
    267         var zoomFactor = 1.1;
    268         /** @const */
    269         var mouseWheelZoomSpeed = 1 / 120;
    270         var scaleFactor = Math.pow(zoomFactor, event.wheelDeltaY * mouseWheelZoomSpeed);
    271         this._onScale(scaleFactor, event.clientX - this.element.totalOffsetLeft(), event.clientY - this.element.totalOffsetTop());
    272     },
    273 
    274     /**
    275      * @param {!Event} event
    276      */
    277     _onMouseMove: function(event)
    278     {
    279         if (event.which !== 1 || typeof this._originX !== "number")
    280             return;
    281         if (this._mode === WebInspector.TransformController.Modes.Rotate) {
    282             this._onRotate(this._oldRotateX + (this._originY - event.clientY) / this.element.clientHeight * 180, this._oldRotateY - (this._originX - event.clientX) / this.element.clientWidth * 180);
    283         } else {
    284             this._onPan(event.clientX - this._originX, event.clientY - this._originY);
    285             this._originX = event.clientX;
    286             this._originY = event.clientY;
    287         }
    288     },
    289 
    290     /**
    291      * @param {!Event} event
    292      */
    293     _setReferencePoint: function(event)
    294     {
    295         this._originX = event.clientX;
    296         this._originY = event.clientY;
    297         this._oldRotateX = this._rotateX;
    298         this._oldRotateY = this._rotateY;
    299     },
    300 
    301     _resetReferencePoint: function()
    302     {
    303         delete this._originX;
    304         delete this._originY;
    305         delete this._oldRotateX;
    306         delete this._oldRotateY;
    307     },
    308 
    309     /**
    310      * @param {!Event} event
    311      */
    312     _onMouseDown: function(event)
    313     {
    314         if (event.which !== 1)
    315             return;
    316         this._setReferencePoint(event);
    317     },
    318 
    319     /**
    320      * @param {!Event} event
    321      */
    322     _onMouseUp: function(event)
    323     {
    324         if (event.which !== 1)
    325             return;
    326         this._resetReferencePoint();
    327     },
    328 
    329     __proto__: WebInspector.Object.prototype
    330 }
    331