blob: 5a9ed3f9cbdc85c93707a099b3d69b3129bf0404 [file] [log] [blame]
// Copyright 2016 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 '../../core/common/common.js';
import * as Host from '../../core/host/host.js';
import * as i18n from '../../core/i18n/i18n.js';
import * as Root from '../../core/root/root.js';
import type * as Platform from '../../core/platform/platform.js';
import * as EmulationModel from '../../models/emulation/emulation.js';
import * as UI from '../../ui/legacy/legacy.js';
import * as MobileThrottling from '../mobile_throttling/mobile_throttling.js';
import * as EmulationComponents from './components/components.js';
import deviceModeToolbarStyles from './deviceModeToolbar.css.legacy.js';
const UIStrings = {
/**
* @description Title of the device dimensions selection iteam in the Device Mode Toolbar.
* webpage in pixels.
*/
dimensions: 'Dimensions',
/**
* @description Title of the width input textbox in the Device Mode Toolbar, for the width of the
* webpage in pixels.
*/
width: 'Width',
/**
* @description Title of the height input textbox in the Device Mode Toolbar, for the height of the
* webpage in pixels. 'leave empty for full' is an instruction to the user - the webpage will be
* full-height if this textbox is left empty.
*/
heightLeaveEmptyForFull: 'Height (leave empty for full)',
/**
* @description Tooltip text for a drop-down menu where the user can select the zoom percentage of
* the webpage preview.
*/
zoom: 'Zoom',
/**
* @description Tooltip tip for a drop-down menu where the user can select the device pixel ratio
* (the ratio between the physical pixels on a screen and CSS logical pixels) of the webpage
* preview.
*/
devicePixelRatio: 'Device pixel ratio',
/**
* @description Tooltip tip for a drop-down menu where the user can select the device type e.g.
* Mobile, Desktop.
*/
deviceType: 'Device type',
/**
* @description Tooltip text for a button to disable Experimental Web Platform Features when they are enabled.
*/
experimentalWebPlatformFeature: '"`Experimental Web Platform Feature`" flag is enabled. Click to disable it.',
/**
* @description Tooltip text for a button to enable Experimental Web Platform Features when they are disabled.
*/
experimentalWebPlatformFeatureFlag: '"`Experimental Web Platform Feature`" flag is disabled. Click to enable it.',
/**
* @description Tooltip text for a 'three dots' style menu button which shows an expanded set of options.
*/
moreOptions: 'More options',
/**
* @description A context menu item in the Device Mode Toolbar. This is a command to resize the
* webpage preview to fit the current window. The placholder is the percentage of full-size that
* will be displayed after fitting.
* @example {30.0} PH1
*/
fitToWindowF: 'Fit to window ({PH1}%)',
/**
* @description A checkbox setting that appears in the context menu for the zoom level, in the
* Device Mode Toolbar.
*/
autoadjustZoom: 'Auto-adjust zoom',
/**
* @description A menu item in the drop-down box that allows the user to select the device pixel
* ratio. Labels the default value which varies between device types, represented by the
* placeholder, which is a number. In the Device Mode Toolbar.
* @example {4.3} PH1
*/
defaultF: 'Default: {PH1}',
/**
* @description Command to hide the frame (like a picture frame) around the mobile device screen.
*/
hideDeviceFrame: 'Hide device frame',
/**
* @description Command to show the frame (like a picture frame) around the mobile device screen.
*/
showDeviceFrame: 'Show device frame',
/**
* @description Command to hide a display in the Device Mode Toolbar that shows the different media
* queries for the device, above the device screen.
* https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries
*/
hideMediaQueries: 'Hide media queries',
/**
* @description Command to show a display in the Device Mode Toolbar that shows the different media
* queries for the device, above the device screen.
* https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries
*/
showMediaQueries: 'Show media queries',
/**
* @description Command in the Device Mode Toolbar to hide a virtual ruler (for measuring),
* displayed above and next to the device screen.
*/
hideRulers: 'Hide rulers',
/**
* @description Command in the Device Mode Toolbar to show a virtual ruler (for measuring),
* displayed above and next to the device screen.
*/
showRulers: 'Show rulers',
/**
* @description Command in the Device Mode Toolbar to remove the drop-down menu from the toolbar
* that lets the user override the device pixel ratio of the emulated device.
*/
removeDevicePixelRatio: 'Remove device pixel ratio',
/**
* @description Command in the Device Mode Toolbar to add the drop-down menu to the toolbar
* that lets the user override the device pixel ratio of the emulated device.
*/
addDevicePixelRatio: 'Add device pixel ratio',
/**
* @description Command in the Device Mode Toolbar to add the drop-down menu to the toolbar
* that lets the user set the device type (e.g. Desktop or Mobile).
*/
removeDeviceType: 'Remove device type',
/**
* @description Command in the Device Mode Toolbar to add the drop-down menu to the toolbar
* that lets the user add the device type (e.g. Desktop or Mobile).
*/
addDeviceType: 'Add device type',
/**
* @description A context menu item in the Device Mode Toolbar that resets all settings back to
* their default values.
*/
resetToDefaults: 'Reset to defaults',
/**
* @description A menu command in the Device Mode Toolbar that closes DevTools.
*/
closeDevtools: 'Close DevTools',
/**
* @description Title of the device selected in the Device Mode Toolbar. The 'response' device is
* not a specific phone/tablet model but a virtual device that can change its height and width
* dynamically by clicking and dragging the sides. 'Response' refers to response design:
* https://developer.mozilla.org/en-US/docs/Learn/CSS/CSS_layout/Responsive_Design
*/
responsive: 'Responsive',
/**
* @description A context menu item in the Device Mode Toolbar that takes the user to a new screen
* where they can add/edit/remove custom devices.
*/
edit: 'Edit…',
/**
* @description Text describing the current orientation of the phone/device (vs. landscape).
*/
portrait: 'Portrait',
/**
* @description Text describing the current orientation of the phone/device (vs. portrait).
*/
landscape: 'Landscape',
/**
* @description Title of button in the Device Mode Toolbar which rotates the device 90 degrees.
*/
rotate: 'Rotate',
/**
* @description Fallback/default text used for the name of a custom device when no name has been
* provided by the user.
*/
none: 'None',
/**
* @description Tooltip of the rotate/screen orientation button.
*/
screenOrientationOptions: 'Screen orientation options',
/**
* @description Tooltip for a button which turns on/off dual-screen mode, which emulates devices
* like tablets which have two screens.
*/
toggleDualscreenMode: 'Toggle dual-screen mode',
};
const str_ = i18n.i18n.registerUIStrings('panels/emulation/DeviceModeToolbar.ts', UIStrings);
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
/**
* Even though the emulation panel uses all UI elements, the tooltips are not supported.
* That's because the emulation elements are rendered around the page context, rather
* than in the DevTools panel itself. Therefore, we need to fall back to using the
* built-in tooltip by setting the title attribute on the button's element.
*/
function setTitleForButton(button: UI.Toolbar.ToolbarButton, title: string): void {
button.setTitle(title);
button.element.title = title;
}
export class DeviceModeToolbar {
private model: EmulationModel.DeviceModeModel.DeviceModeModel;
private readonly showMediaInspectorSetting: Common.Settings.Setting<boolean>;
private readonly showRulersSetting: Common.Settings.Setting<boolean>;
private readonly experimentDualScreenSupport: boolean;
private readonly deviceOutlineSetting: Common.Settings.Setting<boolean>;
private readonly showDeviceScaleFactorSetting: Common.Settings.Setting<boolean>;
private readonly showUserAgentTypeSetting: Common.Settings.Setting<boolean>;
private autoAdjustScaleSetting: Common.Settings.Setting<boolean>;
private readonly lastMode: Map<EmulationModel.EmulatedDevices.EmulatedDevice, EmulationModel.EmulatedDevices.Mode>;
private readonly elementInternal: HTMLDivElement;
private readonly emulatedDevicesList: EmulationModel.EmulatedDevices.EmulatedDevicesList;
private readonly persistenceSetting: Common.Settings.Setting<{device: string, orientation: string, mode: string}>;
private spanButton!: UI.Toolbar.ToolbarButton;
private modeButton!: UI.Toolbar.ToolbarButton;
private widthInput: EmulationComponents.DeviceSizeInputElement.SizeInputElement;
private heightInput: EmulationComponents.DeviceSizeInputElement.SizeInputElement;
private deviceScaleItem!: UI.Toolbar.ToolbarMenuButton;
private deviceSelectItem!: UI.Toolbar.ToolbarMenuButton;
private scaleItem!: UI.Toolbar.ToolbarMenuButton;
private uaItem!: UI.Toolbar.ToolbarMenuButton;
private experimentalButton!: UI.Toolbar.ToolbarToggle|null;
private cachedDeviceScale!: number|null;
private cachedUaType!: string|null;
private xItem?: UI.Toolbar.ToolbarItem;
private throttlingConditionsItem?: UI.Toolbar.ToolbarMenuButton;
private cachedModelType?: EmulationModel.DeviceModeModel.Type;
private cachedScale?: number;
private cachedModelDevice?: EmulationModel.EmulatedDevices.EmulatedDevice|null;
private cachedModelMode?: EmulationModel.EmulatedDevices.Mode|null;
constructor(
model: EmulationModel.DeviceModeModel.DeviceModeModel,
showMediaInspectorSetting: Common.Settings.Setting<boolean>,
showRulersSetting: Common.Settings.Setting<boolean>) {
this.model = model;
this.showMediaInspectorSetting = showMediaInspectorSetting;
this.showRulersSetting = showRulersSetting;
this.experimentDualScreenSupport = Root.Runtime.experiments.isEnabled('dualScreenSupport');
this.deviceOutlineSetting = this.model.deviceOutlineSetting();
this.showDeviceScaleFactorSetting =
Common.Settings.Settings.instance().createSetting('emulation.showDeviceScaleFactor', false);
this.showDeviceScaleFactorSetting.addChangeListener(this.updateDeviceScaleFactorVisibility, this);
this.showUserAgentTypeSetting =
Common.Settings.Settings.instance().createSetting('emulation.showUserAgentType', false);
this.showUserAgentTypeSetting.addChangeListener(this.updateUserAgentTypeVisibility, this);
this.autoAdjustScaleSetting = Common.Settings.Settings.instance().createSetting('emulation.autoAdjustScale', true);
this.lastMode = new Map();
this.elementInternal = document.createElement('div');
this.elementInternal.classList.add('device-mode-toolbar');
const leftContainer = this.elementInternal.createChild('div', 'device-mode-toolbar-spacer');
leftContainer.createChild('div', 'device-mode-toolbar-spacer');
const leftToolbar = new UI.Toolbar.Toolbar('', leftContainer);
this.fillLeftToolbar(leftToolbar);
const mainToolbar = new UI.Toolbar.Toolbar('', this.elementInternal);
mainToolbar.makeWrappable();
this.widthInput = new EmulationComponents.DeviceSizeInputElement.SizeInputElement(i18nString(UIStrings.width));
this.widthInput.addEventListener('sizechanged', ({size: width}) => {
this.model.setWidthAndScaleToFit(width);
});
this.heightInput =
new EmulationComponents.DeviceSizeInputElement.SizeInputElement(i18nString(UIStrings.heightLeaveEmptyForFull));
this.heightInput.addEventListener('sizechanged', ({size: height}) => {
this.model.setHeightAndScaleToFit(height);
});
this.fillMainToolbar(mainToolbar);
const rightContainer = this.elementInternal.createChild('div', 'device-mode-toolbar-spacer');
const rightToolbar = new UI.Toolbar.Toolbar('device-mode-toolbar-fixed-size', rightContainer);
rightToolbar.makeWrappable();
this.fillRightToolbar(rightToolbar);
const modeToolbar = new UI.Toolbar.Toolbar('device-mode-toolbar-fixed-size', rightContainer);
modeToolbar.makeWrappable();
this.fillModeToolbar(modeToolbar);
rightContainer.createChild('div', 'device-mode-toolbar-spacer');
const optionsToolbar = new UI.Toolbar.Toolbar('device-mode-toolbar-options', rightContainer);
optionsToolbar.makeWrappable();
this.fillOptionsToolbar(optionsToolbar);
this.emulatedDevicesList = EmulationModel.EmulatedDevices.EmulatedDevicesList.instance();
this.emulatedDevicesList.addEventListener(
EmulationModel.EmulatedDevices.Events.CustomDevicesUpdated, this.deviceListChanged, this);
this.emulatedDevicesList.addEventListener(
EmulationModel.EmulatedDevices.Events.StandardDevicesUpdated, this.deviceListChanged, this);
this.persistenceSetting = Common.Settings.Settings.instance().createSetting(
'emulation.deviceModeValue', {device: '', orientation: '', mode: ''});
this.model.toolbarControlsEnabledSetting().addChangeListener(updateToolbarsEnabled);
updateToolbarsEnabled();
function updateToolbarsEnabled(): void {
const enabled = model.toolbarControlsEnabledSetting().get();
leftToolbar.setEnabled(enabled);
mainToolbar.setEnabled(enabled);
rightToolbar.setEnabled(enabled);
modeToolbar.setEnabled(enabled);
optionsToolbar.setEnabled(enabled);
}
}
private createEmptyToolbarElement(): Element {
const element = document.createElement('div');
element.classList.add('device-mode-empty-toolbar-element');
return element;
}
private fillLeftToolbar(toolbar: UI.Toolbar.Toolbar): void {
toolbar.appendToolbarItem(this.wrapToolbarItem(this.createEmptyToolbarElement()));
this.deviceSelectItem = new UI.Toolbar.ToolbarMenuButton(this.appendDeviceMenuItems.bind(this));
this.deviceSelectItem.setGlyph('');
this.deviceSelectItem.turnIntoSelect(true);
this.deviceSelectItem.setDarkText();
toolbar.appendToolbarItem(this.deviceSelectItem);
}
private fillMainToolbar(toolbar: UI.Toolbar.Toolbar): void {
toolbar.appendToolbarItem(new UI.Toolbar.ToolbarItem(this.widthInput));
const xElement = document.createElement('div');
xElement.classList.add('device-mode-x');
xElement.textContent = 'Ă—';
this.xItem = this.wrapToolbarItem(xElement);
toolbar.appendToolbarItem(this.xItem);
toolbar.appendToolbarItem(new UI.Toolbar.ToolbarItem(this.heightInput));
}
private fillRightToolbar(toolbar: UI.Toolbar.Toolbar): void {
toolbar.appendToolbarItem(this.wrapToolbarItem(this.createEmptyToolbarElement()));
this.scaleItem = new UI.Toolbar.ToolbarMenuButton(this.appendScaleMenuItems.bind(this));
setTitleForButton(this.scaleItem, i18nString(UIStrings.zoom));
this.scaleItem.setGlyph('');
this.scaleItem.turnIntoSelect();
this.scaleItem.setDarkText();
toolbar.appendToolbarItem(this.scaleItem);
toolbar.appendToolbarItem(this.wrapToolbarItem(this.createEmptyToolbarElement()));
this.deviceScaleItem = new UI.Toolbar.ToolbarMenuButton(this.appendDeviceScaleMenuItems.bind(this));
this.deviceScaleItem.setVisible(this.showDeviceScaleFactorSetting.get());
setTitleForButton(this.deviceScaleItem, i18nString(UIStrings.devicePixelRatio));
this.deviceScaleItem.setGlyph('');
this.deviceScaleItem.turnIntoSelect();
this.deviceScaleItem.setDarkText();
toolbar.appendToolbarItem(this.deviceScaleItem);
toolbar.appendToolbarItem(this.wrapToolbarItem(this.createEmptyToolbarElement()));
this.uaItem = new UI.Toolbar.ToolbarMenuButton(this.appendUserAgentMenuItems.bind(this));
this.uaItem.setVisible(this.showUserAgentTypeSetting.get());
setTitleForButton(this.uaItem, i18nString(UIStrings.deviceType));
this.uaItem.setGlyph('');
this.uaItem.turnIntoSelect();
this.uaItem.setDarkText();
toolbar.appendToolbarItem(this.uaItem);
this.throttlingConditionsItem =
MobileThrottling.ThrottlingManager.throttlingManager().createMobileThrottlingButton();
toolbar.appendToolbarItem(this.throttlingConditionsItem);
}
private fillModeToolbar(toolbar: UI.Toolbar.Toolbar): void {
toolbar.appendToolbarItem(this.wrapToolbarItem(this.createEmptyToolbarElement()));
this.modeButton = new UI.Toolbar.ToolbarButton('', 'largeicon-rotate-screen');
this.modeButton.addEventListener(UI.Toolbar.ToolbarButton.Events.Click, this.modeMenuClicked, this);
toolbar.appendToolbarItem(this.modeButton);
if (this.experimentDualScreenSupport) {
this.spanButton = new UI.Toolbar.ToolbarButton('', 'largeicon-dual-screen');
this.spanButton.addEventListener(UI.Toolbar.ToolbarButton.Events.Click, this.spanClicked, this);
toolbar.appendToolbarItem(this.spanButton);
this.createExperimentalButton(toolbar);
}
}
private createExperimentalButton(toolbar: UI.Toolbar.Toolbar): void {
toolbar.appendToolbarItem(new UI.Toolbar.ToolbarSeparator(true));
const title = (this.model.webPlatformExperimentalFeaturesEnabled()) ?
i18nString(UIStrings.experimentalWebPlatformFeature) :
i18nString(UIStrings.experimentalWebPlatformFeatureFlag);
this.experimentalButton = new UI.Toolbar.ToolbarToggle(title, 'largeicon-experimental-api');
this.experimentalButton.setToggled(this.model.webPlatformExperimentalFeaturesEnabled());
this.experimentalButton.setEnabled(true);
this.experimentalButton.addEventListener(UI.Toolbar.ToolbarButton.Events.Click, this.experimentalClicked, this);
toolbar.appendToolbarItem(this.experimentalButton);
}
private experimentalClicked(): void {
Host.InspectorFrontendHost.InspectorFrontendHostInstance.openInNewTab(
'chrome://flags/#enable-experimental-web-platform-features' as Platform.DevToolsPath.UrlString);
}
private fillOptionsToolbar(toolbar: UI.Toolbar.Toolbar): void {
toolbar.appendToolbarItem(this.wrapToolbarItem(this.createEmptyToolbarElement()));
const moreOptionsButton = new UI.Toolbar.ToolbarMenuButton(this.appendOptionsMenuItems.bind(this));
setTitleForButton(moreOptionsButton, i18nString(UIStrings.moreOptions));
toolbar.appendToolbarItem(moreOptionsButton);
}
private appendScaleMenuItems(contextMenu: UI.ContextMenu.ContextMenu): void {
if (this.model.type() === EmulationModel.DeviceModeModel.Type.Device) {
contextMenu.footerSection().appendItem(
i18nString(UIStrings.fitToWindowF, {PH1: this.getPrettyFitZoomPercentage()}),
this.onScaleMenuChanged.bind(this, this.model.fitScale()), false);
}
contextMenu.footerSection().appendCheckboxItem(
i18nString(UIStrings.autoadjustZoom), this.onAutoAdjustScaleChanged.bind(this),
this.autoAdjustScaleSetting.get());
const boundAppendScaleItem = appendScaleItem.bind(this);
boundAppendScaleItem('50%', 0.5);
boundAppendScaleItem('75%', 0.75);
boundAppendScaleItem('100%', 1);
boundAppendScaleItem('125%', 1.25);
boundAppendScaleItem('150%', 1.5);
boundAppendScaleItem('200%', 2);
function appendScaleItem(this: DeviceModeToolbar, title: string, value: number): void {
contextMenu.defaultSection().appendCheckboxItem(
title, this.onScaleMenuChanged.bind(this, value), this.model.scaleSetting().get() === value, false);
}
}
private onScaleMenuChanged(value: number): void {
this.model.scaleSetting().set(value);
}
private onAutoAdjustScaleChanged(): void {
this.autoAdjustScaleSetting.set(!this.autoAdjustScaleSetting.get());
}
private appendDeviceScaleMenuItems(contextMenu: UI.ContextMenu.ContextMenu): void {
const deviceScaleFactorSetting = this.model.deviceScaleFactorSetting();
const defaultValue = this.model.uaSetting().get() === EmulationModel.DeviceModeModel.UA.Mobile ||
this.model.uaSetting().get() === EmulationModel.DeviceModeModel.UA.MobileNoTouch ?
EmulationModel.DeviceModeModel.defaultMobileScaleFactor :
window.devicePixelRatio;
appendDeviceScaleFactorItem(contextMenu.headerSection(), i18nString(UIStrings.defaultF, {PH1: defaultValue}), 0);
appendDeviceScaleFactorItem(contextMenu.defaultSection(), '1', 1);
appendDeviceScaleFactorItem(contextMenu.defaultSection(), '2', 2);
appendDeviceScaleFactorItem(contextMenu.defaultSection(), '3', 3);
function appendDeviceScaleFactorItem(section: UI.ContextMenu.Section, title: string, value: number): void {
section.appendCheckboxItem(
title, deviceScaleFactorSetting.set.bind(deviceScaleFactorSetting, value),
deviceScaleFactorSetting.get() === value);
}
}
private appendUserAgentMenuItems(contextMenu: UI.ContextMenu.ContextMenu): void {
const uaSetting = this.model.uaSetting();
appendUAItem(EmulationModel.DeviceModeModel.UA.Mobile, EmulationModel.DeviceModeModel.UA.Mobile);
appendUAItem(EmulationModel.DeviceModeModel.UA.MobileNoTouch, EmulationModel.DeviceModeModel.UA.MobileNoTouch);
appendUAItem(EmulationModel.DeviceModeModel.UA.Desktop, EmulationModel.DeviceModeModel.UA.Desktop);
appendUAItem(EmulationModel.DeviceModeModel.UA.DesktopTouch, EmulationModel.DeviceModeModel.UA.DesktopTouch);
function appendUAItem(title: string, value: EmulationModel.DeviceModeModel.UA): void {
contextMenu.defaultSection().appendCheckboxItem(
title, uaSetting.set.bind(uaSetting, value), uaSetting.get() === value);
}
}
private appendOptionsMenuItems(contextMenu: UI.ContextMenu.ContextMenu): void {
const model = this.model;
appendToggleItem(
contextMenu.headerSection(), this.deviceOutlineSetting, i18nString(UIStrings.hideDeviceFrame),
i18nString(UIStrings.showDeviceFrame), model.type() !== EmulationModel.DeviceModeModel.Type.Device);
appendToggleItem(
contextMenu.headerSection(), this.showMediaInspectorSetting, i18nString(UIStrings.hideMediaQueries),
i18nString(UIStrings.showMediaQueries));
appendToggleItem(
contextMenu.headerSection(), this.showRulersSetting, i18nString(UIStrings.hideRulers),
i18nString(UIStrings.showRulers));
appendToggleItem(
contextMenu.defaultSection(), this.showDeviceScaleFactorSetting, i18nString(UIStrings.removeDevicePixelRatio),
i18nString(UIStrings.addDevicePixelRatio));
appendToggleItem(
contextMenu.defaultSection(), this.showUserAgentTypeSetting, i18nString(UIStrings.removeDeviceType),
i18nString(UIStrings.addDeviceType));
contextMenu.appendItemsAtLocation('deviceModeMenu');
contextMenu.footerSection().appendItem(i18nString(UIStrings.resetToDefaults), this.reset.bind(this));
contextMenu.footerSection().appendItem(
i18nString(UIStrings.closeDevtools),
Host.InspectorFrontendHost.InspectorFrontendHostInstance.closeWindow.bind(
Host.InspectorFrontendHost.InspectorFrontendHostInstance));
function appendToggleItem(
section: UI.ContextMenu.Section, setting: Common.Settings.Setting<unknown>, title1: string, title2: string,
disabled?: boolean): void {
if (typeof disabled === 'undefined') {
disabled = model.type() === EmulationModel.DeviceModeModel.Type.None;
}
section.appendItem(setting.get() ? title1 : title2, setting.set.bind(setting, !setting.get()), disabled);
}
}
private reset(): void {
this.deviceOutlineSetting.set(false);
this.showDeviceScaleFactorSetting.set(false);
this.showUserAgentTypeSetting.set(false);
this.showMediaInspectorSetting.set(false);
this.showRulersSetting.set(false);
this.model.reset();
}
private wrapToolbarItem(element: Element): UI.Toolbar.ToolbarItem {
const container = document.createElement('div');
const shadowRoot = UI.Utils.createShadowRootWithCoreStyles(
container, {cssFile: deviceModeToolbarStyles, delegatesFocus: undefined});
shadowRoot.appendChild(element);
return new UI.Toolbar.ToolbarItem(container);
}
private emulateDevice(device: EmulationModel.EmulatedDevices.EmulatedDevice): void {
const scale = this.autoAdjustScaleSetting.get() ? undefined : this.model.scaleSetting().get();
this.model.emulate(
EmulationModel.DeviceModeModel.Type.Device, device, this.lastMode.get(device) || device.modes[0], scale);
}
private switchToResponsive(): void {
this.model.emulate(EmulationModel.DeviceModeModel.Type.Responsive, null, null);
}
private filterDevices(devices: EmulationModel.EmulatedDevices.EmulatedDevice[]):
EmulationModel.EmulatedDevices.EmulatedDevice[] {
devices = devices.filter(function(d) {
return d.show();
});
devices.sort(EmulationModel.EmulatedDevices.EmulatedDevice.deviceComparator);
return devices;
}
private standardDevices(): EmulationModel.EmulatedDevices.EmulatedDevice[] {
return this.filterDevices(this.emulatedDevicesList.standard());
}
private customDevices(): EmulationModel.EmulatedDevices.EmulatedDevice[] {
return this.filterDevices(this.emulatedDevicesList.custom());
}
private allDevices(): EmulationModel.EmulatedDevices.EmulatedDevice[] {
return this.standardDevices().concat(this.customDevices());
}
private appendDeviceMenuItems(contextMenu: UI.ContextMenu.ContextMenu): void {
contextMenu.headerSection().appendCheckboxItem(
i18nString(UIStrings.responsive), this.switchToResponsive.bind(this),
this.model.type() === EmulationModel.DeviceModeModel.Type.Responsive, false);
appendGroup.call(this, this.standardDevices());
appendGroup.call(this, this.customDevices());
contextMenu.footerSection().appendItem(
i18nString(UIStrings.edit), this.emulatedDevicesList.revealCustomSetting.bind(this.emulatedDevicesList), false);
function appendGroup(this: DeviceModeToolbar, devices: EmulationModel.EmulatedDevices.EmulatedDevice[]): void {
if (!devices.length) {
return;
}
const section = contextMenu.section();
for (const device of devices) {
section.appendCheckboxItem(
device.title, this.emulateDevice.bind(this, device), this.model.device() === device, false);
}
}
}
private deviceListChanged(): void {
const device = this.model.device();
if (!device) {
return;
}
const devices = this.allDevices();
if (devices.indexOf(device) === -1) {
if (devices.length) {
this.emulateDevice(devices[0]);
} else {
this.model.emulate(EmulationModel.DeviceModeModel.Type.Responsive, null, null);
}
} else {
this.emulateDevice(device);
}
}
private updateDeviceScaleFactorVisibility(): void {
if (this.deviceScaleItem) {
this.deviceScaleItem.setVisible(this.showDeviceScaleFactorSetting.get());
}
}
private updateUserAgentTypeVisibility(): void {
if (this.uaItem) {
this.uaItem.setVisible(this.showUserAgentTypeSetting.get());
}
}
private spanClicked(): void {
const device = this.model.device();
if (!device || !device.isDualScreen) {
return;
}
const scale = this.autoAdjustScaleSetting.get() ? undefined : this.model.scaleSetting().get();
const mode = this.model.mode();
if (!mode) {
return;
}
const newMode = device.getSpanPartner(mode);
if (!newMode) {
return;
}
this.model.emulate(this.model.type(), device, newMode, scale);
return;
}
private modeMenuClicked(event: {
data: Event,
}): void {
const device = this.model.device();
const model = this.model;
const autoAdjustScaleSetting = this.autoAdjustScaleSetting;
if (model.type() === EmulationModel.DeviceModeModel.Type.Responsive) {
const appliedSize = model.appliedDeviceSize();
if (autoAdjustScaleSetting.get()) {
model.setSizeAndScaleToFit(appliedSize.height, appliedSize.width);
} else {
model.setWidth(appliedSize.height);
model.setHeight(appliedSize.width);
}
return;
}
if (!device) {
return;
}
if ((device.isDualScreen || device.modes.length === 2) &&
device.modes[0].orientation !== device.modes[1].orientation) {
const scale = autoAdjustScaleSetting.get() ? undefined : model.scaleSetting().get();
const mode = model.mode();
if (!mode) {
return;
}
const rotationPartner = device.getRotationPartner(mode);
if (!rotationPartner) {
return;
}
model.emulate(model.type(), model.device(), rotationPartner, scale);
return;
}
if (!this.modeButton) {
return;
}
const contextMenu = new UI.ContextMenu.ContextMenu(event.data, {
useSoftMenu: false,
x: this.modeButton.element.totalOffsetLeft(),
y: this.modeButton.element.totalOffsetTop() + (this.modeButton.element as HTMLElement).offsetHeight,
});
addOrientation(EmulationModel.EmulatedDevices.Vertical, i18nString(UIStrings.portrait));
addOrientation(EmulationModel.EmulatedDevices.Horizontal, i18nString(UIStrings.landscape));
void contextMenu.show();
function addOrientation(orientation: string, title: string): void {
if (!device) {
return;
}
const modes = device.modesForOrientation(orientation);
if (!modes.length) {
return;
}
if (modes.length === 1) {
addMode(modes[0], title);
} else {
for (let index = 0; index < modes.length; index++) {
addMode(modes[index], title + ' \u2013 ' + modes[index].title);
}
}
}
function addMode(mode: EmulationModel.EmulatedDevices.Mode, title: string): void {
contextMenu.defaultSection().appendCheckboxItem(title, applyMode.bind(null, mode), model.mode() === mode, false);
}
function applyMode(mode: EmulationModel.EmulatedDevices.Mode): void {
const scale = autoAdjustScaleSetting.get() ? undefined : model.scaleSetting().get();
model.emulate(model.type(), model.device(), mode, scale);
}
}
private getPrettyFitZoomPercentage(): string {
return `${(this.model.fitScale() * 100).toFixed(0)}`;
}
private getPrettyZoomPercentage(): string {
return `${(this.model.scale() * 100).toFixed(0)}`;
}
element(): Element {
return this.elementInternal;
}
update(): void {
if (this.model.type() !== this.cachedModelType) {
this.cachedModelType = this.model.type();
this.widthInput.disabled = this.model.type() !== EmulationModel.DeviceModeModel.Type.Responsive;
this.heightInput.disabled = this.model.type() !== EmulationModel.DeviceModeModel.Type.Responsive;
this.deviceScaleItem.setEnabled(this.model.type() === EmulationModel.DeviceModeModel.Type.Responsive);
this.uaItem.setEnabled(this.model.type() === EmulationModel.DeviceModeModel.Type.Responsive);
if (this.model.type() === EmulationModel.DeviceModeModel.Type.Responsive) {
this.modeButton.setEnabled(true);
setTitleForButton(this.modeButton, i18nString(UIStrings.rotate));
} else {
this.modeButton.setEnabled(false);
}
}
const size = this.model.appliedDeviceSize();
this.widthInput.size = String(size.width);
this.heightInput.size =
this.model.type() === EmulationModel.DeviceModeModel.Type.Responsive && this.model.isFullHeight() ?
'' :
String(size.height);
this.heightInput.placeholder = String(size.height);
if (this.model.scale() !== this.cachedScale) {
this.scaleItem.setText(`${this.getPrettyZoomPercentage()}%`);
this.cachedScale = this.model.scale();
}
const deviceScale = this.model.appliedDeviceScaleFactor();
if (deviceScale !== this.cachedDeviceScale) {
this.deviceScaleItem.setText(`DPR: ${deviceScale.toFixed(1)}`);
this.cachedDeviceScale = deviceScale;
}
const uaType = this.model.appliedUserAgentType();
if (uaType !== this.cachedUaType) {
this.uaItem.setText(uaType);
this.cachedUaType = uaType;
}
let deviceItemTitle: string = i18nString(UIStrings.none);
if (this.model.type() === EmulationModel.DeviceModeModel.Type.Responsive) {
deviceItemTitle = i18nString(UIStrings.responsive);
}
const device = this.model.device();
if (this.model.type() === EmulationModel.DeviceModeModel.Type.Device && device) {
deviceItemTitle = device.title;
}
this.deviceSelectItem.setText(`${i18nString(UIStrings.dimensions)}: ${deviceItemTitle}`);
if (this.model.device() !== this.cachedModelDevice) {
const device = this.model.device();
if (device) {
const modeCount = device ? device.modes.length : 0;
this.modeButton.setEnabled(modeCount >= 2);
setTitleForButton(
this.modeButton,
modeCount === 2 ? i18nString(UIStrings.rotate) : i18nString(UIStrings.screenOrientationOptions));
}
this.cachedModelDevice = device;
}
if (this.experimentDualScreenSupport && this.experimentalButton) {
const device = this.model.device();
if (device && device.isDualScreen) {
this.spanButton.setVisible(true);
this.experimentalButton.setVisible(true);
} else {
this.spanButton.setVisible(false);
this.experimentalButton.setVisible(false);
}
setTitleForButton(this.spanButton, i18nString(UIStrings.toggleDualscreenMode));
}
if (this.model.type() === EmulationModel.DeviceModeModel.Type.Device) {
this.lastMode.set(
(this.model.device() as EmulationModel.EmulatedDevices.EmulatedDevice),
(this.model.mode() as EmulationModel.EmulatedDevices.Mode));
}
if (this.model.mode() !== this.cachedModelMode && this.model.type() !== EmulationModel.DeviceModeModel.Type.None) {
this.cachedModelMode = this.model.mode();
const value = this.persistenceSetting.get();
const device = this.model.device();
if (device) {
value.device = device.title;
const mode = this.model.mode();
value.orientation = mode ? mode.orientation : '';
value.mode = mode ? mode.title : '';
} else {
value.device = '';
value.orientation = '';
value.mode = '';
}
this.persistenceSetting.set(value);
}
}
restore(): void {
for (const device of this.allDevices()) {
if (device.title === this.persistenceSetting.get().device) {
for (const mode of device.modes) {
if (mode.orientation === this.persistenceSetting.get().orientation &&
mode.title === this.persistenceSetting.get().mode) {
this.lastMode.set(device, mode);
this.emulateDevice(device);
return;
}
}
}
}
this.model.emulate(EmulationModel.DeviceModeModel.Type.Responsive, null, null);
}
}