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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
import { App, Astal, Gtk, Gdk } from "astal/gtk4"
import { bind, exec, GLib, Variable } from "astal"
import AstalTray from "gi://AstalTray?version=0.1";
import AstalWp from "gi://AstalWp?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 AudioVolume() {
const wireplumber = AstalWp.get_default()!;
const speaker = wireplumber.audio.get_default_speaker()!;
return <box cssClasses={["AudioVolume"]}>
<image iconName={bind(speaker, "volumeIcon")} />
<slider
hexpand
onScroll={(_self, dx, dy) => speaker.volume += (dx + dy) * -0.05}
onChangeValue={self => { speaker.volume = self.value; }}
value={bind(speaker, "volume")}
/>
</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}>
<AudioVolume />
<Tray />
<Time format="%I:%M:%S %p %Z" />
</box>
</centerbox>
</window>
}
|