Skip to content

Commit 33b1980

Browse files
authored
use task icon/color in quick pick (microsoft#151990)
1 parent 9e2b6e0 commit 33b1980

File tree

5 files changed

+121
-17
lines changed

5 files changed

+121
-17
lines changed

src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ import { IViewsService, IViewDescriptorService } from 'vs/workbench/common/views
7979
import { isWorkspaceFolder, ITaskQuickPickEntry, QUICKOPEN_DETAIL_CONFIG, TaskQuickPick, QUICKOPEN_SKIP_CONFIG, configureTaskIcon } from 'vs/workbench/contrib/tasks/browser/taskQuickPick';
8080
import { ILogService } from 'vs/platform/log/common/log';
8181
import { once } from 'vs/base/common/functional';
82-
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
82+
import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService';
8383
import { IWorkspaceTrustManagementService, IWorkspaceTrustRequestService } from 'vs/platform/workspace/common/workspaceTrust';
8484
import { VirtualWorkspaceContext } from 'vs/workbench/common/contextkeys';
8585
import { Schemas } from 'vs/base/common/network';
@@ -94,7 +94,7 @@ export namespace ConfigureTaskAction {
9494
export const TEXT = nls.localize('ConfigureTaskRunnerAction.label', "Configure Task");
9595
}
9696

97-
type TaskQuickPickEntryType = (IQuickPickItem & { task: Task }) | (IQuickPickItem & { folder: IWorkspaceFolder }) | (IQuickPickItem & { settingType: string });
97+
export type TaskQuickPickEntryType = (IQuickPickItem & { task: Task }) | (IQuickPickItem & { folder: IWorkspaceFolder }) | (IQuickPickItem & { settingType: string });
9898

9999
class ProblemReporter implements TaskConfig.IProblemReporter {
100100

@@ -256,7 +256,8 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
256256
@IViewDescriptorService private readonly _viewDescriptorService: IViewDescriptorService,
257257
@IWorkspaceTrustRequestService private readonly _workspaceTrustRequestService: IWorkspaceTrustRequestService,
258258
@IWorkspaceTrustManagementService private readonly _workspaceTrustManagementService: IWorkspaceTrustManagementService,
259-
@ILogService private readonly _logService: ILogService
259+
@ILogService private readonly _logService: ILogService,
260+
@IThemeService private readonly _themeService: IThemeService
260261
) {
261262
super();
262263

@@ -2519,7 +2520,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
25192520
}
25202521

25212522
private async _showTwoLevelQuickPick(placeHolder: string, defaultEntry?: ITaskQuickPickEntry) {
2522-
return TaskQuickPick.show(this, this._configurationService, this._quickInputService, this._notificationService, this._dialogService, placeHolder, defaultEntry);
2523+
return TaskQuickPick.show(this, this._configurationService, this._quickInputService, this._notificationService, this._dialogService, this._themeService, placeHolder, defaultEntry);
25232524
}
25242525

25252526
private async _showQuickPick(tasks: Promise<Task[]> | Task[], placeHolder: string, defaultEntry?: ITaskQuickPickEntry, group: boolean = false, sort: boolean = false, selectedEntry?: ITaskQuickPickEntry, additionalEntries?: ITaskQuickPickEntry[]): Promise<ITaskQuickPickEntry | undefined | null> {
@@ -3143,7 +3144,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
31433144
if (this._isTaskEntry(selection)) {
31443145
this._configureTask(selection.task);
31453146
} else if (this._isSettingEntry(selection)) {
3146-
const taskQuickPick = new TaskQuickPick(this, this._configurationService, this._quickInputService, this._notificationService, this._dialogService);
3147+
const taskQuickPick = new TaskQuickPick(this, this._configurationService, this._quickInputService, this._notificationService, this._themeService, this._dialogService);
31473148
taskQuickPick.handleSettingOption(selection.settingType);
31483149
} else if (selection.folder && (this._contextService.getWorkbenchState() !== WorkbenchState.EMPTY)) {
31493150
this._openTaskFile(selection.folder.toResource('.vscode/tasks.json'), TaskSourceKind.Workspace);
@@ -3201,7 +3202,9 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
32013202
if (tasks.length > 0) {
32023203
tasks = tasks.sort((a, b) => a._label.localeCompare(b._label));
32033204
for (const task of tasks) {
3204-
entries.push({ label: task._label, task, description: this.getTaskDescription(task), detail: this._showDetail() ? task.configurationProperties.detail : undefined });
3205+
const entry = { label: TaskQuickPick.getTaskLabelWithIcon(task), task, description: this.getTaskDescription(task), detail: this._showDetail() ? task.configurationProperties.detail : undefined };
3206+
TaskQuickPick.applyColorStyles(task, entry, this._themeService);
3207+
entries.push(entry);
32053208
if (!ContributedTask.is(task)) {
32063209
configuredCount++;
32073210
}

src/vs/workbench/contrib/tasks/browser/taskQuickPick.ts

Lines changed: 101 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,18 @@ import { Task, ContributedTask, CustomTask, ConfiguringTask, TaskSorter, KeyedTa
99
import { IWorkspace, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
1010
import * as Types from 'vs/base/common/types';
1111
import { ITaskService, IWorkspaceFolderTaskResult } from 'vs/workbench/contrib/tasks/common/taskService';
12-
import { IQuickPickItem, QuickPickInput, IQuickPick, IQuickInputButton } from 'vs/base/parts/quickinput/common/quickInput';
12+
import { IQuickPickItem, QuickPickInput, IQuickPick, IQuickInputButton, IQuickPickSeparator } from 'vs/base/parts/quickinput/common/quickInput';
1313
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
1414
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
15-
import { Disposable } from 'vs/base/common/lifecycle';
15+
import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle';
1616
import { Event } from 'vs/base/common/event';
1717
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
1818
import { Codicon } from 'vs/base/common/codicons';
19-
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
19+
import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService';
2020
import { registerIcon } from 'vs/platform/theme/common/iconRegistry';
2121
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
22+
import { getColorClass, getColorStyleElement, getStandardColors } from 'vs/workbench/contrib/terminal/browser/terminalIcon';
23+
import { TaskQuickPickEntryType } from 'vs/workbench/contrib/tasks/browser/abstractTaskService';
2224

2325
export const QUICKOPEN_DETAIL_CONFIG = 'task.quickOpen.detail';
2426
export const QUICKOPEN_SKIP_CONFIG = 'task.quickOpen.skip';
@@ -49,6 +51,7 @@ export class TaskQuickPick extends Disposable {
4951
private _configurationService: IConfigurationService,
5052
private _quickInputService: IQuickInputService,
5153
private _notificationService: INotificationService,
54+
private _themeService: IThemeService,
5255
private _dialogService: IDialogService) {
5356
super();
5457
this._sorter = this._taskService.createSorter();
@@ -61,7 +64,7 @@ export class TaskQuickPick extends Disposable {
6164

6265
private _guessTaskLabel(task: Task | ConfiguringTask): string {
6366
if (task._label) {
64-
return task._label;
67+
return TaskQuickPick.getTaskLabelWithIcon(task);
6568
}
6669
if (ConfiguringTask.is(task)) {
6770
let label: string = task.configures.type;
@@ -74,9 +77,29 @@ export class TaskQuickPick extends Disposable {
7477
return '';
7578
}
7679

80+
public static getTaskLabelWithIcon(task: Task | ConfiguringTask): string {
81+
return task.configurationProperties.icon ? `$(${task.configurationProperties.icon}) ${task._label}` : task.configurationProperties.color ? `$(${Codicon.tools.id}) ${task._label}` : `${task._label}`;
82+
}
83+
84+
public static applyColorStyles(task: Task | ConfiguringTask, entry: TaskQuickPickEntryType | ITaskTwoLevelQuickPickEntry, themeService: IThemeService): void {
85+
if (task.configurationProperties.color) {
86+
const colorTheme = themeService.getColorTheme();
87+
const styleElement = getColorStyleElement(colorTheme);
88+
entry.iconClasses = [getColorClass(task.configurationProperties.color)];
89+
document.body.appendChild(styleElement);
90+
}
91+
}
92+
7793
private _createTaskEntry(task: Task | ConfiguringTask, extraButtons: IQuickInputButton[] = []): ITaskTwoLevelQuickPickEntry {
94+
const customizeIconButton = { iconClass: ThemeIcon.asClassName(Codicon.pencil), tooltip: nls.localize('setIconAndColor', "Choose color and icon") };
7895
const entry: ITaskTwoLevelQuickPickEntry = { label: this._guessTaskLabel(task), description: this._taskService.getTaskDescription(task), task, detail: this._showDetail() ? task.configurationProperties.detail : undefined };
79-
entry.buttons = [{ iconClass: ThemeIcon.asClassName(configureTaskIcon), tooltip: nls.localize('configureTask', "Configure Task") }, ...extraButtons];
96+
entry.buttons = [];
97+
if (CustomTask.is(task)) {
98+
entry.buttons.push(customizeIconButton);
99+
}
100+
entry.buttons.push({ iconClass: ThemeIcon.asClassName(configureTaskIcon), tooltip: nls.localize('configureTask', "Configure Task") });
101+
entry.buttons.push(...extraButtons);
102+
TaskQuickPick.applyColorStyles(task, entry, this._themeService);
80103
return entry;
81104
}
82105

@@ -211,6 +234,9 @@ export class TaskQuickPick extends Disposable {
211234
if (indexToRemove >= 0) {
212235
picker.items = [...picker.items.slice(0, indexToRemove), ...picker.items.slice(indexToRemove + 1)];
213236
}
237+
} else if (context.button.iconClass = ThemeIcon.asClassName(Codicon.pencil)) {
238+
await this._setColor(task);
239+
await this._setIcon(task);
214240
} else {
215241
this._quickInputService.cancel();
216242
if (ContributedTask.is(task)) {
@@ -265,6 +291,74 @@ export class TaskQuickPick extends Disposable {
265291
return;
266292
}
267293

294+
private async _setColor(task: Task | ConfiguringTask | null | string | undefined): Promise<void> {
295+
if (task === undefined || task === null || typeof task === 'string') {
296+
return;
297+
}
298+
const colorTheme = this._themeService.getColorTheme();
299+
const standardColors: string[] = getStandardColors(colorTheme);
300+
const styleElement = getColorStyleElement(colorTheme);
301+
const items: (IQuickPickItem | IQuickPickSeparator)[] = [];
302+
for (const colorKey of standardColors) {
303+
const colorClass = getColorClass(colorKey);
304+
items.push({
305+
label: `$(${Codicon.circleFilled.id}) ${colorKey.replace('terminal.ansi', '')}`, id: colorKey, description: colorKey, iconClasses: [colorClass]
306+
});
307+
}
308+
items.push({ type: 'separator' });
309+
const showAllColorsItem = { label: 'Reset to default' };
310+
items.push(showAllColorsItem);
311+
document.body.appendChild(styleElement);
312+
313+
const quickPick = this._quickInputService.createQuickPick();
314+
quickPick.items = items;
315+
quickPick.matchOnDescription = true;
316+
quickPick.show();
317+
const disposables: IDisposable[] = [];
318+
const result = await new Promise<IQuickPickItem | undefined>(r => {
319+
disposables.push(quickPick.onDidHide(() => r(undefined)));
320+
disposables.push(quickPick.onDidAccept(() => r(quickPick.selectedItems[0])));
321+
});
322+
dispose(disposables);
323+
324+
if (result && task && typeof task !== 'string') {
325+
task.configurationProperties.color = result.id;
326+
}
327+
document.body.removeChild(styleElement);
328+
quickPick.hide();
329+
}
330+
331+
private async _setIcon(task: Task | ConfiguringTask | null | string | undefined): Promise<void> {
332+
if (task === undefined || task === null || typeof task === 'string') {
333+
return;
334+
}
335+
type Item = IQuickPickItem & { icon: ThemeIcon };
336+
const items: Item[] = [];
337+
for (const icon of Codicon.getAll()) {
338+
items.push({ label: `$(${icon.id})`, description: `${icon.id}`, id: icon.id, icon, iconClasses: task.configurationProperties.color ? [getColorClass(task.configurationProperties.color)] : undefined });
339+
}
340+
341+
const quickPick = this._quickInputService.createQuickPick();
342+
quickPick.items = items;
343+
quickPick.matchOnDescription = true;
344+
quickPick.show();
345+
const disposables: IDisposable[] = [];
346+
const result = await new Promise<IQuickPickItem | undefined>(r => {
347+
disposables.push(quickPick.onDidHide(() => r(undefined)));
348+
disposables.push(quickPick.onDidAccept(() => r(quickPick.selectedItems[0])));
349+
});
350+
dispose(disposables);
351+
352+
if (result && task && typeof task !== 'string') {
353+
task.configurationProperties.icon = result.id;
354+
}
355+
if (CustomTask.is(task) && result) {
356+
await this._taskService.customize(task, { icon: result.id, color: task.configurationProperties.color }, false);
357+
}
358+
quickPick.hide();
359+
}
360+
361+
268362
private async _doPickerFirstLevel(picker: IQuickPick<ITaskTwoLevelQuickPickEntry>, taskQuickPickEntries: QuickPickInput<ITaskTwoLevelQuickPickEntry>[]): Promise<Task | ConfiguringTask | string | null | undefined> {
269363
picker.items = taskQuickPickEntries;
270364
const firstLevelPickerResult = await new Promise<ITaskTwoLevelQuickPickEntry | undefined | null>(resolve => {
@@ -367,8 +461,8 @@ export class TaskQuickPick extends Disposable {
367461

368462
static async show(taskService: ITaskService, configurationService: IConfigurationService,
369463
quickInputService: IQuickInputService, notificationService: INotificationService,
370-
dialogService: IDialogService, placeHolder: string, defaultEntry?: ITaskQuickPickEntry) {
371-
const taskQuickPick = new TaskQuickPick(taskService, configurationService, quickInputService, notificationService, dialogService);
464+
dialogService: IDialogService, themeService: IThemeService, placeHolder: string, defaultEntry?: ITaskQuickPickEntry) {
465+
const taskQuickPick = new TaskQuickPick(taskService, configurationService, quickInputService, notificationService, themeService, dialogService);
372466
return taskQuickPick.show(placeHolder, defaultEntry);
373467
}
374468
}

src/vs/workbench/contrib/tasks/browser/tasksQuickAccess.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
1717
import { isString } from 'vs/base/common/types';
1818
import { INotificationService } from 'vs/platform/notification/common/notification';
1919
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
20+
import { IThemeService } from 'vs/platform/theme/common/themeService';
2021

2122
export class TasksQuickAccessProvider extends PickerQuickAccessProvider<IPickerQuickAccessItem> {
2223

@@ -28,7 +29,8 @@ export class TasksQuickAccessProvider extends PickerQuickAccessProvider<IPickerQ
2829
@IConfigurationService private _configurationService: IConfigurationService,
2930
@IQuickInputService private _quickInputService: IQuickInputService,
3031
@INotificationService private _notificationService: INotificationService,
31-
@IDialogService private _dialogService: IDialogService
32+
@IDialogService private _dialogService: IDialogService,
33+
@IThemeService private _themeService: IThemeService
3234
) {
3335
super(TasksQuickAccessProvider.PREFIX, {
3436
noResultsPick: {
@@ -42,7 +44,7 @@ export class TasksQuickAccessProvider extends PickerQuickAccessProvider<IPickerQ
4244
return [];
4345
}
4446

45-
const taskQuickPick = new TaskQuickPick(this._taskService, this._configurationService, this._quickInputService, this._notificationService, this._dialogService);
47+
const taskQuickPick = new TaskQuickPick(this._taskService, this._configurationService, this._quickInputService, this._notificationService, this._themeService, this._dialogService);
4648
const topLevelPicks = await taskQuickPick.getTopLevelEntries();
4749
const taskPicks: Array<IPickerQuickAccessItem | IQuickPickSeparator> = [];
4850

src/vs/workbench/contrib/tasks/common/taskService.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ export interface ICustomizationProperties {
3636
group?: string | { kind?: string; isDefault?: boolean };
3737
problemMatcher?: string | string[];
3838
isBackground?: boolean;
39+
color?: string;
40+
icon?: string;
3941
}
4042

4143
export interface ITaskFilter {

src/vs/workbench/contrib/tasks/electron-sandbox/taskService.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import { ITextFileService } from 'vs/workbench/services/textfile/common/textfile
4343
import { IWorkspaceTrustManagementService, IWorkspaceTrustRequestService } from 'vs/platform/workspace/common/workspaceTrust';
4444
import { ITerminalProfileResolverService } from 'vs/workbench/contrib/terminal/common/terminal';
4545
import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite';
46+
import { IThemeService } from 'vs/platform/theme/common/themeService';
4647

4748
interface IWorkspaceFolderConfigurationResult {
4849
workspaceFolder: IWorkspaceFolder;
@@ -83,7 +84,8 @@ export class TaskService extends AbstractTaskService {
8384
@IViewDescriptorService viewDescriptorService: IViewDescriptorService,
8485
@IWorkspaceTrustRequestService workspaceTrustRequestService: IWorkspaceTrustRequestService,
8586
@IWorkspaceTrustManagementService workspaceTrustManagementService: IWorkspaceTrustManagementService,
86-
@ILogService logService: ILogService) {
87+
@ILogService logService: ILogService,
88+
@IThemeService themeService: IThemeService) {
8789
super(configurationService,
8890
markerService,
8991
outputService,
@@ -115,7 +117,8 @@ export class TaskService extends AbstractTaskService {
115117
viewDescriptorService,
116118
workspaceTrustRequestService,
117119
workspaceTrustManagementService,
118-
logService);
120+
logService,
121+
themeService);
119122
this._register(lifecycleService.onBeforeShutdown(event => event.veto(this.beforeShutdown(), 'veto.tasks')));
120123
}
121124

0 commit comments

Comments
 (0)