mirror of
https://github.com/Zedfrigg/ironbar.git
synced 2025-07-01 10:41:03 +02:00
feat(custom): ability to embed scripts in labels for dynamic content
Fully resolves #34.
This commit is contained in:
parent
e274ba39cd
commit
5d153a02fc
6 changed files with 196 additions and 33 deletions
|
@ -27,7 +27,7 @@ pub struct CommonConfig {
|
|||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
#[serde(tag = "type", rename_all = "kebab-case")]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum ModuleConfig {
|
||||
Clock(ClockModule),
|
||||
Mpd(MpdModule),
|
||||
|
@ -48,7 +48,7 @@ pub enum MonitorConfig {
|
|||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Copy, Clone, PartialEq, Eq)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum BarPosition {
|
||||
Top,
|
||||
Bottom,
|
||||
|
|
|
@ -8,6 +8,7 @@ mod modules;
|
|||
mod popup;
|
||||
mod script;
|
||||
mod style;
|
||||
mod widgets;
|
||||
|
||||
use crate::bar::create_bar;
|
||||
use crate::config::{Config, MonitorConfig};
|
||||
|
|
|
@ -4,6 +4,7 @@ use crate::config::CommonConfig;
|
|||
use color_eyre::{Report, Result};
|
||||
use crate::script::Script;
|
||||
use gtk::prelude::*;
|
||||
use crate::widgets::DynamicLabel;
|
||||
use gtk::{Button, Label, Orientation};
|
||||
use serde::Deserialize;
|
||||
use tokio::spawn;
|
||||
|
@ -48,7 +49,7 @@ pub struct Widget {
|
|||
|
||||
/// Supported GTK widget types
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum WidgetType {
|
||||
Box,
|
||||
Label,
|
||||
|
@ -60,7 +61,7 @@ impl Widget {
|
|||
fn add_to(self, parent: >k::Box, tx: Sender<ExecEvent>, bar_orientation: Orientation) {
|
||||
match self.widget_type {
|
||||
WidgetType::Box => parent.add(&self.into_box(&tx, bar_orientation)),
|
||||
WidgetType::Label => parent.add(&self.into_label()),
|
||||
WidgetType::Label => parent.add(&self.into_label().label),
|
||||
WidgetType::Button => parent.add(&self.into_button(tx, bar_orientation)),
|
||||
}
|
||||
}
|
||||
|
@ -94,7 +95,7 @@ impl Widget {
|
|||
}
|
||||
|
||||
/// Creates a `gtk::Label` from this widget
|
||||
fn into_label(self) -> Label {
|
||||
fn into_label(self) -> DynamicLabel {
|
||||
let mut builder = Label::builder().use_markup(true);
|
||||
|
||||
if let Some(name) = self.name {
|
||||
|
@ -103,15 +104,13 @@ impl Widget {
|
|||
|
||||
let label = builder.build();
|
||||
|
||||
if let Some(text) = self.label {
|
||||
label.set_markup(&text);
|
||||
}
|
||||
|
||||
if let Some(class) = self.class {
|
||||
label.style_context().add_class(&class);
|
||||
}
|
||||
|
||||
label
|
||||
let text = self.label.map_or_else(String::new, |text| text);
|
||||
|
||||
DynamicLabel::new(label, &text)
|
||||
}
|
||||
|
||||
/// Creates a `gtk::Button` from this widget
|
||||
|
|
125
src/widgets/dynamic_label.rs
Normal file
125
src/widgets/dynamic_label.rs
Normal file
|
@ -0,0 +1,125 @@
|
|||
use crate::script::{OutputStream, Script};
|
||||
use gtk::prelude::*;
|
||||
use indexmap::IndexMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use tokio::spawn;
|
||||
|
||||
#[derive(Debug)]
|
||||
enum DynamicLabelSegment {
|
||||
Static(String),
|
||||
Dynamic(Script),
|
||||
}
|
||||
|
||||
pub struct DynamicLabel {
|
||||
pub label: gtk::Label,
|
||||
}
|
||||
|
||||
impl DynamicLabel {
|
||||
pub fn new(label: gtk::Label, input: &str) -> Self {
|
||||
let mut segments = vec![];
|
||||
|
||||
let mut chars = input.chars().collect::<Vec<_>>();
|
||||
while !chars.is_empty() {
|
||||
let char = &chars[..=1];
|
||||
|
||||
let (token, skip) = if let ['{', '{'] = char {
|
||||
const SKIP_BRACKETS: usize = 4;
|
||||
|
||||
let str = chars
|
||||
.iter()
|
||||
.skip(2)
|
||||
.enumerate()
|
||||
.take_while(|(i, &c)| c != '}' && chars[i + 1] != '}')
|
||||
.map(|(_, c)| c)
|
||||
.collect::<String>();
|
||||
|
||||
let len = str.len();
|
||||
|
||||
(
|
||||
DynamicLabelSegment::Dynamic(Script::from(str.as_str())),
|
||||
len + SKIP_BRACKETS,
|
||||
)
|
||||
} else {
|
||||
let str = chars
|
||||
.iter()
|
||||
.enumerate()
|
||||
.take_while(|(i, &c)| !(c == '{' && chars[i + 1] == '{'))
|
||||
.map(|(_, c)| c)
|
||||
.collect::<String>();
|
||||
|
||||
let len = str.len();
|
||||
|
||||
(DynamicLabelSegment::Static(str), len)
|
||||
};
|
||||
|
||||
assert_ne!(skip, 0);
|
||||
|
||||
segments.push(token);
|
||||
chars.drain(..skip);
|
||||
}
|
||||
|
||||
let label_parts = Arc::new(Mutex::new(IndexMap::new()));
|
||||
let (tx, rx) = glib::MainContext::channel(glib::PRIORITY_DEFAULT);
|
||||
|
||||
for (i, segment) in segments.into_iter().enumerate() {
|
||||
match segment {
|
||||
DynamicLabelSegment::Static(str) => {
|
||||
label_parts
|
||||
.lock()
|
||||
.expect("Failed to get lock on label parts")
|
||||
.insert(i, str);
|
||||
}
|
||||
DynamicLabelSegment::Dynamic(script) => {
|
||||
let tx = tx.clone();
|
||||
let label_parts = label_parts.clone();
|
||||
|
||||
spawn(async move {
|
||||
script
|
||||
.run(|(out, _)| {
|
||||
if let OutputStream::Stdout(out) = out {
|
||||
label_parts
|
||||
.lock()
|
||||
.expect("Failed to get lock on label parts")
|
||||
.insert(i, out);
|
||||
tx.send(()).expect("Failed to send update");
|
||||
}
|
||||
})
|
||||
.await;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tx.send(()).expect("Failed to send update");
|
||||
|
||||
{
|
||||
let label = label.clone();
|
||||
rx.attach(None, move |_| {
|
||||
let new_label = label_parts
|
||||
.lock()
|
||||
.expect("Failed to get lock on label parts")
|
||||
.iter()
|
||||
.map(|(_, part)| part.as_str())
|
||||
.collect::<String>();
|
||||
|
||||
label.set_label(new_label.as_str());
|
||||
|
||||
Continue(true)
|
||||
});
|
||||
}
|
||||
|
||||
Self { label }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test() {
|
||||
gtk::init().unwrap();
|
||||
let label = gtk::Label::new(None);
|
||||
DynamicLabel::new(label, "Uptime: {{1000:uptime -p | cut -d ' ' -f2-}}");
|
||||
}
|
||||
}
|
3
src/widgets/mod.rs
Normal file
3
src/widgets/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
mod dynamic_label;
|
||||
|
||||
pub use dynamic_label::DynamicLabel;
|
Loading…
Add table
Add a link
Reference in a new issue