Home | History | Annotate | Download | only in ui
      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