mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-10-07 20:39:31 +08:00
This reverts commit 99620c4bfc.
Reason for revert: crbug.com/1140386
Original change's description:
> Remove dependencies on ShortcutsScreen
>
> This CL removes all dependencies on ShortcutsScreen in preparation for
> its removal. Since CSSPlugin and TransformController actually store
> their shortcuts in ShortcutsScreen, those shortcuts have to be moved
> into the module.jsons so that they can be managed by ShortcutRegistry,
> as most other shortcuts in the DevTools are.
>
> enums.xml changes to match userMetrics changes: https://chromium-review.googlesource.com/c/chromium/src/+/2464047
>
> Custom shortcuts explainer: https://docs.google.com/document/d/1oOPSWPxCHvMoBZ0Fw9jwFZt6gP4lrsrsl8DEAp-Hy7o/edit#
>
> Bug: 174309
> Change-Id: I5d9ae2e28c555c88c5549fb830575b36878e0f96
> Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2472674
> Commit-Queue: Jack Lynch <jalyn@microsoft.com>
> Reviewed-by: Mathias Bynens <mathias@chromium.org>
> Reviewed-by: Tim van der Lippe <tvanderlippe@chromium.org>
TBR=mathias@chromium.org,Rob.Paveza@microsoft.com,jalyn@microsoft.com,tvanderlippe@chromium.org
Change-Id: Ia0b49ea0e53405863e4ed7ee3f0163554d3c4643
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: 174309
Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2485079
Reviewed-by: Alex Rudenko <alexrudenko@chromium.org>
Commit-Queue: Alex Rudenko <alexrudenko@chromium.org>
332 lines
9.1 KiB
JavaScript
332 lines
9.1 KiB
JavaScript
// Copyright 2014 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.
|
|
|
|
import * as Common from '../common/common.js';
|
|
import * as Platform from '../platform/platform.js';
|
|
import * as UI from '../ui/ui.js';
|
|
|
|
/**
|
|
* @unrestricted
|
|
*/
|
|
export class TransformController extends Common.ObjectWrapper.ObjectWrapper {
|
|
/**
|
|
* @param {!HTMLElement} element
|
|
* @param {boolean=} disableRotate
|
|
*/
|
|
constructor(element, disableRotate) {
|
|
super();
|
|
/**
|
|
* @type {!Object.<number, function(!Event=):(boolean|void)>}
|
|
*/
|
|
this._shortcuts = {};
|
|
/**
|
|
* @type {!Modes}
|
|
*/
|
|
this._mode;
|
|
this._scale = 1;
|
|
this._offsetX = 0;
|
|
this._offsetY = 0;
|
|
this._rotateX = 0;
|
|
this._rotateY = 0;
|
|
this._oldRotateX = 0;
|
|
this._oldRotateY = 0;
|
|
this._originX = 0;
|
|
this._originY = 0;
|
|
this.element = element;
|
|
this._registerShortcuts();
|
|
UI.UIUtils.installDragHandle(
|
|
element, this._onDragStart.bind(this), this._onDrag.bind(this), this._onDragEnd.bind(this), 'move', null);
|
|
element.addEventListener('keydown', this._onKeyDown.bind(this), false);
|
|
element.addEventListener('mousewheel', this._onMouseWheel.bind(this), false);
|
|
this._minScale = 0;
|
|
this._maxScale = Infinity;
|
|
|
|
this._controlPanelToolbar = new UI.Toolbar.Toolbar('transform-control-panel');
|
|
|
|
/** @type {!Object<string, !UI.Toolbar.ToolbarToggle>} */
|
|
this._modeButtons = {};
|
|
if (!disableRotate) {
|
|
const panModeButton = new UI.Toolbar.ToolbarToggle(Common.UIString.UIString('Pan mode (X)'), 'largeicon-pan');
|
|
panModeButton.addEventListener(UI.Toolbar.ToolbarButton.Events.Click, this._setMode.bind(this, Modes.Pan));
|
|
this._modeButtons[Modes.Pan] = panModeButton;
|
|
this._controlPanelToolbar.appendToolbarItem(panModeButton);
|
|
const rotateModeButton =
|
|
new UI.Toolbar.ToolbarToggle(Common.UIString.UIString('Rotate mode (V)'), 'largeicon-rotate');
|
|
rotateModeButton.addEventListener(UI.Toolbar.ToolbarButton.Events.Click, this._setMode.bind(this, Modes.Rotate));
|
|
this._modeButtons[Modes.Rotate] = rotateModeButton;
|
|
this._controlPanelToolbar.appendToolbarItem(rotateModeButton);
|
|
}
|
|
this._setMode(Modes.Pan);
|
|
|
|
const resetButton =
|
|
new UI.Toolbar.ToolbarButton(Common.UIString.UIString('Reset transform (0)'), 'largeicon-center');
|
|
resetButton.addEventListener(UI.Toolbar.ToolbarButton.Events.Click, this.resetAndNotify.bind(this, undefined));
|
|
this._controlPanelToolbar.appendToolbarItem(resetButton);
|
|
|
|
this._reset();
|
|
}
|
|
|
|
/**
|
|
* @return {!UI.Toolbar.Toolbar}
|
|
*/
|
|
toolbar() {
|
|
return this._controlPanelToolbar;
|
|
}
|
|
|
|
/**
|
|
* @param {!Event} event
|
|
*/
|
|
_onKeyDown(event) {
|
|
const shortcutKey =
|
|
UI.KeyboardShortcut.KeyboardShortcut.makeKeyFromEventIgnoringModifiers(/** @type {!KeyboardEvent} */ (event));
|
|
const handler = this._shortcuts[shortcutKey];
|
|
if (handler && handler(event)) {
|
|
event.consume();
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {!Array<!{key: number}>} keys
|
|
* @param {function(!Event=):(boolean|void)} handler
|
|
*/
|
|
_addShortcuts(keys, handler) {
|
|
for (let i = 0; i < keys.length; ++i) {
|
|
this._shortcuts[keys[i].key] = handler;
|
|
}
|
|
}
|
|
|
|
_registerShortcuts() {
|
|
this._addShortcuts(UI.ShortcutsScreen.LayersPanelShortcuts.ResetView, this.resetAndNotify.bind(this));
|
|
this._addShortcuts(UI.ShortcutsScreen.LayersPanelShortcuts.PanMode, this._setMode.bind(this, Modes.Pan));
|
|
this._addShortcuts(UI.ShortcutsScreen.LayersPanelShortcuts.RotateMode, this._setMode.bind(this, Modes.Rotate));
|
|
const zoomFactor = 1.1;
|
|
this._addShortcuts(UI.ShortcutsScreen.LayersPanelShortcuts.ZoomIn, this._onKeyboardZoom.bind(this, zoomFactor));
|
|
this._addShortcuts(
|
|
UI.ShortcutsScreen.LayersPanelShortcuts.ZoomOut, this._onKeyboardZoom.bind(this, 1 / zoomFactor));
|
|
this._addShortcuts(UI.ShortcutsScreen.LayersPanelShortcuts.Up, this._onKeyboardPanOrRotate.bind(this, 0, -1));
|
|
this._addShortcuts(UI.ShortcutsScreen.LayersPanelShortcuts.Down, this._onKeyboardPanOrRotate.bind(this, 0, 1));
|
|
this._addShortcuts(UI.ShortcutsScreen.LayersPanelShortcuts.Left, this._onKeyboardPanOrRotate.bind(this, -1, 0));
|
|
this._addShortcuts(UI.ShortcutsScreen.LayersPanelShortcuts.Right, this._onKeyboardPanOrRotate.bind(this, 1, 0));
|
|
}
|
|
|
|
_postChangeEvent() {
|
|
this.dispatchEventToListeners(Events.TransformChanged);
|
|
}
|
|
|
|
_reset() {
|
|
this._scale = 1;
|
|
this._offsetX = 0;
|
|
this._offsetY = 0;
|
|
this._rotateX = 0;
|
|
this._rotateY = 0;
|
|
}
|
|
|
|
/**
|
|
* @param {!Modes} mode
|
|
*/
|
|
_setMode(mode) {
|
|
if (this._mode === mode) {
|
|
return;
|
|
}
|
|
this._mode = mode;
|
|
this._updateModeButtons();
|
|
}
|
|
|
|
_updateModeButtons() {
|
|
for (const mode in this._modeButtons) {
|
|
this._modeButtons[mode].setToggled(mode === this._mode);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {!Event=} event
|
|
*/
|
|
resetAndNotify(event) {
|
|
this._reset();
|
|
this._postChangeEvent();
|
|
if (event) {
|
|
event.preventDefault();
|
|
}
|
|
this.element.focus();
|
|
}
|
|
|
|
/**
|
|
* @param {number} minScale
|
|
* @param {number} maxScale
|
|
*/
|
|
setScaleConstraints(minScale, maxScale) {
|
|
this._minScale = minScale;
|
|
this._maxScale = maxScale;
|
|
this._scale = Platform.NumberUtilities.clamp(this._scale, minScale, maxScale);
|
|
}
|
|
|
|
/**
|
|
* @param {number} minX
|
|
* @param {number} maxX
|
|
* @param {number} minY
|
|
* @param {number} maxY
|
|
*/
|
|
clampOffsets(minX, maxX, minY, maxY) {
|
|
this._offsetX = Platform.NumberUtilities.clamp(this._offsetX, minX, maxX);
|
|
this._offsetY = Platform.NumberUtilities.clamp(this._offsetY, minY, maxY);
|
|
}
|
|
|
|
/**
|
|
* @return {number}
|
|
*/
|
|
scale() {
|
|
return this._scale;
|
|
}
|
|
|
|
/**
|
|
* @return {number}
|
|
*/
|
|
offsetX() {
|
|
return this._offsetX;
|
|
}
|
|
|
|
/**
|
|
* @return {number}
|
|
*/
|
|
offsetY() {
|
|
return this._offsetY;
|
|
}
|
|
|
|
/**
|
|
* @return {number}
|
|
*/
|
|
rotateX() {
|
|
return this._rotateX;
|
|
}
|
|
|
|
/**
|
|
* @return {number}
|
|
*/
|
|
rotateY() {
|
|
return this._rotateY;
|
|
}
|
|
|
|
/**
|
|
* @param {number} scaleFactor
|
|
* @param {number} x
|
|
* @param {number} y
|
|
*/
|
|
_onScale(scaleFactor, x, y) {
|
|
scaleFactor =
|
|
Platform.NumberUtilities.clamp(this._scale * scaleFactor, this._minScale, this._maxScale) / this._scale;
|
|
this._scale *= scaleFactor;
|
|
this._offsetX -= (x - this._offsetX) * (scaleFactor - 1);
|
|
this._offsetY -= (y - this._offsetY) * (scaleFactor - 1);
|
|
this._postChangeEvent();
|
|
}
|
|
|
|
/**
|
|
* @param {number} offsetX
|
|
* @param {number} offsetY
|
|
*/
|
|
_onPan(offsetX, offsetY) {
|
|
this._offsetX += offsetX;
|
|
this._offsetY += offsetY;
|
|
this._postChangeEvent();
|
|
}
|
|
|
|
/**
|
|
* @param {number} rotateX
|
|
* @param {number} rotateY
|
|
*/
|
|
_onRotate(rotateX, rotateY) {
|
|
this._rotateX = rotateX;
|
|
this._rotateY = rotateY;
|
|
this._postChangeEvent();
|
|
}
|
|
|
|
/**
|
|
* @param {number} zoomFactor
|
|
*/
|
|
_onKeyboardZoom(zoomFactor) {
|
|
this._onScale(zoomFactor, this.element.clientWidth / 2, this.element.clientHeight / 2);
|
|
}
|
|
|
|
/**
|
|
* @param {number} xMultiplier
|
|
* @param {number} yMultiplier
|
|
*/
|
|
_onKeyboardPanOrRotate(xMultiplier, yMultiplier) {
|
|
const panStepInPixels = 6;
|
|
const rotateStepInDegrees = 5;
|
|
|
|
if (this._mode === Modes.Rotate) {
|
|
// Sic! _onRotate treats X and Y as "rotate around X" and "rotate around Y", so swap X/Y multiplers.
|
|
this._onRotate(
|
|
this._rotateX + yMultiplier * rotateStepInDegrees, this._rotateY + xMultiplier * rotateStepInDegrees);
|
|
} else {
|
|
this._onPan(xMultiplier * panStepInPixels, yMultiplier * panStepInPixels);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {!Event} event
|
|
*/
|
|
_onMouseWheel(event) {
|
|
/** @const */
|
|
const zoomFactor = 1.1;
|
|
/** @const */
|
|
const mouseWheelZoomSpeed = 1 / 120;
|
|
const mouseEvent = /** @type {*} */ (event);
|
|
const scaleFactor = Math.pow(zoomFactor, mouseEvent.wheelDeltaY * mouseWheelZoomSpeed);
|
|
this._onScale(
|
|
scaleFactor, mouseEvent.clientX - this.element.totalOffsetLeft(),
|
|
mouseEvent.clientY - this.element.totalOffsetTop());
|
|
}
|
|
|
|
/**
|
|
* @param {!Event} event
|
|
*/
|
|
_onDrag(event) {
|
|
const {clientX, clientY} = /** @type {!MouseEvent} */ (event);
|
|
if (this._mode === Modes.Rotate) {
|
|
this._onRotate(
|
|
this._oldRotateX + (this._originY - clientY) / this.element.clientHeight * 180,
|
|
this._oldRotateY - (this._originX - clientX) / this.element.clientWidth * 180);
|
|
} else {
|
|
this._onPan(clientX - this._originX, clientY - this._originY);
|
|
this._originX = clientX;
|
|
this._originY = clientY;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {!MouseEvent} event
|
|
*/
|
|
_onDragStart(event) {
|
|
this.element.focus();
|
|
this._originX = event.clientX;
|
|
this._originY = event.clientY;
|
|
this._oldRotateX = this._rotateX;
|
|
this._oldRotateY = this._rotateY;
|
|
return true;
|
|
}
|
|
|
|
_onDragEnd() {
|
|
this._originX = 0;
|
|
this._originY = 0;
|
|
this._oldRotateX = 0;
|
|
this._oldRotateY = 0;
|
|
}
|
|
}
|
|
|
|
/** @enum {symbol} */
|
|
export const Events = {
|
|
TransformChanged: Symbol('TransformChanged')
|
|
};
|
|
|
|
/**
|
|
* @enum {string}
|
|
*/
|
|
export const Modes = {
|
|
Pan: 'Pan',
|
|
Rotate: 'Rotate',
|
|
};
|