提交 a19b523e 编写于 作者: J Jackson Kearl

Add MessageBox-styled validation

上级 da3d2148
...@@ -223,6 +223,10 @@ ...@@ -223,6 +223,10 @@
position: relative; position: relative;
} }
.settings-editor > .settings-body > .settings-tree-container .monaco-tree-row {
overflow: visible; /* so validation messages dont get clipped */
}
.settings-editor > .settings-body > .settings-tree-container .setting-item { .settings-editor > .settings-body > .settings-tree-container .setting-item {
padding-top: 12px; padding-top: 12px;
padding-bottom: 18px; padding-bottom: 18px;
...@@ -262,7 +266,7 @@ ...@@ -262,7 +266,7 @@
opacity: 0.9; opacity: 0.9;
} }
.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-validation-message, .settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-deprecation-message,
.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description { .settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description {
margin-top: 3px; margin-top: 3px;
overflow: hidden; overflow: hidden;
...@@ -274,6 +278,31 @@ ...@@ -274,6 +278,31 @@
transform: translate3d(0px, 0px, 0px); transform: translate3d(0px, 0px, 0px);
} }
.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-deprecation-message {
position: absolute;
}
.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-validation-message {
display: none;
}
.settings-editor > .settings-body > .settings-tree-container .setting-item.invalid-input .setting-item-validation-message {
display: block;
position: absolute;
padding: 5px;
box-sizing: border-box;
margin-top: -1px;
z-index: 1;
}
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-text .setting-item-validation-message {
width: 500px;
}
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-number .setting-item-validation-message {
width: 200px;
}
.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description-markdown * { .settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description-markdown * {
margin: 0px; margin: 0px;
} }
...@@ -294,8 +323,6 @@ ...@@ -294,8 +323,6 @@
} }
.settings-editor > .settings-body > .settings-tree-container .setting-item.is-expanded .setting-item-description, .settings-editor > .settings-body > .settings-tree-container .setting-item.is-expanded .setting-item-description,
.settings-editor > .settings-body > .settings-tree-container .setting-item.is-expanded .setting-item-validation-message,
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-measure-helper .setting-item-validation-message,
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-measure-helper .setting-item-description { .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-measure-helper .setting-item-description {
height: initial; height: initial;
-webkit-line-clamp: initial; -webkit-line-clamp: initial;
......
...@@ -9,7 +9,7 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; ...@@ -9,7 +9,7 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { IMouseEvent } from 'vs/base/browser/mouseEvent'; import { IMouseEvent } from 'vs/base/browser/mouseEvent';
import { Button } from 'vs/base/browser/ui/button/button'; import { Button } from 'vs/base/browser/ui/button/button';
import { Checkbox } from 'vs/base/browser/ui/checkbox/checkbox'; import { Checkbox } from 'vs/base/browser/ui/checkbox/checkbox';
import { InputBox, MessageType, IInputValidator } from 'vs/base/browser/ui/inputbox/inputBox'; import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox';
import { SelectBox } from 'vs/base/browser/ui/selectBox/selectBox'; import { SelectBox } from 'vs/base/browser/ui/selectBox/selectBox';
import * as arrays from 'vs/base/common/arrays'; import * as arrays from 'vs/base/common/arrays';
import { Color, RGBA } from 'vs/base/common/color'; import { Color, RGBA } from 'vs/base/common/color';
...@@ -32,7 +32,7 @@ import { IContextViewService } from 'vs/platform/contextview/browser/contextView ...@@ -32,7 +32,7 @@ import { IContextViewService } from 'vs/platform/contextview/browser/contextView
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IListService, WorkbenchTreeController } from 'vs/platform/list/browser/listService'; import { IListService, WorkbenchTreeController } from 'vs/platform/list/browser/listService';
import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IOpenerService } from 'vs/platform/opener/common/opener';
import { editorBackground, focusBorder, foreground, errorForeground } from 'vs/platform/theme/common/colorRegistry'; import { editorBackground, focusBorder, foreground, errorForeground, inputValidationErrorBackground, inputValidationErrorBorder } from 'vs/platform/theme/common/colorRegistry';
import { attachButtonStyler, attachInputBoxStyler, attachSelectBoxStyler, attachStyler } from 'vs/platform/theme/common/styler'; import { attachButtonStyler, attachInputBoxStyler, attachSelectBoxStyler, attachStyler } from 'vs/platform/theme/common/styler';
import { ICssStyleCollector, ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { ICssStyleCollector, ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { SettingsTarget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets'; import { SettingsTarget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets';
...@@ -504,6 +504,7 @@ interface ISettingBoolItemTemplate extends ISettingItemTemplate<boolean> { ...@@ -504,6 +504,7 @@ interface ISettingBoolItemTemplate extends ISettingItemTemplate<boolean> {
interface ISettingTextItemTemplate extends ISettingItemTemplate<string> { interface ISettingTextItemTemplate extends ISettingItemTemplate<string> {
inputBox: InputBox; inputBox: InputBox;
validationErrorMessageElement: HTMLElement;
} }
type ISettingNumberItemTemplate = ISettingTextItemTemplate; type ISettingNumberItemTemplate = ISettingTextItemTemplate;
...@@ -741,7 +742,7 @@ export class SettingsRenderer implements ITreeRenderer { ...@@ -741,7 +742,7 @@ export class SettingsRenderer implements ITreeRenderer {
const valueElement = DOM.append(container, $('.setting-item-value')); const valueElement = DOM.append(container, $('.setting-item-value'));
const controlElement = DOM.append(valueElement, $('div.setting-item-control')); const controlElement = DOM.append(valueElement, $('div.setting-item-control'));
const deprecationWarningElement = DOM.append(container, $('.setting-item-validation-message')); const deprecationWarningElement = DOM.append(container, $('.setting-item-deprecation-message'));
const toDispose = []; const toDispose = [];
const template: ISettingItemTemplate = { const template: ISettingItemTemplate = {
...@@ -780,6 +781,7 @@ export class SettingsRenderer implements ITreeRenderer { ...@@ -780,6 +781,7 @@ export class SettingsRenderer implements ITreeRenderer {
private renderSettingTextTemplate(tree: ITree, container: HTMLElement, type = 'text'): ISettingTextItemTemplate { private renderSettingTextTemplate(tree: ITree, container: HTMLElement, type = 'text'): ISettingTextItemTemplate {
const common = this.renderCommonTemplate(tree, container, 'text'); const common = this.renderCommonTemplate(tree, container, 'text');
const validationErrorMessageElement = DOM.append(container, $('.setting-item-validation-message'));
const inputBox = new InputBox(common.controlElement, this.contextViewService); const inputBox = new InputBox(common.controlElement, this.contextViewService);
common.toDispose.push(inputBox); common.toDispose.push(inputBox);
...@@ -799,7 +801,8 @@ export class SettingsRenderer implements ITreeRenderer { ...@@ -799,7 +801,8 @@ export class SettingsRenderer implements ITreeRenderer {
const template: ISettingTextItemTemplate = { const template: ISettingTextItemTemplate = {
...common, ...common,
inputBox inputBox,
validationErrorMessageElement
}; };
this.addSettingElementFocusHandler(template); this.addSettingElementFocusHandler(template);
...@@ -809,6 +812,7 @@ export class SettingsRenderer implements ITreeRenderer { ...@@ -809,6 +812,7 @@ export class SettingsRenderer implements ITreeRenderer {
private renderSettingNumberTemplate(tree: ITree, container: HTMLElement): ISettingNumberItemTemplate { private renderSettingNumberTemplate(tree: ITree, container: HTMLElement): ISettingNumberItemTemplate {
const common = this.renderCommonTemplate(tree, container, 'number'); const common = this.renderCommonTemplate(tree, container, 'number');
const validationErrorMessageElement = DOM.append(container, $('.setting-item-validation-message'));
const inputBox = new InputBox(common.controlElement, this.contextViewService); const inputBox = new InputBox(common.controlElement, this.contextViewService);
common.toDispose.push(inputBox); common.toDispose.push(inputBox);
...@@ -828,7 +832,8 @@ export class SettingsRenderer implements ITreeRenderer { ...@@ -828,7 +832,8 @@ export class SettingsRenderer implements ITreeRenderer {
const template: ISettingNumberItemTemplate = { const template: ISettingNumberItemTemplate = {
...common, ...common,
inputBox inputBox,
validationErrorMessageElement
}; };
this.addSettingElementFocusHandler(template); this.addSettingElementFocusHandler(template);
...@@ -850,7 +855,7 @@ export class SettingsRenderer implements ITreeRenderer { ...@@ -850,7 +855,7 @@ export class SettingsRenderer implements ITreeRenderer {
const controlElement = DOM.append(descriptionAndValueElement, $('.setting-item-bool-control')); const controlElement = DOM.append(descriptionAndValueElement, $('.setting-item-bool-control'));
const descriptionElement = DOM.append(descriptionAndValueElement, $('.setting-item-description')); const descriptionElement = DOM.append(descriptionAndValueElement, $('.setting-item-description'));
const deprecationWarningElement = DOM.append(container, $('.setting-item-validation-message')); const deprecationWarningElement = DOM.append(container, $('.setting-item-deprecation-message'));
const toDispose = []; const toDispose = [];
const checkbox = new Checkbox({ actionClassName: 'setting-value-checkbox', isChecked: true, title: '', inputActiveOptionBorder: null }); const checkbox = new Checkbox({ actionClassName: 'setting-value-checkbox', isChecked: true, title: '', inputActiveOptionBorder: null });
...@@ -1204,10 +1209,9 @@ export class SettingsRenderer implements ITreeRenderer { ...@@ -1204,10 +1209,9 @@ export class SettingsRenderer implements ITreeRenderer {
private renderText(dataElement: SettingsTreeSettingElement, template: ISettingTextItemTemplate, onChange: (value: string) => void): void { private renderText(dataElement: SettingsTreeSettingElement, template: ISettingTextItemTemplate, onChange: (value: string) => void): void {
template.onChange = null; template.onChange = null;
template.inputBox.value = dataElement.value; template.inputBox.value = dataElement.value;
template.inputBox.attachValidator(makeValidator(dataElement)); template.onChange = value => { renderValidations(dataElement, template); onChange(value); };
template.inputBox.validate(); // for some reason this is needed on text but not number. TODO: figure out why
template.onChange = value => onChange(value);
renderValidations(dataElement, template);
// Setup and add ARIA attributes // Setup and add ARIA attributes
// Create id and label for control/input element - parent is wrapper div // Create id and label for control/input element - parent is wrapper div
const id = (dataElement.displayCategory + '_' + dataElement.displayLabel).replace(/ /g, '_'); const id = (dataElement.displayCategory + '_' + dataElement.displayLabel).replace(/ /g, '_');
...@@ -1229,13 +1233,13 @@ export class SettingsRenderer implements ITreeRenderer { ...@@ -1229,13 +1233,13 @@ export class SettingsRenderer implements ITreeRenderer {
private renderNumber(dataElement: SettingsTreeSettingElement, template: ISettingTextItemTemplate, onChange: (value: number) => void): void { private renderNumber(dataElement: SettingsTreeSettingElement, template: ISettingTextItemTemplate, onChange: (value: number) => void): void {
const parseFn = dataElement.valueType === 'integer' ? parseInt : parseFloat;
template.onChange = null; template.onChange = null;
template.inputBox.value = dataElement.value; template.inputBox.value = dataElement.value;
template.inputBox.attachValidator(makeValidator(dataElement)); template.onChange = value => { renderValidations(dataElement, template); onChange(parseFn(value)); };
template.onChange = value => onChange(parseFn(value));
const parseFn = dataElement.valueType === 'integer' ? parseInt : parseFloat;
renderValidations(dataElement, template);
// Setup and add ARIA attributes // Setup and add ARIA attributes
// Create id and label for control/input element - parent is wrapper div // Create id and label for control/input element - parent is wrapper div
const id = (dataElement.displayCategory + '_' + dataElement.displayLabel).replace(/ /g, '_'); const id = (dataElement.displayCategory + '_' + dataElement.displayLabel).replace(/ /g, '_');
...@@ -1270,11 +1274,16 @@ export class SettingsRenderer implements ITreeRenderer { ...@@ -1270,11 +1274,16 @@ export class SettingsRenderer implements ITreeRenderer {
} }
} }
function makeValidator(dataElement: SettingsTreeSettingElement): IInputValidator | null { function renderValidations(dataElement: SettingsTreeSettingElement, template: ISettingTextItemTemplate) {
return value => { if (dataElement.setting.validator) {
let message = dataElement.setting.validator(value); let errMsg = dataElement.setting.validator(template.inputBox.value);
return message ? { content: message, type: MessageType.ERROR } : null; if (errMsg) {
}; DOM.addClass(template.containerElement, 'invalid-input');
template.validationErrorMessageElement.innerText = errMsg;
return;
}
}
DOM.removeClass(template.containerElement, 'invalid-input');
} }
function cleanRenderedMarkdown(element: Node): void { function cleanRenderedMarkdown(element: Node): void {
...@@ -1573,7 +1582,18 @@ export class SettingsTree extends NonExpandableOrSelectableTree { ...@@ -1573,7 +1582,18 @@ export class SettingsTree extends NonExpandableOrSelectableTree {
const errorColor = theme.getColor(errorForeground); const errorColor = theme.getColor(errorForeground);
if (errorColor) { if (errorColor) {
collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-validation-message { color: ${errorColor}; }`); collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-deprecation-message { color: ${errorColor}; }`);
}
const invalidInputBackground = theme.getColor(inputValidationErrorBackground);
if (invalidInputBackground) {
collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-validation-message { background-color: ${invalidInputBackground}; }`);
}
const invalidInputBorder = theme.getColor(inputValidationErrorBorder);
if (invalidInputBorder) {
collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-validation-message { border-style:solid; border-width: 1px; border-color: ${invalidInputBorder}; }`);
collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item.invalid-input .setting-item-control .monaco-inputbox.idle { outline-width: 0; border-style:solid; border-width: 1px; border-color: ${invalidInputBorder}; }`);
} }
const headerForegroundColor = theme.getColor(settingsHeaderForeground); const headerForegroundColor = theme.getColor(settingsHeaderForeground);
......
...@@ -555,12 +555,6 @@ export class DefaultSettings extends Disposable { ...@@ -555,12 +555,6 @@ export class DefaultSettings extends Disposable {
if (this.matchesScope(prop)) { if (this.matchesScope(prop)) {
const value = prop.default; const value = prop.default;
const description = (prop.description || prop.markdownDescription || '').split('\n'); const description = (prop.description || prop.markdownDescription || '').split('\n');
if (prop.deprecationMessage) {
description.push(
'',
prop.deprecationMessage,
nls.localize('deprecatedSetting.unstable', "This setting should not be used, and will be removed in a future release."));
}
const overrides = OVERRIDE_PROPERTY_PATTERN.test(key) ? this.parseOverrideSettings(prop.default) : []; const overrides = OVERRIDE_PROPERTY_PATTERN.test(key) ? this.parseOverrideSettings(prop.default) : [];
result.push({ result.push({
key, key,
...@@ -773,7 +767,7 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements ...@@ -773,7 +767,7 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements
} }
private copySetting(setting: ISetting): ISetting { private copySetting(setting: ISetting): ISetting {
return <ISetting>{ return {
description: setting.description, description: setting.description,
type: setting.type, type: setting.type,
enum: setting.enum, enum: setting.enum,
...@@ -783,7 +777,12 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements ...@@ -783,7 +777,12 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements
range: setting.range, range: setting.range,
overrides: [], overrides: [],
overrideOf: setting.overrideOf, overrideOf: setting.overrideOf,
tags: setting.tags tags: setting.tags,
deprecationMessage: setting.deprecationMessage,
keyRange: undefined,
valueRange: undefined,
descriptionIsMarkdown: undefined,
descriptionRanges: undefined
}; };
} }
...@@ -910,8 +909,7 @@ class SettingsContentBuilder { ...@@ -910,8 +909,7 @@ class SettingsContentBuilder {
setting.descriptionRanges = []; setting.descriptionRanges = [];
const descriptionPreValue = indent + '// '; const descriptionPreValue = indent + '// ';
for (let line of setting.description) { for (let line of (setting.deprecationMessage ? [setting.deprecationMessage] : setting.description)) {
// Remove setting link tag
line = fixSettingLink(line); line = fixSettingLink(line);
this._contentByLines.push(descriptionPreValue + line); this._contentByLines.push(descriptionPreValue + line);
...@@ -964,7 +962,7 @@ class SettingsContentBuilder { ...@@ -964,7 +962,7 @@ class SettingsContentBuilder {
} }
} }
function createValidator(prop: IConfigurationPropertySchema): (value: any) => string { function createValidator(prop: IConfigurationPropertySchema): ((value: any) => string) | null {
let exclusiveMax: number | undefined; let exclusiveMax: number | undefined;
let exclusiveMin: number | undefined; let exclusiveMin: number | undefined;
...@@ -1040,6 +1038,9 @@ function createValidator(prop: IConfigurationPropertySchema): (value: any) => st ...@@ -1040,6 +1038,9 @@ function createValidator(prop: IConfigurationPropertySchema): (value: any) => st
}, },
].filter(validation => validation.enabled); ].filter(validation => validation.enabled);
if ((prop.type === 'number' || prop.type === 'integer') && numericValidations.length === 0) { return null; }
if (prop.type === 'string' && stringValidations.length === 0) { return null; }
return value => { return value => {
let errors = []; let errors = [];
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册