TeaSpeak-Client/modules/renderer/menu.ts

246 lines
6.8 KiB
TypeScript

import {class_to_image} from "./icon-helper";
import * as electron from "electron";
import * as mbar from "tc-shared/ui/frames/MenuBar";
import {Arguments, process_args} from "../shared/process-arguments";
import ipcRenderer = electron.ipcRenderer;
import {Icon} from "tc-shared/FileManager";
namespace native {
import ipcRenderer = electron.ipcRenderer;
let _item_index = 1;
abstract class NativeMenuBase {
protected _handle: NativeMenuBar;
protected _click: () => any;
id: string;
protected constructor(handle: NativeMenuBar, id?: string) {
this._handle = handle;
this.id = id || ("item_" + (_item_index++));
}
abstract build() : electron.MenuItemConstructorOptions;
abstract items(): (mbar.MenuItem | mbar.HRItem)[];
trigger_click() {
if(this._click)
this._click();
}
}
class NativeMenuItem extends NativeMenuBase implements mbar.MenuItem {
private _items: (NativeMenuItem | NativeHrItem)[] = [];
private _label: string;
private _enabled: boolean = true;
private _visible: boolean = true;
private _icon_data: string;
constructor(handle: NativeMenuBar) {
super(handle);
}
append_hr(): mbar.HRItem {
const item = new NativeHrItem(this._handle);
this._items.push(item);
return item;
}
append_item(label: string): mbar.MenuItem {
const item = new NativeMenuItem(this._handle);
item.label(label);
this._items.push(item);
return item;
}
click(callback: () => any): this {
this._click = callback;
return this;
}
delete_item(item: mbar.MenuItem | mbar.HRItem) {
const i_index = this._items.indexOf(item as any);
if(i_index < 0) return;
this._items.splice(i_index, 1);
}
disabled(value?: boolean): boolean {
if(typeof(value) === "boolean")
this._enabled = !value;
return !this._enabled;
}
icon(klass?: string | Promise<Icon> | Icon): string {
if(typeof(klass) === "string") {
const buffer = class_to_image(klass);
if(buffer)
this._icon_data = buffer.toDataURL();
}
return "";
}
items(): (mbar.MenuItem | mbar.HRItem)[] {
return this._items;
}
label(value?: string): string {
if(typeof(value) === "string")
this._label = value;
return this._label;
}
visible(value?: boolean): boolean {
if(typeof(value) === "boolean")
this._visible = value;
return this._visible;
}
build(): Electron.MenuItemConstructorOptions {
return {
id: this.id,
label: this._label || "",
submenu: this._items.length > 0 ? this._items.map(e => e.build()) : undefined,
enabled: this._enabled,
visible: this._visible,
icon: this._icon_data
}
}
}
class NativeHrItem extends NativeMenuBase implements mbar.HRItem {
constructor(handle: NativeMenuBar) {
super(handle);
}
build(): Electron.MenuItemConstructorOptions {
return {
type: 'separator',
id: this.id
}
}
items(): (mbar.MenuItem | mbar.HRItem)[] {
return [];
}
}
function is_similar_deep(a, b) {
if(typeof(a) !== typeof(b))
return false;
if(typeof(a) !== "object")
return a === b;
const aProps = Object.keys(a);
const bProps = Object.keys(b);
if (aProps.length != bProps.length)
return false;
for (let i = 0; i < aProps.length; i++) {
const propName = aProps[i];
if(!is_similar_deep(a[propName], b[propName]))
return false;
}
return true;
}
export class NativeMenuBar implements mbar.MenuBarDriver {
private static _instance: NativeMenuBar;
private menu: electron.Menu;
private _items: NativeMenuItem[] = [];
private _current_menu: electron.MenuItemConstructorOptions[];
public static instance() : NativeMenuBar {
if(!this._instance)
this._instance = new NativeMenuBar();
return this._instance;
}
append_item(label: string): mbar.MenuItem {
const item = new NativeMenuItem(this);
item.label(label);
this._items.push(item);
return item;
}
delete_item(item: mbar.MenuItem) {
const i_index = this._items.indexOf(item as any);
if(i_index < 0) return;
this._items.splice(i_index, 1);
}
flush_changes() {
const target_menu = this.build_menu();
if(is_similar_deep(target_menu, this._current_menu))
return;
this._current_menu = target_menu;
ipcRenderer.send('top-menu', target_menu);
}
private build_menu() : electron.MenuItemConstructorOptions[] {
return this._items.map(e => e.build());
}
items(): mbar.MenuItem[] {
return this._items;
}
initialize() {
this.menu = new electron.remote.Menu();
ipcRenderer.on('top-menu', (event, clicked_item) => {
console.log("Item %o clicked", clicked_item);
const check_item = (item: NativeMenuBase) => {
if(item.id == clicked_item) {
item.trigger_click();
return true;
}
for(const child of item.items())
if(check_item(child as NativeMenuBase))
return true;
};
for(const item of this._items)
if(check_item(item))
return;
});
}
}
}
mbar.set_driver(native.NativeMenuBar.instance());
// @ts-ignore
mbar.native_actions = {
open_change_log() {
call_basic_action("open-changelog");
},
check_native_update() {
call_basic_action("check-native-update");
},
quit() {
call_basic_action("quit");
},
open_dev_tools() {
call_basic_action("open-dev-tools");
},
reload_page() {
call_basic_action("reload-window")
},
show_dev_tools() { return process_args.has_flag(Arguments.DEV_TOOLS); }
};
const call_basic_action = (name: string, ...args: any[]) => ipcRenderer.send('basic-action', name, ...args);