debugActionsWidget.ts 8.0 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

I
isidor 已提交
6 7
import * as lifecycle from 'vs/base/common/lifecycle';
import * as errors from 'vs/base/common/errors';
8
import * as strings from 'vs/base/common/strings';
E
Erich Gamma 已提交
9
import severity from 'vs/base/common/severity';
I
isidor 已提交
10 11
import * as builder from 'vs/base/browser/builder';
import * as dom from 'vs/base/browser/dom';
J
Johannes Rieken 已提交
12
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
I
isidor 已提交
13 14 15
import { IAction } from 'vs/base/common/actions';
import { EventType } from 'vs/base/common/events';
import { ActionBar, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
J
Johannes Rieken 已提交
16
import { IPartService } from 'vs/workbench/services/part/common/partService';
I
isidor 已提交
17 18
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import * as debug from 'vs/workbench/parts/debug/common/debug';
I
isidor 已提交
19
import { AbstractDebugAction, PauseAction, ContinueAction, StepBackAction, StopAction, DisconnectAction, StepOverAction, StepIntoAction, StepOutAction, RestartAction } from 'vs/workbench/parts/debug/browser/debugActions';
J
Johannes Rieken 已提交
20 21 22 23
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IMessageService } from 'vs/platform/message/common/message';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
E
Erich Gamma 已提交
24 25 26

import IDebugService = debug.IDebugService;

I
isidor 已提交
27
const $ = builder.$;
I
isidor 已提交
28
const DEBUG_ACTIONS_WIDGET_POSITION_KEY = 'debug.actionswidgetposition';
E
Erich Gamma 已提交
29

I
isidor 已提交
30
export class DebugActionsWidget implements IWorkbenchContribution {
E
Erich Gamma 已提交
31 32 33
	private static ID = 'debug.actionsWidget';

	private $el: builder.Builder;
I
isidor 已提交
34
	private dragArea: builder.Builder;
E
Erich Gamma 已提交
35
	private toDispose: lifecycle.IDisposable[];
I
isidor 已提交
36
	private actionBar: ActionBar;
I
isidor 已提交
37
	private actions: AbstractDebugAction[];
E
Erich Gamma 已提交
38 39 40 41 42 43 44
	private isVisible: boolean;
	private isBuilt: boolean;

	constructor(
		@IMessageService private messageService: IMessageService,
		@ITelemetryService private telemetryService: ITelemetryService,
		@IDebugService private debugService: IDebugService,
I
isidor 已提交
45
		@IInstantiationService private instantiationService: IInstantiationService,
46
		@IPartService private partService: IPartService,
I
isidor 已提交
47
		@IStorageService private storageService: IStorageService
E
Erich Gamma 已提交
48 49
	) {
		this.$el = $().div().addClass('debug-actions-widget');
I
isidor 已提交
50 51 52 53 54
		this.dragArea = $().div().addClass('drag-area');
		this.$el.append(this.dragArea);

		const actionBarContainter = $().div().addClass('.action-bar-container');
		this.$el.append(actionBarContainter);
E
Erich Gamma 已提交
55 56

		this.toDispose = [];
I
isidor 已提交
57 58
		this.actionBar = new ActionBar(actionBarContainter, {
			orientation: ActionsOrientation.HORIZONTAL
E
Erich Gamma 已提交
59 60 61 62 63 64 65 66 67 68
		});

		this.toDispose.push(this.actionBar);
		this.registerListeners();

		this.hide();
		this.isBuilt = false;
	}

	private registerListeners(): void {
I
isidor 已提交
69
		this.toDispose.push(this.debugService.onDidChangeState(() => {
70
			this.update();
E
Erich Gamma 已提交
71
		}));
I
isidor 已提交
72
		this.toDispose.push(this.actionBar.actionRunner.addListener2(EventType.RUN, (e: any) => {
I
isidor 已提交
73
			// check for error
E
Erich Gamma 已提交
74 75 76 77
			if (e.error && !errors.isPromiseCanceledError(e.error)) {
				this.messageService.show(severity.Error, e.error);
			}

I
isidor 已提交
78
			// log in telemetry
E
Erich Gamma 已提交
79 80 81 82
			if (this.telemetryService) {
				this.telemetryService.publicLog('workbenchActionExecuted', { id: e.action.id, from: 'debugActionsWidget' });
			}
		}));
83
		$(window).on(dom.EventType.RESIZE, () => this.setXCoordinate(), this.toDispose);
I
isidor 已提交
84

85 86 87 88 89 90 91 92 93
		this.dragArea.on(dom.EventType.MOUSE_UP, (event: MouseEvent) => {
			const mouseClickEvent = new StandardMouseEvent(event);
			if (mouseClickEvent.detail === 2) {
				// double click on debug bar centers it again #8250
				this.setXCoordinate(0.5 * window.innerWidth);
			}
		});

		this.dragArea.on(dom.EventType.MOUSE_DOWN, (event: MouseEvent) => {
I
isidor 已提交
94
			const $window = $(window);
I
isidor 已提交
95
			this.dragArea.addClass('dragged');
I
isidor 已提交
96 97 98

			$window.on('mousemove', (e: MouseEvent) => {
				const mouseMoveEvent = new StandardMouseEvent(e);
99 100
				// Prevent default to stop editor selecting text #8524
				mouseMoveEvent.preventDefault();
I
isidor 已提交
101 102 103 104
				this.setXCoordinate(mouseMoveEvent.posx);
			}).once('mouseup', (e: MouseEvent) => {
				const mouseMoveEvent = new StandardMouseEvent(e);
				this.storageService.store(DEBUG_ACTIONS_WIDGET_POSITION_KEY, mouseMoveEvent.posx / window.innerWidth, StorageScope.WORKSPACE);
I
isidor 已提交
105
				this.dragArea.removeClass('dragged');
I
isidor 已提交
106 107 108 109 110
				$window.off('mousemove');
			});
		});
	}

111 112 113 114 115 116 117
	private setXCoordinate(x?: number): void {
		if (!this.isVisible) {
			return;
		}
		if (!x) {
			x = parseFloat(this.storageService.get(DEBUG_ACTIONS_WIDGET_POSITION_KEY, StorageScope.WORKSPACE, '0.5')) * window.innerWidth;
		}
I
isidor 已提交
118 119

		const halfWidgetWidth = this.$el.getHTMLElement().clientWidth / 2;
120
		x = x + halfWidgetWidth - 16; // take into account half the size of the widget
I
isidor 已提交
121
		x = Math.max(148, x); // do not allow the widget to overflow on the left
122
		x = Math.min(x, window.innerWidth - halfWidgetWidth - 10); // do not allow the widget to overflow on the right
I
isidor 已提交
123
		this.$el.style('left', `${x}px`);
E
Erich Gamma 已提交
124 125 126 127 128 129
	}

