1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
import type { Monitor } from "@/services/monitors";
import type { AstalWidget } from "@/utils/types";
import { Variable } from "astal";
import { Astal, Gtk } from "astal/gtk3";
import { navbar as config } from "config";
import AstalHyprland from "gi://AstalHyprland";
import SideBar, { awaitSidebar, paneNames, switchPane, type PaneName } from "./sidebar";
const getPaneIcon = (name: PaneName) => {
if (name === "dashboard") return "dashboard";
if (name === "audio") return "tune";
if (name === "connectivity") return "settings_ethernet";
if (name === "packages") return "package_2";
if (name === "notifpane") return "notifications";
return "date_range";
};
const getPaneName = (name: PaneName) => {
if (name === "dashboard") return "Dash";
if (name === "audio") return "Audio";
if (name === "connectivity") return "Conn";
if (name === "packages") return "Pkgs";
if (name === "notifpane") return "Alrts";
return "Time";
};
const hookIsCurrent = (
self: AstalWidget,
sidebar: Variable<SideBar | null>,
name: PaneName,
callback: (isCurrent: boolean) => void
) => {
const unsub = sidebar.subscribe(s => {
if (!s) return;
self.hook(s.shown, (_, v) => callback(s.visible && v === name));
self.hook(s, "notify::visible", () => callback(s.visible && s.shown.get() === name));
callback(s.visible && s.shown.get() === name);
unsub();
});
};
const PaneButton = ({
monitor,
name,
sidebar,
}: {
monitor: Monitor;
name: PaneName;
sidebar: Variable<SideBar | null>;
}) => (
<button
cursor="pointer"
onClick={() => switchPane(monitor, name)}
setup={self => hookIsCurrent(self, sidebar, name, c => self.toggleClassName("current", c))}
>
<box vertical className="pane-button">
<label className="icon" label={getPaneIcon(name)} />
<revealer
transitionType={Gtk.RevealerTransitionType.SLIDE_DOWN}
transitionDuration={150}
setup={self => hookIsCurrent(self, sidebar, name, c => self.set_reveal_child(c))}
>
<label className="label" label={getPaneName(name)} />
</revealer>
</box>
</button>
);
export default ({ monitor }: { monitor: Monitor }) => {
const sidebar = Variable<SideBar | null>(null);
awaitSidebar(monitor).then(s => sidebar.set(s));
return (
<window
namespace="caelestia-navbar"
monitor={monitor.id}
anchor={Astal.WindowAnchor.TOP | Astal.WindowAnchor.LEFT | Astal.WindowAnchor.BOTTOM}
exclusivity={Astal.Exclusivity.EXCLUSIVE}
visible={config.persistent.get()}
setup={self => {
const hyprland = AstalHyprland.get_default();
const visible = Variable(config.persistent.get());
visible.poll(100, () => {
const width = self.visible
? Math.max(config.appearWidth.get(), self.get_allocated_width())
: config.appearWidth.get();
return hyprland.get_cursor_position().x < width;
});
if (config.persistent.get()) visible.stopPoll();
self.hook(config.persistent, (_, v) => {
if (v) {
visible.stopPoll();
visible.set(true);
} else visible.startPoll();
});
self.hook(visible, (_, v) => self.set_visible(v));
self.connect("destroy", () => visible.drop());
}}
>
<eventbox
onScroll={(_, event) => {
const shown = sidebar.get()?.shown;
if (!shown) return;
const idx = paneNames.indexOf(shown.get());
if (event.delta_y > 0) shown.set(paneNames[Math.min(paneNames.length - 1, idx + 1)]);
else shown.set(paneNames[Math.max(0, idx - 1)]);
}}
>
<box vertical className="navbar">
{paneNames.map(n => (
<PaneButton monitor={monitor} name={n} sidebar={sidebar} />
))}
</box>
</eventbox>
</window>
);
};
|