Add: LeftWM workspace widget
- Show tags in workspace "0" - Callback to switch tag on click
This commit is contained in:
parent
c284df88a1
commit
b679952b88
@ -1,17 +1,30 @@
|
|||||||
use crate::TopBar;
|
use crate::TopBar;
|
||||||
use slint::{ComponentHandle, Timer, TimerMode, SharedString};
|
use serde::Deserialize;
|
||||||
|
use slint::{ComponentHandle, SharedString, Timer, TimerMode, VecModel};
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use serde::Deserialize;
|
|
||||||
|
|
||||||
// Minimal JSON structure
|
// Minimal JSON structures extracted from leftwm-state -q
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct Tag {
|
||||||
|
index: usize,
|
||||||
|
mine: bool,
|
||||||
|
busy: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct Workspace {
|
||||||
|
tags: Vec<Tag>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct LeftState {
|
struct LeftState {
|
||||||
window_title: String,
|
window_title: String,
|
||||||
|
workspaces: Vec<Workspace>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run leftwm-state and return JSON output (or empty string)
|
// Helpers
|
||||||
fn get_leftwm_state() -> String {
|
fn run_leftwm_state() -> String {
|
||||||
Command::new("leftwm-state")
|
Command::new("leftwm-state")
|
||||||
.arg("-q")
|
.arg("-q")
|
||||||
.output()
|
.output()
|
||||||
@ -19,31 +32,64 @@ fn get_leftwm_state() -> String {
|
|||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse only the window title
|
// Pick icon based on tag state
|
||||||
fn parse_window_title(json: &str) -> String {
|
fn icon_for_tag(tag: &Tag) -> &'static str {
|
||||||
serde_json::from_str::<LeftState>(json)
|
if tag.mine {
|
||||||
.map(|v| v.window_title)
|
""
|
||||||
.unwrap_or_default()
|
} else if tag.busy {
|
||||||
|
""
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Main updater loop
|
// Main polling loop
|
||||||
fn start_leftwm_updater(ui: &TopBar) {
|
fn start_leftwm_updater(ui: &TopBar) {
|
||||||
let weak = ui.as_weak();
|
let weak = ui.as_weak();
|
||||||
|
|
||||||
let timer = Box::leak(Box::new(Timer::default()));
|
let timer = Box::leak(Box::new(Timer::default()));
|
||||||
|
|
||||||
// Poll every ~300 ms (fast enough for responsiveness)
|
|
||||||
timer.start(TimerMode::Repeated, Duration::from_millis(300), move || {
|
timer.start(TimerMode::Repeated, Duration::from_millis(300), move || {
|
||||||
if let Some(ui) = weak.upgrade() {
|
let Some(ui) = weak.upgrade() else { return };
|
||||||
let state = get_leftwm_state();
|
|
||||||
let title = parse_window_title(&state);
|
// Run leftwm-state -q once
|
||||||
// Send to UI
|
let json = run_leftwm_state();
|
||||||
ui.set_current_window_title(SharedString::from(title));
|
|
||||||
}
|
// Parse JSON
|
||||||
|
let parsed: LeftState = match serde_json::from_str(&json) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(_) => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Update window title
|
||||||
|
ui.set_current_window_title(SharedString::from(parsed.window_title));
|
||||||
|
|
||||||
|
// Extract current workspace (always index 0)
|
||||||
|
let Some(ws) = parsed.workspaces.get(0) else { return };
|
||||||
|
|
||||||
|
// Build vector of icons
|
||||||
|
let icons: Vec<SharedString> = ws
|
||||||
|
.tags
|
||||||
|
.iter()
|
||||||
|
.map(|t| SharedString::from(icon_for_tag(t)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// Push array to Slint as VecModel
|
||||||
|
let model = std::rc::Rc::new(VecModel::from(icons));
|
||||||
|
ui.set_tag_icons(model.into());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Public entry
|
// Public entry point
|
||||||
pub fn install(ui: &TopBar) {
|
pub fn install(ui: &TopBar) {
|
||||||
|
// Install the polling loop
|
||||||
start_leftwm_updater(ui);
|
start_leftwm_updater(ui);
|
||||||
|
|
||||||
|
// Tag switching callback
|
||||||
|
ui.on_go_to_tag(|i| {
|
||||||
|
// Command: `leftwm-command SendWorkspaceToTag workspace tag_number`
|
||||||
|
// Assuming workspace 0 (first monitor)
|
||||||
|
let _ = Command::new("leftwm-command")
|
||||||
|
.arg(format!("SendWorkspaceToTag 0 {i}"))
|
||||||
|
.spawn();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ export component SquareIconWidget {
|
|||||||
// Basic parameters shared by all square icon widgets
|
// Basic parameters shared by all square icon widgets
|
||||||
in property <int> bar_height;
|
in property <int> bar_height;
|
||||||
in property <string> icon_text;
|
in property <string> icon_text;
|
||||||
|
in property <int> icon_font_size;
|
||||||
in property <string> tooltip_text;
|
in property <string> tooltip_text;
|
||||||
|
|
||||||
// Colors
|
// Colors
|
||||||
@ -34,6 +35,7 @@ export component SquareIconWidget {
|
|||||||
Text {
|
Text {
|
||||||
text: icon_text; // The icon
|
text: icon_text; // The icon
|
||||||
color: icon_color;
|
color: icon_color;
|
||||||
|
font-size: icon_font_size *1px;
|
||||||
vertical-alignment: center;
|
vertical-alignment: center;
|
||||||
horizontal-alignment: center;
|
horizontal-alignment: center;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,6 +12,9 @@ export component TopBar inherits Window {
|
|||||||
|
|
||||||
// Right side
|
// Right side
|
||||||
in property<string> current_window_title;
|
in property<string> current_window_title;
|
||||||
|
in property<int> tag_icon_font_size: bar_height;
|
||||||
|
in property <[string]> tag_icons;
|
||||||
|
callback go_to_tag(int);
|
||||||
|
|
||||||
// Time widget
|
// Time widget
|
||||||
in-out property <string> time_text;
|
in-out property <string> time_text;
|
||||||
@ -114,7 +117,7 @@ export component TopBar inherits Window {
|
|||||||
// Left side
|
// Left side
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: parent.width/3;
|
width: parent.width/3;
|
||||||
//background: #46adbb79;
|
background: #46adbb79;
|
||||||
HorizontalLayout {
|
HorizontalLayout {
|
||||||
alignment: start; // Left-align
|
alignment: start; // Left-align
|
||||||
padding-left: 4px;
|
padding-left: 4px;
|
||||||
@ -133,11 +136,21 @@ export component TopBar inherits Window {
|
|||||||
// Middle
|
// Middle
|
||||||
Rectangle {
|
Rectangle {
|
||||||
background: #5d8b3c79;
|
background: #5d8b3c79;
|
||||||
|
width: parent.width/3;
|
||||||
|
HorizontalLayout {
|
||||||
|
alignment: center; // Right-align
|
||||||
|
for ico[i] in root.tag_icons: SquareIconWidget {
|
||||||
|
icon_text: ico;
|
||||||
|
bar_height: root.bar_height;
|
||||||
|
icon_font_size: root.tag_icon_font_size;
|
||||||
|
square_btn_callback => root.go_to_tag(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Right side
|
// Right side
|
||||||
Rectangle {
|
Rectangle {
|
||||||
//background: #c2779a7a;
|
background: #c2779a7a;
|
||||||
width: parent.width/3;
|
width: parent.width/3;
|
||||||
|
|
||||||
HorizontalLayout {
|
HorizontalLayout {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user