mirror of
https://github.com/lxsang/antos-frontend.git
synced 2025-04-08 09:36:45 +02:00
- Some minor bug fix - Major change: allow split view in CodePad, make CodePad API portable so that it is easy to use another editor other than ACE in the futures (such as monaco editor)
1139 lines
38 KiB
TypeScript
1139 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);
|
|
if (!evt.prevent) {
|
|
delete this._observable;
|
|
if (this.scheme) {
|
|
$(this.scheme).remove();
|
|
}
|
|
if (this.dialog) {
|
|
return this.dialog.quit();
|
|
}
|
|
this.onexit(evt);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
if (this.dialog) {
|
|
this.dialog.show();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
}
|
|
if(this.data && this.data.disable)
|
|
{
|
|
$input.prop('disabled', true);
|
|
}
|
|
(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(undefined);
|
|
}
|
|
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>\
|
|
`;
|
|
}
|
|
}
|
|
}
|