mirror of
https://github.com/Zedfrigg/ironbar.git
synced 2025-07-01 10:41:03 +02:00
feat: mouse event config options
Adds `on_click_middle`, `on_click_right`, `on_scroll_up`, `on_scroll_down`. BREAKING CHANGE: `on_click` is now called `on_click_left` for consistency with new options. Resolves #44.
This commit is contained in:
parent
b2afe78c07
commit
fa67d077b1
5 changed files with 167 additions and 107 deletions
|
@ -1,10 +1,12 @@
|
||||||
By default, you get a single bar at the bottom of all your screens.
|
By default, you get a single bar at the bottom of all your screens.
|
||||||
To change that, you'll unsurprisingly need a config file.
|
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.
|
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!
|
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.
|
and the example pages in the sidebar.
|
||||||
|
|
||||||
## 1. Create config file
|
## 1. Create config file
|
||||||
|
@ -239,7 +241,7 @@ monitors:
|
||||||
<details>
|
<details>
|
||||||
<summary>Corn</summary>
|
<summary>Corn</summary>
|
||||||
|
|
||||||
```
|
```corn
|
||||||
{
|
{
|
||||||
monitors.DP-1 = [
|
monitors.DP-1 = [
|
||||||
{ start = [] }
|
{ 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).
|
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_left` | `Script [oneshot]` | `null` | Runs the script when the module is left clicked. |
|
||||||
| `tooltip` | `string` | `null` | Shows this text on hover. Supports embedding scripts between `{{double braces}}`. |
|
| `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}}`. |
|
||||||
|
|
|
@ -3,13 +3,16 @@ that allow script input to dynamically set values.
|
||||||
|
|
||||||
Scripts are passed to `sh -c`.
|
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
|
Normally they will repeat this at an interval, hence the name, although in some cases they may only run on a user
|
||||||
event.
|
event.
|
||||||
If the script exited code 0, the `stdout` will be used. Otherwise, `stderr` will be printed to the log.
|
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.
|
and used.
|
||||||
|
|
||||||
One should prefer to use watch-mode where possible, as it removes the overhead of regularly spawning processes.
|
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`
|
Both `mode` and `interval` are optional and can be excluded to fall back to their defaults of `poll` and `5000`
|
||||||
respectively.
|
respectively.
|
||||||
|
|
||||||
|
For oneshot scripts, both the mode and interval are ignored.
|
||||||
|
|
||||||
### Shorthand (string)
|
### Shorthand (string)
|
||||||
|
|
||||||
Shorthand scripts should be written in the format:
|
Shorthand scripts should be written in the format:
|
||||||
|
|
54
src/bar.rs
54
src/bar.rs
|
@ -6,7 +6,7 @@ use crate::popup::Popup;
|
||||||
use crate::script::{OutputStream, Script};
|
use crate::script::{OutputStream, Script};
|
||||||
use crate::{await_sync, read_lock, send, write_lock, Config};
|
use crate::{await_sync, read_lock, send, write_lock, Config};
|
||||||
use color_eyre::Result;
|
use color_eyre::Result;
|
||||||
use gtk::gdk::Monitor;
|
use gtk::gdk::{EventMask, Monitor, ScrollDirection};
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
use gtk::{Application, ApplicationWindow, EventBox, Orientation, Widget};
|
use gtk::{Application, ApplicationWindow, EventBox, Orientation, Widget};
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
|
@ -313,6 +313,7 @@ fn setup_receiver<TSend>(
|
||||||
/// The event box container is returned.
|
/// The event box container is returned.
|
||||||
fn wrap_widget<W: IsA<Widget>>(widget: &W) -> EventBox {
|
fn wrap_widget<W: IsA<Widget>>(widget: &W) -> EventBox {
|
||||||
let container = EventBox::new();
|
let container = EventBox::new();
|
||||||
|
container.add_events(EventMask::SCROLL_MASK);
|
||||||
container.add(widget);
|
container.add(widget);
|
||||||
container
|
container
|
||||||
}
|
}
|
||||||
|
@ -345,18 +346,55 @@ fn setup_module_common_options(container: EventBox, common: CommonConfig) {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(on_click) = common.on_click {
|
let left_click_script = common.on_click_left.map(Script::new_polling);
|
||||||
let script = Script::new_polling(on_click);
|
let middle_click_script = common.on_click_middle.map(Script::new_polling);
|
||||||
container.connect_button_press_event(move |_, _| {
|
let right_click_script = common.on_click_right.map(Script::new_polling);
|
||||||
trace!("Running on-click script");
|
|
||||||
|
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 }) {
|
match await_sync(async { script.get_output().await }) {
|
||||||
Ok((OutputStream::Stderr(out), _)) => error!("{out}"),
|
Ok((OutputStream::Stderr(out), _)) => error!("{out}"),
|
||||||
Err(err) => error!("{err:?}"),
|
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 {
|
if let Some(tooltip) = common.tooltip {
|
||||||
DynamicString::new(&tooltip, move |string| {
|
DynamicString::new(&tooltip, move |string| {
|
||||||
|
|
|
@ -1,51 +1,14 @@
|
||||||
use crate::modules::clock::ClockModule;
|
use super::{BarPosition, Config, MonitorConfig};
|
||||||
use crate::modules::custom::CustomModule;
|
use color_eyre::eyre::Result;
|
||||||
use crate::modules::focused::FocusedModule;
|
use color_eyre::eyre::{ContextCompat, WrapErr};
|
||||||
use crate::modules::launcher::LauncherModule;
|
use color_eyre::{Help, Report};
|
||||||
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 dirs::config_dir;
|
use dirs::config_dir;
|
||||||
use eyre::Result;
|
|
||||||
use gtk::Orientation;
|
use gtk::Orientation;
|
||||||
use serde::{Deserialize, Deserializer};
|
use serde::{Deserialize, Deserializer};
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::{env, fs};
|
use std::{env, fs};
|
||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
|
||||||
pub struct CommonConfig {
|
|
||||||
pub show_if: Option<ScriptInput>,
|
|
||||||
pub on_click: Option<ScriptInput>,
|
|
||||||
pub tooltip: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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<Config>),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Manually implement for better untagged enum error handling:
|
// Manually implement for better untagged enum error handling:
|
||||||
// currently open pr: https://github.com/serde-rs/serde/pull/1544
|
// currently open pr: https://github.com/serde-rs/serde/pull/1544
|
||||||
impl<'de> Deserialize<'de> for MonitorConfig {
|
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 {
|
impl BarPosition {
|
||||||
/// Gets the orientation the bar and widgets should use
|
/// Gets the orientation the bar and widgets should use
|
||||||
/// based on this position.
|
/// 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<Vec<ModuleConfig>>,
|
|
||||||
pub center: Option<Vec<ModuleConfig>>,
|
|
||||||
pub end: Option<Vec<ModuleConfig>>,
|
|
||||||
|
|
||||||
pub monitors: Option<HashMap<String, MonitorConfig>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
const fn default_bar_position() -> BarPosition {
|
|
||||||
BarPosition::Bottom
|
|
||||||
}
|
|
||||||
|
|
||||||
const fn default_bar_height() -> i32 {
|
|
||||||
42
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
/// Attempts to load the config file from file,
|
/// Attempts to load the config file from file,
|
||||||
/// parse it and return a new instance of `Self`.
|
/// 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
|
|
||||||
}
|
|
94
src/config/mod.rs
Normal file
94
src/config/mod.rs
Normal file
|
@ -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<ScriptInput>,
|
||||||
|
|
||||||
|
pub on_click_left: Option<ScriptInput>,
|
||||||
|
pub on_click_right: Option<ScriptInput>,
|
||||||
|
pub on_click_middle: Option<ScriptInput>,
|
||||||
|
pub on_scroll_up: Option<ScriptInput>,
|
||||||
|
pub on_scroll_down: Option<ScriptInput>,
|
||||||
|
|
||||||
|
pub tooltip: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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<Config>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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<Vec<ModuleConfig>>,
|
||||||
|
pub center: Option<Vec<ModuleConfig>>,
|
||||||
|
pub end: Option<Vec<ModuleConfig>>,
|
||||||
|
|
||||||
|
pub monitors: Option<HashMap<String, MonitorConfig>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue