99 lines
3.6 KiB
TypeScript
99 lines
3.6 KiB
TypeScript
|
import * as electron from "electron";
|
||
|
import {MenuBarDriver, MenuBarEntry} from "tc-shared/ui/frames/menu-bar";
|
||
|
import {IpcRendererEvent} from "electron";
|
||
|
import {getIconManager} from "tc-shared/file/Icons";
|
||
|
import {clientIconClassToImage, remoteIconDatafier, RemoteIconWrapper} from "./IconHelper";
|
||
|
import {NativeMenuBarEntry} from "../shared/MenuBarDefinitions";
|
||
|
|
||
|
let uniqueEntryIdIndex = 0;
|
||
|
export class NativeMenuBarDriver implements MenuBarDriver {
|
||
|
private readonly ipcChannelListener;
|
||
|
|
||
|
private menuEntries: NativeMenuBarEntry[] = [];
|
||
|
private remoteIconReferences: RemoteIconWrapper[] = [];
|
||
|
private remoteIconListeners: (() => void)[] = [];
|
||
|
private callbacks: {[key: string]: () => void} = {};
|
||
|
|
||
|
constructor() {
|
||
|
this.ipcChannelListener = this.handleMenuBarEvent.bind(this);
|
||
|
|
||
|
electron.ipcRenderer.on("menu-bar", this.ipcChannelListener);
|
||
|
}
|
||
|
|
||
|
destroy() {
|
||
|
electron.ipcRenderer.off("menu-bar", this.ipcChannelListener);
|
||
|
this.internalClearEntries();
|
||
|
}
|
||
|
|
||
|
private internalClearEntries() {
|
||
|
this.callbacks = {};
|
||
|
this.menuEntries = [];
|
||
|
|
||
|
this.remoteIconListeners.forEach(callback => callback());
|
||
|
this.remoteIconListeners = [];
|
||
|
|
||
|
this.remoteIconReferences.forEach(icon => remoteIconDatafier.unrefIcon(icon));
|
||
|
this.remoteIconReferences = [];
|
||
|
}
|
||
|
|
||
|
clearEntries() {
|
||
|
this.internalClearEntries()
|
||
|
electron.ipcRenderer.send("menu-bar", []);
|
||
|
}
|
||
|
|
||
|
setEntries(entries: MenuBarEntry[]) {
|
||
|
this.internalClearEntries();
|
||
|
this.menuEntries = entries.map(e => this.wrapEntry(e)).filter(e => !!e);
|
||
|
electron.ipcRenderer.send("menu-bar", this.menuEntries);
|
||
|
}
|
||
|
|
||
|
private wrapEntry(entry: MenuBarEntry) : NativeMenuBarEntry {
|
||
|
if(entry.type === "separator") {
|
||
|
return { type: "separator", uniqueId: entry.uniqueId || "item-" + (++uniqueEntryIdIndex) };
|
||
|
} else if(entry.type === "normal") {
|
||
|
if(typeof entry.visible === "boolean" && !entry.visible) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
let result = {
|
||
|
type: "normal",
|
||
|
uniqueId: entry.uniqueId || "item-" + (++uniqueEntryIdIndex),
|
||
|
label: entry.label,
|
||
|
disabled: entry.disabled,
|
||
|
children: entry.children?.map(e => this.wrapEntry(e)).filter(e => !!e)
|
||
|
} as NativeMenuBarEntry;
|
||
|
|
||
|
if(entry.click) {
|
||
|
this.callbacks[result.uniqueId] = entry.click;
|
||
|
}
|
||
|
|
||
|
if(typeof entry.icon === "object") {
|
||
|
/* we've a remote icon */
|
||
|
const remoteIcon = getIconManager().resolveIcon(entry.icon.iconId, entry.icon.serverUniqueId, entry.icon.handlerId);
|
||
|
const wrapped = remoteIconDatafier.resolveIcon(remoteIcon);
|
||
|
this.remoteIconReferences.push(wrapped);
|
||
|
|
||
|
wrapped.onDataUrlChange(url => {
|
||
|
result.icon = url;
|
||
|
electron.ipcRenderer.send("menu-bar", this.menuEntries);
|
||
|
});
|
||
|
result.icon = wrapped.getDataUrl();
|
||
|
} else if(typeof entry.icon === "string") {
|
||
|
result.icon = clientIconClassToImage(entry.icon).toDataURL();
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
} else {
|
||
|
return undefined;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private handleMenuBarEvent(_event: IpcRendererEvent, eventType: string, ...args) {
|
||
|
if(eventType === "item-click") {
|
||
|
const callback = this.callbacks[args[0]];
|
||
|
if(typeof callback === "function") {
|
||
|
callback();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|