246 lines
6.8 KiB
TypeScript
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 {LocalIcon} from "tc-shared/file/Icons";
|
|
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<LocalIcon> | LocalIcon): 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); |