Skip to content

Commit 578ad35

Browse files
authored
show auto update checkbox in editor (microsoft#199066)
microsoft#194188 show auto update checkbox in editor
1 parent 7c342a3 commit 578ad35

File tree

8 files changed

+174
-44
lines changed

8 files changed

+174
-44
lines changed

src/vs/base/browser/ui/toggle/toggle.css

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,19 @@
4949
background-size: 16px !important;
5050
}
5151

52+
.monaco-action-bar .checkbox-action-item {
53+
display: flex;
54+
align-items: center;
55+
}
56+
57+
.monaco-action-bar .checkbox-action-item > .monaco-custom-toggle.monaco-checkbox {
58+
margin-right: 4px;
59+
}
60+
61+
.monaco-action-bar .checkbox-action-item > .checkbox-label {
62+
font-size: 12px;
63+
}
64+
5265
/* hide check when unchecked */
5366
.monaco-custom-toggle.monaco-checkbox:not(.checked)::before {
5467
visibility: hidden;

src/vs/base/browser/ui/toggle/toggle.ts

Lines changed: 100 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { ThemeIcon } from 'vs/base/common/themables';
1212
import { Emitter, Event } from 'vs/base/common/event';
1313
import { KeyCode } from 'vs/base/common/keyCodes';
1414
import 'vs/css!./toggle';
15-
import { isActiveElement } from 'vs/base/browser/dom';
15+
import { isActiveElement, $, addDisposableListener, EventType } from 'vs/base/browser/dom';
1616

1717
export interface IToggleOpts extends IToggleStyles {
1818
readonly actionClassName?: string;
@@ -219,6 +219,10 @@ export class Toggle extends Widget {
219219
}
220220

221221
export class Checkbox extends Widget {
222+
223+
private readonly _onChange = this._register(new Emitter<boolean>());
224+
readonly onChange: Event<boolean /* via keyboard */> = this._onChange.event;
225+
222226
private checkbox: Toggle;
223227
private styles: ICheckboxStyles;
224228

@@ -235,7 +239,10 @@ export class Checkbox extends Widget {
235239

236240
this.applyStyles();
237241

238-
this._register(this.checkbox.onChange(() => this.applyStyles()));
242+
this._register(this.checkbox.onChange(keyboard => {
243+
this.applyStyles();
244+
this._onChange.fire(keyboard);
245+
}));
239246
}
240247

241248
get checked(): boolean {
@@ -256,9 +263,100 @@ export class Checkbox extends Widget {
256263
return isActiveElement(this.domNode);
257264
}
258265

266+
enable(): void {
267+
this.checkbox.enable();
268+
}
269+
270+
disable(): void {
271+
this.checkbox.disable();
272+
}
273+
259274
protected applyStyles(): void {
260275
this.domNode.style.color = this.styles.checkboxForeground || '';
261276
this.domNode.style.backgroundColor = this.styles.checkboxBackground || '';
262277
this.domNode.style.borderColor = this.styles.checkboxBorder || '';
263278
}
264279
}
280+
281+
export interface ICheckboxActionViewItemOptions extends IActionViewItemOptions {
282+
checkboxStyles: ICheckboxStyles;
283+
}
284+
285+
export class CheckboxActionViewItem extends BaseActionViewItem {
286+
287+
protected readonly toggle: Checkbox;
288+
private cssClass?: string;
289+
290+
constructor(context: any, action: IAction, options: ICheckboxActionViewItemOptions) {
291+
super(context, action, options);
292+
293+
this.toggle = this._register(new Checkbox(this._action.label, !!this._action.checked, options.checkboxStyles));
294+
this._register(this.toggle.onChange(() => this.onChange()));
295+
}
296+
297+
override render(container: HTMLElement): void {
298+
this.element = container;
299+
this.element.classList.add('checkbox-action-item');
300+
this.element.appendChild(this.toggle.domNode);
301+
if ((<IActionViewItemOptions>this.options).label && this._action.label) {
302+
const label = this.element.appendChild($('span.checkbox-label', undefined, this._action.label));
303+
this._register(addDisposableListener(label, EventType.CLICK, (e: MouseEvent) => {
304+
this.toggle.checked = !this.toggle.checked;
305+
e.stopPropagation();
306+
e.preventDefault();
307+
this.onChange();
308+
}));
309+
}
310+
311+
this.updateEnabled();
312+
this.updateClass();
313+
this.updateChecked();
314+
}
315+
316+
private onChange(): void {
317+
this._action.checked = !!this.toggle && this.toggle.checked;
318+
this.actionRunner.run(this._action, this._context);
319+
}
320+
321+
protected override updateEnabled(): void {
322+
if (this.isEnabled()) {
323+
this.toggle.enable();
324+
} else {
325+
this.toggle.disable();
326+
}
327+
if (this.action.enabled) {
328+
this.element?.classList.remove('disabled');
329+
} else {
330+
this.element?.classList.add('disabled');
331+
}
332+
}
333+
334+
protected override updateChecked(): void {
335+
this.toggle.checked = !!this._action.checked;
336+
}
337+
338+
protected override updateClass(): void {
339+
if (this.cssClass) {
340+
this.toggle.domNode.classList.remove(...this.cssClass.split(' '));
341+
}
342+
this.cssClass = this.getClass();
343+
if (this.cssClass) {
344+
this.toggle.domNode.classList.add(...this.cssClass.split(' '));
345+
}
346+
}
347+
348+
override focus(): void {
349+
this.toggle.domNode.tabIndex = 0;
350+
this.toggle.focus();
351+
}
352+
353+
override blur(): void {
354+
this.toggle.domNode.tabIndex = -1;
355+
this.toggle.domNode.blur();
356+
}
357+
358+
override setFocusable(focusable: boolean): void {
359+
this.toggle.domNode.tabIndex = focusable ? 0 : -1;
360+
}
361+
362+
}

src/vs/workbench/contrib/extensions/browser/extensionEditor.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { renderMarkdown } from 'vs/base/browser/markdownRenderer';
88
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
99
import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel';
1010
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
11+
import { CheckboxActionViewItem } from 'vs/base/browser/ui/toggle/toggle';
1112
import { Action, IAction } from 'vs/base/common/actions';
1213
import * as arrays from 'vs/base/common/arrays';
1314
import { Cache, CacheResult } from 'vs/base/common/cache';
@@ -46,7 +47,7 @@ import { INotificationService, Severity } from 'vs/platform/notification/common/
4647
import { IOpenerService } from 'vs/platform/opener/common/opener';
4748
import { IStorageService } from 'vs/platform/storage/common/storage';
4849
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
49-
import { defaultKeybindingLabelStyles } from 'vs/platform/theme/browser/defaultStyles';
50+
import { defaultCheckboxStyles, defaultKeybindingLabelStyles } from 'vs/platform/theme/browser/defaultStyles';
5051
import { buttonForeground, buttonHoverBackground, editorBackground, textLinkActiveForeground, textLinkForeground } from 'vs/platform/theme/common/colorRegistry';
5152
import { IColorTheme, ICssStyleCollector, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
5253
import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane';
@@ -72,7 +73,6 @@ import {
7273
SetLanguageAction,
7374
SetProductIconThemeAction,
7475
ToggleAutoUpdateForExtensionAction,
75-
ToggleAutoUpdatesForPublisherAction,
7676
SwitchToPreReleaseVersionAction, SwitchToReleasedVersionAction,
7777
ToggleSyncExtensionAction,
7878
UninstallAction,
@@ -345,7 +345,7 @@ export class ExtensionEditor extends EditorPane {
345345
this.instantiationService.createInstance(ReloadAction),
346346
this.instantiationService.createInstance(ExtensionStatusLabelAction),
347347
this.instantiationService.createInstance(ActionWithDropDownAction, 'extensions.updateActions', '',
348-
[[this.instantiationService.createInstance(UpdateAction, true)], [this.instantiationService.createInstance(ToggleAutoUpdateForExtensionAction), this.instantiationService.createInstance(ToggleAutoUpdatesForPublisherAction)]]),
348+
[[this.instantiationService.createInstance(UpdateAction, true)], [this.instantiationService.createInstance(ToggleAutoUpdateForExtensionAction, true, [true, 'onlyEnabledExtensions'])]]),
349349
this.instantiationService.createInstance(SetColorThemeAction),
350350
this.instantiationService.createInstance(SetFileIconThemeAction),
351351
this.instantiationService.createInstance(SetProductIconThemeAction),
@@ -369,6 +369,7 @@ export class ExtensionEditor extends EditorPane {
369369
this.instantiationService.createInstance(SwitchToPreReleaseVersionAction, false),
370370
this.instantiationService.createInstance(SwitchToReleasedVersionAction, false),
371371
this.instantiationService.createInstance(ToggleSyncExtensionAction),
372+
this.instantiationService.createInstance(ToggleAutoUpdateForExtensionAction, false, ['onlySelectedExtensions']),
372373
new ExtensionEditorManageExtensionAction(this.scopedContextKeyService || this.contextKeyService, this.instantiationService),
373374
];
374375

@@ -382,6 +383,9 @@ export class ExtensionEditor extends EditorPane {
382383
if (action instanceof ActionWithDropDownAction) {
383384
return new ExtensionActionWithDropdownActionViewItem(action, { icon: true, label: true, menuActionsOrProvider: { getActions: () => action.menuActions }, menuActionClassNames: (action.class || '').split(' ') }, this.contextMenuService);
384385
}
386+
if (action instanceof ToggleAutoUpdateForExtensionAction) {
387+
return new CheckboxActionViewItem(undefined, action, { icon: true, label: true, checkboxStyles: defaultCheckboxStyles });
388+
}
385389
return undefined;
386390
},
387391
focusOnlyEnabledItems: true

src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1335,14 +1335,14 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi
13351335
id: MenuId.ExtensionContext,
13361336
group: UPDATE_ACTIONS_GROUP,
13371337
order: 1,
1338-
when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.not('isBuiltinExtension'), ContextKeyExpr.equals(`config.${AutoUpdateConfigurationKey}`, 'onlySelectedExtensions'),)
1338+
when: ContextKeyExpr.and(ContextKeyExpr.not('inExtensionEditor'), ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.not('isBuiltinExtension'), ContextKeyExpr.equals(`config.${AutoUpdateConfigurationKey}`, 'onlySelectedExtensions'),)
13391339
},
13401340
run: async (accessor: ServicesAccessor, id: string) => {
13411341
const instantiationService = accessor.get(IInstantiationService);
13421342
const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService);
13431343
const extension = extensionWorkbenchService.local.find(e => areSameExtensions(e.identifier, { id }));
13441344
if (extension) {
1345-
const action = instantiationService.createInstance(ToggleAutoUpdateForExtensionAction);
1345+
const action = instantiationService.createInstance(ToggleAutoUpdateForExtensionAction, false, []);
13461346
action.extension = extension;
13471347
return action.run();
13481348
}

src/vs/workbench/contrib/extensions/browser/extensionsActions.ts

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { Emitter, Event } from 'vs/base/common/event';
1212
import * as json from 'vs/base/common/json';
1313
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
1414
import { disposeIfDisposable } from 'vs/base/common/lifecycle';
15-
import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IExtensionsViewPaneContainer, IExtensionContainer, TOGGLE_IGNORE_EXTENSION_ACTION_ID, SELECT_INSTALL_VSIX_EXTENSION_COMMAND_ID, THEME_ACTIONS_GROUP, INSTALL_ACTIONS_GROUP, UPDATE_ACTIONS_GROUP } from 'vs/workbench/contrib/extensions/common/extensions';
15+
import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IExtensionsViewPaneContainer, IExtensionContainer, TOGGLE_IGNORE_EXTENSION_ACTION_ID, SELECT_INSTALL_VSIX_EXTENSION_COMMAND_ID, THEME_ACTIONS_GROUP, INSTALL_ACTIONS_GROUP, UPDATE_ACTIONS_GROUP, AutoUpdateConfigurationKey, AutoUpdateConfigurationValue } from 'vs/workbench/contrib/extensions/common/extensions';
1616
import { ExtensionsConfigurationInitialContent } from 'vs/workbench/contrib/extensions/common/extensionsFileTemplate';
1717
import { IGalleryExtension, IExtensionGalleryService, ILocalExtension, InstallOptions, InstallOperation, TargetPlatformToString, ExtensionManagementErrorCode } from 'vs/platform/extensionManagement/common/extensionManagement';
1818
import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
@@ -826,30 +826,48 @@ export class UpdateAction extends AbstractUpdateAction {
826826
}
827827
}
828828

