Add multiline text editors in settings editor (#127118)
Also adds an editPresentation prop with descriptions to the schema.
This commit is contained in:
parent
f35bf81919
commit
be81d88a5e
7 changed files with 113 additions and 18 deletions
|
@ -11,6 +11,11 @@ import * as types from 'vs/base/common/types';
|
|||
import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
|
||||
import { IStringDictionary } from 'vs/base/common/collections';
|
||||
|
||||
export enum EditPresentationTypes {
|
||||
Multiline = 'multilineText',
|
||||
Singleline = 'singlelineText'
|
||||
}
|
||||
|
||||
export const Extensions = {
|
||||
Configuration: 'base.contributions.configuration'
|
||||
};
|
||||
|
@ -133,6 +138,12 @@ export interface IConfigurationPropertySchema extends IJSONSchema {
|
|||
disallowSyncIgnore?: boolean;
|
||||
|
||||
enumItemLabels?: string[];
|
||||
|
||||
/**
|
||||
* When specified, controls the presentation format of string settings.
|
||||
* Otherwise, the presentation format defaults to `singleline`.
|
||||
*/
|
||||
editPresentation?: EditPresentationTypes;
|
||||
}
|
||||
|
||||
export interface IConfigurationExtensionInfo {
|
||||
|
|
|
@ -81,6 +81,16 @@ const configurationEntrySchema: IJSONSchema = {
|
|||
markdownDeprecationMessage: {
|
||||
type: 'string',
|
||||
description: nls.localize('scope.markdownDeprecationMessage', 'If set, the property is marked as deprecated and the given message is shown as an explanation in the markdown format.')
|
||||
},
|
||||
editPresentation: {
|
||||
type: 'string',
|
||||
enum: ['singlelineText', 'multilineText'],
|
||||
enumDescriptions: [
|
||||
nls.localize('scope.singlelineText.description', 'The value will be shown in an inputbox.'),
|
||||
nls.localize('scope.multilineText.description', 'The value will be shown in a textarea.')
|
||||
],
|
||||
default: 'singlelineText',
|
||||
description: nls.localize('scope.editPresentation', 'When specified, controls the presentation format of the string setting.')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ import { IEditorMemento, IEditorOpenContext, IEditorPane } from 'vs/workbench/co
|
|||
import { attachSuggestEnabledInputBoxStyler, SuggestEnabledInput } from 'vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput';
|
||||
import { SettingsTarget, SettingsTargetsWidget } from 'vs/workbench/contrib/preferences/browser/preferencesWidgets';
|
||||
import { commonlyUsedData, tocData } from 'vs/workbench/contrib/preferences/browser/settingsLayout';
|
||||
import { AbstractSettingRenderer, ISettingLinkClickEvent, ISettingOverrideClickEvent, resolveConfiguredUntrustedSettings, resolveExtensionsSettings, resolveSettingsTree, SettingsTree, SettingTreeRenderers } from 'vs/workbench/contrib/preferences/browser/settingsTree';
|
||||
import { AbstractSettingRenderer, HeightChangeParams, ISettingLinkClickEvent, ISettingOverrideClickEvent, resolveConfiguredUntrustedSettings, resolveExtensionsSettings, resolveSettingsTree, SettingsTree, SettingTreeRenderers } from 'vs/workbench/contrib/preferences/browser/settingsTree';
|
||||
import { ISettingsEditorViewState, parseQuery, SearchResultIdx, SearchResultModel, SettingsTreeElement, SettingsTreeGroupChild, SettingsTreeGroupElement, SettingsTreeModel, SettingsTreeSettingElement } from 'vs/workbench/contrib/preferences/browser/settingsTreeModels';
|
||||
import { settingsTextInputBorder } from 'vs/workbench/contrib/preferences/browser/settingsWidgets';
|
||||
import { createTOCIterator, TOCTree, TOCTreeModel } from 'vs/workbench/contrib/preferences/browser/tocTree';
|
||||
|
@ -195,7 +195,7 @@ export class SettingsEditor2 extends EditorPane {
|
|||
@IEditorGroupsService protected editorGroupService: IEditorGroupsService,
|
||||
@IUserDataSyncWorkbenchService private readonly userDataSyncWorkbenchService: IUserDataSyncWorkbenchService,
|
||||
@IUserDataAutoSyncEnablementService private readonly userDataAutoSyncEnablementService: IUserDataAutoSyncEnablementService,
|
||||
@IWorkspaceTrustManagementService private readonly workspaceTrustManagementService: IWorkspaceTrustManagementService,
|
||||
@IWorkspaceTrustManagementService private readonly workspaceTrustManagementService: IWorkspaceTrustManagementService
|
||||
) {
|
||||
super(SettingsEditor2.ID, telemetryService, themeService, storageService);
|
||||
this.delayedFilterLogging = new Delayer<void>(1000);
|
||||
|
@ -741,6 +741,14 @@ export class SettingsEditor2 extends EditorPane {
|
|||
|
||||
this.searchWidget.setValue(element.targetKey);
|
||||
}));
|
||||
this._register(this.settingRenderers.onDidChangeSettingHeight((params: HeightChangeParams) => {
|
||||
const { element, height } = params;
|
||||
try {
|
||||
this.settingsTree.updateElementHeight(element, height);
|
||||
} catch (e) {
|
||||
// the element was not found
|
||||
}
|
||||
}));
|
||||
|
||||
this.settingsTree = this._register(this.instantiationService.createInstance(SettingsTree,
|
||||
this.settingsTreeContainer,
|
||||
|
|
|
@ -10,7 +10,7 @@ import { IMouseEvent } from 'vs/base/browser/mouseEvent';
|
|||
import { alert as ariaAlert } from 'vs/base/browser/ui/aria/aria';
|
||||
import { Button } from 'vs/base/browser/ui/button/button';
|
||||
import { Checkbox } from 'vs/base/browser/ui/checkbox/checkbox';
|
||||
import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox';
|
||||
import { IInputOptions, InputBox } from 'vs/base/browser/ui/inputbox/inputBox';
|
||||
import { CachedListVirtualDelegate } from 'vs/base/browser/ui/list/list';
|
||||
import { DefaultStyleController, IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget';
|
||||
import { ISelectOptionItem, SelectBox } from 'vs/base/browser/ui/selectBox/selectBox';
|
||||
|
@ -545,6 +545,7 @@ interface IGroupTitleTemplate extends IDisposableTemplate {
|
|||
|
||||
const SETTINGS_UNTRUSTED_TEMPLATE_ID = 'settings.untrusted.template';
|
||||
const SETTINGS_TEXT_TEMPLATE_ID = 'settings.text.template';
|
||||
const SETTINGS_MULTILINE_TEXT_TEMPLATE_ID = 'settings.multilineText.template';
|
||||
const SETTINGS_NUMBER_TEMPLATE_ID = 'settings.number.template';
|
||||
const SETTINGS_ENUM_TEMPLATE_ID = 'settings.enum.template';
|
||||
const SETTINGS_BOOL_TEMPLATE_ID = 'settings.bool.template';
|
||||
|
@ -600,6 +601,11 @@ function addChildrenToTabOrder(node: Element): void {
|
|||
});
|
||||
}
|
||||
|
||||
export interface HeightChangeParams {
|
||||
element: SettingsTreeElement;
|
||||
height: number;
|
||||
}
|
||||
|
||||
export abstract class AbstractSettingRenderer extends Disposable implements ITreeRenderer<SettingsTreeElement, never, any> {
|
||||
/** To override */
|
||||
abstract get templateId(): string;
|
||||
|
@ -633,6 +639,9 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre
|
|||
private readonly _onDidChangeIgnoredSettings = this._register(new Emitter<void>());
|
||||
readonly onDidChangeIgnoredSettings: Event<void> = this._onDidChangeIgnoredSettings.event;
|
||||
|
||||
protected readonly _onDidChangeSettingHeight = this._register(new Emitter<HeightChangeParams>());
|
||||
readonly onDidChangeSettingHeight: Event<HeightChangeParams> = this._onDidChangeSettingHeight.event;
|
||||
|
||||
constructor(
|
||||
private readonly settingActions: IAction[],
|
||||
private readonly disposableActionFactory: (setting: ISetting) => IAction[],
|
||||
|
@ -1417,14 +1426,19 @@ export class SettingExcludeRenderer extends AbstractSettingRenderer implements I
|
|||
}
|
||||
}
|
||||
|
||||
export class SettingTextRenderer extends AbstractSettingRenderer implements ITreeRenderer<SettingsTreeSettingElement, never, ISettingTextItemTemplate> {
|
||||
templateId = SETTINGS_TEXT_TEMPLATE_ID;
|
||||
abstract class AbstractSettingTextRenderer extends AbstractSettingRenderer implements ITreeRenderer<SettingsTreeSettingElement, never, ISettingTextItemTemplate> {
|
||||
private readonly MULTILINE_MAX_HEIGHT = 150;
|
||||
|
||||
renderTemplate(_container: HTMLElement): ISettingTextItemTemplate {
|
||||
renderTemplate(_container: HTMLElement, useMultiline?: boolean): ISettingTextItemTemplate {
|
||||
const common = this.renderCommonTemplate(null, _container, 'text');
|
||||
const validationErrorMessageElement = DOM.append(common.containerElement, $('.setting-item-validation-message'));
|
||||
|
||||
const inputBox = new InputBox(common.controlElement, this._contextViewService);
|
||||
const inputBoxOptions: IInputOptions = {
|
||||
flexibleHeight: useMultiline,
|
||||
flexibleWidth: false,
|
||||
flexibleMaxHeight: this.MULTILINE_MAX_HEIGHT
|
||||
};
|
||||
const inputBox = new InputBox(common.controlElement, this._contextViewService, inputBoxOptions);
|
||||
common.toDispose.add(inputBox);
|
||||
common.toDispose.add(attachInputBoxStyler(inputBox, this._themeService, {
|
||||
inputBackground: settingsTextInputBackground,
|
||||
|
@ -1441,14 +1455,6 @@ export class SettingTextRenderer extends AbstractSettingRenderer implements ITre
|
|||
inputBox.inputElement.classList.add(AbstractSettingRenderer.CONTROL_CLASS);
|
||||
inputBox.inputElement.tabIndex = 0;
|
||||
|
||||
// TODO@9at8: listWidget filters out all key events from input boxes, so we need to come up with a better way
|
||||
// Disable ArrowUp and ArrowDown behaviour in favor of list navigation
|
||||
common.toDispose.add(DOM.addStandardDisposableListener(inputBox.inputElement, DOM.EventType.KEY_DOWN, e => {
|
||||
if (e.equals(KeyCode.UpArrow) || e.equals(KeyCode.DownArrow)) {
|
||||
e.preventDefault();
|
||||
}
|
||||
}));
|
||||
|
||||
const template: ISettingTextItemTemplate = {
|
||||
...common,
|
||||
inputBox,
|
||||
|
@ -1477,6 +1483,50 @@ export class SettingTextRenderer extends AbstractSettingRenderer implements ITre
|
|||
}
|
||||
}
|
||||
|
||||
export class SettingTextRenderer extends AbstractSettingTextRenderer implements ITreeRenderer<SettingsTreeSettingElement, never, ISettingTextItemTemplate> {
|
||||
templateId = SETTINGS_TEXT_TEMPLATE_ID;
|
||||
|
||||
override renderTemplate(_container: HTMLElement): ISettingTextItemTemplate {
|
||||
const template = super.renderTemplate(_container, false);
|
||||
|
||||
// TODO@9at8: listWidget filters out all key events from input boxes, so we need to come up with a better way
|
||||
// Disable ArrowUp and ArrowDown behaviour in favor of list navigation
|
||||
template.toDispose.add(DOM.addStandardDisposableListener(template.inputBox.inputElement, DOM.EventType.KEY_DOWN, e => {
|
||||
if (e.equals(KeyCode.UpArrow) || e.equals(KeyCode.DownArrow)) {
|
||||
e.preventDefault();
|
||||
}
|
||||
}));
|
||||
|
||||
return template;
|
||||
}
|
||||
}
|
||||
|
||||
export class SettingMultilineTextRenderer extends AbstractSettingTextRenderer implements ITreeRenderer<SettingsTreeSettingElement, never, ISettingTextItemTemplate> {
|
||||
templateId = SETTINGS_MULTILINE_TEXT_TEMPLATE_ID;
|
||||
|
||||
override renderTemplate(_container: HTMLElement): ISettingTextItemTemplate {
|
||||
return super.renderTemplate(_container, true);
|
||||
}
|
||||
|
||||
protected override renderValue(dataElement: SettingsTreeSettingElement, template: ISettingTextItemTemplate, onChange: (value: string) => void) {
|
||||
super.renderValue(dataElement, template, onChange);
|
||||
template.toDispose.add(
|
||||
template.inputBox.onDidHeightChange(e => {
|
||||
const height = template.containerElement.clientHeight;
|
||||
// Don't fire event if height is reported as 0,
|
||||
// which sometimes happens when clicking onto a new setting.
|
||||
if (height) {
|
||||
this._onDidChangeSettingHeight.fire({
|
||||
element: dataElement,
|
||||
height: template.containerElement.clientHeight
|
||||
});
|
||||
}
|
||||
})
|
||||
);
|
||||
template.inputBox.layout();
|
||||
}
|
||||
}
|
||||
|
||||
export class SettingEnumRenderer extends AbstractSettingRenderer implements ITreeRenderer<SettingsTreeSettingElement, never, ISettingEnumItemTemplate> {
|
||||
templateId = SETTINGS_ENUM_TEMPLATE_ID;
|
||||
|
||||
|
@ -1770,6 +1820,8 @@ export class SettingTreeRenderers {
|
|||
|
||||
readonly onDidFocusSetting: Event<SettingsTreeSettingElement>;
|
||||
|
||||
readonly onDidChangeSettingHeight: Event<HeightChangeParams>;
|
||||
|
||||
readonly allRenderers: ITreeRenderer<SettingsTreeElement, never, any>[];
|
||||
|
||||
private readonly settingActions: IAction[];
|
||||
|
@ -1800,6 +1852,7 @@ export class SettingTreeRenderers {
|
|||
this._instantiationService.createInstance(SettingArrayRenderer, this.settingActions, actionFactory),
|
||||
this._instantiationService.createInstance(SettingComplexRenderer, this.settingActions, actionFactory),
|
||||
this._instantiationService.createInstance(SettingTextRenderer, this.settingActions, actionFactory),
|
||||
this._instantiationService.createInstance(SettingMultilineTextRenderer, this.settingActions, actionFactory),
|
||||
this._instantiationService.createInstance(SettingExcludeRenderer, this.settingActions, actionFactory),
|
||||
this._instantiationService.createInstance(SettingEnumRenderer, this.settingActions, actionFactory),
|
||||
this._instantiationService.createInstance(SettingObjectRenderer, this.settingActions, actionFactory),
|
||||
|
@ -1815,6 +1868,7 @@ export class SettingTreeRenderers {
|
|||
this.onDidOpenSettings = Event.any(...settingRenderers.map(r => r.onDidOpenSettings));
|
||||
this.onDidClickSettingLink = Event.any(...settingRenderers.map(r => r.onDidClickSettingLink));
|
||||
this.onDidFocusSetting = Event.any(...settingRenderers.map(r => r.onDidFocusSetting));
|
||||
this.onDidChangeSettingHeight = Event.any(...settingRenderers.map(r => r.onDidChangeSettingHeight));
|
||||
|
||||
this.allRenderers = [
|
||||
...settingRenderers,
|
||||
|
@ -2032,6 +2086,10 @@ class SettingsTreeDelegate extends CachedListVirtualDelegate<SettingsTreeGroupCh
|
|||
return SETTINGS_NUMBER_TEMPLATE_ID;
|
||||
}
|
||||
|
||||
if (element.valueType === SettingValueType.MultilineString) {
|
||||
return SETTINGS_MULTILINE_TEXT_TEMPLATE_ID;
|
||||
}
|
||||
|
||||
if (element.valueType === SettingValueType.String) {
|
||||
return SETTINGS_TEXT_TEMPLATE_ID;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import { FOLDER_SCOPES, WORKSPACE_SCOPES, REMOTE_MACHINE_SCOPES, LOCAL_MACHINE_S
|
|||
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { EditPresentationTypes } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
|
||||
export const ONLINE_SERVICES_SETTING_TAG = 'usesOnlineServices';
|
||||
|
||||
|
@ -227,7 +228,11 @@ export class SettingsTreeSettingElement extends SettingsTreeElement {
|
|||
if (this.setting.enum && (!this.setting.type || settingTypeEnumRenderable(this.setting.type))) {
|
||||
this.valueType = SettingValueType.Enum;
|
||||
} else if (this.setting.type === 'string') {
|
||||
if (this.setting.editPresentation === EditPresentationTypes.Multiline) {
|
||||
this.valueType = SettingValueType.MultilineString;
|
||||
} else {
|
||||
this.valueType = SettingValueType.String;
|
||||
}
|
||||
} else if (isExcludeSetting(this.setting)) {
|
||||
this.valueType = SettingValueType.Exclude;
|
||||
} else if (this.setting.type === 'integer') {
|
||||
|
|
|
@ -11,7 +11,7 @@ import { IJSONSchemaMap, IJSONSchema } from 'vs/base/common/jsonSchema';
|
|||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { localize } from 'vs/nls';
|
||||
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||
import { ConfigurationScope, IConfigurationExtensionInfo } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { ConfigurationScope, EditPresentationTypes, IConfigurationExtensionInfo } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { EditorResolution, IEditorOptions } from 'vs/platform/editor/common/editor';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
|
@ -26,6 +26,7 @@ export enum SettingValueType {
|
|||
Null = 'null',
|
||||
Enum = 'enum',
|
||||
String = 'string',
|
||||
MultilineString = 'multiline-string',
|
||||
Integer = 'integer',
|
||||
Number = 'number',
|
||||
Boolean = 'boolean',
|
||||
|
@ -84,6 +85,7 @@ export interface ISetting {
|
|||
validator?: (value: any) => string | null;
|
||||
enumItemLabels?: string[];
|
||||
allKeysAreBoolean?: boolean;
|
||||
editPresentation?: EditPresentationTypes;
|
||||
}
|
||||
|
||||
export interface IExtensionSetting extends ISetting {
|
||||
|
|
|
@ -669,7 +669,8 @@ export class DefaultSettings extends Disposable {
|
|||
deprecationMessageIsMarkdown: !!prop.markdownDeprecationMessage,
|
||||
validator: createValidator(prop),
|
||||
enumItemLabels: prop.enumItemLabels,
|
||||
allKeysAreBoolean
|
||||
allKeysAreBoolean,
|
||||
editPresentation: prop.editPresentation
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue