mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-05 09:35:39 -05:00
Merge from vscode 966b87dd4013be1a9c06e2b8334522ec61905cc2 (#4696)
This commit is contained in:
285
src/vs/workbench/contrib/debug/browser/debugToolBar.ts
Normal file
285
src/vs/workbench/contrib/debug/browser/debugToolBar.ts
Normal file
@@ -0,0 +1,285 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./media/debugToolBar';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import * as browser from 'vs/base/browser/browser';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import * as arrays from 'vs/base/common/arrays';
|
||||
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { IAction, IRunEvent } from 'vs/base/common/actions';
|
||||
import { ActionBar, ActionsOrientation, Separator } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||
import { IDebugConfiguration, IDebugService, State } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { FocusSessionActionItem } from 'vs/workbench/contrib/debug/browser/debugActionItems';
|
||||
import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { Themable } from 'vs/workbench/common/theme';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { registerColor, contrastBorder, widgetShadow } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { fillInActionBarActions, MenuItemActionItem } from 'vs/platform/actions/browser/menuItemActionItem';
|
||||
import { IMenu, IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { FocusSessionAction } from 'vs/workbench/contrib/debug/browser/debugActions';
|
||||
|
||||
const DEBUG_TOOLBAR_POSITION_KEY = 'debug.actionswidgetposition';
|
||||
const DEBUG_TOOLBAR_Y_KEY = 'debug.actionswidgety';
|
||||
|
||||
export const debugToolBarBackground = registerColor('debugToolBar.background', {
|
||||
dark: '#333333',
|
||||
light: '#F3F3F3',
|
||||
hc: '#000000'
|
||||
}, localize('debugToolBarBackground', "Debug toolbar background color."));
|
||||
export const debugToolBarBorder = registerColor('debugToolBar.border', {
|
||||
dark: null,
|
||||
light: null,
|
||||
hc: null
|
||||
}, localize('debugToolBarBorder', "Debug toolbar border color."));
|
||||
|
||||
export class DebugToolBar extends Themable implements IWorkbenchContribution {
|
||||
|
||||
private $el: HTMLElement;
|
||||
private dragArea: HTMLElement;
|
||||
private actionBar: ActionBar;
|
||||
private activeActions: IAction[];
|
||||
private updateScheduler: RunOnceScheduler;
|
||||
private debugToolBarMenu: IMenu;
|
||||
|
||||
private isVisible: boolean;
|
||||
private isBuilt: boolean;
|
||||
|
||||
constructor(
|
||||
@INotificationService private readonly notificationService: INotificationService,
|
||||
@ITelemetryService private readonly telemetryService: ITelemetryService,
|
||||
@IDebugService private readonly debugService: IDebugService,
|
||||
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
|
||||
@IStorageService private readonly storageService: IStorageService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IKeybindingService private readonly keybindingService: IKeybindingService,
|
||||
@IContextViewService contextViewService: IContextViewService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IMenuService menuService: IMenuService,
|
||||
@IContextMenuService contextMenuService: IContextMenuService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService
|
||||
) {
|
||||
super(themeService);
|
||||
|
||||
this.$el = dom.$('div.debug-toolbar');
|
||||
this.$el.style.top = `${layoutService.getTitleBarOffset()}px`;
|
||||
|
||||
this.dragArea = dom.append(this.$el, dom.$('div.drag-area'));
|
||||
|
||||
const actionBarContainer = dom.append(this.$el, dom.$('div.action-bar-container'));
|
||||
this.debugToolBarMenu = menuService.createMenu(MenuId.DebugToolBar, contextKeyService);
|
||||
this.toDispose.push(this.debugToolBarMenu);
|
||||
|
||||
this.activeActions = [];
|
||||
this.actionBar = this._register(new ActionBar(actionBarContainer, {
|
||||
orientation: ActionsOrientation.HORIZONTAL,
|
||||
actionItemProvider: (action: IAction) => {
|
||||
if (action.id === FocusSessionAction.ID) {
|
||||
return new FocusSessionActionItem(action, this.debugService, this.themeService, contextViewService);
|
||||
}
|
||||
if (action instanceof MenuItemAction) {
|
||||
return new MenuItemActionItem(action, this.keybindingService, this.notificationService, contextMenuService);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
}));
|
||||
|
||||
this.updateScheduler = this._register(new RunOnceScheduler(() => {
|
||||
const state = this.debugService.state;
|
||||
const toolBarLocation = this.configurationService.getValue<IDebugConfiguration>('debug').toolBarLocation;
|
||||
if (state === State.Inactive || toolBarLocation === 'docked' || toolBarLocation === 'hidden') {
|
||||
return this.hide();
|
||||
}
|
||||
|
||||
const actions = DebugToolBar.getActions(this.debugToolBarMenu, this.debugService, this.instantiationService);
|
||||
if (!arrays.equals(actions, this.activeActions, (first, second) => first.id === second.id)) {
|
||||
this.actionBar.clear();
|
||||
this.actionBar.push(actions, { icon: true, label: false });
|
||||
this.activeActions = actions;
|
||||
}
|
||||
this.show();
|
||||
}, 20));
|
||||
|
||||
this.updateStyles();
|
||||
|
||||
this.registerListeners();
|
||||
|
||||
this.hide();
|
||||
this.isBuilt = false;
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this._register(this.debugService.onDidChangeState(() => this.updateScheduler.schedule()));
|
||||
this._register(this.debugService.getViewModel().onDidFocusSession(() => this.updateScheduler.schedule()));
|
||||
this._register(this.configurationService.onDidChangeConfiguration(e => this.onDidConfigurationChange(e)));
|
||||
this._register(this.actionBar.actionRunner.onDidRun((e: IRunEvent) => {
|
||||
// check for error
|
||||
if (e.error && !errors.isPromiseCanceledError(e.error)) {
|
||||
this.notificationService.error(e.error);
|
||||
}
|
||||
|
||||
// log in telemetry
|
||||
if (this.telemetryService) {
|
||||
/* __GDPR__
|
||||
"workbenchActionExecuted" : {
|
||||
"id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"from": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('workbenchActionExecuted', { id: e.action.id, from: 'debugActionsWidget' });
|
||||
}
|
||||
}));
|
||||
this._register(dom.addDisposableListener(window, dom.EventType.RESIZE, () => this.setCoordinates()));
|
||||
|
||||
this._register(dom.addDisposableListener(this.dragArea, dom.EventType.MOUSE_UP, (event: MouseEvent) => {
|
||||
const mouseClickEvent = new StandardMouseEvent(event);
|
||||
if (mouseClickEvent.detail === 2) {
|
||||
// double click on debug bar centers it again #8250
|
||||
const widgetWidth = this.$el.clientWidth;
|
||||
this.setCoordinates(0.5 * window.innerWidth - 0.5 * widgetWidth, 0);
|
||||
this.storePosition();
|
||||
}
|
||||
}));
|
||||
|
||||
this._register(dom.addDisposableListener(this.dragArea, dom.EventType.MOUSE_DOWN, (event: MouseEvent) => {
|
||||
dom.addClass(this.dragArea, 'dragged');
|
||||
|
||||
const mouseMoveListener = dom.addDisposableListener(window, 'mousemove', (e: MouseEvent) => {
|
||||
const mouseMoveEvent = new StandardMouseEvent(e);
|
||||
// Prevent default to stop editor selecting text #8524
|
||||
mouseMoveEvent.preventDefault();
|
||||
// Reduce x by width of drag handle to reduce jarring #16604
|
||||
this.setCoordinates(mouseMoveEvent.posx - 14, mouseMoveEvent.posy - this.layoutService.getTitleBarOffset());
|
||||
});
|
||||
|
||||
const mouseUpListener = dom.addDisposableListener(window, 'mouseup', (e: MouseEvent) => {
|
||||
this.storePosition();
|
||||
dom.removeClass(this.dragArea, 'dragged');
|
||||
|
||||
mouseMoveListener.dispose();
|
||||
mouseUpListener.dispose();
|
||||
});
|
||||
}));
|
||||
|
||||
this._register(this.layoutService.onTitleBarVisibilityChange(() => this.setYCoordinate()));
|
||||
this._register(browser.onDidChangeZoomLevel(() => this.setYCoordinate()));
|
||||
}
|
||||
|
||||
private storePosition(): void {
|
||||
const left = dom.getComputedStyle(this.$el).left;
|
||||
if (left) {
|
||||
const position = parseFloat(left) / window.innerWidth;
|
||||
this.storageService.store(DEBUG_TOOLBAR_POSITION_KEY, position, StorageScope.GLOBAL);
|
||||
}
|
||||
}
|
||||
|
||||
protected updateStyles(): void {
|
||||
super.updateStyles();
|
||||
|
||||
if (this.$el) {
|
||||
this.$el.style.backgroundColor = this.getColor(debugToolBarBackground);
|
||||
|
||||
const widgetShadowColor = this.getColor(widgetShadow);
|
||||
this.$el.style.boxShadow = widgetShadowColor ? `0 5px 8px ${widgetShadowColor}` : null;
|
||||
|
||||
const contrastBorderColor = this.getColor(contrastBorder);
|
||||
const borderColor = this.getColor(debugToolBarBorder);
|
||||
|
||||
if (contrastBorderColor) {
|
||||
this.$el.style.border = `1px solid ${contrastBorderColor}`;
|
||||
} else {
|
||||
this.$el.style.border = borderColor ? `solid ${borderColor}` : 'none';
|
||||
this.$el.style.border = '1px 0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private setYCoordinate(y = 0): void {
|
||||
const titlebarOffset = this.layoutService.getTitleBarOffset();
|
||||
this.$el.style.top = `${titlebarOffset + y}px`;
|
||||
}
|
||||
|
||||
private setCoordinates(x?: number, y?: number): void {
|
||||
if (!this.isVisible) {
|
||||
return;
|
||||
}
|
||||
const widgetWidth = this.$el.clientWidth;
|
||||
if (x === undefined) {
|
||||
const positionPercentage = this.storageService.get(DEBUG_TOOLBAR_POSITION_KEY, StorageScope.GLOBAL);
|
||||
x = positionPercentage !== undefined ? parseFloat(positionPercentage) * window.innerWidth : (0.5 * window.innerWidth - 0.5 * widgetWidth);
|
||||
}
|
||||
|
||||
x = Math.max(0, Math.min(x, window.innerWidth - widgetWidth)); // do not allow the widget to overflow on the right
|
||||
this.$el.style.left = `${x}px`;
|
||||
|
||||
if (y === undefined) {
|
||||
y = this.storageService.getNumber(DEBUG_TOOLBAR_Y_KEY, StorageScope.GLOBAL, 0);
|
||||
}
|
||||
const titleAreaHeight = 35;
|
||||
if ((y < titleAreaHeight / 2) || (y > titleAreaHeight + titleAreaHeight / 2)) {
|
||||
const moveToTop = y < titleAreaHeight;
|
||||
this.setYCoordinate(moveToTop ? 0 : titleAreaHeight);
|
||||
this.storageService.store(DEBUG_TOOLBAR_Y_KEY, moveToTop ? 0 : 2 * titleAreaHeight, StorageScope.GLOBAL);
|
||||
}
|
||||
}
|
||||
|
||||
private onDidConfigurationChange(event: IConfigurationChangeEvent): void {
|
||||
if (event.affectsConfiguration('debug.hideActionBar') || event.affectsConfiguration('debug.toolBarLocation')) {
|
||||
this.updateScheduler.schedule();
|
||||
}
|
||||
}
|
||||
|
||||
private show(): void {
|
||||
if (this.isVisible) {
|
||||
this.setCoordinates();
|
||||
return;
|
||||
}
|
||||
if (!this.isBuilt) {
|
||||
this.isBuilt = true;
|
||||
this.layoutService.getWorkbenchElement().appendChild(this.$el);
|
||||
}
|
||||
|
||||
this.isVisible = true;
|
||||
dom.show(this.$el);
|
||||
this.setCoordinates();
|
||||
}
|
||||
|
||||
private hide(): void {
|
||||
this.isVisible = false;
|
||||
dom.hide(this.$el);
|
||||
}
|
||||
|
||||
public static getActions(menu: IMenu, debugService: IDebugService, instantiationService: IInstantiationService): IAction[] {
|
||||
const actions: IAction[] = [];
|
||||
fillInActionBarActions(menu, undefined, actions, () => false);
|
||||
if (debugService.getViewModel().isMultiSessionView()) {
|
||||
actions.push(instantiationService.createInstance(FocusSessionAction, FocusSessionAction.ID, FocusSessionAction.LABEL));
|
||||
}
|
||||
|
||||
return actions.filter(a => !(a instanceof Separator)); // do not render separators for now
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
super.dispose();
|
||||
|
||||
if (this.$el) {
|
||||
this.$el.remove();
|
||||
delete this.$el;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user