Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adjust command decoration font size and margins based on terminal font size #143939

Merged
merged 7 commits into from Feb 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 0 additions & 10 deletions src/vs/workbench/contrib/terminal/browser/media/terminal.css
Expand Up @@ -102,16 +102,6 @@
padding-right: 20px;
}

.monaco-workbench .pane-body.integrated-terminal .terminal-group .monaco-split-view2.horizontal .split-view-view:first-child .xterm .terminal-command-decoration {
margin-left: -17px;
}

.monaco-workbench .editor-instance .xterm .terminal-command-decoration,
.monaco-split-view2.vertical .split-view-view .xterm .terminal-command-decoration,
.monaco-workbench .pane-body.integrated-terminal .terminal-group .monaco-split-view2.horizontal .split-view-view .xterm .terminal-command-decoration {
margin-left: -12px;
}

.monaco-workbench .editor-instance .xterm a:not(.xterm-invalid-link),
.monaco-workbench .pane-body.integrated-terminal .xterm a:not(.xterm-invalid-link) {
/* To support message box sizing */
Expand Down
134 changes: 88 additions & 46 deletions src/vs/workbench/contrib/terminal/browser/xterm/decorationAddon.ts
Expand Up @@ -28,7 +28,14 @@ const enum DecorationSelector {
ErrorColor = 'error',
DefaultColor = 'default',
Codicon = 'codicon',
XtermDecoration = 'xterm-decoration'
XtermDecoration = 'xterm-decoration',
FirstSplitContainer = '.pane-body.integrated-terminal .terminal-group .monaco-split-view2.horizontal .split-view-view:first-child .xterm'
}

const enum DecorationStyles {
DefaultDimension = 16,
meganrogge marked this conversation as resolved.
Show resolved Hide resolved
MarginLeftFirstSplit = -17,
MarginLeft = -12
}

interface IDisposableDecoration { decoration: IDecoration; disposables: IDisposable[]; exitCode?: number }
Expand Down Expand Up @@ -57,28 +64,36 @@ export class DecorationAddon extends Disposable implements ITerminalAddon {
this._register(this._contextMenuService.onDidShowContextMenu(() => this._contextMenuVisible = true));
this._register(this._contextMenuService.onDidHideContextMenu(() => this._contextMenuVisible = false));
this._hoverDelayer = this._register(new Delayer(this._configurationService.getValue('workbench.hover.delay')));

this._configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration(TerminalSettingId.ShellIntegrationDecorationIcon) ||
e.affectsConfiguration(TerminalSettingId.ShellIntegrationDecorationIconSuccess) ||
e.affectsConfiguration(TerminalSettingId.ShellIntegrationDecorationIconError)) {
if (this._placeholderDecoration?.element) {
this._applyStyles(this._placeholderDecoration.element);
}
for (const decoration of this._decorations) {
if (decoration[1].decoration?.element) {
this._applyStyles(decoration[1].decoration.element, decoration[1].exitCode);
}
}
} else if (e.affectsConfiguration(TerminalSettingId.ShellIntegrationDecorationsEnabled)) {
if (!this._configurationService.getValue(TerminalSettingId.ShellIntegrationDecorationsEnabled)) {
this._commandStartedListener?.dispose();
this._commandFinishedListener?.dispose();
this._clearDecorations();
}
this._refreshClasses();
} else if (e.affectsConfiguration(TerminalSettingId.FontSize) || e.affectsConfiguration(TerminalSettingId.LineHeight)) {
this.refreshLayouts();
} else if (e.affectsConfiguration(TerminalSettingId.ShellIntegrationDecorationsEnabled) && !this._configurationService.getValue(TerminalSettingId.ShellIntegrationDecorationsEnabled)) {
this._commandStartedListener?.dispose();
this._commandFinishedListener?.dispose();
this._clearDecorations();
}
});
}

public refreshLayouts(): void {
this._updateLayout(this._placeholderDecoration?.element);
for (const decoration of this._decorations) {
this._updateLayout(decoration[1].decoration.element);
}
}

