Skip to content

Commit 2542be3

Browse files
committed
Implement Activity Bar Top/Bottom theming support
1 parent 71d10c2 commit 2542be3

File tree

9 files changed

+262
-295
lines changed

9 files changed

+262
-295
lines changed

src/vs/platform/update/electron-main/updateService.darwin.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,13 @@ export class DarwinUpdateService extends AbstractUpdateService implements IRelau
2727
@memoize private get onRawError(): Event<string> { return Event.fromNodeEventEmitter(electron.autoUpdater, 'error', (_, message) => message); }
2828
@memoize private get onRawUpdateNotAvailable(): Event<void> { return Event.fromNodeEventEmitter<void>(electron.autoUpdater, 'update-not-available'); }
2929
@memoize private get onRawUpdateAvailable(): Event<void> { return Event.fromNodeEventEmitter(electron.autoUpdater, 'update-available'); }
30-
@memoize private get onRawUpdateDownloaded(): Event<IUpdate> { return Event.fromNodeEventEmitter(electron.autoUpdater, 'update-downloaded', (_, version: string, productVersion: string, timestamp: number) => ({ version, productVersion, timestamp })); }
30+
@memoize private get onRawUpdateDownloaded(): Event<IUpdate> {
31+
return Event.fromNodeEventEmitter(electron.autoUpdater, 'update-downloaded', (_, version: string, productVersion: string, releaseDate: Date | number) => ({
32+
version,
33+
productVersion,
34+
timestamp: releaseDate instanceof Date ? releaseDate.getTime() || undefined : releaseDate
35+
}));
36+
}
3137

