Merge pull request #284366 from microsoft/dev/dmitriv/update-quick-pick-toggles

Use new toggles in the public API
This commit is contained in:
Dmitriy Vasyura
2025-12-18 21:49:46 -08:00
committed by GitHub
4 changed files with 14 additions and 90 deletions

View File

@@ -85,8 +85,8 @@ export function quickInputButtonToAction(button: IQuickInputButton, id: string,
const action = button.toggle
? new QuickInputToggleButtonAction(
id,
'',
button.tooltip || '',
'',
cssClasses,
true,
button.toggle.checked,

View File

@@ -3,10 +3,9 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Toggle } from '../../../base/browser/ui/toggle/toggle.js';
import { CancellationToken } from '../../../base/common/cancellation.js';
import { Lazy } from '../../../base/common/lazy.js';
import { DisposableStore, IDisposable } from '../../../base/common/lifecycle.js';
import { DisposableStore } from '../../../base/common/lifecycle.js';
import { basenameOrAuthority, dirname, hasTrailingPathSeparator } from '../../../base/common/resources.js';
import { ThemeIcon } from '../../../base/common/themables.js';
import { isUriComponents, URI } from '../../../base/common/uri.js';
@@ -15,8 +14,7 @@ import { getIconClasses } from '../../../editor/common/services/getIconClasses.j
import { IModelService } from '../../../editor/common/services/model.js';
import { FileKind } from '../../../platform/files/common/files.js';
import { ILabelService } from '../../../platform/label/common/label.js';
import { IInputOptions, IPickOptions, IQuickInput, IQuickInputService, IQuickPick, IQuickPickItem, QuickInputButtonLocation } from '../../../platform/quickinput/common/quickInput.js';
import { asCssVariable, inputActiveOptionBackground, inputActiveOptionBorder, inputActiveOptionForeground } from '../../../platform/theme/common/colorRegistry.js';
import { IInputOptions, IPickOptions, IQuickInput, IQuickInputService, IQuickPick, IQuickPickItem } from '../../../platform/quickinput/common/quickInput.js';
import { ICustomEditorLabelService } from '../../services/editor/common/customEditorLabelService.js';
import { extHostNamedCustomer, IExtHostContext } from '../../services/extensions/common/extHostCustomers.js';
import { ExtHostContext, ExtHostQuickOpenShape, IInputBoxOptions, MainContext, MainThreadQuickOpenShape, TransferQuickInput, TransferQuickInputButton, TransferQuickPickItem, TransferQuickPickItemOrSeparator } from '../common/extHost.protocol.js';
@@ -24,7 +22,6 @@ import { ExtHostContext, ExtHostQuickOpenShape, IInputBoxOptions, MainContext, M
interface QuickInputSession {
input: IQuickInput;
handlesToItems: Map<number, TransferQuickPickItem>;
handlesToToggles: Map<number, { toggle: Toggle; listener: IDisposable }>;
store: DisposableStore;
}
@@ -143,7 +140,7 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
this._proxy.$onDidAccept(sessionId);
}));
store.add(input.onDidTriggerButton(button => {
this._proxy.$onDidTriggerButton(sessionId, (button as TransferQuickInputButton).handle);
this._proxy.$onDidTriggerButton(sessionId, (button as TransferQuickInputButton).handle, button.toggle?.checked);
}));
store.add(input.onDidChangeValue(value => {
this._proxy.$onDidChangeValue(sessionId, value);
@@ -154,14 +151,14 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
if (params.type === 'quickPick') {
// Add extra events specific for quick pick
const quickpick = input as IQuickPick<IQuickPickItem>;
store.add(quickpick.onDidChangeActive(items => {
const quickPick = input as IQuickPick<IQuickPickItem>;
store.add(quickPick.onDidChangeActive(items => {
this._proxy.$onDidChangeActive(sessionId, items.map(item => (item as TransferQuickPickItem).handle));
}));
store.add(quickpick.onDidChangeSelection(items => {
store.add(quickPick.onDidChangeSelection(items => {
this._proxy.$onDidChangeSelection(sessionId, items.map(item => (item as TransferQuickPickItem).handle));
}));
store.add(quickpick.onDidTriggerItemButton((e) => {
store.add(quickPick.onDidTriggerItemButton((e) => {
this._proxy.$onDidTriggerItemButton(sessionId, (e.item as TransferQuickPickItem).handle, (e.button as TransferQuickInputButton).handle);
}));
}
@@ -169,7 +166,6 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
session = {
input,
handlesToItems: new Map(),
handlesToToggles: new Map(),
store
};
this.sessions.set(sessionId, session);
@@ -217,24 +213,16 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
break;
case 'buttons': {
const buttons = [], toggles = [];
const buttons = [];
for (const button of params.buttons!) {
if (button.handle === -1) {
buttons.push(this._quickInputService.backButton);
} else {
this.expandIconPath(button);
// Currently buttons are only supported outside of the input box
// and toggles only inside. When/if that changes, this will need to be updated.
if (button.location === QuickInputButtonLocation.Input) {
toggles.push(button);
} else {
buttons.push(button);
}
}
}
input.buttons = buttons;
this.updateToggles(sessionId, session, toggles);
break;
}
@@ -310,60 +298,4 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
target.iconPath = { dark: URI.from(dark), light: URI.from(light) };
}
}
/**
* Updates the toggles for a given quick input session by creating new {@link Toggle}-s
* from buttons, updating existing toggles props and removing old ones.
*/
private updateToggles(sessionId: number, session: QuickInputSession, buttons: TransferQuickInputButton[]) {
const { input, handlesToToggles, store } = session;
// Add new or update existing toggles.
const toggles = [];
for (const button of buttons) {
const title = button.tooltip || '';
const isChecked = !!button.checked;
// TODO: Toggle class only supports ThemeIcon at the moment, but not other formats of IconPath.
// We should consider adding support for the full IconPath to Toggle, in this code should be updated.
const icon = ThemeIcon.isThemeIcon(button.iconPathDto) ? button.iconPathDto : undefined;
let { toggle } = handlesToToggles.get(button.handle) || {};
if (toggle) {
// Toggle already exists, update its props.
toggle.setTitle(title);
toggle.setIcon(icon);
toggle.checked = isChecked;
} else {
// Create a new toggle from the button.
toggle = store.add(new Toggle({
title,
icon,
isChecked,
inputActiveOptionBorder: asCssVariable(inputActiveOptionBorder),
inputActiveOptionForeground: asCssVariable(inputActiveOptionForeground),
inputActiveOptionBackground: asCssVariable(inputActiveOptionBackground)
}));
const listener = store.add(toggle.onChange(() => {
this._proxy.$onDidTriggerButton(sessionId, button.handle, toggle!.checked);
}));
handlesToToggles.set(button.handle, { toggle, listener });
}
toggles.push(toggle);
}
// Remove toggles that are no longer present from the session map.
for (const [handle, { toggle, listener }] of handlesToToggles) {
if (!buttons.some(button => button.handle === handle)) {
handlesToToggles.delete(handle);
store.delete(toggle);
store.delete(listener);
}
}
// Update toggle interfaces on the input widget.
input.toggles = toggles;
}
}

View File

@@ -678,7 +678,7 @@ export interface TransferQuickPickItem {
export interface TransferQuickInputButton extends quickInput.IQuickInputButton {
handle: number;
iconPathDto: IconPathDto;
checked?: boolean;
toggle?: { checked: boolean };
// TODO: These properties are not used for transfer (iconPathDto is used instead) but they cannot be removed
// because this type is used as IQuickInputButton on the main thread. Ideally IQuickInputButton should also use IconPath.

View File

@@ -10,7 +10,7 @@ import { ExtHostCommands } from './extHostCommands.js';
import { IExtHostWorkspaceProvider } from './extHostWorkspace.js';
import { InputBox, InputBoxOptions, InputBoxValidationMessage, QuickInput, QuickInputButton, QuickPick, QuickPickItem, QuickPickItemButtonEvent, QuickPickOptions, WorkspaceFolder, WorkspaceFolderPickOptions } from 'vscode';
import { ExtHostQuickOpenShape, IMainContext, MainContext, TransferQuickInput, TransferQuickInputButton, TransferQuickPickItemOrSeparator } from './extHost.protocol.js';
import { QuickInputButtons, QuickPickItemKind, InputBoxValidationSeverity, QuickInputButtonLocation } from './extHostTypes.js';
import { QuickInputButtons, QuickPickItemKind, InputBoxValidationSeverity } from './extHostTypes.js';
import { isCancellationError } from '../../../base/common/errors.js';
import { IExtensionDescription } from '../../../platform/extensions/common/extensions.js';
import { coalesce } from '../../../base/common/arrays.js';
@@ -397,14 +397,6 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx
checkProposedApiEnabled(this._extension, 'quickInputButtonLocation');
}
if (buttons.some(button =>
typeof button.location === 'number' &&
button.location !== QuickInputButtonLocation.Input &&
typeof button.toggle === 'object' &&
typeof button.toggle.checked === 'boolean')) {
throw new Error('QuickInputButtons with toggle set are only supported in the Input location.');
}
this._buttons = buttons.slice();
this._handlesToButtons.clear();
buttons.forEach((button, i) => {
@@ -418,7 +410,7 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx
tooltip: button.tooltip,
handle: button === QuickInputButtons.Back ? -1 : i,
location: typeof button.location === 'number' ? button.location : undefined,
checked: typeof button.toggle === 'object' && typeof button.toggle.checked === 'boolean' ? button.toggle.checked : undefined
toggle: typeof button.toggle === 'object' && typeof button.toggle.checked === 'boolean' ? { checked: button.toggle.checked } : undefined,
};
})
});