From 21df88c9b47c8d4cee2a202f4f0b7dd46bbc4140 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 23 May 2024 01:26:51 +0200 Subject: [PATCH] more polish (#213267) * more polish * refactor actions * fix hover --- .../browser/media/userDataProfilesEditor.css | 4 +- .../browser/userDataProfilesEditor.ts | 71 ++++++++++--------- .../browser/userDataProfilesEditorModel.ts | 57 ++++++++++----- 3 files changed, 81 insertions(+), 51 deletions(-) diff --git a/src/vs/workbench/contrib/userDataProfile/browser/media/userDataProfilesEditor.css b/src/vs/workbench/contrib/userDataProfile/browser/media/userDataProfilesEditor.css index c0674e54314..0233dfd5309 100644 --- a/src/vs/workbench/contrib/userDataProfile/browser/media/userDataProfilesEditor.css +++ b/src/vs/workbench/contrib/userDataProfile/browser/media/userDataProfilesEditor.css @@ -6,7 +6,7 @@ .profiles-editor { height: 100%; overflow: hidden; - max-width: 1200px; + max-width: 1000px; margin: 20px auto 0px auto; } @@ -159,7 +159,7 @@ } .profiles-editor .contents-container .profile-tree-item-container.new-profile-resource-type-container > .profile-resource-type-label-container { - width: 250px; + width: 150px; } .profiles-editor .contents-container .profile-tree-item-container.new-profile-resource-type-container > .profile-select-container { diff --git a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfilesEditor.ts b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfilesEditor.ts index e14f9af5973..4c294d03ac2 100644 --- a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfilesEditor.ts +++ b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfilesEditor.ts @@ -53,11 +53,12 @@ import { API_OPEN_EDITOR_COMMAND_ID } from 'vs/workbench/browser/parts/editor/ed import { SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { DEFAULT_LABELS_CONTAINER, IResourceLabel, ResourceLabels } from 'vs/workbench/browser/labels'; import { IHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; -import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; -import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { IDialogService, IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { AbstractUserDataProfileElement, IProfileElement, NewProfileElement, UserDataProfileElement, UserDataProfilesEditorModel } from 'vs/workbench/contrib/userDataProfile/browser/userDataProfilesEditorModel'; import { Codicon } from 'vs/base/common/codicons'; +import { WorkbenchToolBar } from 'vs/platform/actions/browser/toolbar'; +import { createInstantHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; export const profilesSashBorder = registerColor('profiles.sashBorder', { dark: PANEL_BORDER, light: PANEL_BORDER, hcDark: PANEL_BORDER, hcLight: PANEL_BORDER }, localize('profilesSashBorder', "The color of the Profiles editor splitview sash border.")); @@ -80,6 +81,7 @@ export class UserDataProfilesEditor extends EditorPane implements IUserDataProfi @IStorageService storageService: IStorageService, @IUserDataProfileManagementService private readonly userDataProfileManagementService: IUserDataProfileManagementService, @IQuickInputService private readonly quickInputService: IQuickInputService, + @IDialogService private readonly dialogService: IDialogService, @IFileDialogService private readonly fileDialogService: IFileDialogService, @IContextMenuService private readonly contextMenuService: IContextMenuService, @IInstantiationService private readonly instantiationService: IInstantiationService, @@ -115,7 +117,7 @@ export class UserDataProfilesEditor extends EditorPane implements IUserDataProfi onDidChange: Event.None, element: sidebarView, minimumSize: 175, - maximumSize: 300, + maximumSize: 350, layout: (width, _, height) => { sidebarView.style.width = `${width}px`; if (height && this.profilesTree) { @@ -123,7 +125,7 @@ export class UserDataProfilesEditor extends EditorPane implements IUserDataProfi this.profilesTree.layout(height - 38, width); } } - }, 250, undefined, true); + }, 300, undefined, true); this.splitView.addView({ onDidChange: Event.None, element: contentsView, @@ -192,7 +194,7 @@ export class UserDataProfilesEditor extends EditorPane implements IUserDataProfi if (this.templates.length) { actions.push(new SubmenuAction('from.template', localize('from template', "From Template"), this.templates.map(template => new Action(`template:${template.url}`, template.name, undefined, true, async () => { - this.model?.createNewProfile(URI.parse(template.url)); + this.createNewProfile(URI.parse(template.url)); })))); actions.push(new Separator()); } @@ -206,12 +208,7 @@ export class UserDataProfilesEditor extends EditorPane implements IUserDataProfi ...defaultButtonStyles })); button.label = `$(add) ${localize('newProfile', "New Profile")}`; - this._register(button.onDidClick(e => { - if (this.model) { - const element = this.model.createNewProfile(); - this.updateProfilesTree(element); - } - })); + this._register(button.onDidClick(e => this.createNewProfile())); } private registerListeners(): void { @@ -227,7 +224,7 @@ export class UserDataProfilesEditor extends EditorPane implements IUserDataProfi if (e.element instanceof AbstractUserDataProfileElement) { this.contextMenuService.showContextMenu({ getAnchor: () => e.anchor, - getActions: () => e.element instanceof AbstractUserDataProfileElement ? e.element.secondaryActions : [], + getActions: () => e.element instanceof AbstractUserDataProfileElement ? e.element.contextMenuActions.slice(0) : [], getActionsContext: () => e.element }); } @@ -263,13 +260,28 @@ export class UserDataProfilesEditor extends EditorPane implements IUserDataProfi } const url = selectedItem.label === quickPick.value ? URI.parse(quickPick.value) : await this.getProfileUriFromFileSystem(); if (url) { - this.model?.createNewProfile(url); + this.createNewProfile(url); } })); disposables.add(quickPick.onDidHide(() => disposables.dispose())); quickPick.show(); } + private async createNewProfile(copyFrom?: URI | IUserDataProfile): Promise { + if (this.model?.profiles.some(p => p instanceof NewProfileElement)) { + const result = await this.dialogService.confirm({ + type: 'info', + message: localize('new profile exists', "A new profile is already being created. Do you want to discard it and create a new one?"), + primaryButton: localize('discard', "Discard & Create"), + cancelButton: localize('cancel', "Cancel") + }); + if (!result.confirmed) { + return; + } + this.model.revert(); + } + this.model?.createNewProfile(copyFrom); + } private async getProfileUriFromFileSystem(): Promise { const profileLocation = await this.fileDialogService.showOpenDialog({ @@ -398,7 +410,7 @@ class ProfileTreeElementRenderer implements ITreeRenderer 0) { - this.actionbar.push(profileElement.secondaryActions.filter(a => !(a instanceof Separator)), { icon: true, label: false }); - } + this.toolbar.setActions(profileElement.titleActions[0].slice(0), profileElement.titleActions[1].slice(0)); this.nameInput.focus(); if (profileElement instanceof NewProfileElement) { @@ -806,7 +819,6 @@ interface INewProfileResourceTemplateData extends IProfileResourceTemplateData { readonly label: HTMLElement; readonly selectContainer: HTMLElement; readonly selectBox: SelectBox; - readonly description: HTMLElement; } interface IProfileResourceChildTreeItemTemplateData extends IProfileResourceTemplateData { @@ -907,12 +919,11 @@ class NewProfileResourceTreeRenderer extends AbstractProfileResourceTreeRenderer const container = append(parent, $('.profile-tree-item-container.new-profile-resource-type-container')); const labelContainer = append(container, $('.profile-resource-type-label-container')); const label = append(labelContainer, $('span.profile-resource-type-label')); - const description = append(labelContainer, $('span.profile-resource-type-description', undefined, localize('use default', "Use Default Profile"))); const selectBox = this._register(this.instantiationService.createInstance(SelectBox, [ - { text: localize('copy', "Copy"), description: localize('copy from selected', "Copy from selected") }, { text: localize('empty', "Empty") }, - { text: localize('exclude', "Exclude"), description: localize('use default', "Use Default Profile") } + { text: localize('copy', "Copy") }, + { text: localize('default', "Use Default Profile") } ], 0, this.contextViewService, @@ -924,7 +935,7 @@ class NewProfileResourceTreeRenderer extends AbstractProfileResourceTreeRenderer const selectContainer = append(container, $('.profile-select-container')); selectBox.render(selectContainer); - return { label, selectContainer, selectBox, description, disposables, elementDisposables: disposables.add(new DisposableStore()) }; + return { label, selectContainer, selectBox, disposables, elementDisposables: disposables.add(new DisposableStore()) }; } renderElement({ element: profileResourceTreeElement }: ITreeNode, index: number, templateData: INewProfileResourceTemplateData, height: number | undefined): void { @@ -937,16 +948,10 @@ class NewProfileResourceTreeRenderer extends AbstractProfileResourceTreeRenderer throw new Error('NewProfileResourceTreeRenderer can only profile resoyrce types'); } templateData.label.textContent = this.getResourceTypeTitle(element); - templateData.description.classList.toggle('hide', !root.getFlag(element)); - templateData.selectBox.select(root.getCopyFlag(element) ? 0 : root.getFlag(element) ? 2 : 1); + templateData.selectBox.select(root.getCopyFlag(element) ? 1 : root.getFlag(element) ? 2 : 0); templateData.elementDisposables.add(templateData.selectBox.onDidSelect(option => { root.setFlag(element, option.index === 2); - root.setCopyFlag(element, option.index === 0); - })); - templateData.elementDisposables.add(root.onDidChange(e => { - if (e.flags) { - templateData.description.classList.toggle('hide', !root.getFlag(element)); - } + root.setCopyFlag(element, option.index === 1); })); } } diff --git a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfilesEditorModel.ts b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfilesEditorModel.ts index 63872690376..bde549ecefa 100644 --- a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfilesEditorModel.ts +++ b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfilesEditorModel.ts @@ -177,7 +177,8 @@ export abstract class AbstractUserDataProfileElement extends Disposable { } abstract readonly primaryAction?: Action; - abstract readonly secondaryActions: IAction[]; + abstract readonly titleActions: [IAction[], IAction[]]; + abstract readonly contextMenuActions: IAction[]; } export class UserDataProfileElement extends AbstractUserDataProfileElement implements IProfileElement { @@ -190,7 +191,8 @@ export class UserDataProfileElement extends AbstractUserDataProfileElement imple constructor( private _profile: IUserDataProfile, - readonly secondaryActions: IAction[], + readonly titleActions: [IAction[], IAction[]], + readonly contextMenuActions: IAction[], @IUserDataProfileService private readonly userDataProfileService: IUserDataProfileService, @IUserDataProfileManagementService private readonly userDataProfileManagementService: IUserDataProfileManagementService, @IUserDataProfilesService userDataProfilesService: IUserDataProfilesService, @@ -273,7 +275,8 @@ export class NewProfileElement extends AbstractUserDataProfileElement implements name: string, copyFrom: URI | IUserDataProfile | undefined, readonly primaryAction: Action, - readonly secondaryActions: Action[], + readonly titleActions: [IAction[], IAction[]], + readonly contextMenuActions: Action[], @IFileService private readonly fileService: IFileService, @IUserDataProfileImportExportService private readonly userDataProfileImportExportService: IUserDataProfileImportExportService, @IUserDataProfilesService userDataProfilesService: IUserDataProfilesService, @@ -421,6 +424,7 @@ export class UserDataProfilesEditorModel extends EditorModel { readonly onDidChange = this._onDidChange.event; constructor( + @IUserDataProfileService private readonly userDataProfileService: IUserDataProfileService, @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, @IUserDataProfileManagementService private readonly userDataProfileManagementService: IUserDataProfileManagementService, @IUserDataProfileImportExportService private readonly userDataProfileImportExportService: IUserDataProfileImportExportService, @@ -453,16 +457,37 @@ export class UserDataProfilesEditorModel extends EditorModel { private createProfileElement(profile: IUserDataProfile): [UserDataProfileElement, DisposableStore] { const disposables = new DisposableStore(); - const actions: IAction[] = []; - actions.push(new Action('userDataProfile.copyFromProfile', localize('copyFromProfile', "Save As..."), ThemeIcon.asClassName(Codicon.copy), true, () => this.createNewProfile(profile))); - actions.push(new Action('userDataProfile.export', localize('export', "Export..."), ThemeIcon.asClassName(Codicon.export), true, () => this.exportProfile(profile))); + + const activateAction = disposables.add(new Action('userDataProfile.activate', localize('active', "Activate"), ThemeIcon.asClassName(Codicon.check), true, () => this.userDataProfileManagementService.switchProfile(profile))); + activateAction.checked = this.userDataProfileService.currentProfile.id === profile.id; + disposables.add(this.userDataProfileService.onDidChangeCurrentProfile(() => activateAction.checked = this.userDataProfileService.currentProfile.id === profile.id)); + const copyFromProfileAction = disposables.add(new Action('userDataProfile.copyFromProfile', localize('copyFromProfile', "Save As..."), ThemeIcon.asClassName(Codicon.copy), true, () => this.createNewProfile(profile))); + const exportAction = disposables.add(new Action('userDataProfile.export', localize('export', "Export..."), ThemeIcon.asClassName(Codicon.export), true, () => this.exportProfile(profile))); + const deleteAction = disposables.add(new Action('userDataProfile.delete', localize('delete', "Delete"), ThemeIcon.asClassName(Codicon.trash), true, () => this.removeProfile(profile))); + + const titlePrimaryActions: IAction[] = []; + titlePrimaryActions.push(activateAction); + titlePrimaryActions.push(exportAction); if (!profile.isDefault) { - actions.push(new Separator()); - actions.push(new Action('userDataProfile.delete', localize('delete', "Delete"), ThemeIcon.asClassName(Codicon.trash), true, () => this.removeProfile(profile))); + titlePrimaryActions.push(deleteAction); + } + + const titleSecondaryActions: IAction[] = []; + titleSecondaryActions.push(copyFromProfileAction); + + const secondaryActions: IAction[] = []; + secondaryActions.push(activateAction); + secondaryActions.push(new Separator()); + secondaryActions.push(copyFromProfileAction); + secondaryActions.push(exportAction); + if (!profile.isDefault) { + secondaryActions.push(new Separator()); + secondaryActions.push(deleteAction); } const profileElement = disposables.add(this.instantiationService.createInstance(UserDataProfileElement, profile, - actions + [titlePrimaryActions, titleSecondaryActions], + secondaryActions, )); return [profileElement, disposables]; } @@ -470,16 +495,16 @@ export class UserDataProfilesEditorModel extends EditorModel { createNewProfile(copyFrom?: URI | IUserDataProfile): IProfileElement { if (!this.newProfileElement) { const disposables = new DisposableStore(); + const discardAction = disposables.add(new Action('userDataProfile.discard', localize('discard', "Discard"), ThemeIcon.asClassName(Codicon.close), true, () => { + this.removeNewProfile(); + this._onDidChange.fire(undefined); + })); this.newProfileElement = disposables.add(this.instantiationService.createInstance(NewProfileElement, localize('untitled', "Untitled"), copyFrom, - new Action('userDataProfile.create', localize('create', "Create & Apply"), undefined, true, () => this.saveNewProfile()), - [ - new Action('userDataProfile.discard', localize('discard', "Discard"), ThemeIcon.asClassName(Codicon.close), true, () => { - this.removeNewProfile(); - this._onDidChange.fire(undefined); - }) - ] + disposables.add(new Action('userDataProfile.create', localize('create', "Create & Apply"), undefined, true, () => this.saveNewProfile())), + [[discardAction], []], + [discardAction], )); this._profiles.push([this.newProfileElement, disposables]); this._onDidChange.fire(this.newProfileElement);