3238
constructor(
3339
@ILifecycleMainService lifecycleMainService: ILifecycleMainService,

src/vs/workbench/browser/parts/activitybar/activitybarPart.ts

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -606,24 +606,7 @@ registerThemingParticipant((theme, collector) => {
606606
`);
607607
}
608608

609-
// Top/Bottom activity bar hover colors
610-
const activityBarTopHoverForeground = theme.getColor(ACTIVITY_BAR_TOP_HOVER_FOREGROUND);
611-
if (activityBarTopHoverForeground) {
612-
collector.addRule(`
613-
.monaco-workbench .part.sidebar .composite-bar-container .action-item:hover .action-label.codicon {
614-
color: ${activityBarTopHoverForeground} !important;
615-
}
616-
`);
617-
}
618609

619-
const activityBarTopHoverBackground = theme.getColor(ACTIVITY_BAR_TOP_HOVER_BACKGROUND);
620-
if (activityBarTopHoverBackground) {
621-
collector.addRule(`
622-
.monaco-workbench .part.sidebar .composite-bar-container .action-item:hover {
623-
background-color: ${activityBarTopHoverBackground};
624-
}
625-
`);
626-
}
627610

628611
// Styling with Outline color (e.g. high contrast theme)
629612
const outline = theme.getColor(activeContrastBorder);

src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart.ts

Lines changed: 160 additions & 256 deletions
Large diffs are not rendered by default.

src/vs/workbench/browser/parts/auxiliarybar/media/auxiliaryBarPart.css

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@
4040

4141
.monaco-workbench .part.auxiliarybar > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item:focus,
4242
.monaco-workbench .part.auxiliarybar > .header-or-footer > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item:focus {
43-
outline: 0 !important; /* activity bar indicates focus custom */
43+
outline: 0 !important;
44+
/* activity bar indicates focus custom */
4445
}
4546

4647
.monaco-workbench .part.auxiliarybar > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item .action-label,
@@ -58,7 +59,8 @@
5859
.monaco-workbench .part.auxiliarybar > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item .action-label::before,
5960
.monaco-workbench .part.auxiliarybar > .header-or-footer > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item .action-label::before {
6061
position: absolute;
61-
left: 5px; /* place icon in center */
62+
left: 5px;
63+
/* place icon in center */
6264
}
6365

6466
.monaco-workbench .part.auxiliarybar > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item.checked:not(:focus) .active-item-indicator:before,
@@ -83,7 +85,7 @@
8385

8486
.monaco-workbench .part.auxiliarybar > .header-or-footer > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item:hover .action-label,
8587
.monaco-workbench .part.auxiliarybar > .header-or-footer > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item:focus .action-label {
86-
color: var(--vscode-activityBarTop-foreground) !important;
88+
color: var(--vscode-activityBarTop-foreground);
8789
}
8890

8991
.monaco-workbench .part.auxiliarybar > .header-or-footer > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item.checked .action-label,

src/vs/workbench/browser/parts/sidebar/media/sidebarpart.css

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@
3131
}
3232

3333
.monaco-workbench .viewlet .collapsible.header .actions {
34-
width: 0; /* not using display: none for keyboard a11y reasons */
34+
width: 0;
35+
/* not using display: none for keyboard a11y reasons */
3536
}
3637

3738
.monaco-workbench .viewlet .split-view-view:hover > .header .actions,
@@ -66,7 +67,8 @@
6667

6768
.monaco-workbench .part.sidebar > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item:focus,
6869
.monaco-workbench .part.sidebar > .header-or-footer > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item:focus {
69-
outline: 0 !important; /* activity bar indicates focus custom */
70+
outline: 0 !important;
71+
/* activity bar indicates focus custom */
7072
}
7173

7274
.monaco-workbench .part.sidebar > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item .action-label,
@@ -84,7 +86,8 @@
8486
.monaco-workbench .part.sidebar > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item .action-label::before,
8587
.monaco-workbench .part.sidebar > .header-or-footer > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item .action-label::before {
8688
position: absolute;
87-
left: 5px; /* place icon in center */
89+
left: 5px;
90+
/* place icon in center */
8891
}
8992

9093
.monaco-workbench .part.sidebar > .header-or-footer.header > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item.checked:not(:focus) .active-item-indicator:before,
@@ -103,14 +106,14 @@
103106
.monaco-workbench .part.sidebar > .header-or-footer > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item:focus .action-label,
104107
.monaco-workbench .part.sidebar > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item:hover .action-label,
105108
.monaco-workbench .part.sidebar > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item:focus .action-label {
106-
color: var(--vscode-activityBarTop-foreground) !important;
109+
color: var(--vscode-activityBarTop-foreground);
107110
}
108111

109112
.monaco-workbench .part.sidebar > .header-or-footer > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item:hover .action-label.uri-icon,
110113
.monaco-workbench .part.sidebar > .header-or-footer > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item:focus .action-label.uri-icon,
111114
.monaco-workbench .part.sidebar > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item:hover .action-label.uri-icon,
112115
.monaco-workbench .part.sidebar > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item:focus .action-label.uri-icon {
113-
background-color: var(--vscode-activityBarTop-foreground) !important;
116+
background-color: var(--vscode-activityBarTop-foreground);
114117
}
115118

116119
.monaco-workbench .sidebar.pane-composite-part > .title > .composite-bar-container {

src/vs/workbench/browser/parts/sidebar/sidebarPart.ts

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -371,23 +371,60 @@ export class SidebarPart extends AbstractPaneCompositePart {
371371
}
372372

373373
registerThemingParticipant((theme, collector) => {
374+
const activeForeground = theme.getColor(ACTIVITY_BAR_TOP_FOREGROUND);
375+
if (activeForeground) {
376+
collector.addRule(`
377+
.monaco-workbench .part.sidebar > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item.checked .action-label.codicon,
378+
.monaco-workbench .part.sidebar > .header-or-footer > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item.checked .action-label.codicon {
379+
color: ${activeForeground} !important;
380+
}
381+
.monaco-workbench .part.sidebar > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item.checked .action-label.uri-icon,
382+
.monaco-workbench .part.sidebar > .header-or-footer > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item.checked .action-label.uri-icon {
383+
background-color: ${activeForeground} !important;
384+
}
385+
`);
386+
}
387+
388+
const inactiveForeground = theme.getColor(ACTIVITY_BAR_TOP_INACTIVE_FOREGROUND);
389+
if (inactiveForeground) {
390+
collector.addRule(`
391+
.monaco-workbench .part.sidebar > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item:not(.checked) .action-label.codicon,
392+
.monaco-workbench .part.sidebar > .header-or-footer > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item:not(.checked) .action-label.codicon {
393+
color: ${inactiveForeground} !important;
394+
}
395+
.monaco-workbench .part.sidebar > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item:not(.checked) .action-label.uri-icon,
396+
.monaco-workbench .part.sidebar > .header-or-footer > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item:not(.checked) .action-label.uri-icon {
397+
background-color: ${inactiveForeground} !important;
398+
}
399+
`);
400+
}
401+
374402
const hoverBackground = theme.getColor(ACTIVITY_BAR_TOP_HOVER_BACKGROUND);
375403
if (hoverBackground) {
376404
collector.addRule(`
377-
.monaco-workbench .part.sidebar > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item:hover .action-label:not(.disabled),
378-
.monaco-workbench .part.sidebar > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item:hover .action-label:not(.disabled).uri-icon {
405+
.monaco-workbench .part.sidebar > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item:hover .active-item-indicator,
406+
.monaco-workbench .part.sidebar > .header-or-footer > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item:hover .active-item-indicator {
379407
background-color: ${hoverBackground} !important;
408+
z-index: 0;
380409
}
381410
`);
382411
}
383412

384413
const hoverForeground = theme.getColor(ACTIVITY_BAR_TOP_HOVER_FOREGROUND);
385414
if (hoverForeground) {
386415
collector.addRule(`
387-
.monaco-workbench .part.sidebar > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item:hover .action-label:not(.disabled),
388-
.monaco-workbench .part.sidebar > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item:hover .action-label:not(.disabled).uri-icon {
416+
.monaco-workbench .part.sidebar > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item:hover .action-label.codicon,
417+
.monaco-workbench .part.sidebar > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item.checked:hover .action-label.codicon,
418+
.monaco-workbench .part.sidebar > .header-or-footer > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item:hover .action-label.codicon,
419+
.monaco-workbench .part.sidebar > .header-or-footer > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item.checked:hover .action-label.codicon {
389420
color: ${hoverForeground} !important;
390421
}
422+
.monaco-workbench .part.sidebar > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item:hover .action-label.uri-icon,
423+
.monaco-workbench .part.sidebar > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item.checked:hover .action-label.uri-icon,
424+
.monaco-workbench .part.sidebar > .header-or-footer > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item:hover .action-label.uri-icon,
425+
.monaco-workbench .part.sidebar > .header-or-footer > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item.checked:hover .action-label.uri-icon {
426+
background-color: ${hoverForeground} !important;
427+
}
391428
`);
392429
}
393430
});

src/vs/workbench/contrib/update/browser/media/updateStatusBarEntry.css

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
display: flex;
88
flex-direction: column;
99
padding: 4px 0;
10-
min-width: 300px;
11-
max-width: 400px;
10+
min-width: 310px;
11+
max-width: 410px;
1212
}
1313

1414
/* Header with title and gear icon */

src/vs/workbench/contrib/update/browser/updateStatusBarEntry.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -362,17 +362,23 @@ export class UpdateStatusBarEntryContribution extends Disposable implements IWor
362362
const productVersion = this.productService.version;
363363
if (productVersion) {
364364
const currentVersion = dom.append(details, dom.$('.product-version'));
365-
currentVersion.textContent = nls.localize('updateStatus.currentVersionLabel', "Current Version: {0}", productVersion);
365+
const currentCommitId = this.productService.commit?.substring(0, 7);
366+
currentVersion.textContent = currentCommitId
367+
? nls.localize('updateStatus.currentVersionLabelWithCommit', "Current Version: {0} ({1})", productVersion, currentCommitId)
368+
: nls.localize('updateStatus.currentVersionLabel', "Current Version: {0}", productVersion);
366369
}
367370

368371
const version = update?.productVersion;
369372
if (version) {
370373
const latestVersion = dom.append(details, dom.$('.product-version'));
371-
latestVersion.textContent = nls.localize('updateStatus.latestVersionLabel', "Latest Version: {0}", version);
374+
const updateCommitId = update.version?.substring(0, 7);
375+
latestVersion.textContent = updateCommitId
376+
? nls.localize('updateStatus.latestVersionLabelWithCommit', "Latest Version: {0} ({1})", version, updateCommitId)
377+
: nls.localize('updateStatus.latestVersionLabel', "Latest Version: {0}", version);
372378
}
373379

374380
const releaseDate = update?.timestamp ?? tryParseDate(this.productService.date);
375-
if (releaseDate) {
381+
if (typeof releaseDate === 'number' && releaseDate > 0) {
376382
const releaseDateNode = dom.append(details, dom.$('.product-release-date'));
377383
releaseDateNode.textContent = nls.localize('updateStatus.releasedLabel', "Released {0}", formatDate(releaseDate));
378384
}
@@ -416,11 +422,11 @@ export class UpdateStatusBarEntryContribution extends Disposable implements IWor
416422
* Tries to parse a date string and returns the timestamp or undefined if parsing fails.
417423
*/
418424
export function tryParseDate(date: string | undefined): number | undefined {
419-
try {
420-
return date !== undefined ? Date.parse(date) : undefined;
421-
} catch {
425+
if (date === undefined) {
422426
return undefined;
423427
}
428+
const parsed = Date.parse(date);
429+
return isNaN(parsed) ? undefined : parsed;
424430
}
425431

426432
/**

src/vs/workbench/contrib/update/test/browser/updateStatusBarEntry.test.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import assert from 'assert';
77
import * as sinon from 'sinon';
88
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';
99
import { Downloading, StateType } from '../../../../../platform/update/common/update.js';
10-
import { computeDownloadSpeed, computeDownloadTimeRemaining, formatBytes, formatTimeRemaining } from '../../browser/updateStatusBarEntry.js';
10+
import { computeDownloadSpeed, computeDownloadTimeRemaining, formatBytes, formatDate, formatTimeRemaining, tryParseDate } from '../../browser/updateStatusBarEntry.js';
1111

1212
suite('UpdateStatusBarEntry', () => {
1313
ensureNoDisposablesAreLeakedInTestSuite();
@@ -129,6 +129,32 @@ suite('UpdateStatusBarEntry', () => {
129129
});
130130
});
131131

132+
suite('tryParseDate', () => {
133+
test('returns undefined for undefined input', () => {
134+
assert.strictEqual(tryParseDate(undefined), undefined);
135+
});
136+
137+
test('returns undefined for invalid date strings', () => {
138+
assert.strictEqual(tryParseDate(''), undefined);
139+
assert.strictEqual(tryParseDate('not-a-date'), undefined);
140+
});
141+
142+
test('parses valid ISO date strings', () => {
143+
const result = tryParseDate('2026-02-06T05:03:03.991Z');
144+
assert.ok(result !== undefined);
145+
assert.strictEqual(typeof result, 'number');
146+
assert.ok(result > 0);
147+
});
148+
});
149+
150+
suite('formatDate', () => {
151+
test('formats a timestamp as a readable date', () => {
152+
const result = formatDate(1705276800000);
153+
assert.ok(result.length > 0);
154+
assert.ok(result.includes('2024'));
155+
});
156+
});
157+
132158
suite('computeDownloadSpeed', () => {
133159
test('returns undefined for invalid or incomplete input', () => {
134160
const now = Date.now();

0 commit comments

Comments
 (0)