140 lines
4.7 KiB
TypeScript
140 lines
4.7 KiB
TypeScript
import AstalTray from "gi://AstalTray";
|
|
import { bind, GObject } from "astal";
|
|
import AstalHyprland from "gi://AstalHyprland";
|
|
import { Gtk } from "astal/gtk4";
|
|
|
|
const hypr = AstalHyprland.get_default();
|
|
const SYNC = GObject.BindingFlags.SYNC_CREATE;
|
|
|
|
const SysTray = () => {
|
|
const trayBox = new Gtk.Box({ cssClasses: ["bar-button"] });
|
|
const tray = AstalTray.get_default();
|
|
|
|
const trayItems = new Map<string, Gtk.MenuButton>();
|
|
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,
|
|
cssClasses: ["tray-item"],
|
|
});
|
|
|
|
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 = () => {
|
|
return (
|
|
<box>
|
|
{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 => (
|
|
<button
|
|
cssClasses={bind(hypr, "focusedWorkspace").as(fw =>
|
|
ws === fw
|
|
? [
|
|
"focused-workspace-button",
|
|
"workspace-button",
|
|
]
|
|
: ["workspace-button"],
|
|
)}
|
|
onButtonPressed={() => ws.focus()}
|
|
child={<label label={String(ws.id)}></label>}
|
|
></button>
|
|
)),
|
|
)}
|
|
</box>
|
|
);
|
|
};
|
|
|
|
/**
|
|
* Displays the name of the currently active window and provides a popover for
|
|
* displaying all available clients
|
|
*/
|
|
const ActiveWindow = () => {
|
|
const focused = bind(hypr, "focusedClient");
|
|
|
|
const WindowPopover = (): Gtk.Popover => {
|
|
// Set up boxes + Popover
|
|
const popover = new Gtk.Popover();
|
|
|
|
const popoverBox = WindowPopoverBox();
|
|
|
|
popover.set_child(popoverBox);
|
|
return popover;
|
|
};
|
|
|
|
const windowPopover = WindowPopover();
|
|
|
|
// ───────────────────────────────────────────────────────────────────
|
|
// Return fully assembled HyprlandFocusedClient box
|
|
// ───────────────────────────────────────────────────────────────────
|
|
return (
|
|
<box visible={focused.as(Boolean)}>
|
|
<button
|
|
onClicked={() => windowPopover.popup()}
|
|
cssClasses={["bar-button"]}
|
|
child={
|
|
focused.as(
|
|
client =>
|
|
client && (
|
|
<label label={bind(client, "title").as(String)} />
|
|
),
|
|
)
|
|
}></button>
|
|
{windowPopover}
|
|
</box >
|
|
);
|
|
};
|
|
|
|
const WindowPopoverBox = () => {
|
|
return <box vertical>
|
|
<label label={"Available Windows"} cssClasses={['title-2']}></label>
|
|
<Gtk.Separator marginTop={5} marginBottom={5}></Gtk.Separator>
|
|
<box vertical>
|
|
{bind(hypr, 'clients').as(clients => {
|
|
return clients.map(client => {
|
|
return <box>
|
|
<label label={bind(client, 'workspace').as(w => `(WS ${w})`)}></label>
|
|
<label label={bind(client, 'initialClass').as(c => `[${c}]`)}></label>
|
|
<label label={bind(client, 'title')}></label>
|
|
</box>
|
|
})
|
|
})}
|
|
</box>
|
|
</box>
|
|
}
|
|
|
|
export default {
|
|
Workspace,
|
|
ActiveWindow,
|
|
SysTray,
|
|
};
|