123 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			123 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
import {ContextMenuEntry, ContextMenuFactory, setGlobalContextMenuFactory} from "tc-shared/ui/ContextMenu";
 | 
						|
import * as electron from "electron";
 | 
						|
import {MenuItemConstructorOptions} from "electron";
 | 
						|
import {clientIconClassToImage, remoteIconDatafier, RemoteIconWrapper} from "./IconHelper";
 | 
						|
import {getIconManager, RemoteIconInfo} from "tc-shared/file/Icons";
 | 
						|
const {Menu} = electron.remote;
 | 
						|
 | 
						|
let currentMenu: ContextMenuInstance;
 | 
						|
class ContextMenuInstance {
 | 
						|
    private readonly closeCallback: () => void | undefined;
 | 
						|
    private readonly menuOptions: MenuItemConstructorOptions[];
 | 
						|
    private currentMenu: electron.Menu;
 | 
						|
 | 
						|
    private wrappedIcons: RemoteIconWrapper[] = [];
 | 
						|
    private wrappedIconListeners: (() => void)[] = [];
 | 
						|
 | 
						|
    constructor(entries: ContextMenuEntry[], closeCallback: () => void | undefined) {
 | 
						|
        this.closeCallback = closeCallback;
 | 
						|
        this.menuOptions = entries.map(e => this.wrapEntry(e)).filter(e => !!e);
 | 
						|
    }
 | 
						|
 | 
						|
    destroy() {
 | 
						|
        this.currentMenu?.closePopup();
 | 
						|
        this.currentMenu = undefined;
 | 
						|
 | 
						|
        this.wrappedIconListeners.forEach(callback => callback());
 | 
						|
        this.wrappedIcons.forEach(icon => remoteIconDatafier.unrefIcon(icon));
 | 
						|
 | 
						|
        this.wrappedIcons = [];
 | 
						|
        this.wrappedIconListeners = [];
 | 
						|
    }
 | 
						|
 | 
						|
    spawn(pageX: number, pageY: number) {
 | 
						|
        this.currentMenu = Menu.buildFromTemplate(this.menuOptions);
 | 
						|
        this.currentMenu.popup({
 | 
						|
            callback: () => {
 | 
						|
                if(this.closeCallback) {
 | 
						|
                    this.closeCallback();
 | 
						|
                }
 | 
						|
                currentMenu = undefined;
 | 
						|
            },
 | 
						|
            x: pageX,
 | 
						|
            y: pageY,
 | 
						|
            window: electron.remote.BrowserWindow.getFocusedWindow()
 | 
						|
        });
 | 
						|
    }
 | 
						|
 | 
						|
    private wrapEntry(entry: ContextMenuEntry) : MenuItemConstructorOptions {
 | 
						|
        if(typeof entry.visible === "boolean" && !entry.visible) { return undefined; }
 | 
						|
 | 
						|
        let options: MenuItemConstructorOptions;
 | 
						|
        let icon: string | RemoteIconInfo | undefined;
 | 
						|
 | 
						|
        switch (entry.type) {
 | 
						|
            case "normal":
 | 
						|
                icon = entry.icon;
 | 
						|
                options = {
 | 
						|
                    type: entry.subMenu?.length ? "submenu" : "normal",
 | 
						|
                    label: typeof entry.label === "string" ? entry.label : entry.label.text,
 | 
						|
                    enabled: entry.enabled,
 | 
						|
                    click: entry.click,
 | 
						|
                    id: entry.uniqueId,
 | 
						|
                    submenu: entry.subMenu?.length ? entry.subMenu.map(e => this.wrapEntry(e)).filter(e => !!e) : undefined
 | 
						|
                };
 | 
						|
                break;
 | 
						|
 | 
						|
            case "checkbox":
 | 
						|
                icon = entry.icon;
 | 
						|
                options = {
 | 
						|
                    type: "checkbox",
 | 
						|
                    label: typeof entry.label === "string" ? entry.label : entry.label.text,
 | 
						|
                    enabled: entry.enabled,
 | 
						|
                    click: entry.click,
 | 
						|
                    id: entry.uniqueId,
 | 
						|
                    checked: entry.checked
 | 
						|
                };
 | 
						|
                break;
 | 
						|
 | 
						|
            case "separator":
 | 
						|
                return {
 | 
						|
                    type: "separator"
 | 
						|
                };
 | 
						|
 | 
						|
            default:
 | 
						|
                return undefined;
 | 
						|
        }
 | 
						|
 | 
						|
        if(typeof icon === "object") {
 | 
						|
            const remoteIcon = getIconManager().resolveIcon(icon.iconId, icon.serverUniqueId, icon.handlerId);
 | 
						|
            const wrapped = remoteIconDatafier.resolveIcon(remoteIcon);
 | 
						|
            remoteIconDatafier.unrefIcon(wrapped);
 | 
						|
 | 
						|
            /*
 | 
						|
            // Sadly we can't update the icon on the fly, so we've to live with whatever we have
 | 
						|
            this.wrappedIcons.push(wrapped);
 | 
						|
            this.wrappedIconListeners.push(wrapped.onDataUrlChange(dataUrl => {
 | 
						|
                options.icon = electron.nativeImage.createFromDataURL(dataUrl);
 | 
						|
            }));
 | 
						|
            */
 | 
						|
 | 
						|
            if(wrapped.getDataUrl()) {
 | 
						|
                options.icon = electron.remote.nativeImage.createFromDataURL(wrapped.getDataUrl());
 | 
						|
            }
 | 
						|
        } else if(typeof icon === "string") {
 | 
						|
            options.icon = clientIconClassToImage(icon);
 | 
						|
        }
 | 
						|
 | 
						|
        return options;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
setGlobalContextMenuFactory(new class implements ContextMenuFactory {
 | 
						|
    closeContextMenu() {
 | 
						|
        currentMenu?.destroy();
 | 
						|
        currentMenu = undefined;
 | 
						|
    }
 | 
						|
 | 
						|
    spawnContextMenu(position: { pageX: number; pageY: number }, entries: ContextMenuEntry[], callbackClose?: () => void) {
 | 
						|
        this.closeContextMenu();
 | 
						|
        currentMenu = new ContextMenuInstance(entries, callbackClose);
 | 
						|
        currentMenu.spawn(position.pageX, position.pageY);
 | 
						|
    }
 | 
						|
}); |