diff --git a/.github/workflows/binary.yml b/.github/workflows/binary.yml
index df72e97..5205987 100644
--- a/.github/workflows/binary.yml
+++ b/.github/workflows/binary.yml
@@ -53,10 +53,10 @@ jobs:
- name: Compress the built binary
if: ${{ matrix.platform.zipext == '.tar.gz' }}
- run: tar -zcvf ${{env.BINARY_NAME}}-${{github.ref_name}}-${{matrix.platform.target}}.tar.gz -C target/${{matrix.platform.target}}/release ${{env.BINARY_NAME}}
+ run: tar -zcvf ${{env.BINARY_NAME}}-${{needs.get_last_release.outputs.latest_release_tag}}-${{matrix.platform.target}}.tar.gz -C target/${{matrix.platform.target}}/release ${{env.BINARY_NAME}}
- name: Upload to release
- run: gh release upload ${{needs.get_last_release.outputs.latest_release_tag}} ${{env.BINARY_NAME}}-${{github.ref_name}}-${{matrix.platform.target}}${{matrix.platform.zipext}}
+ run: gh release upload ${{needs.get_last_release.outputs.latest_release_tag}} ${{env.BINARY_NAME}}-${{needs.get_last_release.outputs.latest_release_tag}}-${{matrix.platform.target}}${{matrix.platform.zipext}}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.gitignore b/.gitignore
index ea8c4bf..0e71e34 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
/target
+.direnv/
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 634b117..adaf6b0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,15 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [v0.15.1] - 2024-05-05
+
+Release to bump hyprland-rs version due to Hyprland v0.40 socket path breaking change.
+
+### :memo: Documentation Changes
+- [`47b6c47`](https://github.com/JakeStanger/ironbar/commit/47b6c477242ad52aae77a6820740d9c5f4bfc263) - **compiling**: add lua deps *(commit by [@JakeStanger](https://github.com/JakeStanger))*
+- [`1971f3b`](https://github.com/JakeStanger/ironbar/commit/1971f3bb1ef3d059b29b99527e77ffaaf92240aa) - **volume**: update deprecated volume token *(PR [#567](https://github.com/JakeStanger/ironbar/pull/567) by [@drendog](https://github.com/drendog))*
+
+
## [v0.15.0] - 2024-04-28
### :sparkles: New Features
- [`f4384b6`](https://github.com/JakeStanger/ironbar/commit/f4384b6252e86d4e2558e1c36810d4ef903bd58c) - enable use of markup in clock module format and format_popup, and update documentation to reflect supporting Pango markup in both *commit by [@Dridus](https://github.com/Dridus))*
@@ -565,3 +574,4 @@ It also requires `lua-lgi` as a runtime dependency.
[v0.14.0]: https://github.com/JakeStanger/ironbar/compare/v0.13.0...v0.14.0
[v0.14.1]: https://github.com/JakeStanger/ironbar/compare/v0.14.0...v0.14.1
[v0.15.0]: https://github.com/JakeStanger/ironbar/compare/v0.14.3...v0.15.0
+[v0.15.1]: https://github.com/JakeStanger/ironbar/compare/v0.15.0...v0.15.1
diff --git a/Cargo.lock b/Cargo.lock
index f2aa0fd..f709497 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -26,6 +26,7 @@ dependencies = [
"cfg-if",
"getrandom",
"once_cell",
+ "serde",
"version_check",
]
@@ -434,6 +435,12 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+[[package]]
+name = "cfg_aliases"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
+
[[package]]
name = "chrono"
version = "0.4.38"
@@ -529,12 +536,6 @@ dependencies = [
"crossbeam-utils",
]
-[[package]]
-name = "convert_case"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
-
[[package]]
name = "core-foundation"
version = "0.9.3"
@@ -703,10 +704,8 @@ version = "0.99.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
dependencies = [
- "convert_case",
"proc-macro2",
"quote 1.0.35",
- "rustc_version",
"syn 1.0.109",
]
@@ -756,12 +755,6 @@ dependencies = [
"libloading",
]
-[[package]]
-name = "doc-comment"
-version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
-
[[package]]
name = "downcast-rs"
version = "1.2.0"
@@ -1491,32 +1484,28 @@ dependencies = [
[[package]]
name = "hyprland"
-version = "0.3.13"
+version = "0.4.0-alpha.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f87a8f1cc065d451894dd3916c0bc3fcf9b67b276126c05f27b1db912688dde8"
+checksum = "d627cd06fb3389f2554b7a4bb21db8c0bfca8863e6e653702cc4c6dbf20d8276"
dependencies = [
- "async-trait",
+ "ahash",
"derive_more",
- "doc-comment",
- "futures",
- "hex",
"hyprland-macros",
- "lazy_static",
"num-traits",
+ "once_cell",
"paste",
"regex",
"serde",
"serde_json",
"serde_repr",
- "strum",
"tokio",
]
[[package]]
name = "hyprland-macros"
-version = "0.3.4"
+version = "0.4.0-alpha.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c941d3d52e979612af8cb94e8de49000c7fada2014a7791d173ab41339f4e4eb"
+checksum = "5dd8ce4c182ce77e485918f49262425ee51a2746fe97f14084869aeff2fbc38e"
dependencies = [
"quote 1.0.35",
"syn 2.0.48",
@@ -1626,7 +1615,7 @@ checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f"
[[package]]
name = "ironbar"
-version = "0.15.0"
+version = "0.16.0-pre"
dependencies = [
"cairo-rs",
"cfg-if",
@@ -1637,6 +1626,7 @@ dependencies = [
"dirs",
"futures-lite 2.3.0",
"futures-signals",
+ "futures-util",
"glib",
"gtk",
"gtk-layer-shell",
@@ -1647,7 +1637,7 @@ dependencies = [
"mlua",
"mpd-utils",
"mpris",
- "nix 0.27.1",
+ "nix 0.28.0",
"notify",
"regex",
"reqwest",
@@ -1714,9 +1704,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
-version = "0.2.150"
+version = "0.2.155"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
+checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
[[package]]
name = "libcorn"
@@ -1790,16 +1780,6 @@ version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456"
-[[package]]
-name = "lock_api"
-version = "0.4.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
-dependencies = [
- "autocfg",
- "scopeguard",
-]
-
[[package]]
name = "log"
version = "0.4.17"
@@ -1895,9 +1875,9 @@ dependencies = [
[[package]]
name = "mlua"
-version = "0.9.7"
+version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d9bed6bce296397a9d6a86f995dd10a547a4e6949825d45225906bdcbfe7367"
+checksum = "e340c022072f3208a4105458286f4985ba5355bfe243c3073afe45cbe9ecf491"
dependencies = [
"bstr",
"mlua-sys",
@@ -1908,9 +1888,9 @@ dependencies = [
[[package]]
name = "mlua-sys"
-version = "0.5.1"
+version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2847b42764435201d8cbee1f517edb79c4cca4181877b90047587c89e1b7bce4"
+checksum = "5552e7e4e22ada0463dfdeee6caf6dc057a189fdc83136408a8f950a5e5c5540"
dependencies = [
"cc",
"cfg-if",
@@ -2009,6 +1989,18 @@ dependencies = [
"libc",
]
+[[package]]
+name = "nix"
+version = "0.28.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4"
+dependencies = [
+ "bitflags 2.4.0",
+ "cfg-if",
+ "cfg_aliases",
+ "libc",
+]
+
[[package]]
name = "nom"
version = "7.1.3"
@@ -2068,9 +2060,9 @@ dependencies = [
[[package]]
name = "num-traits"
-version = "0.2.15"
+version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
@@ -2203,34 +2195,11 @@ version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae"
-[[package]]
-name = "parking_lot"
-version = "0.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
-dependencies = [
- "lock_api",
- "parking_lot_core",
-]
-
-[[package]]
-name = "parking_lot_core"
-version = "0.9.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
-dependencies = [
- "cfg-if",
- "libc",
- "redox_syscall 0.4.1",
- "smallvec",
- "windows-targets 0.48.0",
-]
-
[[package]]
name = "paste"
-version = "1.0.12"
+version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79"
+checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
[[package]]
name = "percent-encoding"
@@ -2508,15 +2477,6 @@ dependencies = [
"bitflags 1.3.2",
]
-[[package]]
-name = "redox_syscall"
-version = "0.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
-dependencies = [
- "bitflags 1.3.2",
-]
-
[[package]]
name = "redox_users"
version = "0.4.3"
@@ -2688,12 +2648,6 @@ version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecd36cc4259e3e4514335c4a138c6b43171a8d61d8f5c9348f9fc7529416f247"
-[[package]]
-name = "rustversion"
-version = "1.0.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06"
-
[[package]]
name = "ryu"
version = "1.0.13"
@@ -2761,18 +2715,18 @@ checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed"
[[package]]
name = "serde"
-version = "1.0.198"
+version = "1.0.202"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc"
+checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.198"
+version = "1.0.202"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9"
+checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838"
dependencies = [
"proc-macro2",
"quote 1.0.35",
@@ -2781,9 +2735,9 @@ dependencies = [
[[package]]
name = "serde_json"
-version = "1.0.116"
+version = "1.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813"
+checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
dependencies = [
"itoa",
"ryu",
@@ -2972,28 +2926,6 @@ version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01"
-[[package]]
-name = "strum"
-version = "0.26.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "723b93e8addf9aa965ebe2d11da6d7540fa2283fcea14b3371ff055f7ba13f5f"
-dependencies = [
- "strum_macros",
-]
-
-[[package]]
-name = "strum_macros"
-version = "0.26.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a3417fc93d76740d974a01654a09777cb500428cc874ca9f45edfe0c4d4cd18"
-dependencies = [
- "heck 0.4.1",
- "proc-macro2",
- "quote 1.0.35",
- "rustversion",
- "syn 2.0.48",
-]
-
[[package]]
name = "swayipc-async"
version = "2.0.1"
@@ -3204,7 +3136,6 @@ dependencies = [
"libc",
"mio",
"num_cpus",
- "parking_lot",
"pin-project-lite",
"signal-hook-registry",
"socket2 0.5.5",
diff --git a/Cargo.toml b/Cargo.toml
index 7a147c7..f22cb7f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "ironbar"
-version = "0.15.0"
+version = "0.16.0-pre"
edition = "2021"
license = "MIT"
description = "Customisable GTK Layer Shell wlroots/sway bar"
@@ -99,7 +99,7 @@ tracing-error = { version = "0.2.0" , default-features = false }
tracing-appender = "0.2.3"
strip-ansi-escapes = "0.2.0"
color-eyre = "0.6.3"
-serde = { version = "1.0.198", features = ["derive"] }
+serde = { version = "1.0.202", features = ["derive"] }
indexmap = "2.2.6"
dirs = "5.0.1"
walkdir = "2.5.0"
@@ -117,18 +117,18 @@ cfg-if = "1.0.0"
clap = { version = "4.5.4", optional = true, features = ["derive"] }
# ipc
-serde_json = { version = "1.0.116", optional = true }
+serde_json = { version = "1.0.117", optional = true }
# http
reqwest = { version = "0.12.4", default_features = false, features = ["default-tls", "http2"], optional = true }
# cairo
lua-src = { version = "546.0.2", optional = true }
-mlua = { version = "0.9.7", optional = true, features = ["luajit"] }
+mlua = { version = "0.9.8", optional = true, features = ["luajit"] }
cairo-rs = { version = "0.18.5", optional = true, features = ["png"] }
# clipboard
-nix = { version = "0.27.1", optional = true, features = ["event"] }
+nix = { version = "0.28.0", optional = true, features = ["event", "fs"] }
# clock
chrono = { version = "0.4.38", optional = true, default_features = false, features = ["clock", "unstable-locales"] }
@@ -154,7 +154,8 @@ libpulse-binding = { version = "2.28.1", optional = true }
# workspaces
swayipc-async = { version = "2.0.1", optional = true }
-hyprland = { version = "0.3.13", default_features = false, features = ["listener", "tokio", "silent"], optional = true }
+hyprland = { version = "0.4.0-alpha.2", features = ["silent"], optional = true }
+futures-util = { version = "0.3.30", optional = true }
# shared
futures-lite = { version = "2.3.0", optional = true } # networkmanager, upower, workspaces
diff --git a/README.md b/README.md
index 5fb5ecf..67809c3 100644
--- a/README.md
+++ b/README.md
@@ -59,6 +59,10 @@ Ironbar is designed to support anything from a lightweight bar to a full desktop
[](https://repology.org/project/ironbar/versions)
+Ironbar can be installed from source or using your preferred package manager.
+
+It is also recommended to install a [Nerd Font](https://www.nerdfonts.com/#home) for displaying symbols.
+
### Cargo
[crate](https://crates.io/crates/ironbar)
diff --git a/docs/Compiling.md b/docs/Compiling.md
index ed5d89d..6b1c9a7 100644
--- a/docs/Compiling.md
+++ b/docs/Compiling.md
@@ -9,6 +9,8 @@ cargo build --release
install target/release/ironbar ~/.local/bin/ironbar
```
+It is also recommended to install a [Nerd Font](https://www.nerdfonts.com/#home) for displaying symbols.
+
## Build requirements
To build from source, you must have GTK (>= 3.22) and GTK Layer Shell installed.
@@ -45,7 +47,7 @@ dnf install gtk3-devel gtk-layer-shell-devel
# for http support
dnf install openssl-devel
# for volume support
-dnf install libpulseaudio-devel
+dnf install pulseaudio-libs-devel
# for lua/cairo support
dnf install luajit-devel lua-lgi
```
diff --git a/docs/Configuration guide.md b/docs/Configuration guide.md
index 7caeaf5..5edf0b1 100644
--- a/docs/Configuration guide.md
+++ b/docs/Configuration guide.md
@@ -9,6 +9,8 @@ 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.
+The examples make use of [Nerd Fonts](https://www.nerdfonts.com/#home) for displaying symbols.
+
## 1. Create config file
The config file lives inside the `ironbar` directory in your XDG_CONFIG_DIR, which is usually `~/.config/ironbar`.
diff --git a/docs/modules/Upower.md b/docs/modules/Upower.md
index e5db9aa..620c3bd 100644
--- a/docs/modules/Upower.md
+++ b/docs/modules/Upower.md
@@ -84,11 +84,10 @@ and will be replaced with values from the current battery state:
| Selector | Description |
|---------------------------------|--------------------------------|
-| `.upower` | Upower widget container. |
-| `.upower .button` | Upower widget button. |
-| `.upower .button .contents` | Upower widget button contents. |
-| `.upower .button .icon` | Upower widget battery icon. |
-| `.upower .button .label` | Upower widget button label. |
+| `.upower` | Upower widget button. |
+| `.upower .contents` | Upower widget button contents. |
+| `.upower .icon` | Upower widget battery icon. |
+| `.upower .label` | Upower widget button label. |
| `.popup-upower` | Upower popup box. |
| `.popup-upower .upower-details` | Label inside the popup. |
diff --git a/examples/config.corn b/examples/config.corn
index 69b3d5f..03416d2 100644
--- a/examples/config.corn
+++ b/examples/config.corn
@@ -55,7 +55,7 @@ let {
interval.networks = 3
format = [
- " {cpu_percent}% | {temp_c:k10temp_Tccd1}°C"
+ " {cpu_percent}% | {temp_c:k10temp-Tccd1}°C"
" {memory_used} / {memory_total} GB ({memory_percent}%)"
"| {swap_used} / {swap_total} GB ({swap_percent}%)"
" {disk_used:/} / {disk_total:/} GB ({disk_percent:/}%)"
@@ -81,7 +81,7 @@ let {
$volume = {
type = "volume"
- format = "{icon} {volume}%"
+ format = "{icon} {percentage}%"
max_volume = 100
icons.volume_high = ""
icons.volume_medium = ""
diff --git a/examples/config.json b/examples/config.json
index 87b788b..a54dd12 100644
--- a/examples/config.json
+++ b/examples/config.json
@@ -63,7 +63,7 @@
"networks": 3
},
"format": [
- " {cpu_percent}% | {temp_c:k10temp_Tccd1}°C",
+ " {cpu_percent}% | {temp_c:k10temp-Tccd1}°C",
" {memory_used} / {memory_total} GB ({memory_percent}%)",
"| {swap_used} / {swap_total} GB ({swap_percent}%)",
" {disk_used:/} / {disk_total:/} GB ({disk_percent:/}%)",
@@ -74,7 +74,7 @@
},
{
"type": "volume",
- "format": "{icon} {volume}%",
+ "format": "{icon} {percentage}%",
"max_volume": 100,
"icons": {
"volume_high": "",
diff --git a/examples/config.toml b/examples/config.toml
index 830e858..540c053 100644
--- a/examples/config.toml
+++ b/examples/config.toml
@@ -53,7 +53,7 @@ interval = 500
[[end]]
type = "sys_info"
format = [
- " {cpu_percent}% | {temp_c:k10temp_Tccd1}°C",
+ " {cpu_percent}% | {temp_c:k10temp-Tccd1}°C",
" {memory_used} / {memory_total} GB ({memory_percent}%)",
"| {swap_used} / {swap_total} GB ({swap_percent}%)",
" {disk_used:/} / {disk_total:/} GB ({disk_percent:/}%)",
@@ -71,7 +71,7 @@ networks = 3
[[end]]
type = "volume"
-format = "{icon} {volume}%"
+format = "{icon} {percentage}%"
max_volume = 100
[end.icons]
diff --git a/examples/config.yaml b/examples/config.yaml
index 7693414..e05c4a1 100644
--- a/examples/config.yaml
+++ b/examples/config.yaml
@@ -43,7 +43,7 @@ end:
disks: 300
networks: 3
format:
- - {cpu_percent}% | {temp_c:k10temp_Tccd1}°C
+ - {cpu_percent}% | {temp_c:k10temp-Tccd1}°C
- {memory_used} / {memory_total} GB ({memory_percent}%)
- '| {swap_used} / {swap_total} GB ({swap_percent}%)'
- {disk_used:/} / {disk_total:/} GB ({disk_percent:/}%)
@@ -51,7 +51,7 @@ end:
- {load_average:1} | {load_average:5} | {load_average:15}
- {uptime}
- type: volume
- format: '{icon} {volume}%'
+ format: '{icon} {percentage}%'
max_volume: 100
icons:
volume_high:
diff --git a/examples/test.corn b/examples/test.corn
new file mode 100644
index 0000000..219e303
--- /dev/null
+++ b/examples/test.corn
@@ -0,0 +1,60 @@
+let {
+ $config_dir = "/home/jake/.config/ironbar"
+
+ $workspaces = { type = "workspaces" }
+ $launcher = { type = "launcher" }
+ $volume = {
+ type = "volume"
+ format = "{icon} {percentage}%"
+ max_volume = 100
+ icons.volume_high = ""
+ icons.volume_medium = ""
+ icons.volume_low = ""
+ icons.muted = ""
+ }
+ $network_manager = { type = "networkmanager" }
+ $clock = {
+ type = "clock"
+ // disable_popup = true
+ // format = "%d/%m/%Y %H:%M:%S"
+ }
+ $tray = { type = "tray" prefer_theme_icons = false }
+
+ // $label = { type = "label" label = "hello" }
+ $label = { type = "label" label = "#random" }
+ $clipboard = { type = "clipboard" }
+
+ $notifications = {
+ type = "notifications"
+ show_count = true
+
+ icons.closed_none = ""
+ icons.closed_some = ""
+ icons.closed_dnd = ""
+ icons.open_none = ""
+ icons.open_some = ""
+ icons.open_dnd = ""
+ }
+
+ $focused = { type = "focused" }
+
+ $cairo = { type = "cairo" path = "$config_dir/clock.lua" frequency = 50 width = 300 height = 300 }
+
+ $custom = {
+ type = "custom"
+ bar = [ { type = "button" on_click = "popup:toggle" widgets = [ $focused ] } ]
+ popup = [ { type = "box" orientation = "v" widgets = [ $clock $cairo ] } ]
+ }
+
+ $mpris = { type = "music" }
+} in {
+ // ironvar_defaults.color = "red"
+
+ position = "bottom"
+
+ icon_theme = "Paper"
+
+ start = [ $workspaces $label ]
+ center = [ $custom ]
+ end = [ $notifications $clock ]
+}
\ No newline at end of file
diff --git a/flake.lock b/flake.lock
index 3a3b651..a1d9b2d 100644
--- a/flake.lock
+++ b/flake.lock
@@ -7,11 +7,11 @@
]
},
"locked": {
- "lastModified": 1711681752,
- "narHash": "sha256-LEg6/dmEFxx6Ygti5DO9MOhGNpyB7zdxdWtzv/FCTXk=",
+ "lastModified": 1713979152,
+ "narHash": "sha256-apdecPuh8SOQnkEET/kW/UcfjCRb8JbV5BKjoH+DcP4=",
"owner": "ipetkov",
"repo": "crane",
- "rev": "ada0fb4dcce4561acb1eb17c59b7306d9d4a95f3",
+ "rev": "a5eca68a2cf11adb32787fc141cddd29ac8eb79c",
"type": "github"
},
"original": {
@@ -43,11 +43,11 @@
"nixpkgs": "nixpkgs"
},
"locked": {
- "lastModified": 1698420672,
- "narHash": "sha256-/TdeHMPRjjdJub7p7+w55vyABrsJlt5QkznPYy55vKA=",
+ "lastModified": 1713520724,
+ "narHash": "sha256-CO8MmVDmqZX2FovL75pu5BvwhW+Vugc7Q6ze7Hj8heI=",
"owner": "nix-community",
"repo": "naersk",
- "rev": "aeb58d5e8faead8980a807c840232697982d47b9",
+ "rev": "c5037590290c6c7dae2e42e7da1e247e54ed2d49",
"type": "github"
},
"original": {
@@ -58,11 +58,11 @@
},
"nixpkgs": {
"locked": {
- "lastModified": 1711715736,
- "narHash": "sha256-9slQ609YqT9bT/MNX9+5k5jltL9zgpn36DpFB7TkttM=",
+ "lastModified": 1714314149,
+ "narHash": "sha256-yNAevSKF4krRWacmLUsLK7D7PlfuY3zF0lYnGYNi9vQ=",
"owner": "NixOS",
"repo": "nixpkgs",
- "rev": "807c549feabce7eddbf259dbdcec9e0600a0660d",
+ "rev": "cf8cc1201be8bc71b7cbbbdaf349b22f4f99c7ae",
"type": "github"
},
"original": {
@@ -72,11 +72,11 @@
},
"nixpkgs_2": {
"locked": {
- "lastModified": 1711703276,
- "narHash": "sha256-iMUFArF0WCatKK6RzfUJknjem0H9m4KgorO/p3Dopkk=",
+ "lastModified": 1714253743,
+ "narHash": "sha256-mdTQw2XlariysyScCv2tTE45QSU9v/ezLcHJ22f0Nxc=",
"owner": "nixos",
"repo": "nixpkgs",
- "rev": "d8fe5e6c92d0d190646fb9f1056741a229980089",
+ "rev": "58a1abdbae3217ca6b702f03d3b35125d88a2994",
"type": "github"
},
"original": {
@@ -102,11 +102,11 @@
]
},
"locked": {
- "lastModified": 1711851236,
- "narHash": "sha256-EJ03x3N9ihhonAttkaCrqxb0djDq3URCuDpmVPbNZhA=",
+ "lastModified": 1714443211,
+ "narHash": "sha256-lKTA3XqRo4aVgkyTSCtpcALpGXdmkilHTtN00eRg0QU=",
"owner": "oxalica",
"repo": "rust-overlay",
- "rev": "f258266af947599e8069df1c2e933189270f143a",
+ "rev": "ce35c36f58f82cee6ec959e0d44c587d64281b6f",
"type": "github"
},
"original": {
diff --git a/src/clients/compositor/hyprland.rs b/src/clients/compositor/hyprland.rs
index 56184eb..ee526ea 100644
--- a/src/clients/compositor/hyprland.rs
+++ b/src/clients/compositor/hyprland.rs
@@ -149,12 +149,27 @@ impl Client {
}
{
- event_listener.add_workspace_destroy_handler(move |workspace_type| {
- let _lock = lock!(lock);
- debug!("Received workspace destroy: {workspace_type:?}");
+ let tx = tx.clone();
+ let lock = lock.clone();
- let name = get_workspace_name(workspace_type);
- send!(tx, WorkspaceUpdate::Remove(name));
+ event_listener.add_workspace_rename_handler(move |data| {
+ let _lock = lock!(lock);
+
+ send!(
+ tx,
+ WorkspaceUpdate::Rename {
+ id: data.workspace_id as i64,
+ name: data.workspace_name
+ }
+ );
+ });
+ }
+
+ {
+ event_listener.add_workspace_destroy_handler(move |data| {
+ let _lock = lock!(lock);
+ debug!("Received workspace destroy: {data:?}");
+ send!(tx, WorkspaceUpdate::Remove(data.workspace_id as i64));
});
}
@@ -186,6 +201,7 @@ impl Client {
fn get_workspace(name: &str, active: Option<&Workspace>) -> Option {
Workspaces::get()
.expect("Failed to get workspaces")
+ .into_iter()
.find_map(|w| {
if w.name == name {
let vis = Visibility::from((&w, active.map(|w| w.name.as_ref()), &|w| {
@@ -228,6 +244,7 @@ impl WorkspaceClient for Client {
let workspaces = Workspaces::get()
.expect("Failed to get workspaces")
+ .into_iter()
.map(|w| {
let vis = Visibility::from((&w, active_id.as_deref(), &is_visible));
@@ -262,7 +279,7 @@ fn create_is_visible() -> impl Fn(&HWorkspace) -> bool {
impl From<(Visibility, HWorkspace)> for Workspace {
fn from((visibility, workspace): (Visibility, HWorkspace)) -> Self {
Self {
- id: workspace.id.to_string(),
+ id: workspace.id as i64,
name: workspace.name,
monitor: workspace.monitor,
visibility,
diff --git a/src/clients/compositor/mod.rs b/src/clients/compositor/mod.rs
index 0371913..051a7b8 100644
--- a/src/clients/compositor/mod.rs
+++ b/src/clients/compositor/mod.rs
@@ -74,7 +74,7 @@ impl Compositor {
#[derive(Debug, Clone)]
pub struct Workspace {
/// Unique identifier
- pub id: String,
+ pub id: i64,
/// Workspace friendly name
pub name: String,
/// Name of the monitor (output) the workspace is located on
@@ -119,13 +119,19 @@ pub enum WorkspaceUpdate {
/// This is re-sent to all subscribers when a new subscription is created.
Init(Vec),
Add(Workspace),
- Remove(String),
+ Remove(i64),
Move(Workspace),
/// Declares focus moved from the old workspace to the new.
Focus {
old: Option,
new: Workspace,
},
+
+ Rename {
+ id: i64,
+ name: String,
+ },
+
/// An update was triggered by the compositor but this was not mapped by Ironbar.
///
/// This is purely used for ergonomics within the compositor clients
diff --git a/src/clients/compositor/sway.rs b/src/clients/compositor/sway.rs
index 28e8f7f..b3f4197 100644
--- a/src/clients/compositor/sway.rs
+++ b/src/clients/compositor/sway.rs
@@ -90,7 +90,7 @@ impl From for Workspace {
let visibility = Visibility::from(&node);
Self {
- id: node.id.to_string(),
+ id: node.id,
name: node.name.unwrap_or_default(),
monitor: node.output.unwrap_or_default(),
visibility,
@@ -103,7 +103,7 @@ impl From for Workspace {
let visibility = Visibility::from(&workspace);
Self {
- id: workspace.id.to_string(),
+ id: workspace.id,
name: workspace.name,
monitor: workspace.output,
visibility,
@@ -141,13 +141,9 @@ impl From for WorkspaceUpdate {
WorkspaceChange::Init => {
Self::Add(event.current.expect("Missing current workspace").into())
}
- WorkspaceChange::Empty => Self::Remove(
- event
- .current
- .expect("Missing current workspace")
- .name
- .unwrap_or_default(),
- ),
+ WorkspaceChange::Empty => {
+ Self::Remove(event.current.expect("Missing current workspace").id)
+ }
WorkspaceChange::Focus => Self::Focus {
old: event.old.map(Workspace::from),
new: Workspace::from(event.current.expect("Missing current workspace")),
diff --git a/src/clients/wayland/wlr_data_control/mod.rs b/src/clients/wayland/wlr_data_control/mod.rs
index 1cf76a5..dbe5f8e 100644
--- a/src/clients/wayland/wlr_data_control/mod.rs
+++ b/src/clients/wayland/wlr_data_control/mod.rs
@@ -11,7 +11,7 @@ use crate::{lock, try_send, Ironbar};
use device::DataControlDevice;
use glib::Bytes;
use nix::fcntl::{fcntl, F_GETPIPE_SZ, F_SETPIPE_SZ};
-use nix::sys::epoll::{Epoll, EpollCreateFlags, EpollEvent, EpollFlags};
+use nix::sys::epoll::{Epoll, EpollCreateFlags, EpollEvent, EpollFlags, EpollTimeout};
use smithay_client_toolkit::data_device_manager::WritePipe;
use smithay_client_toolkit::reexports::calloop::{PostAction, RegistrationToken};
use std::cmp::min;
@@ -274,7 +274,7 @@ impl DataControlDeviceHandler for Environment {
Ok(token) => {
cur_offer.token.replace(token);
}
- Err(err) => error!("{err:?}"),
+ Err(err) => error!("Failed to insert read pipe event: {err:?}"),
}
}
}
@@ -294,15 +294,15 @@ impl DataControlOfferHandler for Environment {
}
impl DataControlSourceHandler for Environment {
- fn accept_mime(
- &mut self,
- _conn: &Connection,
- _qh: &QueueHandle,
- _source: &ZwlrDataControlSourceV1,
- mime: Option,
- ) {
- debug!("Accepted mime type: {mime:?}");
- }
+ // fn accept_mime(
+ // &mut self,
+ // _conn: &Connection,
+ // _qh: &QueueHandle,
+ // _source: &ZwlrDataControlSourceV1,
+ // mime: Option,
+ // ) {
+ // debug!("Accepted mime type: {mime:?}");
+ // }
/// Writes the current clipboard item to 'paste' it
/// upon request from a compositor client.
@@ -349,11 +349,12 @@ impl DataControlSourceHandler for Environment {
.add(fd, epoll_event)
.expect("to send valid epoll operation");
+ let timeout = EpollTimeout::from(100u16);
while !bytes.is_empty() {
let chunk = &bytes[..min(pipe_size as usize, bytes.len())];
epoll_fd
- .wait(&mut events, 100)
+ .wait(&mut events, timeout)
.expect("Failed to wait to epoll");
match file.write(chunk) {
diff --git a/src/clients/wayland/wlr_data_control/offer.rs b/src/clients/wayland/wlr_data_control/offer.rs
index a078ade..73f06f6 100644
--- a/src/clients/wayland/wlr_data_control/offer.rs
+++ b/src/clients/wayland/wlr_data_control/offer.rs
@@ -5,7 +5,7 @@ use nix::unistd::{close, pipe2};
use smithay_client_toolkit::data_device_manager::data_offer::DataOfferError;
use smithay_client_toolkit::data_device_manager::ReadPipe;
use std::ops::DerefMut;
-use std::os::fd::{BorrowedFd, FromRawFd};
+use std::os::fd::{AsFd, AsRawFd};
use std::sync::{Arc, Mutex};
use tracing::{trace, warn};
use wayland_client::{Connection, Dispatch, Proxy, QueueHandle};
@@ -176,11 +176,11 @@ pub unsafe fn receive(
// create a pipe
let (readfd, writefd) = pipe2(OFlag::O_CLOEXEC)?;
- offer.receive(mime_type, BorrowedFd::borrow_raw(writefd));
+ offer.receive(mime_type, writefd.as_fd());
- if let Err(err) = close(writefd) {
+ if let Err(err) = close(writefd.as_raw_fd()) {
warn!("Failed to close write pipe: {}", err);
}
- Ok(FromRawFd::from_raw_fd(readfd))
+ Ok(ReadPipe::from(readfd))
}
diff --git a/src/clients/wayland/wlr_data_control/source.rs b/src/clients/wayland/wlr_data_control/source.rs
index a184d8d..b8f2a1c 100644
--- a/src/clients/wayland/wlr_data_control/source.rs
+++ b/src/clients/wayland/wlr_data_control/source.rs
@@ -10,13 +10,13 @@ use wayland_protocols_wlr::data_control::v1::client::zwlr_data_control_source_v1
pub struct DataControlSourceData {}
pub trait DataControlSourceDataExt: Send + Sync {
- fn data_source_data(&self) -> &DataControlSourceData;
+ // fn data_source_data(&self) -> &DataControlSourceData;
}
impl DataControlSourceDataExt for DataControlSourceData {
- fn data_source_data(&self) -> &DataControlSourceData {
- self
- }
+ // fn data_source_data(&self) -> &DataControlSourceData {
+ // self
+ // }
}
/// Handler trait for `DataSource` events.
@@ -24,13 +24,13 @@ impl DataControlSourceDataExt for DataControlSourceData {
/// The functions defined in this trait are called as `DataSource` events are received from the compositor.
pub trait DataControlSourceHandler: Sized {
/// This may be called multiple times, once for each accepted mime type from the destination, if any.
- fn accept_mime(
- &mut self,
- conn: &Connection,
- qh: &QueueHandle,
- source: &ZwlrDataControlSourceV1,
- mime: Option,
- );
+ // fn accept_mime(
+ // &mut self,
+ // conn: &Connection,
+ // qh: &QueueHandle,
+ // source: &ZwlrDataControlSourceV1,
+ // mime: Option,
+ // );
/// The client has requested the data for this source to be sent.
/// Send the data, then close the fd.
diff --git a/src/clients/wayland/wlr_foreign_toplevel/mod.rs b/src/clients/wayland/wlr_foreign_toplevel/mod.rs
index d0c3d9e..48032dc 100644
--- a/src/clients/wayland/wlr_foreign_toplevel/mod.rs
+++ b/src/clients/wayland/wlr_foreign_toplevel/mod.rs
@@ -77,7 +77,6 @@ impl ToplevelHandleHandler for Environment {
match handle.info() {
Some(info) => {
trace!("Updating handle: {info:?}");
- self.handles.push(handle.clone());
if let Some(info) = handle.info() {
try_send!(self.event_tx, Event::Toplevel(ToplevelEvent::Update(info)));
}
diff --git a/src/config/common.rs b/src/config/common.rs
index 25a8944..d1446e2 100644
--- a/src/config/common.rs
+++ b/src/config/common.rs
@@ -7,26 +7,153 @@ use gtk::{EventBox, Orientation, Revealer, RevealerTransitionType};
use serde::Deserialize;
use tracing::trace;
-/// Common configuration options
-/// which can be set on every module.
+/// The following are module-level options which are present on **all** modules.
+///
+/// Each module also provides options specific to its type.
+/// For details on those, check the relevant module documentation.
+///
+/// For information on the Script type, and embedding scripts in strings,
+/// see [here](script).
+/// For information on styling, please see the [styling guide](styling-guide).
#[derive(Debug, Default, Deserialize, Clone)]
pub struct CommonConfig {
- pub class: Option,
+ /// Sets the unique widget name,
+ /// allowing you to target it in CSS using `#name`.
+ ///
+ /// It is best practise (although not required) to ensure that the value is
+ /// globally unique throughout the Ironbar instance
+ /// to avoid clashes.
+ ///
+ /// **Default**: `null`
pub name: Option,
+ /// Sets one or more CSS classes,
+ /// allowing you to target it in CSS using `.class`.
+ ///
+ /// Unlike [name](#name), the `class` property is not expected to be unique.
+ ///
+ /// **Default**: `null`
+ pub class: Option,
+
+ /// Shows this text on hover.
+ /// Supports embedding scripts between `{{double braces}}`.
+ ///
+ /// Note that full dynamic string support is not currently supported.
+ ///
+ /// **Default**: `null`
+ pub tooltip: Option,
+
+ /// Shows the module only if the dynamic boolean evaluates to true.
+ ///
+ /// This allows for modules to be dynamically shown or hidden
+ /// based on custom events.
+ ///
+ /// **Default**: `null`
pub show_if: Option,
+
+ /// The transition animation to use when showing/hiding the widget.
+ ///
+ /// Note this has no effect if `show_if` is not configured.
+ ///
+ /// **Valid options**: `slide_start`, `slide_end`, `crossfade`, `none`
+ ///
+ /// **Default**: `slide_start`
pub transition_type: Option,
+
+ /// The length in milliseconds
+ /// of the transition animation to use when showing/hiding the widget.
+ ///
+ /// Note this has no effect if `show_if` is not configured.
+ ///
+ /// **Default**: `250`
pub transition_duration: Option,
+ /// A [script](scripts) to run when the module is left-clicked.
+ ///
+ /// **Supported script types**: `oneshot`.
+ ///
+ /// **Default**: `null`
+ ///
+ /// # Example
+ ///
+ /// ```corn
+ /// { on_click_left = "echo 'event' >> log.txt" }
+ /// ```
pub on_click_left: Option,
+
+ /// A [script](scripts) to run when the module is right-clicked.
+ ///
+ /// **Supported script types**: `oneshot`.
+ ///
+ /// **Default**: `null`
+ /// /// # Example
+ ///
+ /// ```corn
+ /// { on_click_right = "echo 'event' >> log.txt" }
+ /// ```
pub on_click_right: Option,
+
+ /// A [script](scripts) to run when the module is middle-clicked.
+ ///
+ /// **Supported script types**: `oneshot`.
+ ///
+ /// **Default**: `null`
+ /// # Example
+ ///
+ /// ```corn
+ /// { on_click_middle = "echo 'event' >> log.txt" }
+ /// ```
pub on_click_middle: Option,
+
+ /// A [script](scripts) to run when the module is scrolled up on.
+ ///
+ /// **Supported script types**: `oneshot`.
+ ///
+ /// **Default**: `null`
+ /// # Example
+ ///
+ /// ```corn
+ /// { on_scroll_up = "echo 'event' >> log.txt" }
+ /// ```
pub on_scroll_up: Option,
+
+ /// A [script](scripts) to run when the module is scrolled down on.
+ ///
+ /// **Supported script types**: `oneshot`.
+ ///
+ /// **Default**: `null`
+ /// # Example
+ ///
+ /// ```corn
+ /// { on_scroll_down = "echo 'event' >> log.txt" }
+ /// ```
pub on_scroll_down: Option,
+
+ /// A [script](scripts) to run when the cursor begins hovering over the module.
+ ///
+ /// **Supported script types**: `oneshot`.
+ ///
+ /// **Default**: `null`
+ /// # Example
+ ///
+ /// ```corn
+ /// { on_mouse_enter = "echo 'event' >> log.txt" }
+ /// ```
pub on_mouse_enter: Option,
+
+ /// A [script](scripts) to run when the cursor stops hovering over the module.
+ ///
+ /// **Supported script types**: `oneshot`.
+ ///
+ /// **Default**: `null`
+ /// # Example
+ ///
+ /// ```corn
+ /// { on_mouse_exit = "echo 'event' >> log.txt" }
+ /// ```
pub on_mouse_exit: Option,
- pub tooltip: Option,
+ /// Prevents the popup from opening on-click for this widget.
#[serde(default)]
pub disable_popup: bool,
}
diff --git a/src/config/mod.rs b/src/config/mod.rs
index 03c4daf..852fc7d 100644
--- a/src/config/mod.rs
+++ b/src/config/mod.rs
@@ -122,12 +122,6 @@ impl ModuleConfig {
}
}
-#[derive(Debug, Deserialize, Clone)]
-pub enum BarEntryConfig {
- Single(BarConfig),
- Monitors(HashMap),
-}
-
#[derive(Debug, Clone)]
pub enum MonitorConfig {
Single(BarConfig),
@@ -161,32 +155,107 @@ pub struct MarginConfig {
pub top: i32,
}
+/// The following is a list of all top-level bar config options.
+///
+/// These options can either be written at the very top object of your config,
+/// or within an object in the [monitors](#monitors) config,
+/// depending on your [use-case](#2-pick-your-use-case).
+///
#[derive(Debug, Deserialize, Clone)]
pub struct BarConfig {
- #[serde(default)]
- pub position: BarPosition,
- #[serde(default = "default_true")]
- pub anchor_to_edges: bool,
- #[serde(default = "default_bar_height")]
- pub height: i32,
- #[serde(default)]
- pub margin: MarginConfig,
+ /// A unique identifier for the bar, used for controlling it over IPC.
+ /// If not set, uses a generated integer suffix.
+ ///
+ /// **Default**: `bar-n`
pub name: Option,
+ /// The bar's position on screen.
+ ///
+ /// **Valid options**: `top`, `bottom`, `left`, `right`
+ ///
+ /// **Default**: `bottom`
+ #[serde(default)]
+ pub position: BarPosition,
+
+ /// Whether to anchor the bar to the edges of the screen.
+ /// Setting to false centers the bar.
+ ///
+ /// **Default**: `true`
+ #[serde(default = "default_true")]
+ pub anchor_to_edges: bool,
+
+ /// The bar's height in pixels.
+ ///
+ /// Note that GTK treats this as a target minimum,
+ /// and if content inside the bar is over this,
+ /// it will automatically expand to fit.
+ ///
+ /// **Default**: `42`
+ #[serde(default = "default_bar_height")]
+ pub height: i32,
+
+ /// The margin to use on each side of the bar, in pixels.
+ /// Object which takes `top`, `bottom`, `left` and `right` keys.
+ ///
+ /// **Default**: `0` on all sides.
+ ///
+ /// # Example
+ ///
+ /// The following would set a 10px margin around each edge.
+ ///
+ /// ```corn
+ /// {
+ /// margin.top = 10
+ /// margin.bottom = 10
+ /// margin.left = 10
+ /// margin.right = 10
+ /// }
+ /// ```
+ #[serde(default)]
+ pub margin: MarginConfig,
+
+ /// The size of the gap in pixels
+ /// between the bar and the popup window.
+ ///
+ /// **Default**: `5`
+ #[serde(default = "default_popup_gap")]
+ pub popup_gap: i32,
+
+ /// Whether the bar should be hidden when Ironbar starts.
+ ///
+ /// **Default**: `false`, unless `autohide` is set.
#[serde(default)]
pub start_hidden: Option,
+
+ /// The duration in milliseconds before the bar is hidden after the cursor leaves.
+ /// Leave unset to disable auto-hide behaviour.
+ ///
+ /// **Default**: `null`
#[serde(default)]
pub autohide: Option,
- /// GTK icon theme to use.
+ /// The name of the GTK icon theme to use.
+ /// Leave unset to use the default Adwaita theme.
+ ///
+ /// **Default**: `null`
pub icon_theme: Option,
+ /// An array of modules to append to the start of the bar.
+ /// Depending on the orientation, this is either the top of the left edge.
+ ///
+ /// **Default**: `[]`
pub start: Option>,
- pub center: Option>,
- pub end: Option>,
- #[serde(default = "default_popup_gap")]
- pub popup_gap: i32,
+ /// An array of modules to append to the center of the bar.
+ ///
+ /// **Default**: `[]`
+ pub center: Option>,
+
+ /// An array of modules to append to the end of the bar.
+ /// Depending on the orientation, this is either the bottom or right edge.
+ ///
+ /// **Default**: `[]`
+ pub end: Option>,
}
impl Default for BarConfig {
@@ -230,10 +299,41 @@ impl Default for BarConfig {
#[derive(Debug, Deserialize, Clone, Default)]
pub struct Config {
+ /// A map of [ironvar](ironvar) keys and values
+ /// to initialize Ironbar with on startup.
+ ///
+ /// **Default**: `{}`
+ ///
+ /// # Example
+ ///
+ /// The following initializes an ironvar called `foo` set to `bar` on startup:
+ ///
+ /// ```corn
+ /// { ironvar_defaults.foo = "bar" }
+ /// ```
+ ///
+ /// The variable can then be immediately fetched without needing to be manually set:
+ ///
+ /// ```sh
+ /// $ ironbar get foo
+ /// ok
+ /// bar
+ /// ```
pub ironvar_defaults: Option, String>>,
+ /// The configuration for the bar.
+ /// Setting through this will enable a single identical bar on each monitor.
#[serde(flatten)]
pub bar: BarConfig,
+
+ /// A map of monitor names to configs.
+ ///
+ /// The config values can be either:
+ ///
+ /// - a single object, which denotes a single bar for that monitor,
+ /// - an array of multiple objects, which denotes multiple for that monitor.
+ ///
+ /// Providing this option overrides the single, global `bar` option.
pub monitors: Option>,
}
diff --git a/src/config/truncate.rs b/src/config/truncate.rs
index fed4ffc..87451e4 100644
--- a/src/config/truncate.rs
+++ b/src/config/truncate.rs
@@ -20,13 +20,68 @@ impl From for GtkEllipsizeMode {
}
}
+/// Some modules provide options for truncating text.
+/// This is controlled using a common `TruncateMode` type,
+/// which is defined below.
+///
+/// The option can be configured in one of two modes.
+///
#[derive(Debug, Deserialize, Clone, Copy)]
#[serde(untagged)]
pub enum TruncateMode {
+ /// Auto mode lets GTK decide when to ellipsize.
+ ///
+ /// To use this mode, set the truncate option to a string
+ /// declaring the location to truncate text from and place the ellipsis.
+ ///
+ /// # Example
+ ///
+ /// ```corn
+ /// { truncate = "start" }
+ /// ```
+ ///
+ /// **Valid options**: `start`, `middle`, `end`
+ ///
+ /// **Default**: `null`
Auto(EllipsizeMode),
+
+ /// Length mode defines a fixed point at which to ellipsize.
+ ///
+ /// Generally you will want to set only one of `length` or `max_length`,
+ /// but you can set both if required.
+ ///
+ /// # Example
+ ///
+ /// ```corn
+ /// {
+ /// truncate.mode = "start"
+ /// truncate.length = 50
+ /// truncate.max_length = 70
+ /// }
+ /// ```
Length {
+ /// The location to truncate text from and place the ellipsis.
+ /// **Valid options**: `start`, `middle`, `end`
+ ///
+ /// **Default**: `null`
mode: EllipsizeMode,
+
+ /// The fixed width (in characters) of the widget.
+ ///
+ /// The widget will be expanded to this width
+ /// if it would have otherwise been smaller.
+ ///
+ /// Leave unset to let GTK automatically handle.
+ ///
+ /// **Default**: `null`
length: Option,
+
+ /// The maximum number of characters to show
+ /// before truncating.
+ ///
+ /// Leave unset to let GTK automatically handle.
+ ///
+ /// **Default**: `null`
max_length: Option,
},
}
diff --git a/src/image/gtk.rs b/src/image/gtk.rs
index 8ed38a3..bf80bcd 100644
--- a/src/image/gtk.rs
+++ b/src/image/gtk.rs
@@ -44,7 +44,7 @@ pub fn new_icon_label(input: &str, icon_theme: &IconTheme, size: i32) -> gtk::Bo
ImageProvider::parse(input, icon_theme, false, size)
.map(|provider| provider.load_into_image(image));
} else {
- let label = Label::new(Some(input));
+ let label = Label::builder().use_markup(true).label(input).build();
label.add_class("icon");
label.add_class("text-icon");
diff --git a/src/ironvar.rs b/src/ironvar.rs
index e99a3b0..190fea2 100644
--- a/src/ironvar.rs
+++ b/src/ironvar.rs
@@ -93,7 +93,7 @@ impl IronVar {
/// Sets the current variable value.
/// The change is broadcast to all receivers.
fn set(&mut self, value: Option) {
- self.value = value.clone();
+ self.value.clone_from(&value);
send!(self.tx, value);
}
diff --git a/src/main.rs b/src/main.rs
index 0df08ea..441c0e3 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -9,7 +9,7 @@ use std::rc::Rc;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
#[cfg(feature = "ipc")]
use std::sync::RwLock;
-use std::sync::{mpsc, Arc, OnceLock};
+use std::sync::{mpsc, Arc, Mutex, OnceLock};
use cfg_if::cfg_if;
#[cfg(feature = "cli")]
@@ -339,17 +339,36 @@ fn load_output_bars(
app: &Application,
output: &OutputInfo,
) -> Result> {
+ // Hack to track monitor positions due to new GTK3/wlroots bug:
+ // https://github.com/swaywm/sway/issues/8164
+ // This relies on Wayland always tracking monitors in the same order as GDK.
+ // We also need this static to ensure hot-reloading continues to work as best we can.
+ static INDEX_MAP: OnceLock>> = OnceLock::new();
+
let Some(monitor_name) = &output.name else {
return Err(Report::msg("Output missing monitor name"));
};
+ let map = INDEX_MAP.get_or_init(|| Mutex::new(vec![]));
+
+ let index = lock!(map).iter().position(|n| n == monitor_name);
+ let index = match index {
+ Some(index) => index,
+ None => {
+ lock!(map).push(monitor_name.clone());
+ lock!(map).len() - 1
+ }
+ };
+
let config = ironbar.config.borrow();
let display = get_display();
- let pos = output.logical_position.unwrap_or_default();
- let monitor = display
- .monitor_at_point(pos.0, pos.1)
- .expect("monitor to exist");
+ // let pos = output.logical_position.unwrap_or_default();
+ // let monitor = display
+ // .monitor_at_point(pos.0, pos.1)
+ // .expect("monitor to exist");
+
+ let monitor = display.monitor(index as i32).expect("monitor to exist");
let show_default_bar =
config.bar.start.is_some() || config.bar.center.is_some() || config.bar.end.is_some();
diff --git a/src/modules/cairo.rs b/src/modules/cairo.rs
index 9e21aec..3ce42bb 100644
--- a/src/modules/cairo.rs
+++ b/src/modules/cairo.rs
@@ -19,16 +19,33 @@ use tracing::{debug, error};
#[derive(Debug, Clone, Deserialize)]
pub struct CairoModule {
+ /// The path to the Lua script to load.
+ /// This can be absolute, or relative to the working directory.
+ ///
+ /// The script must contain the entry `draw` function.
+ ///
+ /// **Required**
path: PathBuf,
+ /// The number of milliseconds between each draw call.
+ ///
+ /// **Default**: `200`
#[serde(default = "default_frequency")]
frequency: u64,
+ /// The canvas width in pixels.
+ ///
+ /// **Default**: `42`
#[serde(default = "default_size")]
width: u32,
+
+ /// The canvas height in pixels.
+ ///
+ /// **Default**: `42`
#[serde(default = "default_size")]
height: u32,
+ /// See [common options](module-level-options#common-options).
#[serde(flatten)]
pub common: Option,
}
diff --git a/src/modules/clipboard.rs b/src/modules/clipboard.rs
index ae34a03..6365088 100644
--- a/src/modules/clipboard.rs
+++ b/src/modules/clipboard.rs
@@ -18,18 +18,34 @@ use tracing::{debug, error};
#[derive(Debug, Deserialize, Clone)]
pub struct ClipboardModule {
+ /// The icon to show on the bar widget button.
+ /// Supports [image](images) icons.
+ ///
+ /// **Default**: ``
#[serde(default = "default_icon")]
icon: String,
+ /// The size to render the icon at.
+ /// Note this only applies to image-type icons.
+ ///
+ /// **Default**: `32`
#[serde(default = "default_icon_size")]
icon_size: i32,
+ /// The maximum number of items to keep in the history,
+ /// and to show in the popup.
+ ///
+ /// **Default**: `10`
#[serde(default = "default_max_items")]
max_items: usize,
// -- Common --
+ /// See [truncate options](module-level-options#truncate-mode).
+ ///
+ /// **Default**: `null`
truncate: Option,
+ /// See [common options](module-level-options#common-options).
#[serde(flatten)]
pub common: Option,
}
diff --git a/src/modules/clock.rs b/src/modules/clock.rs
index 602c6e4..a5be924 100644
--- a/src/modules/clock.rs
+++ b/src/modules/clock.rs
@@ -17,23 +17,47 @@ use crate::{glib_recv, module_impl, send_async, spawn, try_send};
#[derive(Debug, Deserialize, Clone)]
pub struct ClockModule {
- /// Date/time format string.
- /// Default: `%d/%m/%Y %H:%M`
+ /// The format string to use for the date/time shown on the bar.
+ /// Pango markup is supported.
///
/// Detail on available tokens can be found here:
///
+ ///
+ /// **Default**: `%d/%m/%Y %H:%M`
#[serde(default = "default_format")]
format: String,
+ /// The format string to use for the date/time shown in the popup header.
+ /// Pango markup is supported.
+ ///
+ /// Detail on available tokens can be found here:
+ ///
+ ///
+ /// **Default**: `%H:%M:%S`
#[serde(default = "default_popup_format")]
format_popup: String,
+ /// The locale to use when formatting dates.
+ ///
+ /// Note this will not control the calendar -
+ /// for that you must set `LC_TIME`.
+ ///
+ /// **Valid options**: See [here](https://docs.rs/pure-rust-locales/0.8.1/pure_rust_locales/enum.Locale.html#variants)
+ ///
+ /// **Default**: `$LC_TIME` or `$LANG` or `'POSIX'`
#[serde(default = "default_locale")]
locale: String,
+ /// The orientation to display the widget contents.
+ /// Setting to vertical will rotate text 90 degrees.
+ ///
+ /// **Valid options**: `horizontal`, `vertical`
+ ///
+ /// **Default**: `horizontal`
#[serde(default)]
orientation: ModuleOrientation,
+ /// See [common options](module-level-options#common-options).
#[serde(flatten)]
pub common: Option,
}
diff --git a/src/modules/custom/box.rs b/src/modules/custom/box.rs
index dca848a..7e448f4 100644
--- a/src/modules/custom/box.rs
+++ b/src/modules/custom/box.rs
@@ -7,9 +7,26 @@ use serde::Deserialize;
#[derive(Debug, Deserialize, Clone)]
pub struct BoxWidget {
+ /// Widget name.
+ ///
+ /// **Default**: `null`
name: Option,
+
+ /// Widget class name.
+ ///
+ /// **Default**: `null`
class: Option,
+
+ /// Whether child widgets should be horizontally or vertically added.
+ ///
+ /// **Valid options**: `horizontal`, `vertical`, `h`, `v`
+ ///
+ /// **Default**: `horizontal`
orientation: Option,
+
+ /// Modules and widgets to add to this box.
+ ///
+ /// **Default**: `null`
widgets: Option>,
}
diff --git a/src/modules/custom/button.rs b/src/modules/custom/button.rs
index 199420d..9382da2 100644
--- a/src/modules/custom/button.rs
+++ b/src/modules/custom/button.rs
@@ -11,13 +11,43 @@ use super::{CustomWidget, CustomWidgetContext, ExecEvent, WidgetConfig};
#[derive(Debug, Deserialize, Clone)]
pub struct ButtonWidget {
+ /// Widget name.
+ ///
+ /// **Default**: `null`
name: Option,
+
+ /// Widget class name.
+ ///
+ /// **Default**: `null`
class: Option,
+
+ /// Widget text label. Pango markup and embedded scripts are supported.
+ ///
+ /// This is a shorthand for adding a label widget to the button.
+ /// Ignored if `widgets` is set.
+ ///
+ /// This is a [Dynamic String](dynamic-values#dynamic-string).
+ ///
+ /// **Default**: `null`
label: Option,
+
+ /// Command to execute. More on this [below](#commands).
+ ///
+ /// **Default**: `null`
on_click: Option,
- widgets: Option>,
+
+ /// Orientation of the button.
+ ///
+ /// **Valid options**: `horizontal`, `vertical`, `h`, `v`
+ ///
+ /// **Default**: `horizontal`
#[serde(default)]
orientation: ModuleOrientation,
+
+ /// Modules and widgets to add to this box.
+ ///
+ /// **Default**: `null`
+ widgets: Option>,
}
impl CustomWidget for ButtonWidget {
diff --git a/src/modules/custom/image.rs b/src/modules/custom/image.rs
index e5d099d..fffac2d 100644
--- a/src/modules/custom/image.rs
+++ b/src/modules/custom/image.rs
@@ -10,9 +10,27 @@ use super::{CustomWidget, CustomWidgetContext};
#[derive(Debug, Deserialize, Clone)]
pub struct ImageWidget {
+ /// Widget name.
+ ///
+ /// **Default**: `null`
name: Option,
+
+ /// Widget class name.
+ ///
+ /// **Default**: `null`
class: Option,
+
+ /// Image source.
+ ///
+ /// This is an [image](image) via [Dynamic String](dynamic-values#dynamic-string).
+ ///
+ /// **Required**
src: String,
+
+ /// The width/height of the image.
+ /// Aspect ratio is preserved.
+ ///
+ /// **Default**: `32`
#[serde(default = "default_size")]
size: i32,
}
diff --git a/src/modules/custom/label.rs b/src/modules/custom/label.rs
index ca551c8..65f303b 100644
--- a/src/modules/custom/label.rs
+++ b/src/modules/custom/label.rs
@@ -10,9 +10,29 @@ use super::{CustomWidget, CustomWidgetContext};
#[derive(Debug, Deserialize, Clone)]
pub struct LabelWidget {
+ /// Widget name.
+ ///
+ /// **Default**: `null`
name: Option,
+
+ /// Widget class name.
+ ///
+ /// **Default**: `null`
class: Option,
+
+ /// Widget text label. Pango markup and embedded scripts are supported.
+ ///
+ /// This is a [Dynamic String](dynamic-values#dynamic-string).
+ ///
+ /// **Required**
label: String,
+
+ /// Orientation of the label.
+ /// Setting to vertical will rotate text 90 degrees.
+ ///
+ /// **Valid options**: `horizontal`, `vertical`, `h`, `v`
+ ///
+ /// **Default**: `horizontal`
#[serde(default)]
orientation: ModuleOrientation,
}
@@ -24,7 +44,6 @@ impl CustomWidget for LabelWidget {
let label = build!(self, Self::Widget);
label.set_angle(self.orientation.to_angle());
-
label.set_use_markup(true);
{
diff --git a/src/modules/custom/mod.rs b/src/modules/custom/mod.rs
index dc54e83..5cffb00 100644
--- a/src/modules/custom/mod.rs
+++ b/src/modules/custom/mod.rs
@@ -29,19 +29,28 @@ use tracing::{debug, error};
#[derive(Debug, Deserialize, Clone)]
pub struct CustomModule {
- /// Widgets to add to the bar container
+ /// Modules and widgets to add to the bar container.
+ ///
+ /// **Default**: `[]`
bar: Vec,
- /// Widgets to add to the popup container
+
+ /// Modules and widgets to add to the popup container.
+ ///
+ /// **Default**: `null`
popup: Option>,
+ /// See [common options](module-level-options#common-options).
#[serde(flatten)]
pub common: Option,
}
#[derive(Debug, Deserialize, Clone)]
pub struct WidgetConfig {
+ /// One of a custom module native Ironbar module.
#[serde(flatten)]
widget: WidgetOrModule,
+
+ /// See [common options](module-level-options#common-options).
#[serde(flatten)]
common: CommonConfig,
}
@@ -49,18 +58,27 @@ pub struct WidgetConfig {
#[derive(Debug, Deserialize, Clone)]
#[serde(untagged)]
pub enum WidgetOrModule {
+ /// A custom-module specific basic widget
Widget(Widget),
+ /// A native Ironbar module, such as `clock` or `focused`.
+ /// All widgets are supported, including their popups.
Module(ModuleConfig),
}
#[derive(Debug, Deserialize, Clone)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum Widget {
+ /// A container to place nested widgets inside.
Box(BoxWidget),
+ /// A text label. Pango markup is supported.
Label(LabelWidget),
+ /// A clickable button, which can run a command when clicked.
Button(ButtonWidget),
+ /// An image or icon from disk or http.
Image(ImageWidget),
+ /// A draggable slider.
Slider(SliderWidget),
+ /// A progress bar.
Progress(ProgressWidget),
}
diff --git a/src/modules/custom/progress.rs b/src/modules/custom/progress.rs
index 16f46f8..199ea8c 100644
--- a/src/modules/custom/progress.rs
+++ b/src/modules/custom/progress.rs
@@ -14,14 +14,49 @@ use super::{CustomWidget, CustomWidgetContext};
#[derive(Debug, Deserialize, Clone)]
pub struct ProgressWidget {
+ /// Widget name.
+ ///
+ /// **Default**: `null`
name: Option,
+
+ /// Widget class name.
+ ///
+ /// **Default**: `null`
class: Option,
+
+ /// Orientation of the progress bar.
+ ///
+ /// **Valid options**: `horizontal`, `vertical`, `h`, `v`
+ ///
+ /// **Default**: `horizontal`
#[serde(default)]
orientation: ModuleOrientation,
+
+ /// Text label to show for the progress bar.
+ ///
+ /// This is a [Dynamic String](dynamic-values#dynamic-string).
+ ///
+ /// **Default**: `null`
label: Option,
+
+ /// Script to run to get the progress bar value.
+ /// Output must be a valid percentage.
+ ///
+ /// Note that this expects a numeric value between `0`-`max` as output.
+ ///
+ /// **Default**: `null`
value: Option,
+
+ /// The maximum progress bar value.
+ ///
+ /// **Default**: `100`
#[serde(default = "default_max")]
max: f64,
+
+ /// The progress bar length, in pixels.
+ /// GTK will automatically determine the size if left blank.
+ ///
+ /// **Default**: `null`
length: Option,
}
diff --git a/src/modules/custom/slider.rs b/src/modules/custom/slider.rs
index 6bb3634..c533719 100644
--- a/src/modules/custom/slider.rs
+++ b/src/modules/custom/slider.rs
@@ -17,18 +17,67 @@ use super::{CustomWidget, CustomWidgetContext, ExecEvent};
#[derive(Debug, Deserialize, Clone)]
pub struct SliderWidget {
+ /// Widget name.
+ ///
+ /// **Default**: `null`
name: Option,
+
+ /// Widget class name.
+ ///
+ /// **Default**: `null`
class: Option,
+
+ /// Orientation of the slider.
+ ///
+ /// **Valid options**: `horizontal`, `vertical`, `h`, `v`
+ ///
+ /// **Default**: `horizontal`
#[serde(default)]
orientation: ModuleOrientation,
+
+ /// Script to run to get the slider value.
+ /// Output must be a valid number.
+ ///
+ /// **Default**: `null`
value: Option,
+
+ /// Command to execute when the slider changes.
+ /// More on this [below](#slider).
+ ///
+ /// Note that this will provide the floating point value as an argument.
+ /// If your input program requires an integer, you will need to round it.
+ ///
+ /// **Default**: `null`
on_change: Option,
+
+ /// Minimum slider value.
+ ///
+ /// **Default**: `0`
#[serde(default = "default_min")]
min: f64,
+
+ /// Maximum slider value.
+ ///
+ /// **Default**: `100`
#[serde(default = "default_max")]
max: f64,
+
+ /// If the increment to change when scrolling with the mousewheel.
+ /// If left blank, GTK will use the default value,
+ /// determined by the current environment.
+ ///
+ /// **Default**: `null`
step: Option,
+
+ /// The slider length.
+ /// GTK will automatically determine the size if left blank.
+ ///
+ /// **Default**: `null`
length: Option,
+
+ /// Whether to show the value label above the slider.
+ ///
+ /// **Default**: `true`
#[serde(default = "crate::config::default_true")]
show_label: bool,
}
diff --git a/src/modules/focused.rs b/src/modules/focused.rs
index c63d263..9fb43d2 100644
--- a/src/modules/focused.rs
+++ b/src/modules/focused.rs
@@ -14,18 +14,29 @@ use tracing::debug;
#[derive(Debug, Deserialize, Clone)]
pub struct FocusedModule {
/// Whether to show icon on the bar.
+ ///
+ /// **Default**: `true`
#[serde(default = "crate::config::default_true")]
show_icon: bool,
/// Whether to show app name on the bar.
+ ///
+ /// **Default**: `true`
#[serde(default = "crate::config::default_true")]
show_title: bool,
/// Icon size in pixels.
+ ///
+ /// **Default**: `32`
#[serde(default = "default_icon_size")]
icon_size: i32,
+ // -- common --
+ /// See [truncate options](module-level-options#truncate-mode).
+ ///
+ /// **Default**: `null`
truncate: Option,
+ /// See [common options](module-level-options#common-options).
#[serde(flatten)]
pub common: Option,
}
@@ -81,7 +92,6 @@ impl Module for FocusedModule {
while let Ok(event) = wlrx.recv().await {
match event {
ToplevelEvent::Update(info) => {
- println!("{current:?} | {info:?}");
if info.focused {
debug!("Changing focus");
diff --git a/src/modules/label.rs b/src/modules/label.rs
index dde6f46..2a8a20c 100644
--- a/src/modules/label.rs
+++ b/src/modules/label.rs
@@ -10,8 +10,13 @@ use tokio::sync::mpsc;
#[derive(Debug, Deserialize, Clone)]
pub struct LabelModule {
+ /// The text to show on the label.
+ /// This is a [Dynamic String](dynamic-values#dynamic-string).
+ ///
+ /// **Required**
label: String,
+ /// See [common options](module-level-options#common-options).
#[serde(flatten)]
pub common: Option,
}
diff --git a/src/modules/launcher/item.rs b/src/modules/launcher/item.rs
index 924b74a..07d7fb3 100644
--- a/src/modules/launcher/item.rs
+++ b/src/modules/launcher/item.rs
@@ -40,7 +40,7 @@ impl Item {
let id = info.id;
if self.windows.is_empty() {
- self.name = info.title.clone();
+ self.name.clone_from(&info.title);
}
let window = Window::from(info);
@@ -59,7 +59,7 @@ impl Item {
pub fn set_window_name(&mut self, window_id: usize, name: String) {
if let Some(window) = self.windows.get_mut(&window_id) {
if let OpenState::Open { focused: true, .. } = window.open_state {
- self.name = name.clone();
+ self.name.clone_from(&name);
}
window.name = name;
diff --git a/src/modules/launcher/mod.rs b/src/modules/launcher/mod.rs
index 03599b1..bb48cd9 100644
--- a/src/modules/launcher/mod.rs
+++ b/src/modules/launcher/mod.rs
@@ -22,20 +22,38 @@ use tracing::{debug, error, trace};
pub struct LauncherModule {
/// List of app IDs (or classes) to always show regardless of open state,
/// in the order specified.
+ ///
+ /// **Default**: `null`
favorites: Option>,
+
/// Whether to show application names on the bar.
+ ///
+ /// **Default**: `false`
#[serde(default = "crate::config::default_false")]
show_names: bool,
+
/// Whether to show application icons on the bar.
+ ///
+ /// **Default**: `true`
#[serde(default = "crate::config::default_true")]
show_icons: bool,
+ /// Size in pixels to render icon at (image icons only).
+ ///
+ /// **Default**: `32`
#[serde(default = "default_icon_size")]
icon_size: i32,
+ /// Whether items should be added from right-to-left
+ /// instead of left-to-right.
+ ///
+ /// This includes favourites.
+ ///
+ /// **Default**: `false`
#[serde(default = "crate::config::default_false")]
reversed: bool,
+ /// See [common options](module-level-options#common-options).
#[serde(flatten)]
pub common: Option,
}
@@ -182,13 +200,22 @@ impl Module for LauncherModule {
}?;
}
ToplevelEvent::Update(info) => {
- if let Some(item) = lock!(items).get_mut(&info.app_id) {
+ // check if open, as updates can be sent as program closes
+ // if it's a focused favourite closing, it otherwise incorrectly re-focuses.
+ let is_open = if let Some(item) = lock!(items).get_mut(&info.app_id) {
item.set_window_focused(info.id, info.focused);
item.set_window_name(info.id, info.title.clone());
- }
- send_update(LauncherUpdate::Focus(info.app_id.clone(), info.focused))
- .await?;
+ item.open_state.is_open()
+ } else {
+ false
+ };
+
+ send_update(LauncherUpdate::Focus(
+ info.app_id.clone(),
+ is_open && info.focused,
+ ))
+ .await?;
send_update(LauncherUpdate::Title(
info.app_id.clone(),
info.id,
@@ -355,8 +382,7 @@ impl Module for LauncherModule {
button.set_open(true);
button.set_focused(win.open_state.is_focused());
- let mut menu_state = write_lock!(button.menu_state);
- menu_state.num_windows += 1;
+ write_lock!(button.menu_state).num_windows += 1;
}
}
LauncherUpdate::RemoveItem(app_id) => {
diff --git a/src/modules/music/config.rs b/src/modules/music/config.rs
index c772684..6d3de79 100644
--- a/src/modules/music/config.rs
+++ b/src/modules/music/config.rs
@@ -6,34 +6,50 @@ use std::path::PathBuf;
#[derive(Debug, Deserialize, Clone)]
pub struct Icons {
/// Icon to display when playing.
+ ///
+ /// **Default**: ``
#[serde(default = "default_icon_play")]
pub(crate) play: String,
/// Icon to display when paused.
+ ///
+ /// **Default**: ``
#[serde(default = "default_icon_pause")]
pub(crate) pause: String,
/// Icon to display for previous button.
+ ///
+ /// **Default**: ``
#[serde(default = "default_icon_prev")]
pub(crate) prev: String,
/// Icon to display for next button.
+ ///
+ /// **Default**: ``
#[serde(default = "default_icon_next")]
pub(crate) next: String,
- /// Icon to display under volume slider
+ /// Icon to display under volume slider.
+ ///
+ /// **Default**: ``
#[serde(default = "default_icon_volume")]
pub(crate) volume: String,
- /// Icon to display nex to track title
+ /// Icon to display nex to track title.
+ ///
+ /// **Default**: ``
#[serde(default = "default_icon_track")]
pub(crate) track: String,
- /// Icon to display nex to album name
+ /// Icon to display nex to album name.
+ ///
+ /// **Default**: ``
#[serde(default = "default_icon_album")]
pub(crate) album: String,
- /// Icon to display nex to artist name
+ /// Icon to display nex to artist name.
+ ///
+ /// **Default**: ``
#[serde(default = "default_icon_artist")]
pub(crate) artist: String,
}
@@ -73,33 +89,62 @@ pub struct MusicModule {
pub(crate) player_type: PlayerType,
/// Format of current song info to display on the bar.
+ ///
+ /// Info on formatting tokens [below](#formatting-tokens).
+ ///
+ /// **Default**: `{title} / {artist}`
#[serde(default = "default_format")]
pub(crate) format: String,
- /// Player state icons
+ /// Player state icons.
+ ///
+ /// See [icons](#icons).
#[serde(default)]
pub(crate) icons: Icons,
- // -- MPD --
- /// TCP or Unix socket address.
- #[serde(default = "default_socket")]
- pub(crate) host: String,
- /// Path to root of music directory.
- #[serde(default = "default_music_dir")]
- pub(crate) music_dir: PathBuf,
-
+ /// Whether to show the play/pause status icon
+ /// on the bar.
+ ///
+ /// **Default**: `true`
#[serde(default = "crate::config::default_true")]
pub(crate) show_status_icon: bool,
+ /// Size to render the icons at, in pixels (image icons only).
+ ///
+ /// **Default** `32`
#[serde(default = "default_icon_size")]
pub(crate) icon_size: i32,
+ /// Size to render the album art image at inside the popup, in pixels.
+ ///
+ /// **Default**: `128`
#[serde(default = "default_cover_image_size")]
pub(crate) cover_image_size: i32,
+ // -- MPD --
+ /// *[MPD Only]*
+ /// TCP or Unix socket address of the MPD server.
+ /// For TCP, this should include the port number.
+ ///
+ /// **Default**: `localhost:6600`
+ #[serde(default = "default_socket")]
+ pub(crate) host: String,
+
+ /// *[MPD Only]*
+ /// Path to root of the MPD server's music directory.
+ /// This is required for displaying album art.
+ ///
+ /// **Default**: `$HOME/Music`
+ #[serde(default = "default_music_dir")]
+ pub(crate) music_dir: PathBuf,
+
// -- Common --
+ /// See [truncate options](module-level-options#truncate-mode).
+ ///
+ /// **Default**: `null`
pub(crate) truncate: Option,
+ /// See [common options](module-level-options#common-options).
#[serde(flatten)]
pub common: Option,
}
diff --git a/src/modules/music/mod.rs b/src/modules/music/mod.rs
index 3e9604c..a8bb7b0 100644
--- a/src/modules/music/mod.rs
+++ b/src/modules/music/mod.rs
@@ -191,6 +191,7 @@ impl Module