private _refreshClasses(): void {
this._updateClasses(this._placeholderDecoration?.element);
for (const decoration of this._decorations.values()) {
this._updateClasses(decoration.decoration.element, decoration.exitCode);
}
}

private _clearDecorations(): void {
this._placeholderDecoration?.dispose();
this._placeholderDecoration?.marker.dispose();
Expand Down Expand Up @@ -153,55 +168,82 @@ export class DecorationAddon extends Disposable implements ITerminalAddon {
if (!decoration) {
return undefined;
}
decoration.onRender(target => {
if (target) {
const disposables = command.exitCode === undefined ? [] : [this._createContextMenu(target, command), ...this._createHover(target, command)];
if (!beforeCommandExecution) {
this._decorations.set(decoration.marker.id, { decoration, disposables, exitCode: command.exitCode });
}
decoration.onRender(element => {
if (beforeCommandExecution) {
this._placeholderDecoration = decoration;
} else {
this._decorations.set(decoration.marker.id,
{
decoration,
disposables: command.exitCode === undefined ? [] : [this._createContextMenu(element, command), ...this._createHover(element, command)],
exitCode: command.exitCode
});
}
if (!target.classList.contains(DecorationSelector.Codicon)) {
// must be inlined to override the inlined styles from xterm
target.style.width = '16px';
target.style.height = '16px';
this._applyStyles(target, command.exitCode);

if (!element.classList.contains(DecorationSelector.Codicon)) {
// first render
this._updateLayout(element);
this._updateClasses(element, command.exitCode);
}
});
if (beforeCommandExecution) {
this._placeholderDecoration = decoration;
}
return decoration;
}

private _applyStyles(target: HTMLElement, exitCode?: number): void {
for (const classes of target.classList) {
target.classList.remove(classes);
private _updateLayout(element?: HTMLElement): void {
if (!element) {
return;
}
const fontSize = this._configurationService.inspect(TerminalSettingId.FontSize).value;
const defaultFontSize = this._configurationService.inspect(TerminalSettingId.FontSize).defaultValue;
if (typeof fontSize === 'number' && typeof defaultFontSize === 'number') {
const scalar = (fontSize / defaultFontSize) <= 1 ? (fontSize / defaultFontSize) : 1;

// must be inlined to override the inlined styles from xterm
element.style.width = `${scalar * DecorationStyles.DefaultDimension}px`;
element.style.height = `${scalar * DecorationStyles.DefaultDimension}px`;
element.style.fontSize = `${scalar * DecorationStyles.DefaultDimension}px`;

// the first split terminal in the panel has more room
if (element.closest(DecorationSelector.FirstSplitContainer)) {
element.style.marginLeft = `${scalar * DecorationStyles.MarginLeftFirstSplit}px`;
} else {
element.style.marginLeft = `${scalar * DecorationStyles.MarginLeft}px`;
}
}
}

private _updateClasses(element?: HTMLElement, exitCode?: number): void {
if (!element) {
return;
}
for (const classes of element.classList) {
element.classList.remove(classes);
}
target.classList.add(DecorationSelector.CommandDecoration, DecorationSelector.Codicon, DecorationSelector.XtermDecoration);
element.classList.add(DecorationSelector.CommandDecoration, DecorationSelector.Codicon, DecorationSelector.XtermDecoration);
if (exitCode === undefined) {
target.classList.add(DecorationSelector.DefaultColor);
target.classList.add(`codicon-${this._configurationService.getValue(TerminalSettingId.ShellIntegrationDecorationIcon)}`);
element.classList.add(DecorationSelector.DefaultColor);
element.classList.add(`codicon-${this._configurationService.getValue(TerminalSettingId.ShellIntegrationDecorationIcon)}`);
} else if (exitCode) {
target.classList.add(DecorationSelector.ErrorColor);
target.classList.add(`codicon-${this._configurationService.getValue(TerminalSettingId.ShellIntegrationDecorationIconError)}`);
element.classList.add(DecorationSelector.ErrorColor);
element.classList.add(`codicon-${this._configurationService.getValue(TerminalSettingId.ShellIntegrationDecorationIconError)}`);
} else {
target.classList.add(`codicon-${this._configurationService.getValue(TerminalSettingId.ShellIntegrationDecorationIconSuccess)}`);
element.classList.add(`codicon-${this._configurationService.getValue(TerminalSettingId.ShellIntegrationDecorationIconSuccess)}`);
}
}

private _createContextMenu(target: HTMLElement, command: ITerminalCommand): IDisposable {
private _createContextMenu(element: HTMLElement, command: ITerminalCommand): IDisposable {
// When the xterm Decoration gets disposed of, its element gets removed from the dom
// along with its listeners
return dom.addDisposableListener(target, dom.EventType.CLICK, async () => {
return dom.addDisposableListener(element, dom.EventType.CLICK, async () => {
this._hideHover();
const actions = await this._getCommandActions(command);
this._contextMenuService.showContextMenu({ getAnchor: () => target, getActions: () => actions });
this._contextMenuService.showContextMenu({ getAnchor: () => element, getActions: () => actions });
});
}

private _createHover(target: HTMLElement, command: ITerminalCommand): IDisposable[] {
private _createHover(element: HTMLElement, command: ITerminalCommand): IDisposable[] {
return [
dom.addDisposableListener(target, dom.EventType.MOUSE_ENTER, () => {
dom.addDisposableListener(element, dom.EventType.MOUSE_ENTER, () => {
if (this._contextMenuVisible) {
return;
}
Expand All @@ -217,11 +259,11 @@ export class DecorationAddon extends Disposable implements ITerminalAddon {
} else {
hoverContent += localize('terminalPromptCommandSuccess', 'Command executed {0}', fromNow(command.timestamp, true));
}
this._hoverService.showHover({ content: new MarkdownString(hoverContent), target });
this._hoverService.showHover({ content: new MarkdownString(hoverContent), target: element });
});
}),
dom.addDisposableListener(target, dom.EventType.MOUSE_LEAVE, () => this._hideHover()),
dom.addDisposableListener(target, dom.EventType.MOUSE_OUT, () => this._hideHover())
dom.addDisposableListener(element, dom.EventType.MOUSE_LEAVE, () => this._hideHover()),
dom.addDisposableListener(element, dom.EventType.MOUSE_OUT, () => this._hideHover())
];
}

Expand Down
Expand Up @@ -58,6 +58,7 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal {
// Always on addons
private _commandTrackerAddon: CommandTrackerAddon;
private _shellIntegrationAddon: ShellIntegrationAddon;
private _decorationAddon: DecorationAddon | undefined;

// Optional addons
private _searchAddon?: SearchAddonType;
Expand Down Expand Up @@ -146,6 +147,7 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal {
this.add(this._viewDescriptorService.onDidChangeLocation(({ views }) => {
if (views.some(v => v.id === TERMINAL_VIEW_ID)) {
this._updateTheme();
this._decorationAddon?.refreshLayouts();
meganrogge marked this conversation as resolved.
Show resolved Hide resolved
}
}));

Expand All @@ -160,9 +162,9 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal {
}
}
private _createDecorationAddon(capabilities: ITerminalCapabilityStore): void {
const decorationAddon = this._instantiationService.createInstance(DecorationAddon, capabilities);
decorationAddon.onDidRequestRunCommand(command => this._onDidRequestRunCommand.fire(command));
this.raw.loadAddon(decorationAddon);
this._decorationAddon = this._instantiationService.createInstance(DecorationAddon, capabilities);
this._decorationAddon.onDidRequestRunCommand(command => this._onDidRequestRunCommand.fire(command));
this.raw.loadAddon(this._decorationAddon);
}

attachToElement(container: HTMLElement) {
Expand Down