From 684dedeb1a527a74de31cb405168ad39ec802a51 Mon Sep 17 00:00:00 2001
From: Mora Unie Youer <mora_unie_youer@riseup.net>
Date: Sun, 2 Mar 2025 14:16:48 +0300
Subject: feat: basic topbar features

---
 widget/Bar.tsx | 124 ++++++++++++++++++++++++++++++++++++++++++---------------
 1 file changed, 93 insertions(+), 31 deletions(-)

(limited to 'widget/Bar.tsx')

diff --git a/widget/Bar.tsx b/widget/Bar.tsx
index c2db8c5..02aa3ba 100644
--- a/widget/Bar.tsx
+++ b/widget/Bar.tsx
@@ -1,36 +1,98 @@
 import { App, Astal, Gtk, Gdk } from "astal/gtk4"
-import { Variable } from "astal"
+import { exec, GLib, Variable } from "astal"
+
+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>
+}
+
+
+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>
+}
 
-const time = Variable("").poll(1000, "date")
 
 export default function Bar(gdkmonitor: Gdk.Monitor) {
-    const { TOP, LEFT, RIGHT } = Astal.WindowAnchor
-
-    return <window
-        visible
-        cssClasses={["Bar"]}
-        gdkmonitor={gdkmonitor}
-        exclusivity={Astal.Exclusivity.EXCLUSIVE}
-        anchor={TOP | LEFT | RIGHT}
-        application={App}>
-        <centerbox cssName="centerbox">
-            <button
-                onClicked="echo hello"
-                hexpand
-                halign={Gtk.Align.CENTER}
-            >
-                Welcome to AGS!
-            </button>
-            <box />
-            <menubutton
-                hexpand
-                halign={Gtk.Align.CENTER}
-            >
-                <label label={time()} />
-                <popover>
-                    <Gtk.Calendar />
-                </popover>
-            </menubutton>
-        </centerbox>
-    </window>
+  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}>
+        <Time format="%I:%M:%S %p %Z" />
+      </box>
+    </centerbox>
+  </window>
 }
-- 
cgit v1.2.3-70-g09d2

