mirror of
https://github.com/lxsang/antos-frontend.git
synced 2025-04-08 09:36:45 +02:00
1133 lines
38 KiB
TypeScript
1133 lines
38 KiB
TypeScript
// Copyright 2017-2018 Xuan Sang LE <xsang.le AT gmail DOT com>
|
|
|
|
// AnTOS Web desktop is is licensed under the GNU General Public
|
|
// License v3.0, see the LICENCE file for more information
|
|
|
|
// This program is free software: you can redistribute it and/or
|
|
// modify it under the terms of the GNU General Public License as
|
|
// published by the Free Software Foundation, either version 3 of
|
|
// the License, or (at your option) any later version.
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
// General Public License for more details.
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
//along with this program. If not, see https://www.gnu.org/licenses/.
|
|
namespace OS {
|
|
export namespace GUI {
|
|
/**
|
|
* the SubWindow class is the abstract prototype of all
|
|
* modal windows or dialogs definition in AntOS
|
|
*
|
|
* @export
|
|
* @abstract
|
|
* @class SubWindow
|
|
* @extends {BaseModel}
|
|
*/
|
|
export abstract class SubWindow extends BaseModel {
|
|
/**
|
|
* Placeholder indicates whether the sub window is in
|
|
* modal mode. This value is reserver for future use
|
|
*
|
|
* @type {boolean}
|
|
* @memberof SubWindow
|
|
*/
|
|
modal: boolean;
|
|
|
|
/**
|
|
* Reference to the parent of the current sub-window
|
|
*
|
|
* @type {(BaseModel | typeof GUI)}
|
|
* @memberof SubWindow
|
|
*/
|
|
parent: BaseModel | typeof GUI;
|
|
|
|
/**
|
|
*Creates an instance of SubWindow.
|
|
* @param {string} name SubWindow (class) name
|
|
* @memberof SubWindow
|
|
*/
|
|
constructor(name: string) {
|
|
super(name, null);
|
|
this.parent = undefined;
|
|
this.modal = false;
|
|
}
|
|
|
|
/**
|
|
* Exit the sub-window
|
|
*
|
|
* @returns {void}
|
|
* @memberof SubWindow
|
|
*/
|
|
quit(): void {
|
|
const evt = new BaseEvent("exit", false);
|
|
this.onexit(evt);
|
|
if (!evt.prevent) {
|
|
delete this._observable;
|
|
if (this.scheme) {
|
|
$(this.scheme).remove();
|
|
}
|
|
if (this.dialog) {
|
|
return this.dialog.quit();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Init the sub-window, this function is called
|
|
* on creation of the sub-window object. It is used
|
|
* to render the sub-window UI.
|
|
*
|
|
* Need to be implemented by subclasses
|
|
*
|
|
* @abstract
|
|
* @memberof SubWindow
|
|
*/
|
|
abstract init(): void;
|
|
|
|
/**
|
|
* Main entry point after rendering of the sub-window
|
|
*
|
|
* @abstract
|
|
* @memberof SubWindow
|
|
*/
|
|
abstract main(): void;
|
|
|
|
/**
|
|
* Return the parent meta-data of the current
|
|
* sub-window
|
|
*
|
|
* @returns {API.PackageMetaType}
|
|
* @memberof SubWindow
|
|
*/
|
|
meta(): API.PackageMetaType {
|
|
const p = this.parent as BaseModel;
|
|
if (p && p.meta) {
|
|
return p.meta();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Show the sub-window
|
|
*
|
|
* @memberof SubWindow
|
|
*/
|
|
show(): void {
|
|
this.trigger("focus");
|
|
$(this.scheme).css("z-index", GUI.zindex + 2);
|
|
}
|
|
|
|
/**
|
|
* Hide the sub-window
|
|
*
|
|
* @returns {void}
|
|
* @memberof SubWindow
|
|
*/
|
|
hide(): void {
|
|
return this.trigger("hide");
|
|
}
|
|
}
|
|
|
|
SubWindow.type = ModelType.SubWindow;
|
|
|
|
/**
|
|
* Abstract prototype of all AntOS dialogs widget
|
|
*
|
|
* @export
|
|
* @abstract
|
|
* @class BaseDialog
|
|
* @extends {SubWindow}
|
|
*/
|
|
export abstract class BaseDialog extends SubWindow {
|
|
/**
|
|
* Placeholder for the dialog callback on exit
|
|
*
|
|
* @memberof BaseDialog
|
|
*/
|
|
handle: (d: any) => void;
|
|
|
|
/**
|
|
* Placeholder of the dialog input data
|
|
*
|
|
* @type {GenericObject<any>}
|
|
* @memberof BaseDialog
|
|
*/
|
|
data: GenericObject<any>;
|
|
|
|
/**
|
|
*Creates an instance of BaseDialog.
|
|
* @param {string} name Dialog (class) name
|
|
* @memberof BaseDialog
|
|
*/
|
|
constructor(name: string) {
|
|
super(name);
|
|
this.handle = undefined;
|
|
}
|
|
|
|
/**
|
|
* Function called when dialog exits
|
|
*
|
|
* @protected
|
|
* @param {BaseEvent} e
|
|
* @returns {void}
|
|
* @memberof BaseDialog
|
|
*/
|
|
protected onexit(e: BaseEvent): void {
|
|
if (this.parent) {
|
|
return (this.parent.dialog = undefined);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A basic dialog renders a dialog widget using the UI
|
|
* scheme provided in it constructor or defined in its
|
|
* class variable `scheme`
|
|
*
|
|
* @export
|
|
* @class BasicDialog
|
|
* @extends {BaseDialog}
|
|
*/
|
|
export class BasicDialog extends BaseDialog {
|
|
/**
|
|
* Placeholder for the UI scheme to be rendered. This can
|
|
* be either the string definition of the scheme or
|
|
* the VFS file handle of the scheme file
|
|
*
|
|
* @private
|
|
* @type {(string | OS.API.VFS.BaseFileHandle)}
|
|
* @memberof BasicDialog
|
|
*/
|
|
private markup: string | OS.API.VFS.BaseFileHandle;
|
|
|
|
/**
|
|
* If the `markup` variable is not provided, then
|
|
* the [[init]] function will find the scheme definition
|
|
* in this class variable
|
|
*
|
|
* @static
|
|
* @type {string}
|
|
* @memberof BasicDialog
|
|
*/
|
|
static scheme: string;
|
|
|
|
/**
|
|
*Creates an instance of BasicDialog.
|
|
* @param {string} name dialog name
|
|
* @param {(string | OS.API.VFS.BaseFileHandle)} [markup] UI scheme definition
|
|
* @memberof BasicDialog
|
|
*/
|
|
constructor(
|
|
name: string,
|
|
markup?: string | OS.API.VFS.BaseFileHandle
|
|
) {
|
|
super(name);
|
|
this.markup = markup;
|
|
}
|
|
|
|
/**
|
|
* Render the dialog using the UI scheme provided by either
|
|
* the `markup` instance variable or the `scheme` class variable
|
|
*
|
|
* @returns {void}
|
|
* @memberof BasicDialog
|
|
*/
|
|
init(): void {
|
|
if (this.markup) {
|
|
if (typeof this.markup === "string") {
|
|
return GUI.htmlToScheme(this.markup, this, this.host);
|
|
} else {
|
|
// a file handle
|
|
return this.render(this.markup.path);
|
|
}
|
|
} else if (
|
|
GUI.dialogs[this.name] &&
|
|
GUI.dialogs[this.name].scheme
|
|
) {
|
|
const html: string = GUI.dialogs[this.name].scheme;
|
|
return GUI.htmlToScheme(html.trim(), this, this.host);
|
|
} else {
|
|
this.error(__("Unable to find dialog scheme"));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Main entry point for the dialog
|
|
*
|
|
* @memberof BasicDialog
|
|
*/
|
|
main(): void {
|
|
const win = this.scheme as tag.WindowTag;
|
|
if (this.data && this.data.title) {
|
|
win.apptitle = this.data.title;
|
|
}
|
|
win.resizable = false;
|
|
win.minimizable = false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The namespace `dialogs` is dedicated to all Dialog definition
|
|
* in AntOS
|
|
*/
|
|
export namespace dialogs {
|
|
/**
|
|
* Simple prompt dialog to get user input text.
|
|
* The input data of the dialog:
|
|
*
|
|
* ```typescript
|
|
* {
|
|
* title: string, // window title
|
|
* label: string, // label text
|
|
* value: string, // user input text
|
|
* type: string // input type: text or password
|
|
* }
|
|
* ```
|
|
*
|
|
* The data passing from the dialog to the callback function is
|
|
* in the string text of the user input value
|
|
*
|
|
* @export
|
|
* @class PromptDialog
|
|
* @extends {BasicDialog}
|
|
*/
|
|
export class PromptDialog extends BasicDialog {
|
|
/**
|
|
*Creates an instance of PromptDialog.
|
|
* @memberof PromptDialog
|
|
*/
|
|
constructor() {
|
|
super("PromptDialog");
|
|
}
|
|
|
|
/**
|
|
* Main entry point
|
|
*
|
|
* @memberof PromptDialog
|
|
*/
|
|
main(): void {
|
|
super.main();
|
|
const $input = $(this.find("txtInput"));
|
|
if (this.data && this.data.label) {
|
|
(this.find(
|
|
"lbl"
|
|
) as tag.LabelTag).text = this.data.label;
|
|
}
|
|
if (this.data && this.data.value) {
|
|
$input.val(this.data.value);
|
|
}
|
|
|
|
if (this.data && this.data.type)
|
|
{
|
|
($input[0] as HTMLInputElement).type = this.data.type
|
|
}
|
|
|
|
(this.find("btnOk") as tag.ButtonTag).onbtclick = (e) => {
|
|
if (this.handle) {
|
|
this.handle($input.val());
|
|
}
|
|
return this.quit();
|
|
};
|
|
|
|
(this.find("btnCancel") as tag.ButtonTag).onbtclick = (
|
|
e
|
|
) => {
|
|
return this.quit();
|
|
};
|
|
|
|
$input.keyup((e) => {
|
|
if (e.which !== 13) {
|
|
return;
|
|
}
|
|
if (this.handle) {
|
|
this.handle($input.val());
|
|
}
|
|
return this.quit();
|
|
});
|
|
|
|
$input.focus();
|
|
}
|
|
}
|
|
/**
|
|
* Scheme definition of the Prompt dialog
|
|
*/
|
|
PromptDialog.scheme = `\
|
|
<afx-app-window width='200' height='150' apptitle = "Prompt">
|
|
<afx-vbox>
|
|
<afx-hbox>
|
|
<div data-width = "10" />
|
|
<afx-vbox>
|
|
<div data-height="10" />
|
|
<afx-label data-id = "lbl" />
|
|
<input type = "text" data-id= "txtInput" />
|
|
<div data-height="10" />
|
|
<afx-hbox data-height="30">
|
|
<div />
|
|
<afx-button data-id = "btnOk" text = "__(Ok)" data-width = "40" />
|
|
<afx-button data-id = "btnCancel" text = "__(Cancel)" data-width = "50" />
|
|
</afx-hbox>
|
|
</afx-vbox>
|
|
<div data-width = "10" />
|
|
</afx-hbox>
|
|
</afx-vbox>
|
|
</afx-app-window>\
|
|
`;
|
|
|
|
/**
|
|
* A text dialog is similar to a [[PromptDialog]] nut allows
|
|
* user to input multi-line text.
|
|
*
|
|
* Refer to [[PromptDialog]] for the definition of input and callback data
|
|
* of the dialog
|
|
*
|
|
* @export
|
|
* @class TextDialog
|
|
* @extends {BasicDialog}
|
|
*/
|
|
export class TextDialog extends BasicDialog {
|
|
/**
|
|
*Creates an instance of TextDialog.
|
|
* @memberof TextDialog
|
|
*/
|
|
constructor() {
|
|
super("TextDialog");
|
|
}
|
|
|
|
/**
|
|
* Main entry point
|
|
*
|
|
* @memberof TextDialog
|
|
*/
|
|
main(): void {
|
|
super.main();
|
|
const $input = $(this.find("txtInput"));
|
|
if (this.data && this.data.value) {
|
|
$input.val(this.data.value);
|
|
}
|
|
|
|
(this.find("btnOk") as tag.ButtonTag).onbtclick = (e) => {
|
|
const value = $input.val();
|
|
if (!value || value === "") {
|
|
return;
|
|
}
|
|
if (this.handle) {
|
|
this.handle(value);
|
|
}
|
|
return this.quit();
|
|
};
|
|
|
|
(this.find("btnCancel") as tag.ButtonTag).onbtclick = (
|
|
e
|
|
): void => {
|
|
return this.quit();
|
|
};
|
|
|
|
$input.focus();
|
|
}
|
|
}
|
|
/**
|
|
* Scheme definition
|
|
*/
|
|
TextDialog.scheme = `\
|
|
<afx-app-window data-id = "TextDialog" width='400' height='300'>
|
|
<afx-vbox>
|
|
<afx-hbox>
|
|
<div data-width = "10" />
|
|
<afx-vbox>
|
|
<div data-height="10" />
|
|
<textarea data-id= "txtInput" />
|
|
<div data-height="10" />
|
|
<afx-hbox data-height="30">
|
|
<div />
|
|
<afx-button data-id = "btnOk" text = "__(Ok)" data-width = "40" />
|
|
<afx-button data-id = "btnCancel" text = "__(Cancel)" data-width = "50" />
|
|
</afx-hbox>
|
|
</afx-vbox>
|
|
<div data-width = "10" />
|
|
</afx-hbox>
|
|
</afx-vbox>
|
|
</afx-app-window>\
|
|
`;
|
|
|
|
/**
|
|
* A Calendar dialog allows user to select a date
|
|
*
|
|
* Input data:
|
|
*
|
|
* ```typescript
|
|
* {
|
|
* title: string // window title
|
|
* }
|
|
* ```
|
|
*
|
|
* @export
|
|
* @class CalendarDialog
|
|
* @extends {BasicDialog}
|
|
*/
|
|
export class CalendarDialog extends BasicDialog {
|
|
/**
|
|
* Creates an instance of CalendarDialog.
|
|
*
|
|
* Callback data: a Date object represent the selected date
|
|
*
|
|
*
|
|
* @memberof CalendarDialog
|
|
*/
|
|
constructor() {
|
|
super("CalendarDialog");
|
|
}
|
|
|
|
/**
|
|
*
|
|
*
|
|
* @memberof CalendarDialog
|
|
*/
|
|
main(): void {
|
|
super.main();
|
|
(this.find("btnOk") as tag.ButtonTag).onbtclick = (
|
|
e
|
|
): void => {
|
|
const date = (this.find("cal") as tag.CalendarTag)
|
|
.selectedDate;
|
|
if (!date) {
|
|
return this.notify(__("Please select a day"));
|
|
}
|
|
if (this.handle) {
|
|
this.handle(date);
|
|
}
|
|
return this.quit();
|
|
};
|
|
|
|
(this.find("btnCancel") as tag.ButtonTag).onbtclick = (
|
|
e
|
|
): void => {
|
|
return this.quit();
|
|
};
|
|
}
|
|
}
|
|
/**
|
|
* Scheme definition
|
|
*/
|
|
CalendarDialog.scheme = `\
|
|
<afx-app-window width='300' height='230' apptitle = "Calendar" >
|
|
<afx-vbox>
|
|
<afx-hbox>
|
|
<div data-width = "10" />
|
|
<afx-vbox>
|
|
<div data-height="10" />
|
|
<afx-calendar-view data-id = "cal" />
|
|
<div data-height="10" />
|
|
<afx-hbox data-height="30">
|
|
<div />
|
|
<afx-button data-id = "btnOk" text = "__(Ok)" data-width = "40" />
|
|
<afx-button data-id = "btnCancel" text = "__(Cancel)" data-width = "50" />
|
|
</afx-hbox>
|
|
<div data-height="10" />
|
|
</afx-vbox>
|
|
<div data-width = "10" />
|
|
</afx-hbox>
|
|
</afx-vbox>
|
|
</afx-app-window>\
|
|
`;
|
|
|
|
/**
|
|
* Color picker dialog
|
|
*
|
|
* Input data:
|
|
*
|
|
* ```typescript
|
|
* {
|
|
* title: string // window title
|
|
* }
|
|
* ```
|
|
* Callback data: [[ColorType]] object
|
|
*
|
|
* @export
|
|
* @class ColorPickerDialog
|
|
* @extends {BasicDialog}
|
|
*/
|
|
export class ColorPickerDialog extends BasicDialog {
|
|
/**
|
|
*Creates an instance of ColorPickerDialog.
|
|
* @memberof ColorPickerDialog
|
|
*/
|
|
constructor() {
|
|
super("ColorPickerDialog");
|
|
}
|
|
|
|
/**
|
|
*
|
|
*
|
|
* @memberof ColorPickerDialog
|
|
*/
|
|
main(): void {
|
|
super.main();
|
|
(this.find("btnOk") as tag.ButtonTag).onbtclick = (
|
|
e
|
|
): void => {
|
|
const color = (this.find(
|
|
"cpicker"
|
|
) as tag.ColorPickerTag).selectedColor;
|
|
if (!color) {
|
|
return this.notify(__("Please select color"));
|
|
}
|
|
if (this.handle) {
|
|
this.handle(color);
|
|
}
|
|
return this.quit();
|
|
};
|
|
|
|
(this.find("btnCancel") as tag.ButtonTag).onbtclick = (
|
|
e
|
|
): void => {
|
|
return this.quit();
|
|
};
|
|
}
|
|
}
|
|
/**
|
|
* Scheme definition
|
|
*/
|
|
ColorPickerDialog.scheme = `\
|
|
<afx-app-window width='320' height='250' apptitle = "Color picker" >
|
|
<afx-vbox>
|
|
<afx-hbox>
|
|
<div data-width = "10" />
|
|
<afx-vbox>
|
|
<div data-height="10" />
|
|
<afx-color-picker data-id = "cpicker" />
|
|
<div data-height="10" />
|
|
<afx-hbox data-height="30">
|
|
<div />
|
|
<afx-button data-id = "btnOk" text = "__(Ok)" data-width = "40" />
|
|
<afx-button data-id = "btnCancel" text = "__(Cancel)" data-width = "50" />
|
|
</afx-hbox>
|
|
<div data-height="10" />
|
|
</afx-vbox>
|
|
<div data-width = "10" />
|
|
</afx-hbox>
|
|
</afx-vbox>
|
|
</afx-app-window>\
|
|
`;
|
|
|
|
/**
|
|
* Show key-value pair of the input object
|
|
*
|
|
* Input data:
|
|
*
|
|
* ```typescript
|
|
* {
|
|
* title: string, // window title
|
|
* [propName:string]: any
|
|
* }
|
|
* ```
|
|
*
|
|
* No data for callback
|
|
*
|
|
* @export
|
|
* @class InfoDialog
|
|
* @extends {BasicDialog}
|
|
*/
|
|
export class InfoDialog extends BasicDialog {
|
|
/**
|
|
*Creates an instance of InfoDialog.
|
|
* @memberof InfoDialog
|
|
*/
|
|
constructor() {
|
|
super("InfoDialog");
|
|
}
|
|
|
|
/**
|
|
*
|
|
*
|
|
* @memberof InfoDialog
|
|
*/
|
|
main(): void {
|
|
super.main();
|
|
const rows = [];
|
|
if (this.data && this.data.title) {
|
|
delete this.data.title;
|
|
}
|
|
for (let k in this.data) {
|
|
const v = this.data[k];
|
|
rows.push([{ text: k }, { text: v }]);
|
|
}
|
|
const grid = this.find("grid") as tag.GridViewTag;
|
|
grid.header = [
|
|
{ text: __("Name"), width: 70 },
|
|
{ text: __("Value") },
|
|
];
|
|
grid.rows = rows;
|
|
(this.find("btnCancel") as tag.ButtonTag).onbtclick = (
|
|
e
|
|
): void => {
|
|
return this.quit();
|
|
};
|
|
}
|
|
}
|
|
/**
|
|
* Scheme definition
|
|
*/
|
|
InfoDialog.scheme = `\
|
|
<afx-app-window width='250' height='300' apptitle = "Info" >
|
|
<afx-vbox>
|
|
<afx-hbox>
|
|
<div data-width = "10" />
|
|
<afx-vbox>
|
|
<div data-height="10" />
|
|
<afx-grid-view data-id = "grid" />
|
|
<div data-height="10" />
|
|
<afx-hbox data-height="30">
|
|
<div />
|
|
<afx-button data-id = "btnCancel" text = "__(Cancel)" data-width = "50" />
|
|
</afx-hbox>
|
|
<div data-height="10" />
|
|
</afx-vbox>
|
|
<div data-width = "10" />
|
|
</afx-hbox>
|
|
</afx-vbox>
|
|
</afx-app-window>\
|
|
`;
|
|
|
|
/**
|
|
* A simple confirm dialog
|
|
*
|
|
* Input data:
|
|
*
|
|
* ```typescript
|
|
* {
|
|
* title: string, // window title
|
|
* icon?: string, // label icon
|
|
* iconclass?: string, // label iconclass
|
|
* text: string // label text
|
|
* }
|
|
* ```
|
|
*
|
|
* Callback data: `boolean`
|
|
*
|
|
* @export
|
|
* @class YesNoDialog
|
|
* @extends {BasicDialog}
|
|
*/
|
|
export class YesNoDialog extends BasicDialog {
|
|
/**
|
|
*Creates an instance of YesNoDialog.
|
|
* @memberof YesNoDialog
|
|
*/
|
|
constructor() {
|
|
super("YesNoDialog");
|
|
}
|
|
|
|
/**
|
|
* Main entry point
|
|
*
|
|
* @memberof YesNoDialog
|
|
*/
|
|
main(): void {
|
|
super.main();
|
|
if (this.data) {
|
|
(this.find("lbl") as tag.LabelTag).set(this.data);
|
|
}
|
|
(this.find("btnYes") as tag.ButtonTag).onbtclick = (
|
|
e
|
|
): void => {
|
|
if (this.handle) {
|
|
this.handle(true);
|
|
}
|
|
return this.quit();
|
|
};
|
|
(this.find("btnNo") as tag.ButtonTag).onbtclick = (
|
|
e
|
|
): void => {
|
|
if (this.handle) {
|
|
this.handle(false);
|
|
}
|
|
return this.quit();
|
|
};
|
|
}
|
|
}
|
|
/**
|
|
* Scheme definition
|
|
*/
|
|
YesNoDialog.scheme = `\
|
|
<afx-app-window width='200' height='150' apptitle = "Prompt">
|
|
<afx-vbox>
|
|
<afx-hbox>
|
|
<div data-width = "10" />
|
|
<afx-vbox>
|
|
<div data-height="10" />
|
|
<afx-label data-id = "lbl" />
|
|
<div data-height="10" />
|
|
<afx-hbox data-height="30">
|
|
<div />
|
|
<afx-button data-id = "btnYes" text = "__(Yes)" data-width = "40" />
|
|
<afx-button data-id = "btnNo" text = "__(No)" data-width = "40" />
|
|
</afx-hbox>
|
|
</afx-vbox>
|
|
<div data-width = "10" />
|
|
</afx-hbox>
|
|
</afx-vbox>
|
|
</afx-app-window>\
|
|
`;
|
|
|
|
/**
|
|
* A selection dialog provide user with a list of options to
|
|
* select.
|
|
*
|
|
* Input data:
|
|
*
|
|
* ```typescript
|
|
* {
|
|
* title: string, // window title
|
|
* data:
|
|
* {
|
|
* text: string,
|
|
* [propName:string]: any
|
|
* } [] // list data
|
|
* ```
|
|
*
|
|
* Callback data: the selected data in the input list
|
|
*
|
|
* @export
|
|
* @class SelectionDialog
|
|
* @extends {BasicDialog}
|
|
*/
|
|
export class SelectionDialog extends BasicDialog {
|
|
/**
|
|
*Creates an instance of SelectionDialog.
|
|
* @memberof SelectionDialog
|
|
*/
|
|
constructor() {
|
|
super("SelectionDialog");
|
|
}
|
|
|
|
/**
|
|
* Main entry
|
|
*
|
|
* @memberof SelectionDialog
|
|
*/
|
|
main(): void {
|
|
super.main();
|
|
const listview = this.find("list") as tag.ListViewTag;
|
|
if (this.data && this.data.data) {
|
|
listview.data = this.data.data;
|
|
}
|
|
const fn = (e: TagEventType<GUI.tag.ListItemEventData>) => {
|
|
const data = listview.selectedItem;
|
|
if (!data) {
|
|
return this.notify(__("Please select an item"));
|
|
}
|
|
if (this.handle) {
|
|
this.handle(data.data);
|
|
}
|
|
return this.quit();
|
|
};
|
|
listview.onlistdbclick = fn;
|
|
(this.find("btnOk") as tag.ButtonTag).onbtclick = fn;
|
|
|
|
(this.find("btnCancel") as tag.ButtonTag).onbtclick = (
|
|
e
|
|
): void => {
|
|
return this.quit();
|
|
};
|
|
}
|
|
}
|
|
/**
|
|
* Scheme definition
|
|
*/
|
|
SelectionDialog.scheme = `\
|
|
<afx-app-window width='250' height='300' apptitle = "Selection">
|
|
<afx-vbox>
|
|
<afx-hbox>
|
|
<div data-width = "10" />
|
|
<afx-vbox>
|
|
<div data-height="10" />
|
|
<afx-list-view data-id = "list" />
|
|
<div data-height="10" />
|
|
<afx-hbox data-height="30">
|
|
<div />
|
|
<afx-button data-id = "btnOk" text = "__(Ok)" data-width = "40" />
|
|
<afx-button data-id = "btnCancel" text = "__(Cancel)" data-width = "50" />
|
|
</afx-hbox>
|
|
</afx-vbox>
|
|
<div data-width = "10" />
|
|
</afx-hbox>
|
|
</afx-vbox>
|
|
</afx-app-window>\
|
|
`;
|
|
|
|
/**
|
|
* An About dialog is dedicated to show the parent
|
|
* application meta-data
|
|
*
|
|
* Input data: no
|
|
*
|
|
* Callback data: no
|
|
*
|
|
* @export
|
|
* @class AboutDialog
|
|
* @extends {BasicDialog}
|
|
*/
|
|
export class AboutDialog extends BasicDialog {
|
|
/**
|
|
*Creates an instance of AboutDialog.
|
|
* @memberof AboutDialog
|
|
*/
|
|
constructor() {
|
|
super("AboutDialog");
|
|
}
|
|
|
|
/**
|
|
* Main entry point
|
|
*
|
|
* @returns {void}
|
|
* @memberof AboutDialog
|
|
*/
|
|
main(): void {
|
|
super.main();
|
|
const mt = this.meta();
|
|
(this.scheme as tag.WindowTag).apptitle = __(
|
|
"About: {0}",
|
|
mt.name
|
|
);
|
|
(this.find("mylabel") as tag.LabelTag).set({
|
|
icon: mt.icon,
|
|
iconclass: mt.iconclass,
|
|
text: `${mt.name}(v${mt.version})`,
|
|
});
|
|
$(this.find("mydesc")).html(mt.description);
|
|
// grid data for author info
|
|
if (!mt.info) {
|
|
return;
|
|
}
|
|
const rows = [];
|
|
for (let k in mt.info) {
|
|
const v = mt.info[k];
|
|
rows.push([{ text: k }, { text: v }]);
|
|
}
|
|
const grid = this.find("mygrid") as tag.GridViewTag;
|
|
grid.header = [{ text: "", width: 100 }, { text: "" }];
|
|
grid.rows = rows;
|
|
(this.find("btnCancel") as tag.ButtonTag).onbtclick = (
|
|
e
|
|
): void => {
|
|
return this.quit();
|
|
};
|
|
}
|
|
}
|
|
/**
|
|
* Scheme definition
|
|
*/
|
|
AboutDialog.scheme = `\
|
|
<afx-app-window data-id = 'about-window' width='300' height='200'>
|
|
<afx-vbox>
|
|
<div style="text-align:center; margin-top:10px;" data-height="50">
|
|
<h3 style = "margin:0;padding:0;">
|
|
<afx-label data-id = 'mylabel'></afx-label>
|
|
</h3>
|
|
<i><p style = "margin:0; padding:0" data-id = 'mydesc'></p></i>
|
|
</div>
|
|
<afx-hbox>
|
|
<div data-width="10"></div>
|
|
<afx-grid-view data-id = 'mygrid'></afx-grid-view>
|
|
</afx-hbox>
|
|
|
|
<afx-hbox data-height="30">
|
|
<div />
|
|
<afx-button data-id = "btnCancel" text = "__(Cancel)" data-width = "60" />
|
|
</afx-hbox>
|
|
<div data-height = "10"/>
|
|
</afx-vbox>
|
|
</afx-app-window>\
|
|
`;
|
|
|
|
/**
|
|
* File dialog allows user to select a file/folder
|
|
*
|
|
* Input data:
|
|
*
|
|
* ```typescript
|
|
* {
|
|
* title: string, // window title
|
|
* root?: string, // the root path folder of the file view
|
|
* type?: "file"|"dir"|"app", // file type to be selected
|
|
* mimes?: string[], // mime types of file to be selected
|
|
* hidden?: boolean, // show/hide hidden file
|
|
* file?: string // file name
|
|
*
|
|
* }
|
|
* ```
|
|
*
|
|
* Callback data:
|
|
*
|
|
* ```typescript
|
|
* {
|
|
* file: string, // selected file path
|
|
* name: string // user input file name
|
|
* }
|
|
* ```
|
|
*
|
|
* @export
|
|
* @class FileDialog
|
|
* @extends {BasicDialog}
|
|
*/
|
|
export class FileDialog extends BasicDialog {
|
|
/**
|
|
*Creates an instance of FileDialog.
|
|
* @memberof FileDialog
|
|
*/
|
|
constructor() {
|
|
super("FileDialog");
|
|
}
|
|
|
|
/**
|
|
*
|
|
*
|
|
* @returns {void}
|
|
* @memberof FileDialog
|
|
*/
|
|
main(): void {
|
|
super.main();
|
|
const fileview = this.find("fileview") as tag.FileViewTag;
|
|
const location = this.find("location") as tag.ListViewTag;
|
|
const filename = this.find("filename") as HTMLInputElement;
|
|
fileview.fetch = (path: string) =>
|
|
new Promise(function (resolve, reject) {
|
|
if (!path) {
|
|
return resolve();
|
|
}
|
|
return path
|
|
.asFileHandle()
|
|
.read()
|
|
.then(function (d) {
|
|
if (d.error) {
|
|
return reject(d);
|
|
}
|
|
return resolve(d.result);
|
|
})
|
|
.catch((e: Error): void => reject(__e(e)));
|
|
});
|
|
const setroot = async (path: string) => {
|
|
const d = await path.asFileHandle().read();
|
|
if (d.error) {
|
|
return this.error(
|
|
__("Resource not found: {0}", path)
|
|
);
|
|
}
|
|
return (fileview.path = path);
|
|
};
|
|
|
|
if (!this.data || !this.data.root) {
|
|
location.onlistselect = function (e): void {
|
|
if (!e || !e.data.item) {
|
|
return;
|
|
}
|
|
setroot(e.data.item.data.path);
|
|
};
|
|
location.data = this.systemsetting.VFS.mountpoints.filter(
|
|
(i) => i.type !== "app"
|
|
);
|
|
if (location.selectedItem === undefined) {
|
|
location.selected = 0;
|
|
}
|
|
} else {
|
|
$(location).hide();
|
|
this.trigger("resize");
|
|
setroot(this.data.root);
|
|
}
|
|
fileview.onfileselect = function (e) {
|
|
if (e.data.type === "file") {
|
|
return $(filename).val(e.data.filename);
|
|
}
|
|
};
|
|
(this.find("bt-ok") as tag.ButtonTag).onbtclick = (e) => {
|
|
const f = fileview.selectedFile;
|
|
if (!f) {
|
|
return this.notify(
|
|
__("Please select a file/fofler")
|
|
);
|
|
}
|
|
if (
|
|
this.data &&
|
|
this.data.type &&
|
|
this.data.type !== f.type
|
|
) {
|
|
return this.notify(
|
|
__("Please select {0} only", this.data.type)
|
|
);
|
|
}
|
|
if (this.data && this.data.mimes) {
|
|
//verify the mime
|
|
let m = false;
|
|
if (f.mime) {
|
|
for (let v of this.data.mimes) {
|
|
if (
|
|
f.mime.match(
|
|
new RegExp(v as string, "g")
|
|
)
|
|
) {
|
|
m = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!m) {
|
|
return this.notify(
|
|
__(
|
|
"Only {0} could be selected",
|
|
this.data.mimes.join(",")
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
const name = $(filename).val();
|
|
if (this.handle) {
|
|
this.handle({ file: f, name });
|
|
}
|
|
return this.quit();
|
|
};
|
|
|
|
(this.find("bt-cancel") as tag.ButtonTag).onbtclick = (
|
|
e
|
|
) => {
|
|
return this.quit();
|
|
};
|
|
|
|
if (this.data && this.data.file) {
|
|
$(filename)
|
|
.css("display", "block")
|
|
.val(this.data.file.basename || "Untitled");
|
|
this.trigger("resize");
|
|
}
|
|
if (this.data && this.data.hidden) {
|
|
return (fileview.showhidden = this.data.hidden);
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* Scheme definition
|
|
*/
|
|
FileDialog.scheme = `\
|
|
<afx-app-window width='400' height='300'>
|
|
<afx-hbox>
|
|
<afx-list-view data-id = "location" dropdown = "false" data-width = "120"></afx-list-view>
|
|
<afx-vbox>
|
|
<afx-file-view data-id = "fileview" view="tree" status = "false"></afx-file-view>
|
|
<input data-height = '26' type = "text" data-id = "filename" style="margin-left:5px; margin-right:5px;display:none;" />
|
|
<afx-hbox data-height = '30'>
|
|
<div style=' text-align:right;'>
|
|
<afx-button data-id = "bt-ok" text = "__(Ok)"></afx-button>
|
|
<afx-button data-id = "bt-cancel" text = "__(Cancel)"></afx-button>
|
|
</div>
|
|
<div data-width="5"></div>
|
|
</afx-hbox>
|
|
</afx-vbox>
|
|
</afx-hbox>
|
|
</afx-app-window>\
|
|
`;
|
|
}
|
|
}
|
|
}
|