diff --git a/docs/Configuration guide.md b/docs/Configuration guide.md index 9d50132..8b67280 100644 --- a/docs/Configuration guide.md +++ b/docs/Configuration guide.md @@ -279,10 +279,10 @@ The following table lists each of the top-level bar config options: The following table lists each of the module-level options that are present on **all** modules. For details on available modules and each of their config options, check the sidebar. -For information on the `Script` type, see [here](script). +For information on the `Script` type, and embedding scripts in strings, see [here](script). | Name | Type | Default | Description | |------------|--------------------|---------|--------------------------------------------------------------------------------------------------------------------| | `show_if` | `Script [polling]` | `null` | Polls the script to check its exit code. If exit code is zero, the module is shown. For other codes, it is hidden. | | `on_click` | `Script [polling]` | `null` | Runs the script when the module is clicked. | -| `tooltip` | `string` | `null` | Shows this text on hover. | +| `tooltip` | `string` | `null` | Shows this text on hover. Supports embedding scripts between `{{double braces}}`. | diff --git a/docs/Scripts.md b/docs/Scripts.md index b5fde4a..04b28c3 100644 --- a/docs/Scripts.md +++ b/docs/Scripts.md @@ -42,6 +42,17 @@ For example: poll:5000:uptime -p | cut -d ' ' -f2- ``` +#### Embedding + +Some string config options support "embedding scripts". This allows you to mix static/dynamic content. +An example of this is the common `tooltip` option. + +Scripts can be embedded in these cases using `{{double braces}}` and the shorthand syntax: + +```json +"Up: {{30000:uptime -p | cut -d ' ' -f2-}}" +``` + ### Longhand (object) An object consisting of the `cmd` key and optionally the `mode` and/or `interval` keys. diff --git a/examples/config.corn b/examples/config.corn index cd293e9..195d183 100644 --- a/examples/config.corn +++ b/examples/config.corn @@ -67,6 +67,8 @@ let { { type = "label" name = "uptime" label = "Up: {{30000:uptime -p | cut -d ' ' -f2-}}" } ] } ] + + tooltip = "Up: {{30000:uptime -p | cut -d ' ' -f2-}}" } $left = [ $workspaces $launcher ] diff --git a/src/bar.rs b/src/bar.rs index 2ca6501..080a4a9 100644 --- a/src/bar.rs +++ b/src/bar.rs @@ -7,6 +7,7 @@ use crate::modules::workspaces::WorkspaceUpdate; use crate::modules::{Module, ModuleInfoBuilder, ModuleLocation, ModuleUpdateEvent, WidgetContext}; use crate::popup::Popup; use crate::script::{OutputStream, Script}; +use crate::widgets::DynamicString; use crate::{await_sync, Config}; use chrono::{DateTime, Local}; use color_eyre::Result; @@ -212,7 +213,10 @@ fn add_modules( } if let Some(tooltip) = common.tooltip { - container.set_tooltip_text(Some(&tooltip)); + DynamicString::new(&tooltip, move |string| { + container.set_tooltip_text(Some(&string)); + Continue(true) + }); } let has_popup = widget.popup.is_some(); diff --git a/src/modules/custom.rs b/src/modules/custom.rs index fe4c3e6..34b1061 100644 --- a/src/modules/custom.rs +++ b/src/modules/custom.rs @@ -2,7 +2,7 @@ use crate::config::CommonConfig; use crate::modules::{Module, ModuleInfo, ModuleUpdateEvent, ModuleWidget, WidgetContext}; use crate::popup::{ButtonGeometry, Popup}; use crate::script::Script; -use crate::widgets::DynamicLabel; +use crate::widgets::DynamicString; use color_eyre::{Report, Result}; use gtk::prelude::*; use gtk::{Button, Label, Orientation}; @@ -61,7 +61,7 @@ impl Widget { fn add_to(self, parent: >k::Box, tx: Sender, bar_orientation: Orientation) { match self.widget_type { WidgetType::Box => parent.add(&self.into_box(&tx, bar_orientation)), - WidgetType::Label => parent.add(&self.into_label().label), + WidgetType::Label => parent.add(&self.into_label()), WidgetType::Button => parent.add(&self.into_button(tx, bar_orientation)), } } @@ -95,7 +95,7 @@ impl Widget { } /// Creates a `gtk::Label` from this widget - fn into_label(self) -> DynamicLabel { + fn into_label(self) -> Label { let mut builder = Label::builder().use_markup(true); if let Some(name) = self.name { @@ -110,7 +110,17 @@ impl Widget { let text = self.label.map_or_else(String::new, |text| text); - DynamicLabel::new(label, &text) + { + let label = label.clone(); + DynamicString::new(&text, move |string| { + label.set_label(&string); + Continue(true) + }); + } + + label + + // DynamicString::new(label, &text) } /// Creates a `gtk::Button` from this widget diff --git a/src/widgets/dynamic_label.rs b/src/widgets/dynamic_label.rs index 790f2fb..b693e76 100644 --- a/src/widgets/dynamic_label.rs +++ b/src/widgets/dynamic_label.rs @@ -5,17 +5,20 @@ use std::sync::{Arc, Mutex}; use tokio::spawn; #[derive(Debug)] -enum DynamicLabelSegment { +enum DynamicStringSegment { Static(String), Dynamic(Script), } -pub struct DynamicLabel { - pub label: gtk::Label, +pub struct DynamicString { + // pub label: gtk::Label, } -impl DynamicLabel { - pub fn new(label: gtk::Label, input: &str) -> Self { +impl DynamicString { + pub fn new(input: &str, f: F) -> Self + where + F: FnMut(String) -> Continue + 'static, + { let mut segments = vec![]; let mut chars = input.chars().collect::>(); @@ -36,7 +39,7 @@ impl DynamicLabel { let len = str.len(); ( - DynamicLabelSegment::Dynamic(Script::from(str.as_str())), + DynamicStringSegment::Dynamic(Script::from(str.as_str())), len + SKIP_BRACKETS, ) } else { @@ -49,7 +52,7 @@ impl DynamicLabel { let len = str.len(); - (DynamicLabelSegment::Static(str), len) + (DynamicStringSegment::Static(str), len) }; assert_ne!(skip, 0); @@ -63,13 +66,13 @@ impl DynamicLabel { for (i, segment) in segments.into_iter().enumerate() { match segment { - DynamicLabelSegment::Static(str) => { + DynamicStringSegment::Static(str) => { label_parts .lock() .expect("Failed to get lock on label parts") .insert(i, str); } - DynamicLabelSegment::Dynamic(script) => { + DynamicStringSegment::Dynamic(script) => { let tx = tx.clone(); let label_parts = label_parts.clone(); @@ -77,11 +80,21 @@ impl DynamicLabel { script .run(|(out, _)| { if let OutputStream::Stdout(out) = out { - label_parts + let mut label_parts = label_parts .lock() - .expect("Failed to get lock on label parts") + .expect("Failed to get lock on label parts"); + + label_parts + // .lock() + // .expect("Failed to get lock on label parts") .insert(i, out); - tx.send(()).expect("Failed to send update"); + + let string = label_parts + .iter() + .map(|(_, part)| part.as_str()) + .collect::(); + + tx.send(string).expect("Failed to send update"); } }) .await; @@ -90,25 +103,22 @@ impl DynamicLabel { } } - tx.send(()).expect("Failed to send update"); - + // initialize { - 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::(); + let label_parts = label_parts + .lock() + .expect("Failed to get lock on label parts") + .iter() + .map(|(_, part)| part.as_str()) + .collect::(); - label.set_label(new_label.as_str()); - - Continue(true) - }); + tx.send(label_parts).expect("Failed to send update"); } - Self { label } + rx.attach(None, f); + + // Self { label } + Self {} } } @@ -121,7 +131,13 @@ mod tests { // TODO: see if we can run gtk tests in ci if gtk::init().is_ok() { let label = gtk::Label::new(None); - DynamicLabel::new(label, "Uptime: {{1000:uptime -p | cut -d ' ' -f2-}}"); + DynamicString::new( + "Uptime: {{1000:uptime -p | cut -d ' ' -f2-}}", + move |string| { + label.set_label(string); + Continue(true) + }, + ); } } } diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs index a1fdcd8..43845de 100644 --- a/src/widgets/mod.rs +++ b/src/widgets/mod.rs @@ -1,3 +1,3 @@ mod dynamic_label; -pub use dynamic_label::DynamicLabel; +pub use dynamic_label::DynamicString;