import AstalTray from "gi://AstalTray"; import { bind, GObject } from "astal"; import AstalHyprland from "gi://AstalHyprland"; import { Gtk } from "astal/gtk4"; const SYNC = GObject.BindingFlags.SYNC_CREATE; const SysTray = () => { const trayBox = new Gtk.Box(); const tray = AstalTray.get_default(); const trayItems = new Map(); const trayAddedHandler = tray.connect("item-added", (_, id) => { const item = tray.get_item(id); const popover = Gtk.PopoverMenu.new_from_model(item.menu_model); const icon = new Gtk.Image(); const button = new Gtk.MenuButton({ popover, child: icon }); item.bind_property("gicon", icon, "gicon", SYNC); popover.insert_action_group("dbusmenu", item.action_group); item.connect("notify::action-group", () => { popover.insert_action_group("dbusmenu", item.action_group); }); trayItems.set(id, button); trayBox.append(button); }) const trayRemovedHandler = tray.connect("item-removed", (_, id) => { const button = trayItems.get(id); if (button) { trayBox.remove(button); button.run_dispose(); trayItems.delete(id); } }); trayBox.connect("destroy", () => { tray.disconnect(trayAddedHandler); tray.disconnect(trayRemovedHandler); }); return trayBox; } const Workspace = () => { const hypr = AstalHyprland.get_default() return {bind(hypr, "workspaces").as(wss => wss .filter(ws => !(ws.id >= -99 && ws.id <= -2)) // filter out special workspaces .sort((a, b) => a.id - b.id) .map(ws => ( )) )} } /** * Displays the name of the currently active window and provides a popover for * displaying all available clients */ const ActiveWindow = () => { const hypr = AstalHyprland.get_default(); const focused = bind( hypr, "focusedClient" ); const WindowPopover = (): Gtk.Popover => { // Set up boxes + Popover const clients = new Map(); const popover = new Gtk.Popover(); const popoverBox = new Gtk.Box( { orientation: Gtk.Orientation.VERTICAL } ); const addClient = ( client: AstalHyprland.Client ) => { const clientBox = new Gtk.Box(); // Workspace description const descWS = new Gtk.Label( { label: '(WS ' } ); // Workpsace information const workspace = new Gtk.Label(); client.workspace.bind_property( 'name', workspace, 'label', SYNC ); const windowClassDesc = new Gtk.Label( { label: ') [' } ); const windowClass = new Gtk.Label(); windowClass.label = client.get_initial_class(); const titleDesc = new Gtk.Label( { label: '] ' } ); titleDesc.set_margin_end( 2 ); const title = new Gtk.Label(); client.bind_property( 'title', title, 'label', SYNC ); clientBox.append( descWS ); clientBox.append( workspace ); clientBox.append( windowClassDesc ); clientBox.append( windowClass ); clientBox.append( titleDesc ); clientBox.append( title ); popoverBox.append( clientBox ); clients.set( client.get_address(), clientBox ); } // Populate with already added clients const c = hypr.get_clients(); for ( let index = 0; index < c.length; index++ ) { addClient( c[ index ] ); } hypr.connect( 'client-added', ( _, client ) => { addClient( client ); } ); hypr.connect( 'client-removed', ( _, client ) => { const c = clients.get( client ); if ( c ) { popoverBox.remove( c ); c.run_dispose(); clients.delete( client ); } } ); popover.set_child( popoverBox ); return popover; } const windowPopover = WindowPopover(); // ─────────────────────────────────────────────────────────────────── // Return fully assembled HyprlandFocusedClient box // ─────────────────────────────────────────────────────────────────── return { windowPopover } } export default { Workspace, ActiveWindow, SysTray }