1use std::collections::{BTreeMap, BTreeSet};
2
3use button::Button;
4use config::Config;
5use error::Error;
6use state::State;
7use waybar_cffi::{
8 gtk::{
9 self,
10 glib::MainContext,
11 traits::{BoxExt, ContainerExt, StyleContextExt, WidgetExt},
12 Orientation,
13 },
14 waybar_module, Module,
15};
16
17mod button;
18mod config;
19mod error;
20mod icon;
21mod niri;
22mod state;
23
24struct TaskbarModule {}
25
26impl Module for TaskbarModule {
27 type Config = Config;
28
29 fn init(info: &waybar_cffi::InitInfo, config: Config) -> Self {
30 let module = Self {};
31 let state = State::new(config);
32
33 if let Err(e) = init(info, state) {
34 eprintln!("niri taskbar module init error: {e:?}");
35 }
36
37 module
38 }
39}
40
41waybar_module!(TaskbarModule);
42
43fn init(info: &waybar_cffi::InitInfo, state: State) -> Result<(), Error> {
44 let root = info.get_root_widget();
46 let container = gtk::Box::new(Orientation::Horizontal, 0);
47 container.style_context().add_class("niri-taskbar");
48 root.add(&container);
49
50 let context = MainContext::default();
52 let stream = state.niri().window_stream()?;
53
54 context.spawn_local(async move {
55 let mut buttons = BTreeMap::new();
59
60 while let Some(windows) = stream.next().await {
61 let mut omitted = buttons.keys().copied().collect::<BTreeSet<_>>();
63
64 for window in windows.into_iter() {
65 let button = buttons.entry(window.id).or_insert_with(|| {
66 let button = Button::new(&state, &window);
67
68 container.add(button.widget());
71 button
72 });
73
74 button.set_focus(window.is_focused);
76 button.set_title(window.title.as_deref());
77
78 omitted.remove(&window.id);
80
81 container.reorder_child(button.widget(), -1);
84 }
85
86 for id in omitted.into_iter() {
88 if let Some(button) = buttons.remove(&id) {
89 container.remove(button.widget());
90 }
91 }
92
93 container.show_all();
95 }
96 });
97
98 Ok(())
99}