diff --git a/docs/Configuration guide.md b/docs/Configuration guide.md index 8b672805..3ff70b07 100644 --- a/docs/Configuration guide.md +++ b/docs/Configuration guide.md @@ -1,10 +1,12 @@ By default, you get a single bar at the bottom of all your screens. To change that, you'll unsurprisingly need a config file. -This page details putting together the skeleton for your config to get you to a stage where you can start configuring modules. -It may look long and overwhelming, but that is just because the bar supports a lot of scenarios! +This page details putting together the skeleton for your config to get you to a stage where you can start configuring +modules. +It may look long and overwhelming, but that is just because the bar supports a lot of scenarios! -If you want to see some ready-to-go config files check the [examples folder](https://github.com/JakeStanger/ironbar/tree/master/examples) +If you want to see some ready-to-go config files check +the [examples folder](https://github.com/JakeStanger/ironbar/tree/master/examples) and the example pages in the sidebar. ## 1. Create config file @@ -239,7 +241,7 @@ monitors:
Corn -``` +```corn { monitors.DP-1 = [ { start = [] } @@ -281,8 +283,12 @@ For details on available modules and each of their config options, check the sid 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. Supports embedding scripts between `{{double braces}}`. | +| 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_left` | `Script [oneshot]` | `null` | Runs the script when the module is left clicked. | +| `on_click_middle` | `Script [oneshot]` | `null` | Runs the script when the module is middle clicked. | +| `on_click_right` | `Script [oneshot]` | `null` | Runs the script when the module is right clicked. | +| `on_scroll_up` | `Script [oneshot]` | `null` | Runs the script when the module is scroll up on. | +| `on_scroll_down` | `Script [oneshot]` | `null` | Runs the script when the module is scrolled down on. | +| `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 04b28c3e..18b022f9 100644 --- a/docs/Scripts.md +++ b/docs/Scripts.md @@ -3,13 +3,16 @@ that allow script input to dynamically set values. Scripts are passed to `sh -c`. -Two types of scripts exist: polling and watching: +Three types of scripts exist: polling, oneshot and watching: -- Polling scripts will run and wait for exit. +- **Polling** scripts will run and wait for exit. Normally they will repeat this at an interval, hence the name, although in some cases they may only run on a user event. If the script exited code 0, the `stdout` will be used. Otherwise, `stderr` will be printed to the log. -- Watching scripts start a long-running process. Every time the process writes to `stdout`, the last line is captured +- **Oneshot** scripts are a variant of polling scripts. + They wait for script to exit, and may do something with the output, but are only fired by user events instead of the interval. + Generally options that accept oneshot scripts do not support the other types. +- **Watching** scripts start a long-running process. Every time the process writes to `stdout`, the last line is captured and used. One should prefer to use watch-mode where possible, as it removes the overhead of regularly spawning processes. @@ -28,6 +31,8 @@ spawning the script. Both `mode` and `interval` are optional and can be excluded to fall back to their defaults of `poll` and `5000` respectively. +For oneshot scripts, both the mode and interval are ignored. + ### Shorthand (string) Shorthand scripts should be written in the format: diff --git a/src/bar.rs b/src/bar.rs index aa4e8672..ce7d83dc 100644 --- a/src/bar.rs +++ b/src/bar.rs @@ -6,7 +6,7 @@ use crate::popup::Popup; use crate::script::{OutputStream, Script}; use crate::{await_sync, read_lock, send, write_lock, Config}; use color_eyre::Result; -use gtk::gdk::Monitor; +use gtk::gdk::{EventMask, Monitor, ScrollDirection}; use gtk::prelude::*; use gtk::{Application, ApplicationWindow, EventBox, Orientation, Widget}; use std::sync::{Arc, RwLock}; @@ -313,6 +313,7 @@ fn setup_receiver( /// The event box container is returned. fn wrap_widget>(widget: &W) -> EventBox { let container = EventBox::new(); + container.add_events(EventMask::SCROLL_MASK); container.add(widget); container } @@ -345,18 +346,55 @@ fn setup_module_common_options(container: EventBox, common: CommonConfig) { }, ); - if let Some(on_click) = common.on_click { - let script = Script::new_polling(on_click); - container.connect_button_press_event(move |_, _| { - trace!("Running on-click script"); + let left_click_script = common.on_click_left.map(Script::new_polling); + let middle_click_script = common.on_click_middle.map(Script::new_polling); + let right_click_script = common.on_click_right.map(Script::new_polling); + + container.connect_button_press_event(move |_, event| { + let script = match event.button() { + 1 => left_click_script.as_ref(), + 2 => middle_click_script.as_ref(), + 3 => right_click_script.as_ref(), + _ => None, + }; + + if let Some(script) = script { + trace!("Running on-click script: {}", event.button()); + match await_sync(async { script.get_output().await }) { Ok((OutputStream::Stderr(out), _)) => error!("{out}"), Err(err) => error!("{err:?}"), _ => {} } - Inhibit(false) - }); - } + } + + Inhibit(false) + }); + + let scroll_up_script = common.on_scroll_up.map(Script::new_polling); + let scroll_down_script = common.on_scroll_down.map(Script::new_polling); + + container.connect_scroll_event(move |_, event| { + println!("{:?}", event.direction()); + + let script = match event.direction() { + ScrollDirection::Up => scroll_up_script.as_ref(), + ScrollDirection::Down => scroll_down_script.as_ref(), + _ => None, + }; + + if let Some(script) = script { + trace!("Running on-scroll script: {}", event.direction()); + + match await_sync(async { script.get_output().await }) { + Ok((OutputStream::Stderr(out), _)) => error!("{out}"), + Err(err) => error!("{err:?}"), + _ => {} + } + } + + Inhibit(false) + }); if let Some(tooltip) = common.tooltip { DynamicString::new(&tooltip, move |string| { diff --git a/src/config.rs b/src/config/impl.rs similarity index 71% rename from src/config.rs rename to src/config/impl.rs index b619ef40..e1e560da 100644 --- a/src/config.rs +++ b/src/config/impl.rs @@ -1,51 +1,14 @@ -use crate::modules::clock::ClockModule; -use crate::modules::custom::CustomModule; -use crate::modules::focused::FocusedModule; -use crate::modules::launcher::LauncherModule; -use crate::modules::mpd::MpdModule; -use crate::modules::script::ScriptModule; -use crate::modules::sysinfo::SysInfoModule; -use crate::modules::tray::TrayModule; -use crate::modules::workspaces::WorkspacesModule; -use crate::script::ScriptInput; -use color_eyre::eyre::{Context, ContextCompat}; -use color_eyre::{eyre, Help, Report}; +use super::{BarPosition, Config, MonitorConfig}; +use color_eyre::eyre::Result; +use color_eyre::eyre::{ContextCompat, WrapErr}; +use color_eyre::{Help, Report}; use dirs::config_dir; -use eyre::Result; use gtk::Orientation; use serde::{Deserialize, Deserializer}; -use std::collections::HashMap; use std::path::{Path, PathBuf}; use std::{env, fs}; use tracing::instrument; -#[derive(Debug, Deserialize, Clone)] -pub struct CommonConfig { - pub show_if: Option, - pub on_click: Option, - pub tooltip: Option, -} - -#[derive(Debug, Deserialize, Clone)] -#[serde(tag = "type", rename_all = "snake_case")] -pub enum ModuleConfig { - Clock(ClockModule), - Mpd(MpdModule), - Tray(TrayModule), - Workspaces(WorkspacesModule), - SysInfo(SysInfoModule), - Launcher(LauncherModule), - Script(ScriptModule), - Focused(FocusedModule), - Custom(CustomModule), -} - -#[derive(Debug, Clone)] -pub enum MonitorConfig { - Single(Config), - Multiple(Vec), -} - // Manually implement for better untagged enum error handling: // currently open pr: https://github.com/serde-rs/serde/pull/1544 impl<'de> Deserialize<'de> for MonitorConfig { @@ -78,21 +41,6 @@ impl<'de> Deserialize<'de> for MonitorConfig { } } -#[derive(Debug, Deserialize, Copy, Clone, PartialEq, Eq)] -#[serde(rename_all = "snake_case")] -pub enum BarPosition { - Top, - Bottom, - Left, - Right, -} - -impl Default for BarPosition { - fn default() -> Self { - Self::Bottom - } -} - impl BarPosition { /// Gets the orientation the bar and widgets should use /// based on this position. @@ -115,30 +63,6 @@ impl BarPosition { } } -#[derive(Debug, Deserialize, Clone)] -pub struct Config { - #[serde(default = "default_bar_position")] - pub position: BarPosition, - #[serde(default = "default_true")] - pub anchor_to_edges: bool, - #[serde(default = "default_bar_height")] - pub height: i32, - - pub start: Option>, - pub center: Option>, - pub end: Option>, - - pub monitors: Option>, -} - -const fn default_bar_position() -> BarPosition { - BarPosition::Bottom -} - -const fn default_bar_height() -> i32 { - 42 -} - impl Config { /// Attempts to load the config file from file, /// parse it and return a new instance of `Self`. @@ -214,10 +138,3 @@ impl Config { } } } - -pub const fn default_false() -> bool { - false -} -pub const fn default_true() -> bool { - true -} diff --git a/src/config/mod.rs b/src/config/mod.rs new file mode 100644 index 00000000..91a46816 --- /dev/null +++ b/src/config/mod.rs @@ -0,0 +1,94 @@ +mod r#impl; + +use crate::modules::clock::ClockModule; +use crate::modules::custom::CustomModule; +use crate::modules::focused::FocusedModule; +use crate::modules::launcher::LauncherModule; +use crate::modules::mpd::MpdModule; +use crate::modules::script::ScriptModule; +use crate::modules::sysinfo::SysInfoModule; +use crate::modules::tray::TrayModule; +use crate::modules::workspaces::WorkspacesModule; +use crate::script::ScriptInput; +use serde::Deserialize; +use std::collections::HashMap; + +#[derive(Debug, Deserialize, Clone)] +pub struct CommonConfig { + pub show_if: Option, + + pub on_click_left: Option, + pub on_click_right: Option, + pub on_click_middle: Option, + pub on_scroll_up: Option, + pub on_scroll_down: Option, + + pub tooltip: Option, +} + +#[derive(Debug, Deserialize, Clone)] +#[serde(tag = "type", rename_all = "snake_case")] +pub enum ModuleConfig { + Clock(ClockModule), + Mpd(MpdModule), + Tray(TrayModule), + Workspaces(WorkspacesModule), + SysInfo(SysInfoModule), + Launcher(LauncherModule), + Script(ScriptModule), + Focused(FocusedModule), + Custom(CustomModule), +} + +#[derive(Debug, Clone)] +pub enum MonitorConfig { + Single(Config), + Multiple(Vec), +} + +#[derive(Debug, Deserialize, Copy, Clone, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +pub enum BarPosition { + Top, + Bottom, + Left, + Right, +} + +impl Default for BarPosition { + fn default() -> Self { + Self::Bottom + } +} + +#[derive(Debug, Deserialize, Clone)] +pub struct Config { + #[serde(default = "default_bar_position")] + pub position: BarPosition, + #[serde(default = "default_true")] + pub anchor_to_edges: bool, + #[serde(default = "default_bar_height")] + pub height: i32, + + pub start: Option>, + pub center: Option>, + pub end: Option>, + + pub monitors: Option>, +} + +const fn default_bar_position() -> BarPosition { + BarPosition::Bottom +} + +const fn default_bar_height() -> i32 { + 42 +} + +pub const fn default_false() -> bool { + false +} + +pub const fn default_true() -> bool { + true +}