Blob Blame History Raw
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported init */

const { Clutter, Shell, St } = imports.gi;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const System = imports.system;

const PANEL_ICON_SIZE = 16;

const STANDARD_TRAY_ICON_IMPLEMENTATIONS = [
    'bluetooth-applet',
    'gnome-sound-applet',
    'nm-applet',
    'gnome-power-manager',
    'keyboard',
    'a11y-keyboard',
    'kbd-scrolllock',
    'kbd-numlock',
    'kbd-capslock',
    'ibus-ui-gtk'
];

class SysTray {
    constructor() {
        this._icons = [];
        this._tray = null;
    }

    _onTrayIconAdded(o, icon) {
        let wmClass = icon.wm_class ? icon.wm_class.toLowerCase() : '';
        if (STANDARD_TRAY_ICON_IMPLEMENTATIONS.includes(wmClass))
            return;

        let button = new PanelMenu.Button(0.5, null, true);

        let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
        let iconSize = PANEL_ICON_SIZE * scaleFactor;

        icon.set({
            width: iconSize,
            height: iconSize,
            x_align: Clutter.ActorAlign.CENTER,
            y_align: Clutter.ActorAlign.CENTER
        });

        let iconBin = new St.Widget({
            layout_manager: new Clutter.BinLayout()
        });
        iconBin.add_actor(icon);
        button.add_actor(iconBin);

        this._icons.push(icon);

        button.connect('button-release-event', (actor, event) => {
            icon.click(event);
        });
        button.connect('key-press-event', (actor, event) => {
            icon.click(event);
        });

        icon.connect('destroy', () => {
            button.destroy();
        });

        let role = wmClass || `${icon}`;
        Main.panel.addToStatusArea(role, button);
    }

    _onTrayIconRemoved(o, icon) {
        let parent = icon.get_parent();
        parent.destroy();
        this._icons.splice(this._icons.indexOf(icon), 1);
    }

    enable() {
        this._tray = new Shell.TrayManager();
        this._tray.connect('tray-icon-added',
            this._onTrayIconAdded.bind(this));
        this._tray.connect('tray-icon-removed',
            this._onTrayIconRemoved.bind(this));
        this._tray.manage_screen(Main.panel.actor);
    }

    disable() {
        this._icons.forEach(icon => icon.get_parent().destroy());
        this._icons = [];

        this._tray = null;
        System.gc(); // force finalizing tray to unmanage screen
    }
}

function init() {
    return new SysTray();
}