diff options
-rw-r--r-- | app.ts | 12 | ||||
-rw-r--r-- | style.scss | 48 | ||||
-rw-r--r-- | widget/Bar.tsx | 124 |
3 files changed, 136 insertions, 48 deletions
@@ -2,9 +2,13 @@ import { App } from "astal/gtk4" import style from "./style.scss" import Bar from "./widget/Bar" +const windows = [ + Bar, +] + App.start({ - css: style, - main() { - App.get_monitors().map(Bar) - }, + css: style, + main() { + windows.forEach(window => App.get_monitors().map(window)) + }, }) @@ -1,20 +1,42 @@ // https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gtk/theme/Adwaita/_colors-public.scss -$fg-color: #{"@theme_fg_color"}; -$bg-color: #{"@theme_bg_color"}; +// $fg-color: #{"@theme_fg_color"}; +// $bg-color: #{"@theme_bg_color"}; window.Bar { - background: transparent; - color: $fg-color; - font-weight: bold; - - >centerbox { - background: $bg-color; - border-radius: 10px; - margin: 8px; + border: none; + box-shadow: none; + background-color: #000; + color: #fff; + + >box { + padding: 4px 0; + } +} + +box.Workspaces { + padding-left: 3px; + + >button { + min-width: 10px; + min-height: 10px; + border-radius: 5px; + + border: none; + margin: 2px 4px; + padding: 0; + background: #ccc; + + transition: min-width 2s ease-in-out; + // transition-property: min-width; + // transition-duration: 2s; + // transition-timing-function: ease-out; + + &.active { + min-width: 25px; } - button { - border-radius: 8px; - margin: 2px; + &.focused { + background: #AD49E1; } + } } 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> } |