diff options
author | 2025-03-02 17:56:12 +0300 | |
---|---|---|
committer | 2025-03-02 17:57:55 +0300 | |
commit | e79fa091cfbdbd9dfe2ed19a0a5f92604047b0a9 (patch) | |
tree | aca9f03423c6cd8566a0204aeda8b31e1f83b989 /widget/bar | |
parent | feat: basic topbar features (diff) | |
download | ags-config-e79fa091cfbdbd9dfe2ed19a0a5f92604047b0a9.tar.gz ags-config-e79fa091cfbdbd9dfe2ed19a0a5f92604047b0a9.tar.bz2 ags-config-e79fa091cfbdbd9dfe2ed19a0a5f92604047b0a9.tar.lz ags-config-e79fa091cfbdbd9dfe2ed19a0a5f92604047b0a9.tar.xz ags-config-e79fa091cfbdbd9dfe2ed19a0a5f92604047b0a9.tar.zst ags-config-e79fa091cfbdbd9dfe2ed19a0a5f92604047b0a9.zip |
feat: basic tray and improved workspaces buttons
Diffstat (limited to 'widget/bar')
-rw-r--r-- | widget/bar/Bar.tsx | 122 |
1 files changed, 122 insertions, 0 deletions
diff --git a/widget/bar/Bar.tsx b/widget/bar/Bar.tsx new file mode 100644 index 0000000..b057cdb --- /dev/null +++ b/widget/bar/Bar.tsx @@ -0,0 +1,122 @@ +import { App, Astal, Gtk, Gdk } from "astal/gtk4" +import { bind, exec, GLib, Variable } from "astal" +import AstalTray from "gi://AstalTray?version=0.1"; + +type NiriWorkspace = { + id: number, + idx: number, + name: string | null, + output: string, + is_active: boolean, + is_focused: boolean, + active_window_id: number | null, +}; + +function getWorkspaces(): NiriWorkspace[] { + // NOTE: this works only in non-systemd environment on NixOS + // TODO: try to use Niri socket if it is documented + return JSON.parse(exec("niri msg -j workspaces")); +} + +function getWorkspacesByOutput(output: string): NiriWorkspace[] { + return getWorkspaces().filter(workspace => workspace.output == output).sort((a, b) => a.idx - b.idx); +} + +function focusWorkspace(idx: number) { + // NOTE: this works only in non-systemd environment on NixOS + // TODO: try to use Niri socket if it is documented + exec(`niri msg action focus-workspace ${idx}`); +} + +type WorkspaceButtonArguments = { + idx: number, + isActive: boolean, + isFocused: boolean, +}; + +function WorkspaceButton(args: WorkspaceButtonArguments) { + const classes: string[] = []; + args.isActive && classes.push("active"); + args.isFocused && classes.push("focused"); + return <button cssClasses={classes} onClicked={() => focusWorkspace(args.idx)} /> +} + +type WorkspacesArguments = { + connector: string, +}; + +function Workspaces(args: WorkspacesArguments) { + // NOTE: it is pretty inefficient and not so much responsive + // TODO: it would be better to use Niri socket in the future + const workspaces: Variable<NiriWorkspace[]> = Variable(getWorkspacesByOutput(args.connector)) + .poll(1000, () => getWorkspacesByOutput(args.connector)); + + // BUG: on workspace change there's no CSS transition + // I guess it is due to rerender here + return <box cssClasses={["Workspaces"]}> + {workspaces(v => v.map(workspace => <WorkspaceButton idx={workspace.idx} isActive={workspace.is_active} isFocused={workspace.is_focused} />))} + </box> +} + + +function Tray() { + // BUG: personally I have one fantom icon being along other tray icons + // For now I don't have any ideas why this is happening + // TODO: rewrite this using more elements, as this is really restricted design + const tray = AstalTray.get_default(); + + return <box cssClasses={["Tray"]}> + {bind(tray, "items").as(items => + items.map(item => + <menubutton + setup={self => self.insert_action_group("dbusmenu", item.actionGroup)} + tooltipText={bind(item, "tooltipMarkup")} + > + <image gicon={bind(item, "gicon")} /> + {Gtk.PopoverMenu.new_from_model(item.menuModel)} + </menubutton> + ) + )} + </box> +} + + +type TimeArguments = { + format: string, +}; + +function Time(args: TimeArguments) { + const time = Variable<GLib.DateTime>(GLib.DateTime.new_now_local()).poll(1000, () => GLib.DateTime.new_now_local()) + + return <box> + {time(v => v.format(args.format))} + </box> +} + + +export default function Bar(gdkmonitor: Gdk.Monitor) { + const { TOP, LEFT, RIGHT } = Astal.WindowAnchor + + return <window + visible + name={"Bar"} + cssClasses={["Bar"]} + gdkmonitor={gdkmonitor} + exclusivity={Astal.Exclusivity.EXCLUSIVE} + anchor={TOP | LEFT | RIGHT} + application={App}> + <centerbox cssClasses={["bar-container"]}> + <box halign={Gtk.Align.START}> + <Workspaces connector={gdkmonitor.get_connector()!} /> + </box> + + <box halign={Gtk.Align.CENTER}> + </box> + + <box halign={Gtk.Align.END}> + <Tray /> + <Time format="%I:%M:%S %p %Z" /> + </box> + </centerbox> + </window> +} |