step 5: live Hyprland workspace buttons via EventStream
This commit is contained in:
parent
542cbae939
commit
241e3bc2cb
4 changed files with 109 additions and 10 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -12,6 +12,7 @@ checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
|||
name = "aster"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"futures-lite",
|
||||
"gtk4",
|
||||
"gtk4-layer-shell",
|
||||
"hyprland",
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ edition = "2021"
|
|||
gtk4 = { version = "0.11", features = ["v4_12"] }
|
||||
gtk4-layer-shell = "0.8"
|
||||
relm4 = { version = "0.11", features = ["macros"] }
|
||||
hyprland = "0.4.0-beta.3"
|
||||
hyprland = { version = "0.4.0-beta.3", features = ["tokio"] }
|
||||
futures-lite = "2"
|
||||
zbus = { version = "5", default-features = false, features = ["tokio"] }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
|
|
|
|||
|
|
@ -1 +1,50 @@
|
|||
use futures_lite::StreamExt;
|
||||
use gtk4::prelude::*;
|
||||
use hyprland::{
|
||||
data::{Workspace, Workspaces},
|
||||
event_listener::{Event, EventStream},
|
||||
prelude::*,
|
||||
shared::WorkspaceId,
|
||||
};
|
||||
use relm4::ComponentSender;
|
||||
|
||||
use crate::AppInput;
|
||||
|
||||
pub fn spawn_watcher(sender: ComponentSender<crate::App>) {
|
||||
relm4::spawn(async move {
|
||||
if let Ok(ws) = Workspaces::get_async().await {
|
||||
sender.input(AppInput::WorkspaceList(ws.to_vec()));
|
||||
}
|
||||
if let Ok(active) = Workspace::get_active_async().await {
|
||||
sender.input(AppInput::ActiveWorkspace(active.id));
|
||||
}
|
||||
|
||||
let mut stream = EventStream::new();
|
||||
while let Some(Ok(event)) = stream.next().await {
|
||||
match event {
|
||||
Event::WorkspaceChanged(data) => {
|
||||
sender.input(AppInput::ActiveWorkspace(data.id));
|
||||
}
|
||||
Event::WorkspaceAdded(_) | Event::WorkspaceDeleted(_) => {
|
||||
if let Ok(ws) = Workspaces::get_async().await {
|
||||
sender.input(AppInput::WorkspaceList(ws.to_vec()));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn make_button(id: WorkspaceId, name: &str, active: WorkspaceId) -> gtk4::Button {
|
||||
let btn = gtk4::Button::with_label(name);
|
||||
btn.add_css_class("workspace-btn");
|
||||
if id == active {
|
||||
btn.add_css_class("active");
|
||||
}
|
||||
btn.connect_clicked(move |_| {
|
||||
use hyprland::dispatch::{Dispatch, DispatchType, WorkspaceIdentifierWithSpecial};
|
||||
let _ = Dispatch::call(DispatchType::Workspace(WorkspaceIdentifierWithSpecial::Id(id)));
|
||||
});
|
||||
btn
|
||||
}
|
||||
|
|
|
|||
66
src/main.rs
66
src/main.rs
|
|
@ -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 = >k::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() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue