1
0
Fork 0
mirror of https://github.com/Zedfrigg/ironbar.git synced 2025-04-19 19:34:24 +02:00

feat: dynamic tooltips

Resolves #36
This commit is contained in:
Jake Stanger 2022-11-30 22:27:56 +00:00
parent 1320639d4e
commit d20972cb32
No known key found for this signature in database
GPG key ID: C51FC8F9CB0BEA61
7 changed files with 79 additions and 36 deletions

View file

@ -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. 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 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 | | 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. | | `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. | | `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}}`. |

View file

@ -42,6 +42,17 @@ For example:
poll:5000:uptime -p | cut -d ' ' -f2- 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) ### Longhand (object)
An object consisting of the `cmd` key and optionally the `mode` and/or `interval` keys. An object consisting of the `cmd` key and optionally the `mode` and/or `interval` keys.

View file

@ -67,6 +67,8 @@ let {
{ type = "label" name = "uptime" label = "Up: {{30000:uptime -p | cut -d ' ' -f2-}}" } { type = "label" name = "uptime" label = "Up: {{30000:uptime -p | cut -d ' ' -f2-}}" }
] ]
} ] } ]
tooltip = "Up: {{30000:uptime -p | cut -d ' ' -f2-}}"
} }
$left = [ $workspaces $launcher ] $left = [ $workspaces $launcher ]

View file

@ -7,6 +7,7 @@ use crate::modules::workspaces::WorkspaceUpdate;
use crate::modules::{Module, ModuleInfoBuilder, ModuleLocation, ModuleUpdateEvent, WidgetContext}; use crate::modules::{Module, ModuleInfoBuilder, ModuleLocation, ModuleUpdateEvent, WidgetContext};
use crate::popup::Popup; use crate::popup::Popup;
use crate::script::{OutputStream, Script}; use crate::script::{OutputStream, Script};
use crate::widgets::DynamicString;
use crate::{await_sync, Config}; use crate::{await_sync, Config};
use chrono::{DateTime, Local}; use chrono::{DateTime, Local};
use color_eyre::Result; use color_eyre::Result;
@ -212,7 +213,10 @@ fn add_modules(
} }
if let Some(tooltip) = common.tooltip { 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(); let has_popup = widget.popup.is_some();

View file

@ -2,7 +2,7 @@ use crate::config::CommonConfig;
use crate::modules::{Module, ModuleInfo, ModuleUpdateEvent, ModuleWidget, WidgetContext}; use crate::modules::{Module, ModuleInfo, ModuleUpdateEvent, ModuleWidget, WidgetContext};
use crate::popup::{ButtonGeometry, Popup}; use crate::popup::{ButtonGeometry, Popup};
use crate::script::Script; use crate::script::Script;
use crate::widgets::DynamicLabel; use crate::widgets::DynamicString;
use color_eyre::{Report, Result}; use color_eyre::{Report, Result};
use gtk::prelude::*; use gtk::prelude::*;
use gtk::{Button, Label, Orientation}; use gtk::{Button, Label, Orientation};
@ -61,7 +61,7 @@ impl Widget {
fn add_to(self, parent: &gtk::Box, tx: Sender<ExecEvent>, bar_orientation: Orientation) { fn add_to(self, parent: &gtk::Box, tx: Sender<ExecEvent>, bar_orientation: Orientation) {
match self.widget_type { match self.widget_type {
WidgetType::Box => parent.add(&self.into_box(&tx, bar_orientation)), 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)), WidgetType::Button => parent.add(&self.into_button(tx, bar_orientation)),
} }
} }
@ -95,7 +95,7 @@ impl Widget {
} }
/// Creates a `gtk::Label` from this 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); let mut builder = Label::builder().use_markup(true);
if let Some(name) = self.name { if let Some(name) = self.name {
@ -110,7 +110,17 @@ impl Widget {
let text = self.label.map_or_else(String::new, |text| text); 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 /// Creates a `gtk::Button` from this widget

View file

@ -5,17 +5,20 @@ use std::sync::{Arc, Mutex};
use tokio::spawn; use tokio::spawn;
#[derive(Debug)] #[derive(Debug)]
enum DynamicLabelSegment { enum DynamicStringSegment {
Static(String), Static(String),
Dynamic(Script), Dynamic(Script),
} }
pub struct DynamicLabel { pub struct DynamicString {
pub label: gtk::Label, // pub label: gtk::Label,
} }
impl DynamicLabel { impl DynamicString {
pub fn new(label: gtk::Label, input: &str) -> Self { pub fn new<F>(input: &str, f: F) -> Self
where
F: FnMut(String) -> Continue + 'static,
{
let mut segments = vec![]; let mut segments = vec![];
let mut chars = input.chars().collect::<Vec<_>>(); let mut chars = input.chars().collect::<Vec<_>>();
@ -36,7 +39,7 @@ impl DynamicLabel {
let len = str.len(); let len = str.len();
( (
DynamicLabelSegment::Dynamic(Script::from(str.as_str())), DynamicStringSegment::Dynamic(Script::from(str.as_str())),
len + SKIP_BRACKETS, len + SKIP_BRACKETS,
) )
} else { } else {
@ -49,7 +52,7 @@ impl DynamicLabel {
let len = str.len(); let len = str.len();
(DynamicLabelSegment::Static(str), len) (DynamicStringSegment::Static(str), len)
}; };
assert_ne!(skip, 0); assert_ne!(skip, 0);
@ -63,13 +66,13 @@ impl DynamicLabel {
for (i, segment) in segments.into_iter().enumerate() { for (i, segment) in segments.into_iter().enumerate() {
match segment { match segment {
DynamicLabelSegment::Static(str) => { DynamicStringSegment::Static(str) => {
label_parts label_parts
.lock() .lock()
.expect("Failed to get lock on label parts") .expect("Failed to get lock on label parts")
.insert(i, str); .insert(i, str);
} }
DynamicLabelSegment::Dynamic(script) => { DynamicStringSegment::Dynamic(script) => {
let tx = tx.clone(); let tx = tx.clone();
let label_parts = label_parts.clone(); let label_parts = label_parts.clone();
@ -77,11 +80,21 @@ impl DynamicLabel {
script script
.run(|(out, _)| { .run(|(out, _)| {
if let OutputStream::Stdout(out) = out { if let OutputStream::Stdout(out) = out {
label_parts let mut label_parts = label_parts
.lock() .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); .insert(i, out);
tx.send(()).expect("Failed to send update");
let string = label_parts
.iter()
.map(|(_, part)| part.as_str())
.collect::<String>();
tx.send(string).expect("Failed to send update");
} }
}) })
.await; .await;
@ -90,25 +103,22 @@ impl DynamicLabel {
} }
} }
tx.send(()).expect("Failed to send update"); // initialize
{ {
let label = label.clone(); let label_parts = label_parts
rx.attach(None, move |_| { .lock()
let new_label = label_parts .expect("Failed to get lock on label parts")
.lock() .iter()
.expect("Failed to get lock on label parts") .map(|(_, part)| part.as_str())
.iter() .collect::<String>();
.map(|(_, part)| part.as_str())
.collect::<String>();
label.set_label(new_label.as_str()); tx.send(label_parts).expect("Failed to send update");
Continue(true)
});
} }
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 // TODO: see if we can run gtk tests in ci
if gtk::init().is_ok() { if gtk::init().is_ok() {
let label = gtk::Label::new(None); 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)
},
);
} }
} }
} }

View file

@ -1,3 +1,3 @@
mod dynamic_label; mod dynamic_label;
pub use dynamic_label::DynamicLabel; pub use dynamic_label::DynamicString;