UNPKG

monaco-editor-core

Version:
898 lines • 45 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } }; var QuickInputController_1; import * as dom from '../../../base/browser/dom.js'; import * as domStylesheetsJs from '../../../base/browser/domStylesheets.js'; import { ActionBar } from '../../../base/browser/ui/actionbar/actionbar.js'; import { Button } from '../../../base/browser/ui/button/button.js'; import { CountBadge } from '../../../base/browser/ui/countBadge/countBadge.js'; import { ProgressBar } from '../../../base/browser/ui/progressbar/progressbar.js'; import { CancellationToken } from '../../../base/common/cancellation.js'; import { Emitter, Event } from '../../../base/common/event.js'; import { Disposable, dispose } from '../../../base/common/lifecycle.js'; import Severity from '../../../base/common/severity.js'; import { isString } from '../../../base/common/types.js'; import { localize } from '../../../nls.js'; import { QuickInputHideReason } from '../common/quickInput.js'; import { QuickInputBox } from './quickInputBox.js'; import { QuickPick, backButton, InputBox, InQuickInputContextKey, QuickInputTypeContextKey, EndOfQuickInputBoxContextKey, QuickInputAlignmentContextKey } from './quickInput.js'; import { ILayoutService } from '../../layout/browser/layoutService.js'; import { mainWindow } from '../../../base/browser/window.js'; import { IInstantiationService } from '../../instantiation/common/instantiation.js'; import { QuickInputList } from './quickInputList.js'; import { IContextKeyService } from '../../contextkey/common/contextkey.js'; import './quickInputActions.js'; import { autorun, observableValue } from '../../../base/common/observable.js'; import { StandardMouseEvent } from '../../../base/browser/mouseEvent.js'; import { IStorageService } from '../../storage/common/storage.js'; import { IConfigurationService } from '../../configuration/common/configuration.js'; import { platform, setTimeout0 } from '../../../base/common/platform.js'; import { getWindowControlsStyle } from '../../window/common/window.js'; import { getZoomFactor } from '../../../base/browser/browser.js'; import { TriStateCheckbox } from '../../../base/browser/ui/toggle/toggle.js'; import { defaultCheckboxStyles } from '../../theme/browser/defaultStyles.js'; import { QuickInputTreeController } from './tree/quickInputTreeController.js'; const $ = dom.$; const VIEWSTATE_STORAGE_KEY = 'workbench.quickInput.viewState'; let QuickInputController = class QuickInputController extends Disposable { static { QuickInputController_1 = this; } static { this.MAX_WIDTH = 600; } // Max total width of quick input widget get currentQuickInput() { return this.controller ?? undefined; } get container() { return this._container; } constructor(options, layoutService, instantiationService, contextKeyService, storageService) { super(); this.options = options; this.layoutService = layoutService; this.instantiationService = instantiationService; this.storageService = storageService; this.enabled = true; this.onDidAcceptEmitter = this._register(new Emitter()); this.onDidCustomEmitter = this._register(new Emitter()); this.onDidTriggerButtonEmitter = this._register(new Emitter()); this.keyMods = { ctrlCmd: false, alt: false }; this.controller = null; this.onShowEmitter = this._register(new Emitter()); this.onShow = this.onShowEmitter.event; this.onHideEmitter = this._register(new Emitter()); this.onHide = this.onHideEmitter.event; this.inQuickInputContext = InQuickInputContextKey.bindTo(contextKeyService); this.quickInputTypeContext = QuickInputTypeContextKey.bindTo(contextKeyService); this.endOfQuickInputBoxContext = EndOfQuickInputBoxContextKey.bindTo(contextKeyService); this.idPrefix = options.idPrefix; this._container = options.container; this.styles = options.styles; this._register(Event.runAndSubscribe(dom.onDidRegisterWindow, ({ window, disposables }) => this.registerKeyModsListeners(window, disposables), { window: mainWindow, disposables: this._store })); this._register(dom.onWillUnregisterWindow(window => { if (this.ui && dom.getWindow(this.ui.container) === window) { // The window this quick input is contained in is about to // close, so we have to make sure to reparent it back to an // existing parent to not loose functionality. // (https://github.com/microsoft/vscode/issues/195870) this.reparentUI(this.layoutService.mainContainer); this.layout(this.layoutService.mainContainerDimension, this.layoutService.mainContainerOffset.quickPickTop); } })); this.viewState = this.loadViewState(); } registerKeyModsListeners(window, disposables) { const listener = (e) => { this.keyMods.ctrlCmd = e.ctrlKey || e.metaKey; this.keyMods.alt = e.altKey; }; for (const event of [dom.EventType.KEY_DOWN, dom.EventType.KEY_UP, dom.EventType.MOUSE_DOWN]) { disposables.add(dom.addDisposableListener(window, event, listener, true)); } } getUI(showInActiveContainer) { 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 = domStylesheetsJs.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 = this._register(new TriStateCheckbox(localize(1763, "Toggle all checkboxes"), false, { ...defaultCheckboxStyles, size: 15 })); dom.append(headerContainer, checkAll.domNode); this._register(checkAll.onChange(() => { const checked = checkAll.checked; list.setAllVisibleChecked(checked === true); })); this._register(dom.addDisposableListener(checkAll.domNode, 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 = this._register(new CountBadge(visibleCountContainer, { countFormat: localize(1764, "{0} Results") }, this.styles.countBadge)); const countContainer = dom.append(filterContainer, $('.quick-input-count')); countContainer.setAttribute('aria-live', 'polite'); const count = this._register(new CountBadge(countContainer, { countFormat: localize(1765, "{0} Selected") }, this.styles.countBadge)); const inlineActionBar = this._register(new ActionBar(headerContainer, { hoverDelegate: this.options.hoverDelegate })); inlineActionBar.domNode.classList.add('quick-input-inline-action-bar'); const okContainer = dom.append(headerContainer, $('.quick-input-action')); const ok = this._register(new Button(okContainer, this.styles.button)); ok.label = localize(1766, "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(1767, "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')); // List const listId = this.idPrefix + 'list'; const list = this._register(this.instantiationService.createInstance(QuickInputList, container, this.options.hoverDelegate, this.options.linkOpenerDelegate, listId)); inputBox.setAttribute('aria-controls', listId); this._register(list.onDidChangeFocus(() => { if (inputBox.hasFocus()) { inputBox.setAttribute('aria-activedescendant', list.getActiveDescendant() ?? ''); } })); this._register(list.onChangedAllVisibleChecked(checked => { // TODO: Support tri-state checkbox when we remove the .indent property that is faking tree structure. checkAll.checked = checked; })); this._register(list.onChangedVisibleCount(c => { visibleCount.setCount(c); })); this._register(list.onChangedCheckedCount(c => { // TODO@TylerLeonhardt: Without this setTimeout, the screen reader will not read out // the final count of checked items correctly. Investigate a better way // to do this. ref https://github.com/microsoft/vscode/issues/258617 setTimeout0(() => 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); })); // Tree const tree = this._register(this.instantiationService.createInstance(QuickInputTreeController, container, this.options.hoverDelegate)); this._register(tree.tree.onDidChangeFocus(() => { if (inputBox.hasFocus()) { inputBox.setAttribute('aria-activedescendant', tree.getActiveDescendant() ?? ''); } })); this._register(tree.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(); tree.tree.setFocus([]); }, 0); })); // Wire up tree's accept event to the UI's accept emitter for non-pickable items this._register(tree.onDidAccept(() => { this.onDidAcceptEmitter.fire(); })); this._register(tree.tree.onDidChangeContentHeight(() => this.updateLayout())); 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, 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, 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); } // Allow screenreaders to read what's in the input // Note: this works for arrow keys and selection changes, // but not for deletions since that often triggers a // change in the list. inputBox.removeAttribute('aria-activedescendant'); })); this._register(dom.addDisposableListener(container, dom.EventType.FOCUS, (e) => { inputBox.setFocus(); })); // Drag and Drop support this.dndController = this._register(this.instantiationService.createInstance(QuickInputDragAndDropController, this._container, container, [ { node: titleBar, includeChildren: true }, { node: headerContainer, includeChildren: false } ], this.viewState)); // DnD update layout this._register(autorun(reader => { const dndViewState = this.dndController?.dndViewState.read(reader); if (!dndViewState) { return; } if (dndViewState.top !== undefined && dndViewState.left !== undefined) { this.viewState = { ...this.viewState, top: dndViewState.top, left: dndViewState.left }; } else { // Reset position/size this.viewState = undefined; } this.updateLayout(); // Save position if (dndViewState.done) { this.saveViewState(this.viewState); } })); this.ui = { container, styleSheet, leftActionBar, titleBar, title, description1, description2, widget, rightActionBar, inlineActionBar, checkAll, inputContainer, filterContainer, inputBox, visibleCountContainer, visibleCount, countContainer, count, okContainer, ok, message, customButtonContainer, customButton, list, tree, 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; } reparentUI(container) { if (this.ui) { this._container = container; dom.append(this._container, this.ui.container); this.dndController?.reparentUI(this._container); } } pick(picks, options = {}, token = CancellationToken.None) { return new Promise((doResolve, reject) => { let resolve = (result) => { resolve = doResolve; options.onKeyMods?.(input.keyMods); doResolve(result); }; if (token.isCancellationRequested) { resolve(undefined); return; } const input = this.createQuickPick({ useSeparators: true }); let activeItem; const disposables = [ input, input.onDidAccept(() => { if (input.canSelectMany) { resolve(input.selectedItems.slice()); input.hide(); } else { const result = input.activeItems[0]; if (result) { resolve(result); input.hide(); } } }), input.onDidChangeActive(items => { const focused = items[0]; if (focused && options.onDidFocus) { options.onDidFocus(focused); } }), input.onDidChangeSelection(items => { if (!input.canSelectMany) { const result = items[0]; if (result) { resolve(result); input.hide(); } } }), input.onDidTriggerItemButton(event => options.onDidTriggerItemButton && options.onDidTriggerItemButton({ ...event, removeItem: () => { const index = input.items.indexOf(event.item); if (index !== -1) { const items = input.items.slice(); const removed = items.splice(index, 1); const activeItems = input.activeItems.filter(activeItem => activeItem !== removed[0]); const keepScrollPositionBefore = input.keepScrollPosition; input.keepScrollPosition = true; input.items = items; if (activeItems) { input.activeItems = activeItems; } input.keepScrollPosition = keepScrollPositionBefore; } } })), input.onDidTriggerSeparatorButton(event => options.onDidTriggerSeparatorButton?.(event)), input.onDidChangeValue(value => { if (activeItem && !value && (input.activeItems.length !== 1 || input.activeItems[0] !== activeItem)) { input.activeItems = [activeItem]; } }), token.onCancellationRequested(() => { input.hide(); }), input.onDidHide(() => { dispose(disposables); resolve(undefined); }), ]; input.title = options.title; if (options.value) { input.value = options.value; } input.canSelectMany = !!options.canPickMany; input.placeholder = options.placeHolder; input.prompt = options.prompt; input.ignoreFocusOut = !!options.ignoreFocusLost; input.matchOnDescription = !!options.matchOnDescription; input.matchOnDetail = !!options.matchOnDetail; if (options.sortByLabel !== undefined) { input.sortByLabel = options.sortByLabel; } input.matchOnLabel = (options.matchOnLabel === undefined) || options.matchOnLabel; // default to true input.quickNavigate = options.quickNavigate; input.hideInput = !!options.hideInput; input.contextKey = options.contextKey; input.busy = true; Promise.all([picks, options.activeItem]) .then(([items, _activeItem]) => { activeItem = _activeItem; input.busy = false; input.items = items; if (input.canSelectMany) { input.selectedItems = items.filter(item => item.type !== 'separator' && item.picked); } if (activeItem) { input.activeItems = [activeItem]; } }); input.show(); Promise.resolve(picks).then(undefined, err => { reject(err); input.hide(); }); }); } setValidationOnInput(input, validationResult) { if (validationResult && isString(validationResult)) { input.severity = Severity.Error; input.validationMessage = validationResult; } else if (validationResult && !isString(validationResult)) { input.severity = validationResult.severity; input.validationMessage = validationResult.content; } else { input.severity = Severity.Ignore; input.validationMessage = undefined; } } input(options = {}, token = CancellationToken.None) { return new Promise((resolve) => { if (token.isCancellationRequested) { resolve(undefined); return; } const input = this.createInputBox(); const validateInput = options.validateInput || (() => Promise.resolve(undefined)); const onDidValueChange = Event.debounce(input.onDidChangeValue, (last, cur) => cur, 100); let validationValue = options.value || ''; let validation = Promise.resolve(validateInput(validationValue)); const disposables = [ input, onDidValueChange(value => { if (value !== validationValue) { validation = Promise.resolve(validateInput(value)); validationValue = value; } validation.then(result => { if (value === validationValue) { this.setValidationOnInput(input, result); } }); }), input.onDidAccept(() => { const value = input.value; if (value !== validationValue) { validation = Promise.resolve(validateInput(value)); validationValue = value; } validation.then(result => { if (!result || (!isString(result) && result.severity !== Severity.Error)) { resolve(value); input.hide(); } else if (value === validationValue) { this.setValidationOnInput(input, result); } }); }), token.onCancellationRequested(() => { input.hide(); }), input.onDidHide(() => { dispose(disposables); resolve(undefined); }), ]; input.title = options.title; input.value = options.value || ''; input.valueSelection = options.valueSelection; input.prompt = options.prompt; input.placeholder = options.placeHolder; input.password = !!options.password; input.ignoreFocusOut = !!options.ignoreFocusLost; input.show(); }); } createQuickPick(options = { useSeparators: false }) { const ui = this.getUI(true); return new QuickPick(ui); } createInputBox() { const ui = this.getUI(true); return new InputBox(ui); } show(controller) { const ui = this.getUI(true); this.onShowEmitter.fire(); const oldController = this.controller; this.controller = controller; oldController?.didHide(); this.setEnabled(true); ui.leftActionBar.clear(); ui.title.textContent = ''; ui.description1.textContent = ''; ui.description2.textContent = ''; dom.reset(ui.widget); ui.rightActionBar.clear(); ui.inlineActionBar.clear(); ui.checkAll.checked = false; // ui.inputBox.value = ''; Avoid triggering an event. ui.inputBox.placeholder = ''; ui.inputBox.password = false; ui.inputBox.showDecoration(Severity.Ignore); ui.visibleCount.setCount(0); ui.count.setCount(0); dom.reset(ui.message); ui.progressBar.stop(); ui.progressBar.getContainer().setAttribute('aria-hidden', 'true'); ui.list.setElements([]); ui.list.matchOnDescription = false; ui.list.matchOnDetail = false; ui.list.matchOnLabel = true; ui.list.sortByLabel = true; ui.tree.updateFilterOptions({ matchOnDescription: false, matchOnLabel: true }); ui.tree.sortByLabel = true; ui.ignoreFocusOut = false; ui.inputBox.toggles = undefined; const backKeybindingLabel = this.options.backKeybindingLabel(); backButton.tooltip = backKeybindingLabel ? localize(1768, "Back ({0})", backKeybindingLabel) : localize(1769, "Back"); ui.container.style.display = ''; this.updateLayout(); this.dndController?.layoutContainer(); ui.inputBox.setFocus(); this.quickInputTypeContext.set(controller.type); } isVisible() { return !!this.ui && this.ui.container.style.display !== 'none'; } setVisibilities(visibilities) { const ui = this.getUI(); ui.title.style.display = visibilities.title ? '' : 'none'; ui.description1.style.display = visibilities.description && (visibilities.inputBox || visibilities.checkAll) ? '' : 'none'; ui.description2.style.display = visibilities.description && !(visibilities.inputBox || visibilities.checkAll) ? '' : 'none'; ui.checkAll.domNode.style.display = visibilities.checkAll ? '' : 'none'; ui.inputContainer.style.display = visibilities.inputBox ? '' : 'none'; ui.filterContainer.style.display = visibilities.inputBox ? '' : 'none'; ui.visibleCountContainer.style.display = visibilities.visibleCount ? '' : 'none'; ui.countContainer.style.display = visibilities.count ? '' : 'none'; ui.okContainer.style.display = visibilities.ok ? '' : 'none'; ui.customButtonContainer.style.display = visibilities.customButton ? '' : 'none'; ui.message.style.display = visibilities.message ? '' : 'none'; ui.progressBar.getContainer().style.display = visibilities.progressBar ? '' : 'none'; ui.list.displayed = !!visibilities.list; ui.tree.displayed = !!visibilities.tree; ui.container.classList.toggle('show-checkboxes', !!visibilities.checkBox); ui.container.classList.toggle('hidden-input', !visibilities.inputBox && !visibilities.description); this.updateLayout(); // TODO } setEnabled(enabled) { if (enabled !== this.enabled) { this.enabled = enabled; const ui = this.getUI(); for (const item of ui.leftActionBar.viewItems) { item.action.enabled = enabled; } for (const item of ui.rightActionBar.viewItems) { item.action.enabled = enabled; } if (enabled) { ui.checkAll.enable(); } else { ui.checkAll.disable(); } ui.inputBox.enabled = enabled; ui.ok.enabled = enabled; ui.list.enabled = enabled; } } hide(reason) { const controller = this.controller; if (!controller) { return; } controller.willHide(reason); const container = this.ui?.container; const focusChanged = container && !dom.isAncestorOfActiveElement(container); this.controller = null; this.onHideEmitter.fire(); if (container) { container.style.display = 'none'; } if (!focusChanged) { let currentElement = this.previousFocusElement; while (currentElement && !currentElement.offsetParent) { currentElement = currentElement.parentElement ?? undefined; } if (currentElement?.offsetParent) { currentElement.focus(); this.previousFocusElement = undefined; } else { this.options.returnFocus(); } } controller.didHide(reason); } toggleHover() { if (this.isVisible() && this.controller instanceof QuickPick) { this.getUI().list.toggleHover(); } } layout(dimension, titleBarOffset) { this.dimension = dimension; this.titleBarOffset = titleBarOffset; this.updateLayout(); } updateLayout() { if (this.ui && this.isVisible()) { const style = this.ui.container.style; const width = Math.min(this.dimension.width * 0.62 /* golden cut */, QuickInputController_1.MAX_WIDTH); style.width = width + 'px'; // Position style.top = `${this.viewState?.top ? Math.round(this.dimension.height * this.viewState.top) : this.titleBarOffset}px`; style.left = `${Math.round((this.dimension.width * (this.viewState?.left ?? 0.5 /* center */)) - (width / 2))}px`; this.ui.inputBox.layout(); this.ui.list.layout(this.dimension && this.dimension.height * 0.4); this.ui.tree.layout(this.dimension && this.dimension.height * 0.4); } } applyStyles(styles) { this.styles = styles; this.updateStyles(); } updateStyles() { if (this.ui) { const { quickInputTitleBackground, quickInputBackground, quickInputForeground, widgetBorder, widgetShadow, } = this.styles.widget; this.ui.titleBar.style.backgroundColor = quickInputTitleBackground ?? ''; this.ui.container.style.backgroundColor = quickInputBackground ?? ''; this.ui.container.style.color = quickInputForeground ?? ''; this.ui.container.style.border = widgetBorder ? `1px solid ${widgetBorder}` : ''; this.ui.container.style.boxShadow = widgetShadow ? `0 0 8px 2px ${widgetShadow}` : ''; this.ui.list.style(this.styles.list); this.ui.tree.tree.style(this.styles.list); const content = []; if (this.styles.pickerGroup.pickerGroupBorder) { content.push(`.quick-input-list .quick-input-list-entry { border-top-color: ${this.styles.pickerGroup.pickerGroupBorder}; }`); } if (this.styles.pickerGroup.pickerGroupForeground) { content.push(`.quick-input-list .quick-input-list-separator { color: ${this.styles.pickerGroup.pickerGroupForeground}; }`); } if (this.styles.pickerGroup.pickerGroupForeground) { content.push(`.quick-input-list .quick-input-list-separator-as-item { color: var(--vscode-descriptionForeground); }`); } if (this.styles.keybindingLabel.keybindingLabelBackground || this.styles.keybindingLabel.keybindingLabelBorder || this.styles.keybindingLabel.keybindingLabelBottomBorder || this.styles.keybindingLabel.keybindingLabelShadow || this.styles.keybindingLabel.keybindingLabelForeground) { content.push('.quick-input-list .monaco-keybinding > .monaco-keybinding-key {'); if (this.styles.keybindingLabel.keybindingLabelBackground) { content.push(`background-color: ${this.styles.keybindingLabel.keybindingLabelBackground};`); } if (this.styles.keybindingLabel.keybindingLabelBorder) { // Order matters here. `border-color` must come before `border-bottom-color`. content.push(`border-color: ${this.styles.keybindingLabel.keybindingLabelBorder};`); } if (this.styles.keybindingLabel.keybindingLabelBottomBorder) { content.push(`border-bottom-color: ${this.styles.keybindingLabel.keybindingLabelBottomBorder};`); } if (this.styles.keybindingLabel.keybindingLabelShadow) { content.push(`box-shadow: inset 0 -1px 0 ${this.styles.keybindingLabel.keybindingLabelShadow};`); } if (this.styles.keybindingLabel.keybindingLabelForeground) { content.push(`color: ${this.styles.keybindingLabel.keybindingLabelForeground};`); } content.push('}'); } const newStyles = content.join('\n'); if (newStyles !== this.ui.styleSheet.textContent) { this.ui.styleSheet.textContent = newStyles; } } } loadViewState() { try { const data = JSON.parse(this.storageService.get(VIEWSTATE_STORAGE_KEY, -1 /* StorageScope.APPLICATION */, '{}')); if (data.top !== undefined || data.left !== undefined) { return data; } } catch { } return undefined; } saveViewState(viewState) { const isMainWindow = this.layoutService.activeContainer === this.layoutService.mainContainer; if (!isMainWindow) { return; } if (viewState !== undefined) { this.storageService.store(VIEWSTATE_STORAGE_KEY, JSON.stringify(viewState), -1 /* StorageScope.APPLICATION */, 1 /* StorageTarget.MACHINE */); } else { this.storageService.remove(VIEWSTATE_STORAGE_KEY, -1 /* StorageScope.APPLICATION */); } } }; QuickInputController = QuickInputController_1 = __decorate([ __param(1, ILayoutService), __param(2, IInstantiationService), __param(3, IContextKeyService), __param(4, IStorageService) ], QuickInputController); export { QuickInputController }; let QuickInputDragAndDropController = class QuickInputDragAndDropController extends Disposable { constructor(_container, _quickInputContainer, _quickInputDragAreas, initialViewState, _layoutService, contextKeyService, configurationService) { super(); this._container = _container; this._quickInputContainer = _quickInputContainer; this._quickInputDragAreas = _quickInputDragAreas; this._layoutService = _layoutService; this.configurationService = configurationService; this.dndViewState = observableValue(this, undefined); this._snapThreshold = 20; this._snapLineHorizontalRatio = 0.25; this._quickInputAlignmentContext = QuickInputAlignmentContextKey.bindTo(contextKeyService); const customWindowControls = getWindowControlsStyle(this.configurationService) === "custom" /* WindowControlsStyle.CUSTOM */; // Do not allow the widget to overflow or underflow window controls. // Use CSS calculations to avoid having to force layout with `.clientWidth` this._controlsOnLeft = customWindowControls && platform === 1 /* Platform.Mac */; this._controlsOnRight = customWindowControls && (platform === 3 /* Platform.Windows */ || platform === 2 /* Platform.Linux */); this._registerLayoutListener(); this.registerMouseListeners(); this.dndViewState.set({ ...initialViewState, done: true }, undefined); } reparentUI(container) { this._container = container; } layoutContainer(dimension = this._layoutService.activeContainerDimension) { const state = this.dndViewState.get(); const dragAreaRect = this._quickInputContainer.getBoundingClientRect(); if (state?.top && state?.left) { const a = Math.round(state.left * 1e2) / 1e2; const b = dimension.width; const c = dragAreaRect.width; const d = a * b - c / 2; this._layout(state.top * dimension.height, d); } } _registerLayoutListener() { this._register(Event.filter(this._layoutService.onDidLayoutContainer, e => e.container === this._container)((e) => this.layoutContainer(e.dimension))); } registerMouseListeners() { const dragArea = this._quickInputContainer; // Double click this._register(dom.addDisposableGenericMouseUpListener(dragArea, (event) => { const originEvent = new StandardMouseEvent(dom.getWindow(dragArea), event); if (originEvent.detail !== 2) { return; } // Ignore event if the target is not the drag area if (!this._quickInputDragAreas.some(({ node, includeChildren }) => includeChildren ? dom.isAncestor(originEvent.target, node) : originEvent.target === node)) { return; } this.dndViewState.set({ top: undefined, left: undefined, done: true }, undefined); })); // Mouse down this._register(dom.addDisposableGenericMouseDownListener(dragArea, (e) => { const activeWindow = dom.getWindow(this._layoutService.activeContainer); const originEvent = new StandardMouseEvent(activeWindow, e); // Ignore event if the target is not the drag area if (!this._quickInputDragAreas.some(({ node, includeChildren }) => includeChildren ? dom.isAncestor(originEvent.target, node) : originEvent.target === node)) { return; } // Mouse position offset relative to dragArea const dragAreaRect = this._quickInputContainer.getBoundingClientRect(); const dragOffsetX = originEvent.browserEvent.clientX - dragAreaRect.left; const dragOffsetY = originEvent.browserEvent.clientY - dragAreaRect.top; let isMovingQuickInput = false; const mouseMoveListener = dom.addDisposableGenericMouseMoveListener(activeWindow, (e) => { const mouseMoveEvent = new StandardMouseEvent(activeWindow, e); mouseMoveEvent.preventDefault(); if (!isMovingQuickInput) { isMovingQuickInput = true; } this._layout(e.clientY - dragOffsetY, e.clientX - dragOffsetX); }); const mouseUpListener = dom.addDisposableGenericMouseUpListener(activeWindow, (e) => { if (isMovingQuickInput) { // Save position const state = this.dndViewState.get(); this.dndViewState.set({ top: state?.top, left: state?.left, done: true }, undefined); } // Dispose listeners mouseMoveListener.dispose(); mouseUpListener.dispose(); }); })); } _layout(topCoordinate, leftCoordinate) { const snapCoordinateYTop = this._getTopSnapValue(); const snapCoordinateY = this._getCenterYSnapValue(); const snapCoordinateX = this._getCenterXSnapValue(); // Make sure the quick input is not moved outside the container topCoordinate = Math.max(0, Math.min(topCoordinate, this._container.clientHeight - this._quickInputContainer.clientHeight)); if (topCoordinate < this._layoutService.activeContainerOffset.top) { if (this._controlsOnLeft) { leftCoordinate = Math.max(leftCoordinate, 80 / getZoomFactor(dom.getActiveWindow())); } else if (this._controlsOnRight) { leftCoordinate = Math.min(leftCoordinate, this._container.clientWidth - this._quickInputContainer.clientWidth - (140 / getZoomFactor(dom.getActiveWindow()))); } } const snappingToTop = Math.abs(topCoordinate - snapCoordinateYTop) < this._snapThreshold; topCoordinate = snappingToTop ? snapCoordinateYTop : topCoordinate; const snappingToCenter = Math.abs(topCoordinate - snapCoordinateY) < this._snapThreshold; topCoordinate = snappingToCenter ? snapCoordinateY : topCoordinate; const top = topCoordinate / this._container.clientHeight; // Make sure the quick input is not moved outside the container leftCoordinate = Math.max(0, Math.min(leftCoordinate, this._container.clientWidth - this._quickInputContainer.clientWidth)); const snappingToCenterX = Math.abs(leftCoordinate - snapCoordinateX) < this._snapThreshold; leftCoordinate = snappingToCenterX ? snapCoordinateX : leftCoordinate; const b = this._container.clientWidth; const c = this._quickInputContainer.clientWidth; const d = leftCoordinate; const left = (d + c / 2) / b; this.dndViewState.set({ top, left, done: false }, undefined); if (snappingToCenterX) { if (snappingToTop) { this._quickInputAlignmentContext.set('top'); return; } else if (snappingToCenter) { this._quickInputAlignmentContext.set('center'); return; } } this._quickInputAlignmentContext.set(undefined); } _getTopSnapValue() { return this._layoutService.activeContainerOffset.quickPickTop; } _getCenterYSnapValue() { return Math.round(this._container.clientHeight * this._snapLineHorizontalRatio); } _getCenterXSnapValue() { return Math.round(this._container.clientWidth / 2) - Math.round(this._quickInputContainer.clientWidth / 2); } }; QuickInputDragAndDropController = __decorate([ __param(4, ILayoutService), __param(5, IContextKeyService), __param(6, IConfigurationService) ], QuickInputDragAndDropController); //# sourceMappingURL=quickInputController.js.map