step 5: live Hyprland workspace buttons via EventStream

This commit is contained in:
Breadway 2026-05-17 08:34:31 +08:00
parent 542cbae939
commit 241e3bc2cb
4 changed files with 109 additions and 10 deletions

View file

@ -4,14 +4,27 @@ mod theme;
use gtk4::prelude::*;
use gtk4_layer_shell::{Edge, Layer, LayerShell};
use hyprland::data::Workspace;
use hyprland::shared::WorkspaceId;
use relm4::prelude::*;
struct App;
pub struct App {
workspaces: Vec<Workspace>,
active_ws: WorkspaceId,
// Stored handle so update() can manipulate the live widget directly.
workspace_box: gtk4::Box,
}
#[relm4::component]
#[derive(Debug)]
pub enum AppInput {
WorkspaceList(Vec<Workspace>),
ActiveWorkspace(WorkspaceId),
}
#[relm4::component(pub)]
impl SimpleComponent for App {
type Init = ();
type Input = ();
type Input = AppInput;
type Output = ();
view! {
@ -24,11 +37,13 @@ impl SimpleComponent for App {
#[wrap(Some)]
set_start_widget = &gtk::Box {
set_orientation: gtk::Orientation::Horizontal,
set_spacing: 4,
set_spacing: 0,
set_margin_start: 8,
gtk::Label {
set_label: "1 2 3",
#[name = "workspace_box"]
gtk::Box {
set_orientation: gtk::Orientation::Horizontal,
set_spacing: 4,
}
},
@ -54,7 +69,7 @@ impl SimpleComponent for App {
fn init(
_: Self::Init,
root: Self::Root,
_sender: ComponentSender<Self>,
sender: ComponentSender<Self>,
) -> ComponentParts<Self> {
root.init_layer_shell();
root.set_layer(Layer::Top);
@ -63,15 +78,48 @@ impl SimpleComponent for App {
root.set_anchor(Edge::Right, true);
root.set_exclusive_zone(32);
let model = App;
// Placeholder until view_output! gives us the real handle.
let mut model = App {
workspaces: vec![],
active_ws: 1,
workspace_box: gtk4::Box::new(gtk4::Orientation::Horizontal, 4),
};
let widgets = view_output!();
// Swap in the actual widget so update() can reach it.
model.workspace_box = widgets.workspace_box.clone();
theme::apply();
bar::workspaces::spawn_watcher(sender);
ComponentParts { model, widgets }
}
fn update(&mut self, _: Self::Input, _: ComponentSender<Self>) {}
fn update(&mut self, msg: Self::Input, _: ComponentSender<Self>) {
match msg {
AppInput::WorkspaceList(list) => {
let mut sorted = list;
sorted.sort_by_key(|w| w.id);
self.workspaces = sorted;
}
AppInput::ActiveWorkspace(id) => {
self.active_ws = id;
}
}
self.rebuild_buttons();
}
}
impl App {
fn rebuild_buttons(&self) {
while let Some(child) = self.workspace_box.first_child() {
self.workspace_box.remove(&child);
}
for ws in &self.workspaces {
self.workspace_box
.append(&bar::workspaces::make_button(ws.id, &ws.name, self.active_ws));
}
}
}
fn main() {