181 lines
6.0 KiB
TypeScript
181 lines
6.0 KiB
TypeScript
window["require_setup"](module);
|
|
|
|
import * as electron from "electron";
|
|
const remote = electron.remote;
|
|
const {Menu, MenuItem} = remote;
|
|
|
|
import {isFunction} from "util";
|
|
import NativeImage = electron.NativeImage;
|
|
|
|
class ElectronContextMenu implements contextmenu.ContextMenuProvider {
|
|
private _close_listeners: (() => any)[] = [];
|
|
private _current_menu: electron.Menu;
|
|
|
|
private _icon_mash_url: string;
|
|
private _icon_mask_img: NativeImage;
|
|
private _div: JQuery;
|
|
|
|
despawn_context_menu() {
|
|
if(!this._current_menu)
|
|
return;
|
|
this._current_menu.closePopup();
|
|
this._current_menu = undefined;
|
|
|
|
for(const listener of this._close_listeners)
|
|
listener();
|
|
this._close_listeners = [];
|
|
}
|
|
|
|
finalize() {
|
|
this._icon_mask_img = undefined;
|
|
this._icon_mash_url = undefined;
|
|
if(this._div) this._div.detach();
|
|
this._div = undefined;
|
|
this._cache_klass_map = undefined;
|
|
}
|
|
|
|
initialize() {
|
|
this.initialize_icons();
|
|
}
|
|
|
|
private async initialize_icons() {
|
|
if(!this._div) {
|
|
this._div = $.spawn("div");
|
|
this._div.css('display', 'none');
|
|
this._div.appendTo(document.body);
|
|
}
|
|
|
|
const image = new Image();
|
|
image.src = 'img/client_icon_sprite.svg';
|
|
await new Promise((resolve, reject) => {
|
|
image.onload = resolve;
|
|
image.onerror = reject;
|
|
});
|
|
|
|
/* TODO: Get a size! */
|
|
const canvas = document.createElement("canvas");
|
|
canvas.width = 1024;
|
|
canvas.height = 1024;
|
|
canvas.getContext("2d").drawImage(image, 0, 0);
|
|
|
|
this._icon_mash_url = canvas.toDataURL();
|
|
this._icon_mask_img = remote.nativeImage.createFromDataURL(this._icon_mash_url);
|
|
}
|
|
|
|
|
|
private _cache_klass_map: {[key: string]: NativeImage} = {};
|
|
private class_to_image(klass: string) : NativeImage {
|
|
if(!klass || !this._icon_mask_img)
|
|
return undefined;
|
|
if(this._cache_klass_map[klass])
|
|
return this._cache_klass_map[klass];
|
|
|
|
this._div[0].classList.value = 'icon ' + klass;
|
|
const data = window.getComputedStyle(this._div[0]);
|
|
|
|
const offset_x = parseInt(data.backgroundPositionX.split(",")[0]);
|
|
const offset_y = parseInt(data.backgroundPositionY.split(",")[0]);
|
|
|
|
//http://localhost/home/TeaSpeak/Web-Client/web/environment/development/img/client_icon_sprite.svg
|
|
//const hight = element.css('height');
|
|
//const width = element.css('width');
|
|
console.log("Offset: x: %o y: %o;", offset_x, offset_y);
|
|
return this._cache_klass_map[klass] = this._icon_mask_img.crop({
|
|
height: 16,
|
|
width: 16,
|
|
x: offset_x == 0 ? 0 : -offset_x,
|
|
y: offset_y == 0 ? 0 : -offset_y
|
|
});
|
|
}
|
|
|
|
private _entry_id = 0;
|
|
private build_menu(entry: contextmenu.MenuEntry) : electron.MenuItem {
|
|
if(entry.type == contextmenu.MenuEntryType.CLOSE) {
|
|
this._close_listeners.push(entry.callback);
|
|
return undefined;
|
|
}
|
|
|
|
const click_callback = () => {
|
|
if(entry.callback)
|
|
entry.callback();
|
|
this.despawn_context_menu();
|
|
};
|
|
const _id = "entry_" + (this._entry_id++);
|
|
if(entry.type == contextmenu.MenuEntryType.ENTRY) {
|
|
return new MenuItem({
|
|
id: _id,
|
|
label: (isFunction(entry.name) ? (entry.name as (() => string))() : entry.name) as string,
|
|
type: "normal",
|
|
click: click_callback,
|
|
icon: this.class_to_image(entry.icon_class),
|
|
visible: entry.visible,
|
|
enabled: !entry.disabled
|
|
});
|
|
} else if(entry.type == contextmenu.MenuEntryType.HR) {
|
|
if(typeof(entry.visible) === "boolean" && !entry.visible)
|
|
return undefined;
|
|
|
|
return new MenuItem({
|
|
id: _id,
|
|
type: "separator",
|
|
label: '',
|
|
click: click_callback
|
|
})
|
|
} else if(entry.type == contextmenu.MenuEntryType.CHECKBOX) {
|
|
return new MenuItem({
|
|
id: _id,
|
|
label: (isFunction(entry.name) ? (entry.name as (() => string))() : entry.name) as string,
|
|
type: "checkbox",
|
|
checked: !!entry.checkbox_checked,
|
|
click: click_callback,
|
|
icon: this.class_to_image(entry.icon_class),
|
|
visible: entry.visible,
|
|
enabled: !entry.disabled
|
|
});
|
|
} else if (entry.type == contextmenu.MenuEntryType.SUB_MENU) {
|
|
const sub_menu = new Menu();
|
|
for(const e of entry.sub_menu) {
|
|
const build = this.build_menu(e);
|
|
if(!build)
|
|
continue;
|
|
sub_menu.append(build);
|
|
}
|
|
return new MenuItem({
|
|
id: _id,
|
|
label: (isFunction(entry.name) ? (entry.name as (() => string))() : entry.name) as string,
|
|
type: "submenu",
|
|
submenu: sub_menu,
|
|
click: click_callback,
|
|
icon: this.class_to_image(entry.icon_class),
|
|
visible: entry.visible,
|
|
enabled: !entry.disabled
|
|
});
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
spawn_context_menu(x: number, y: number, ...entries: contextmenu.MenuEntry[]) {
|
|
this.despawn_context_menu();
|
|
|
|
this._current_menu = new Menu();
|
|
for(const entry of entries) {
|
|
const build = this.build_menu(entry);
|
|
if(!build)
|
|
continue;
|
|
this._current_menu.append(build);
|
|
}
|
|
|
|
this._current_menu.popup({
|
|
window: remote.getCurrentWindow(),
|
|
x: x,
|
|
y: y,
|
|
callback: () => this.despawn_context_menu()
|
|
});
|
|
}
|
|
|
|
html_format_enabled() { return false; }
|
|
}
|
|
|
|
contextmenu.set_provider(new ElectronContextMenu());
|
|
|
|
export {}; |