Simplify button logic and enable button updates for custom dialogs (#1283)

This commit is contained in:
Matt Irvine
2018-04-27 16:29:18 -07:00
committed by GitHub
parent 886717d330
commit 24e8c20511
11 changed files with 119 additions and 72 deletions

View File

@@ -15,11 +15,21 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
const defaultOptions: IModalOptions = { hasBackButton: true, isWide: false }; const defaultOptions: IModalOptions = { hasBackButton: true, isWide: false };
export class CustomDialogService { export class CustomDialogService {
private _dialogModals = new Map<Dialog, DialogModal>();
constructor( @IInstantiationService private _instantiationService: IInstantiationService) { } constructor( @IInstantiationService private _instantiationService: IInstantiationService) { }
public showDialog(dialog: Dialog, options?: IModalOptions): void { public showDialog(dialog: Dialog, options?: IModalOptions): void {
let optionsDialog = this._instantiationService.createInstance(DialogModal, dialog, 'CustomDialog', options || defaultOptions); let dialogModal = this._instantiationService.createInstance(DialogModal, dialog, 'CustomDialog', options || defaultOptions);
optionsDialog.render(); this._dialogModals.set(dialog, dialogModal);
optionsDialog.open(); dialogModal.render();
dialogModal.open();
}
public closeDialog(dialog: Dialog): void {
let dialogModal = this._dialogModals.get(dialog);
if (dialogModal) {
dialogModal.cancel();
}
} }
} }

View File

@@ -61,25 +61,33 @@ export class DialogModal extends Modal {
if (this._dialog.customButtons) { if (this._dialog.customButtons) {
this._dialog.customButtons.forEach(button => { this._dialog.customButtons.forEach(button => {
let buttonElement = this.addDialogButton(button); let buttonElement = this.addDialogButton(button);
buttonElement.enabled = button.enabled; this.updateButtonElement(buttonElement, button);
attachButtonStyler(buttonElement, this._themeService);
}); });
} }
this._cancelButton = this.addDialogButton(this._dialog.cancelButton, () => this.cancel()); this._cancelButton = this.addDialogButton(this._dialog.cancelButton, () => this.cancel());
this._cancelButton.enabled = this._dialog.cancelButton.enabled; this.updateButtonElement(this._cancelButton, this._dialog.cancelButton);
this._doneButton = this.addDialogButton(this._dialog.okButton, () => this.done()); this._doneButton = this.addDialogButton(this._dialog.okButton, () => this.done());
this._doneButton.enabled = this._dialog.okButton.enabled; this.updateButtonElement(this._doneButton, this._dialog.okButton);
attachButtonStyler(this._cancelButton, this._themeService);
attachButtonStyler(this._doneButton, this._themeService);
} }
private addDialogButton(button: DialogButton, onSelect: () => void = () => undefined): Button { private addDialogButton(button: DialogButton, onSelect: () => void = () => undefined): Button {
let buttonElement = this.addFooterButton(button.label, onSelect); let buttonElement = this.addFooterButton(button.label, onSelect);
buttonElement.enabled = button.enabled;
button.registerClickEvent(buttonElement.onDidClick); button.registerClickEvent(buttonElement.onDidClick);
button.onUpdate(() => {
this.updateButtonElement(buttonElement, button);
});
attachButtonStyler(buttonElement, this._themeService);
return buttonElement; return buttonElement;
} }
private updateButtonElement(buttonElement: Button, dialogButton: DialogButton) {
buttonElement.label = dialogButton.label;
buttonElement.enabled = dialogButton.enabled;
dialogButton.hidden ? buttonElement.element.classList.add('dialogModal-hidden') : buttonElement.element.classList.remove('dialogModal-hidden');
}
protected renderBody(container: HTMLElement): void { protected renderBody(container: HTMLElement): void {
new Builder(container).div({ class: 'dialogModal-body' }, (bodyBuilder) => { new Builder(container).div({ class: 'dialogModal-body' }, (bodyBuilder) => {
this._body = bodyBuilder.getHTMLElement(); this._body = bodyBuilder.getHTMLElement();

View File

@@ -92,4 +92,9 @@ export class DialogPane extends Disposable implements IThemable {
this._body.style.backgroundColor = styles.dialogBodyBackground ? styles.dialogBodyBackground.toString() : undefined; this._body.style.backgroundColor = styles.dialogBodyBackground ? styles.dialogBodyBackground.toString() : undefined;
this._body.style.color = styles.dialogForeground ? styles.dialogForeground.toString() : undefined; this._body.style.color = styles.dialogForeground ? styles.dialogForeground.toString() : undefined;
} }
public dispose() {
super.dispose();
this._moduleRef.destroy();
}
} }

View File

@@ -17,8 +17,6 @@ export class DialogTab implements sqlops.window.modelviewdialog.DialogTab {
this.content = content; this.content = content;
} }
} }
public updateContent(): void { }
} }
export class Dialog implements sqlops.window.modelviewdialog.Dialog { export class Dialog implements sqlops.window.modelviewdialog.Dialog {
@@ -35,21 +33,48 @@ export class Dialog implements sqlops.window.modelviewdialog.Dialog {
this.content = content; this.content = content;
} }
} }
public open(): void { }
public close(): void { }
public updateContent(): void { }
} }
export class DialogButton implements sqlops.window.modelviewdialog.Button { export class DialogButton implements sqlops.window.modelviewdialog.Button {
public label: string; private _label: string;
public enabled: boolean; private _enabled: boolean;
private _hidden: boolean;
private _onClick: Emitter<void> = new Emitter<void>(); private _onClick: Emitter<void> = new Emitter<void>();
public readonly onClick: Event<void> = this._onClick.event; public readonly onClick: Event<void> = this._onClick.event;
private _onUpdate: Emitter<void> = new Emitter<void>();
public readonly onUpdate: Event<void> = this._onUpdate.event;
constructor(label: string, enabled: boolean) { constructor(label: string, enabled: boolean) {
this.label = label; this._label = label;
this.enabled = enabled; this._enabled = enabled;
this._hidden = false;
}
public get label(): string {
return this._label;
}
public set label(label: string) {
this._label = label;
this._onUpdate.fire();
}
public get enabled(): boolean {
return this._enabled;
}
public set enabled(enabled: boolean) {
this._enabled = enabled;
this._onUpdate.fire();
}
public get hidden(): boolean {
return this._hidden;
}
public set hidden(hidden: boolean) {
this._hidden = hidden;
this._onUpdate.fire();
} }
/** /**

View File

@@ -23,6 +23,6 @@
height: 100%; height: 100%;
} }
.dialogModal-pane.dialogModal-hidden { .dialogModal-hidden {
display: none; display: none;
} }

View File

@@ -287,6 +287,16 @@ declare module 'sqlops' {
*/ */
export function createButton(label: string): Button; export function createButton(label: string): Button;
/**
* Opens the given dialog if it is not already open
*/
export function openDialog(dialog: Dialog): void;
/**
* Closes the given dialog if it is open
*/
export function closeDialog(dialog: Dialog): void;
// Model view dialog classes // Model view dialog classes
export interface Dialog { export interface Dialog {
/** /**
@@ -297,7 +307,6 @@ declare module 'sqlops' {
/** /**
* The content of the dialog. If multiple tabs are given they will be displayed with tabs * The content of the dialog. If multiple tabs are given they will be displayed with tabs
* If a string is given, it should be the ID of the dialog's model view content * If a string is given, it should be the ID of the dialog's model view content
* TODO mairvine 4/18/18: use a model view content type
*/ */
content: string | DialogTab[], content: string | DialogTab[],
@@ -315,51 +324,35 @@ declare module 'sqlops' {
* Any additional buttons that should be displayed * Any additional buttons that should be displayed
*/ */
customButtons: Button[]; customButtons: Button[];
/**
* Opens the dialog
*/
open(): void;
/**
* Closes the dialog
*/
close(): void;
/**
* Updates the dialog on screen to reflect changes to the buttons or content
*/
updateContent(): void;
} }
export interface DialogTab { export interface DialogTab {
/** /**
* The title of the tab * The title of the tab
*/ */
title: string, title: string;
/** /**
* A string giving the ID of the tab's model view content * A string giving the ID of the tab's model view content
* TODO mairvine 4/18/18: use a model view content type
*/ */
content: string; content: string;
/**
* Updates the dialog on screen to reflect changes to the content
*/
updateContent(): void;
} }
export interface Button { export interface Button {
/** /**
* The label displayed on the button * The label displayed on the button
*/ */
label: string, label: string;
/** /**
* Whether the button is enabled * Whether the button is enabled
*/ */
enabled: boolean, enabled: boolean;
/**
* Whether the button is hidden
*/
hidden: boolean;
/** /**
* Raised when the button is clicked * Raised when the button is clicked

View File

@@ -115,4 +115,5 @@ export interface IModelViewTabDetails {
export interface IModelViewButtonDetails { export interface IModelViewButtonDetails {
label: string; label: string;
enabled: boolean; enabled: boolean;
hidden: boolean;
} }

View File

@@ -26,18 +26,6 @@ class DialogImpl implements sqlops.window.modelviewdialog.Dialog {
this.okButton = this._extHostModelViewDialog.createButton(nls.localize('dialogOkLabel', 'Done')); this.okButton = this._extHostModelViewDialog.createButton(nls.localize('dialogOkLabel', 'Done'));
this.cancelButton = this._extHostModelViewDialog.createButton(nls.localize('dialogCancelLabel', 'Cancel')); this.cancelButton = this._extHostModelViewDialog.createButton(nls.localize('dialogCancelLabel', 'Cancel'));
} }
public open(): void {
this._extHostModelViewDialog.open(this);
}
public close(): void {
this._extHostModelViewDialog.close(this);
}
public updateContent(): void {
this._extHostModelViewDialog.updateDialogContent(this);
}
} }
class TabImpl implements sqlops.window.modelviewdialog.DialogTab { class TabImpl implements sqlops.window.modelviewdialog.DialogTab {
@@ -45,21 +33,19 @@ class TabImpl implements sqlops.window.modelviewdialog.DialogTab {
public content: string; public content: string;
constructor(private _extHostModelViewDialog: ExtHostModelViewDialog) { } constructor(private _extHostModelViewDialog: ExtHostModelViewDialog) { }
public updateContent(): void {
this._extHostModelViewDialog.updateTabContent(this);
}
} }
class ButtonImpl implements sqlops.window.modelviewdialog.Button { class ButtonImpl implements sqlops.window.modelviewdialog.Button {
private _label: string; private _label: string;
private _enabled: boolean; private _enabled: boolean;
private _hidden: boolean;
private _onClick = new Emitter<void>(); private _onClick = new Emitter<void>();
public onClick = this._onClick.event; public onClick = this._onClick.event;
constructor(private _extHostModelViewDialog: ExtHostModelViewDialog) { constructor(private _extHostModelViewDialog: ExtHostModelViewDialog) {
this._enabled = true; this._enabled = true;
this._hidden = false;
} }
public get label(): string { public get label(): string {
@@ -80,6 +66,15 @@ class ButtonImpl implements sqlops.window.modelviewdialog.Button {
this._extHostModelViewDialog.updateButton(this); this._extHostModelViewDialog.updateButton(this);
} }
public get hidden(): boolean {
return this._hidden;
}
public set hidden(hidden: boolean) {
this._hidden = hidden;
this._extHostModelViewDialog.updateButton(this);
}
public getOnClickCallback(): () => void { public getOnClickCallback(): () => void {
return () => this._onClick.fire(); return () => this._onClick.fire();
} }
@@ -154,7 +149,7 @@ export class ExtHostModelViewDialog implements ExtHostModelViewDialogShape {
let handle = this.getDialogHandle(dialog); let handle = this.getDialogHandle(dialog);
let tabs = dialog.content; let tabs = dialog.content;
if (tabs && typeof tabs !== 'string') { if (tabs && typeof tabs !== 'string') {
tabs.forEach(tab => tab.updateContent()); tabs.forEach(tab => this.updateTabContent(tab));
} }
if (dialog.customButtons) { if (dialog.customButtons) {
dialog.customButtons.forEach(button => this.updateButton(button)); dialog.customButtons.forEach(button => this.updateButton(button));
@@ -182,7 +177,8 @@ export class ExtHostModelViewDialog implements ExtHostModelViewDialogShape {
let handle = this.getButtonHandle(button); let handle = this.getButtonHandle(button);
this._proxy.$setButtonDetails(handle, { this._proxy.$setButtonDetails(handle, {
label: button.label, label: button.label,
enabled: button.enabled enabled: button.enabled,
hidden: button.hidden
}); });
} }

View File

@@ -22,7 +22,7 @@ export class MainThreadModelViewDialog implements MainThreadModelViewDialogShape
constructor( constructor(
context: IExtHostContext, context: IExtHostContext,
@IInstantiationService instatiationService: IInstantiationService @IInstantiationService instatiationService: IInstantiationService,
) { ) {
this._proxy = context.getProxy(SqlExtHostContext.ExtHostModelViewDialog); this._proxy = context.getProxy(SqlExtHostContext.ExtHostModelViewDialog);
this._dialogService = new CustomDialogService(instatiationService); this._dialogService = new CustomDialogService(instatiationService);
@@ -40,7 +40,7 @@ export class MainThreadModelViewDialog implements MainThreadModelViewDialogShape
public $close(handle: number): Thenable<void> { public $close(handle: number): Thenable<void> {
let dialog = this.getDialog(handle); let dialog = this.getDialog(handle);
dialog.close(); this._dialogService.closeDialog(dialog);
return Promise.resolve(); return Promise.resolve();
} }
@@ -66,7 +66,6 @@ export class MainThreadModelViewDialog implements MainThreadModelViewDialogShape
dialog.customButtons = details.customButtons.map(buttonHandle => this.getButton(buttonHandle)); dialog.customButtons = details.customButtons.map(buttonHandle => this.getButton(buttonHandle));
} }
dialog.updateContent();
return Promise.resolve(); return Promise.resolve();
} }
@@ -79,8 +78,6 @@ export class MainThreadModelViewDialog implements MainThreadModelViewDialogShape
tab.title = details.title; tab.title = details.title;
tab.content = details.content; tab.content = details.content;
tab.updateContent();
return Promise.resolve(); return Promise.resolve();
} }
@@ -88,11 +85,13 @@ export class MainThreadModelViewDialog implements MainThreadModelViewDialogShape
let button = this._buttons.get(handle); let button = this._buttons.get(handle);
if (!button) { if (!button) {
button = new DialogButton(details.label, details.enabled); button = new DialogButton(details.label, details.enabled);
button.hidden = details.hidden;
button.onClick(() => this.onButtonClick(handle)); button.onClick(() => this.onButtonClick(handle));
this._buttons.set(handle, button); this._buttons.set(handle, button);
} else { } else {
button.label = details.label; button.label = details.label;
button.enabled = details.enabled; button.enabled = details.enabled;
button.hidden = details.hidden;
} }
return Promise.resolve(); return Promise.resolve();

View File

@@ -293,6 +293,12 @@ export function createApiFactory(
}, },
createButton(label: string): sqlops.window.modelviewdialog.Button { createButton(label: string): sqlops.window.modelviewdialog.Button {
return extHostModelViewDialog.createButton(label); return extHostModelViewDialog.createButton(label);
},
openDialog(dialog: sqlops.window.modelviewdialog.Dialog) {
return extHostModelViewDialog.open(dialog);
},
closeDialog(dialog: sqlops.window.modelviewdialog.Dialog) {
return extHostModelViewDialog.close(dialog);
} }
}; };

View File

@@ -55,19 +55,23 @@ suite('MainThreadModelViewDialog Tests', () => {
// Set up the dialog details // Set up the dialog details
button1Details = { button1Details = {
label: 'button1', label: 'button1',
enabled: false enabled: false,
hidden: false
}; };
button2Details = { button2Details = {
label: 'button2', label: 'button2',
enabled: true enabled: true,
hidden: false
}; };
okButtonDetails = { okButtonDetails = {
label: 'ok_label', label: 'ok_label',
enabled: true enabled: true,
hidden: false
}; };
cancelButtonDetails = { cancelButtonDetails = {
label: 'cancel_label', label: 'cancel_label',
enabled: true enabled: true,
hidden: false
}; };
tab1Details = { tab1Details = {
title: 'tab1', title: 'tab1',