User a custom tag to manage the desktop instead of GUI

This commit is contained in:
Dany LE 2021-11-26 00:03:01 +01:00
parent 0624f421f7
commit a6d725ea71
11 changed files with 416 additions and 196 deletions

View File

@ -37,7 +37,8 @@ tags = dist/core/tags/tag.js \
dist/core/tags/FileViewTag.js \ dist/core/tags/FileViewTag.js \
dist/core/tags/OverlayTag.js \ dist/core/tags/OverlayTag.js \
dist/core/tags/AppDockTag.js \ dist/core/tags/AppDockTag.js \
dist/core/tags/SystemPanelTag.js dist/core/tags/SystemPanelTag.js \
dist/core/tags/DesktopTag.js
javascripts= dist/core/core.js \ javascripts= dist/core/core.js \
dist/core/settings.js \ dist/core/settings.js \

106
d.ts/antos.d.ts vendored
View File

@ -710,6 +710,20 @@ declare namespace OS {
* @param {boolean} force force to clear the system theme before applying the new one * @param {boolean} force force to clear the system theme before applying the new one
*/ */
function loadTheme(name: string, force: boolean): void; function loadTheme(name: string, force: boolean): void;
/**
* Get the system dock tag
*
* @export
* @return {*} {GUI.tag.AppDockTag}
*/
function systemDock(): GUI.tag.AppDockTag;
/**
* Get the current virtual desktop
*
* @export
* @return {*} {GUI.tag.DesktopTag}
*/
function desktop(): GUI.tag.DesktopTag;
/** /**
* Open a system dialog. * Open a system dialog.
* *
@ -4463,10 +4477,10 @@ declare namespace OS {
* The HTML element ID of the virtual desktop * The HTML element ID of the virtual desktop
* *
* @protected * @protected
* @type {string} * @type {HTMLElement}
* @memberof BaseModel * @memberof BaseModel
*/ */
protected host: string; protected host: HTMLElement;
/** /**
* The process number of the current model. * The process number of the current model.
* For sub-window this number is the number * For sub-window this number is the number
@ -4702,7 +4716,6 @@ declare namespace OS {
/** /**
* trigger a local event * trigger a local event
* *
* @protected
* @param {string} e event name * @param {string} e event name
* @param {*} [d] event data * @param {*} [d] event data
* @returns {void} * @returns {void}
@ -5526,6 +5539,89 @@ declare namespace OS {
} }
} }
} }
declare namespace OS {
namespace GUI {
namespace tag {
/**
* Meta tag that represents the virtual desktop environment.
* In near future, we may have multiple virtual desktop environments.
* Each desktop environment has a simple file manager and a window
* manager that render the window in a specific order.
*
* @export
* @class DesktopTag
* @extends {FloatListTag}
*/
class DesktopTag extends FloatListTag {
/**
* internal handle to the desktop file location
*
* @private
* @type {API.VFS.BaseFileHandle}
* @memberof DesktopTag
*/
private file;
/**
* local observer that detect if a new child element is
* added or removed
*
* @private
* @type {MutationObserver}
* @memberof DesktopTag
*/
private observer;
/**
* Internal list of the current opened window
*
* @private
* @type {Set<WindowTag>}
* @memberof DesktopTag
*/
private window_list;
/**
* Creates an instance of DesktopTag.
* @memberof DesktopTag
*/
constructor();
/**
* Mount the virtual desktop to the DOM tree
*
* @protected
* @memberof DesktopTag
*/
protected mount(): void;
/**
* Display all files and folders in the specific desktop location
*
* @return {*} {Promise<any>}
* @memberof DesktopTag
*/
refresh(): Promise<any>;
/**
* Remove this element from its parent
*
* @memberof DesktopTag
*/
remove(): void;
/**
* Active a window above all other windows
*
* @private
* @param {WindowTag} win
* @memberof DesktopTag
*/
private selectWindow;
/**
* Render all windows in order from bottom to top
*
* @private
* @memberof DesktopTag
*/
private render;
}
}
}
}
declare namespace OS { declare namespace OS {
namespace GUI { namespace GUI {
namespace tag { namespace tag {
@ -7988,10 +8084,6 @@ declare namespace OS {
* Tag event callback type * Tag event callback type
*/ */
type TagEventCallback<T> = (e: TagEventType<T>) => void; type TagEventCallback<T> = (e: TagEventType<T>) => void;
/**
* Top most element z index value, start by 10
*/
var zindex: number;
/** /**
* Base abstract class for tag implementation, any AFX tag should be * Base abstract class for tag implementation, any AFX tag should be
* subclass of this class * subclass of this class

Binary file not shown.

View File

@ -109,6 +109,7 @@ namespace OS {
): void => { ): void => {
return this.trigger("menuselect", d); return this.trigger("menuselect", d);
}; };
this.trigger("focused", undefined);
if (this.dialog) { if (this.dialog) {
return this.dialog.show(); return this.dialog.show();
} }

View File

@ -116,7 +116,7 @@ namespace OS {
*/ */
show(): void { show(): void {
this.trigger("focus"); this.trigger("focus");
$(this.scheme).css("z-index", GUI.zindex + 2); this.trigger("focused", undefined);
if (this.dialog) { if (this.dialog) {
this.dialog.show(); this.dialog.show();
} }

View File

@ -193,10 +193,10 @@ namespace OS {
* The HTML element ID of the virtual desktop * The HTML element ID of the virtual desktop
* *
* @protected * @protected
* @type {string} * @type {HTMLElement}
* @memberof BaseModel * @memberof BaseModel
*/ */
protected host: string; protected host: HTMLElement;
/** /**
* The process number of the current model. * The process number of the current model.
@ -294,7 +294,7 @@ namespace OS {
this._gui = GUI; this._gui = GUI;
this.systemsetting = setting; this.systemsetting = setting;
this.on("exit", () => this.quit(false)); this.on("exit", () => this.quit(false));
this.host = this._gui.workspace; this.host = this._gui.desktop();
this.dialog = undefined; this.dialog = undefined;
} }
@ -504,7 +504,6 @@ namespace OS {
/** /**
* trigger a local event * trigger a local event
* *
* @protected
* @param {string} e event name * @param {string} e event name
* @param {*} [d] event data * @param {*} [d] event data
* @returns {void} * @returns {void}

View File

@ -119,12 +119,12 @@ namespace OS {
app: BaseModel, app: BaseModel,
parent: Element | string parent: Element | string
): void { ): void {
const scheme = $.parseHTML(html); const scheme = $.parseHTML(html)[0];
if (app.scheme) { if (app.scheme) {
$(app.scheme).remove(); $(app.scheme).remove();
} }
$(parent as GenericObject<any>).append(scheme); (parent as HTMLElement).append(scheme);
app.scheme = scheme[0] as HTMLElement; app.scheme = scheme as HTMLElement;
app.scheme.uify(app.observable, true); app.scheme.uify(app.observable, true);
app.main(); app.main();
app.show(); app.show();
@ -182,6 +182,28 @@ namespace OS {
$("head link#ostheme").attr("href", path); $("head link#ostheme").attr("href", path);
} }
/**
* Get the system dock tag
*
* @export
* @return {*} {GUI.tag.AppDockTag}
*/
export function systemDock(): GUI.tag.AppDockTag
{
return $("#sysdock")[0] as tag.AppDockTag;
}
/**
* Get the current virtual desktop
*
* @export
* @return {*} {GUI.tag.DesktopTag}
*/
export function desktop(): GUI.tag.DesktopTag
{
return $(workspace)[0] as tag.DesktopTag;
}
/** /**
* Open a system dialog. * Open a system dialog.
* *
@ -452,25 +474,19 @@ namespace OS {
const srv = arr[1]; const srv = arr[1];
const app = arr[0]; const app = arr[0];
try { try {
if (application[srv]) { if (!application[srv]) {
const d = await OS.PM.createProcess(
srv,
application[srv]
);
return resolve(d);
} else {
await loadApp(app); await loadApp(app);
if (!application[srv]) { if (!application[srv]) {
return reject( return reject(
API.throwe(__("Service not found: {0}", ph)) API.throwe(__("Service not found: {0}", ph))
); );
} }
const d_1 = await PM.createProcess( }
const d = await PM.createProcess(
srv, srv,
application[srv] application[srv]
); );
return resolve(d_1); return resolve(d);
}
} }
catch (e) { catch (e) {
return reject(__e(e)); return reject(__e(e));
@ -811,50 +827,6 @@ namespace OS {
.css("left", left + "px"); .css("left", left + "px");
} }
/**
* Refresh the content of the virtual desktop
*
* @param {tag.FloatListTag} desktop
*/
function dkfetch(desktop: tag.FloatListTag): void {
const file = setting.desktop.path.asFileHandle();
const fn = () =>
file.read().then(function (d) {
if (d.error) {
return announcer.osfail(d.error, API.throwe(d.error));
}
const items = [];
$.each(d.result, function (i, v) {
if (
v.filename[0] === "." &&
!setting.desktop.showhidden
) {
return;
}
v.text = v.filename;
//v.text = v.text.substring(0,9) + "..." ifv.text.length > 10
v.iconclass = v.type;
return items.push(v);
});
desktop.data = items;
return desktop.calibrate();
});
file.onready()
.then(() => fn())
.catch(async function (e) {
// try to create the path
console.log(`${file.path} not found`);
const name = file.basename;
try {
const r = await file.parent().asFileHandle().mk(name);
return API.throwe("OS.VFS");
} catch (e_1) {
return announcer.osfail(e_1.toString(), e_1);
}
});
}
/** /**
* Init the virtual desktop on boot: * Init the virtual desktop on boot:
* *
@ -927,119 +899,8 @@ namespace OS {
e e
); );
}); });
const fp = setting.desktop.path.asFileHandle();
// desktop default file manager
const desktop = $(workspace)[0] as tag.FloatListTag;
desktop.onready = function (e: tag.FloatListTag) {
e.observable = OS.announcer.observable;
window.onresize = function () {
announcer.trigger("desktopresize", undefined);
return e.calibrate();
};
desktop.onlistselect = function (
d: TagEventType<tag.ListItemEventData>
) {
($("#sysdock")[0] as tag.AppDockTag).selectedApp = null;
};
desktop.onlistdbclick = function (
d: TagEventType<tag.ListItemEventData>
) {
($("#sysdock")[0] as tag.AppDockTag).selectedApp = null;
const it = desktop.selectedItem;
return openWith(it.data as AppArgumentsType);
};
//($ "#workingenv").on "click", (e) ->
// desktop[0].set "selected", -1
$(desktop).on("click", function (e) {
let el: any = $(e.target).closest("afx-app-window")[0];
if (el) {
return;
}
el = $(e.target).parent();
if (!(el.length > 0)) {
return;
}
el = el.parent();
if (!(el.length > 0)) {
return;
}
if (el[0] !== desktop) {
return;
}
desktop.unselect();
($("#sysdock")[0] as tag.AppDockTag).selectedApp = null;
});
desktop.contextmenuHandle = function (e, m) {
if (e.target.tagName.toUpperCase() === "UL") {
desktop.unselect();
}
($("#sysdock")[0] as tag.AppDockTag).selectedApp = null;
let menu = [
{ text: __("Open"), dataid: "desktop-open" },
{ text: __("Refresh"), dataid: "desktop-refresh" },
];
menu = menu.concat(
(() => {
const result = [];
for (let k in setting.desktop.menu) {
const v = setting.desktop.menu[k];
result.push(v);
}
return result;
})()
);
m.items = menu;
m.onmenuselect = function (
evt: TagEventType<tag.MenuEventData>
) {
if (!evt.data || !evt.data.item) return;
const item = evt.data.item.data;
switch (item.dataid) {
case "desktop-open":
var it = desktop.selectedItem;
if (it) {
return openWith(
it.data as AppArgumentsType
);
}
let arg = setting.desktop.path.asFileHandle() as AppArgumentsType;
arg.mime = "dir";
arg.type = "dir";
return openWith(arg);
case "desktop-refresh":
return dkfetch(desktop);
default:
if (item.app) {
return launch(item.app, item.args);
}
}
};
return m.show(e);
};
dkfetch(desktop);
announcer.observable.on("VFS", function (d: API.AnnouncementDataType<API.VFS.BaseFileHandle>) {
if (["read", "publish", "download"].includes(d.message as string)) {
return;
}
if (
d.u_data.hash() === fp.hash() ||
d.u_data.parent().hash() === fp.hash()
) {
return dkfetch(desktop);
}
});
return announcer.ostrigger("desktoploaded", undefined);
};
// mount it // mount it
desktop.uify(undefined); desktop().uify(undefined);
} }
/** /**
@ -1048,7 +909,7 @@ namespace OS {
* @export * @export
*/ */
export function refreshDesktop(): void { export function refreshDesktop(): void {
dkfetch($(workspace)[0] as tag.FloatListTag); desktop().refresh();
} }
/** /**
@ -1184,7 +1045,7 @@ namespace OS {
<afx-sys-panel id = "syspanel"></afx-sys-panel> <afx-sys-panel id = "syspanel"></afx-sys-panel>
<div id = "workspace"> <div id = "workspace">
<afx-apps-dock id="sysdock"></afx-apps-dock> <afx-apps-dock id="sysdock"></afx-apps-dock>
<afx-float-list id = "desktop" dir="vertical" ></afx-float-list> <afx-desktop id = "desktop" dir="vertical" ></afx-desktop>
</div> </div>
<afx-menu id="contextmenu" data-id="contextmenu" context="true" style="display:none;"></afx-menu> <afx-menu id="contextmenu" data-id="contextmenu" context="true" style="display:none;"></afx-menu>
<afx-label id="systooltip" data-id="systooltip" style="display:none;position:absolute;"></afx-label> <afx-label id="systooltip" data-id="systooltip" style="display:none;position:absolute;"></afx-label>

273
src/core/tags/DesktopTag.ts Normal file
View File

@ -0,0 +1,273 @@
namespace OS {
export namespace GUI {
export namespace tag {
/**
* Meta tag that represents the virtual desktop environment.
* In near future, we may have multiple virtual desktop environments.
* Each desktop environment has a simple file manager and a window
* manager that render the window in a specific order.
*
* @export
* @class DesktopTag
* @extends {FloatListTag}
*/
export class DesktopTag extends FloatListTag {
/**
* internal handle to the desktop file location
*
* @private
* @type {API.VFS.BaseFileHandle}
* @memberof DesktopTag
*/
private file: API.VFS.BaseFileHandle;
/**
* local observer that detect if a new child element is
* added or removed
*
* @private
* @type {MutationObserver}
* @memberof DesktopTag
*/
private observer: MutationObserver;
/**
* Internal list of the current opened window
*
* @private
* @type {Set<WindowTag>}
* @memberof DesktopTag
*/
private window_list: Set<WindowTag>;
/**
* Creates an instance of DesktopTag.
* @memberof DesktopTag
*/
constructor() {
super();
this.observer = undefined;
this.window_list = new Set<WindowTag>();
}
/**
* Mount the virtual desktop to the DOM tree
*
* @protected
* @memberof DesktopTag
*/
protected mount(): void {
if(this.observer)
{
this.observer.disconnect();
this.observer = undefined;
}
this.observer = new MutationObserver((mutations_list) =>{
mutations_list.forEach((mutation) => {
mutation.removedNodes.forEach((removed_node) =>{
if((removed_node as HTMLElement).tagName === "AFX-APP-WINDOW")
{
this.window_list.delete(removed_node as WindowTag);
}
});
mutation.addedNodes.forEach((added_node) =>{
if((added_node as HTMLElement).tagName === "AFX-APP-WINDOW")
{
this.selectWindow(added_node as WindowTag);
}
});
});
});
this.observer.observe(this, { subtree: false, childList: true });
this.onready = (_) => {
this.observable = OS.announcer.observable;
window.onresize = () => {
announcer.trigger("desktopresize", undefined);
this.calibrate();
};
this.onlistselect = (d: TagEventType<tag.ListItemEventData>) => {
GUI.systemDock().selectedApp = null;
};
this.onlistdbclick = (d: TagEventType<tag.ListItemEventData>) => {
GUI.systemDock().selectedApp = null;
const it = this.selectedItem;
return GUI.openWith(it.data as AppArgumentsType);
};
//($ "#workingenv").on "click", (e) ->
// desktop[0].set "selected", -1
$(this).on("click", (e) => {
let el: any = $(e.target).closest("afx-app-window")[0];
if (el) {
return;
}
el = $(e.target).parent();
if (!(el.length > 0)) {
return;
}
el = el.parent();
if (!(el.length > 0)) {
return;
}
if (el[0] !== this) {
return;
}
this.unselect();
GUI.systemDock().selectedApp = null;
});
this.contextmenuHandle = (e, m) => {
if (e.target.tagName.toUpperCase() === "UL") {
this.unselect();
}
GUI.systemDock().selectedApp = null;
let menu = [
{ text: __("Open"), dataid: "desktop-open" } as GUI.BasicItemType,
{ text: __("Refresh"), dataid: "desktop-refresh" } as GUI.BasicItemType,
];
menu = menu.concat(setting.desktop.menu.map(e => e));
m.items = menu;
m.onmenuselect = (evt: TagEventType<tag.MenuEventData>) => {
if (!evt.data || !evt.data.item) return;
const item = evt.data.item.data;
switch (item.dataid) {
case "desktop-open":
var it = this.selectedItem;
if (it) {
return GUI.openWith(
it.data as AppArgumentsType
);
}
let arg = setting.desktop.path.asFileHandle() as AppArgumentsType;
arg.mime = "dir";
arg.type = "dir";
return GUI.openWith(arg);
case "desktop-refresh":
return this.refresh();
default:
if (item.app) {
return GUI.launch(item.app, item.args);
}
}
};
return m.show(e);
};
this.refresh();
announcer.observable.on("VFS", (d: API.AnnouncementDataType<API.VFS.BaseFileHandle>) => {
if (["read", "publish", "download"].includes(d.message as string)) {
return;
}
if (d.u_data.hash() === this.file.hash() || d.u_data.parent().hash() === this.file.hash()) {
return this.refresh();
}
});
return announcer.ostrigger("desktoploaded", undefined);
};
super.mount();
}
/**
* Display all files and folders in the specific desktop location
*
* @return {*} {Promise<any>}
* @memberof DesktopTag
*/
refresh(): Promise<any> {
return new Promise<any>(async (resolve, reject) => {
try {
this.file = setting.desktop.path.asFileHandle();
await this.file.onready();
const d = await this.file.read();
if (d.error) {
throw new Error(d.error);
}
const items = [];
$.each(d.result, function (i, v) {
if (v.filename[0] === "." && !setting.desktop.showhidden) {
return;
}
v.text = v.filename;
//v.text = v.text.substring(0,9) + "..." ifv.text.length > 10
v.iconclass = v.type;
return items.push(v);
});
this.data = items;
this.calibrate();
}
catch (err) {
announcer.osfail(err.toString(), err);
reject(__e(err));
}
});
}
/**
* Remove this element from its parent
*
* @memberof DesktopTag
*/
remove(): void {
if(this.observer)
{
this.observer.disconnect();
}
super.remove();
}
/**
* Active a window above all other windows
*
* @private
* @param {WindowTag} win
* @memberof DesktopTag
*/
private selectWindow(win: WindowTag)
{
if(this.window_list.has(win))
{
this.window_list.delete(win);
}
else
{
win.observable.on("focused",(_)=>{
this.selectWindow(win);
});
}
this.window_list.add(win);
console.log("number of windows", this.window_list.size);
this.render();
}
/**
* Render all windows in order from bottom to top
*
* @private
* @memberof DesktopTag
*/
private render(){
let zindex = 10;
for(let win of this.window_list)
{
$(win).css("z-index", zindex++);
}
}
}
define("afx-desktop", DesktopTag);
}
}
}

View File

@ -284,7 +284,7 @@ namespace OS {
.css("position", "absolute") .css("position", "absolute")
.css("left", `${left}px`) .css("left", `${left}px`)
.css("top", `${top}px`) .css("top", `${top}px`)
.css("z-index", Ant.OS.GUI.zindex++); .css("z-index", 10);
$(this).on("mousedown", (e) => { $(this).on("mousedown", (e) => {
if (this._shown) { if (this._shown) {
return; return;
@ -301,10 +301,8 @@ namespace OS {
this.observable.on("resize", (e) => this.resize()); this.observable.on("resize", (e) => this.resize());
this.observable.on("focus", () => { this.observable.on("focus", () => {
Ant.OS.GUI.zindex++;
$(this) $(this)
.show() .show()
.css("z-index", Ant.OS.GUI.zindex)
.removeClass("unactive"); .removeClass("unactive");
this._shown = true; this._shown = true;
$(this.refs.win_overlay).hide(); $(this.refs.win_overlay).hide();

View File

@ -247,11 +247,6 @@ namespace OS {
* Tag event callback type * Tag event callback type
*/ */
export type TagEventCallback<T> = (e: TagEventType<T>) => void; export type TagEventCallback<T> = (e: TagEventType<T>) => void;
/**
* Top most element z index value, start by 10
*/
export var zindex: number = 10;
/** /**
* Base abstract class for tag implementation, any AFX tag should be * Base abstract class for tag implementation, any AFX tag should be
* subclass of this class * subclass of this class

View File

@ -1,11 +1,11 @@
afx-float-list div.list-container > ul{ afx-desktop div.list-container > ul, afx-float-list div.list-container > ul{
padding: 0; padding: 0;
margin: 0; margin: 0;
background-color: transparent; background-color: transparent;
} }
afx-float-list div.list-container > ul li{ afx-desktop div.list-container > ul li, afx-float-list div.list-container > ul li{
padding: 0; padding: 0;
margin: 0; margin: 0;
background-color: transparent; background-color: transparent;