// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
'use strict';
base.requireStylesheet('ui.tool_button');
base.requireStylesheet('ui.mouse_mode_selector');
base.requireTemplate('ui.mouse_mode_selector');
base.require('base.events');
base.require('tracing.mouse_mode_constants');
base.require('ui');
base.exportTo('ui', function() {
/**
* Provides a panel for switching the interaction mode between
* panning, selecting and zooming. It handles the user interaction
* and dispatches events for the various modes, which are picked up
* and used by the Timeline Track View.
*
* @constructor
* @extends {HTMLDivElement}
*/
var MouseModeSelector = ui.define('div');
MouseModeSelector.prototype = {
__proto__: HTMLDivElement.prototype,
decorate: function(parentEl) {
this.classList.add('mouse-mode-selector');
this.parentEl_ = parentEl;
var node = base.instantiateTemplate('#mouse-mode-selector-template');
this.appendChild(node);
this.buttonsEl_ = this.querySelector('.buttons');
this.dragHandleEl_ = this.querySelector('.drag-handle');
this.panScanModeButton_ =
this.buttonsEl_.querySelector('.pan-scan-mode-button');
this.selectionModeButton_ =
this.buttonsEl_.querySelector('.selection-mode-button');
this.zoomModeButton_ =
this.buttonsEl_.querySelector('.zoom-mode-button');
this.pos_ = {
x: base.Settings.get('mouse_mode_selector.x', window.innerWidth - 50),
y: base.Settings.get('mouse_mode_selector.y', 100)
};
this.constrainPositionToWindowBounds_();
this.updateStylesFromPosition_();
this.isDraggingModeSelectorDragHandle_ = false;
this.initialRelativeMouseDownPos_ = {x: 0, y: 0};
this.dragHandleEl_.addEventListener('mousedown',
this.onDragHandleMouseDown_.bind(this));
document.addEventListener('mousemove',
this.onDragHandleMouseMove_.bind(this));
document.addEventListener('mouseup',
this.onDragHandleMouseUp_.bind(this));
window.addEventListener('resize',
this.onWindowResize_.bind(this));
this.buttonsEl_.addEventListener('mouseup', this.onButtonMouseUp_);
this.buttonsEl_.addEventListener('mousedown', this.onButtonMouseDown_);
this.buttonsEl_.addEventListener('click', this.onButtonPress_.bind(this));
document.addEventListener('mousemove', this.onMouseMove_.bind(this));
document.addEventListener('mouseup', this.onMouseUp_.bind(this));
this.parentEl_.addEventListener('mousedown',
this.onMouseDown_.bind(this));
document.addEventListener('keypress', this.onKeyPress_.bind(this));
document.addEventListener('keydown', this.onKeyDown_.bind(this));
document.addEventListener('keyup', this.onKeyUp_.bind(this));
this.mode = base.Settings.get('mouse_mode_selector.mouseMode',
tracing.mouseModeConstants.MOUSE_MODE_PANSCAN);
this.isInTemporaryAlternativeMouseMode_ = false;
this.isInteracting_ = false;
},
get mode() {
return this.currentMode_;
},
set mode(newMode) {
if (this.currentMode_ === newMode)
return;
var mouseModeConstants = tracing.mouseModeConstants;
this.currentMode_ = newMode;
this.panScanModeButton_.classList.remove('active');
this.selectionModeButton_.classList.remove('active');
this.zoomModeButton_.classList.remove('active');
switch (newMode) {
case mouseModeConstants.MOUSE_MODE_PANSCAN:
this.panScanModeButton_.classList.add('active');
break;
case mouseModeConstants.MOUSE_MODE_SELECTION:
this.selectionModeButton_.classList.add('active');
break;
case mouseModeConstants.MOUSE_MODE_ZOOM:
this.zoomModeButton_.classList.add('active');
break;
default:
throw new Error('Unknown selection mode: ' + newMode);
break;
}
base.Settings.set('mouse_mode_selector.mouseMode', newMode);
},
getCurrentModeEventNames_: function() {
var mouseModeConstants = tracing.mouseModeConstants;
var modeEventNames = {
begin: '',
update: '',
end: ''
};
switch (this.mode) {
case mouseModeConstants.MOUSE_MODE_PANSCAN:
modeEventNames.begin = 'beginpan';
modeEventNames.update = 'updatepan';
modeEventNames.end = 'endpan';
break;
case mouseModeConstants.MOUSE_MODE_SELECTION:
modeEventNames.begin = 'beginselection';
modeEventNames.update = 'updateselection';
modeEventNames.end = 'endselection';
break;
case mouseModeConstants.MOUSE_MODE_ZOOM:
modeEventNames.begin = 'beginzoom';
modeEventNames.update = 'updatezoom';
modeEventNames.end = 'endzoom';
break;
default:
throw new Error('Unsupported interaction mode');
break;
}
return modeEventNames;
},
onMouseDown_: function(e) {
var eventNames = this.getCurrentModeEventNames_();
var mouseEvent = new base.Event(eventNames.begin, true, true);
mouseEvent.data = e;
this.dispatchEvent(mouseEvent);
this.isInteracting_ = true;
},
onMouseMove_: function(e) {
var eventNames = this.getCurrentModeEventNames_();
var mouseEvent = new base.Event(eventNames.update, true, true);
mouseEvent.data = e;
this.dispatchEvent(mouseEvent);
},
onMouseUp_: function(e) {
var eventNames = this.getCurrentModeEventNames_();
var mouseEvent = new base.Event(eventNames.end, true, true);
var userHasReleasedShiftKey = !e.shiftKey;
var userHasReleasedCmdOrCtrl = (base.isMac && !e.metaKey) ||
(!base.isMac && !e.ctrlKey);
mouseEvent.data = e;
this.dispatchEvent(mouseEvent);
this.isInteracting_ = false;
if (this.isInTemporaryAlternativeMouseMode_ && userHasReleasedShiftKey &&
userHasReleasedCmdOrCtrl) {
this.mode = tracing.mouseModeConstants.MOUSE_MODE_PANSCAN;
}
},
onButtonMouseDown_: function(e) {
e.preventDefault();
e.stopImmediatePropagation();
},
onButtonMouseUp_: function(e) {
e.preventDefault();
e.stopImmediatePropagation();
},
onButtonPress_: function(e) {
var mouseModeConstants = tracing.mouseModeConstants;
var newInteractionMode = mouseModeConstants.MOUSE_MODE_PANSCAN;
switch (e.target) {
case this.panScanModeButton_:
this.mode = mouseModeConstants.MOUSE_MODE_PANSCAN;
break;
case this.selectionModeButton_:
this.mode = mouseModeConstants.MOUSE_MODE_SELECTION;
break;
case this.zoomModeButton_:
this.mode = mouseModeConstants.MOUSE_MODE_ZOOM;
break;
default:
throw new Error('Unknown mouse mode button pressed');
break;
}
e.preventDefault();
this.isInTemporaryAlternativeMouseMode_ = false;
},
onKeyPress_: function(e) {
// Prevent the user from changing modes during an interaction.
if (this.isInteracting_)
return;
var mouseModeConstants = tracing.mouseModeConstants;
switch (e.keyCode) {
case 49: // 1
this.mode = mouseModeConstants.MOUSE_MODE_PANSCAN;
break;
case 50: // 2
this.mode = mouseModeConstants.MOUSE_MODE_SELECTION;
break;
case 51: // 3
this.mode = mouseModeConstants.MOUSE_MODE_ZOOM;
break;
}
},
onKeyDown_: function(e) {
// Prevent the user from changing modes during an interaction.
if (this.isInteracting_)
return;
var mouseModeConstants = tracing.mouseModeConstants;
var userIsPressingCmdOrCtrl = (base.isMac && e.metaKey) ||
(!base.isMac && e.ctrlKey);
var userIsPressingShiftKey = e.shiftKey;
if (this.mode !== mouseModeConstants.MOUSE_MODE_PANSCAN)
return;
if (userIsPressingCmdOrCtrl || userIsPressingShiftKey) {
this.mode = userIsPressingCmdOrCtrl ?
mouseModeConstants.MOUSE_MODE_ZOOM :
mouseModeConstants.MOUSE_MODE_SELECTION;
this.isInTemporaryAlternativeMouseMode_ = true;
e.preventDefault();
}
},
onKeyUp_: function(e) {
// Prevent the user from changing modes during an interaction.
if (this.isInteracting_)
return;
var mouseModeConstants = tracing.mouseModeConstants;
var userHasReleasedCmdOrCtrl = (base.isMac && !e.metaKey) ||
(!base.isMac && !e.ctrlKey);
var userHasReleasedShiftKey = e.shiftKey;
if (this.isInTemporaryAlternativeMouseMode_ &&
(userHasReleasedCmdOrCtrl || userHasReleasedShiftKey)) {
this.mode = mouseModeConstants.MOUSE_MODE_PANSCAN;
}
this.isInTemporaryAlternativeMouseMode_ = false;
},
constrainPositionToWindowBounds_: function() {
var top = 0;
var bottom = window.innerHeight - this.offsetHeight;
var left = 0;
var right = window.innerWidth - this.offsetWidth;
this.pos_.x = Math.max(this.pos_.x, left);
this.pos_.x = Math.min(this.pos_.x, right);
this.pos_.y = Math.max(this.pos_.y, top);
this.pos_.y = Math.min(this.pos_.y, bottom);
},
updateStylesFromPosition_: function() {
this.style.left = this.pos_.x + 'px';
this.style.top = this.pos_.y + 'px';
base.Settings.set('mouse_mode_selector.x', this.pos_.x);
base.Settings.set('mouse_mode_selector.y', this.pos_.y);
},
onDragHandleMouseDown_: function(e) {
e.preventDefault();
e.stopImmediatePropagation();
this.isDraggingModeSelectorDragHandle_ = true;
this.initialRelativeMouseDownPos_.x = e.clientX - this.offsetLeft;
this.initialRelativeMouseDownPos_.y = e.clientY - this.offsetTop;
},
onDragHandleMouseMove_: function(e) {
if (!this.isDraggingModeSelectorDragHandle_)
return;
this.pos_.x = (e.clientX - this.initialRelativeMouseDownPos_.x);
this.pos_.y = (e.clientY - this.initialRelativeMouseDownPos_.y);
this.constrainPositionToWindowBounds_();
this.updateStylesFromPosition_();
},
onDragHandleMouseUp_: function(e) {
this.isDraggingModeSelectorDragHandle_ = false;
},
onWindowResize_: function(e) {
this.constrainPositionToWindowBounds_();
this.updateStylesFromPosition_();
}
};
return {
MouseModeSelector: MouseModeSelector
};
});