Compare commits
76 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c8c84446d6 | ||
|
|
103a224355 | ||
|
|
18b36423e2 | ||
|
|
09a26d04bc | ||
|
|
96323801d9 | ||
|
|
de98cf3dae | ||
|
|
b3b96673b0 | ||
|
|
de3aa5d7b1 | ||
|
|
ac34c05d2e | ||
|
|
6f7af07cdd | ||
|
|
96d36c43d4 | ||
|
|
e11177fea3 | ||
|
|
cbba2bc614 | ||
|
|
658d040607 | ||
|
|
81c69f644e | ||
|
|
55327c6f98 | ||
|
|
fc2e93491f | ||
|
|
a10cbd6287 | ||
|
|
1991662bc4 | ||
|
|
b4f1c7ac2d | ||
|
|
1e799f7635 | ||
|
|
a9fe75b2f7 | ||
|
|
8ce7f8cb80 | ||
|
|
e709200242 | ||
|
|
f8a2c0f002 | ||
|
|
3b18e1d8a1 | ||
|
|
f37bc80292 | ||
|
|
e0bc05acb5 | ||
|
|
5a675153b4 | ||
|
|
090a6669b8 | ||
|
|
80655883ef | ||
|
|
ce015f8d19 | ||
|
|
5a09e46b28 | ||
|
|
0fce762eef | ||
|
|
0a6da15bb2 | ||
|
|
19d1414daa | ||
|
|
4456bb5d20 | ||
|
|
942d401472 | ||
|
|
952fef270e | ||
|
|
a5ecb363fd | ||
|
|
e036ff03c1 | ||
|
|
6c48d40e5b | ||
|
|
a0881fc909 | ||
|
|
d938298e7b | ||
|
|
e18fb0661d | ||
|
|
6950c79906 | ||
|
|
c4af6a8069 | ||
|
|
7ede33c9c1 | ||
|
|
7f0fdf2391 | ||
|
|
807a31bf92 | ||
|
|
e1b0c9b43d | ||
|
|
e6a70f7663 | ||
|
|
b4d7344200 | ||
|
|
a6b686624b | ||
|
|
b9740cba8f | ||
|
|
1f980ca783 | ||
|
|
48d6af0281 | ||
|
|
22b630a10b | ||
|
|
5877f773aa | ||
|
|
87ca399220 | ||
|
|
960da55a05 | ||
|
|
0e65f93a23 | ||
|
|
91ed1ee384 | ||
|
|
9012feee4f | ||
|
|
3b54d527b2 | ||
|
|
242b70ed39 | ||
|
|
bd144e87a8 | ||
|
|
3ccb54b49c | ||
|
|
ff315ff5db | ||
|
|
cdeafbdc72 | ||
|
|
13d39235ad | ||
|
|
327e345630 | ||
|
|
f82f897982 | ||
|
|
fe12251af8 | ||
|
|
d116a51083 | ||
|
|
31a57ae637 |
11
.github/dependabot.yml
vendored
Normal file
11
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "cargo" # See documentation for possible values
|
||||
directory: "/" # Location of package manifests
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
67
CHANGELOG.md
67
CHANGELOG.md
@@ -4,6 +4,70 @@ 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.12.0] - 2023-05-06
|
||||
### :boom: BREAKING CHANGES
|
||||
- due to [`dea6641`](https://github.com/JakeStanger/ironbar/commit/dea66415c2e11e34ba44d016aaa6cfb4ef7b9f9b) - module-level `name` and `class` options *(commit by [@JakeStanger](https://github.com/JakeStanger))*:
|
||||
|
||||
To allow for the `name` property, any widgets that were previously targeted by name should be targeted by class instead. This affects **all modules and all popups**, as well as several widgets inside modules. **This will break a lot of rules in your stylesheet**. To attempt to mitigate the damage, a migration script can be found [here](https://raw.githubusercontent.com/JakeStanger/ironbar/master/scripts/migrate-styles.sh) that should get you most of the way.
|
||||
|
||||
|
||||
### :sparkles: New Features
|
||||
- [`6c62286`](https://github.com/JakeStanger/ironbar/commit/6c622864b388548eaaa595f41993606cc151d585) - new label module *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`cac064f`](https://github.com/JakeStanger/ironbar/commit/cac064f4795e9f418cc0820f04944f91121c426a) - ability to configure popup gap *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`dfe1964`](https://github.com/JakeStanger/ironbar/commit/dfe1964abf9ca54beb38cad0bcf02bd9fb0b5c4d) - **custom**: slider widget *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`72b14b6`](https://github.com/JakeStanger/ironbar/commit/72b14b6c4ed3dccfe7b4b23b220ab0a87ec79aa2) - **custom**: progress bar widget. *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`a9d1233`](https://github.com/JakeStanger/ironbar/commit/a9d12339097cbe0fef1628460ef538319a048223) - **custom**: support dynamic strings on buttons *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`3d308ab`](https://github.com/JakeStanger/ironbar/commit/3d308ab572a39ada2501ddc1b822e50e1f8a8363) - **custom**: support dynamic string in image source *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`4a09b70`](https://github.com/JakeStanger/ironbar/commit/4a09b70854dad33bf890a3fe766f854d9195e786) - **custom**: support common options in widgets *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`83f44fd`](https://github.com/JakeStanger/ironbar/commit/83f44fd92fe74b45fcdfc242fb90fc932dd2b00b) - wrap modules in a revealer to support animated show/hide *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`1fa0c0e`](https://github.com/JakeStanger/ironbar/commit/1fa0c0e9774c302727d414f5aef999ab71a7acb8) - **custom**: support mouse wheel on slider *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`2da28b9`](https://github.com/JakeStanger/ironbar/commit/2da28b9bf5790adfc46c58b6f6d5fdd13cc17195) - ability to configure image icon sizes *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`033d0f7`](https://github.com/JakeStanger/ironbar/commit/033d0f7e6e450b3f2d62d9a75210d52611cf346d) - **custom**: option to toggle slider label *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`76e2b7b`](https://github.com/JakeStanger/ironbar/commit/76e2b7ba3e788f273039d74635881ddb96264258) - **music**: option to hide status icon on widget *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`ad3c171`](https://github.com/JakeStanger/ironbar/commit/ad3c171ecacaebf10408c2583ed7361ed029075e) - implement upower module *(commit by [@p00f](https://github.com/p00f))*
|
||||
- [`2a155b9`](https://github.com/JakeStanger/ironbar/commit/2a155b9aa8a3634908512d9b83680925962d478f) - **music**: add css selector for button contents *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`c1ea5fa`](https://github.com/JakeStanger/ironbar/commit/c1ea5fad7ec308895f0454b6de05a3177563626c) - **logging**: include line numbers *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`dea6641`](https://github.com/JakeStanger/ironbar/commit/dea66415c2e11e34ba44d016aaa6cfb4ef7b9f9b) - module-level `name` and `class` options *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
|
||||
### :bug: Bug Fixes
|
||||
- [`9109453`](https://github.com/JakeStanger/ironbar/commit/910945306c3261190a16300da2ed28efb945a6ac) - **dynamic string**: parser issue related to incorrectly matching braces *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`7355db7`](https://github.com/JakeStanger/ironbar/commit/7355db74ec9118c2cb46899534a3adac8d7165d9) - **image**: http provider not handling non-success codes *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`a87d8d5`](https://github.com/JakeStanger/ironbar/commit/a87d8d5c3071a1d8ab149deae17d261ae97368ea) - **tray**: icons sometimes not showing *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`15a9d8d`](https://github.com/JakeStanger/ironbar/commit/15a9d8d42c9319a7062e6a90086e0c1c3323f5d8) - **script**: parser incorrectly handling colons *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`68bc823`](https://github.com/JakeStanger/ironbar/commit/68bc8230ddf3352cc0de9f8cc770632744c22747) - **tray**: icons sometimes not showing *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`b038e76`](https://github.com/JakeStanger/ironbar/commit/b038e7671af4bfa41060adf724deb8c6151fac1f) - **tray**: icons sometimes not showing *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`7926bb0`](https://github.com/JakeStanger/ironbar/commit/7926bb07eb181edaf6da2f11a7dc00f8be2240eb) - **nix**: Fix `nix run` support *(commit by [@yavko](https://github.com/yavko))*
|
||||
- [`2c88c99`](https://github.com/JakeStanger/ironbar/commit/2c88c99cb605d312e2d76d620f502c7e7cd8866e) - **dynamic string**: crash when last segment is static and a single char *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`338f5a0`](https://github.com/JakeStanger/ironbar/commit/338f5a0e1b58dc9b52caee61d6a9748cf13153c5) - **nix**: Attempt to fix image blurriness *(commit by [@yavko](https://github.com/yavko))*
|
||||
- [`db0868a`](https://github.com/JakeStanger/ironbar/commit/db0868a3fc0734daa61067e377018c692599ebff) - **image**: not scaling icons for hidpi *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`14b6c1a`](https://github.com/JakeStanger/ironbar/commit/14b6c1a69f28836ed9e3b74eeb97a42ea60ffc27) - bars duplicate when starting second instance *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`98aaaa0`](https://github.com/JakeStanger/ironbar/commit/98aaaa0d1407681b3d790c933c4972b8122f8007) - fallback to default icon theme for notifier items *(commit by [@oknozor](https://github.com/oknozor))*
|
||||
- [`735f5cc`](https://github.com/JakeStanger/ironbar/commit/735f5cc9f1518c256785d42f3d21ed5c68b11711) - **launcher**: crash when focusing window *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`e1abadc`](https://github.com/JakeStanger/ironbar/commit/e1abadcf39a2d39078e75179a167e9277ee5e550) - **clipboard**: copying large images filling write pipe *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
|
||||
### :recycle: Refactors
|
||||
- [`2ab06f0`](https://github.com/JakeStanger/ironbar/commit/2ab06f044ec300628d6648852d395889b6752b76) - **custom**: split into enum with separate file per widget *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`3613aef`](https://github.com/JakeStanger/ironbar/commit/3613aef5c5a4051b5a44e33342c0eaaab3d4a690) - **custom**: reduce a lot of repeated code *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`c214f65`](https://github.com/JakeStanger/ironbar/commit/c214f65ecb86a0da6559025203701661924f65bb) - fix strict clippy warnings *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`27d11de`](https://github.com/JakeStanger/ironbar/commit/27d11de6616c410422d7abd579d09b3abc02f43a) - **config**: split common code into separate file *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`6fd69d6`](https://github.com/JakeStanger/ironbar/commit/6fd69d657c6224bc47c9b3cb5affcf74b63a6aa6) - move module creation code to module module *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`e63509a`](https://github.com/JakeStanger/ironbar/commit/e63509a3a7673ea41b4c937089a1cf6d2362fed3) - fix a few new clippy warnings *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`7f46cb4`](https://github.com/JakeStanger/ironbar/commit/7f46cb49767bd722be8d42999a9ba69887efcd40) - **wayland**: update to 0.30.0 *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`38da59c`](https://github.com/JakeStanger/ironbar/commit/38da59cd419fa0023d0ea0b435b11f0f9dea3f15) - fix a few pedantic clippy warnings *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
|
||||
### :memo: Documentation Changes
|
||||
- [`1b0287b`](https://github.com/JakeStanger/ironbar/commit/1b0287becc161e5addd8a8fed8bd9e8c437cd242) - update CHANGELOG.md for v0.11.0 [skip ci] *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`e928b30`](https://github.com/JakeStanger/ironbar/commit/e928b30f9927aa7c895c0d9855ee3ef09e559dc7) - **custom**: rewrite widget options to be clearer *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`138b5b3`](https://github.com/JakeStanger/ironbar/commit/138b5b39038a005d17069830a04b88d52730bed5) - **custom**: fix potential error in progress example *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`07df51c`](https://github.com/JakeStanger/ironbar/commit/07df51c2497977a31b2f5ef5bc7d051e0bd88564) - include readme in rust docs *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`dd7c9f3`](https://github.com/JakeStanger/ironbar/commit/dd7c9f30db6e4e1ede4d57255122b359636b8f58) - add transition module-level options *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`610c352`](https://github.com/JakeStanger/ironbar/commit/610c3528af98b8c6b02af7ce5c07190776522c3a) - add missing link to upower page *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`ea9f7ca`](https://github.com/JakeStanger/ironbar/commit/ea9f7caaf7a35eebd603ce2854672d5af2901018) - add missing `upower` feature flag *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`618b7ef`](https://github.com/JakeStanger/ironbar/commit/618b7ef5520de6f3796b66e42422a36c5a191ab0) - improve example css *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`139bc5d`](https://github.com/JakeStanger/ironbar/commit/139bc5d23f7f887b7b65d50adc21fa6679ea291e) - **compiling**: improve requirements list *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`cf32870`](https://github.com/JakeStanger/ironbar/commit/cf32870f8a380c305a436593950c3da524a2296f) - **compiling**: add ron feature flag *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
|
||||
|
||||
## [v0.11.0] - 2023-04-01
|
||||
### :boom: BREAKING CHANGES
|
||||
- due to [`ca4fe42`](https://github.com/JakeStanger/ironbar/commit/ca4fe422f22866748f2cb6239b31170a974d254b) - ability to set fixed length *(commit by [@JakeStanger](https://github.com/JakeStanger))*:
|
||||
@@ -271,4 +335,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
[v0.8.0]: https://github.com/JakeStanger/ironbar/compare/v0.7.0...v0.8.0
|
||||
[v0.9.0]: https://github.com/JakeStanger/ironbar/compare/v0.8.0...v0.9.0
|
||||
[v0.10.0]: https://github.com/JakeStanger/ironbar/compare/v0.9.0...v0.10.0
|
||||
[v0.11.0]: https://github.com/JakeStanger/ironbar/compare/v0.10.0...v0.11.0
|
||||
[v0.11.0]: https://github.com/JakeStanger/ironbar/compare/v0.10.0...v0.11.0
|
||||
[v0.12.0]: https://github.com/JakeStanger/ironbar/compare/v0.11.0...v0.12.0
|
||||
@@ -4,7 +4,8 @@ I welcome contributions of any kind with open arms. That said, please do stick t
|
||||
- Fix any `cargo clippy` warnings, using at least the default configuration.
|
||||
- Make sure your code is formatted using `cargo fmt`.
|
||||
- Keep any documentation up to date.
|
||||
- I won't enforce it, but preferably stick to [conventional commit](https://www.conventionalcommits.org/en/v1.0.0/) messages.
|
||||
- Please use [conventional commit](https://www.conventionalcommits.org/en/v1.0.0/) messages.
|
||||
This ensures your contributions are automatically included in the changelog.
|
||||
|
||||
|
||||
- For PRs:
|
||||
|
||||
842
Cargo.lock
generated
842
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
31
Cargo.toml
31
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ironbar"
|
||||
version = "0.12.0"
|
||||
version = "0.12.1"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
description = "Customisable GTK Layer Shell wlroots/sway bar"
|
||||
@@ -49,20 +49,20 @@ workspaces = ["futures-util"]
|
||||
# core
|
||||
gtk = "0.17.0"
|
||||
gtk-layer-shell = "0.6.0"
|
||||
glib = "0.17.5"
|
||||
tokio = { version = "1.21.2", features = ["macros", "rt-multi-thread", "time", "process", "sync", "io-util", "net"] }
|
||||
glib = "0.17.10"
|
||||
tokio = { version = "1.28.2", features = ["macros", "rt-multi-thread", "time", "process", "sync", "io-util", "net"] }
|
||||
tracing = "0.1.37"
|
||||
tracing-subscriber = { version = "0.3.16", features = ["env-filter"] }
|
||||
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
|
||||
tracing-error = "0.2.0"
|
||||
tracing-appender = "0.2.2"
|
||||
strip-ansi-escapes = "0.1.1"
|
||||
color-eyre = "0.6.2"
|
||||
serde = { version = "1.0.141", features = ["derive"] }
|
||||
serde = { version = "1.0.164", features = ["derive"] }
|
||||
indexmap = "1.9.1"
|
||||
dirs = "5.0.0"
|
||||
dirs = "5.0.1"
|
||||
walkdir = "2.3.2"
|
||||
notify = { version = "5.0.0", default-features = false }
|
||||
wayland-client = "0.30.0"
|
||||
notify = { version = "6.0.0", default-features = false }
|
||||
wayland-client = "0.30.2"
|
||||
wayland-protocols = { version = "0.30.0", features = ["unstable", "client"] }
|
||||
wayland-protocols-wlr = { version = "0.1.0", features = ["client"] }
|
||||
smithay-client-toolkit = { version = "0.17.0", default-features = false, features = ["calloop"] }
|
||||
@@ -73,20 +73,20 @@ async_once = "0.2.6"
|
||||
cfg-if = "1.0.0"
|
||||
|
||||
# http
|
||||
reqwest = { version = "0.11.14", optional = true }
|
||||
reqwest = { version = "0.11.18", optional = true }
|
||||
|
||||
# clipboard
|
||||
nix = { version = "0.26.2", optional = true, features = ["event"] }
|
||||
|
||||
# clock
|
||||
chrono = { version = "0.4.19", optional = true }
|
||||
chrono = { version = "0.4.26", optional = true }
|
||||
|
||||
# music
|
||||
mpd_client = { version = "1.0.0", optional = true }
|
||||
mpris = { version = "2.0.0", optional = true }
|
||||
|
||||
# sys_info
|
||||
sysinfo = { version = "0.28.4", optional = true }
|
||||
sysinfo = { version = "0.29.2", optional = true }
|
||||
|
||||
# tray
|
||||
stray = { version = "0.1.3", optional = true }
|
||||
@@ -94,12 +94,15 @@ stray = { version = "0.1.3", optional = true }
|
||||
# upower
|
||||
upower_dbus = { version = "0.3.2", optional = true }
|
||||
futures-lite = { version = "1.12.0", optional = true }
|
||||
zbus = { version = "3.11.0", optional = true }
|
||||
zbus = { version = "3.13.1", optional = true }
|
||||
|
||||
# workspaces
|
||||
swayipc-async = { version = "2.0.1", optional = true }
|
||||
hyprland = { version = "0.3.1", optional = true }
|
||||
hyprland = { version = "=0.3.1", optional = true }
|
||||
futures-util = { version = "0.3.21", optional = true }
|
||||
|
||||
# shared
|
||||
regex = { version = "1.6.0", default-features = false, features = ["std"], optional = true } # music, sys_info
|
||||
regex = { version = "1.8.4", default-features = false, features = ["std"], optional = true } # music, sys_info
|
||||
|
||||
[patch.crates-io]
|
||||
stray = { git = "https://github.com/jakestanger/stray", branch = "fix/connection-errors" }
|
||||
127
README.md
127
README.md
@@ -1,49 +1,86 @@
|
||||
# Ironbar
|
||||
<h1 align="center" >--- Ironbar ---</h1>
|
||||
|
||||
Ironbar is a customisable and feature-rich bar for wlroots compositors, written in Rust.
|
||||
It uses GTK3 and gtk-layer-shell.
|
||||
<div align="center">
|
||||
<a href="https://github.com/JakeStanger/ironbar/releases">
|
||||
<img src="https://img.shields.io/crates/v/ironbar?label=version&style=for-the-badge" alt="Current version" />
|
||||
</a>
|
||||
<a href="https://github.com/JakeStanger/ironbar/actions/workflows/build.yml">
|
||||
<img src="https://img.shields.io/github/actions/workflow/status/jakestanger/ironbar/build.yml?style=for-the-badge" alt="Build status" />
|
||||
</a>
|
||||
<a href="https://github.com/JakeStanger/ironbar/issues">
|
||||
<img src="https://img.shields.io/github/issues/jakestanger/ironbar?style=for-the-badge" alt="Open issues" />
|
||||
</a>
|
||||
<a href="https://github.com/JakeStanger/ironbar/blob/master/LICENSE">
|
||||
<img src="https://img.shields.io/github/license/jakestanger/ironbar?style=for-the-badge" alt="License" />
|
||||
</a>
|
||||
<a href="https://crates.io/crates/ironbar">
|
||||
<img src="https://img.shields.io/crates/d/ironbar?label=crates.io%20downloads&style=for-the-badge" alt="Crates.io downloads" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
The bar can be styled to your liking using CSS and hot-loads style changes.
|
||||
For information and examples on styling please see the [wiki](https://github.com/JakeStanger/ironbar/wiki).
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
A customisable and feature-rich GTK bar for wlroots compositors, written in Rust.
|
||||
|
||||
Ironbar is designed to support anything from a lightweight bar to a full desktop panel with ease.
|
||||
|
||||
---
|
||||
|
||||
## Getting Started
|
||||
|
||||
[Wiki](https://github.com/JakeStanger/ironbar/wiki)
|
||||
|
|
||||
[Configuration Guide](https://github.com/JakeStanger/ironbar/wiki/configuration-guide)
|
||||
|
|
||||
[Style Guide](https://github.com/JakeStanger/ironbar/wiki/styling-guide)
|
||||
|
||||
|
||||
---
|
||||
|
||||

|
||||
|
||||
✨ Looking for a starting point, or want to show off? Head to [Show and tell](https://github.com/JakeStanger/ironbar/discussions/categories/show-and-tell) ✨
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
- First-class support for Sway and Hyprland, but should (mostly) work on any wlroots compositor.
|
||||
- Fully themeable with CSS and hot-loaded styles.
|
||||
- Support for multiple configuration languages.
|
||||
- Popups used by widgets to show rich content and controls on click.
|
||||
- Out of the box widgets which can be used to create anything from a lightweight to a more traditional desktop experience.
|
||||
- Ability to create custom widgets (including popups), run scripts and inject dynamic content.
|
||||
- First-class support for Sway and Hyprland
|
||||
- Fully themeable with hot-loaded CSS
|
||||
- Popups to show rich content
|
||||
- Ability to create custom widgets, run scripts and embed dynamic content
|
||||
- Easy to configure anything from a single bar across all monitors, to multiple different unique bars per monitor
|
||||
- Support for multiple config languages
|
||||
|
||||
## Installation
|
||||
|
||||
### Cargo
|
||||
|
||||
[crate](https://crates.io/crates/ironbar)
|
||||
|
||||
Ensure you have the [build dependencies](https://github.com/JakeStanger/ironbar/wiki/compiling#Build-requirements) installed.
|
||||
|
||||
```sh
|
||||
cargo install ironbar
|
||||
```
|
||||
|
||||
[crate](https://crates.io/crates/ironbar)
|
||||
|
||||
### Arch Linux
|
||||
|
||||
[aur package](https://aur.archlinux.org/packages/ironbar-git)
|
||||
|
||||
```sh
|
||||
yay -S ironbar-git
|
||||
```
|
||||
|
||||
[aur package](https://aur.archlinux.org/packages/ironbar-git)
|
||||
|
||||
### Nix Flake
|
||||
|
||||
A flake is included with the repo which can be used with home-manager.
|
||||
A flake is included with the repo which can be used with Home Manager.
|
||||
|
||||
#### Example
|
||||
|
||||
Here is an example nix flake that uses Ironbar.
|
||||
<details>
|
||||
<summary>Example usage</summary>
|
||||
|
||||
```nix
|
||||
{
|
||||
@@ -80,13 +117,14 @@ Here is an example nix flake that uses Ironbar.
|
||||
}
|
||||
```
|
||||
|
||||
#### Binary Caching
|
||||
</details>
|
||||
|
||||
There is a Cachix cache available at `https://app.cachix.org/cache/jakestanger`
|
||||
in case you don't want to compile Ironbar.
|
||||
There is a Cachix cache available at `https://app.cachix.org/cache/jakestanger`.
|
||||
|
||||
### Source
|
||||
|
||||
[repo](https://github.com/jakestanger/ironbar)
|
||||
|
||||
Ensure you have the [build dependencies](https://github.com/JakeStanger/ironbar/wiki/compiling#Build-requirements) installed.
|
||||
|
||||
```sh
|
||||
@@ -100,53 +138,36 @@ install target/release/ironbar ~/.local/bin/ironbar
|
||||
By default, all features are enabled.
|
||||
See [here](https://github.com/JakeStanger/ironbar/wiki/compiling#features) for controlling which features are included.
|
||||
|
||||
[repo](https://github.com/jakestanger/ironbar)
|
||||
|
||||
## Running
|
||||
|
||||
All of the above installation methods provide a binary called `ironbar`.
|
||||
Once installed, you will need to create a config and optionally a stylesheet in `.config/ironbar`.
|
||||
See the [Configuration Guide](https://github.com/JakeStanger/ironbar/wiki/configuration-guide) and [Style Guide](https://github.com/JakeStanger/ironbar/wiki/styling-guide) for full details.
|
||||
|
||||
Ironbar can be launched using the `ironbar` binary.
|
||||
|
||||
Log verbosity can be changed using `IRONBAR_LOG` or `IRONBAR_FILE_LOG`. You can use any of `error`, `warn`, `info`, `debug` or `trace`.
|
||||
|
||||
You can set the `IRONBAR_LOG` or `IRONBAR_FILE_LOG` environment variables to
|
||||
`error`, `warn`, `info`, `debug` or `trace` to configure the log output level.
|
||||
These default to `IRONBAR_LOG=info` and `IRONBAR_FILE_LOG=error`.
|
||||
|
||||
File output can be found at `~/.local/share/ironbar/error.log`.
|
||||
|
||||
## Configuration
|
||||
## Status
|
||||
|
||||
Ironbar gives a lot of flexibility when configuring, including multiple file formats
|
||||
and options for scaling complexity: you can use a single config across all monitors,
|
||||
or configure different/multiple bars per monitor.
|
||||
Ironbar is an **alpha** project.
|
||||
It is unfinished and subject to constant breaking changes, and will continue that way until the foundation is rock solid.
|
||||
|
||||
A full configuration guide can be found [here](https://github.com/JakeStanger/ironbar/wiki/configuration-guide).
|
||||
If you would like to take the risk and help shape development, any bug reports, feature requests and discussion is welcome.
|
||||
|
||||
## Styling
|
||||
I use Ironbar on my daily driver, so development is active. Features aim to be stable and well documented before being merged.
|
||||
|
||||
To get started, create a stylesheet at `.config/ironbar/style.css`. Changes will be hot-reloaded every time you save the
|
||||
file.
|
||||
|
||||
A full styling guide can be found [here](https://github.com/JakeStanger/ironbar/wiki/styling-guide).
|
||||
|
||||
## Project Status
|
||||
|
||||
This project is in alpha, but should be usable.
|
||||
Everything that is implemented works and should be documented.
|
||||
Proper error handling is in place so things should either fail gracefully with detail, or not fail at all.
|
||||
|
||||
There is currently room for lots more modules, and lots more configuration options for the existing modules.
|
||||
The current configuration schema is not set in stone and breaking changes could come along at any point;
|
||||
until the project matures I am more interested in ease of use than backwards compatibility.
|
||||
|
||||
A few bugs do exist, and I am sure there are plenty more to be found.
|
||||
|
||||
The project will be *actively developed* as I am using it on my daily driver.
|
||||
Bugs will be fixed, features will be added, code will be refactored.
|
||||
|
||||
## Contribution Guidelines
|
||||
|
||||
Please check [here](https://github.com/JakeStanger/ironbar/blob/master/CONTRIBUTING.md).
|
||||
All are welcome, but I ask a few basic things to help make things easier. Please check [here](https://github.com/JakeStanger/ironbar/blob/master/CONTRIBUTING.md) for details.
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
- [Waybar](https://github.com/Alexays/Waybar) - A lot of the initial inspiration, and a pretty great bar.
|
||||
- [Rustbar](https://github.com/zeroeightysix/rustbar) - Served as a good demo for writing a basic GTK bar in Rust
|
||||
- [Smithay Client Toolkit](https://github.com/Smithay/client-toolkit) - Essential in being able to communicate to Wayland
|
||||
- [gtk-layer-shell](https://github.com/wmww/gtk-layer-shell) - Ironbar and many other projects would be impossible without this
|
||||
@@ -86,6 +86,9 @@ end:
|
||||
|--------------------------------------|------------------------------------------------------|
|
||||
| `.clipboard` | Clipboard widget. |
|
||||
| `.clipboard .btn` | Clipboard widget button. |
|
||||
| `.clipboard .btn .icon` | Clipboard widget button icon (any type). |
|
||||
| `.clipboard .btn .text-icon` | Clipboard widget button icon (textual only). |
|
||||
| `.clipboard .btn .image` | Clipboard widget button icon (image only). |
|
||||
| `.popup-clipboard` | Clipboard popup box. |
|
||||
| `.popup-clipboard .item` | Clipboard row item inside the popup. |
|
||||
| `.popup-clipboard .item .btn` | Clipboard row item radio button. |
|
||||
|
||||
@@ -30,8 +30,8 @@ in MPRIS mode, the widget will listen to all players and automatically detect/di
|
||||
| `show_status_icon` | `boolean` | `true` | Whether to show the play/pause icon on the widget. |
|
||||
| `icon_size` | `integer` | `32` | Size to render icon at (image icons only). |
|
||||
| `cover_image_size` | `integer` | `128` | Size to render album art image at inside popup. |
|
||||
| `host` | `string/image` | `localhost:6600` | [MPD Only] TCP or Unix socket for the MPD server. |
|
||||
| `music_dir` | `string/image` | `$HOME/Music` | [MPD Only] Path to MPD server's music directory on disc. Required for album art. |
|
||||
| `host` | `string` | `localhost:6600` | [MPD Only] TCP or Unix socket for the MPD server. |
|
||||
| `music_dir` | `string` | `$HOME/Music` | [MPD Only] Path to MPD server's music directory on disc. Required for album art. |
|
||||
|
||||
See [here](images) for information on images.
|
||||
|
||||
@@ -133,28 +133,40 @@ and will be replaced with values from the currently playing track:
|
||||
|
||||
## Styling
|
||||
|
||||
| Selector | Description |
|
||||
|-------------------------------------|------------------------------------------|
|
||||
| `.music` | Tray widget button |
|
||||
| `.music .contents` | Tray widget button contents box |
|
||||
| `.popup-music` | Popup box |
|
||||
| `.popup-music .album-art` | Album art image inside popup box |
|
||||
| `.popup-music .title` | Track title container inside popup box |
|
||||
| `.popup-music .title .icon` | Track title icon label inside popup box |
|
||||
| `.popup-music .title .label` | Track title label inside popup box |
|
||||
| `.popup-music .album` | Track album container inside popup box |
|
||||
| `.popup-music .album .icon` | Track album icon label inside popup box |
|
||||
| `.popup-music .album .label` | Track album label inside popup box |
|
||||
| `.popup-music .artist` | Track artist container inside popup box |
|
||||
| `.popup-music .artist .icon` | Track artist icon label inside popup box |
|
||||
| `.popup-music .artist .label` | Track artist label inside popup box |
|
||||
| `.popup-music .controls` | Controls container inside popup box |
|
||||
| `.popup-music .controls .btn-prev` | Previous button inside popup box |
|
||||
| `.popup-music .controls .btn-play` | Play button inside popup box |
|
||||
| `.popup-music .controls .btn-pause` | Pause button inside popup box |
|
||||
| `.popup-music .controls .btn-next` | Next button inside popup box |
|
||||
| `.popup-music .volume` | Volume container inside popup box |
|
||||
| `.popup-music .volume .slider` | Volume slider popup box |
|
||||
| `.popup-music .volume .icon` | Volume icon label inside popup box |
|
||||
| Selector | Description |
|
||||
|---------------------------------------------|-------------------------------------------------------|
|
||||
| `.music` | Tray widget button |
|
||||
| `.music .contents` | Tray widget button contents box |
|
||||
| `.music .contents .icon` | Tray widget button icon (any type) |
|
||||
| `.music .contents .text-icon` | Tray widget button icon (textual only) |
|
||||
| `.music .contents .image` | Tray widget button icon (image only) |
|
||||
| `.popup-music` | Popup box |
|
||||
| `.popup-music .album-art` | Album art image inside popup box |
|
||||
| `.popup-music .title` | Track title container inside popup box |
|
||||
| `.popup-music .title .icon-box` | Track title icon container inside popup box |
|
||||
| `.popup-music .title .icon-box .icon` | Track title icon inside its container (any type) |
|
||||
| `.popup-music .title .icon-box .text-icon` | Track title icon inside its container (textual only) |
|
||||
| `.popup-music .title .icon-box .image` | Track title icon inside its container (image only) |
|
||||
| `.popup-music .title .label` | Track title label inside popup box |
|
||||
| `.popup-music .album` | Track album container inside popup box |
|
||||
| `.popup-music .album .icon-box` | Track album icon container inside popup box |
|
||||
| `.popup-music .album .icon-box .icon` | Track album icon inside its container (any type) |
|
||||
| `.popup-music .album .icon-box .text-icon` | Track album icon inside its container (textual only) |
|
||||
| `.popup-music .album .icon-box .image` | Track album icon inside its container (image only) |
|
||||
| `.popup-music .album .label` | Track album label inside popup box |
|
||||
| `.popup-music .artist` | Track artist container inside popup box |
|
||||
| `.popup-music .artist .icon-box` | Track artist icon container inside popup box |
|
||||
| `.popup-music .artist .icon-box .icon` | Track artist icon inside its container (any type) |
|
||||
| `.popup-music .artist .icon-box .text-icon` | Track artist icon inside its container (textual only) |
|
||||
| `.popup-music .artist .icon-box .image` | Track artist icon inside its container (image only) |
|
||||
| `.popup-music .artist .label` | Track artist label inside popup box |
|
||||
| `.popup-music .controls` | Controls container inside popup box |
|
||||
| `.popup-music .controls .btn-prev` | Previous button inside popup box |
|
||||
| `.popup-music .controls .btn-play` | Play button inside popup box |
|
||||
| `.popup-music .controls .btn-pause` | Pause button inside popup box |
|
||||
| `.popup-music .controls .btn-next` | Next button inside popup box |
|
||||
| `.popup-music .volume` | Volume container inside popup box |
|
||||
| `.popup-music .volume .slider` | Volume slider popup box |
|
||||
| `.popup-music .volume .icon` | Volume icon label inside popup box |
|
||||
|
||||
For more information on styling, please see the [styling guide](styling-guide).
|
||||
@@ -168,6 +168,8 @@ The following tokens can be used in the `format` configuration option:
|
||||
| `{load_average:15}` | 15-minute load average. |
|
||||
| `{uptime}` | System uptime formatted as `HH:mm`. |
|
||||
|
||||
For Intel CPUs, you can typically use `coretemp-Package-id-0` for the temperature sensor. For AMD, you can use `k10temp_Tccd1`.
|
||||
|
||||
## Styling
|
||||
|
||||
| Selector | Description |
|
||||
@@ -175,4 +177,4 @@ The following tokens can be used in the `format` configuration option:
|
||||
| `.sysinfo` | Sysinfo widget box |
|
||||
| `.sysinfo .item` | Individual information label |
|
||||
|
||||
For more information on styling, please see the [styling guide](styling-guide).
|
||||
For more information on styling, please see the [styling guide](styling-guide).
|
||||
|
||||
@@ -70,13 +70,14 @@ end:
|
||||
|
||||
## Styling
|
||||
|
||||
| Selector | Description |
|
||||
|---------------------------------|-----------------------------|
|
||||
| `.upower` | Upower widget container. |
|
||||
| `.upower .icon` | Upower widget battery icon. |
|
||||
| `.upower .button` | Upower widget button. |
|
||||
| `.upower .button .label` | Upower widget button label. |
|
||||
| `.popup-upower` | Upower popup box. |
|
||||
| `.popup-upower .upower-details` | Label inside the popup. |
|
||||
| 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. |
|
||||
| `.popup-upower` | Upower popup box. |
|
||||
| `.popup-upower .upower-details` | Label inside the popup. |
|
||||
|
||||
For more information on styling, please see the [styling guide](styling-guide).
|
||||
For more information on styling, please see the [styling guide](styling-guide).
|
||||
|
||||
@@ -89,10 +89,13 @@ end:
|
||||
|
||||
## Styling
|
||||
|
||||
| Selector | Description |
|
||||
|-----------------------------|--------------------------------------|
|
||||
| `.workspaces` | Workspaces widget box |
|
||||
| `.workspaces .item` | Workspace button |
|
||||
| `.workspaces .item.focused` | Workspace button (workspace focused) |
|
||||
| Selector | Description |
|
||||
|--------------------------------|--------------------------------------|
|
||||
| `.workspaces` | Workspaces widget box |
|
||||
| `.workspaces .item` | Workspace button |
|
||||
| `.workspaces .item.focused` | Workspace button (workspace focused) |
|
||||
| `.workspaces .item .icon` | Workspace button icon (any type) |
|
||||
| `.workspaces .item .text-icon` | Workspace button icon (textual only) |
|
||||
| `.workspaces .item .image` | Workspace button icon (image only) |
|
||||
|
||||
For more information on styling, please see the [styling guide](styling-guide).
|
||||
@@ -15,7 +15,7 @@ let {
|
||||
|
||||
$launcher = {
|
||||
type = "launcher"
|
||||
favorites = ["firefox" "discord" "Steam"]
|
||||
favorites = ["firefox" "discord" "steam"]
|
||||
show_names = false
|
||||
show_icons = true
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@
|
||||
"favorites": [
|
||||
"firefox",
|
||||
"discord",
|
||||
"Steam"
|
||||
"steam"
|
||||
],
|
||||
"show_icons": true,
|
||||
"show_names": false,
|
||||
|
||||
@@ -113,7 +113,7 @@ type = 'launcher'
|
||||
favorites = [
|
||||
'firefox',
|
||||
'discord',
|
||||
'Steam',
|
||||
'steam',
|
||||
]
|
||||
|
||||
[[start]]
|
||||
|
||||
@@ -78,7 +78,7 @@ start:
|
||||
- favorites:
|
||||
- firefox
|
||||
- discord
|
||||
- Steam
|
||||
- steam
|
||||
show_icons: true
|
||||
show_names: false
|
||||
type: launcher
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
box, menubar, button {
|
||||
background-color: @color_bg;
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
button, label {
|
||||
|
||||
12
flake.lock
generated
12
flake.lock
generated
@@ -20,11 +20,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1682786779,
|
||||
"narHash": "sha256-m7QFzPS/CE8hbkbIVK4UStihAQMtczr0vSpOgETOM1g=",
|
||||
"lastModified": 1686960236,
|
||||
"narHash": "sha256-AYCC9rXNLpUWzD9hm+askOfpliLEC9kwAo7ITJc4HIw=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "08e4dc3a907a6dfec8bb3bbf1540d8abbffea22b",
|
||||
"rev": "04af42f3b31dba0ef742d254456dc4c14eedac86",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -48,11 +48,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1682821181,
|
||||
"narHash": "sha256-7MYRqO9Ge46sULbQwJbcH/IMDNBdxCGUO9w7bEOc3CI=",
|
||||
"lastModified": 1686968542,
|
||||
"narHash": "sha256-Gjlj7UeHqMFRAYyefeoLnSjLo8V+0XheIamojNEyTbE=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "1be440e9119e69b68151cd9c84876ff3063a2e45",
|
||||
"rev": "01d84cd842e48e89be67e4c2d9dc46aa7709adc5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -32,8 +32,11 @@ rustPlatform.buildRustPackage rec {
|
||||
then false
|
||||
else true;
|
||||
buildFeatures = features;
|
||||
cargoDeps = rustPlatform.importCargoLock {lockFile = ../Cargo.lock;};
|
||||
cargoDeps = rustPlatform.importCargoLock {
|
||||
lockFile = ../Cargo.lock;
|
||||
};
|
||||
cargoLock.lockFile = ../Cargo.lock;
|
||||
cargoLock.outputHashes."stray-0.1.3" = "sha256-7mvsWZFmPWti9AiX67h6ZlWiVVRZRWIxq3pVaviOUtc=";
|
||||
nativeBuildInputs = [pkg-config wrapGAppsHook gobject-introspection];
|
||||
buildInputs = [gtk3 gdk-pixbuf glib gtk-layer-shell glib-networking shared-mime-info gnome.adwaita-icon-theme hicolor-icon-theme gsettings-desktop-schemas libxkbcommon openssl];
|
||||
propagatedBuildInputs = [
|
||||
|
||||
19
src/bar.rs
19
src/bar.rs
@@ -3,6 +3,7 @@ use crate::modules::{
|
||||
create_module, set_widget_identifiers, wrap_widget, ModuleInfo, ModuleLocation,
|
||||
};
|
||||
use crate::popup::Popup;
|
||||
use crate::unique_id::get_unique_usize;
|
||||
use crate::Config;
|
||||
use color_eyre::Result;
|
||||
use gtk::gdk::Monitor;
|
||||
@@ -163,19 +164,23 @@ fn load_modules(
|
||||
};
|
||||
}
|
||||
|
||||
// popup ignores module location so can bodge this for now
|
||||
let popup = Popup::new(&info!(ModuleLocation::Left), config.popup_gap);
|
||||
let popup = Arc::new(RwLock::new(popup));
|
||||
|
||||
if let Some(modules) = config.start {
|
||||
let info = info!(ModuleLocation::Left);
|
||||
add_modules(left, modules, &info, config.popup_gap)?;
|
||||
add_modules(left, modules, &info, &popup)?;
|
||||
}
|
||||
|
||||
if let Some(modules) = config.center {
|
||||
let info = info!(ModuleLocation::Center);
|
||||
add_modules(center, modules, &info, config.popup_gap)?;
|
||||
add_modules(center, modules, &info, &popup)?;
|
||||
}
|
||||
|
||||
if let Some(modules) = config.end {
|
||||
let info = info!(ModuleLocation::Right);
|
||||
add_modules(right, modules, &info, config.popup_gap)?;
|
||||
add_modules(right, modules, &info, &popup)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -187,11 +192,8 @@ fn add_modules(
|
||||
content: >k::Box,
|
||||
modules: Vec<ModuleConfig>,
|
||||
info: &ModuleInfo,
|
||||
popup_gap: i32,
|
||||
popup: &Arc<RwLock<Popup>>,
|
||||
) -> Result<()> {
|
||||
let popup = Popup::new(info, popup_gap);
|
||||
let popup = Arc::new(RwLock::new(popup));
|
||||
|
||||
let orientation = info.bar_position.get_orientation();
|
||||
|
||||
macro_rules! add_module {
|
||||
@@ -205,7 +207,8 @@ fn add_modules(
|
||||
}};
|
||||
}
|
||||
|
||||
for (id, config) in modules.into_iter().enumerate() {
|
||||
for config in modules.into_iter() {
|
||||
let id = get_unique_usize();
|
||||
match config {
|
||||
#[cfg(feature = "clipboard")]
|
||||
ModuleConfig::Clipboard(mut module) => add_module!(module, id),
|
||||
|
||||
@@ -231,6 +231,16 @@ impl MusicClient for Client {
|
||||
if let Err(err) = Self::send_update(&player, &self.tx) {
|
||||
error!("{err:?}");
|
||||
}
|
||||
} else {
|
||||
let status = Status {
|
||||
playlist_position: 0,
|
||||
playlist_length: 0,
|
||||
state: PlayerState::Stopped,
|
||||
elapsed: None,
|
||||
duration: None,
|
||||
volume_percent: 0,
|
||||
};
|
||||
send!(self.tx, PlayerUpdate::Update(Box::new(None), status));
|
||||
}
|
||||
|
||||
rx
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::unique_id::get_unique_usize;
|
||||
use crate::{lock, send};
|
||||
use async_once::AsyncOnce;
|
||||
use color_eyre::Report;
|
||||
@@ -24,11 +25,13 @@ pub struct TrayEventReceiver {
|
||||
|
||||
impl TrayEventReceiver {
|
||||
async fn new() -> stray::error::Result<Self> {
|
||||
let id = format!("ironbar-{}", get_unique_usize());
|
||||
|
||||
let (tx, rx) = mpsc::channel(16);
|
||||
let (b_tx, b_rx) = broadcast::channel(16);
|
||||
|
||||
let tray = StatusNotifierWatcher::new(rx).await?;
|
||||
let mut host = tray.create_notifier_host("ironbar").await?;
|
||||
let mut host = tray.create_notifier_host(&id).await?;
|
||||
|
||||
let tray = Arc::new(Mutex::new(BTreeMap::new()));
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ use self::device::{DataControlDeviceDataExt, DataControlDeviceHandler};
|
||||
use self::offer::{DataControlDeviceOffer, DataControlOfferHandler, SelectionOffer};
|
||||
use self::source::DataControlSourceHandler;
|
||||
use crate::clients::wayland::Environment;
|
||||
use crate::unique_id::get_unique_usize;
|
||||
use crate::{lock, send};
|
||||
use device::DataControlDevice;
|
||||
use glib::Bytes;
|
||||
@@ -19,21 +20,14 @@ use std::fmt::{Debug, Formatter};
|
||||
use std::fs::File;
|
||||
use std::io::{ErrorKind, Read, Write};
|
||||
use std::os::fd::{AsRawFd, OwnedFd, RawFd};
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::{fs, io};
|
||||
use tracing::{debug, error, trace};
|
||||
use wayland_client::{Connection, QueueHandle};
|
||||
use wayland_protocols_wlr::data_control::v1::client::zwlr_data_control_source_v1::ZwlrDataControlSourceV1;
|
||||
|
||||
static COUNTER: AtomicUsize = AtomicUsize::new(1);
|
||||
|
||||
const INTERNAL_MIME_TYPE: &str = "x-ironbar-internal";
|
||||
|
||||
fn get_id() -> usize {
|
||||
COUNTER.fetch_add(1, Ordering::Relaxed)
|
||||
}
|
||||
|
||||
pub struct SelectionOfferItem {
|
||||
offer: SelectionOffer,
|
||||
token: Option<RegistrationToken>,
|
||||
@@ -151,7 +145,7 @@ impl Environment {
|
||||
};
|
||||
|
||||
Ok(ClipboardItem {
|
||||
id: get_id(),
|
||||
id: get_unique_usize(),
|
||||
value,
|
||||
mime_type: mime_type.value.clone(),
|
||||
})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use super::manager::ToplevelManagerState;
|
||||
use crate::lock;
|
||||
use crate::unique_id::get_unique_usize;
|
||||
use std::collections::HashSet;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use tracing::trace;
|
||||
use wayland_client::protocol::wl_output::WlOutput;
|
||||
@@ -11,12 +11,6 @@ use wayland_protocols_wlr::foreign_toplevel::v1::client::zwlr_foreign_toplevel_h
|
||||
Event, ZwlrForeignToplevelHandleV1,
|
||||
};
|
||||
|
||||
static COUNTER: AtomicUsize = AtomicUsize::new(1);
|
||||
|
||||
fn get_id() -> usize {
|
||||
COUNTER.fetch_add(1, Ordering::Relaxed)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ToplevelHandle {
|
||||
pub handle: ZwlrForeignToplevelHandleV1,
|
||||
@@ -74,7 +68,7 @@ pub struct ToplevelInfo {
|
||||
impl Default for ToplevelInfo {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
id: get_id(),
|
||||
id: get_unique_usize(),
|
||||
app_id: String::new(),
|
||||
title: String::new(),
|
||||
fullscreen: false,
|
||||
|
||||
@@ -2,7 +2,6 @@ use super::ImageProvider;
|
||||
use crate::gtk_helpers::add_class;
|
||||
use gtk::prelude::*;
|
||||
use gtk::{Button, IconTheme, Image, Label, Orientation};
|
||||
use tracing::error;
|
||||
|
||||
#[cfg(any(feature = "music", feature = "workspaces", feature = "clipboard"))]
|
||||
pub fn new_icon_button(input: &str, icon_theme: &IconTheme, size: i32) -> Button {
|
||||
@@ -11,16 +10,16 @@ pub fn new_icon_button(input: &str, icon_theme: &IconTheme, size: i32) -> Button
|
||||
if ImageProvider::is_definitely_image_input(input) {
|
||||
let image = Image::new();
|
||||
add_class(&image, "image");
|
||||
add_class(&image, "icon");
|
||||
|
||||
match ImageProvider::parse(input, icon_theme, size)
|
||||
.and_then(|provider| provider.load_into_image(image.clone()))
|
||||
.map(|provider| provider.load_into_image(image.clone()))
|
||||
{
|
||||
Ok(_) => {
|
||||
Some(_) => {
|
||||
button.set_image(Some(&image));
|
||||
button.set_always_show_image(true);
|
||||
}
|
||||
Err(err) => {
|
||||
error!("{err:?}");
|
||||
None => {
|
||||
button.set_label(input);
|
||||
}
|
||||
}
|
||||
@@ -37,18 +36,17 @@ pub fn new_icon_label(input: &str, icon_theme: &IconTheme, size: i32) -> gtk::Bo
|
||||
|
||||
if ImageProvider::is_definitely_image_input(input) {
|
||||
let image = Image::new();
|
||||
add_class(&image, "icon");
|
||||
add_class(&image, "image");
|
||||
|
||||
container.add(&image);
|
||||
|
||||
if let Err(err) = ImageProvider::parse(input, icon_theme, size)
|
||||
.and_then(|provider| provider.load_into_image(image))
|
||||
{
|
||||
error!("{err:?}");
|
||||
}
|
||||
ImageProvider::parse(input, icon_theme, size)
|
||||
.map(|provider| provider.load_into_image(image));
|
||||
} else {
|
||||
let label = Label::new(Some(input));
|
||||
add_class(&label, "label");
|
||||
add_class(&label, "icon");
|
||||
add_class(&label, "text-icon");
|
||||
|
||||
container.add(&label);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
use crate::desktop_file::get_desktop_icon_name;
|
||||
use cfg_if::cfg_if;
|
||||
use color_eyre::{Help, Report, Result};
|
||||
use gtk::cairo::Surface;
|
||||
use gtk::gdk::ffi::gdk_cairo_surface_create_from_pixbuf;
|
||||
use gtk::gdk_pixbuf::Pixbuf;
|
||||
use gtk::prelude::*;
|
||||
use gtk::{IconLookupFlags, IconTheme};
|
||||
use std::path::{Path, PathBuf};
|
||||
use tracing::warn;
|
||||
|
||||
cfg_if!(
|
||||
if #[cfg(feature = "http")] {
|
||||
@@ -38,9 +41,9 @@ impl<'a> ImageProvider<'a> {
|
||||
///
|
||||
/// Note this checks that icons exist in theme, or files exist on disk
|
||||
/// but no other check is performed.
|
||||
pub fn parse(input: &str, theme: &'a IconTheme, size: i32) -> Result<Self> {
|
||||
pub fn parse(input: &str, theme: &'a IconTheme, size: i32) -> Option<Self> {
|
||||
let location = Self::get_location(input, theme, size)?;
|
||||
Ok(Self { location, size })
|
||||
Some(Self { location, size })
|
||||
}
|
||||
|
||||
/// Returns true if the input starts with a prefix
|
||||
@@ -54,44 +57,56 @@ impl<'a> ImageProvider<'a> {
|
||||
|| input.starts_with("https://")
|
||||
}
|
||||
|
||||
fn get_location(input: &str, theme: &'a IconTheme, size: i32) -> Result<ImageLocation<'a>> {
|
||||
fn get_location(input: &str, theme: &'a IconTheme, size: i32) -> Option<ImageLocation<'a>> {
|
||||
let (input_type, input_name) = input
|
||||
.split_once(':')
|
||||
.map_or((None, input), |(t, n)| (Some(t), n));
|
||||
|
||||
match input_type {
|
||||
Some(input_type) if input_type == "icon" => Ok(ImageLocation::Icon {
|
||||
Some(input_type) if input_type == "icon" => Some(ImageLocation::Icon {
|
||||
name: input_name.to_string(),
|
||||
theme,
|
||||
}),
|
||||
Some(input_type) if input_type == "file" => Ok(ImageLocation::Local(PathBuf::from(
|
||||
Some(input_type) if input_type == "file" => Some(ImageLocation::Local(PathBuf::from(
|
||||
input_name[2..].to_string(),
|
||||
))),
|
||||
#[cfg(feature = "http")]
|
||||
Some(input_type) if input_type == "http" || input_type == "https" => {
|
||||
Ok(ImageLocation::Remote(input.parse()?))
|
||||
input.parse().ok().map(ImageLocation::Remote)
|
||||
}
|
||||
None if input.starts_with("steam_app_") => Ok(ImageLocation::Steam(
|
||||
None if input.starts_with("steam_app_") => Some(ImageLocation::Steam(
|
||||
input_name.chars().skip("steam_app_".len()).collect(),
|
||||
)),
|
||||
None if theme
|
||||
.lookup_icon(input, size, IconLookupFlags::empty())
|
||||
.is_some() =>
|
||||
{
|
||||
Ok(ImageLocation::Icon {
|
||||
Some(ImageLocation::Icon {
|
||||
name: input_name.to_string(),
|
||||
theme,
|
||||
})
|
||||
}
|
||||
Some(input_type) => Err(Report::msg(format!("Unsupported image type: {input_type}"))
|
||||
.note("You may need to recompile with support if available")),
|
||||
None if PathBuf::from(input_name).is_file() => {
|
||||
Ok(ImageLocation::Local(PathBuf::from(input_name)))
|
||||
Some(input_type) => {
|
||||
warn!(
|
||||
"{:?}",
|
||||
Report::msg(format!("Unsupported image type: {input_type}"))
|
||||
.note("You may need to recompile with support if available")
|
||||
);
|
||||
None
|
||||
}
|
||||
None if PathBuf::from(input_name).is_file() => {
|
||||
Some(ImageLocation::Local(PathBuf::from(input_name)))
|
||||
}
|
||||
None => {
|
||||
if let Some(location) = get_desktop_icon_name(input_name)
|
||||
.map(|input| Self::get_location(&input, theme, size))
|
||||
{
|
||||
location
|
||||
} else {
|
||||
warn!("Failed to find image: {input}");
|
||||
None
|
||||
}
|
||||
}
|
||||
None => get_desktop_icon_name(input_name).map_or_else(
|
||||
|| Err(Report::msg(format!("Unknown image type: '{input}'"))),
|
||||
|input| Self::get_location(&input, theme, size),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,17 +130,24 @@ impl<'a> ImageProvider<'a> {
|
||||
let size = self.size;
|
||||
rx.attach(None, move |bytes| {
|
||||
let stream = MemoryInputStream::from_bytes(&bytes);
|
||||
|
||||
let scale = image.scale_factor();
|
||||
let scaled_size = size * scale;
|
||||
|
||||
let pixbuf = Pixbuf::from_stream_at_scale(
|
||||
&stream,
|
||||
size,
|
||||
size,
|
||||
scaled_size,
|
||||
scaled_size,
|
||||
true,
|
||||
Some(&Cancellable::new()),
|
||||
);
|
||||
|
||||
match pixbuf {
|
||||
Ok(pixbuf) => image.set_pixbuf(Some(&pixbuf)),
|
||||
// Different error types makes this a bit awkward
|
||||
match pixbuf.map(|pixbuf| Self::create_and_load_surface(&pixbuf, &image, scale))
|
||||
{
|
||||
Ok(Err(err)) => error!("{err:?}"),
|
||||
Err(err) => error!("{err:?}"),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Continue(false)
|
||||
@@ -141,18 +163,35 @@ impl<'a> ImageProvider<'a> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Attempts to synchronously fetch an image from location
|
||||
/// and load into into the image.
|
||||
fn load_into_image_sync(&self, image: >k::Image) -> Result<()> {
|
||||
let scale = image.scale_factor();
|
||||
|
||||
let pixbuf = match &self.location {
|
||||
ImageLocation::Icon { name, theme } => {
|
||||
self.get_from_icon(name, theme, image.scale_factor())
|
||||
}
|
||||
ImageLocation::Local(path) => self.get_from_file(path),
|
||||
ImageLocation::Steam(steam_id) => self.get_from_steam_id(steam_id),
|
||||
ImageLocation::Icon { name, theme } => self.get_from_icon(name, theme, scale),
|
||||
ImageLocation::Local(path) => self.get_from_file(path, scale),
|
||||
ImageLocation::Steam(steam_id) => self.get_from_steam_id(steam_id, scale),
|
||||
#[cfg(feature = "http")]
|
||||
_ => unreachable!(), // handled above
|
||||
}?;
|
||||
|
||||
image.set_pixbuf(Some(&pixbuf));
|
||||
Self::create_and_load_surface(&pixbuf, image, scale)
|
||||
}
|
||||
|
||||
/// Attempts to create a Cairo surface from the provided `Pixbuf`,
|
||||
/// using the provided scaling factor.
|
||||
/// The surface is then loaded into the provided image.
|
||||
///
|
||||
/// This is necessary for HiDPI since `Pixbuf`s are always treated as scale factor 1.
|
||||
fn create_and_load_surface(pixbuf: &Pixbuf, image: >k::Image, scale: i32) -> Result<()> {
|
||||
let surface = unsafe {
|
||||
let ptr =
|
||||
gdk_cairo_surface_create_from_pixbuf(pixbuf.as_ptr(), scale, std::ptr::null_mut());
|
||||
Surface::from_raw_full(ptr)
|
||||
}?;
|
||||
|
||||
image.set_from_surface(Some(&surface));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -161,7 +200,7 @@ impl<'a> ImageProvider<'a> {
|
||||
fn get_from_icon(&self, name: &str, theme: &IconTheme, scale: i32) -> Result<Pixbuf> {
|
||||
let pixbuf =
|
||||
match theme.lookup_icon_for_scale(name, self.size, scale, IconLookupFlags::empty()) {
|
||||
Some(_) => theme.load_icon(name, self.size, IconLookupFlags::FORCE_SIZE),
|
||||
Some(_) => theme.load_icon(name, self.size * scale, IconLookupFlags::FORCE_SIZE),
|
||||
None => Ok(None),
|
||||
}?;
|
||||
|
||||
@@ -172,14 +211,15 @@ impl<'a> ImageProvider<'a> {
|
||||
}
|
||||
|
||||
/// Attempts to get a `Pixbuf` from a local file.
|
||||
fn get_from_file(&self, path: &Path) -> Result<Pixbuf> {
|
||||
let pixbuf = Pixbuf::from_file_at_scale(path, self.size, self.size, true)?;
|
||||
fn get_from_file(&self, path: &Path, scale: i32) -> Result<Pixbuf> {
|
||||
let scaled_size = self.size * scale;
|
||||
let pixbuf = Pixbuf::from_file_at_scale(path, scaled_size, scaled_size, true)?;
|
||||
Ok(pixbuf)
|
||||
}
|
||||
|
||||
/// Attempts to get a `Pixbuf` from a local file,
|
||||
/// using the Steam game ID to look it up.
|
||||
fn get_from_steam_id(&self, steam_id: &str) -> Result<Pixbuf> {
|
||||
fn get_from_steam_id(&self, steam_id: &str, scale: i32) -> Result<Pixbuf> {
|
||||
// TODO: Can we load this from icon theme with app id `steam_icon_{}`?
|
||||
let path = dirs::data_dir().map_or_else(
|
||||
|| Err(Report::msg("Missing XDG data dir")),
|
||||
@@ -190,7 +230,7 @@ impl<'a> ImageProvider<'a> {
|
||||
},
|
||||
)?;
|
||||
|
||||
self.get_from_file(&path)
|
||||
self.get_from_file(&path, scale)
|
||||
}
|
||||
|
||||
/// Attempts to get `Bytes` from an HTTP resource asynchronously.
|
||||
|
||||
@@ -15,6 +15,7 @@ mod modules;
|
||||
mod popup;
|
||||
mod script;
|
||||
mod style;
|
||||
mod unique_id;
|
||||
|
||||
use crate::bar::create_bar;
|
||||
use crate::config::{Config, MonitorConfig};
|
||||
|
||||
@@ -5,7 +5,6 @@ use crate::image::ImageProvider;
|
||||
use gtk::prelude::*;
|
||||
use gtk::Image;
|
||||
use serde::Deserialize;
|
||||
use tracing::error;
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub struct ImageWidget {
|
||||
@@ -31,12 +30,8 @@ impl CustomWidget for ImageWidget {
|
||||
let icon_theme = context.icon_theme.clone();
|
||||
|
||||
DynamicString::new(&self.src, move |src| {
|
||||
let res = ImageProvider::parse(&src, &icon_theme, self.size)
|
||||
.and_then(|image| image.load_into_image(gtk_image.clone()));
|
||||
|
||||
if let Err(err) = res {
|
||||
error!("{err:?}");
|
||||
}
|
||||
ImageProvider::parse(&src, &icon_theme, self.size)
|
||||
.map(|image| image.load_into_image(gtk_image.clone()));
|
||||
|
||||
Continue(true)
|
||||
});
|
||||
|
||||
@@ -11,7 +11,7 @@ use gtk::Label;
|
||||
use serde::Deserialize;
|
||||
use tokio::spawn;
|
||||
use tokio::sync::mpsc::{Receiver, Sender};
|
||||
use tracing::{debug, error};
|
||||
use tracing::debug;
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub struct FocusedModule {
|
||||
@@ -97,7 +97,10 @@ impl Module<gtk::Box> for FocusedModule {
|
||||
let container = gtk::Box::new(info.bar_position.get_orientation(), 5);
|
||||
|
||||
let icon = gtk::Image::new();
|
||||
add_class(&icon, "icon");
|
||||
if self.show_icon {
|
||||
add_class(&icon, "icon");
|
||||
container.add(&icon);
|
||||
}
|
||||
|
||||
let label = Label::new(None);
|
||||
add_class(&label, "label");
|
||||
@@ -106,17 +109,17 @@ impl Module<gtk::Box> for FocusedModule {
|
||||
truncate.truncate_label(&label);
|
||||
}
|
||||
|
||||
container.add(&icon);
|
||||
container.add(&label);
|
||||
|
||||
{
|
||||
let icon_theme = icon_theme.clone();
|
||||
context.widget_rx.attach(None, move |(name, id)| {
|
||||
if self.show_icon {
|
||||
if let Err(err) = ImageProvider::parse(&id, &icon_theme, self.icon_size)
|
||||
.and_then(|image| image.load_into_image(icon.clone()))
|
||||
match ImageProvider::parse(&id, &icon_theme, self.icon_size)
|
||||
.map(|image| image.load_into_image(icon.clone()))
|
||||
{
|
||||
error!("{err:?}");
|
||||
Some(Ok(_)) => icon.show(),
|
||||
_ => icon.hide(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -192,16 +192,13 @@ impl ItemButton {
|
||||
let gtk_image = gtk::Image::new();
|
||||
let image =
|
||||
ImageProvider::parse(&item.app_id.clone(), icon_theme, appearance.icon_size);
|
||||
match image {
|
||||
Ok(image) => {
|
||||
button.set_image(Some(>k_image));
|
||||
button.set_always_show_image(true);
|
||||
if let Some(image) = image {
|
||||
button.set_image(Some(>k_image));
|
||||
button.set_always_show_image(true);
|
||||
|
||||
if let Err(err) = image.load_into_image(gtk_image) {
|
||||
error!("{err:?}");
|
||||
}
|
||||
if let Err(err) = image.load_into_image(gtk_image) {
|
||||
error!("{err:?}");
|
||||
}
|
||||
Err(err) => error!("{err:?}"),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -468,10 +468,6 @@ impl Module<gtk::Box> for LauncherModule {
|
||||
let tx = controller_tx.clone();
|
||||
button.connect_clicked(move |button| {
|
||||
try_send!(tx, ItemEvent::FocusWindow(win.id));
|
||||
|
||||
if let Some(win) = button.window() {
|
||||
win.hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -121,27 +121,27 @@ fn default_icon_pause() -> String {
|
||||
}
|
||||
|
||||
fn default_icon_prev() -> String {
|
||||
String::from("\u{f9ad}")
|
||||
String::from("")
|
||||
}
|
||||
|
||||
fn default_icon_next() -> String {
|
||||
String::from("\u{f9ac}")
|
||||
String::from("")
|
||||
}
|
||||
|
||||
fn default_icon_volume() -> String {
|
||||
String::from("墳")
|
||||
String::from("")
|
||||
}
|
||||
|
||||
fn default_icon_track() -> String {
|
||||
String::from("\u{f886}")
|
||||
String::from("")
|
||||
}
|
||||
|
||||
fn default_icon_album() -> String {
|
||||
String::from("\u{f524}")
|
||||
String::from("")
|
||||
}
|
||||
|
||||
fn default_icon_artist() -> String {
|
||||
String::from("\u{fd01}")
|
||||
String::from("")
|
||||
}
|
||||
|
||||
fn default_music_dir() -> PathBuf {
|
||||
|
||||
@@ -342,19 +342,15 @@ impl Module<Button> for MusicModule {
|
||||
let new_cover = update.song.cover_path;
|
||||
if prev_cover != new_cover {
|
||||
prev_cover = new_cover.clone();
|
||||
let res = match new_cover.map(|cover_path| {
|
||||
let res = if let Some(image) = new_cover.and_then(|cover_path| {
|
||||
ImageProvider::parse(&cover_path, &icon_theme, image_size)
|
||||
}) {
|
||||
Some(Ok(image)) => image.load_into_image(album_image.clone()),
|
||||
Some(Err(err)) => {
|
||||
album_image.set_from_pixbuf(None);
|
||||
Err(err)
|
||||
}
|
||||
None => {
|
||||
album_image.set_from_pixbuf(None);
|
||||
Ok(())
|
||||
}
|
||||
image.load_into_image(album_image.clone())
|
||||
} else {
|
||||
album_image.set_from_pixbuf(None);
|
||||
Ok(())
|
||||
};
|
||||
|
||||
if let Err(err) = res {
|
||||
error!("{err:?}");
|
||||
}
|
||||
@@ -458,7 +454,7 @@ impl IconLabel {
|
||||
let icon = new_icon_label(icon_input, icon_theme, 24);
|
||||
let label = Label::new(label);
|
||||
|
||||
add_class(&icon, "icon");
|
||||
add_class(&icon, "icon-box");
|
||||
add_class(&label, "label");
|
||||
|
||||
container.add(&icon);
|
||||
|
||||
@@ -41,7 +41,7 @@ pub struct UpowerProperties {
|
||||
time_to_empty: i64,
|
||||
}
|
||||
|
||||
impl Module<gtk::Box> for UpowerModule {
|
||||
impl Module<gtk::Button> for UpowerModule {
|
||||
type SendMessage = UpowerProperties;
|
||||
type ReceiveMessage = ();
|
||||
|
||||
@@ -143,7 +143,7 @@ impl Module<gtk::Box> for UpowerModule {
|
||||
self,
|
||||
context: WidgetContext<Self::SendMessage, Self::ReceiveMessage>,
|
||||
info: &ModuleInfo,
|
||||
) -> Result<ModuleWidget<gtk::Box>> {
|
||||
) -> Result<ModuleWidget<Button>> {
|
||||
let icon_theme = info.icon_theme.clone();
|
||||
let icon = gtk::Image::new();
|
||||
add_class(&icon, "icon");
|
||||
@@ -154,15 +154,15 @@ impl Module<gtk::Box> for UpowerModule {
|
||||
.build();
|
||||
add_class(&label, "label");
|
||||
|
||||
let container = gtk::Box::new(Orientation::Horizontal, 0);
|
||||
add_class(&container, "upower");
|
||||
let container = gtk::Box::new(Orientation::Horizontal, 5);
|
||||
add_class(&container, "contents");
|
||||
|
||||
let button = Button::new();
|
||||
add_class(&button, "button");
|
||||
|
||||
button.add(&label);
|
||||
container.add(&button);
|
||||
container.add(&icon);
|
||||
container.add(&label);
|
||||
button.add(&container);
|
||||
|
||||
let orientation = info.bar_position.get_orientation();
|
||||
button.connect_clicked(move |button| {
|
||||
@@ -180,11 +180,8 @@ impl Module<gtk::Box> for UpowerModule {
|
||||
.attach(None, move |properties: UpowerProperties| {
|
||||
let format = format.replace("{percentage}", &properties.percentage.to_string());
|
||||
let icon_name = String::from("icon:") + &properties.icon_name;
|
||||
if let Err(err) = ImageProvider::parse(&icon_name, &icon_theme, 32)
|
||||
.and_then(|provider| provider.load_into_image(icon.clone()))
|
||||
{
|
||||
error!("{err:?}");
|
||||
}
|
||||
ImageProvider::parse(&icon_name, &icon_theme, 24)
|
||||
.map(|provider| provider.load_into_image(icon.clone()));
|
||||
label.set_markup(format.as_ref());
|
||||
Continue(true)
|
||||
});
|
||||
@@ -192,7 +189,7 @@ impl Module<gtk::Box> for UpowerModule {
|
||||
let popup = self.into_popup(context.controller_tx, context.popup_rx, info);
|
||||
|
||||
Ok(ModuleWidget {
|
||||
widget: container,
|
||||
widget: button,
|
||||
popup,
|
||||
})
|
||||
}
|
||||
@@ -212,28 +209,35 @@ impl Module<gtk::Box> for UpowerModule {
|
||||
|
||||
let label = Label::new(None);
|
||||
add_class(&label, "upower-details");
|
||||
container.add(&label);
|
||||
|
||||
rx.attach(None, move |properties| {
|
||||
let mut format = String::new();
|
||||
let state = u32_to_battery_state(properties.state);
|
||||
match state {
|
||||
let format = match state {
|
||||
Ok(BatteryState::Charging | BatteryState::PendingCharge) => {
|
||||
let ttf = properties.time_to_full;
|
||||
if ttf > 0 {
|
||||
format = format!("Full in {}", seconds_to_string(ttf));
|
||||
format!("Full in {}", seconds_to_string(ttf))
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
Ok(BatteryState::Discharging | BatteryState::PendingDischarge) => {
|
||||
let tte = properties.time_to_empty;
|
||||
if tte > 0 {
|
||||
format = format!("Empty in {}", seconds_to_string(tte));
|
||||
format!("Empty in {}", seconds_to_string(tte))
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
Err(state) => error!("Invalid battery state: {state}"),
|
||||
_ => {}
|
||||
}
|
||||
label.set_markup(&format);
|
||||
Err(state) => {
|
||||
error!("Invalid battery state: {state}");
|
||||
String::new()
|
||||
}
|
||||
_ => String::new(),
|
||||
};
|
||||
|
||||
label.set_markup(&format);
|
||||
Continue(true)
|
||||
});
|
||||
|
||||
|
||||
@@ -234,7 +234,7 @@ impl Script {
|
||||
|
||||
debug!("Running sh with args: {args_list:?}");
|
||||
|
||||
let output = Command::new("sh")
|
||||
let output = Command::new("/bin/sh")
|
||||
.args(&args_list)
|
||||
.output()
|
||||
.await
|
||||
@@ -265,7 +265,7 @@ impl Script {
|
||||
/// Returns a `mpsc::Receiver` that sends a message
|
||||
/// every time a new line is written to `stdout` or `stderr`.
|
||||
pub async fn spawn(&self) -> Result<mpsc::Receiver<OutputStream>> {
|
||||
let mut handle = Command::new("sh")
|
||||
let mut handle = Command::new("/bin/sh")
|
||||
.args(["-c", &self.cmd])
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
|
||||
9
src/unique_id.rs
Normal file
9
src/unique_id.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
static COUNTER: AtomicUsize = AtomicUsize::new(1);
|
||||
|
||||
/// Gets a `usize` ID value that is unique to the entire Ironbar instance.
|
||||
/// This is just an `AtomicUsize` that increments every time this function is called.
|
||||
pub fn get_unique_usize() -> usize {
|
||||
COUNTER.fetch_add(1, Ordering::Relaxed)
|
||||
}
|
||||
Reference in New Issue
Block a user