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