Skip to content

Commit f1fb100

Browse files
authored
adjust command decoration font size and margins based on terminal font size (microsoft#143939)
1 parent 8a9180f commit f1fb100

File tree

3 files changed

+93
-59
lines changed

3 files changed

+93
-59
lines changed

src/vs/workbench/contrib/terminal/browser/media/terminal.css

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -102,16 +102,6 @@
102102
padding-right: 20px;
103103
}
104104

105-
.monaco-workbench .pane-body.integrated-terminal .terminal-group .monaco-split-view2.horizontal .split-view-view:first-child .xterm .terminal-command-decoration {
106-
margin-left: -17px;
107-
}
108-
109-
.monaco-workbench .editor-instance .xterm .terminal-command-decoration,
110-
.monaco-split-view2.vertical .split-view-view .xterm .terminal-command-decoration,
111-
.monaco-workbench .pane-body.integrated-terminal .terminal-group .monaco-split-view2.horizontal .split-view-view .xterm .terminal-command-decoration {
112-
margin-left: -12px;
113-
}
114-
115105
.monaco-workbench .editor-instance .xterm a:not(.xterm-invalid-link),
116106
.monaco-workbench .pane-body.integrated-terminal .xterm a:not(.xterm-invalid-link) {
117107
/* To support message box sizing */

src/vs/workbench/contrib/terminal/browser/xterm/decorationAddon.ts

Lines changed: 88 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,14 @@ const enum DecorationSelector {
2828
ErrorColor = 'error',
2929
DefaultColor = 'default',
3030
Codicon = 'codicon',
31-
XtermDecoration = 'xterm-decoration'
31+
XtermDecoration = 'xterm-decoration',
32+
FirstSplitContainer = '.pane-body.integrated-terminal .terminal-group .monaco-split-view2.horizontal .split-view-view:first-child .xterm'
33+
}
34+
35+
const enum DecorationStyles {
36+
DefaultDimension = 16,
37+
MarginLeftFirstSplit = -17,
38+
MarginLeft = -12
3239
}
3340

3441
interface IDisposableDecoration { decoration: IDecoration; disposables: IDisposable[]; exitCode?: number }
@@ -57,28 +64,36 @@ export class DecorationAddon extends Disposable implements ITerminalAddon {
5764
this._register(this._contextMenuService.onDidShowContextMenu(() => this._contextMenuVisible = true));
5865
this._register(this._contextMenuService.onDidHideContextMenu(() => this._contextMenuVisible = false));
5966
this._hoverDelayer = this._register(new Delayer(this._configurationService.getValue('workbench.hover.delay')));
67+
6068
this._configurationService.onDidChangeConfiguration(e => {
6169
if (e.affectsConfiguration(TerminalSettingId.ShellIntegrationDecorationIcon) ||
6270
e.affectsConfiguration(TerminalSettingId.ShellIntegrationDecorationIconSuccess) ||
6371
e.affectsConfiguration(TerminalSettingId.ShellIntegrationDecorationIconError)) {
64-
if (this._placeholderDecoration?.element) {
65-
this._applyStyles(this._placeholderDecoration.element);
66-
}
67-
for (const decoration of this._decorations) {
68-
if (decoration[1].decoration?.element) {
69-
this._applyStyles(decoration[1].decoration.element, decoration[1].exitCode);
70-
}
71-
}
72-
} else if (e.affectsConfiguration(TerminalSettingId.ShellIntegrationDecorationsEnabled)) {
73-
if (!this._configurationService.getValue(TerminalSettingId.ShellIntegrationDecorationsEnabled)) {
74-
this._commandStartedListener?.dispose();
75-
this._commandFinishedListener?.dispose();
76-
this._clearDecorations();
77-
}
72+
this._refreshClasses();
73+
} else if (e.affectsConfiguration(TerminalSettingId.FontSize) || e.affectsConfiguration(TerminalSettingId.LineHeight)) {
74+
this.refreshLayouts();
75+
} else if (e.affectsConfiguration(TerminalSettingId.ShellIntegrationDecorationsEnabled) && !this._configurationService.getValue(TerminalSettingId.ShellIntegrationDecorationsEnabled)) {
76+
this._commandStartedListener?.dispose();
77+
this._commandFinishedListener?.dispose();
78+
this._clearDecorations();
7879
}
7980
});
8081
}
8182

83+
public refreshLayouts(): void {
84+
this._updateLayout(this._placeholderDecoration?.element);
85+
for (const decoration of this._decorations) {
86+
this._updateLayout(decoration[1].decoration.element);
87+
}
88+
}
89+
90+
private _refreshClasses(): void {
91+
this._updateClasses(this._placeholderDecoration?.element);
92+
for (const decoration of this._decorations.values()) {
93+
this._updateClasses(decoration.decoration.element, decoration.exitCode);
94+
}
95+
}
96+
8297
private _clearDecorations(): void {
8398
this._placeholderDecoration?.dispose();
8499
this._placeholderDecoration?.marker.dispose();
@@ -153,55 +168,82 @@ export class DecorationAddon extends Disposable implements ITerminalAddon {
153168
if (!decoration) {
154169
return undefined;
155170
}
156-
decoration.onRender(target => {
157-
if (target) {
158-
const disposables = command.exitCode === undefined ? [] : [this._createContextMenu(target, command), ...this._createHover(target, command)];
159-
if (!beforeCommandExecution) {
160-
this._decorations.set(decoration.marker.id, { decoration, disposables, exitCode: command.exitCode });
161-
}
171+
decoration.onRender(element => {
172+
if (beforeCommandExecution) {
173+
this._placeholderDecoration = decoration;
174+
} else {
175+
this._decorations.set(decoration.marker.id,
176+
{
177+
decoration,
178+
disposables: command.exitCode === undefined ? [] : [this._createContextMenu(element, command), ...this._createHover(element, command)],
179+
exitCode: command.exitCode
180+
});
162181
}
163-
if (!target.classList.contains(DecorationSelector.Codicon)) {
164-
// must be inlined to override the inlined styles from xterm
165-
target.style.width = '16px';
166-
target.style.height = '16px';
167-
this._applyStyles(target, command.exitCode);
182+
183+
if (!element.classList.contains(DecorationSelector.Codicon)) {
184+
// first render
185+
this._updateLayout(element);
186+
this._updateClasses(element, command.exitCode);
168187
}
169188
});
170-
if (beforeCommandExecution) {
171-
this._placeholderDecoration = decoration;
172-
}
173189
return decoration;
174190
}
175191

176-
private _applyStyles(target: HTMLElement, exitCode?: number): void {
177-
for (const classes of target.classList) {
178-
target.classList.remove(classes);
192+
private _updateLayout(element?: HTMLElement): void {
193+
if (!element) {
194+
return;
195+
}
196+
const fontSize = this._configurationService.inspect(TerminalSettingId.FontSize).value;
197+
const defaultFontSize = this._configurationService.inspect(TerminalSettingId.FontSize).defaultValue;
198+
if (typeof fontSize === 'number' && typeof defaultFontSize === 'number') {
199+
const scalar = (fontSize / defaultFontSize) <= 1 ? (fontSize / defaultFontSize) : 1;
200+
201+
// must be inlined to override the inlined styles from xterm
202+
element.style.width = `${scalar * DecorationStyles.DefaultDimension}px`;
203+
element.style.height = `${scalar * DecorationStyles.DefaultDimension}px`;
204+
element.style.fontSize = `${scalar * DecorationStyles.DefaultDimension}px`;
205+
206+
// the first split terminal in the panel has more room
207+
if (element.closest(DecorationSelector.FirstSplitContainer)) {
208+
element.style.marginLeft = `${scalar * DecorationStyles.MarginLeftFirstSplit}px`;
209+
} else {
210+
element.style.marginLeft = `${scalar * DecorationStyles.MarginLeft}px`;
211+
}
212+
}
213+
}
214+
215+
private _updateClasses(element?: HTMLElement, exitCode?: number): void {
216+
if (!element) {
217+
return;
218+
}
219+
for (const classes of element.classList) {
220+
element.classList.remove(classes);
179221
}
180-
target.classList.add(DecorationSelector.CommandDecoration, DecorationSelector.Codicon, DecorationSelector.XtermDecoration);
222+
element.classList.add(DecorationSelector.CommandDecoration, DecorationSelector.Codicon, DecorationSelector.XtermDecoration);
181223
if (exitCode === undefined) {
182-
target.classList.add(DecorationSelector.DefaultColor);
183-
target.classList.add(`codicon-${this._configurationService.getValue(TerminalSettingId.ShellIntegrationDecorationIcon)}`);
224+
element.classList.add(DecorationSelector.DefaultColor);
225+
element.classList.add(`codicon-${this._configurationService.getValue(TerminalSettingId.ShellIntegrationDecorationIcon)}`);
184226
} else if (exitCode) {
185-
target.classList.add(DecorationSelector.ErrorColor);
186-
target.classList.add(`codicon-${this._configurationService.getValue(TerminalSettingId.ShellIntegrationDecorationIconError)}`);
227+
element.classList.add(DecorationSelector.ErrorColor);
228+
element.classList.add(`codicon-${this._configurationService.getValue(TerminalSettingId.ShellIntegrationDecorationIconError)}`);
187229
} else {
188-
target.classList.add(`codicon-${this._configurationService.getValue(TerminalSettingId.ShellIntegrationDecorationIconSuccess)}`);
230+
element.classList.add(`codicon-${this._configurationService.getValue(TerminalSettingId.ShellIntegrationDecorationIconSuccess)}`);
189231
}
190232
}
191233

192-
private _createContextMenu(target: HTMLElement, command: ITerminalCommand): IDisposable {
234+
private _createContextMenu(element: HTMLElement, command: ITerminalCommand): IDisposable {
193235
// When the xterm Decoration gets disposed of, its element gets removed from the dom
194236
// along with its listeners
195-
return dom.addDisposableListener(target, dom.EventType.CLICK, async () => {
237+
return dom.addDisposableListener(element, dom.EventType.CLICK, async () => {
196238
this._hideHover();
197239
const actions = await this._getCommandActions(command);
198-
this._contextMenuService.showContextMenu({ getAnchor: () => target, getActions: () => actions });
240+
this._contextMenuService.showContextMenu({ getAnchor: () => element, getActions: () => actions });
199241
});
200242
}
201243

202-
private _createHover(target: HTMLElement, command: ITerminalCommand): IDisposable[] {
244+
private _createHover(element: HTMLElement, command: ITerminalCommand): IDisposable[] {
203245
return [
204-
dom.addDisposableListener(target, dom.EventType.MOUSE_ENTER, () => {
246+
dom.addDisposableListener(element, dom.EventType.MOUSE_ENTER, () => {
205247
if (this._contextMenuVisible) {
206248
return;
207249
}
@@ -217,11 +259,11 @@ export class DecorationAddon extends Disposable implements ITerminalAddon {
217259
} else {
218260
hoverContent += localize('terminalPromptCommandSuccess', 'Command executed {0}', fromNow(command.timestamp, true));
219261
}
220-
this._hoverService.showHover({ content: new MarkdownString(hoverContent), target });
262+
this._hoverService.showHover({ content: new MarkdownString(hoverContent), target: element });
221263
});
222264
}),
223-
dom.addDisposableListener(target, dom.EventType.MOUSE_LEAVE, () => this._hideHover()),
224-
dom.addDisposableListener(target, dom.EventType.MOUSE_OUT, () => this._hideHover())
265+
dom.addDisposableListener(element, dom.EventType.MOUSE_LEAVE, () => this._hideHover()),
266+
dom.addDisposableListener(element, dom.EventType.MOUSE_OUT, () => this._hideHover())
225267
];
226268
}
227269

src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal {
5858
// Always on addons
5959
private _commandTrackerAddon: CommandTrackerAddon;
6060
private _shellIntegrationAddon: ShellIntegrationAddon;
61+
private _decorationAddon: DecorationAddon | undefined;
6162

6263
// Optional addons
6364
private _searchAddon?: SearchAddonType;
@@ -146,6 +147,7 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal {
146147
this.add(this._viewDescriptorService.onDidChangeLocation(({ views }) => {
147148
if (views.some(v => v.id === TERMINAL_VIEW_ID)) {
148149
this._updateTheme();
150+
this._decorationAddon?.refreshLayouts();
149151
}
150152
}));
151153

@@ -160,9 +162,9 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal {
160162
}
161163
}
162164
private _createDecorationAddon(capabilities: ITerminalCapabilityStore): void {
163-
const decorationAddon = this._instantiationService.createInstance(DecorationAddon, capabilities);
164-
decorationAddon.onDidRequestRunCommand(command => this._onDidRequestRunCommand.fire(command));
165-
this.raw.loadAddon(decorationAddon);
165+
this._decorationAddon = this._instantiationService.createInstance(DecorationAddon, capabilities);
166+
this._decorationAddon.onDidRequestRunCommand(command => this._onDidRequestRunCommand.fire(command));
167+
this.raw.loadAddon(this._decorationAddon);
166168
}
167169

168170
attachToElement(container: HTMLElement) {

0 commit comments

Comments
 (0)