	public getId(): string {
		return DebugActionsWidget.ID;
	}

130
	private update(): void {
I
isidor 已提交
131
		const state = this.debugService.state;
132
		if (state === debug.State.Disabled || state === debug.State.Inactive) {
E
Erich Gamma 已提交
133 134 135 136
			return this.hide();
		}

		this.actionBar.clear();
I
isidor 已提交
137
		this.actionBar.push(this.getActions(), { icon: true, label: false });
E
Erich Gamma 已提交
138 139 140 141 142 143 144 145 146
		this.show();
	}

	private show(): void {
		if (this.isVisible) {
			return;
		}
		if (!this.isBuilt) {
			this.isBuilt = true;
147
			this.$el.build(builder.withElementById(this.partService.getWorkbenchElementId()).getHTMLElement());
E
Erich Gamma 已提交
148 149 150 151
		}

		this.isVisible = true;
		this.$el.show();
152
		this.setXCoordinate();
E
Erich Gamma 已提交
153 154 155 156 157 158 159
	}

	private hide(): void {
		this.isVisible = false;
		this.$el.hide();
	}

I
isidor 已提交
160
	private getActions(): IAction[] {
E
Erich Gamma 已提交
161
		if (!this.actions) {
I
isidor 已提交
162 163 164 165 166 167 168 169 170 171
			this.actions = [];
			this.actions.push(this.instantiationService.createInstance(ContinueAction, ContinueAction.ID, ContinueAction.LABEL));
			this.actions.push(this.instantiationService.createInstance(PauseAction, PauseAction.ID, PauseAction.LABEL));
			this.actions.push(this.instantiationService.createInstance(StopAction, StopAction.ID, StopAction.LABEL));
			this.actions.push(this.instantiationService.createInstance(DisconnectAction, DisconnectAction.ID, DisconnectAction.LABEL));
			this.actions.push(this.instantiationService.createInstance(StepOverAction, StepOverAction.ID, StepOverAction.LABEL));
			this.actions.push(this.instantiationService.createInstance(StepIntoAction, StepIntoAction.ID, StepIntoAction.LABEL));
			this.actions.push(this.instantiationService.createInstance(StepOutAction, StepOutAction.ID, StepOutAction.LABEL));
			this.actions.push(this.instantiationService.createInstance(RestartAction, RestartAction.ID, RestartAction.LABEL));
			this.actions.push(this.instantiationService.createInstance(StepBackAction, StepBackAction.ID, StepBackAction.LABEL));
E
Erich Gamma 已提交
172 173 174 175
			this.actions.forEach(a => {
				this.toDispose.push(a);
			});
		}
176

I
isidor 已提交
177
		const state = this.debugService.state;
178
		const process = this.debugService.getViewModel().focusedProcess;
I
isidor 已提交
179
		const attached = process && !strings.equalsIgnoreCase(process.session.configuration.type, 'extensionHost') && process.session.requestType === debug.SessionRequestType.ATTACH;
E
Erich Gamma 已提交
180

I
isidor 已提交
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
		return this.actions.filter(a => {
			if (a.id === ContinueAction.ID) {
				return state !== debug.State.Running;
			}
			if (a.id === PauseAction.ID) {
				return state === debug.State.Running;
			}
			if (a.id === StepBackAction.ID) {
				return process && process.session.configuration.capabilities.supportsStepBack;
			}
			if (a.id === DisconnectAction.ID) {
				return attached;
			}
			if (a.id === StopAction.ID) {
				return !attached;
196 197
			}

I
isidor 已提交
198 199
			return true;
		}).sort((first, second) => first.weight - second.weight);
E
Erich Gamma 已提交
200 201 202
	}

	public dispose(): void {
J
Joao Moreno 已提交
203
		this.toDispose = lifecycle.dispose(this.toDispose);
E
Erich Gamma 已提交
204 205 206 207 208 209 210

		if (this.$el) {
			this.$el.destroy();
			delete this.$el;
		}
	}
}