in patched-vscode/src/vs/platform/quickinput/browser/quickInputController.ts [99:353]
private getUI(showInActiveContainer?: boolean) {
if (this.ui) {
// In order to support aux windows, re-parent the controller
// if the original event is from a different document
if (showInActiveContainer) {
if (dom.getWindow(this._container) !== dom.getWindow(this.layoutService.activeContainer)) {
this.reparentUI(this.layoutService.activeContainer);
this.layout(this.layoutService.activeContainerDimension, this.layoutService.activeContainerOffset.quickPickTop);
}
}
return this.ui;
}
const container = dom.append(this._container, $('.quick-input-widget.show-file-icons'));
container.tabIndex = -1;
container.style.display = 'none';
const styleSheet = dom.createStyleSheet(container);
const titleBar = dom.append(container, $('.quick-input-titlebar'));
const leftActionBar = this._register(new ActionBar(titleBar, { hoverDelegate: this.options.hoverDelegate }));
leftActionBar.domNode.classList.add('quick-input-left-action-bar');
const title = dom.append(titleBar, $('.quick-input-title'));
const rightActionBar = this._register(new ActionBar(titleBar, { hoverDelegate: this.options.hoverDelegate }));
rightActionBar.domNode.classList.add('quick-input-right-action-bar');
const headerContainer = dom.append(container, $('.quick-input-header'));
const checkAll = <HTMLInputElement>dom.append(headerContainer, $('input.quick-input-check-all'));
checkAll.type = 'checkbox';
checkAll.setAttribute('aria-label', localize('quickInput.checkAll', "Toggle all checkboxes"));
this._register(dom.addStandardDisposableListener(checkAll, dom.EventType.CHANGE, e => {
const checked = checkAll.checked;
list.setAllVisibleChecked(checked);
}));
this._register(dom.addDisposableListener(checkAll, dom.EventType.CLICK, e => {
if (e.x || e.y) { // Avoid 'click' triggered by 'space'...
inputBox.setFocus();
}
}));
const description2 = dom.append(headerContainer, $('.quick-input-description'));
const inputContainer = dom.append(headerContainer, $('.quick-input-and-message'));
const filterContainer = dom.append(inputContainer, $('.quick-input-filter'));
const inputBox = this._register(new QuickInputBox(filterContainer, this.styles.inputBox, this.styles.toggle));
inputBox.setAttribute('aria-describedby', `${this.idPrefix}message`);
const visibleCountContainer = dom.append(filterContainer, $('.quick-input-visible-count'));
visibleCountContainer.setAttribute('aria-live', 'polite');
visibleCountContainer.setAttribute('aria-atomic', 'true');
const visibleCount = new CountBadge(visibleCountContainer, { countFormat: localize({ key: 'quickInput.visibleCount', comment: ['This tells the user how many items are shown in a list of items to select from. The items can be anything. Currently not visible, but read by screen readers.'] }, "{0} Results") }, this.styles.countBadge);
const countContainer = dom.append(filterContainer, $('.quick-input-count'));
countContainer.setAttribute('aria-live', 'polite');
const count = new CountBadge(countContainer, { countFormat: localize({ key: 'quickInput.countSelected', comment: ['This tells the user how many items are selected in a list of items to select from. The items can be anything.'] }, "{0} Selected") }, this.styles.countBadge);
const okContainer = dom.append(headerContainer, $('.quick-input-action'));
const ok = this._register(new Button(okContainer, this.styles.button));
ok.label = localize('ok', "OK");
this._register(ok.onDidClick(e => {
this.onDidAcceptEmitter.fire();
}));
const customButtonContainer = dom.append(headerContainer, $('.quick-input-action'));
const customButton = this._register(new Button(customButtonContainer, { ...this.styles.button, supportIcons: true }));
customButton.label = localize('custom', "Custom");
this._register(customButton.onDidClick(e => {
this.onDidCustomEmitter.fire();
}));
const message = dom.append(inputContainer, $(`#${this.idPrefix}message.quick-input-message`));
const progressBar = this._register(new ProgressBar(container, this.styles.progressBar));
progressBar.getContainer().classList.add('quick-input-progress');
const widget = dom.append(container, $('.quick-input-html-widget'));
widget.tabIndex = -1;
const description1 = dom.append(container, $('.quick-input-description'));
const listId = this.idPrefix + 'list';
const list = this._register(this.instantiationService.createInstance(QuickInputTree, container, this.options.hoverDelegate, this.options.linkOpenerDelegate, listId));
inputBox.setAttribute('aria-controls', listId);
this._register(list.onDidChangeFocus(() => {
inputBox.setAttribute('aria-activedescendant', list.getActiveDescendant() ?? '');
}));
this._register(list.onChangedAllVisibleChecked(checked => {
checkAll.checked = checked;
}));
this._register(list.onChangedVisibleCount(c => {
visibleCount.setCount(c);
}));
this._register(list.onChangedCheckedCount(c => {
count.setCount(c);
}));
this._register(list.onLeave(() => {
// Defer to avoid the input field reacting to the triggering key.
// TODO@TylerLeonhardt https://github.com/microsoft/vscode/issues/203675
setTimeout(() => {
if (!this.controller) {
return;
}
inputBox.setFocus();
if (this.controller instanceof QuickPick && this.controller.canSelectMany) {
list.clearFocus();
}
}, 0);
}));
const focusTracker = dom.trackFocus(container);
this._register(focusTracker);
this._register(dom.addDisposableListener(container, dom.EventType.FOCUS, e => {
const ui = this.getUI();
if (dom.isAncestor(e.relatedTarget as HTMLElement, ui.inputContainer)) {
const value = ui.inputBox.isSelectionAtEnd();
if (this.endOfQuickInputBoxContext.get() !== value) {
this.endOfQuickInputBoxContext.set(value);
}
}
// Ignore focus events within container
if (dom.isAncestor(e.relatedTarget as HTMLElement, ui.container)) {
return;
}
this.inQuickInputContext.set(true);
this.previousFocusElement = dom.isHTMLElement(e.relatedTarget) ? e.relatedTarget : undefined;
}, true));
this._register(focusTracker.onDidBlur(() => {
if (!this.getUI().ignoreFocusOut && !this.options.ignoreFocusOut()) {
this.hide(QuickInputHideReason.Blur);
}
this.inQuickInputContext.set(false);
this.endOfQuickInputBoxContext.set(false);
this.previousFocusElement = undefined;
}));
this._register(inputBox.onKeyDown(_ => {
const value = this.getUI().inputBox.isSelectionAtEnd();
if (this.endOfQuickInputBoxContext.get() !== value) {
this.endOfQuickInputBoxContext.set(value);
}
}));
this._register(dom.addDisposableListener(container, dom.EventType.FOCUS, (e: FocusEvent) => {
inputBox.setFocus();
}));
// TODO: Turn into commands instead of handling KEY_DOWN
// Keybindings for the quickinput widget as a whole
this._register(dom.addStandardDisposableListener(container, dom.EventType.KEY_DOWN, (event) => {
if (dom.isAncestor(event.target, widget)) {
return; // Ignore event if target is inside widget to allow the widget to handle the event.
}
switch (event.keyCode) {
case KeyCode.Enter:
dom.EventHelper.stop(event, true);
if (this.enabled) {
this.onDidAcceptEmitter.fire();
}
break;
case KeyCode.Escape:
dom.EventHelper.stop(event, true);
this.hide(QuickInputHideReason.Gesture);
break;
case KeyCode.Tab:
if (!event.altKey && !event.ctrlKey && !event.metaKey) {
// detect only visible actions
const selectors = [
'.quick-input-list .monaco-action-bar .always-visible',
'.quick-input-list-entry:hover .monaco-action-bar',
'.monaco-list-row.focused .monaco-action-bar'
];
if (container.classList.contains('show-checkboxes')) {
selectors.push('input');
} else {
selectors.push('input[type=text]');
}
if (this.getUI().list.isDisplayed()) {
selectors.push('.monaco-list');
}
// focus links if there are any
if (this.getUI().message) {
selectors.push('.quick-input-message a');
}
if (this.getUI().widget) {
if (dom.isAncestor(event.target, this.getUI().widget)) {
// let the widget control tab
break;
}
selectors.push('.quick-input-html-widget');
}
const stops = container.querySelectorAll<HTMLElement>(selectors.join(', '));
if (event.shiftKey && event.target === stops[0]) {
// Clear the focus from the list in order to allow
// screen readers to read operations in the input box.
dom.EventHelper.stop(event, true);
list.clearFocus();
} else if (!event.shiftKey && dom.isAncestor(event.target, stops[stops.length - 1])) {
dom.EventHelper.stop(event, true);
stops[0].focus();
}
}
break;
case KeyCode.Space:
if (event.ctrlKey) {
dom.EventHelper.stop(event, true);
this.getUI().list.toggleHover();
}
break;
}
}));
this.ui = {
container,
styleSheet,
leftActionBar,
titleBar,
title,
description1,
description2,
widget,
rightActionBar,
checkAll,
inputContainer,
filterContainer,
inputBox,
visibleCountContainer,
visibleCount,
countContainer,
count,
okContainer,
ok,
message,
customButtonContainer,
customButton,
list,
progressBar,
onDidAccept: this.onDidAcceptEmitter.event,
onDidCustom: this.onDidCustomEmitter.event,
onDidTriggerButton: this.onDidTriggerButtonEmitter.event,
ignoreFocusOut: false,
keyMods: this.keyMods,
show: controller => this.show(controller),
hide: () => this.hide(),
setVisibilities: visibilities => this.setVisibilities(visibilities),
setEnabled: enabled => this.setEnabled(enabled),
setContextKey: contextKey => this.options.setContextKey(contextKey),
linkOpenerDelegate: content => this.options.linkOpenerDelegate(content)
};
this.updateStyles();
return this.ui;
}