829-
export class ToggleAutoUpdateForExtensionAction extends AbstractUpdateAction {
829+
export class ToggleAutoUpdateForExtensionAction extends ExtensionAction {
830830

831831
static readonly ID = 'workbench.extensions.action.toggleAutoUpdateForExtension';
832832
static readonly LABEL = localize('enableAutoUpdateLabel', "Auto Update");
833833

834+
private static readonly EnabledClass = `${ExtensionAction.EXTENSION_ACTION_CLASS} auto-update`;
835+
private static readonly DisabledClass = `${ToggleAutoUpdateForExtensionAction.EnabledClass} hide`;
836+
834837
constructor(
835-
@IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService
838+
private readonly enableWhenOutdated: boolean,
839+
private readonly enableWhenAutoUpdateValue: AutoUpdateConfigurationValue[],
840+
@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
841+
@IConfigurationService configurationService: IConfigurationService,
842+
836843
) {
837-
super(ToggleAutoUpdateForExtensionAction.ID, ToggleAutoUpdateForExtensionAction.LABEL, extensionsWorkbenchService);
844+
super(ToggleAutoUpdateForExtensionAction.ID, ToggleAutoUpdateForExtensionAction.LABEL, ToggleAutoUpdateForExtensionAction.DisabledClass);
845+
this._register(configurationService.onDidChangeConfiguration(e => {
846+
if (e.affectsConfiguration(AutoUpdateConfigurationKey)) {
847+
this.update();
848+
}
849+
}));
850+
this.update();
838851
}
839852

840853
override update() {
841854
this.enabled = false;
855+
this.class = ToggleAutoUpdateForExtensionAction.DisabledClass;
842856
if (!this.extension) {
843857
return;
844858
}
845859
if (this.extension.isBuiltin) {
846860
return;
847861
}
848-
if (!this.extensionsWorkbenchService.isAutoUpdateEnabled()) {
862+
if (this.enableWhenOutdated && !this.extension.outdated) {
849863
return;
850864
}
851-
super.update();
852-
this._checked = this.extensionsWorkbenchService.isAutoUpdateEnabledFor(this.extension);
865+
if (!this.enableWhenAutoUpdateValue.includes(this.extensionsWorkbenchService.getAutoUpdateValue())) {
866+
return;
867+
}
868+
this.enabled = true;
869+
this.class = ToggleAutoUpdateForExtensionAction.EnabledClass;
870+
this.checked = this.extensionsWorkbenchService.isAutoUpdateEnabledFor(this.extension);
853871
}
854872

855873
override async run(): Promise<any> {
@@ -868,7 +886,7 @@ export class ToggleAutoUpdateForExtensionAction extends AbstractUpdateAction {
868886
}
869887
}
870888

871-
export class ToggleAutoUpdatesForPublisherAction extends AbstractUpdateAction {
889+
export class ToggleAutoUpdatesForPublisherAction extends ExtensionAction {
872890

873891
static readonly ID = 'workbench.extensions.action.toggleAutoUpdatesForPublisher';
874892
static readonly LABEL = localize('toggleAutoUpdatesForPublisherLabel', "Auto Update (Publisher)");
@@ -878,27 +896,12 @@ export class ToggleAutoUpdatesForPublisherAction extends AbstractUpdateAction {
878896
}
879897

880898
constructor(
881-
@IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService
899+
@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService
882900
) {
883-
super(ToggleAutoUpdatesForPublisherAction.ID, ToggleAutoUpdatesForPublisherAction.LABEL, extensionsWorkbenchService);
901+
super(ToggleAutoUpdatesForPublisherAction.ID, ToggleAutoUpdatesForPublisherAction.LABEL);
884902
}
885903

886-
override update() {
887-
if (!this.extension) {
888-
return;
889-
}
890-
if (this.extension.isBuiltin) {
891-
this.enabled = false;
892-
return;
893-
}
894-
if (this.extensionsWorkbenchService.getAutoUpdateValue() !== 'onlySelectedExtensions') {
895-
this.enabled = false;
896-
return;
897-
}
898-
super.update();
899-
this._checked = this.extensionsWorkbenchService.isAutoUpdateEnabledFor(this.extension.publisher);
900-
this.label = ToggleAutoUpdatesForPublisherAction.getLabel(this.extension);
901-
}
904+
override update() { }
902905

903906
override async run(): Promise<any> {
904907
if (!this.extension) {

src/vs/workbench/contrib/extensions/browser/extensionsList.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
1313
import { IPagedRenderer } from 'vs/base/browser/ui/list/listPaging';
1414
import { Event } from 'vs/base/common/event';
1515
import { IExtension, ExtensionContainers, ExtensionState, IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions';
16-
import { ManageExtensionAction, ReloadAction, ExtensionStatusLabelAction, RemoteInstallAction, ExtensionStatusAction, LocalInstallAction, ActionWithDropDownAction, InstallDropdownAction, InstallingLabelAction, ExtensionActionWithDropdownActionViewItem, ExtensionDropDownAction, WebInstallAction, SwitchToPreReleaseVersionAction, SwitchToReleasedVersionAction, MigrateDeprecatedExtensionAction, SetLanguageAction, ClearLanguageAction, UpdateAction, ToggleAutoUpdateForExtensionAction, ToggleAutoUpdatesForPublisherAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions';
16+
import { ManageExtensionAction, ReloadAction, ExtensionStatusLabelAction, RemoteInstallAction, ExtensionStatusAction, LocalInstallAction, ActionWithDropDownAction, InstallDropdownAction, InstallingLabelAction, ExtensionActionWithDropdownActionViewItem, ExtensionDropDownAction, WebInstallAction, SwitchToPreReleaseVersionAction, SwitchToReleasedVersionAction, MigrateDeprecatedExtensionAction, SetLanguageAction, ClearLanguageAction, UpdateAction, ToggleAutoUpdateForExtensionAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions';
1717
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
1818
import { RatingsWidget, InstallCountWidget, RecommendationWidget, RemoteBadgeWidget, ExtensionPackCountWidget as ExtensionPackBadgeWidget, SyncIgnoredWidget, ExtensionHoverWidget, ExtensionActivationStatusWidget, PreReleaseBookmarkWidget, extensionVerifiedPublisherIconColor, VerifiedPublisherWidget } from 'vs/workbench/contrib/extensions/browser/extensionsWidgets';
1919
import { IExtensionService, toExtension } from 'vs/workbench/services/extensions/common/extensions';
@@ -119,7 +119,7 @@ export class Renderer implements IPagedRenderer<IExtension, ITemplateData> {
119119
this.instantiationService.createInstance(MigrateDeprecatedExtensionAction, true),
120120
this.instantiationService.createInstance(ReloadAction),
121121
this.instantiationService.createInstance(ActionWithDropDownAction, 'extensions.updateActions', '',
122-
[[this.instantiationService.createInstance(UpdateAction, false)], [this.instantiationService.createInstance(ToggleAutoUpdateForExtensionAction), this.instantiationService.createInstance(ToggleAutoUpdatesForPublisherAction)]]),
122+
[[this.instantiationService.createInstance(UpdateAction, false)], [this.instantiationService.createInstance(ToggleAutoUpdateForExtensionAction, true, [true, 'onlyEnabledExtensions'])]]),
123123
this.instantiationService.createInstance(InstallDropdownAction),
124124
this.instantiationService.createInstance(InstallingLabelAction),
125125
this.instantiationService.createInstance(SetLanguageAction),

0 commit comments

Comments
 (0)