1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 'use strict'; 6 7 base.requireStylesheet('ui.tool_button'); 8 base.requireStylesheet('ui.mouse_mode_selector'); 9 10 base.requireTemplate('ui.mouse_mode_selector'); 11 12 base.require('base.events'); 13 base.require('tracing.mouse_mode_constants'); 14 base.require('ui'); 15 16 base.exportTo('ui', function() { 17 18 /** 19 * Provides a panel for switching the interaction mode between 20 * panning, selecting and zooming. It handles the user interaction 21 * and dispatches events for the various modes, which are picked up 22 * and used by the Timeline Track View. 23 * 24 * @constructor 25 * @extends {HTMLDivElement} 26 */ 27 var MouseModeSelector = ui.define('div'); 28 29 MouseModeSelector.prototype = { 30 __proto__: HTMLDivElement.prototype, 31 32 decorate: function(parentEl) { 33 34 this.classList.add('mouse-mode-selector'); 35 this.parentEl_ = parentEl; 36 37 var node = base.instantiateTemplate('#mouse-mode-selector-template'); 38 this.appendChild(node); 39 40 this.buttonsEl_ = this.querySelector('.buttons'); 41 this.dragHandleEl_ = this.querySelector('.drag-handle'); 42 this.panScanModeButton_ = 43 this.buttonsEl_.querySelector('.pan-scan-mode-button'); 44 this.selectionModeButton_ = 45 this.buttonsEl_.querySelector('.selection-mode-button'); 46 this.zoomModeButton_ = 47 this.buttonsEl_.querySelector('.zoom-mode-button'); 48 49 this.pos_ = { 50 x: base.Settings.get('mouse_mode_selector.x', window.innerWidth - 50), 51 y: base.Settings.get('mouse_mode_selector.y', 100) 52 }; 53 54 this.constrainPositionToWindowBounds_(); 55 this.updateStylesFromPosition_(); 56 57 this.isDraggingModeSelectorDragHandle_ = false; 58 this.initialRelativeMouseDownPos_ = {x: 0, y: 0}; 59 60 this.dragHandleEl_.addEventListener('mousedown', 61 this.onDragHandleMouseDown_.bind(this)); 62 document.addEventListener('mousemove', 63 this.onDragHandleMouseMove_.bind(this)); 64 document.addEventListener('mouseup', 65 this.onDragHandleMouseUp_.bind(this)); 66 window.addEventListener('resize', 67 this.onWindowResize_.bind(this)); 68 69 this.buttonsEl_.addEventListener('mouseup', this.onButtonMouseUp_); 70 this.buttonsEl_.addEventListener('mousedown', this.onButtonMouseDown_); 71 this.buttonsEl_.addEventListener('click', this.onButtonPress_.bind(this)); 72 73 document.addEventListener('mousemove', this.onMouseMove_.bind(this)); 74 document.addEventListener('mouseup', this.onMouseUp_.bind(this)); 75 this.parentEl_.addEventListener('mousedown', 76 this.onMouseDown_.bind(this)); 77 78 document.addEventListener('keypress', this.onKeyPress_.bind(this)); 79 document.addEventListener('keydown', this.onKeyDown_.bind(this)); 80 document.addEventListener('keyup', this.onKeyUp_.bind(this)); 81 82 this.mode = base.Settings.get('mouse_mode_selector.mouseMode', 83 tracing.mouseModeConstants.MOUSE_MODE_PANSCAN); 84 85 this.isInTemporaryAlternativeMouseMode_ = false; 86 this.isInteracting_ = false; 87 }, 88 89 get mode() { 90 return this.currentMode_; 91 }, 92 93 set mode(newMode) { 94 95 if (this.currentMode_ === newMode) 96 return; 97 98 var mouseModeConstants = tracing.mouseModeConstants; 99 100 this.currentMode_ = newMode; 101 this.panScanModeButton_.classList.remove('active'); 102 this.selectionModeButton_.classList.remove('active'); 103 this.zoomModeButton_.classList.remove('active'); 104 105 switch (newMode) { 106 107 case mouseModeConstants.MOUSE_MODE_PANSCAN: 108 this.panScanModeButton_.classList.add('active'); 109 break; 110 111 case mouseModeConstants.MOUSE_MODE_SELECTION: 112 this.selectionModeButton_.classList.add('active'); 113 break; 114 115 case mouseModeConstants.MOUSE_MODE_ZOOM: 116 this.zoomModeButton_.classList.add('active'); 117 break; 118 119 default: 120 throw new Error('Unknown selection mode: ' + newMode); 121 break; 122 } 123 124 base.Settings.set('mouse_mode_selector.mouseMode', newMode); 125 }, 126 127 getCurrentModeEventNames_: function() { 128 129 var mouseModeConstants = tracing.mouseModeConstants; 130 var modeEventNames = { 131 begin: '', 132 update: '', 133 end: '' 134 }; 135 136 switch (this.mode) { 137 138 case mouseModeConstants.MOUSE_MODE_PANSCAN: 139 modeEventNames.begin = 'beginpan'; 140 modeEventNames.update = 'updatepan'; 141 modeEventNames.end = 'endpan'; 142 break; 143 144 case mouseModeConstants.MOUSE_MODE_SELECTION: 145 modeEventNames.begin = 'beginselection'; 146 modeEventNames.update = 'updateselection'; 147 modeEventNames.end = 'endselection'; 148 break; 149 150 case mouseModeConstants.MOUSE_MODE_ZOOM: 151 modeEventNames.begin = 'beginzoom'; 152 modeEventNames.update = 'updatezoom'; 153 modeEventNames.end = 'endzoom'; 154 break; 155 156 default: 157 throw new Error('Unsupported interaction mode'); 158 break; 159 } 160 161 return modeEventNames; 162 }, 163 164 onMouseDown_: function(e) { 165 var eventNames = this.getCurrentModeEventNames_(); 166 var mouseEvent = new base.Event(eventNames.begin, true, true); 167 mouseEvent.data = e; 168 this.dispatchEvent(mouseEvent); 169 this.isInteracting_ = true; 170 }, 171 172 onMouseMove_: function(e) { 173 var eventNames = this.getCurrentModeEventNames_(); 174 var mouseEvent = new base.Event(eventNames.update, true, true); 175 mouseEvent.data = e; 176 this.dispatchEvent(mouseEvent); 177 }, 178 179 onMouseUp_: function(e) { 180 var eventNames = this.getCurrentModeEventNames_(); 181 var mouseEvent = new base.Event(eventNames.end, true, true); 182 var userHasReleasedShiftKey = !e.shiftKey; 183 var userHasReleasedCmdOrCtrl = (base.isMac && !e.metaKey) || 184 (!base.isMac && !e.ctrlKey); 185 186 mouseEvent.data = e; 187 this.dispatchEvent(mouseEvent); 188 this.isInteracting_ = false; 189 190 if (this.isInTemporaryAlternativeMouseMode_ && userHasReleasedShiftKey && 191 userHasReleasedCmdOrCtrl) { 192 this.mode = tracing.mouseModeConstants.MOUSE_MODE_PANSCAN; 193 } 194 }, 195 196 onButtonMouseDown_: function(e) { 197 e.preventDefault(); 198 e.stopImmediatePropagation(); 199 }, 200 201 onButtonMouseUp_: function(e) { 202 e.preventDefault(); 203 e.stopImmediatePropagation(); 204 }, 205 206 onButtonPress_: function(e) { 207 208 var mouseModeConstants = tracing.mouseModeConstants; 209 var newInteractionMode = mouseModeConstants.MOUSE_MODE_PANSCAN; 210 211 switch (e.target) { 212 case this.panScanModeButton_: 213 this.mode = mouseModeConstants.MOUSE_MODE_PANSCAN; 214 break; 215 216 case this.selectionModeButton_: 217 this.mode = mouseModeConstants.MOUSE_MODE_SELECTION; 218 break; 219 220 case this.zoomModeButton_: 221 this.mode = mouseModeConstants.MOUSE_MODE_ZOOM; 222 break; 223 224 default: 225 throw new Error('Unknown mouse mode button pressed'); 226 break; 227 } 228 229 e.preventDefault(); 230 this.isInTemporaryAlternativeMouseMode_ = false; 231 }, 232 233 onKeyPress_: function(e) { 234 235 // Prevent the user from changing modes during an interaction. 236 if (this.isInteracting_) 237 return; 238 239 var mouseModeConstants = tracing.mouseModeConstants; 240 241 switch (e.keyCode) { 242 case 49: // 1 243 this.mode = mouseModeConstants.MOUSE_MODE_PANSCAN; 244 break; 245 case 50: // 2 246 this.mode = mouseModeConstants.MOUSE_MODE_SELECTION; 247 break; 248 case 51: // 3 249 this.mode = mouseModeConstants.MOUSE_MODE_ZOOM; 250 break; 251 } 252 }, 253 254 onKeyDown_: function(e) { 255 256 // Prevent the user from changing modes during an interaction. 257 if (this.isInteracting_) 258 return; 259 260 var mouseModeConstants = tracing.mouseModeConstants; 261 var userIsPressingCmdOrCtrl = (base.isMac && e.metaKey) || 262 (!base.isMac && e.ctrlKey); 263 var userIsPressingShiftKey = e.shiftKey; 264 265 if (this.mode !== mouseModeConstants.MOUSE_MODE_PANSCAN) 266 return; 267 268 if (userIsPressingCmdOrCtrl || userIsPressingShiftKey) { 269 270 this.mode = userIsPressingCmdOrCtrl ? 271 mouseModeConstants.MOUSE_MODE_ZOOM : 272 mouseModeConstants.MOUSE_MODE_SELECTION; 273 274 this.isInTemporaryAlternativeMouseMode_ = true; 275 e.preventDefault(); 276 } 277 }, 278 279 onKeyUp_: function(e) { 280 281 // Prevent the user from changing modes during an interaction. 282 if (this.isInteracting_) 283 return; 284 285 var mouseModeConstants = tracing.mouseModeConstants; 286 var userHasReleasedCmdOrCtrl = (base.isMac && !e.metaKey) || 287 (!base.isMac && !e.ctrlKey); 288 var userHasReleasedShiftKey = e.shiftKey; 289 290 if (this.isInTemporaryAlternativeMouseMode_ && 291 (userHasReleasedCmdOrCtrl || userHasReleasedShiftKey)) { 292 this.mode = mouseModeConstants.MOUSE_MODE_PANSCAN; 293 } 294 295 this.isInTemporaryAlternativeMouseMode_ = false; 296 297 }, 298 299 constrainPositionToWindowBounds_: function() { 300 var top = 0; 301 var bottom = window.innerHeight - this.offsetHeight; 302 var left = 0; 303 var right = window.innerWidth - this.offsetWidth; 304 305 this.pos_.x = Math.max(this.pos_.x, left); 306 this.pos_.x = Math.min(this.pos_.x, right); 307 308 this.pos_.y = Math.max(this.pos_.y, top); 309 this.pos_.y = Math.min(this.pos_.y, bottom); 310 }, 311 312 updateStylesFromPosition_: function() { 313 this.style.left = this.pos_.x + 'px'; 314 this.style.top = this.pos_.y + 'px'; 315 316 base.Settings.set('mouse_mode_selector.x', this.pos_.x); 317 base.Settings.set('mouse_mode_selector.y', this.pos_.y); 318 }, 319 320 onDragHandleMouseDown_: function(e) { 321 e.preventDefault(); 322 e.stopImmediatePropagation(); 323 324 this.isDraggingModeSelectorDragHandle_ = true; 325 326 this.initialRelativeMouseDownPos_.x = e.clientX - this.offsetLeft; 327 this.initialRelativeMouseDownPos_.y = e.clientY - this.offsetTop; 328 329 }, 330 331 onDragHandleMouseMove_: function(e) { 332 if (!this.isDraggingModeSelectorDragHandle_) 333 return; 334 335 this.pos_.x = (e.clientX - this.initialRelativeMouseDownPos_.x); 336 this.pos_.y = (e.clientY - this.initialRelativeMouseDownPos_.y); 337 338 this.constrainPositionToWindowBounds_(); 339 this.updateStylesFromPosition_(); 340 }, 341 342 onDragHandleMouseUp_: function(e) { 343 this.isDraggingModeSelectorDragHandle_ = false; 344 }, 345 346 onWindowResize_: function(e) { 347 this.constrainPositionToWindowBounds_(); 348 this.updateStylesFromPosition_(); 349 } 350 }; 351 352 return { 353 MouseModeSelector: MouseModeSelector 354 }; 355 }); 356