mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-02-12 00:07:40 +01:00
Compare commits
1 Commits
feature/fl
...
feature/sp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7fed31bc54 |
1246
Cargo.lock
generated
1246
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -8,8 +8,7 @@ members = [
|
||||
"komorebic",
|
||||
"komorebic-no-console",
|
||||
"komorebi-bar",
|
||||
"komorebi-themes",
|
||||
"komorebi-shortcuts"
|
||||
"komorebi-themes"
|
||||
]
|
||||
|
||||
[workspace.dependencies]
|
||||
@@ -34,7 +33,7 @@ tracing = "0.1"
|
||||
tracing-appender = "0.2"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
paste = "1"
|
||||
sysinfo = "0.34"
|
||||
sysinfo = "0.33"
|
||||
uds_windows = "1"
|
||||
win32-display-data = { git = "https://github.com/LGUG2Z/win32-display-data", rev = "a28c6559a9de2f92c142a714947a9b081776caca" }
|
||||
windows-numerics = { version = "0.2" }
|
||||
@@ -73,4 +72,4 @@ features = [
|
||||
"Win32_System_WindowsProgramming",
|
||||
"Media",
|
||||
"Media_Control"
|
||||
]
|
||||
]
|
||||
@@ -394,7 +394,7 @@ every `WindowManagerEvent` and `SocketMessage` handled by `komorebi` in a Rust c
|
||||
Below is a simple example of how to use `komorebi-client` in a basic Rust application.
|
||||
|
||||
```rust
|
||||
// komorebi-client = { git = "https://github.com/LGUG2Z/komorebi", tag = "v0.1.36"}
|
||||
// komorebi-client = { git = "https://github.com/LGUG2Z/komorebi", tag = "v0.1.35"}
|
||||
|
||||
use anyhow::Result;
|
||||
use komorebi_client::Notification;
|
||||
|
||||
37
deny.toml
37
deny.toml
@@ -13,8 +13,7 @@ feature-depth = 1
|
||||
[advisories]
|
||||
ignore = [
|
||||
{ id = "RUSTSEC-2020-0016", reason = "local tcp connectivity is an opt-in feature, and there is no upgrade path for TcpStreamExt" },
|
||||
{ id = "RUSTSEC-2024-0436", reason = "paste being unmaintained is not an issue in our use" },
|
||||
{ id = "RUSTSEC-2024-0320", reason = "not using any yaml features from this library" }
|
||||
{ id = "RUSTSEC-2024-0436", reason = "paste being unmaintained is not an issue in our use" }
|
||||
]
|
||||
|
||||
[licenses]
|
||||
@@ -34,58 +33,43 @@ allow = [
|
||||
"Ubuntu-font-1.0",
|
||||
"Unicode-3.0",
|
||||
"Zlib",
|
||||
"LicenseRef-Komorebi-2.0"
|
||||
"LicenseRef-Komorebi-1.0"
|
||||
]
|
||||
confidence-threshold = 0.8
|
||||
|
||||
[[licenses.clarify]]
|
||||
crate = "komorebi"
|
||||
expression = "LicenseRef-Komorebi-2.0"
|
||||
expression = "LicenseRef-Komorebi-1.0"
|
||||
license-files = []
|
||||
|
||||
[[licenses.clarify]]
|
||||
crate = "komorebi-client"
|
||||
expression = "LicenseRef-Komorebi-2.0"
|
||||
expression = "LicenseRef-Komorebi-1.0"
|
||||
license-files = []
|
||||
|
||||
[[licenses.clarify]]
|
||||
crate = "komorebic"
|
||||
expression = "LicenseRef-Komorebi-2.0"
|
||||
expression = "LicenseRef-Komorebi-1.0"
|
||||
license-files = []
|
||||
|
||||
[[licenses.clarify]]
|
||||
crate = "komorebic-no-console"
|
||||
expression = "LicenseRef-Komorebi-2.0"
|
||||
expression = "LicenseRef-Komorebi-1.0"
|
||||
license-files = []
|
||||
|
||||
[[licenses.clarify]]
|
||||
crate = "komorebi-themes"
|
||||
expression = "LicenseRef-Komorebi-2.0"
|
||||
expression = "LicenseRef-Komorebi-1.0"
|
||||
license-files = []
|
||||
|
||||
[[licenses.clarify]]
|
||||
crate = "komorebi-gui"
|
||||
expression = "LicenseRef-Komorebi-2.0"
|
||||
expression = "LicenseRef-Komorebi-1.0"
|
||||
license-files = []
|
||||
|
||||
[[licenses.clarify]]
|
||||
crate = "komorebi-bar"
|
||||
expression = "LicenseRef-Komorebi-2.0"
|
||||
license-files = []
|
||||
|
||||
[[licenses.clarify]]
|
||||
crate = "komorebi-shortcuts"
|
||||
expression = "LicenseRef-Komorebi-2.0"
|
||||
license-files = []
|
||||
|
||||
[[licenses.clarify]]
|
||||
crate = "whkd-core"
|
||||
expression = "LicenseRef-Komorebi-2.0"
|
||||
license-files = []
|
||||
|
||||
[[licenses.clarify]]
|
||||
crate = "whkd-parser"
|
||||
expression = "LicenseRef-Komorebi-2.0"
|
||||
expression = "LicenseRef-Komorebi-1.0"
|
||||
license-files = []
|
||||
|
||||
[[licenses.clarify]]
|
||||
@@ -109,7 +93,4 @@ allow-git = [
|
||||
"https://github.com/LGUG2Z/catppuccin-egui",
|
||||
"https://github.com/LGUG2Z/windows-icons",
|
||||
"https://github.com/LGUG2Z/win32-display-data",
|
||||
"https://github.com/LGUG2Z/flavours",
|
||||
"https://github.com/LGUG2Z/base16_color_scheme",
|
||||
"https://github.com/LGUG2Z/whkd",
|
||||
]
|
||||
|
||||
@@ -3,9 +3,8 @@
|
||||
[
|
||||
"0BSD",
|
||||
[
|
||||
"adler 1.0.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"adler2 2.0.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"win32-display-data 0.1.0 git+https://github.com/LGUG2Z/win32-display-data?rev=a28c6559a9de2f92c142a714947a9b081776caca"
|
||||
"win32-display-data 0.1.0 git+https://github.com/LGUG2Z/win32-display-data?rev=55cebdebfbd68dbd14945a1ba90f6b05b7be2893"
|
||||
]
|
||||
],
|
||||
[
|
||||
@@ -17,85 +16,74 @@
|
||||
"accesskit_consumer 0.26.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"accesskit_windows 0.24.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"accesskit_winit 0.23.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"adler 1.0.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"adler2 2.0.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"ahash 0.8.11 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"allocator-api2 0.2.21 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"anstream 0.6.18 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"anstyle 1.0.10 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"anstyle-parse 0.2.6 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"anstyle-query 1.1.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"anstyle-wincon 3.0.7 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"anyhow 1.0.98 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"approx 0.3.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"arboard 3.5.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"anyhow 1.0.97 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"arboard 3.4.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"arrayvec 0.7.6 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"atomic-waker 1.1.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"autocfg 1.4.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"backtrace 0.3.71 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"backtrace-ext 0.2.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"base16_color_scheme 0.3.2 git+https://github.com/LGUG2Z/base16_color_scheme",
|
||||
"base64 0.22.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"beef 0.5.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"bit_field 0.10.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"bitflags 1.3.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"bitflags 2.9.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"bitstream-io 2.6.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"bytemuck 1.22.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"bytemuck_derive 1.9.3 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"cc 1.2.19 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"bytemuck_derive 1.8.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"cc 1.2.16 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"cfg-if 0.1.10 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"cfg-if 1.0.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"chrono 0.4.40 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"chrono-tz 0.10.3 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"chrono-tz-build 0.4.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"clap 4.5.37 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"clap_builder 4.5.37 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"clap_derive 4.5.32 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"clap 4.5.31 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"clap_builder 4.5.31 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"clap_derive 4.5.28 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"clap_lex 0.7.4 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"color-eyre 0.6.3 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"color-spantrace 0.2.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"colorchoice 1.0.3 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"crc32fast 1.4.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"crossbeam-channel 0.5.15 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"crossbeam-channel 0.5.14 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"crossbeam-deque 0.8.6 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"crossbeam-epoch 0.9.18 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"crossbeam-utils 0.8.21 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"ctrlc 3.4.6 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"ctrlc 3.4.5 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"cursor-icon 1.1.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"deflate 0.8.6 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"deranged 0.4.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"dirs 3.0.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"dirs 4.0.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"deranged 0.3.11 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"dirs 6.0.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"dirs-sys 0.3.7 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"dirs-sys 0.5.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"displaydoc 0.2.5 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"document-features 0.2.11 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"dpi 0.1.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"dunce 1.0.5 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"dyn-clone 1.0.19 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"ecolor 0.31.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"eframe 0.31.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"egui 0.31.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"ecolor 0.31.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"eframe 0.31.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"egui 0.31.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"egui-phosphor 0.9.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"egui-winit 0.31.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"egui_extras 0.31.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"egui_glow 0.31.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"either 1.15.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"emath 0.31.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"egui-winit 0.31.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"egui_extras 0.31.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"egui_glow 0.31.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"either 1.14.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"emath 0.31.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"encoding_rs 0.8.35 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"enum-map 2.7.3 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"enum-map-derive 0.17.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"env_home 0.1.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"epaint 0.31.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"epaint_default_fonts 0.31.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"epaint 0.31.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"epaint_default_fonts 0.31.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"equivalent 1.0.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"eyre 0.6.12 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"fastrand 2.3.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"fdeflate 0.3.7 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"filetime 0.2.25 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"flate2 1.1.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"flate2 1.1.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"fnv 1.0.7 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"form_urlencoded 1.2.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"futures 0.3.31 registry+https://github.com/rust-lang/crates.io-index",
|
||||
@@ -107,67 +95,53 @@
|
||||
"futures-sink 0.3.31 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"futures-task 0.3.31 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"futures-util 0.3.31 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"getrandom 0.1.16 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"getrandom 0.2.15 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"getrandom 0.3.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"gif 0.11.4 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"getrandom 0.3.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"gif 0.13.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"git2 0.20.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"git2 0.20.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"gl_generator 0.14.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"glob 0.3.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"glow 0.16.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"glutin 0.32.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"glutin_egl_sys 0.7.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"glutin_wgl_sys 0.6.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"half 2.6.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"hashbrown 0.12.3 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"hashbrown 0.14.5 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"half 2.4.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"hashbrown 0.15.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"heck 0.5.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"hex 0.4.3 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"hex_color 3.0.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"hotwatch 0.5.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"http 1.3.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"http 1.2.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"httparse 1.10.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"hyper-tls 0.6.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"iana-time-zone 0.1.63 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"ident_case 1.0.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"iana-time-zone 0.1.61 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"idna 1.0.3 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"idna_adapter 1.2.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"image 0.25.6 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"image 0.25.5 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"image-webp 0.2.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"imgref 1.11.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"immutable-chunkmap 2.0.6 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"indenter 0.3.3 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"indexmap 1.9.3 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"indexmap 2.9.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"indexmap 2.7.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"ipnet 2.11.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"is_debug 1.1.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"is_terminal_polyfill 1.70.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"itertools 0.12.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"itertools 0.14.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"itoa 1.0.15 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"jobserver 0.1.33 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"jpeg-decoder 0.1.22 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"jobserver 0.1.32 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"jpeg-decoder 0.3.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"khronos_api 3.1.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"lazy_static 1.5.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"libc 0.2.172 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"libgit2-sys 0.18.1+1.9.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"libz-sys 1.1.22 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"linked-hash-map 0.5.6 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"libc 0.2.170 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"libgit2-sys 0.18.0+1.9.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"libz-sys 1.1.21 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"litrs 0.4.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"lock_api 0.4.12 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"log 0.4.27 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"logos 0.14.4 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"logos-codegen 0.14.4 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"logos-derive 0.14.4 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"miette 7.6.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"miette-derive 7.6.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"log 0.4.26 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"miette 7.5.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"miette-derive 7.5.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"mime 0.3.17 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"minimal-lexical 0.2.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"miniz_oxide 0.4.4 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"miniz_oxide 0.8.8 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"miniz_oxide 0.8.5 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"miow 0.6.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"native-tls 0.2.14 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"net2 0.2.39 registry+https://github.com/rust-lang/crates.io-index",
|
||||
@@ -180,13 +154,10 @@
|
||||
"num-derive 0.4.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"num-integer 0.1.46 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"num-iter 0.1.45 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"num-rational 0.3.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"num-rational 0.4.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"num-traits 0.2.19 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"once_cell 1.21.3 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"once_cell 1.20.3 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"owned_ttf_parser 0.25.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"palette 0.5.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"palette_derive 0.5.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"parking_lot 0.12.3 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"parking_lot_core 0.9.10 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"paste 1.0.15 registry+https://github.com/rust-lang/crates.io-index",
|
||||
@@ -194,26 +165,20 @@
|
||||
"pin-project-lite 0.2.16 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"pin-utils 0.1.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"pkg-config 0.3.32 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"png 0.16.8 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"png 0.17.16 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"powerfmt 0.2.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"ppv-lite86 0.2.21 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"ppv-lite86 0.2.20 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"proc-macro-error-attr2 2.0.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"proc-macro-error2 2.0.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"proc-macro2 1.0.95 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"proc-macro2 1.0.94 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"profiling 1.0.16 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"profiling-procmacros 1.0.16 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"psm 0.1.26 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"qoi 0.4.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"quick-error 2.0.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"quote 1.0.40 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"rand 0.7.3 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"quote 1.0.39 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"rand 0.8.5 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"rand_chacha 0.2.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"rand_chacha 0.3.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"rand_core 0.5.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"rand_core 0.6.4 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"rand_pcg 0.2.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"raw-window-handle 0.6.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"rayon 1.10.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"rayon-core 1.12.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
@@ -221,58 +186,46 @@
|
||||
"regex-automata 0.4.9 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"regex-syntax 0.6.29 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"regex-syntax 0.8.5 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"reqwest 0.12.15 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"roxmltree 0.20.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"reqwest 0.12.12 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"rustc-demangle 0.1.24 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"rustls-pemfile 2.2.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"rustls-pki-types 1.11.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"rustversion 1.0.20 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"ryu 1.0.20 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"scopeguard 1.2.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"serde 1.0.219 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"serde_derive 1.0.219 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"serde 1.0.218 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"serde_derive 1.0.218 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"serde_derive_internals 0.29.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"serde_json 1.0.140 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"serde_json_lenient 0.2.4 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"serde_urlencoded 0.7.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"serde_variant 0.1.3 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"serde_with 3.12.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"serde_with_macros 3.12.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"serde_yaml 0.8.26 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"serde_yaml 0.9.34+deprecated registry+https://github.com/rust-lang/crates.io-index",
|
||||
"shadow-rs 1.1.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"shell-words 1.1.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"shellexpand 2.1.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"shadow-rs 1.0.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"shlex 1.3.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"siphasher 0.3.11 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"siphasher 1.0.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"smallvec 1.15.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"smallvec 1.14.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"smol_str 0.2.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"socket2 0.5.9 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"socket2 0.5.8 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"stable_deref_trait 1.2.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"stacker 0.1.21 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"static_assertions 1.1.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"supports-color 3.0.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"supports-hyperlinks 3.1.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"supports-unicode 3.0.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"syn 1.0.109 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"syn 2.0.100 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"syn 2.0.99 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"sync_wrapper 1.0.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"tempfile 3.19.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"terminal_size 0.4.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"tempfile 3.17.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"terminal_size 0.4.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"thiserror 1.0.69 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"thiserror 2.0.12 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"thiserror-impl 1.0.69 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"thiserror-impl 2.0.12 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"thread_local 1.1.8 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"time 0.3.41 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"time-core 0.1.4 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"toml 0.5.11 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"time 0.3.37 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"time-core 0.1.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"ttf-parser 0.25.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"typenum 1.18.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"tz-rs 0.7.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"tzdb 0.7.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"tzdb_data 0.2.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"unicase 2.8.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"unicode-ident 1.0.18 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"unicode-linebreak 0.1.5 registry+https://github.com/rust-lang/crates.io-index",
|
||||
@@ -288,59 +241,48 @@
|
||||
"vcpkg 0.2.15 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"version_check 0.9.5 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"web-time 1.1.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"webbrowser 1.0.4 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"webbrowser 1.0.3 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"weezl 0.1.8 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"winapi 0.3.9 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows 0.57.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows 0.58.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows 0.60.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows 0.61.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-collections 0.1.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-collections 0.2.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-core 0.52.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-core 0.57.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-core 0.58.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-core 0.60.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-core 0.61.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-future 0.1.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-future 0.2.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-implement 0.57.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-implement 0.58.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-implement 0.59.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-implement 0.60.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-interface 0.57.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-interface 0.58.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-interface 0.59.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-link 0.1.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-interface 0.59.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-link 0.1.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-numerics 0.1.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-numerics 0.2.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-registry 0.4.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-registry 0.2.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-result 0.1.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-result 0.2.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-result 0.3.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-result 0.3.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-strings 0.1.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-strings 0.3.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-strings 0.4.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-sys 0.48.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-sys 0.52.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-sys 0.59.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-targets 0.48.5 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-targets 0.52.6 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-targets 0.53.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows_aarch64_msvc 0.48.5 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows_aarch64_msvc 0.52.6 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows_aarch64_msvc 0.53.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows_i686_msvc 0.48.5 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows_i686_msvc 0.52.6 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows_i686_msvc 0.53.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows_x86_64_msvc 0.48.5 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows_x86_64_msvc 0.52.6 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows_x86_64_msvc 0.53.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"winit 0.30.9 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"wmi 0.15.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"wmi 0.15.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"write16 1.0.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"yaml-rust 0.4.5 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"zerocopy 0.7.35 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"zerocopy 0.8.24 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"zerocopy-derive 0.7.35 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"zune-core 0.4.12 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"zune-inflate 0.2.54 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"zune-jpeg 0.4.14 registry+https://github.com/rust-lang/crates.io-index"
|
||||
@@ -360,7 +302,7 @@
|
||||
"rav1e 0.7.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"v_frame 0.3.8 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"zerocopy 0.7.35 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"zerocopy 0.8.24 registry+https://github.com/rust-lang/crates.io-index"
|
||||
"zerocopy-derive 0.7.35 registry+https://github.com/rust-lang/crates.io-index"
|
||||
]
|
||||
],
|
||||
[
|
||||
@@ -374,7 +316,7 @@
|
||||
"encoding_rs 0.8.35 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"exr 1.73.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"lebe 0.5.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"ravif 0.11.12 registry+https://github.com/rust-lang/crates.io-index"
|
||||
"ravif 0.11.11 registry+https://github.com/rust-lang/crates.io-index"
|
||||
]
|
||||
],
|
||||
[
|
||||
@@ -401,7 +343,7 @@
|
||||
"is_ci 1.2.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"libloading 0.8.6 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"rustls-pemfile 2.2.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"starship-battery 0.10.1 registry+https://github.com/rust-lang/crates.io-index"
|
||||
"starship-battery 0.10.0 registry+https://github.com/rust-lang/crates.io-index"
|
||||
]
|
||||
],
|
||||
[
|
||||
@@ -410,28 +352,24 @@
|
||||
"accesskit 0.17.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"accesskit_consumer 0.26.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"accesskit_windows 0.24.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"adler 1.0.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"adler2 2.0.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"ahash 0.8.11 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"aho-corasick 1.1.3 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"aligned-vec 0.5.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"allocator-api2 0.2.21 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"anstream 0.6.18 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"anstyle 1.0.10 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"anstyle-parse 0.2.6 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"anstyle-query 1.1.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"anstyle-wincon 3.0.7 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"anyhow 1.0.98 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"arboard 3.5.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"anyhow 1.0.97 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"arboard 3.4.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"arg_enum_proc_macro 0.3.4 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"arrayvec 0.7.6 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"atomic-waker 1.1.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"autocfg 1.4.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"backtrace 0.3.71 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"backtrace-ext 0.2.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"base16_color_scheme 0.3.2 git+https://github.com/LGUG2Z/base16_color_scheme",
|
||||
"base64 0.22.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"beef 0.5.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"bit_field 0.10.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"bitflags 1.3.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"bitflags 2.9.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
@@ -440,72 +378,58 @@
|
||||
"brotli-decompressor 2.5.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"built 0.7.7 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"bytemuck 1.22.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"bytemuck_derive 1.9.3 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"bytemuck_derive 1.8.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"byteorder 1.5.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"byteorder-lite 0.1.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"bytes 1.10.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"calm_io 0.1.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"calmio_filters 0.1.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"bytes 1.10.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"catppuccin-egui 5.3.1 git+https://github.com/LGUG2Z/catppuccin-egui?rev=bdaff30959512c4f7ee7304117076a48633d777f",
|
||||
"cc 1.2.19 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"cc 1.2.16 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"cfg-if 0.1.10 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"cfg-if 1.0.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"cfg_aliases 0.2.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"chrono 0.4.40 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"chrono-tz 0.10.3 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"chrono-tz-build 0.4.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"chumsky 0.9.3 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"clap 4.5.37 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"clap_builder 4.5.37 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"clap_derive 4.5.32 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"clap 4.5.31 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"clap_builder 4.5.31 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"clap_derive 4.5.28 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"clap_lex 0.7.4 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"color-eyre 0.6.3 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"color-spantrace 0.2.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"color-thief 0.2.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"color_quant 1.1.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"colorchoice 1.0.3 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"crc32fast 1.4.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"crossbeam-channel 0.5.15 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"crossbeam-channel 0.5.14 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"crossbeam-deque 0.8.6 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"crossbeam-epoch 0.9.18 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"crossbeam-utils 0.8.21 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"ctrlc 3.4.6 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"ctrlc 3.4.5 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"cursor-icon 1.1.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"darling 0.20.11 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"darling_core 0.20.11 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"darling_macro 0.20.11 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"deflate 0.8.6 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"deranged 0.4.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"dirs 3.0.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"dirs 4.0.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"deranged 0.3.11 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"dirs 6.0.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"dirs-sys 0.3.7 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"dirs-sys 0.5.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"displaydoc 0.2.5 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"document-features 0.2.11 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"dyn-clone 1.0.19 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"ecolor 0.31.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"eframe 0.31.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"egui 0.31.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"ecolor 0.31.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"eframe 0.31.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"egui 0.31.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"egui-phosphor 0.9.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"egui-winit 0.31.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"egui_extras 0.31.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"egui_glow 0.31.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"either 1.15.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"emath 0.31.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"egui-winit 0.31.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"egui_extras 0.31.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"egui_glow 0.31.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"either 1.14.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"emath 0.31.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"encoding_rs 0.8.35 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"enum-map 2.7.3 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"enum-map-derive 0.17.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"env_home 0.1.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"epaint 0.31.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"epaint_default_fonts 0.31.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"epaint 0.31.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"epaint_default_fonts 0.31.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"equivalent 1.0.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"eyre 0.6.12 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"fastrand 2.3.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"fdeflate 0.3.7 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"filetime 0.2.25 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"flate2 1.1.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"flavours 0.7.2 git+https://github.com/LGUG2Z/flavours",
|
||||
"flate2 1.1.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"fnv 1.0.7 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"font-loader 0.11.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"form_urlencoded 1.2.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
@@ -519,80 +443,64 @@
|
||||
"futures-sink 0.3.31 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"futures-task 0.3.31 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"futures-util 0.3.31 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"getrandom 0.1.16 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"getrandom 0.2.15 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"getrandom 0.3.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"getrandom 0.3.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"getset 0.1.5 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"gif 0.11.4 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"gif 0.13.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"git2 0.20.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"glob 0.3.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"git2 0.20.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"glow 0.16.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"glutin-winit 0.5.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"h2 0.4.9 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"half 2.6.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"hashbrown 0.12.3 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"hashbrown 0.14.5 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"h2 0.4.8 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"half 2.4.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"hashbrown 0.15.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"heck 0.5.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"hex 0.4.3 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"hex_color 3.0.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"hotwatch 0.5.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"http 1.3.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"http 1.2.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"http-body 1.0.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"http-body-util 0.1.3 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"http-body-util 0.1.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"httparse 1.10.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"hyper 1.6.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"hyper-tls 0.6.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"hyper-util 0.1.11 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"iana-time-zone 0.1.63 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"ident_case 1.0.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"hyper-util 0.1.10 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"iana-time-zone 0.1.61 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"idna 1.0.3 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"idna_adapter 1.2.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"image 0.23.14 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"image 0.25.6 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"image 0.25.5 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"image-webp 0.2.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"immutable-chunkmap 2.0.6 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"indenter 0.3.3 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"indexmap 1.9.3 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"indexmap 2.9.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"indexmap 2.7.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"ipnet 2.11.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"is_debug 1.1.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"is_terminal_polyfill 1.70.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"itertools 0.12.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"itertools 0.14.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"itoa 1.0.15 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"jobserver 0.1.33 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"jpeg-decoder 0.1.22 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"jobserver 0.1.32 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"jpeg-decoder 0.3.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"lazy_static 1.5.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"libc 0.2.172 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"libgit2-sys 0.18.1+1.9.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"libz-sys 1.1.22 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"linked-hash-map 0.5.6 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"libc 0.2.170 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"libgit2-sys 0.18.0+1.9.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"libz-sys 1.1.21 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"litrs 0.4.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"lock_api 0.4.12 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"log 0.4.27 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"logos 0.14.4 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"logos-codegen 0.14.4 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"logos-derive 0.14.4 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"log 0.4.26 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"loop9 0.1.5 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"matchers 0.1.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"maybe-rayon 0.1.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"memchr 2.7.4 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"memoffset 0.9.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"mime 0.3.17 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"mime_guess2 2.3.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"mime_guess2 2.0.5 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"minimal-lexical 0.2.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"miniz_oxide 0.3.7 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"miniz_oxide 0.4.4 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"miniz_oxide 0.8.8 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"miniz_oxide 0.8.5 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"mio 1.0.3 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"miow 0.6.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"nanoid 0.4.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"native-tls 0.2.14 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"net2 0.2.39 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"netdev 0.34.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"netdev 0.32.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"new_debug_unreachable 1.0.6 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"nohash-hasher 0.2.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"nom 7.1.3 registry+https://github.com/rust-lang/crates.io-index",
|
||||
@@ -606,54 +514,36 @@
|
||||
"num-derive 0.4.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"num-integer 0.1.46 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"num-iter 0.1.45 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"num-rational 0.3.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"num-rational 0.4.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"num-traits 0.2.19 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"once_cell 1.21.3 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"once_cell 1.20.3 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"os_info 3.10.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"overload 0.1.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"owo-colors 3.5.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"owo-colors 4.2.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"palette 0.5.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"palette_derive 0.5.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"parking_lot 0.12.3 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"parking_lot_core 0.9.10 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"parse-zoneinfo 0.3.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"paste 1.0.15 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"percent-encoding 2.3.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"phf 0.11.3 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"phf 0.8.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"phf_codegen 0.11.3 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"phf_codegen 0.8.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"phf_generator 0.11.3 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"phf_generator 0.8.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"phf_shared 0.11.3 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"phf_shared 0.8.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"pin-project-lite 0.2.16 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"pin-utils 0.1.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"pkg-config 0.3.32 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"png 0.16.8 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"png 0.17.16 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"powerfmt 0.2.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"powershell_script 1.1.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"ppv-lite86 0.2.21 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"ppv-lite86 0.2.20 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"proc-macro-error-attr2 2.0.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"proc-macro-error2 2.0.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"proc-macro2 1.0.95 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"proc-macro2 1.0.94 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"profiling 1.0.16 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"profiling-procmacros 1.0.16 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"psm 0.1.26 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"qoi 0.4.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"quick-error 2.0.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"quote 1.0.40 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"rand 0.7.3 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"quote 1.0.39 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"rand 0.8.5 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"rand_chacha 0.2.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"rand_chacha 0.3.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"rand_core 0.5.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"rand_core 0.6.4 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"rand_pcg 0.2.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"random_word 0.5.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"random_word 0.4.3 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"raw-window-handle 0.6.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"rayon 1.10.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"rayon-core 1.12.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
@@ -662,9 +552,8 @@
|
||||
"regex-automata 0.4.9 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"regex-syntax 0.6.29 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"regex-syntax 0.8.5 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"reqwest 0.12.15 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"reqwest 0.12.12 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"rgb 0.8.50 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"roxmltree 0.20.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"rustc-demangle 0.1.24 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"rustls-pemfile 2.2.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"rustls-pki-types 1.11.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
@@ -673,59 +562,46 @@
|
||||
"schannel 0.1.27 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"schemars 0.8.22 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"schemars_derive 0.8.22 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"scoped_threadpool 0.1.9 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"scopeguard 1.2.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"serde 1.0.219 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"serde_derive 1.0.219 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"serde 1.0.218 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"serde_derive 1.0.218 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"serde_derive_internals 0.29.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"serde_json 1.0.140 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"serde_json_lenient 0.2.4 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"serde_urlencoded 0.7.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"serde_variant 0.1.3 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"serde_with 3.12.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"serde_with_macros 3.12.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"serde_yaml 0.8.26 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"serde_yaml 0.9.34+deprecated registry+https://github.com/rust-lang/crates.io-index",
|
||||
"shadow-rs 1.1.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"shadow-rs 1.0.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"sharded-slab 0.1.7 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"shell-words 1.1.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"shellexpand 2.1.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"shlex 1.3.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"simd-adler32 0.3.7 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"simd_helpers 0.1.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"siphasher 0.3.11 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"siphasher 1.0.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"slab 0.4.9 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"smallvec 1.15.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"smallvec 1.14.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"smol_str 0.2.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"socket2 0.5.9 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"socket2 0.5.8 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"stable_deref_trait 1.2.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"stacker 0.1.21 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"static_assertions 1.1.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"strsim 0.11.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"strum 0.27.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"strum_macros 0.27.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"syn 1.0.109 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"syn 2.0.100 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"syn 2.0.99 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"synstructure 0.13.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"sysinfo 0.33.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"sysinfo 0.34.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"tempfile 3.19.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"terminal_size 0.4.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"tempfile 3.17.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"terminal_size 0.4.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"textwrap 0.16.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"thiserror 1.0.69 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"thiserror 2.0.12 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"thiserror-impl 1.0.69 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"thiserror-impl 2.0.12 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"thread_local 1.1.8 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"tiff 0.6.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"tiff 0.9.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"time 0.3.41 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"time-core 0.1.4 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"tokio 1.44.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"time 0.3.37 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"time-core 0.1.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"tokio 1.43.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"tokio-native-tls 0.3.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"tokio-util 0.7.14 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"toml 0.5.11 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"tokio-util 0.7.13 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"tower 0.5.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"tower-layer 0.3.3 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"tower-service 0.3.3 registry+https://github.com/rust-lang/crates.io-index",
|
||||
@@ -740,7 +616,6 @@
|
||||
"ttf-parser 0.25.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"typenum 1.18.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"tz-rs 0.7.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"tzdb_data 0.2.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"uds_windows 1.1.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"unicase 2.8.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"unicode-ident 1.0.18 registry+https://github.com/rust-lang/crates.io-index",
|
||||
@@ -759,66 +634,54 @@
|
||||
"walkdir 2.5.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"want 0.3.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"web-time 1.1.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"webbrowser 1.0.4 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"webbrowser 1.0.3 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"weezl 0.1.8 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"which 7.0.3 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"which 7.0.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"winapi 0.3.9 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"winapi-util 0.1.9 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows 0.57.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows 0.58.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows 0.60.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows 0.61.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-collections 0.1.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-collections 0.2.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-core 0.52.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-core 0.57.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-core 0.58.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-core 0.60.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-core 0.61.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-future 0.1.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-future 0.2.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-icons 0.1.0 git+https://github.com/LGUG2Z/windows-icons?rev=0c9d7ee1b807347c507d3a9862dd007b4d3f4354",
|
||||
"windows-icons 0.1.0 git+https://github.com/LGUG2Z/windows-icons?rev=d67cc9920aa9b4883393e411fb4fa2ddd4c498b5",
|
||||
"windows-implement 0.57.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-implement 0.58.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-implement 0.59.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-implement 0.60.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-interface 0.57.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-interface 0.58.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-interface 0.59.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-link 0.1.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-interface 0.59.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-link 0.1.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-numerics 0.1.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-numerics 0.2.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-registry 0.4.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-registry 0.2.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-result 0.1.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-result 0.2.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-result 0.3.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-result 0.3.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-strings 0.1.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-strings 0.3.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-strings 0.4.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-sys 0.48.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-sys 0.52.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-sys 0.59.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-targets 0.48.5 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-targets 0.52.6 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows-targets 0.53.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows_aarch64_msvc 0.48.5 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows_aarch64_msvc 0.52.6 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows_aarch64_msvc 0.53.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows_i686_msvc 0.48.5 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows_i686_msvc 0.52.6 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows_i686_msvc 0.53.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows_x86_64_msvc 0.48.5 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows_x86_64_msvc 0.52.6 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"windows_x86_64_msvc 0.53.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"winput 0.2.5 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"winreg 0.55.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"winsafe 0.0.19 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"wmi 0.15.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"wmi 0.15.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"write16 1.0.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"xml-rs 0.8.26 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"yaml-rust 0.4.5 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"xml-rs 0.8.25 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"zerocopy 0.7.35 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"zerocopy 0.8.24 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"zerocopy-derive 0.7.35 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"zune-core 0.4.12 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"zune-inflate 0.2.54 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"zune-jpeg 0.4.14 registry+https://github.com/rust-lang/crates.io-index"
|
||||
@@ -828,26 +691,25 @@
|
||||
"MIT-0",
|
||||
[
|
||||
"dunce 1.0.5 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"tzdb_data 0.2.2 registry+https://github.com/rust-lang/crates.io-index"
|
||||
"tzdb_data 0.2.1 registry+https://github.com/rust-lang/crates.io-index"
|
||||
]
|
||||
],
|
||||
[
|
||||
"MPL-2.0",
|
||||
[
|
||||
"option-ext 0.2.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"ramhorns 1.0.1 registry+https://github.com/rust-lang/crates.io-index"
|
||||
"option-ext 0.2.0 registry+https://github.com/rust-lang/crates.io-index"
|
||||
]
|
||||
],
|
||||
[
|
||||
"OFL-1.1",
|
||||
[
|
||||
"epaint_default_fonts 0.31.1 registry+https://github.com/rust-lang/crates.io-index"
|
||||
"epaint_default_fonts 0.31.0 registry+https://github.com/rust-lang/crates.io-index"
|
||||
]
|
||||
],
|
||||
[
|
||||
"Ubuntu-font-1.0",
|
||||
[
|
||||
"epaint_default_fonts 0.31.1 registry+https://github.com/rust-lang/crates.io-index"
|
||||
"epaint_default_fonts 0.31.0 registry+https://github.com/rust-lang/crates.io-index"
|
||||
]
|
||||
],
|
||||
[
|
||||
@@ -856,11 +718,11 @@
|
||||
"icu_collections 1.5.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"icu_locid 1.5.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"icu_locid_transform 1.5.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"icu_locid_transform_data 1.5.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"icu_locid_transform_data 1.5.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"icu_normalizer 1.5.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"icu_normalizer_data 1.5.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"icu_normalizer_data 1.5.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"icu_properties 1.5.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"icu_properties_data 1.5.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"icu_properties_data 1.5.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"icu_provider 1.5.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"icu_provider_macros 1.5.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"litemap 0.7.5 registry+https://github.com/rust-lang/crates.io-index",
|
||||
@@ -891,16 +753,14 @@
|
||||
[
|
||||
"Zlib",
|
||||
[
|
||||
"adler32 1.2.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"bytemuck 1.22.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"bytemuck_derive 1.9.3 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"bytemuck_derive 1.8.1 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"const_format 0.2.34 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"const_format_proc_macros 0.2.34 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"cursor-icon 1.1.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"foldhash 0.1.5 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"foldhash 0.1.4 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"glow 0.16.0 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"miniz_oxide 0.4.4 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"miniz_oxide 0.8.8 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"miniz_oxide 0.8.5 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"raw-window-handle 0.6.2 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"zune-core 0.4.12 registry+https://github.com/rust-lang/crates.io-index",
|
||||
"zune-inflate 0.2.54 registry+https://github.com/rust-lang/crates.io-index",
|
||||
|
||||
@@ -10,8 +10,9 @@ Options:
|
||||
Desired ease function for animation
|
||||
|
||||
[default: linear]
|
||||
[possible values: linear, ease-in-sine, ease-out-sine, ease-in-out-sine, ease-in-quad, ease-out-quad, ease-in-out-quad, ease-in-cubic, ease-in-out-cubic, ease-in-quart, ease-out-quart, ease-in-out-quart, ease-in-quint, ease-out-quint, ease-in-out-quint,
|
||||
ease-in-expo, ease-out-expo, ease-in-out-expo, ease-in-circ, ease-out-circ, ease-in-out-circ, ease-in-back, ease-out-back, ease-in-out-back, ease-in-elastic, ease-out-elastic, ease-in-out-elastic, ease-in-bounce, ease-out-bounce, ease-in-out-bounce]
|
||||
[possible values: linear, ease-in-sine, ease-out-sine, ease-in-out-sine, ease-in-quad, ease-out-quad, ease-in-out-quad, ease-in-cubic, ease-in-out-cubic, ease-in-quart, ease-out-quart,
|
||||
ease-in-out-quart, ease-in-quint, ease-out-quint, ease-in-out-quint, ease-in-expo, ease-out-expo, ease-in-out-expo, ease-in-circ, ease-out-circ, ease-in-out-circ, ease-in-back, ease-out-back,
|
||||
ease-in-out-back, ease-in-elastic, ease-out-elastic, ease-in-out-elastic, ease-in-bounce, ease-out-bounce, ease-in-out-bounce]
|
||||
|
||||
-a, --animation-type <ANIMATION_TYPE>
|
||||
Animation type to apply the style to. If not specified, sets global style
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
# clear-session-float-rules
|
||||
|
||||
```
|
||||
Clear all session float rules
|
||||
|
||||
Usage: komorebic.exe clear-session-float-rules
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
```
|
||||
@@ -1,12 +0,0 @@
|
||||
# data-directory
|
||||
|
||||
```
|
||||
Show the path to komorebi's data directory in %LOCALAPPDATA%
|
||||
|
||||
Usage: komorebic.exe data-directory
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
```
|
||||
@@ -7,7 +7,7 @@ Usage: komorebic.exe query <STATE_QUERY>
|
||||
|
||||
Arguments:
|
||||
<STATE_QUERY>
|
||||
[possible values: focused-monitor-index, focused-workspace-index, focused-container-index, focused-window-index, focused-workspace-name, focused-workspace-layout, version]
|
||||
[possible values: focused-monitor-index, focused-workspace-index, focused-container-index, focused-window-index, focused-workspace-name]
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
# session-float-rule
|
||||
|
||||
```
|
||||
Add a rule to float the foreground window for the rest of this session
|
||||
|
||||
Usage: komorebic.exe session-float-rule
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
```
|
||||
@@ -1,12 +0,0 @@
|
||||
# session-float-rules
|
||||
|
||||
```
|
||||
Show all session float rules
|
||||
|
||||
Usage: komorebic.exe session-float-rules
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
```
|
||||
@@ -1,7 +1,8 @@
|
||||
# toggle-workspace-float-override
|
||||
|
||||
```
|
||||
Enable or disable float override, which makes it so every new window opens in floating mode, for the currently focused workspace. If there was no override value set for the workspace previously it takes the opposite of the global value
|
||||
Enable or disable float override, which makes it so every new window opens in floating mode, for the currently focused workspace. If there was no override value set for the workspace previously it takes
|
||||
the opposite of the global value
|
||||
|
||||
Usage: komorebic.exe toggle-workspace-float-override
|
||||
|
||||
|
||||
@@ -8,8 +8,12 @@ configuration file.
|
||||
```json
|
||||
{
|
||||
"default_workspace_padding": 0,
|
||||
"default_container_padding": -1,
|
||||
"default_container_padding": 0,
|
||||
"border_width": 0,
|
||||
"border_offset": -1
|
||||
}
|
||||
```
|
||||
|
||||
A restart of `komorebi` is required after changing these settings.
|
||||
|
||||
[](https://www.youtube.com/watch?v=6QYLao953XE)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/LGUG2Z/komorebi/v0.1.36/schema.bar.json",
|
||||
"$schema": "https://raw.githubusercontent.com/LGUG2Z/komorebi/v0.1.35/schema.bar.json",
|
||||
"monitor": 0,
|
||||
"font_family": "JetBrains Mono",
|
||||
"theme": {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/LGUG2Z/komorebi/v0.1.36/schema.json",
|
||||
"$schema": "https://raw.githubusercontent.com/LGUG2Z/komorebi/v0.1.35/schema.json",
|
||||
"app_specific_configuration_path": "$Env:USERPROFILE/applications.json",
|
||||
"window_hiding_behaviour": "Cloak",
|
||||
"cross_monitor_move_behaviour": "Insert",
|
||||
@@ -14,6 +14,13 @@
|
||||
"unfocused_border": "Base03",
|
||||
"bar_accent": "Base0D"
|
||||
},
|
||||
"stackbar": {
|
||||
"height": 40,
|
||||
"mode": "OnStack",
|
||||
"tabs": {
|
||||
"width": 300
|
||||
}
|
||||
},
|
||||
"monitors": [
|
||||
{
|
||||
"workspaces": [
|
||||
|
||||
@@ -132,19 +132,3 @@ running `komorebic stop` and `komorebic start`.
|
||||
We can see the _komorebi_ state is no longer associated with the previous
|
||||
device: `null`, suggesting an issue when the display resumes from a suspended
|
||||
state.
|
||||
|
||||
## Komorebi Bar does not render transparency on Nvidia GPUs
|
||||
|
||||
Users with Nvidia GPUs may have issues with transparency on the Komorebi Bar.
|
||||
|
||||
To solve this the user can do the following:
|
||||
1. Open the Nvidia Control Panel
|
||||
2. On the left menu tree, under "3D Settings", select "Manage 3D Settings"
|
||||
3. Select the "Program Settings" tab
|
||||
4. Press the "Add" button and select "komorebi-bar"
|
||||
5. Under "3. Specify the settings for this program:", find the feature labelled, "OpenGL GDI compatibility"
|
||||
6. Change the setting to "Prefer compatibility"
|
||||
7. At the bottom of the window select "Apply"
|
||||
8. Restart the Komorebi Bar with "komorebic stop --bar; komorebic start --bar"
|
||||
|
||||
This should resolve the issue and your Komorebi Bar should render with the proper transparency.
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
alt + o : taskkill /f /im whkd.exe; Start-Process whkd -WindowStyle hidden # if shell is pwsh / powershell
|
||||
alt + shift + o : komorebic reload-configuration
|
||||
|
||||
alt + i : komorebic toggle-shortcuts
|
||||
|
||||
# App shortcuts - these require shell to be pwsh / powershell
|
||||
# The apps will be focused if open, or launched if not open
|
||||
# alt + f : if ($wshell.AppActivate('Firefox') -eq $False) { start firefox }
|
||||
|
||||
7
justfile
7
justfile
@@ -31,7 +31,7 @@ install:
|
||||
just install-targets komorebic komorebic-no-console komorebi komorebi-bar komorebi-gui
|
||||
|
||||
install-with-jsonschema:
|
||||
just install-targets-with-jsonschema komorebic komorebic-no-console komorebi komorebi-bar komorebi-gui komorebi-shortcuts
|
||||
just install-targets-with-jsonschema komorebic komorebic-no-console komorebi komorebi-bar komorebi-gui
|
||||
|
||||
build-targets *targets:
|
||||
"{{ targets }}" -split ' ' | ForEach-Object { just build-target $_ }
|
||||
@@ -40,7 +40,7 @@ build-target target:
|
||||
cargo +stable build --package {{ target }} --locked --release --no-default-features
|
||||
|
||||
build:
|
||||
just build-targets komorebic komorebic-no-console komorebi komorebi-bar komorebi-gui komorebi-shortcuts
|
||||
just build-targets komorebic komorebic-no-console komorebi komorebi-bar komorebi-gui
|
||||
|
||||
copy-target target:
|
||||
cp .\target\release\{{ target }}.exe $Env:USERPROFILE\.cargo\bin
|
||||
@@ -88,6 +88,3 @@ schemagen:
|
||||
generate-schema-doc ./schema.json --config template_name=js_offline --config minify=false ./static-config-docs/
|
||||
generate-schema-doc ./schema.bar.json --config template_name=js_offline --config minify=false ./bar-config-docs/
|
||||
mv ./bar-config-docs/schema.bar.html ./bar-config-docs/schema.html
|
||||
|
||||
depgen:
|
||||
cargo deny list --format json | jq 'del(.unlicensed)' > dependencies.json
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "komorebi-bar"
|
||||
version = "0.1.37"
|
||||
version = "0.1.36"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
@@ -22,7 +22,7 @@ font-loader = "0.11"
|
||||
hotwatch = { workspace = true }
|
||||
image = "0.25"
|
||||
lazy_static = { workspace = true }
|
||||
netdev = "0.34"
|
||||
netdev = "0.33"
|
||||
num = "0.4"
|
||||
num-derive = "0.4"
|
||||
num-traits = "0.2"
|
||||
@@ -35,7 +35,6 @@ starship-battery = "0.10"
|
||||
sysinfo = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
tracing-subscriber = { workspace = true }
|
||||
which = { workspace = true }
|
||||
windows = { workspace = true }
|
||||
windows-core = { workspace = true }
|
||||
windows-icons = { git = "https://github.com/LGUG2Z/windows-icons", rev = "0c9d7ee1b807347c507d3a9862dd007b4d3f4354" }
|
||||
@@ -43,4 +42,4 @@ windows-icons-fallback = { package = "windows-icons", git = "https://github.com/
|
||||
|
||||
[features]
|
||||
default = ["schemars"]
|
||||
schemars = ["dep:schemars", "komorebi-client/schemars", "komorebi-themes/schemars"]
|
||||
schemars = ["dep:schemars"]
|
||||
|
||||
@@ -14,8 +14,6 @@ use crate::widgets::komorebi::KomorebiNotificationState;
|
||||
use crate::widgets::widget::BarWidget;
|
||||
use crate::widgets::widget::WidgetConfig;
|
||||
use crate::KomorebiEvent;
|
||||
use crate::AUTO_SELECT_FILL_COLOUR;
|
||||
use crate::AUTO_SELECT_TEXT_COLOUR;
|
||||
use crate::BAR_HEIGHT;
|
||||
use crate::DEFAULT_PADDING;
|
||||
use crate::MAX_LABEL_WIDTH;
|
||||
@@ -45,16 +43,12 @@ use eframe::egui::Vec2;
|
||||
use eframe::egui::Visuals;
|
||||
use font_loader::system_fonts;
|
||||
use font_loader::system_fonts::FontPropertyBuilder;
|
||||
use komorebi_client::Colour;
|
||||
use komorebi_client::KomorebiTheme;
|
||||
use komorebi_client::MonitorNotification;
|
||||
use komorebi_client::NotificationEvent;
|
||||
use komorebi_client::PathExt;
|
||||
use komorebi_client::SocketMessage;
|
||||
use komorebi_client::VirtualDesktopNotification;
|
||||
use komorebi_themes::catppuccin_egui;
|
||||
use komorebi_themes::Base16Value;
|
||||
use komorebi_themes::Base16Wrapper;
|
||||
use komorebi_themes::Catppuccin;
|
||||
use komorebi_themes::CatppuccinValue;
|
||||
use std::cell::RefCell;
|
||||
@@ -93,86 +87,75 @@ pub fn apply_theme(
|
||||
grouping: Option<Grouping>,
|
||||
render_config: Rc<RefCell<RenderConfig>>,
|
||||
) {
|
||||
let (auto_select_fill, auto_select_text) = match theme {
|
||||
match theme {
|
||||
KomobarTheme::Catppuccin {
|
||||
name: catppuccin,
|
||||
accent: catppuccin_value,
|
||||
auto_select_fill: catppuccin_auto_select_fill,
|
||||
auto_select_text: catppuccin_auto_select_text,
|
||||
} => {
|
||||
match catppuccin {
|
||||
Catppuccin::Frappe => {
|
||||
catppuccin_egui::set_theme(ctx, catppuccin_egui::FRAPPE);
|
||||
let catppuccin_value = catppuccin_value.unwrap_or_default();
|
||||
let accent = catppuccin_value.color32(catppuccin.as_theme());
|
||||
} => match catppuccin {
|
||||
Catppuccin::Frappe => {
|
||||
catppuccin_egui::set_theme(ctx, catppuccin_egui::FRAPPE);
|
||||
let catppuccin_value = catppuccin_value.unwrap_or_default();
|
||||
let accent = catppuccin_value.color32(catppuccin.as_theme());
|
||||
|
||||
ctx.style_mut(|style| {
|
||||
style.visuals.selection.stroke.color = accent;
|
||||
style.visuals.widgets.hovered.fg_stroke.color = accent;
|
||||
style.visuals.widgets.active.fg_stroke.color = accent;
|
||||
style.visuals.override_text_color = None;
|
||||
});
|
||||
ctx.style_mut(|style| {
|
||||
style.visuals.selection.stroke.color = accent;
|
||||
style.visuals.widgets.hovered.fg_stroke.color = accent;
|
||||
style.visuals.widgets.active.fg_stroke.color = accent;
|
||||
style.visuals.override_text_color = None;
|
||||
});
|
||||
|
||||
bg_color.replace(catppuccin_egui::FRAPPE.base);
|
||||
}
|
||||
Catppuccin::Latte => {
|
||||
catppuccin_egui::set_theme(ctx, catppuccin_egui::LATTE);
|
||||
let catppuccin_value = catppuccin_value.unwrap_or_default();
|
||||
let accent = catppuccin_value.color32(catppuccin.as_theme());
|
||||
|
||||
ctx.style_mut(|style| {
|
||||
style.visuals.selection.stroke.color = accent;
|
||||
style.visuals.widgets.hovered.fg_stroke.color = accent;
|
||||
style.visuals.widgets.active.fg_stroke.color = accent;
|
||||
style.visuals.override_text_color = None;
|
||||
});
|
||||
|
||||
bg_color.replace(catppuccin_egui::LATTE.base);
|
||||
}
|
||||
Catppuccin::Macchiato => {
|
||||
catppuccin_egui::set_theme(ctx, catppuccin_egui::MACCHIATO);
|
||||
let catppuccin_value = catppuccin_value.unwrap_or_default();
|
||||
let accent = catppuccin_value.color32(catppuccin.as_theme());
|
||||
|
||||
ctx.style_mut(|style| {
|
||||
style.visuals.selection.stroke.color = accent;
|
||||
style.visuals.widgets.hovered.fg_stroke.color = accent;
|
||||
style.visuals.widgets.active.fg_stroke.color = accent;
|
||||
style.visuals.override_text_color = None;
|
||||
});
|
||||
|
||||
bg_color.replace(catppuccin_egui::MACCHIATO.base);
|
||||
}
|
||||
Catppuccin::Mocha => {
|
||||
catppuccin_egui::set_theme(ctx, catppuccin_egui::MOCHA);
|
||||
let catppuccin_value = catppuccin_value.unwrap_or_default();
|
||||
let accent = catppuccin_value.color32(catppuccin.as_theme());
|
||||
|
||||
ctx.style_mut(|style| {
|
||||
style.visuals.selection.stroke.color = accent;
|
||||
style.visuals.widgets.hovered.fg_stroke.color = accent;
|
||||
style.visuals.widgets.active.fg_stroke.color = accent;
|
||||
style.visuals.override_text_color = None;
|
||||
});
|
||||
|
||||
bg_color.replace(catppuccin_egui::MOCHA.base);
|
||||
}
|
||||
bg_color.replace(catppuccin_egui::FRAPPE.base);
|
||||
}
|
||||
Catppuccin::Latte => {
|
||||
catppuccin_egui::set_theme(ctx, catppuccin_egui::LATTE);
|
||||
let catppuccin_value = catppuccin_value.unwrap_or_default();
|
||||
let accent = catppuccin_value.color32(catppuccin.as_theme());
|
||||
|
||||
(
|
||||
catppuccin_auto_select_fill.map(|c| c.color32(catppuccin.as_theme())),
|
||||
catppuccin_auto_select_text.map(|c| c.color32(catppuccin.as_theme())),
|
||||
)
|
||||
}
|
||||
ctx.style_mut(|style| {
|
||||
style.visuals.selection.stroke.color = accent;
|
||||
style.visuals.widgets.hovered.fg_stroke.color = accent;
|
||||
style.visuals.widgets.active.fg_stroke.color = accent;
|
||||
style.visuals.override_text_color = None;
|
||||
});
|
||||
|
||||
bg_color.replace(catppuccin_egui::LATTE.base);
|
||||
}
|
||||
Catppuccin::Macchiato => {
|
||||
catppuccin_egui::set_theme(ctx, catppuccin_egui::MACCHIATO);
|
||||
let catppuccin_value = catppuccin_value.unwrap_or_default();
|
||||
let accent = catppuccin_value.color32(catppuccin.as_theme());
|
||||
|
||||
ctx.style_mut(|style| {
|
||||
style.visuals.selection.stroke.color = accent;
|
||||
style.visuals.widgets.hovered.fg_stroke.color = accent;
|
||||
style.visuals.widgets.active.fg_stroke.color = accent;
|
||||
style.visuals.override_text_color = None;
|
||||
});
|
||||
|
||||
bg_color.replace(catppuccin_egui::MACCHIATO.base);
|
||||
}
|
||||
Catppuccin::Mocha => {
|
||||
catppuccin_egui::set_theme(ctx, catppuccin_egui::MOCHA);
|
||||
let catppuccin_value = catppuccin_value.unwrap_or_default();
|
||||
let accent = catppuccin_value.color32(catppuccin.as_theme());
|
||||
|
||||
ctx.style_mut(|style| {
|
||||
style.visuals.selection.stroke.color = accent;
|
||||
style.visuals.widgets.hovered.fg_stroke.color = accent;
|
||||
style.visuals.widgets.active.fg_stroke.color = accent;
|
||||
style.visuals.override_text_color = None;
|
||||
});
|
||||
|
||||
bg_color.replace(catppuccin_egui::MOCHA.base);
|
||||
}
|
||||
},
|
||||
KomobarTheme::Base16 {
|
||||
name: base16,
|
||||
accent: base16_value,
|
||||
auto_select_fill: base16_auto_select_fill,
|
||||
auto_select_text: base16_auto_select_text,
|
||||
} => {
|
||||
ctx.set_style(base16.style());
|
||||
let base16_value = base16_value.unwrap_or_default();
|
||||
let accent = base16_value.color32(Base16Wrapper::Base16(base16));
|
||||
let accent = base16_value.color32(base16);
|
||||
|
||||
ctx.style_mut(|style| {
|
||||
style.visuals.selection.stroke.color = accent;
|
||||
@@ -181,46 +164,8 @@ pub fn apply_theme(
|
||||
});
|
||||
|
||||
bg_color.replace(base16.background());
|
||||
|
||||
(
|
||||
base16_auto_select_fill.map(|c| c.color32(Base16Wrapper::Base16(base16))),
|
||||
base16_auto_select_text.map(|c| c.color32(Base16Wrapper::Base16(base16))),
|
||||
)
|
||||
}
|
||||
KomobarTheme::Custom {
|
||||
colours,
|
||||
accent: base16_value,
|
||||
auto_select_fill: base16_auto_select_fill,
|
||||
auto_select_text: base16_auto_select_text,
|
||||
} => {
|
||||
let background = colours.background();
|
||||
ctx.set_style(colours.style());
|
||||
let base16_value = base16_value.unwrap_or_default();
|
||||
let accent = base16_value.color32(Base16Wrapper::Custom(colours.clone()));
|
||||
|
||||
ctx.style_mut(|style| {
|
||||
style.visuals.selection.stroke.color = accent;
|
||||
style.visuals.widgets.hovered.fg_stroke.color = accent;
|
||||
style.visuals.widgets.active.fg_stroke.color = accent;
|
||||
});
|
||||
|
||||
bg_color.replace(background);
|
||||
|
||||
(
|
||||
base16_auto_select_fill.map(|c| c.color32(Base16Wrapper::Custom(colours.clone()))),
|
||||
base16_auto_select_text.map(|c| c.color32(Base16Wrapper::Custom(colours.clone()))),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
AUTO_SELECT_FILL_COLOUR.store(
|
||||
auto_select_fill.map_or(0, |c| Colour::from(c).into()),
|
||||
Ordering::SeqCst,
|
||||
);
|
||||
AUTO_SELECT_TEXT_COLOUR.store(
|
||||
auto_select_text.map_or(0, |c| Colour::from(c).into()),
|
||||
Ordering::SeqCst,
|
||||
);
|
||||
}
|
||||
|
||||
// Apply transparency_alpha
|
||||
let theme_color = *bg_color.borrow();
|
||||
@@ -486,11 +431,11 @@ impl Komobar {
|
||||
}
|
||||
|
||||
fn try_apply_theme(&mut self, ctx: &Context) {
|
||||
match &self.config.theme {
|
||||
match self.config.theme {
|
||||
Some(theme) => {
|
||||
apply_theme(
|
||||
ctx,
|
||||
theme.clone(),
|
||||
theme,
|
||||
self.bg_color.clone(),
|
||||
self.bg_color_with_alpha.clone(),
|
||||
self.config.transparency_alpha,
|
||||
@@ -502,16 +447,13 @@ impl Komobar {
|
||||
let home_dir: PathBuf = std::env::var("KOMOREBI_CONFIG_HOME").map_or_else(
|
||||
|_| dirs::home_dir().expect("there is no home directory"),
|
||||
|home_path| {
|
||||
let home = home_path.replace_env();
|
||||
|
||||
assert!(
|
||||
home.is_dir(),
|
||||
"$Env:KOMOREBI_CONFIG_HOME is set to '{}', which is not a valid directory",
|
||||
home_path
|
||||
);
|
||||
|
||||
home
|
||||
let home = PathBuf::from(&home_path);
|
||||
|
||||
if home.as_path().is_dir() {
|
||||
home
|
||||
} else {
|
||||
panic!("$Env:KOMOREBI_CONFIG_HOME is set to '{home_path}', which is not a valid directory");
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
@@ -521,26 +463,6 @@ impl Komobar {
|
||||
match komorebi_client::StaticConfig::read(&config) {
|
||||
Ok(config) => {
|
||||
if let Some(theme) = config.theme {
|
||||
let stack_accent = match theme {
|
||||
KomorebiTheme::Catppuccin {
|
||||
name, stack_border, ..
|
||||
} => stack_border
|
||||
.unwrap_or(CatppuccinValue::Green)
|
||||
.color32(name.as_theme()),
|
||||
KomorebiTheme::Base16 {
|
||||
name, stack_border, ..
|
||||
} => stack_border
|
||||
.unwrap_or(Base16Value::Base0B)
|
||||
.color32(Base16Wrapper::Base16(name)),
|
||||
KomorebiTheme::Custom {
|
||||
ref colours,
|
||||
stack_border,
|
||||
..
|
||||
} => stack_border
|
||||
.unwrap_or(Base16Value::Base0B)
|
||||
.color32(Base16Wrapper::Custom(colours.clone())),
|
||||
};
|
||||
|
||||
apply_theme(
|
||||
ctx,
|
||||
KomobarTheme::from(theme),
|
||||
@@ -551,6 +473,17 @@ impl Komobar {
|
||||
self.render_config.clone(),
|
||||
);
|
||||
|
||||
let stack_accent = match theme {
|
||||
KomorebiTheme::Catppuccin {
|
||||
name, stack_border, ..
|
||||
} => stack_border
|
||||
.unwrap_or(CatppuccinValue::Green)
|
||||
.color32(name.as_theme()),
|
||||
KomorebiTheme::Base16 {
|
||||
name, stack_border, ..
|
||||
} => stack_border.unwrap_or(Base16Value::Base0B).color32(name),
|
||||
};
|
||||
|
||||
if let Some(state) = &self.komorebi_notification_state {
|
||||
state.borrow_mut().stack_accent = Some(stack_accent);
|
||||
}
|
||||
@@ -759,30 +692,6 @@ impl eframe::App for Komobar {
|
||||
self.monitor_index = monitor_index;
|
||||
let mut should_apply_config = false;
|
||||
|
||||
match notification.event {
|
||||
NotificationEvent::VirtualDesktop(
|
||||
VirtualDesktopNotification::EnteredAssociatedVirtualDesktop,
|
||||
) => {
|
||||
tracing::debug!(
|
||||
"back on komorebi's associated virtual desktop - restoring bar"
|
||||
);
|
||||
if let Some(hwnd) = self.hwnd {
|
||||
komorebi_client::WindowsApi::restore_window(hwnd);
|
||||
}
|
||||
}
|
||||
NotificationEvent::VirtualDesktop(
|
||||
VirtualDesktopNotification::LeftAssociatedVirtualDesktop,
|
||||
) => {
|
||||
tracing::debug!(
|
||||
"no longer on komorebi's associated virtual desktop - minimizing bar"
|
||||
);
|
||||
if let Some(hwnd) = self.hwnd {
|
||||
komorebi_client::WindowsApi::minimize_window(hwnd);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if self.monitor_index.is_none()
|
||||
|| self
|
||||
.monitor_index
|
||||
@@ -873,7 +782,7 @@ impl eframe::App for Komobar {
|
||||
self.bg_color_with_alpha.clone(),
|
||||
self.config.transparency_alpha,
|
||||
self.config.grouping,
|
||||
self.config.theme.clone(),
|
||||
self.config.theme,
|
||||
self.render_config.clone(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ use std::path::PathBuf;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
/// The `komorebi.bar.json` configuration file reference for `v0.1.37`
|
||||
/// The `komorebi.bar.json` configuration file reference for `v0.1.36`
|
||||
pub struct KomobarConfig {
|
||||
/// Bar height (default: 50)
|
||||
pub height: Option<f32>,
|
||||
@@ -373,7 +373,7 @@ impl From<Position> for Pos2 {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
#[serde(tag = "palette")]
|
||||
pub enum KomobarTheme {
|
||||
@@ -382,24 +382,12 @@ pub enum KomobarTheme {
|
||||
/// Name of the Catppuccin theme (theme previews: https://github.com/catppuccin/catppuccin)
|
||||
name: komorebi_themes::Catppuccin,
|
||||
accent: Option<komorebi_themes::CatppuccinValue>,
|
||||
auto_select_fill: Option<komorebi_themes::CatppuccinValue>,
|
||||
auto_select_text: Option<komorebi_themes::CatppuccinValue>,
|
||||
},
|
||||
/// A theme from base16-egui-themes
|
||||
Base16 {
|
||||
/// Name of the Base16 theme (theme previews: https://tinted-theming.github.io/tinted-gallery/)
|
||||
name: komorebi_themes::Base16,
|
||||
accent: Option<komorebi_themes::Base16Value>,
|
||||
auto_select_fill: Option<komorebi_themes::Base16Value>,
|
||||
auto_select_text: Option<komorebi_themes::Base16Value>,
|
||||
},
|
||||
/// A custom Base16 theme
|
||||
Custom {
|
||||
/// Colours of the custom Base16 theme palette
|
||||
colours: Box<komorebi_themes::Base16ColourPalette>,
|
||||
accent: Option<komorebi_themes::Base16Value>,
|
||||
auto_select_fill: Option<komorebi_themes::Base16Value>,
|
||||
auto_select_text: Option<komorebi_themes::Base16Value>,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -411,26 +399,12 @@ impl From<KomorebiTheme> for KomobarTheme {
|
||||
} => Self::Catppuccin {
|
||||
name,
|
||||
accent: bar_accent,
|
||||
auto_select_fill: None,
|
||||
auto_select_text: None,
|
||||
},
|
||||
KomorebiTheme::Base16 {
|
||||
name, bar_accent, ..
|
||||
} => Self::Base16 {
|
||||
name,
|
||||
accent: bar_accent,
|
||||
auto_select_fill: None,
|
||||
auto_select_text: None,
|
||||
},
|
||||
KomorebiTheme::Custom {
|
||||
colours,
|
||||
bar_accent,
|
||||
..
|
||||
} => Self::Custom {
|
||||
colours,
|
||||
accent: bar_accent,
|
||||
auto_select_fill: None,
|
||||
auto_select_text: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,6 @@ use font_loader::system_fonts;
|
||||
use hotwatch::EventKind;
|
||||
use hotwatch::Hotwatch;
|
||||
use image::RgbaImage;
|
||||
use komorebi_client::replace_env_in_path;
|
||||
use komorebi_client::PathExt;
|
||||
use komorebi_client::SocketMessage;
|
||||
use komorebi_client::SubscribeOptions;
|
||||
use std::collections::HashMap;
|
||||
@@ -25,7 +23,6 @@ use std::io::BufReader;
|
||||
use std::io::Read;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::atomic::AtomicI32;
|
||||
use std::sync::atomic::AtomicU32;
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::LazyLock;
|
||||
@@ -50,9 +47,6 @@ pub static MONITOR_INDEX: AtomicUsize = AtomicUsize::new(0);
|
||||
pub static BAR_HEIGHT: f32 = 50.0;
|
||||
pub static DEFAULT_PADDING: f32 = 10.0;
|
||||
|
||||
pub static AUTO_SELECT_FILL_COLOUR: AtomicU32 = AtomicU32::new(0);
|
||||
pub static AUTO_SELECT_TEXT_COLOUR: AtomicU32 = AtomicU32::new(0);
|
||||
|
||||
pub static ICON_CACHE: LazyLock<Mutex<HashMap<isize, RgbaImage>>> =
|
||||
LazyLock::new(|| Mutex::new(HashMap::new()));
|
||||
|
||||
@@ -67,7 +61,6 @@ struct Opts {
|
||||
fonts: bool,
|
||||
/// Path to a JSON or YAML configuration file
|
||||
#[clap(short, long)]
|
||||
#[clap(value_parser = replace_env_in_path)]
|
||||
config: Option<PathBuf>,
|
||||
/// Write an example komorebi.bar.json to disk
|
||||
#[clap(long)]
|
||||
@@ -162,15 +155,13 @@ fn main() -> color_eyre::Result<()> {
|
||||
let home_dir: PathBuf = std::env::var("KOMOREBI_CONFIG_HOME").map_or_else(
|
||||
|_| dirs::home_dir().expect("there is no home directory"),
|
||||
|home_path| {
|
||||
let home = home_path.replace_env();
|
||||
let home = PathBuf::from(&home_path);
|
||||
|
||||
assert!(
|
||||
home.is_dir(),
|
||||
"$Env:KOMOREBI_CONFIG_HOME is set to '{}', which is not a valid directory",
|
||||
home_path
|
||||
);
|
||||
|
||||
home
|
||||
if home.as_path().is_dir() {
|
||||
home
|
||||
} else {
|
||||
panic!("$Env:KOMOREBI_CONFIG_HOME is set to '{home_path}', which is not a valid directory");
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
@@ -179,7 +170,7 @@ fn main() -> color_eyre::Result<()> {
|
||||
std::fs::write(home_dir.join("komorebi.bar.json"), komorebi_bar_json)?;
|
||||
println!(
|
||||
"Example komorebi.bar.json file written to {}",
|
||||
home_dir.display()
|
||||
home_dir.as_path().display()
|
||||
);
|
||||
|
||||
std::process::exit(0);
|
||||
@@ -187,11 +178,16 @@ fn main() -> color_eyre::Result<()> {
|
||||
|
||||
let default_config_path = home_dir.join("komorebi.bar.json");
|
||||
|
||||
let config_path = opts.config.or_else(|| {
|
||||
default_config_path
|
||||
.is_file()
|
||||
.then_some(default_config_path.clone())
|
||||
});
|
||||
let config_path = opts.config.map_or_else(
|
||||
|| {
|
||||
if !default_config_path.is_file() {
|
||||
None
|
||||
} else {
|
||||
Some(default_config_path.clone())
|
||||
}
|
||||
},
|
||||
Option::from,
|
||||
);
|
||||
|
||||
let mut config = match config_path {
|
||||
None => {
|
||||
@@ -201,14 +197,17 @@ fn main() -> color_eyre::Result<()> {
|
||||
std::fs::write(&default_config_path, komorebi_bar_json)?;
|
||||
tracing::info!(
|
||||
"created example configuration file: {}",
|
||||
default_config_path.display()
|
||||
default_config_path.as_path().display()
|
||||
);
|
||||
|
||||
KomobarConfig::read(&default_config_path)?
|
||||
}
|
||||
Some(ref config) => {
|
||||
if !opts.aliases {
|
||||
tracing::info!("found configuration file: {}", config.display());
|
||||
tracing::info!(
|
||||
"found configuration file: {}",
|
||||
config.as_path().to_string_lossy()
|
||||
);
|
||||
}
|
||||
|
||||
KomobarConfig::read(config)?
|
||||
@@ -308,7 +307,10 @@ fn main() -> color_eyre::Result<()> {
|
||||
hotwatch.watch(config_path, move |event| match event.kind {
|
||||
EventKind::Modify(_) | EventKind::Remove(_) => match KomobarConfig::read(&config_path_cl) {
|
||||
Ok(updated) => {
|
||||
tracing::info!("configuration file updated: {}", config_path_cl.display());
|
||||
tracing::info!(
|
||||
"configuration file updated: {}",
|
||||
config_path_cl.as_path().to_string_lossy()
|
||||
);
|
||||
|
||||
if let Err(error) = tx_config.send(updated) {
|
||||
tracing::error!("could not send configuration update to gui: {error}")
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
use crate::bar::Alignment;
|
||||
use crate::config::KomobarConfig;
|
||||
use crate::config::MonitorConfigOrIndex;
|
||||
use crate::AUTO_SELECT_FILL_COLOUR;
|
||||
use crate::AUTO_SELECT_TEXT_COLOUR;
|
||||
use eframe::egui::Color32;
|
||||
use eframe::egui::Context;
|
||||
use eframe::egui::CornerRadius;
|
||||
@@ -13,11 +11,8 @@ use eframe::egui::Margin;
|
||||
use eframe::egui::Shadow;
|
||||
use eframe::egui::TextStyle;
|
||||
use eframe::egui::Ui;
|
||||
use komorebi_client::Colour;
|
||||
use komorebi_client::Rgb;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::num::NonZeroU32;
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
@@ -60,10 +55,6 @@ pub struct RenderConfig {
|
||||
pub icon_font_id: FontId,
|
||||
/// Show all icons on the workspace section of the Komorebi widget
|
||||
pub show_all_icons: bool,
|
||||
/// Background color of the selected frame
|
||||
pub auto_select_fill: Option<Color32>,
|
||||
/// Text color of the selected frame
|
||||
pub auto_select_text: Option<Color32>,
|
||||
}
|
||||
|
||||
pub trait RenderExt {
|
||||
@@ -117,10 +108,6 @@ impl RenderExt for &KomobarConfig {
|
||||
text_font_id,
|
||||
icon_font_id,
|
||||
show_all_icons,
|
||||
auto_select_fill: NonZeroU32::new(AUTO_SELECT_FILL_COLOUR.load(Ordering::SeqCst))
|
||||
.map(|c| Colour::Rgb(Rgb::from(c.get())).into()),
|
||||
auto_select_text: NonZeroU32::new(AUTO_SELECT_TEXT_COLOUR.load(Ordering::SeqCst))
|
||||
.map(|c| Colour::Rgb(Rgb::from(c.get())).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -146,8 +133,6 @@ impl RenderConfig {
|
||||
text_font_id: FontId::default(),
|
||||
icon_font_id: FontId::default(),
|
||||
show_all_icons: false,
|
||||
auto_select_fill: None,
|
||||
auto_select_text: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,29 +10,15 @@ use eframe::egui::Ui;
|
||||
/// Same as SelectableLabel, but supports all content
|
||||
pub struct SelectableFrame {
|
||||
selected: bool,
|
||||
selected_fill: Option<Color32>,
|
||||
}
|
||||
|
||||
impl SelectableFrame {
|
||||
pub fn new(selected: bool) -> Self {
|
||||
Self {
|
||||
selected,
|
||||
selected_fill: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_auto(selected: bool, selected_fill: Option<Color32>) -> Self {
|
||||
Self {
|
||||
selected,
|
||||
selected_fill,
|
||||
}
|
||||
Self { selected }
|
||||
}
|
||||
|
||||
pub fn show<R>(self, ui: &mut Ui, add_contents: impl FnOnce(&mut Ui) -> R) -> Response {
|
||||
let Self {
|
||||
selected,
|
||||
selected_fill,
|
||||
} = self;
|
||||
let Self { selected } = self;
|
||||
|
||||
Frame::NONE
|
||||
.show(ui, |ui| {
|
||||
@@ -46,16 +32,7 @@ impl SelectableFrame {
|
||||
);
|
||||
|
||||
// since the stroke is drawn inside the frame, we always reserve space for it
|
||||
if selected && response.hovered() {
|
||||
let visuals = ui.style().interact_selectable(&response, selected);
|
||||
|
||||
Frame::NONE
|
||||
.stroke(Stroke::new(1.0, visuals.bg_stroke.color))
|
||||
.corner_radius(visuals.corner_radius)
|
||||
.fill(selected_fill.unwrap_or(visuals.bg_fill))
|
||||
.inner_margin(inner_margin)
|
||||
.show(ui, add_contents);
|
||||
} else if response.hovered() || response.highlighted() || response.has_focus() {
|
||||
if response.hovered() || response.highlighted() || response.has_focus() {
|
||||
let visuals = ui.style().interact_selectable(&response, selected);
|
||||
|
||||
Frame::NONE
|
||||
@@ -70,7 +47,7 @@ impl SelectableFrame {
|
||||
Frame::NONE
|
||||
.stroke(Stroke::new(1.0, visuals.bg_fill))
|
||||
.corner_radius(visuals.corner_radius)
|
||||
.fill(selected_fill.unwrap_or(visuals.bg_fill))
|
||||
.fill(visuals.bg_fill)
|
||||
.inner_margin(inner_margin)
|
||||
.show(ui, add_contents);
|
||||
} else {
|
||||
|
||||
@@ -1,356 +0,0 @@
|
||||
use super::komorebi::img_to_texture;
|
||||
use crate::render::RenderConfig;
|
||||
use crate::selected_frame::SelectableFrame;
|
||||
use crate::widgets::widget::BarWidget;
|
||||
use eframe::egui::vec2;
|
||||
use eframe::egui::Color32;
|
||||
use eframe::egui::Context;
|
||||
use eframe::egui::CornerRadius;
|
||||
use eframe::egui::FontId;
|
||||
use eframe::egui::Frame;
|
||||
use eframe::egui::Image;
|
||||
use eframe::egui::Label;
|
||||
use eframe::egui::Margin;
|
||||
use eframe::egui::RichText;
|
||||
use eframe::egui::Sense;
|
||||
use eframe::egui::Stroke;
|
||||
use eframe::egui::StrokeKind;
|
||||
use eframe::egui::Ui;
|
||||
use eframe::egui::Vec2;
|
||||
use image::DynamicImage;
|
||||
use image::RgbaImage;
|
||||
use komorebi_client::PathExt;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use std::time::Duration;
|
||||
use std::time::Instant;
|
||||
use tracing;
|
||||
use which::which;
|
||||
|
||||
/// Minimum interval between consecutive application launches to prevent accidental spamming.
|
||||
const MIN_LAUNCH_INTERVAL: Duration = Duration::from_millis(800);
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub struct ApplicationsConfig {
|
||||
/// Enables or disables the applications widget.
|
||||
pub enable: bool,
|
||||
/// Whether to show the launch command on hover (optional).
|
||||
/// Could be overridden per application. Defaults to `false` if not set.
|
||||
pub show_command_on_hover: Option<bool>,
|
||||
/// Horizontal spacing between application buttons.
|
||||
pub spacing: Option<f32>,
|
||||
/// Default display format for all applications (optional).
|
||||
/// Could be overridden per application. Defaults to `Icon`.
|
||||
pub display: Option<DisplayFormat>,
|
||||
/// List of configured applications to display.
|
||||
pub items: Vec<AppConfig>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub struct AppConfig {
|
||||
/// Whether to enable this application button (optional).
|
||||
/// Inherits from the global `Applications` setting if omitted.
|
||||
pub enable: Option<bool>,
|
||||
/// Whether to show the launch command on hover (optional).
|
||||
/// Inherits from the global `Applications` setting if omitted.
|
||||
pub show_command_on_hover: Option<bool>,
|
||||
/// Display name of the application.
|
||||
pub name: String,
|
||||
/// Optional icon: a path to an image or a text-based glyph (e.g., from Nerd Fonts).
|
||||
/// If not set, and if the `command` is a path to an executable, an icon might be extracted from it.
|
||||
/// Note: glyphs require a compatible `font_family`.
|
||||
pub icon: Option<String>,
|
||||
/// Command to execute (e.g. path to the application or shell command).
|
||||
pub command: String,
|
||||
/// Display format for this application button (optional). Overrides global format if set.
|
||||
pub display: Option<DisplayFormat>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Default)]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub enum DisplayFormat {
|
||||
/// Show only the application icon.
|
||||
#[default]
|
||||
Icon,
|
||||
/// Show only the application name as text.
|
||||
Text,
|
||||
/// Show both the application icon and name.
|
||||
IconAndText,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Applications {
|
||||
/// Whether the applications widget is enabled.
|
||||
pub enable: bool,
|
||||
/// Horizontal spacing between application buttons.
|
||||
pub spacing: Option<f32>,
|
||||
/// Applications to be rendered in the UI.
|
||||
pub items: Vec<App>,
|
||||
}
|
||||
|
||||
impl BarWidget for Applications {
|
||||
fn render(&mut self, ctx: &Context, ui: &mut Ui, config: &mut RenderConfig) {
|
||||
if !self.enable {
|
||||
return;
|
||||
}
|
||||
|
||||
let icon_config = IconConfig {
|
||||
font_id: config.icon_font_id.clone(),
|
||||
size: config.icon_font_id.size,
|
||||
color: ctx.style().visuals.selection.stroke.color,
|
||||
};
|
||||
|
||||
if let Some(spacing) = self.spacing {
|
||||
ui.spacing_mut().item_spacing.x = spacing;
|
||||
}
|
||||
|
||||
config.apply_on_widget(false, ui, |ui| {
|
||||
for app in &mut self.items {
|
||||
app.render(ctx, ui, &icon_config);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&ApplicationsConfig> for Applications {
|
||||
fn from(applications_config: &ApplicationsConfig) -> Self {
|
||||
// Allow immediate launch by initializing last_launch in the past.
|
||||
let last_launch = Instant::now() - 2 * MIN_LAUNCH_INTERVAL;
|
||||
let mut applications_config = applications_config.clone();
|
||||
let items = applications_config
|
||||
.items
|
||||
.iter_mut()
|
||||
.enumerate()
|
||||
.map(|(index, app_config)| {
|
||||
app_config.command = app_config
|
||||
.command
|
||||
.replace_env()
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
|
||||
if let Some(icon) = &mut app_config.icon {
|
||||
*icon = icon.replace_env().to_string_lossy().to_string();
|
||||
}
|
||||
|
||||
App {
|
||||
enable: app_config.enable.unwrap_or(applications_config.enable),
|
||||
name: app_config
|
||||
.name
|
||||
.is_empty()
|
||||
.then(|| format!("App {}", index + 1))
|
||||
.unwrap_or_else(|| app_config.name.clone()),
|
||||
icon: Icon::try_from(app_config),
|
||||
command: app_config.command.clone(),
|
||||
display: app_config
|
||||
.display
|
||||
.or(applications_config.display)
|
||||
.unwrap_or_default(),
|
||||
show_command_on_hover: app_config
|
||||
.show_command_on_hover
|
||||
.or(applications_config.show_command_on_hover)
|
||||
.unwrap_or(false),
|
||||
last_launch,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
Self {
|
||||
enable: applications_config.enable,
|
||||
items,
|
||||
spacing: applications_config.spacing,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A single resolved application entry used at runtime.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct App {
|
||||
/// Whether this application is enabled.
|
||||
pub enable: bool,
|
||||
/// Display name of the application. Defaults to "App N" if not set.
|
||||
pub name: String,
|
||||
/// Icon to display for this application, if available.
|
||||
pub icon: Option<Icon>,
|
||||
/// Command to execute when the application is launched.
|
||||
pub command: String,
|
||||
/// Display format (icon, text, or both).
|
||||
pub display: DisplayFormat,
|
||||
/// Whether to show the launch command on hover.
|
||||
pub show_command_on_hover: bool,
|
||||
/// Last time this application was launched (used for cooldown control).
|
||||
pub last_launch: Instant,
|
||||
}
|
||||
|
||||
impl App {
|
||||
/// Renders the application button in the provided `Ui` context with a given icon size.
|
||||
#[inline]
|
||||
pub fn render(&mut self, ctx: &Context, ui: &mut Ui, icon_config: &IconConfig) {
|
||||
if self.enable
|
||||
&& SelectableFrame::new(false)
|
||||
.show(ui, |ui| {
|
||||
ui.spacing_mut().item_spacing = Vec2::splat(4.0);
|
||||
|
||||
match self.display {
|
||||
DisplayFormat::Icon => self.draw_icon(ctx, ui, icon_config),
|
||||
DisplayFormat::Text => self.draw_name(ui),
|
||||
DisplayFormat::IconAndText => {
|
||||
self.draw_icon(ctx, ui, icon_config);
|
||||
self.draw_name(ui);
|
||||
}
|
||||
}
|
||||
|
||||
// Add hover text with command information
|
||||
if self.show_command_on_hover {
|
||||
ui.response()
|
||||
.on_hover_text(format!("Launch: {}", self.command));
|
||||
} else {
|
||||
ui.response();
|
||||
}
|
||||
})
|
||||
.clicked()
|
||||
{
|
||||
// Launch the application when clicked
|
||||
self.launch_if_ready();
|
||||
}
|
||||
}
|
||||
|
||||
/// Draws the application's icon within the UI if available,
|
||||
/// or falls back to a default placeholder icon.
|
||||
#[inline]
|
||||
fn draw_icon(&self, ctx: &Context, ui: &mut Ui, icon_config: &IconConfig) {
|
||||
if let Some(icon) = &self.icon {
|
||||
icon.draw(ctx, ui, icon_config);
|
||||
} else {
|
||||
Icon::draw_fallback(ui, Vec2::splat(icon_config.size));
|
||||
}
|
||||
}
|
||||
|
||||
/// Displays the application's name as a non-selectable label within the UI.
|
||||
#[inline]
|
||||
fn draw_name(&self, ui: &mut Ui) {
|
||||
ui.add(Label::new(&self.name).selectable(false));
|
||||
}
|
||||
|
||||
/// Attempts to launch the specified command in a separate thread if enough time has passed
|
||||
/// since the last launch. This prevents repeated launches from rapid consecutive clicks.
|
||||
///
|
||||
/// Errors during launch are logged using the `tracing` crate.
|
||||
pub fn launch_if_ready(&mut self) {
|
||||
let now = Instant::now();
|
||||
if now.duration_since(self.last_launch) < MIN_LAUNCH_INTERVAL {
|
||||
return;
|
||||
}
|
||||
|
||||
self.last_launch = now;
|
||||
let command_string = self.command.clone();
|
||||
// Launch the application in a separate thread to avoid blocking the UI
|
||||
std::thread::spawn(move || {
|
||||
if let Err(e) = Command::new("cmd").args(["/C", &command_string]).spawn() {
|
||||
tracing::error!("Failed to launch command '{}': {}", command_string, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Holds decoded image data to be used as an icon in the UI.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Icon {
|
||||
/// RGBA image used for rendering the icon.
|
||||
Image(RgbaImage),
|
||||
/// Text-based icon, e.g. from a font like Nerd Fonts.
|
||||
Text(String),
|
||||
}
|
||||
|
||||
impl Icon {
|
||||
/// Attempts to create an `Icon` from the given `AppConfig`.
|
||||
/// Loads the image from a specified icon path or extracts it from the application's
|
||||
/// executable if the command points to a valid executable file.
|
||||
#[inline]
|
||||
pub fn try_from(config: &AppConfig) -> Option<Self> {
|
||||
if let Some(icon) = config.icon.as_deref().map(str::trim) {
|
||||
if !icon.is_empty() {
|
||||
let path = Path::new(&icon);
|
||||
if path.is_file() {
|
||||
match image::open(path).as_ref().map(DynamicImage::to_rgba8) {
|
||||
Ok(image) => return Some(Icon::Image(image)),
|
||||
Err(err) => {
|
||||
tracing::error!("Failed to load icon from {}, error: {}", icon, err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Some(Icon::Text(icon.to_owned()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let binary = PathBuf::from(config.command.split(".exe").next()?);
|
||||
let path = if binary.is_file() {
|
||||
Some(binary)
|
||||
} else {
|
||||
which(binary).ok()
|
||||
};
|
||||
|
||||
match path {
|
||||
Some(path) => windows_icons::get_icon_by_path(&path.to_string_lossy())
|
||||
.or_else(|| windows_icons_fallback::get_icon_by_path(&path.to_string_lossy()))
|
||||
.map(Icon::Image),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Renders the icon in the given `Ui` context with the specified size.
|
||||
#[inline]
|
||||
pub fn draw(&self, ctx: &Context, ui: &mut Ui, icon_config: &IconConfig) {
|
||||
match self {
|
||||
Icon::Image(image) => {
|
||||
Frame::NONE
|
||||
.inner_margin(Margin::same(ui.style().spacing.button_padding.y as i8))
|
||||
.show(ui, |ui| {
|
||||
ui.add(
|
||||
Image::from(&img_to_texture(ctx, image))
|
||||
.maintain_aspect_ratio(true)
|
||||
.fit_to_exact_size(Vec2::splat(icon_config.size)),
|
||||
);
|
||||
});
|
||||
}
|
||||
Icon::Text(icon) => {
|
||||
let rich_text = RichText::new(icon)
|
||||
.font(icon_config.font_id.clone())
|
||||
.size(icon_config.size)
|
||||
.color(icon_config.color);
|
||||
ui.add(Label::new(rich_text).selectable(false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Draws a fallback icon when the specified icon cannot be loaded.
|
||||
/// Displays a simple crossed-out rectangle as a placeholder.
|
||||
#[inline]
|
||||
pub fn draw_fallback(ui: &mut Ui, icon_size: Vec2) {
|
||||
let (response, painter) = ui.allocate_painter(icon_size, Sense::hover());
|
||||
let stroke = Stroke::new(1.0, ui.style().visuals.text_color());
|
||||
let mut rect = response.rect;
|
||||
let rounding = CornerRadius::same((rect.width() * 0.1) as u8);
|
||||
rect = rect.shrink(stroke.width);
|
||||
let c = rect.center();
|
||||
let r = rect.width() / 2.0;
|
||||
painter.rect_stroke(rect, rounding, stroke, StrokeKind::Outside);
|
||||
painter.line_segment([c - vec2(r, r), c + vec2(r, r)], stroke);
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration structure for icon rendering
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct IconConfig {
|
||||
/// Font used for text-based icons
|
||||
pub font_id: FontId,
|
||||
/// Size of the icon
|
||||
pub size: f32,
|
||||
/// Color of the icon used for text-based icons
|
||||
pub color: Color32,
|
||||
}
|
||||
@@ -28,8 +28,6 @@ pub struct BatteryConfig {
|
||||
pub data_refresh_interval: Option<u64>,
|
||||
/// Display label prefix
|
||||
pub label_prefix: Option<LabelPrefix>,
|
||||
/// Select when the current percentage is under this value [[1-100]]
|
||||
pub auto_select_under: Option<u8>,
|
||||
}
|
||||
|
||||
impl From<BatteryConfig> for Battery {
|
||||
@@ -40,10 +38,9 @@ impl From<BatteryConfig> for Battery {
|
||||
enable: value.enable,
|
||||
hide_on_full_charge: value.hide_on_full_charge.unwrap_or(false),
|
||||
manager: Manager::new().unwrap(),
|
||||
last_state: None,
|
||||
last_state: String::new(),
|
||||
data_refresh_interval,
|
||||
label_prefix: value.label_prefix.unwrap_or(LabelPrefix::Icon),
|
||||
auto_select_under: value.auto_select_under.map(|u| u.clamp(1, 100)),
|
||||
state: BatteryState::Discharging,
|
||||
last_updated: Instant::now()
|
||||
.checked_sub(Duration::from_secs(data_refresh_interval))
|
||||
@@ -55,16 +52,6 @@ impl From<BatteryConfig> for Battery {
|
||||
pub enum BatteryState {
|
||||
Charging,
|
||||
Discharging,
|
||||
High,
|
||||
Medium,
|
||||
Low,
|
||||
Warning,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct BatteryOutput {
|
||||
label: String,
|
||||
selected: bool,
|
||||
}
|
||||
|
||||
pub struct Battery {
|
||||
@@ -74,53 +61,37 @@ pub struct Battery {
|
||||
pub state: BatteryState,
|
||||
data_refresh_interval: u64,
|
||||
label_prefix: LabelPrefix,
|
||||
auto_select_under: Option<u8>,
|
||||
last_state: Option<BatteryOutput>,
|
||||
last_state: String,
|
||||
last_updated: Instant,
|
||||
}
|
||||
|
||||
impl Battery {
|
||||
fn output(&mut self) -> Option<BatteryOutput> {
|
||||
fn output(&mut self) -> String {
|
||||
let mut output = self.last_state.clone();
|
||||
|
||||
let now = Instant::now();
|
||||
if now.duration_since(self.last_updated) > Duration::from_secs(self.data_refresh_interval) {
|
||||
output = None;
|
||||
output.clear();
|
||||
|
||||
if let Ok(mut batteries) = self.manager.batteries() {
|
||||
if let Some(Ok(first)) = batteries.nth(0) {
|
||||
let percentage = first.state_of_charge().get::<percent>().round() as u8;
|
||||
let percentage = first.state_of_charge().get::<percent>();
|
||||
|
||||
if percentage == 100 && self.hide_on_full_charge {
|
||||
output = None
|
||||
if percentage == 100.0 && self.hide_on_full_charge {
|
||||
output = String::new()
|
||||
} else {
|
||||
match first.state() {
|
||||
State::Charging => self.state = BatteryState::Charging,
|
||||
State::Discharging => {
|
||||
self.state = match percentage {
|
||||
p if p > 75 => BatteryState::Discharging,
|
||||
p if p > 50 => BatteryState::High,
|
||||
p if p > 25 => BatteryState::Medium,
|
||||
p if p > 10 => BatteryState::Low,
|
||||
_ => BatteryState::Warning,
|
||||
}
|
||||
}
|
||||
State::Discharging => self.state = BatteryState::Discharging,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let selected = self.auto_select_under.is_some_and(|u| percentage <= u);
|
||||
|
||||
output = Some(BatteryOutput {
|
||||
label: match self.label_prefix {
|
||||
LabelPrefix::Text | LabelPrefix::IconAndText => {
|
||||
format!("BAT: {percentage}%")
|
||||
}
|
||||
LabelPrefix::None | LabelPrefix::Icon => {
|
||||
format!("{percentage}%")
|
||||
}
|
||||
},
|
||||
selected,
|
||||
})
|
||||
output = match self.label_prefix {
|
||||
LabelPrefix::Text | LabelPrefix::IconAndText => {
|
||||
format!("BAT: {percentage:.0}%")
|
||||
}
|
||||
LabelPrefix::None | LabelPrefix::Icon => format!("{percentage:.0}%"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -137,43 +108,35 @@ impl BarWidget for Battery {
|
||||
fn render(&mut self, ctx: &Context, ui: &mut Ui, config: &mut RenderConfig) {
|
||||
if self.enable {
|
||||
let output = self.output();
|
||||
if let Some(output) = output {
|
||||
if !output.is_empty() {
|
||||
let emoji = match self.state {
|
||||
BatteryState::Charging => egui_phosphor::regular::BATTERY_CHARGING,
|
||||
BatteryState::Discharging => egui_phosphor::regular::BATTERY_FULL,
|
||||
BatteryState::High => egui_phosphor::regular::BATTERY_HIGH,
|
||||
BatteryState::Medium => egui_phosphor::regular::BATTERY_MEDIUM,
|
||||
BatteryState::Low => egui_phosphor::regular::BATTERY_LOW,
|
||||
BatteryState::Warning => egui_phosphor::regular::BATTERY_WARNING,
|
||||
};
|
||||
|
||||
let auto_text_color = config.auto_select_text.filter(|_| output.selected);
|
||||
|
||||
let mut layout_job = LayoutJob::simple(
|
||||
match self.label_prefix {
|
||||
LabelPrefix::Icon | LabelPrefix::IconAndText => emoji.to_string(),
|
||||
LabelPrefix::None | LabelPrefix::Text => String::new(),
|
||||
},
|
||||
config.icon_font_id.clone(),
|
||||
auto_text_color.unwrap_or(ctx.style().visuals.selection.stroke.color),
|
||||
ctx.style().visuals.selection.stroke.color,
|
||||
100.0,
|
||||
);
|
||||
|
||||
layout_job.append(
|
||||
&output.label,
|
||||
&output,
|
||||
10.0,
|
||||
TextFormat {
|
||||
font_id: config.text_font_id.clone(),
|
||||
color: auto_text_color.unwrap_or(ctx.style().visuals.text_color()),
|
||||
color: ctx.style().visuals.text_color(),
|
||||
valign: Align::Center,
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
let auto_focus_fill = config.auto_select_fill;
|
||||
|
||||
config.apply_on_widget(false, ui, |ui| {
|
||||
if SelectableFrame::new_auto(output.selected, auto_focus_fill)
|
||||
if SelectableFrame::new(false)
|
||||
.show(ui, |ui| ui.add(Label::new(layout_job).selectable(false)))
|
||||
.clicked()
|
||||
{
|
||||
|
||||
@@ -25,8 +25,6 @@ pub struct CpuConfig {
|
||||
pub data_refresh_interval: Option<u64>,
|
||||
/// Display label prefix
|
||||
pub label_prefix: Option<LabelPrefix>,
|
||||
/// Select when the current percentage is over this value [[1-100]]
|
||||
pub auto_select_over: Option<u8>,
|
||||
}
|
||||
|
||||
impl From<CpuConfig> for Cpu {
|
||||
@@ -40,7 +38,6 @@ impl From<CpuConfig> for Cpu {
|
||||
),
|
||||
data_refresh_interval,
|
||||
label_prefix: value.label_prefix.unwrap_or(LabelPrefix::IconAndText),
|
||||
auto_select_over: value.auto_select_over.map(|o| o.clamp(1, 100)),
|
||||
last_updated: Instant::now()
|
||||
.checked_sub(Duration::from_secs(data_refresh_interval))
|
||||
.unwrap(),
|
||||
@@ -48,38 +45,26 @@ impl From<CpuConfig> for Cpu {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct CpuOutput {
|
||||
label: String,
|
||||
selected: bool,
|
||||
}
|
||||
|
||||
pub struct Cpu {
|
||||
pub enable: bool,
|
||||
system: System,
|
||||
data_refresh_interval: u64,
|
||||
label_prefix: LabelPrefix,
|
||||
auto_select_over: Option<u8>,
|
||||
last_updated: Instant,
|
||||
}
|
||||
|
||||
impl Cpu {
|
||||
fn output(&mut self) -> CpuOutput {
|
||||
fn output(&mut self) -> String {
|
||||
let now = Instant::now();
|
||||
if now.duration_since(self.last_updated) > Duration::from_secs(self.data_refresh_interval) {
|
||||
self.system.refresh_cpu_usage();
|
||||
self.last_updated = now;
|
||||
}
|
||||
|
||||
let used = self.system.global_cpu_usage() as u8;
|
||||
let selected = self.auto_select_over.is_some_and(|o| used >= o);
|
||||
|
||||
CpuOutput {
|
||||
label: match self.label_prefix {
|
||||
LabelPrefix::Text | LabelPrefix::IconAndText => format!("CPU: {}%", used),
|
||||
LabelPrefix::None | LabelPrefix::Icon => format!("{}%", used),
|
||||
},
|
||||
selected,
|
||||
let used = self.system.global_cpu_usage();
|
||||
match self.label_prefix {
|
||||
LabelPrefix::Text | LabelPrefix::IconAndText => format!("CPU: {:.0}%", used),
|
||||
LabelPrefix::None | LabelPrefix::Icon => format!("{:.0}%", used),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -88,9 +73,7 @@ impl BarWidget for Cpu {
|
||||
fn render(&mut self, ctx: &Context, ui: &mut Ui, config: &mut RenderConfig) {
|
||||
if self.enable {
|
||||
let output = self.output();
|
||||
if !output.label.is_empty() {
|
||||
let auto_text_color = config.auto_select_text.filter(|_| output.selected);
|
||||
|
||||
if !output.is_empty() {
|
||||
let mut layout_job = LayoutJob::simple(
|
||||
match self.label_prefix {
|
||||
LabelPrefix::Icon | LabelPrefix::IconAndText => {
|
||||
@@ -99,25 +82,23 @@ impl BarWidget for Cpu {
|
||||
LabelPrefix::None | LabelPrefix::Text => String::new(),
|
||||
},
|
||||
config.icon_font_id.clone(),
|
||||
auto_text_color.unwrap_or(ctx.style().visuals.selection.stroke.color),
|
||||
ctx.style().visuals.selection.stroke.color,
|
||||
100.0,
|
||||
);
|
||||
|
||||
layout_job.append(
|
||||
&output.label,
|
||||
&output,
|
||||
10.0,
|
||||
TextFormat {
|
||||
font_id: config.text_font_id.clone(),
|
||||
color: auto_text_color.unwrap_or(ctx.style().visuals.text_color()),
|
||||
color: ctx.style().visuals.text_color(),
|
||||
valign: Align::Center,
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
let auto_focus_fill = config.auto_select_fill;
|
||||
|
||||
config.apply_on_widget(false, ui, |ui| {
|
||||
if SelectableFrame::new_auto(output.selected, auto_focus_fill)
|
||||
if SelectableFrame::new(false)
|
||||
.show(ui, |ui| ui.add(Label::new(layout_job).selectable(false)))
|
||||
.clicked()
|
||||
{
|
||||
|
||||
@@ -11,9 +11,7 @@ use crate::widgets::widget::BarWidget;
|
||||
use crate::ICON_CACHE;
|
||||
use crate::MAX_LABEL_WIDTH;
|
||||
use crate::MONITOR_INDEX;
|
||||
use eframe::egui::text::LayoutJob;
|
||||
use eframe::egui::vec2;
|
||||
use eframe::egui::Align;
|
||||
use eframe::egui::Color32;
|
||||
use eframe::egui::ColorImage;
|
||||
use eframe::egui::Context;
|
||||
@@ -26,7 +24,6 @@ use eframe::egui::RichText;
|
||||
use eframe::egui::Sense;
|
||||
use eframe::egui::Stroke;
|
||||
use eframe::egui::StrokeKind;
|
||||
use eframe::egui::TextFormat;
|
||||
use eframe::egui::TextureHandle;
|
||||
use eframe::egui::TextureOptions;
|
||||
use eframe::egui::Ui;
|
||||
@@ -58,11 +55,8 @@ pub struct KomorebiConfig {
|
||||
pub layout: Option<KomorebiLayoutConfig>,
|
||||
/// Configure the Workspace Layer widget
|
||||
pub workspace_layer: Option<KomorebiWorkspaceLayerConfig>,
|
||||
/// Configure the Focused Container widget
|
||||
#[serde(alias = "focused_window")]
|
||||
pub focused_container: Option<KomorebiFocusedContainerConfig>,
|
||||
/// Configure the Locked Container widget
|
||||
pub locked_container: Option<KomorebiLockedContainerConfig>,
|
||||
/// Configure the Focused Window widget
|
||||
pub focused_window: Option<KomorebiFocusedWindowConfig>,
|
||||
/// Configure the Configuration Switcher widget
|
||||
pub configuration_switcher: Option<KomorebiConfigurationSwitcherConfig>,
|
||||
}
|
||||
@@ -102,26 +96,15 @@ pub struct KomorebiWorkspaceLayerConfig {
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub struct KomorebiFocusedContainerConfig {
|
||||
/// Enable the Komorebi Focused Container widget
|
||||
pub struct KomorebiFocusedWindowConfig {
|
||||
/// Enable the Komorebi Focused Window widget
|
||||
pub enable: bool,
|
||||
/// DEPRECATED: use 'display' instead (Show the icon of the currently focused container)
|
||||
/// DEPRECATED: use 'display' instead (Show the icon of the currently focused window)
|
||||
pub show_icon: Option<bool>,
|
||||
/// Display format of the currently focused container
|
||||
/// Display format of the currently focused window
|
||||
pub display: Option<DisplayFormat>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub struct KomorebiLockedContainerConfig {
|
||||
/// Enable the Komorebi Locked Container widget
|
||||
pub enable: bool,
|
||||
/// Display format of the current locked state
|
||||
pub display: Option<DisplayFormat>,
|
||||
/// Show the widget event if the layer is unlocked
|
||||
pub show_when_unlocked: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub struct KomorebiConfigurationSwitcherConfig {
|
||||
@@ -157,19 +140,15 @@ impl From<&KomorebiConfig> for Komorebi {
|
||||
.unwrap_or_default(),
|
||||
mouse_follows_focus: true,
|
||||
work_area_offset: None,
|
||||
focused_container_information: (
|
||||
false,
|
||||
KomorebiNotificationStateContainerInformation::EMPTY,
|
||||
),
|
||||
focused_container_information: KomorebiNotificationStateContainerInformation::EMPTY,
|
||||
stack_accent: None,
|
||||
monitor_index: MONITOR_INDEX.load(Ordering::SeqCst),
|
||||
monitor_usr_idx_map: HashMap::new(),
|
||||
})),
|
||||
workspaces: value.workspaces,
|
||||
layout: value.layout.clone(),
|
||||
focused_container: value.focused_container,
|
||||
focused_window: value.focused_window,
|
||||
workspace_layer: value.workspace_layer,
|
||||
locked_container: value.locked_container,
|
||||
configuration_switcher,
|
||||
}
|
||||
}
|
||||
@@ -180,9 +159,8 @@ pub struct Komorebi {
|
||||
pub komorebi_notification_state: Rc<RefCell<KomorebiNotificationState>>,
|
||||
pub workspaces: Option<KomorebiWorkspacesConfig>,
|
||||
pub layout: Option<KomorebiLayoutConfig>,
|
||||
pub focused_container: Option<KomorebiFocusedContainerConfig>,
|
||||
pub focused_window: Option<KomorebiFocusedWindowConfig>,
|
||||
pub workspace_layer: Option<KomorebiWorkspaceLayerConfig>,
|
||||
pub locked_container: Option<KomorebiLockedContainerConfig>,
|
||||
pub configuration_switcher: Option<KomorebiConfigurationSwitcherConfig>,
|
||||
}
|
||||
|
||||
@@ -200,10 +178,9 @@ impl BarWidget for Komorebi {
|
||||
let format = workspaces.display.unwrap_or(DisplayFormat::Text.into());
|
||||
|
||||
config.apply_on_widget(false, ui, |ui| {
|
||||
for (i, (ws, containers, _, should_show)) in
|
||||
for (i, (ws, containers, _)) in
|
||||
komorebi_notification_state.workspaces.iter().enumerate()
|
||||
{
|
||||
if *should_show {
|
||||
let is_selected = komorebi_notification_state.selected_workspace.eq(ws);
|
||||
|
||||
if SelectableFrame::new(
|
||||
@@ -292,6 +269,7 @@ impl BarWidget for Komorebi {
|
||||
komorebi_notification_state.monitor_index,
|
||||
i,
|
||||
),
|
||||
SocketMessage::RetileWithResizeDimensions,
|
||||
SocketMessage::MouseFollowsFocus(true),
|
||||
])
|
||||
.is_err()
|
||||
@@ -300,6 +278,7 @@ impl BarWidget for Komorebi {
|
||||
"could not send the following batch of messages to komorebi:\n
|
||||
MouseFollowsFocus(false)\n
|
||||
FocusMonitorWorkspaceNumber({}, {})\n
|
||||
RetileWithResizeDimensions
|
||||
MouseFollowsFocus(true)\n",
|
||||
komorebi_notification_state.monitor_index,
|
||||
i,
|
||||
@@ -310,18 +289,19 @@ impl BarWidget for Komorebi {
|
||||
komorebi_notification_state.monitor_index,
|
||||
i,
|
||||
),
|
||||
SocketMessage::RetileWithResizeDimensions,
|
||||
])
|
||||
.is_err()
|
||||
{
|
||||
tracing::error!(
|
||||
"could not send the following batch of messages to komorebi:\n
|
||||
FocusMonitorWorkspaceNumber({}, {})\n",
|
||||
FocusMonitorWorkspaceNumber({}, {})\n
|
||||
RetileWithResizeDimensions",
|
||||
komorebi_notification_state.monitor_index,
|
||||
i,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -338,7 +318,7 @@ impl BarWidget for Komorebi {
|
||||
.workspaces
|
||||
.iter()
|
||||
.find(|o| komorebi_notification_state.selected_workspace.eq(&o.0))
|
||||
.map(|(_, _, layer, _)| layer);
|
||||
.map(|(_, _, layer)| layer);
|
||||
|
||||
if let Some(layer) = layer {
|
||||
if (layer_config.show_when_tiling.unwrap_or_default()
|
||||
@@ -355,7 +335,7 @@ impl BarWidget for Komorebi {
|
||||
if matches!(layer, WorkspaceLayer::Tiling) {
|
||||
let (response, painter) =
|
||||
ui.allocate_painter(size, Sense::hover());
|
||||
let color = ctx.style().visuals.selection.stroke.color;
|
||||
let color = ui.style().visuals.text_color();
|
||||
let stroke = Stroke::new(1.0, color);
|
||||
let mut rect = response.rect;
|
||||
let corner =
|
||||
@@ -385,7 +365,7 @@ impl BarWidget for Komorebi {
|
||||
} else {
|
||||
let (response, painter) =
|
||||
ui.allocate_painter(size, Sense::hover());
|
||||
let color = ctx.style().visuals.selection.stroke.color;
|
||||
let color = ui.style().visuals.text_color();
|
||||
let stroke = Stroke::new(1.0, color);
|
||||
let mut rect = response.rect;
|
||||
let corner =
|
||||
@@ -468,109 +448,72 @@ impl BarWidget for Komorebi {
|
||||
for (name, location) in configuration_switcher.configurations.iter() {
|
||||
let path = PathBuf::from(location);
|
||||
if path.is_file() {
|
||||
config.apply_on_widget(false, ui, |ui| {
|
||||
if SelectableFrame::new(false)
|
||||
.show(ui, |ui| ui.add(Label::new(name).selectable(false)))
|
||||
.clicked()
|
||||
config.apply_on_widget(false, ui,|ui|{
|
||||
if SelectableFrame::new(false).show(ui, |ui|{
|
||||
ui.add(Label::new(name).selectable(false))
|
||||
})
|
||||
.clicked()
|
||||
{
|
||||
let canonicalized = dunce::canonicalize(path.clone()).unwrap_or(path);
|
||||
let mut proceed = true;
|
||||
if komorebi_client::send_message(&SocketMessage::ReplaceConfiguration(
|
||||
canonicalized,
|
||||
))
|
||||
.is_err()
|
||||
{
|
||||
let canonicalized =
|
||||
dunce::canonicalize(path.clone()).unwrap_or(path);
|
||||
tracing::error!(
|
||||
"could not send message to komorebi: ReplaceConfiguration"
|
||||
);
|
||||
proceed = false;
|
||||
}
|
||||
|
||||
if komorebi_client::send_message(
|
||||
&SocketMessage::ReplaceConfiguration(canonicalized),
|
||||
)
|
||||
.is_err()
|
||||
{
|
||||
tracing::error!(
|
||||
"could not send message to komorebi: ReplaceConfiguration"
|
||||
);
|
||||
if let Some(rect) = komorebi_notification_state.work_area_offset {
|
||||
if proceed {
|
||||
match komorebi_client::send_query(&SocketMessage::Query(
|
||||
komorebi_client::StateQuery::FocusedMonitorIndex,
|
||||
)) {
|
||||
Ok(idx) => {
|
||||
if let Ok(monitor_idx) = idx.parse::<usize>() {
|
||||
if komorebi_client::send_message(
|
||||
&SocketMessage::MonitorWorkAreaOffset(
|
||||
monitor_idx,
|
||||
rect,
|
||||
),
|
||||
)
|
||||
.is_err()
|
||||
{
|
||||
tracing::error!(
|
||||
"could not send message to komorebi: MonitorWorkAreaOffset"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
tracing::error!(
|
||||
"could not send message to komorebi: Query"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(locked_container_config) = self.locked_container {
|
||||
if locked_container_config.enable {
|
||||
let is_locked = komorebi_notification_state.focused_container_information.0;
|
||||
|
||||
if locked_container_config
|
||||
.show_when_unlocked
|
||||
.unwrap_or_default()
|
||||
|| is_locked
|
||||
{
|
||||
let titles = &komorebi_notification_state
|
||||
.focused_container_information
|
||||
.1
|
||||
.titles;
|
||||
|
||||
if !titles.is_empty() {
|
||||
let display_format = locked_container_config
|
||||
.display
|
||||
.unwrap_or(DisplayFormat::Text);
|
||||
|
||||
let mut layout_job = LayoutJob::simple(
|
||||
if display_format != DisplayFormat::Text {
|
||||
if is_locked {
|
||||
egui_phosphor::regular::LOCK_KEY.to_string()
|
||||
} else {
|
||||
egui_phosphor::regular::LOCK_SIMPLE_OPEN.to_string()
|
||||
}
|
||||
} else {
|
||||
String::new()
|
||||
},
|
||||
config.icon_font_id.clone(),
|
||||
ctx.style().visuals.selection.stroke.color,
|
||||
100.0,
|
||||
);
|
||||
|
||||
if display_format != DisplayFormat::Icon {
|
||||
layout_job.append(
|
||||
if is_locked { "Locked" } else { "Unlocked" },
|
||||
10.0,
|
||||
TextFormat {
|
||||
font_id: config.text_font_id.clone(),
|
||||
color: ctx.style().visuals.text_color(),
|
||||
valign: Align::Center,
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
config.apply_on_widget(false, ui, |ui| {
|
||||
if SelectableFrame::new(false)
|
||||
.show(ui, |ui| ui.add(Label::new(layout_job).selectable(false)))
|
||||
.clicked()
|
||||
&& komorebi_client::send_batch([
|
||||
SocketMessage::FocusMonitorAtCursor,
|
||||
SocketMessage::ToggleLock,
|
||||
])
|
||||
.is_err()
|
||||
{
|
||||
tracing::error!("could not send ToggleLock");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(focused_container_config) = self.focused_container {
|
||||
if focused_container_config.enable {
|
||||
if let Some(focused_window) = self.focused_window {
|
||||
if focused_window.enable {
|
||||
let titles = &komorebi_notification_state
|
||||
.focused_container_information
|
||||
.1
|
||||
.titles;
|
||||
|
||||
if !titles.is_empty() {
|
||||
config.apply_on_widget(false, ui, |ui| {
|
||||
let icons = &komorebi_notification_state
|
||||
.focused_container_information.1
|
||||
.focused_container_information
|
||||
.icons;
|
||||
let focused_window_idx = komorebi_notification_state
|
||||
.focused_container_information.1
|
||||
.focused_container_information
|
||||
.focused_window_idx;
|
||||
|
||||
let iter = titles.iter().zip(icons.iter());
|
||||
@@ -578,13 +521,13 @@ impl BarWidget for Komorebi {
|
||||
|
||||
for (i, (title, icon)) in iter.enumerate() {
|
||||
let selected = i == focused_window_idx && len != 1;
|
||||
let text_color = if selected { ctx.style().visuals.selection.stroke.color } else { ui.style().visuals.text_color() };
|
||||
let text_color = if selected { ctx.style().visuals.selection.stroke.color} else { ui.style().visuals.text_color() };
|
||||
|
||||
if SelectableFrame::new(selected)
|
||||
.show(ui, |ui| {
|
||||
// handle legacy setting
|
||||
let format = focused_container_config.display.unwrap_or(
|
||||
if focused_container_config.show_icon.unwrap_or(false) {
|
||||
let format = focused_window.display.unwrap_or(
|
||||
if focused_window.show_icon.unwrap_or(false) {
|
||||
DisplayFormat::IconAndText
|
||||
} else {
|
||||
DisplayFormat::Text
|
||||
@@ -670,7 +613,7 @@ impl BarWidget for Komorebi {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn img_to_texture(ctx: &Context, rgba_image: &RgbaImage) -> TextureHandle {
|
||||
fn img_to_texture(ctx: &Context, rgba_image: &RgbaImage) -> TextureHandle {
|
||||
let size = [rgba_image.width() as usize, rgba_image.height() as usize];
|
||||
let pixels = rgba_image.as_flat_samples();
|
||||
let color_image = ColorImage::from_rgba_unmultiplied(size, pixels.as_slice());
|
||||
@@ -684,10 +627,9 @@ pub struct KomorebiNotificationState {
|
||||
String,
|
||||
Vec<(bool, KomorebiNotificationStateContainerInformation)>,
|
||||
WorkspaceLayer,
|
||||
bool,
|
||||
)>,
|
||||
pub selected_workspace: String,
|
||||
pub focused_container_information: (bool, KomorebiNotificationStateContainerInformation),
|
||||
pub focused_container_information: KomorebiNotificationStateContainerInformation,
|
||||
pub layout: KomorebiLayout,
|
||||
pub hide_empty_workspaces: bool,
|
||||
pub mouse_follows_focus: bool,
|
||||
@@ -718,7 +660,6 @@ impl KomorebiNotificationState {
|
||||
let show_all_icons = render_config.borrow().show_all_icons;
|
||||
|
||||
match notification.event {
|
||||
NotificationEvent::VirtualDesktop(_) => {}
|
||||
NotificationEvent::WindowManager(_) => {}
|
||||
NotificationEvent::Monitor(_) => {}
|
||||
NotificationEvent::Socket(message) => match message {
|
||||
@@ -754,7 +695,7 @@ impl KomorebiNotificationState {
|
||||
SocketMessage::Theme(theme) => {
|
||||
apply_theme(
|
||||
ctx,
|
||||
KomobarTheme::from(*theme),
|
||||
KomobarTheme::from(theme),
|
||||
bg_color,
|
||||
bg_color_with_alpha.clone(),
|
||||
transparency_alpha,
|
||||
@@ -801,41 +742,42 @@ impl KomorebiNotificationState {
|
||||
true
|
||||
};
|
||||
|
||||
workspaces.push((
|
||||
ws.name().to_owned().unwrap_or_else(|| format!("{}", i + 1)),
|
||||
if show_all_icons {
|
||||
let mut containers = vec![];
|
||||
let mut has_monocle = false;
|
||||
if should_show {
|
||||
workspaces.push((
|
||||
ws.name().to_owned().unwrap_or_else(|| format!("{}", i + 1)),
|
||||
if show_all_icons {
|
||||
let mut containers = vec![];
|
||||
let mut has_monocle = false;
|
||||
|
||||
// add monocle container
|
||||
if let Some(container) = ws.monocle_container() {
|
||||
containers.push((true, container.into()));
|
||||
has_monocle = true;
|
||||
}
|
||||
// add monocle container
|
||||
if let Some(container) = ws.monocle_container() {
|
||||
containers.push((true, container.into()));
|
||||
has_monocle = true;
|
||||
}
|
||||
|
||||
// add all tiled windows
|
||||
for (i, container) in ws.containers().iter().enumerate() {
|
||||
containers.push((
|
||||
!has_monocle && i == ws.focused_container_idx(),
|
||||
container.into(),
|
||||
));
|
||||
}
|
||||
// add all tiled windows
|
||||
for (i, container) in ws.containers().iter().enumerate() {
|
||||
containers.push((
|
||||
!has_monocle && i == ws.focused_container_idx(),
|
||||
container.into(),
|
||||
));
|
||||
}
|
||||
|
||||
// add all floating windows
|
||||
for floating_window in ws.floating_windows() {
|
||||
containers.push((
|
||||
!has_monocle && floating_window.is_focused(),
|
||||
floating_window.into(),
|
||||
));
|
||||
}
|
||||
// add all floating windows
|
||||
for floating_window in ws.floating_windows() {
|
||||
containers.push((
|
||||
!has_monocle && floating_window.is_focused(),
|
||||
floating_window.into(),
|
||||
));
|
||||
}
|
||||
|
||||
containers
|
||||
} else {
|
||||
vec![(true, ws.into())]
|
||||
},
|
||||
ws.layer().to_owned(),
|
||||
should_show,
|
||||
));
|
||||
containers
|
||||
} else {
|
||||
vec![(true, ws.into())]
|
||||
},
|
||||
ws.layer().to_owned(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
self.workspaces = workspaces;
|
||||
@@ -856,12 +798,7 @@ impl KomorebiNotificationState {
|
||||
};
|
||||
}
|
||||
|
||||
let focused_workspace = &monitor.workspaces()[focused_workspace_idx];
|
||||
let is_focused = focused_workspace
|
||||
.locked_containers()
|
||||
.contains(&focused_workspace.focused_container_idx());
|
||||
|
||||
self.focused_container_information = (is_focused, focused_workspace.into());
|
||||
self.focused_container_information = (&monitor.workspaces()[focused_workspace_idx]).into();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -251,7 +251,7 @@ impl KomorebiLayout {
|
||||
let layout_frame = SelectableFrame::new(false)
|
||||
.show(ui, |ui| {
|
||||
if let DisplayFormat::Icon | DisplayFormat::IconAndText = format {
|
||||
self.show_icon(true, font_id.clone(), ctx, ui);
|
||||
self.show_icon(false, font_id.clone(), ctx, ui);
|
||||
}
|
||||
|
||||
if let DisplayFormat::Text | DisplayFormat::IconAndText = format {
|
||||
|
||||
@@ -25,8 +25,6 @@ pub struct MemoryConfig {
|
||||
pub data_refresh_interval: Option<u64>,
|
||||
/// Display label prefix
|
||||
pub label_prefix: Option<LabelPrefix>,
|
||||
/// Select when the current percentage is over this value [[1-100]]
|
||||
pub auto_select_over: Option<u8>,
|
||||
}
|
||||
|
||||
impl From<MemoryConfig> for Memory {
|
||||
@@ -40,7 +38,6 @@ impl From<MemoryConfig> for Memory {
|
||||
),
|
||||
data_refresh_interval,
|
||||
label_prefix: value.label_prefix.unwrap_or(LabelPrefix::IconAndText),
|
||||
auto_select_over: value.auto_select_over.map(|o| o.clamp(1, 100)),
|
||||
last_updated: Instant::now()
|
||||
.checked_sub(Duration::from_secs(data_refresh_interval))
|
||||
.unwrap(),
|
||||
@@ -48,23 +45,16 @@ impl From<MemoryConfig> for Memory {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct MemoryOutput {
|
||||
label: String,
|
||||
selected: bool,
|
||||
}
|
||||
|
||||
pub struct Memory {
|
||||
pub enable: bool,
|
||||
system: System,
|
||||
data_refresh_interval: u64,
|
||||
label_prefix: LabelPrefix,
|
||||
auto_select_over: Option<u8>,
|
||||
last_updated: Instant,
|
||||
}
|
||||
|
||||
impl Memory {
|
||||
fn output(&mut self) -> MemoryOutput {
|
||||
fn output(&mut self) -> String {
|
||||
let now = Instant::now();
|
||||
if now.duration_since(self.last_updated) > Duration::from_secs(self.data_refresh_interval) {
|
||||
self.system.refresh_memory();
|
||||
@@ -73,17 +63,11 @@ impl Memory {
|
||||
|
||||
let used = self.system.used_memory();
|
||||
let total = self.system.total_memory();
|
||||
let usage = ((used * 100) / total) as u8;
|
||||
let selected = self.auto_select_over.is_some_and(|o| usage >= o);
|
||||
|
||||
MemoryOutput {
|
||||
label: match self.label_prefix {
|
||||
LabelPrefix::Text | LabelPrefix::IconAndText => {
|
||||
format!("RAM: {}%", usage)
|
||||
}
|
||||
LabelPrefix::None | LabelPrefix::Icon => format!("{}%", usage),
|
||||
},
|
||||
selected,
|
||||
match self.label_prefix {
|
||||
LabelPrefix::Text | LabelPrefix::IconAndText => {
|
||||
format!("RAM: {}%", (used * 100) / total)
|
||||
}
|
||||
LabelPrefix::None | LabelPrefix::Icon => format!("{}%", (used * 100) / total),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -92,9 +76,7 @@ impl BarWidget for Memory {
|
||||
fn render(&mut self, ctx: &Context, ui: &mut Ui, config: &mut RenderConfig) {
|
||||
if self.enable {
|
||||
let output = self.output();
|
||||
if !output.label.is_empty() {
|
||||
let auto_text_color = config.auto_select_text.filter(|_| output.selected);
|
||||
|
||||
if !output.is_empty() {
|
||||
let mut layout_job = LayoutJob::simple(
|
||||
match self.label_prefix {
|
||||
LabelPrefix::Icon | LabelPrefix::IconAndText => {
|
||||
@@ -103,25 +85,23 @@ impl BarWidget for Memory {
|
||||
LabelPrefix::None | LabelPrefix::Text => String::new(),
|
||||
},
|
||||
config.icon_font_id.clone(),
|
||||
auto_text_color.unwrap_or(ctx.style().visuals.selection.stroke.color),
|
||||
ctx.style().visuals.selection.stroke.color,
|
||||
100.0,
|
||||
);
|
||||
|
||||
layout_job.append(
|
||||
&output.label,
|
||||
&output,
|
||||
10.0,
|
||||
TextFormat {
|
||||
font_id: config.text_font_id.clone(),
|
||||
color: auto_text_color.unwrap_or(ctx.style().visuals.text_color()),
|
||||
color: ctx.style().visuals.text_color(),
|
||||
valign: Align::Center,
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
let auto_focus_fill = config.auto_select_fill;
|
||||
|
||||
config.apply_on_widget(false, ui, |ui| {
|
||||
if SelectableFrame::new_auto(output.selected, auto_focus_fill)
|
||||
if SelectableFrame::new(false)
|
||||
.show(ui, |ui| ui.add(Label::new(layout_job).selectable(false)))
|
||||
.clicked()
|
||||
{
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
pub mod applications;
|
||||
pub mod battery;
|
||||
pub mod cpu;
|
||||
pub mod date;
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
use crate::bar::Alignment;
|
||||
use crate::config::LabelPrefix;
|
||||
use crate::render::RenderConfig;
|
||||
use crate::selected_frame::SelectableFrame;
|
||||
use crate::widgets::widget::BarWidget;
|
||||
use eframe::egui::text::LayoutJob;
|
||||
use eframe::egui::Align;
|
||||
use eframe::egui::Color32;
|
||||
use eframe::egui::Context;
|
||||
use eframe::egui::Label;
|
||||
use eframe::egui::TextFormat;
|
||||
@@ -24,36 +22,18 @@ use sysinfo::Networks;
|
||||
pub struct NetworkConfig {
|
||||
/// Enable the Network widget
|
||||
pub enable: bool,
|
||||
/// Show total received and transmitted activity
|
||||
#[serde(alias = "show_total_data_transmitted")]
|
||||
pub show_total_activity: bool,
|
||||
/// Show received and transmitted activity
|
||||
#[serde(alias = "show_network_activity")]
|
||||
pub show_activity: bool,
|
||||
/// Show total data transmitted
|
||||
pub show_total_data_transmitted: bool,
|
||||
/// Show network activity
|
||||
pub show_network_activity: bool,
|
||||
/// Show default interface
|
||||
pub show_default_interface: Option<bool>,
|
||||
/// Characters to reserve for received and transmitted activity
|
||||
#[serde(alias = "network_activity_fill_characters")]
|
||||
pub activity_left_padding: Option<usize>,
|
||||
/// Characters to reserve for network activity data
|
||||
pub network_activity_fill_characters: Option<usize>,
|
||||
/// Data refresh interval (default: 10 seconds)
|
||||
pub data_refresh_interval: Option<u64>,
|
||||
/// Display label prefix
|
||||
pub label_prefix: Option<LabelPrefix>,
|
||||
/// Select when the value is over a limit (1MiB is 1048576 bytes (1024*1024))
|
||||
pub auto_select: Option<NetworkSelectConfig>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub struct NetworkSelectConfig {
|
||||
/// Select the total received data when it's over this value
|
||||
pub total_received_over: Option<u64>,
|
||||
/// Select the total transmitted data when it's over this value
|
||||
pub total_transmitted_over: Option<u64>,
|
||||
/// Select the received data when it's over this value
|
||||
pub received_over: Option<u64>,
|
||||
/// Select the transmitted data when it's over this value
|
||||
pub transmitted_over: Option<u64>,
|
||||
}
|
||||
|
||||
impl From<NetworkConfig> for Network {
|
||||
@@ -62,15 +42,16 @@ impl From<NetworkConfig> for Network {
|
||||
|
||||
Self {
|
||||
enable: value.enable,
|
||||
show_total_activity: value.show_total_activity,
|
||||
show_activity: value.show_activity,
|
||||
show_total_activity: value.show_total_data_transmitted,
|
||||
show_activity: value.show_network_activity,
|
||||
show_default_interface: value.show_default_interface.unwrap_or(true),
|
||||
networks_network_activity: Networks::new_with_refreshed_list(),
|
||||
default_interface: String::new(),
|
||||
data_refresh_interval,
|
||||
label_prefix: value.label_prefix.unwrap_or(LabelPrefix::Icon),
|
||||
auto_select: value.auto_select,
|
||||
activity_left_padding: value.activity_left_padding.unwrap_or_default(),
|
||||
network_activity_fill_characters: value
|
||||
.network_activity_fill_characters
|
||||
.unwrap_or_default(),
|
||||
last_state_total_activity: vec![],
|
||||
last_state_activity: vec![],
|
||||
last_updated_network_activity: Instant::now()
|
||||
@@ -88,12 +69,11 @@ pub struct Network {
|
||||
networks_network_activity: Networks,
|
||||
data_refresh_interval: u64,
|
||||
label_prefix: LabelPrefix,
|
||||
auto_select: Option<NetworkSelectConfig>,
|
||||
default_interface: String,
|
||||
last_state_total_activity: Vec<NetworkReading>,
|
||||
last_state_activity: Vec<NetworkReading>,
|
||||
last_updated_network_activity: Instant,
|
||||
activity_left_padding: usize,
|
||||
network_activity_fill_characters: usize,
|
||||
}
|
||||
|
||||
impl Network {
|
||||
@@ -125,32 +105,24 @@ impl Network {
|
||||
for (interface_name, data) in &self.networks_network_activity {
|
||||
if friendly_name.eq(interface_name) {
|
||||
if self.show_activity {
|
||||
let received = Self::to_pretty_bytes(
|
||||
data.received(),
|
||||
self.data_refresh_interval,
|
||||
);
|
||||
let transmitted = Self::to_pretty_bytes(
|
||||
data.transmitted(),
|
||||
self.data_refresh_interval,
|
||||
);
|
||||
|
||||
activity.push(NetworkReading::new(
|
||||
NetworkReadingFormat::Speed,
|
||||
ReadingValue::from(received),
|
||||
ReadingValue::from(transmitted),
|
||||
Self::to_pretty_bytes(
|
||||
data.received(),
|
||||
self.data_refresh_interval,
|
||||
),
|
||||
Self::to_pretty_bytes(
|
||||
data.transmitted(),
|
||||
self.data_refresh_interval,
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
if self.show_total_activity {
|
||||
let total_received =
|
||||
Self::to_pretty_bytes(data.total_received(), 1);
|
||||
let total_transmitted =
|
||||
Self::to_pretty_bytes(data.total_transmitted(), 1);
|
||||
|
||||
total_activity.push(NetworkReading::new(
|
||||
NetworkReadingFormat::Total,
|
||||
ReadingValue::from(total_received),
|
||||
ReadingValue::from(total_transmitted),
|
||||
Self::to_pretty_bytes(data.total_received(), 1),
|
||||
Self::to_pretty_bytes(data.total_transmitted(), 1),
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -166,121 +138,105 @@ impl Network {
|
||||
(activity, total_activity)
|
||||
}
|
||||
|
||||
fn reading_to_labels(
|
||||
fn reading_to_label(
|
||||
&self,
|
||||
select_received: bool,
|
||||
select_transmitted: bool,
|
||||
ctx: &Context,
|
||||
reading: &NetworkReading,
|
||||
reading: NetworkReading,
|
||||
config: RenderConfig,
|
||||
) -> (Label, Label) {
|
||||
) -> Label {
|
||||
let (text_down, text_up) = match self.label_prefix {
|
||||
LabelPrefix::None | LabelPrefix::Icon => match reading.format {
|
||||
NetworkReadingFormat::Speed => (
|
||||
format!(
|
||||
"{: >width$}/s ",
|
||||
reading.received.pretty,
|
||||
width = self.activity_left_padding
|
||||
reading.received_text,
|
||||
width = self.network_activity_fill_characters
|
||||
),
|
||||
format!(
|
||||
"{: >width$}/s",
|
||||
reading.transmitted.pretty,
|
||||
width = self.activity_left_padding
|
||||
reading.transmitted_text,
|
||||
width = self.network_activity_fill_characters
|
||||
),
|
||||
),
|
||||
NetworkReadingFormat::Total => (
|
||||
format!("{} ", reading.received.pretty),
|
||||
reading.transmitted.pretty.clone(),
|
||||
format!("{} ", reading.received_text),
|
||||
reading.transmitted_text,
|
||||
),
|
||||
},
|
||||
LabelPrefix::Text | LabelPrefix::IconAndText => match reading.format {
|
||||
NetworkReadingFormat::Speed => (
|
||||
format!(
|
||||
"DOWN: {: >width$}/s ",
|
||||
reading.received.pretty,
|
||||
width = self.activity_left_padding
|
||||
reading.received_text,
|
||||
width = self.network_activity_fill_characters
|
||||
),
|
||||
format!(
|
||||
"UP: {: >width$}/s",
|
||||
reading.transmitted.pretty,
|
||||
width = self.activity_left_padding
|
||||
reading.transmitted_text,
|
||||
width = self.network_activity_fill_characters
|
||||
),
|
||||
),
|
||||
NetworkReadingFormat::Total => (
|
||||
format!("\u{2211}DOWN: {}/s ", reading.received.pretty),
|
||||
format!("\u{2211}UP: {}/s", reading.transmitted.pretty),
|
||||
format!("\u{2211}DOWN: {}/s ", reading.received_text),
|
||||
format!("\u{2211}UP: {}/s", reading.transmitted_text),
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
let auto_text_color_received = config.auto_select_text.filter(|_| select_received);
|
||||
let auto_text_color_transmitted = config.auto_select_text.filter(|_| select_transmitted);
|
||||
let icon_format = TextFormat::simple(
|
||||
config.icon_font_id.clone(),
|
||||
ctx.style().visuals.selection.stroke.color,
|
||||
);
|
||||
let text_format = TextFormat {
|
||||
font_id: config.text_font_id.clone(),
|
||||
color: ctx.style().visuals.text_color(),
|
||||
valign: Align::Center,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
// icon
|
||||
let mut layout_job_down = LayoutJob::simple(
|
||||
let mut layout_job = LayoutJob::simple(
|
||||
match self.label_prefix {
|
||||
LabelPrefix::Icon | LabelPrefix::IconAndText => {
|
||||
if select_received {
|
||||
egui_phosphor::regular::ARROW_FAT_LINES_DOWN.to_string()
|
||||
} else {
|
||||
egui_phosphor::regular::ARROW_FAT_DOWN.to_string()
|
||||
}
|
||||
egui_phosphor::regular::ARROW_FAT_DOWN.to_string()
|
||||
}
|
||||
LabelPrefix::None | LabelPrefix::Text => String::new(),
|
||||
},
|
||||
config.icon_font_id.clone(),
|
||||
auto_text_color_received.unwrap_or(ctx.style().visuals.selection.stroke.color),
|
||||
icon_format.font_id.clone(),
|
||||
icon_format.color,
|
||||
100.0,
|
||||
);
|
||||
|
||||
// text
|
||||
layout_job_down.append(
|
||||
layout_job.append(
|
||||
&text_down,
|
||||
ctx.style().spacing.item_spacing.x,
|
||||
TextFormat {
|
||||
font_id: config.text_font_id.clone(),
|
||||
color: auto_text_color_received.unwrap_or(ctx.style().visuals.text_color()),
|
||||
valign: Align::Center,
|
||||
..Default::default()
|
||||
},
|
||||
text_format.clone(),
|
||||
);
|
||||
|
||||
// icon
|
||||
let mut layout_job_up = LayoutJob::simple(
|
||||
match self.label_prefix {
|
||||
layout_job.append(
|
||||
&match self.label_prefix {
|
||||
LabelPrefix::Icon | LabelPrefix::IconAndText => {
|
||||
if select_transmitted {
|
||||
egui_phosphor::regular::ARROW_FAT_LINES_UP.to_string()
|
||||
} else {
|
||||
egui_phosphor::regular::ARROW_FAT_UP.to_string()
|
||||
}
|
||||
egui_phosphor::regular::ARROW_FAT_UP.to_string()
|
||||
}
|
||||
LabelPrefix::None | LabelPrefix::Text => String::new(),
|
||||
},
|
||||
config.icon_font_id.clone(),
|
||||
auto_text_color_transmitted.unwrap_or(ctx.style().visuals.selection.stroke.color),
|
||||
100.0,
|
||||
0.0,
|
||||
icon_format.clone(),
|
||||
);
|
||||
|
||||
// text
|
||||
layout_job_up.append(
|
||||
layout_job.append(
|
||||
&text_up,
|
||||
ctx.style().spacing.item_spacing.x,
|
||||
TextFormat {
|
||||
font_id: config.text_font_id.clone(),
|
||||
color: auto_text_color_transmitted.unwrap_or(ctx.style().visuals.text_color()),
|
||||
valign: Align::Center,
|
||||
..Default::default()
|
||||
},
|
||||
text_format.clone(),
|
||||
);
|
||||
|
||||
(
|
||||
Label::new(layout_job_down).selectable(false),
|
||||
Label::new(layout_job_up).selectable(false),
|
||||
)
|
||||
Label::new(layout_job).selectable(false)
|
||||
}
|
||||
|
||||
fn to_pretty_bytes(input_in_bytes: u64, timespan_in_s: u64) -> (u64, String) {
|
||||
fn to_pretty_bytes(input_in_bytes: u64, timespan_in_s: u64) -> String {
|
||||
let input = input_in_bytes as f32 / timespan_in_s as f32;
|
||||
let mut magnitude = input.log(1024f32) as u32;
|
||||
|
||||
@@ -292,30 +248,10 @@ impl Network {
|
||||
let base: Option<DataUnit> = num::FromPrimitive::from_u32(magnitude);
|
||||
let result = input / ((1u64) << (magnitude * 10)) as f32;
|
||||
|
||||
(
|
||||
input as u64,
|
||||
match base {
|
||||
Some(DataUnit::B) => format!("{result:.1} B"),
|
||||
Some(unit) => format!("{result:.1} {unit}iB"),
|
||||
None => String::from("Unknown data unit"),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn show_frame<R>(
|
||||
&self,
|
||||
selected: bool,
|
||||
auto_focus_fill: Option<Color32>,
|
||||
ui: &mut Ui,
|
||||
add_contents: impl FnOnce(&mut Ui) -> R,
|
||||
) {
|
||||
if SelectableFrame::new_auto(selected, auto_focus_fill)
|
||||
.show(ui, add_contents)
|
||||
.clicked()
|
||||
{
|
||||
if let Err(error) = Command::new("cmd.exe").args(["/C", "ncpa"]).spawn() {
|
||||
eprintln!("{}", error);
|
||||
}
|
||||
match base {
|
||||
Some(DataUnit::B) => format!("{result:.1} B"),
|
||||
Some(unit) => format!("{result:.1} {unit}iB"),
|
||||
None => String::from("Unknown data unit"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -323,8 +259,6 @@ impl Network {
|
||||
impl BarWidget for Network {
|
||||
fn render(&mut self, ctx: &Context, ui: &mut Ui, config: &mut RenderConfig) {
|
||||
if self.enable {
|
||||
let is_reversed = matches!(config.alignment, Some(Alignment::Right));
|
||||
|
||||
// widget spacing: make sure to use the same config to call the apply_on_widget function
|
||||
let mut render_config = config.clone();
|
||||
|
||||
@@ -332,102 +266,17 @@ impl BarWidget for Network {
|
||||
let (activity, total_activity) = self.network_activity();
|
||||
|
||||
if self.show_total_activity {
|
||||
for reading in &total_activity {
|
||||
render_config.apply_on_widget(false, ui, |ui| {
|
||||
let select_received = self.auto_select.is_some_and(|f| {
|
||||
f.total_received_over
|
||||
.is_some_and(|o| reading.received.value > o)
|
||||
});
|
||||
let select_transmitted = self.auto_select.is_some_and(|f| {
|
||||
f.total_transmitted_over
|
||||
.is_some_and(|o| reading.transmitted.value > o)
|
||||
});
|
||||
|
||||
let labels = self.reading_to_labels(
|
||||
select_received,
|
||||
select_transmitted,
|
||||
ctx,
|
||||
reading,
|
||||
config.clone(),
|
||||
);
|
||||
|
||||
if is_reversed {
|
||||
self.show_frame(
|
||||
select_transmitted,
|
||||
config.auto_select_fill,
|
||||
ui,
|
||||
|ui| ui.add(labels.1),
|
||||
);
|
||||
self.show_frame(
|
||||
select_received,
|
||||
config.auto_select_fill,
|
||||
ui,
|
||||
|ui| ui.add(labels.0),
|
||||
);
|
||||
} else {
|
||||
self.show_frame(
|
||||
select_received,
|
||||
config.auto_select_fill,
|
||||
ui,
|
||||
|ui| ui.add(labels.0),
|
||||
);
|
||||
self.show_frame(
|
||||
select_transmitted,
|
||||
config.auto_select_fill,
|
||||
ui,
|
||||
|ui| ui.add(labels.1),
|
||||
);
|
||||
}
|
||||
for reading in total_activity {
|
||||
render_config.apply_on_widget(true, ui, |ui| {
|
||||
ui.add(self.reading_to_label(ctx, reading, config.clone()));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if self.show_activity {
|
||||
for reading in &activity {
|
||||
render_config.apply_on_widget(false, ui, |ui| {
|
||||
let select_received = self.auto_select.is_some_and(|f| {
|
||||
f.received_over.is_some_and(|o| reading.received.value > o)
|
||||
});
|
||||
let select_transmitted = self.auto_select.is_some_and(|f| {
|
||||
f.transmitted_over
|
||||
.is_some_and(|o| reading.transmitted.value > o)
|
||||
});
|
||||
|
||||
let labels = self.reading_to_labels(
|
||||
select_received,
|
||||
select_transmitted,
|
||||
ctx,
|
||||
reading,
|
||||
config.clone(),
|
||||
);
|
||||
|
||||
if is_reversed {
|
||||
self.show_frame(
|
||||
select_transmitted,
|
||||
config.auto_select_fill,
|
||||
ui,
|
||||
|ui| ui.add(labels.1),
|
||||
);
|
||||
self.show_frame(
|
||||
select_received,
|
||||
config.auto_select_fill,
|
||||
ui,
|
||||
|ui| ui.add(labels.0),
|
||||
);
|
||||
} else {
|
||||
self.show_frame(
|
||||
select_received,
|
||||
config.auto_select_fill,
|
||||
ui,
|
||||
|ui| ui.add(labels.0),
|
||||
);
|
||||
self.show_frame(
|
||||
select_transmitted,
|
||||
config.auto_select_fill,
|
||||
ui,
|
||||
|ui| ui.add(labels.1),
|
||||
);
|
||||
}
|
||||
for reading in activity {
|
||||
render_config.apply_on_widget(true, ui, |ui| {
|
||||
ui.add(self.reading_to_label(ctx, reading, config.clone()));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -465,9 +314,15 @@ impl BarWidget for Network {
|
||||
);
|
||||
|
||||
render_config.apply_on_widget(false, ui, |ui| {
|
||||
self.show_frame(false, None, ui, |ui| {
|
||||
ui.add(Label::new(layout_job).selectable(false))
|
||||
});
|
||||
if SelectableFrame::new(false)
|
||||
.show(ui, |ui| ui.add(Label::new(layout_job).selectable(false)))
|
||||
.clicked()
|
||||
{
|
||||
if let Err(error) = Command::new("cmd.exe").args(["/C", "ncpa"]).spawn()
|
||||
{
|
||||
eprintln!("{}", error)
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -484,38 +339,19 @@ enum NetworkReadingFormat {
|
||||
Total = 1,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct ReadingValue {
|
||||
value: u64,
|
||||
pretty: String,
|
||||
}
|
||||
|
||||
impl From<(u64, String)> for ReadingValue {
|
||||
fn from(value: (u64, String)) -> Self {
|
||||
Self {
|
||||
value: value.0,
|
||||
pretty: value.1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct NetworkReading {
|
||||
format: NetworkReadingFormat,
|
||||
received: ReadingValue,
|
||||
transmitted: ReadingValue,
|
||||
pub format: NetworkReadingFormat,
|
||||
pub received_text: String,
|
||||
pub transmitted_text: String,
|
||||
}
|
||||
|
||||
impl NetworkReading {
|
||||
fn new(
|
||||
format: NetworkReadingFormat,
|
||||
received: ReadingValue,
|
||||
transmitted: ReadingValue,
|
||||
) -> Self {
|
||||
Self {
|
||||
pub fn new(format: NetworkReadingFormat, received: String, transmitted: String) -> Self {
|
||||
NetworkReading {
|
||||
format,
|
||||
received,
|
||||
transmitted,
|
||||
received_text: received,
|
||||
transmitted_text: transmitted,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use crate::bar::Alignment;
|
||||
use crate::config::LabelPrefix;
|
||||
use crate::render::RenderConfig;
|
||||
use crate::selected_frame::SelectableFrame;
|
||||
@@ -25,10 +24,6 @@ pub struct StorageConfig {
|
||||
pub data_refresh_interval: Option<u64>,
|
||||
/// Display label prefix
|
||||
pub label_prefix: Option<LabelPrefix>,
|
||||
/// Select when the current percentage is over this value [[1-100]]
|
||||
pub auto_select_over: Option<u8>,
|
||||
/// Hide when the current percentage is under this value [[1-100]]
|
||||
pub auto_hide_under: Option<u8>,
|
||||
}
|
||||
|
||||
impl From<StorageConfig> for Storage {
|
||||
@@ -38,30 +33,21 @@ impl From<StorageConfig> for Storage {
|
||||
disks: Disks::new_with_refreshed_list(),
|
||||
data_refresh_interval: value.data_refresh_interval.unwrap_or(10),
|
||||
label_prefix: value.label_prefix.unwrap_or(LabelPrefix::IconAndText),
|
||||
auto_select_over: value.auto_select_over.map(|o| o.clamp(1, 100)),
|
||||
auto_hide_under: value.auto_hide_under.map(|o| o.clamp(1, 100)),
|
||||
last_updated: Instant::now(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct StorageDisk {
|
||||
label: String,
|
||||
selected: bool,
|
||||
}
|
||||
|
||||
pub struct Storage {
|
||||
pub enable: bool,
|
||||
disks: Disks,
|
||||
data_refresh_interval: u64,
|
||||
label_prefix: LabelPrefix,
|
||||
auto_select_over: Option<u8>,
|
||||
auto_hide_under: Option<u8>,
|
||||
last_updated: Instant,
|
||||
}
|
||||
|
||||
impl Storage {
|
||||
fn output(&mut self) -> Vec<StorageDisk> {
|
||||
fn output(&mut self) -> Vec<String> {
|
||||
let now = Instant::now();
|
||||
if now.duration_since(self.last_updated) > Duration::from_secs(self.data_refresh_interval) {
|
||||
self.disks.refresh(true);
|
||||
@@ -75,26 +61,17 @@ impl Storage {
|
||||
let total = disk.total_space();
|
||||
let available = disk.available_space();
|
||||
let used = total - available;
|
||||
let percentage = ((used * 100) / total) as u8;
|
||||
|
||||
let hide = self.auto_hide_under.is_some_and(|u| percentage <= u);
|
||||
|
||||
if !hide {
|
||||
let selected = self.auto_select_over.is_some_and(|o| percentage >= o);
|
||||
|
||||
disks.push(StorageDisk {
|
||||
label: match self.label_prefix {
|
||||
LabelPrefix::Text | LabelPrefix::IconAndText => {
|
||||
format!("{} {}%", mount.to_string_lossy(), percentage)
|
||||
}
|
||||
LabelPrefix::None | LabelPrefix::Icon => format!("{}%", percentage),
|
||||
},
|
||||
selected,
|
||||
})
|
||||
}
|
||||
disks.push(match self.label_prefix {
|
||||
LabelPrefix::Text | LabelPrefix::IconAndText => {
|
||||
format!("{} {}%", mount.to_string_lossy(), (used * 100) / total)
|
||||
}
|
||||
LabelPrefix::None | LabelPrefix::Icon => format!("{}%", (used * 100) / total),
|
||||
})
|
||||
}
|
||||
|
||||
disks.sort_by(|a, b| a.label.cmp(&b.label));
|
||||
disks.sort();
|
||||
disks.reverse();
|
||||
|
||||
disks
|
||||
}
|
||||
@@ -103,16 +80,7 @@ impl Storage {
|
||||
impl BarWidget for Storage {
|
||||
fn render(&mut self, ctx: &Context, ui: &mut Ui, config: &mut RenderConfig) {
|
||||
if self.enable {
|
||||
let mut output = self.output();
|
||||
let is_reversed = matches!(config.alignment, Some(Alignment::Right));
|
||||
|
||||
if is_reversed {
|
||||
output.reverse();
|
||||
}
|
||||
|
||||
for output in output {
|
||||
let auto_text_color = config.auto_select_text.filter(|_| output.selected);
|
||||
|
||||
for output in self.output() {
|
||||
let mut layout_job = LayoutJob::simple(
|
||||
match self.label_prefix {
|
||||
LabelPrefix::Icon | LabelPrefix::IconAndText => {
|
||||
@@ -121,25 +89,23 @@ impl BarWidget for Storage {
|
||||
LabelPrefix::None | LabelPrefix::Text => String::new(),
|
||||
},
|
||||
config.icon_font_id.clone(),
|
||||
auto_text_color.unwrap_or(ctx.style().visuals.selection.stroke.color),
|
||||
ctx.style().visuals.selection.stroke.color,
|
||||
100.0,
|
||||
);
|
||||
|
||||
layout_job.append(
|
||||
&output.label,
|
||||
&output,
|
||||
10.0,
|
||||
TextFormat {
|
||||
font_id: config.text_font_id.clone(),
|
||||
color: auto_text_color.unwrap_or(ctx.style().visuals.text_color()),
|
||||
color: ctx.style().visuals.text_color(),
|
||||
valign: Align::Center,
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
let auto_focus_fill = config.auto_select_fill;
|
||||
|
||||
config.apply_on_widget(false, ui, |ui| {
|
||||
if SelectableFrame::new_auto(output.selected, auto_focus_fill)
|
||||
if SelectableFrame::new(false)
|
||||
.show(ui, |ui| ui.add(Label::new(layout_job).selectable(false)))
|
||||
.clicked()
|
||||
{
|
||||
@@ -147,7 +113,7 @@ impl BarWidget for Storage {
|
||||
.args([
|
||||
"/C",
|
||||
"explorer.exe",
|
||||
output.label.split(' ').collect::<Vec<&str>>()[0],
|
||||
output.split(' ').collect::<Vec<&str>>()[0],
|
||||
])
|
||||
.spawn()
|
||||
{
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
use crate::render::RenderConfig;
|
||||
use crate::widgets::applications::Applications;
|
||||
use crate::widgets::applications::ApplicationsConfig;
|
||||
use crate::widgets::battery::Battery;
|
||||
use crate::widgets::battery::BatteryConfig;
|
||||
use crate::widgets::cpu::Cpu;
|
||||
@@ -35,7 +33,6 @@ pub trait BarWidget {
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub enum WidgetConfig {
|
||||
Applications(ApplicationsConfig),
|
||||
Battery(BatteryConfig),
|
||||
Cpu(CpuConfig),
|
||||
Date(DateConfig),
|
||||
@@ -52,7 +49,6 @@ pub enum WidgetConfig {
|
||||
impl WidgetConfig {
|
||||
pub fn as_boxed_bar_widget(&self) -> Box<dyn BarWidget> {
|
||||
match self {
|
||||
WidgetConfig::Applications(config) => Box::new(Applications::from(config)),
|
||||
WidgetConfig::Battery(config) => Box::new(Battery::from(*config)),
|
||||
WidgetConfig::Cpu(config) => Box::new(Cpu::from(*config)),
|
||||
WidgetConfig::Date(config) => Box::new(Date::from(config.clone())),
|
||||
@@ -69,7 +65,6 @@ impl WidgetConfig {
|
||||
|
||||
pub fn enabled(&self) -> bool {
|
||||
match self {
|
||||
WidgetConfig::Applications(config) => config.enable,
|
||||
WidgetConfig::Battery(config) => config.enable,
|
||||
WidgetConfig::Cpu(config) => config.enable,
|
||||
WidgetConfig::Date(config) => config.enable,
|
||||
@@ -77,7 +72,7 @@ impl WidgetConfig {
|
||||
WidgetConfig::Komorebi(config) => {
|
||||
config.workspaces.as_ref().is_some_and(|w| w.enable)
|
||||
|| config.layout.as_ref().is_some_and(|w| w.enable)
|
||||
|| config.focused_container.as_ref().is_some_and(|w| w.enable)
|
||||
|| config.focused_window.as_ref().is_some_and(|w| w.enable)
|
||||
|| config
|
||||
.configuration_switcher
|
||||
.as_ref()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "komorebi-client"
|
||||
version = "0.1.37"
|
||||
version = "0.1.36"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@@ -5,6 +5,8 @@ pub use komorebi::animation::prefix::AnimationPrefix;
|
||||
pub use komorebi::animation::PerAnimationPrefixConfig;
|
||||
pub use komorebi::asc::ApplicationSpecificConfiguration;
|
||||
pub use komorebi::border_manager::BorderInfo;
|
||||
pub use komorebi::colour::Colour;
|
||||
pub use komorebi::colour::Rgb;
|
||||
pub use komorebi::config_generation::ApplicationConfiguration;
|
||||
pub use komorebi::config_generation::IdWithIdentifier;
|
||||
pub use komorebi::config_generation::IdWithIdentifierAndComment;
|
||||
@@ -12,7 +14,7 @@ pub use komorebi::config_generation::MatchingRule;
|
||||
pub use komorebi::config_generation::MatchingStrategy;
|
||||
pub use komorebi::container::Container;
|
||||
pub use komorebi::core::config_generation::ApplicationConfigurationGenerator;
|
||||
pub use komorebi::core::replace_env_in_path;
|
||||
pub use komorebi::core::resolve_home_path;
|
||||
pub use komorebi::core::AnimationStyle;
|
||||
pub use komorebi::core::ApplicationIdentifier;
|
||||
pub use komorebi::core::Arrangement;
|
||||
@@ -55,7 +57,6 @@ pub use komorebi::AnimationsConfig;
|
||||
pub use komorebi::AppSpecificConfigurationPath;
|
||||
pub use komorebi::AspectRatio;
|
||||
pub use komorebi::BorderColours;
|
||||
pub use komorebi::Colour;
|
||||
pub use komorebi::CrossBoundaryBehaviour;
|
||||
pub use komorebi::GlobalState;
|
||||
pub use komorebi::KomorebiTheme;
|
||||
@@ -63,14 +64,12 @@ pub use komorebi::MonitorConfig;
|
||||
pub use komorebi::Notification;
|
||||
pub use komorebi::NotificationEvent;
|
||||
pub use komorebi::PredefinedAspectRatio;
|
||||
pub use komorebi::Rgb;
|
||||
pub use komorebi::RuleDebug;
|
||||
pub use komorebi::StackbarConfig;
|
||||
pub use komorebi::State;
|
||||
pub use komorebi::StaticConfig;
|
||||
pub use komorebi::SubscribeOptions;
|
||||
pub use komorebi::TabsConfig;
|
||||
pub use komorebi::VirtualDesktopNotification;
|
||||
pub use komorebi::WindowContainerBehaviour;
|
||||
pub use komorebi::WindowsApi;
|
||||
pub use komorebi::WorkspaceConfig;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "komorebi-gui"
|
||||
version = "0.1.37"
|
||||
version = "0.1.36"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@@ -41,9 +41,7 @@ struct BorderColours {
|
||||
single: Color32,
|
||||
stack: Color32,
|
||||
monocle: Color32,
|
||||
floating: Color32,
|
||||
unfocused: Color32,
|
||||
unfocused_locked: Color32,
|
||||
}
|
||||
|
||||
struct BorderConfig {
|
||||
@@ -156,9 +154,7 @@ impl KomorebiGui {
|
||||
single: colour32(global_state.border_colours.single),
|
||||
stack: colour32(global_state.border_colours.stack),
|
||||
monocle: colour32(global_state.border_colours.monocle),
|
||||
floating: colour32(global_state.border_colours.floating),
|
||||
unfocused: colour32(global_state.border_colours.unfocused),
|
||||
unfocused_locked: colour32(global_state.border_colours.unfocused_locked),
|
||||
};
|
||||
|
||||
let border_config = BorderConfig {
|
||||
@@ -381,22 +377,6 @@ impl eframe::App for KomorebiGui {
|
||||
}
|
||||
});
|
||||
|
||||
ui.collapsing("Floating", |ui| {
|
||||
if egui::color_picker::color_picker_color32(
|
||||
ui,
|
||||
&mut self.border_config.border_colours.floating,
|
||||
Alpha::Opaque,
|
||||
) {
|
||||
komorebi_client::send_message(&SocketMessage::BorderColour(
|
||||
WindowKind::Floating,
|
||||
self.border_config.border_colours.floating.r() as u32,
|
||||
self.border_config.border_colours.floating.g() as u32,
|
||||
self.border_config.border_colours.floating.b() as u32,
|
||||
))
|
||||
.unwrap();
|
||||
}
|
||||
});
|
||||
|
||||
ui.collapsing("Unfocused", |ui| {
|
||||
if egui::color_picker::color_picker_color32(
|
||||
ui,
|
||||
@@ -411,22 +391,6 @@ impl eframe::App for KomorebiGui {
|
||||
))
|
||||
.unwrap();
|
||||
}
|
||||
});
|
||||
|
||||
ui.collapsing("Unfocused Locked", |ui| {
|
||||
if egui::color_picker::color_picker_color32(
|
||||
ui,
|
||||
&mut self.border_config.border_colours.unfocused_locked,
|
||||
Alpha::Opaque,
|
||||
) {
|
||||
komorebi_client::send_message(&SocketMessage::BorderColour(
|
||||
WindowKind::UnfocusedLocked,
|
||||
self.border_config.border_colours.unfocused_locked.r() as u32,
|
||||
self.border_config.border_colours.unfocused_locked.g() as u32,
|
||||
self.border_config.border_colours.unfocused_locked.b() as u32,
|
||||
))
|
||||
.unwrap();
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
[package]
|
||||
name = "komorebi-shortcuts"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
whkd-parser = { git = "https://github.com/LGUG2Z/whkd", rev = "29df24ff2dd715655b0366bd2a598837c699a8e9" }
|
||||
whkd-core = { git = "https://github.com/LGUG2Z/whkd", rev = "29df24ff2dd715655b0366bd2a598837c699a8e9" }
|
||||
|
||||
eframe = { workspace = true }
|
||||
dirs = { workspace = true }
|
||||
@@ -1,106 +0,0 @@
|
||||
use eframe::egui::ViewportBuilder;
|
||||
use std::path::PathBuf;
|
||||
use whkd_core::HotkeyBinding;
|
||||
use whkd_core::Whkdrc;
|
||||
|
||||
#[derive(Default)]
|
||||
struct Quicklook {
|
||||
whkdrc: Option<Whkdrc>,
|
||||
filter: String,
|
||||
}
|
||||
|
||||
impl Quicklook {
|
||||
fn new(_cc: &eframe::CreationContext<'_>) -> Self {
|
||||
// Customize egui here with cc.egui_ctx.set_fonts and cc.egui_ctx.set_visuals.
|
||||
// Restore app state using cc.storage (requires the "persistence" feature).
|
||||
// Use the cc.gl (a glow::Context) to create graphics shaders and buffers that you can use
|
||||
// for e.g. egui::PaintCallback.
|
||||
let mut home = std::env::var("WHKD_CONFIG_HOME").map_or_else(
|
||||
|_| {
|
||||
dirs::home_dir()
|
||||
.expect("no home directory found")
|
||||
.join(".config")
|
||||
},
|
||||
|home_path| {
|
||||
let home = PathBuf::from(&home_path);
|
||||
|
||||
if home.as_path().is_dir() {
|
||||
home
|
||||
} else {
|
||||
panic!(
|
||||
"$Env:WHKD_CONFIG_HOME is set to '{home_path}', which is not a valid directory",
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
home.push("whkdrc");
|
||||
|
||||
Self {
|
||||
whkdrc: whkd_parser::load(&home).ok(),
|
||||
filter: String::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl eframe::App for Quicklook {
|
||||
fn update(&mut self, ctx: &eframe::egui::Context, _frame: &mut eframe::Frame) {
|
||||
eframe::egui::CentralPanel::default().show(ctx, |ui| {
|
||||
ui.set_max_width(ui.available_width());
|
||||
ui.set_max_height(ui.available_height());
|
||||
eframe::egui::ScrollArea::vertical().show(ui, |ui| {
|
||||
eframe::egui::Grid::new("grid")
|
||||
.num_columns(2)
|
||||
.striped(true)
|
||||
.spacing([40.0, 4.0])
|
||||
.min_col_width(ui.available_width() / 2.0 - 20.0)
|
||||
.show(ui, |ui| {
|
||||
if let Some(whkdrc) = &self.whkdrc {
|
||||
ui.label("Filter");
|
||||
ui.add(
|
||||
eframe::egui::text_edit::TextEdit::singleline(&mut self.filter)
|
||||
.background_color(ctx.style().visuals.faint_bg_color),
|
||||
);
|
||||
ui.end_row();
|
||||
ui.end_row();
|
||||
|
||||
for binding in &whkdrc.bindings {
|
||||
if is_komorebic_binding(binding) {
|
||||
let keys = binding.keys.join(" + ");
|
||||
if self.filter.is_empty()
|
||||
|| binding.command.contains(&self.filter)
|
||||
{
|
||||
ui.label(keys);
|
||||
ui.label(&binding.command);
|
||||
ui.end_row();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let viewport_builder = ViewportBuilder::default()
|
||||
.with_resizable(true)
|
||||
.with_decorations(false);
|
||||
|
||||
let native_options = eframe::NativeOptions {
|
||||
viewport: viewport_builder,
|
||||
centered: true,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
eframe::run_native(
|
||||
"komorebi-shortcuts",
|
||||
native_options,
|
||||
Box::new(|cc| Ok(Box::new(Quicklook::new(cc)))),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn is_komorebic_binding(binding: &HotkeyBinding) -> bool {
|
||||
binding.command.starts_with("komorebic")
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "komorebi-themes"
|
||||
version = "0.1.37"
|
||||
version = "0.1.36"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
@@ -8,13 +8,7 @@ base16-egui-themes = { git = "https://github.com/LGUG2Z/base16-egui-themes", rev
|
||||
catppuccin-egui = { git = "https://github.com/LGUG2Z/catppuccin-egui", rev = "bdaff30959512c4f7ee7304117076a48633d777f", default-features = false, features = ["egui31"] }
|
||||
#catppuccin-egui = { version = "5", default-features = false, features = ["egui30"] }
|
||||
eframe = { workspace = true }
|
||||
schemars = { workspace = true, optional = true }
|
||||
schemars = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_variant = "0.1"
|
||||
strum = { workspace = true }
|
||||
hex_color = { version = "3", features = ["serde"] }
|
||||
flavours = { git = "https://github.com/LGUG2Z/flavours", version = "0.7.2" }
|
||||
|
||||
[features]
|
||||
default = ["schemars"]
|
||||
schemars = ["dep:schemars"]
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
use crate::colour::Colour;
|
||||
use crate::colour::Hex;
|
||||
use crate::Base16ColourPalette;
|
||||
use hex_color::HexColor;
|
||||
use std::collections::VecDeque;
|
||||
use std::fmt::Display;
|
||||
use std::fmt::Formatter;
|
||||
use std::path::Path;
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Debug, Default, Copy, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub enum ThemeVariant {
|
||||
#[default]
|
||||
Dark,
|
||||
Light,
|
||||
}
|
||||
|
||||
impl Display for ThemeVariant {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ThemeVariant::Dark => write!(f, "dark"),
|
||||
ThemeVariant::Light => write!(f, "light"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ThemeVariant> for flavours::operations::generate::Mode {
|
||||
fn from(value: ThemeVariant) -> Self {
|
||||
match value {
|
||||
ThemeVariant::Dark => Self::Dark,
|
||||
ThemeVariant::Light => Self::Light,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_base16_palette(
|
||||
image_path: &Path,
|
||||
variant: ThemeVariant,
|
||||
) -> Result<Base16ColourPalette, hex_color::ParseHexColorError> {
|
||||
Base16ColourPalette::try_from(
|
||||
&flavours::operations::generate::generate(image_path, variant.into(), false)
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
}
|
||||
|
||||
impl TryFrom<&VecDeque<String>> for Base16ColourPalette {
|
||||
type Error = hex_color::ParseHexColorError;
|
||||
|
||||
fn try_from(value: &VecDeque<String>) -> Result<Self, Self::Error> {
|
||||
let fixed = value.iter().map(|s| format!("#{s}")).collect::<Vec<_>>();
|
||||
if fixed.len() != 16 {
|
||||
return Err(hex_color::ParseHexColorError::Empty);
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
base_00: Colour::Hex(Hex(HexColor::parse(&fixed[0])?)),
|
||||
base_01: Colour::Hex(Hex(HexColor::parse(&fixed[1])?)),
|
||||
base_02: Colour::Hex(Hex(HexColor::parse(&fixed[2])?)),
|
||||
base_03: Colour::Hex(Hex(HexColor::parse(&fixed[3])?)),
|
||||
base_04: Colour::Hex(Hex(HexColor::parse(&fixed[4])?)),
|
||||
base_05: Colour::Hex(Hex(HexColor::parse(&fixed[5])?)),
|
||||
base_06: Colour::Hex(Hex(HexColor::parse(&fixed[6])?)),
|
||||
base_07: Colour::Hex(Hex(HexColor::parse(&fixed[7])?)),
|
||||
base_08: Colour::Hex(Hex(HexColor::parse(&fixed[8])?)),
|
||||
base_09: Colour::Hex(Hex(HexColor::parse(&fixed[9])?)),
|
||||
base_0a: Colour::Hex(Hex(HexColor::parse(&fixed[10])?)),
|
||||
base_0b: Colour::Hex(Hex(HexColor::parse(&fixed[11])?)),
|
||||
base_0c: Colour::Hex(Hex(HexColor::parse(&fixed[12])?)),
|
||||
base_0d: Colour::Hex(Hex(HexColor::parse(&fixed[13])?)),
|
||||
base_0e: Colour::Hex(Hex(HexColor::parse(&fixed[14])?)),
|
||||
base_0f: Colour::Hex(Hex(HexColor::parse(&fixed[15])?)),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,32 +1,18 @@
|
||||
#![warn(clippy::all)]
|
||||
#![allow(clippy::missing_errors_doc)]
|
||||
|
||||
pub mod colour;
|
||||
mod generator;
|
||||
|
||||
pub use generator::generate_base16_palette;
|
||||
pub use generator::ThemeVariant;
|
||||
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use strum::Display;
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use crate::colour::Colour;
|
||||
pub use base16_egui_themes::Base16;
|
||||
pub use catppuccin_egui;
|
||||
use eframe::egui::style::Selection;
|
||||
use eframe::egui::style::WidgetVisuals;
|
||||
use eframe::egui::style::Widgets;
|
||||
pub use eframe::egui::Color32;
|
||||
use eframe::egui::Shadow;
|
||||
use eframe::egui::Stroke;
|
||||
use eframe::egui::Style;
|
||||
use eframe::egui::Visuals;
|
||||
use serde_variant::to_variant_name;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum Theme {
|
||||
/// A theme from catppuccin-egui
|
||||
@@ -39,140 +25,6 @@ pub enum Theme {
|
||||
name: Base16,
|
||||
accent: Option<Base16Value>,
|
||||
},
|
||||
/// A custom base16 palette
|
||||
Custom {
|
||||
palette: Box<Base16ColourPalette>,
|
||||
accent: Option<Base16Value>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
|
||||
pub struct Base16ColourPalette {
|
||||
pub base_00: Colour,
|
||||
pub base_01: Colour,
|
||||
pub base_02: Colour,
|
||||
pub base_03: Colour,
|
||||
pub base_04: Colour,
|
||||
pub base_05: Colour,
|
||||
pub base_06: Colour,
|
||||
pub base_07: Colour,
|
||||
pub base_08: Colour,
|
||||
pub base_09: Colour,
|
||||
pub base_0a: Colour,
|
||||
pub base_0b: Colour,
|
||||
pub base_0c: Colour,
|
||||
pub base_0d: Colour,
|
||||
pub base_0e: Colour,
|
||||
pub base_0f: Colour,
|
||||
}
|
||||
|
||||
impl Base16ColourPalette {
|
||||
pub fn background(self) -> Color32 {
|
||||
self.base_01.into()
|
||||
}
|
||||
pub fn style(self) -> Style {
|
||||
let original = Style::default();
|
||||
Style {
|
||||
visuals: Visuals {
|
||||
widgets: Widgets {
|
||||
noninteractive: WidgetVisuals {
|
||||
bg_fill: self.base_01.into(),
|
||||
weak_bg_fill: self.base_01.into(),
|
||||
bg_stroke: Stroke {
|
||||
color: self.base_02.into(),
|
||||
..original.visuals.widgets.noninteractive.bg_stroke
|
||||
},
|
||||
fg_stroke: Stroke {
|
||||
color: self.base_05.into(),
|
||||
..original.visuals.widgets.noninteractive.fg_stroke
|
||||
},
|
||||
..original.visuals.widgets.noninteractive
|
||||
},
|
||||
inactive: WidgetVisuals {
|
||||
bg_fill: self.base_02.into(),
|
||||
weak_bg_fill: self.base_02.into(),
|
||||
bg_stroke: Stroke {
|
||||
color: Color32::from_rgba_premultiplied(0, 0, 0, 0),
|
||||
..original.visuals.widgets.inactive.bg_stroke
|
||||
},
|
||||
fg_stroke: Stroke {
|
||||
color: self.base_05.into(),
|
||||
..original.visuals.widgets.inactive.fg_stroke
|
||||
},
|
||||
..original.visuals.widgets.inactive
|
||||
},
|
||||
hovered: WidgetVisuals {
|
||||
bg_fill: self.base_02.into(),
|
||||
weak_bg_fill: self.base_02.into(),
|
||||
bg_stroke: Stroke {
|
||||
color: self.base_03.into(),
|
||||
..original.visuals.widgets.hovered.bg_stroke
|
||||
},
|
||||
fg_stroke: Stroke {
|
||||
color: self.base_06.into(),
|
||||
..original.visuals.widgets.hovered.fg_stroke
|
||||
},
|
||||
..original.visuals.widgets.hovered
|
||||
},
|
||||
active: WidgetVisuals {
|
||||
bg_fill: self.base_02.into(),
|
||||
weak_bg_fill: self.base_02.into(),
|
||||
bg_stroke: Stroke {
|
||||
color: self.base_03.into(),
|
||||
..original.visuals.widgets.hovered.bg_stroke
|
||||
},
|
||||
fg_stroke: Stroke {
|
||||
color: self.base_06.into(),
|
||||
..original.visuals.widgets.hovered.fg_stroke
|
||||
},
|
||||
..original.visuals.widgets.active
|
||||
},
|
||||
open: WidgetVisuals {
|
||||
bg_fill: self.base_01.into(),
|
||||
weak_bg_fill: self.base_01.into(),
|
||||
bg_stroke: Stroke {
|
||||
color: self.base_02.into(),
|
||||
..original.visuals.widgets.open.bg_stroke
|
||||
},
|
||||
fg_stroke: Stroke {
|
||||
color: self.base_06.into(),
|
||||
..original.visuals.widgets.open.fg_stroke
|
||||
},
|
||||
..original.visuals.widgets.open
|
||||
},
|
||||
},
|
||||
selection: Selection {
|
||||
bg_fill: self.base_02.into(),
|
||||
stroke: Stroke {
|
||||
color: self.base_06.into(),
|
||||
..original.visuals.selection.stroke
|
||||
},
|
||||
},
|
||||
hyperlink_color: self.base_08.into(),
|
||||
faint_bg_color: Color32::from_rgba_premultiplied(0, 0, 0, 0),
|
||||
extreme_bg_color: self.base_00.into(),
|
||||
code_bg_color: self.base_02.into(),
|
||||
warn_fg_color: self.base_0c.into(),
|
||||
error_fg_color: self.base_0b.into(),
|
||||
window_shadow: Shadow {
|
||||
color: Color32::from_rgba_premultiplied(0, 0, 0, 96),
|
||||
..original.visuals.window_shadow
|
||||
},
|
||||
window_fill: self.base_01.into(),
|
||||
window_stroke: Stroke {
|
||||
color: self.base_02.into(),
|
||||
..original.visuals.window_stroke
|
||||
},
|
||||
panel_fill: self.base_01.into(),
|
||||
popup_shadow: Shadow {
|
||||
color: Color32::from_rgba_premultiplied(0, 0, 0, 96),
|
||||
..original.visuals.popup_shadow
|
||||
},
|
||||
..original.visuals
|
||||
},
|
||||
..original
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Theme {
|
||||
@@ -193,7 +45,6 @@ impl Theme {
|
||||
.to_string()
|
||||
})
|
||||
.collect(),
|
||||
Theme::Custom { .. } => vec!["Custom".to_string()],
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -219,50 +70,25 @@ pub enum Base16Value {
|
||||
Base0F,
|
||||
}
|
||||
|
||||
pub enum Base16Wrapper {
|
||||
Base16(Base16),
|
||||
Custom(Box<Base16ColourPalette>),
|
||||
}
|
||||
|
||||
impl Base16Value {
|
||||
pub fn color32(&self, theme: Base16Wrapper) -> Color32 {
|
||||
match theme {
|
||||
Base16Wrapper::Base16(theme) => match self {
|
||||
Base16Value::Base00 => theme.base00(),
|
||||
Base16Value::Base01 => theme.base01(),
|
||||
Base16Value::Base02 => theme.base02(),
|
||||
Base16Value::Base03 => theme.base03(),
|
||||
Base16Value::Base04 => theme.base04(),
|
||||
Base16Value::Base05 => theme.base05(),
|
||||
Base16Value::Base06 => theme.base06(),
|
||||
Base16Value::Base07 => theme.base07(),
|
||||
Base16Value::Base08 => theme.base08(),
|
||||
Base16Value::Base09 => theme.base09(),
|
||||
Base16Value::Base0A => theme.base0a(),
|
||||
Base16Value::Base0B => theme.base0b(),
|
||||
Base16Value::Base0C => theme.base0c(),
|
||||
Base16Value::Base0D => theme.base0d(),
|
||||
Base16Value::Base0E => theme.base0e(),
|
||||
Base16Value::Base0F => theme.base0f(),
|
||||
},
|
||||
Base16Wrapper::Custom(colours) => match self {
|
||||
Base16Value::Base00 => colours.base_00.into(),
|
||||
Base16Value::Base01 => colours.base_01.into(),
|
||||
Base16Value::Base02 => colours.base_02.into(),
|
||||
Base16Value::Base03 => colours.base_03.into(),
|
||||
Base16Value::Base04 => colours.base_04.into(),
|
||||
Base16Value::Base05 => colours.base_05.into(),
|
||||
Base16Value::Base06 => colours.base_06.into(),
|
||||
Base16Value::Base07 => colours.base_07.into(),
|
||||
Base16Value::Base08 => colours.base_08.into(),
|
||||
Base16Value::Base09 => colours.base_09.into(),
|
||||
Base16Value::Base0A => colours.base_0a.into(),
|
||||
Base16Value::Base0B => colours.base_0b.into(),
|
||||
Base16Value::Base0C => colours.base_0c.into(),
|
||||
Base16Value::Base0D => colours.base_0d.into(),
|
||||
Base16Value::Base0E => colours.base_0e.into(),
|
||||
Base16Value::Base0F => colours.base_0f.into(),
|
||||
},
|
||||
pub fn color32(&self, theme: Base16) -> Color32 {
|
||||
match self {
|
||||
Base16Value::Base00 => theme.base00(),
|
||||
Base16Value::Base01 => theme.base01(),
|
||||
Base16Value::Base02 => theme.base02(),
|
||||
Base16Value::Base03 => theme.base03(),
|
||||
Base16Value::Base04 => theme.base04(),
|
||||
Base16Value::Base05 => theme.base05(),
|
||||
Base16Value::Base06 => theme.base06(),
|
||||
Base16Value::Base07 => theme.base07(),
|
||||
Base16Value::Base08 => theme.base08(),
|
||||
Base16Value::Base09 => theme.base09(),
|
||||
Base16Value::Base0A => theme.base0a(),
|
||||
Base16Value::Base0B => theme.base0b(),
|
||||
Base16Value::Base0C => theme.base0c(),
|
||||
Base16Value::Base0D => theme.base0d(),
|
||||
Base16Value::Base0E => theme.base0e(),
|
||||
Base16Value::Base0F => theme.base0f(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "komorebi"
|
||||
version = "0.1.37"
|
||||
version = "0.1.36"
|
||||
description = "A tiling window manager for Windows"
|
||||
repository = "https://github.com/LGUG2Z/komorebi"
|
||||
edition = "2021"
|
||||
@@ -19,6 +19,7 @@ ctrlc = { version = "3", features = ["termination"] }
|
||||
dirs = { workspace = true }
|
||||
dunce = { workspace = true }
|
||||
getset = "0.1"
|
||||
hex_color = { version = "3", features = ["serde"] }
|
||||
hotwatch = { workspace = true }
|
||||
lazy_static = { workspace = true }
|
||||
miow = "0.6"
|
||||
@@ -27,7 +28,6 @@ net2 = "0.2"
|
||||
os_info = "3.10"
|
||||
parking_lot = "0.12"
|
||||
paste = { workspace = true }
|
||||
powershell_script = "1.0"
|
||||
regex = "1"
|
||||
schemars = { workspace = true, optional = true }
|
||||
serde = { workspace = true }
|
||||
@@ -49,7 +49,6 @@ windows-implement = { workspace = true }
|
||||
windows-interface = { workspace = true }
|
||||
winput = "0.2"
|
||||
winreg = "0.55"
|
||||
serde_with = { version = "3.12", features = ["schemars_0_8"] }
|
||||
|
||||
[build-dependencies]
|
||||
shadow-rs = { workspace = true }
|
||||
|
||||
@@ -355,61 +355,6 @@ impl Ease for EaseInOutBounce {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CubicBezier {
|
||||
pub x1: f64,
|
||||
pub y1: f64,
|
||||
pub x2: f64,
|
||||
pub y2: f64,
|
||||
}
|
||||
|
||||
impl CubicBezier {
|
||||
fn x(&self, s: f64) -> f64 {
|
||||
3.0 * self.x1 * s * (1.0 - s).powi(2) + 3.0 * self.x2 * s.powi(2) * (1.0 - s) + s.powi(3)
|
||||
}
|
||||
|
||||
fn y(&self, s: f64) -> f64 {
|
||||
3.0 * self.y1 * s * (1.0 - s).powi(2) + 3.0 * self.y2 * s.powi(2) * (1.0 - s) + s.powi(3)
|
||||
}
|
||||
|
||||
fn dx_ds(&self, s: f64) -> f64 {
|
||||
3.0 * self.x1 * (1.0 - s) * (1.0 - 3.0 * s)
|
||||
+ 3.0 * self.x2 * (2.0 * s - 3.0 * s.powi(2))
|
||||
+ 3.0 * s.powi(2)
|
||||
}
|
||||
|
||||
fn find_s(&self, t: f64) -> f64 {
|
||||
if t <= 0.0 {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
if t >= 1.0 {
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
let mut s = t;
|
||||
|
||||
for _ in 0..8 {
|
||||
let x_val = self.x(s);
|
||||
let dx_val = self.dx_ds(s);
|
||||
if dx_val.abs() < 1e-6 {
|
||||
break;
|
||||
}
|
||||
let delta = (x_val - t) / dx_val;
|
||||
s = (s - delta).clamp(0.0, 1.0);
|
||||
if delta.abs() < 1e-6 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
s
|
||||
}
|
||||
|
||||
fn evaluate(&self, t: f64) -> f64 {
|
||||
let s = self.find_s(t.clamp(0.0, 1.0));
|
||||
self.y(s)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_ease_func(t: f64, style: AnimationStyle) -> f64 {
|
||||
match style {
|
||||
AnimationStyle::Linear => Linear::evaluate(t),
|
||||
@@ -442,6 +387,5 @@ pub fn apply_ease_func(t: f64, style: AnimationStyle) -> f64 {
|
||||
AnimationStyle::EaseInBounce => EaseInBounce::evaluate(t),
|
||||
AnimationStyle::EaseOutBounce => EaseOutBounce::evaluate(t),
|
||||
AnimationStyle::EaseInOutBounce => EaseInOutBounce::evaluate(t),
|
||||
AnimationStyle::CubicBezier(x1, y1, x2, y2) => CubicBezier { x1, y1, x2, y2 }.evaluate(t),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,11 +9,13 @@ use crate::core::Rect;
|
||||
use crate::windows_api;
|
||||
use crate::WindowsApi;
|
||||
use crate::WINDOWS_11;
|
||||
use color_eyre::eyre::anyhow;
|
||||
use std::collections::HashMap;
|
||||
use std::ops::Deref;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::mpsc;
|
||||
use std::sync::LazyLock;
|
||||
use std::sync::OnceLock;
|
||||
use windows::Win32::Foundation::FALSE;
|
||||
use windows::Win32::Foundation::HWND;
|
||||
use windows::Win32::Foundation::LPARAM;
|
||||
@@ -116,7 +118,7 @@ pub struct Border {
|
||||
pub hwnd: isize,
|
||||
pub id: String,
|
||||
pub monitor_idx: Option<usize>,
|
||||
pub render_target: Option<RenderTarget>,
|
||||
pub render_target: OnceLock<RenderTarget>,
|
||||
pub tracking_hwnd: isize,
|
||||
pub window_rect: Rect,
|
||||
pub window_kind: WindowKind,
|
||||
@@ -134,7 +136,7 @@ impl From<isize> for Border {
|
||||
hwnd: value,
|
||||
id: String::new(),
|
||||
monitor_idx: None,
|
||||
render_target: None,
|
||||
render_target: OnceLock::new(),
|
||||
tracking_hwnd: 0,
|
||||
window_rect: Rect::default(),
|
||||
window_kind: WindowKind::Unfocused,
|
||||
@@ -182,7 +184,7 @@ impl Border {
|
||||
hwnd: 0,
|
||||
id: container_id,
|
||||
monitor_idx: Some(monitor_idx),
|
||||
render_target: None,
|
||||
render_target: OnceLock::new(),
|
||||
tracking_hwnd,
|
||||
window_rect: WindowsApi::window_rect(tracking_hwnd).unwrap_or_default(),
|
||||
window_kind: WindowKind::Unfocused,
|
||||
@@ -241,14 +243,8 @@ impl Border {
|
||||
let _ = DwmEnableBlurBehindWindow(border.hwnd(), &bh);
|
||||
}
|
||||
|
||||
border.update_brushes()?;
|
||||
|
||||
Ok(border)
|
||||
}
|
||||
|
||||
pub fn update_brushes(&mut self) -> color_eyre::Result<()> {
|
||||
let hwnd_render_target_properties = D2D1_HWND_RENDER_TARGET_PROPERTIES {
|
||||
hwnd: HWND(windows_api::as_ptr!(self.hwnd)),
|
||||
hwnd: HWND(windows_api::as_ptr!(border.hwnd)),
|
||||
pixelSize: Default::default(),
|
||||
presentOptions: D2D1_PRESENT_OPTIONS_IMMEDIATELY,
|
||||
};
|
||||
@@ -269,7 +265,7 @@ impl Border {
|
||||
.CreateHwndRenderTarget(&render_target_properties, &hwnd_render_target_properties)
|
||||
} {
|
||||
Ok(render_target) => unsafe {
|
||||
self.brush_properties = *BRUSH_PROPERTIES.deref();
|
||||
border.brush_properties = *BRUSH_PROPERTIES.deref();
|
||||
for window_kind in [
|
||||
WindowKind::Single,
|
||||
WindowKind::Stack,
|
||||
@@ -287,18 +283,24 @@ impl Border {
|
||||
};
|
||||
|
||||
if let Ok(brush) =
|
||||
render_target.CreateSolidColorBrush(&color, Some(&self.brush_properties))
|
||||
render_target.CreateSolidColorBrush(&color, Some(&border.brush_properties))
|
||||
{
|
||||
self.brushes.insert(window_kind, brush);
|
||||
border.brushes.insert(window_kind, brush);
|
||||
}
|
||||
}
|
||||
|
||||
render_target.SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
|
||||
|
||||
self.render_target = Some(RenderTarget(render_target));
|
||||
if border
|
||||
.render_target
|
||||
.set(RenderTarget(render_target.clone()))
|
||||
.is_err()
|
||||
{
|
||||
return Err(anyhow!("could not store border render target"));
|
||||
}
|
||||
|
||||
self.rounded_rect = {
|
||||
let radius = 8.0 + self.width as f32 / 2.0;
|
||||
border.rounded_rect = {
|
||||
let radius = 8.0 + border.width as f32 / 2.0;
|
||||
D2D1_ROUNDED_RECT {
|
||||
rect: Default::default(),
|
||||
radiusX: radius,
|
||||
@@ -306,7 +308,7 @@ impl Border {
|
||||
}
|
||||
};
|
||||
|
||||
Ok(())
|
||||
Ok(border)
|
||||
},
|
||||
Err(error) => Err(error.into()),
|
||||
}
|
||||
@@ -393,7 +395,7 @@ impl Border {
|
||||
}
|
||||
|
||||
if !rect.is_same_size_as(&old_rect) {
|
||||
if let Some(render_target) = (*border_pointer).render_target.as_ref() {
|
||||
if let Some(render_target) = (*border_pointer).render_target.get() {
|
||||
let border_width = (*border_pointer).width;
|
||||
let border_offset = (*border_pointer).offset;
|
||||
|
||||
@@ -475,7 +477,7 @@ impl Border {
|
||||
tracing::error!("failed to update border position {error}");
|
||||
}
|
||||
|
||||
if let Some(render_target) = (*border_pointer).render_target.as_ref() {
|
||||
if let Some(render_target) = (*border_pointer).render_target.get() {
|
||||
(*border_pointer).width = BORDER_WIDTH.load(Ordering::Relaxed);
|
||||
(*border_pointer).offset = BORDER_OFFSET.load(Ordering::Relaxed);
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@ use crate::core::WindowKind;
|
||||
use crate::ring::Ring;
|
||||
use crate::windows_api;
|
||||
use crate::workspace::WorkspaceLayer;
|
||||
use crate::Colour;
|
||||
use crate::Rgb;
|
||||
use crate::WindowManager;
|
||||
use crate::WindowsApi;
|
||||
use border::border_hwnds;
|
||||
@@ -15,8 +17,6 @@ use crossbeam_channel::Receiver;
|
||||
use crossbeam_channel::Sender;
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use crossbeam_utils::atomic::AtomicConsume;
|
||||
use komorebi_themes::colour::Colour;
|
||||
use komorebi_themes::colour::Rgb;
|
||||
use lazy_static::lazy_static;
|
||||
use parking_lot::Mutex;
|
||||
use serde::Deserialize;
|
||||
@@ -73,10 +73,7 @@ impl Deref for RenderTarget {
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Notification {
|
||||
Update(Option<isize>),
|
||||
ForceUpdate,
|
||||
}
|
||||
pub struct Notification(pub Option<isize>);
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq)]
|
||||
pub struct BorderInfo {
|
||||
@@ -105,21 +102,16 @@ fn event_rx() -> Receiver<Notification> {
|
||||
}
|
||||
|
||||
pub fn window_border(hwnd: isize) -> Option<BorderInfo> {
|
||||
let id = WINDOWS_BORDERS.lock().get(&hwnd)?.clone();
|
||||
BORDER_STATE.lock().get(&id).map(|b| BorderInfo {
|
||||
border_hwnd: b.hwnd,
|
||||
window_kind: b.window_kind,
|
||||
WINDOWS_BORDERS.lock().get(&hwnd).and_then(|id| {
|
||||
BORDER_STATE.lock().get(id).map(|b| BorderInfo {
|
||||
border_hwnd: b.hwnd,
|
||||
window_kind: b.window_kind,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn send_notification(hwnd: Option<isize>) {
|
||||
if event_tx().try_send(Notification::Update(hwnd)).is_err() {
|
||||
tracing::warn!("channel is full; dropping notification")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_force_update() {
|
||||
if event_tx().try_send(Notification::ForceUpdate).is_err() {
|
||||
if event_tx().try_send(Notification(hwnd)).is_err() {
|
||||
tracing::warn!("channel is full; dropping notification")
|
||||
}
|
||||
}
|
||||
@@ -135,8 +127,6 @@ pub fn destroy_all_borders() -> color_eyre::Result<()> {
|
||||
let _ = destroy_border(border);
|
||||
}
|
||||
|
||||
drop(borders);
|
||||
|
||||
WINDOWS_BORDERS.lock().clear();
|
||||
|
||||
let mut remaining_hwnds = vec![];
|
||||
@@ -185,7 +175,7 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
|
||||
tracing::info!("listening");
|
||||
|
||||
let receiver = event_rx();
|
||||
event_tx().send(Notification::Update(None))?;
|
||||
event_tx().send(Notification(None))?;
|
||||
|
||||
let mut previous_snapshot = Ring::default();
|
||||
let mut previous_pending_move_op = None;
|
||||
@@ -271,75 +261,64 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
|
||||
}
|
||||
}
|
||||
BorderImplementation::Komorebi => {
|
||||
let should_process_notification = match notification {
|
||||
Notification::Update(notification_hwnd) => {
|
||||
let mut should_process_notification = true;
|
||||
let mut should_process_notification = true;
|
||||
|
||||
if monitors == previous_snapshot
|
||||
// handle the window dragging edge case
|
||||
&& pending_move_op == previous_pending_move_op
|
||||
{
|
||||
should_process_notification = false;
|
||||
}
|
||||
if monitors == previous_snapshot
|
||||
// handle the window dragging edge case
|
||||
&& pending_move_op == previous_pending_move_op
|
||||
{
|
||||
should_process_notification = false;
|
||||
}
|
||||
|
||||
// handle the pause edge case
|
||||
if is_paused && !previous_is_paused {
|
||||
// handle the pause edge case
|
||||
if is_paused && !previous_is_paused {
|
||||
should_process_notification = true;
|
||||
}
|
||||
|
||||
// handle the unpause edge case
|
||||
if previous_is_paused && !is_paused {
|
||||
should_process_notification = true;
|
||||
}
|
||||
|
||||
// handle the retile edge case
|
||||
if !should_process_notification && BORDER_STATE.lock().is_empty() {
|
||||
should_process_notification = true;
|
||||
}
|
||||
|
||||
// when we switch focus to/from a floating window
|
||||
let switch_focus_to_from_floating_window = floating_window_hwnds.iter().any(|fw| {
|
||||
// if we switch focus to a floating window
|
||||
fw == ¬ification.0.unwrap_or_default() ||
|
||||
// if there is any floating window with a `WindowKind::Floating` border
|
||||
// that no longer is the foreground window then we need to update that
|
||||
// border.
|
||||
(fw != &foreground_window
|
||||
&& window_border(*fw)
|
||||
.is_some_and(|b| b.window_kind == WindowKind::Floating))
|
||||
});
|
||||
|
||||
// when the focused window has an `Unfocused` border kind, usually this happens if
|
||||
// we focus an admin window and then refocus the previously focused window. For
|
||||
// komorebi it will have the same state has before, however the previously focused
|
||||
// window changed its border to unfocused so now we need to update it again.
|
||||
if !should_process_notification
|
||||
&& window_border(notification.0.unwrap_or_default())
|
||||
.is_some_and(|b| b.window_kind == WindowKind::Unfocused)
|
||||
{
|
||||
should_process_notification = true;
|
||||
}
|
||||
|
||||
if !should_process_notification && switch_focus_to_from_floating_window {
|
||||
should_process_notification = true;
|
||||
}
|
||||
|
||||
if !should_process_notification {
|
||||
if let Some(ref previous) = previous_notification {
|
||||
if previous.0.unwrap_or_default() != notification.0.unwrap_or_default() {
|
||||
should_process_notification = true;
|
||||
}
|
||||
|
||||
// handle the unpause edge case
|
||||
if previous_is_paused && !is_paused {
|
||||
should_process_notification = true;
|
||||
}
|
||||
|
||||
// handle the retile edge case
|
||||
if !should_process_notification && BORDER_STATE.lock().is_empty() {
|
||||
should_process_notification = true;
|
||||
}
|
||||
|
||||
// when we switch focus to/from a floating window
|
||||
let switch_focus_to_from_floating_window =
|
||||
floating_window_hwnds.iter().any(|fw| {
|
||||
// if we switch focus to a floating window
|
||||
fw == ¬ification_hwnd.unwrap_or_default() ||
|
||||
// if there is any floating window with a `WindowKind::Floating` border
|
||||
// that no longer is the foreground window then we need to update that
|
||||
// border.
|
||||
(fw != &foreground_window
|
||||
&& window_border(*fw)
|
||||
.is_some_and(|b| b.window_kind == WindowKind::Floating))
|
||||
});
|
||||
|
||||
// when the focused window has an `Unfocused` border kind, usually this happens if
|
||||
// we focus an admin window and then refocus the previously focused window. For
|
||||
// komorebi it will have the same state has before, however the previously focused
|
||||
// window changed its border to unfocused so now we need to update it again.
|
||||
if !should_process_notification
|
||||
&& window_border(notification_hwnd.unwrap_or_default())
|
||||
.is_some_and(|b| b.window_kind == WindowKind::Unfocused)
|
||||
{
|
||||
should_process_notification = true;
|
||||
}
|
||||
|
||||
if !should_process_notification && switch_focus_to_from_floating_window {
|
||||
should_process_notification = true;
|
||||
}
|
||||
|
||||
if !should_process_notification {
|
||||
if let Some(Notification::Update(ref previous)) = previous_notification
|
||||
{
|
||||
if previous.unwrap_or_default()
|
||||
!= notification_hwnd.unwrap_or_default()
|
||||
{
|
||||
should_process_notification = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
should_process_notification
|
||||
}
|
||||
Notification::ForceUpdate => true,
|
||||
};
|
||||
}
|
||||
|
||||
if !should_process_notification {
|
||||
tracing::trace!("monitor state matches latest snapshot, skipping notification");
|
||||
@@ -436,11 +415,6 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
|
||||
|
||||
if new_border {
|
||||
border.set_position(&rect, focused_window_hwnd)?;
|
||||
} else if matches!(notification, Notification::ForceUpdate) {
|
||||
// Update the border brushes if there was a forced update
|
||||
// notification and this is not a new border (new border's
|
||||
// already have their brushes updated on creation)
|
||||
border.update_brushes()?;
|
||||
}
|
||||
|
||||
border.invalidate();
|
||||
@@ -570,20 +544,12 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
|
||||
border.window_rect = rect;
|
||||
|
||||
let layer_changed = previous_layer != workspace_layer;
|
||||
let forced_update = matches!(notification, Notification::ForceUpdate);
|
||||
|
||||
let should_invalidate = new_border
|
||||
|| (last_focus_state != new_focus_state)
|
||||
|| layer_changed
|
||||
|| forced_update;
|
||||
|| layer_changed;
|
||||
|
||||
if should_invalidate {
|
||||
if forced_update && !new_border {
|
||||
// Update the border brushes if there was a forced update
|
||||
// notification and this is not a new border (new border's
|
||||
// already have their brushes updated on creation)
|
||||
border.update_brushes()?;
|
||||
}
|
||||
border.set_position(&rect, focused_window_hwnd)?;
|
||||
border.invalidate();
|
||||
}
|
||||
@@ -628,21 +594,12 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
|
||||
border.window_rect = rect;
|
||||
|
||||
let layer_changed = previous_layer != workspace_layer;
|
||||
let forced_update =
|
||||
matches!(notification, Notification::ForceUpdate);
|
||||
|
||||
let should_invalidate = new_border
|
||||
|| (last_focus_state != new_focus_state)
|
||||
|| layer_changed
|
||||
|| forced_update;
|
||||
|| layer_changed;
|
||||
|
||||
if should_invalidate {
|
||||
if forced_update && !new_border {
|
||||
// Update the border brushes if there was a forced update
|
||||
// notification and this is not a new border (new border's
|
||||
// already have their brushes updated on creation)
|
||||
border.update_brushes()?;
|
||||
}
|
||||
border.set_position(&rect, window.hwnd)?;
|
||||
border.invalidate();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use hex_color::HexColor;
|
||||
use komorebi_themes::Color32;
|
||||
#[cfg(feature = "schemars")]
|
||||
use schemars::gen::SchemaGenerator;
|
||||
#[cfg(feature = "schemars")]
|
||||
@@ -8,7 +9,6 @@ use schemars::schema::Schema;
|
||||
#[cfg(feature = "schemars")]
|
||||
use schemars::schema::SchemaObject;
|
||||
|
||||
use crate::Color32;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
@@ -57,7 +57,7 @@ impl From<Colour> for Color32 {
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct Hex(pub HexColor);
|
||||
pub struct Hex(HexColor);
|
||||
|
||||
#[cfg(feature = "schemars")]
|
||||
impl schemars::JsonSchema for Hex {
|
||||
@@ -1,12 +1,11 @@
|
||||
use clap::ValueEnum;
|
||||
|
||||
use serde::ser::SerializeSeq;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use strum::Display;
|
||||
use strum::EnumString;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Display, EnumString, ValueEnum, PartialEq)]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, Display, EnumString, ValueEnum, PartialEq)]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub enum AnimationStyle {
|
||||
Linear,
|
||||
@@ -39,81 +38,4 @@ pub enum AnimationStyle {
|
||||
EaseInBounce,
|
||||
EaseOutBounce,
|
||||
EaseInOutBounce,
|
||||
#[value(skip)]
|
||||
CubicBezier(f64, f64, f64, f64),
|
||||
}
|
||||
|
||||
// Custom serde implementation
|
||||
impl<'de> Deserialize<'de> for AnimationStyle {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
struct AnimationStyleVisitor;
|
||||
|
||||
impl<'de> serde::de::Visitor<'de> for AnimationStyleVisitor {
|
||||
type Value = AnimationStyle;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
formatter.write_str("a string or an array of four f64 values")
|
||||
}
|
||||
|
||||
// Handle string variants (e.g., "EaseInOutExpo")
|
||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
value.parse().map_err(|_| E::unknown_variant(value, &[]))
|
||||
}
|
||||
|
||||
// Handle CubicBezier array (e.g., [0.32, 0.72, 0.0, 1.0])
|
||||
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
||||
where
|
||||
A: serde::de::SeqAccess<'de>,
|
||||
{
|
||||
let x1 = seq
|
||||
.next_element()?
|
||||
.ok_or_else(|| serde::de::Error::invalid_length(0, &self))?;
|
||||
let y1 = seq
|
||||
.next_element()?
|
||||
.ok_or_else(|| serde::de::Error::invalid_length(1, &self))?;
|
||||
let x2 = seq
|
||||
.next_element()?
|
||||
.ok_or_else(|| serde::de::Error::invalid_length(2, &self))?;
|
||||
let y2 = seq
|
||||
.next_element()?
|
||||
.ok_or_else(|| serde::de::Error::invalid_length(3, &self))?;
|
||||
|
||||
// Ensure no extra elements
|
||||
if seq.next_element::<serde::de::IgnoredAny>()?.is_some() {
|
||||
return Err(serde::de::Error::invalid_length(5, &self));
|
||||
}
|
||||
|
||||
Ok(AnimationStyle::CubicBezier(x1, y1, x2, y2))
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_any(AnimationStyleVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for AnimationStyle {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
match self {
|
||||
// Serialize CubicBezier as an array
|
||||
AnimationStyle::CubicBezier(x1, y1, x2, y2) => {
|
||||
let mut seq = serializer.serialize_seq(Some(4))?;
|
||||
seq.serialize_element(x1)?;
|
||||
seq.serialize_element(y1)?;
|
||||
seq.serialize_element(x2)?;
|
||||
seq.serialize_element(y2)?;
|
||||
seq.end()
|
||||
}
|
||||
// Serialize all other variants as strings
|
||||
_ => serializer.serialize_str(&self.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
#![warn(clippy::all)]
|
||||
#![allow(clippy::missing_errors_doc, clippy::use_self, clippy::doc_markdown)]
|
||||
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
|
||||
use clap::ValueEnum;
|
||||
use color_eyre::eyre::anyhow;
|
||||
use color_eyre::Result;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
@@ -26,10 +28,7 @@ pub use default_layout::DefaultLayout;
|
||||
pub use direction::Direction;
|
||||
pub use layout::Layout;
|
||||
pub use operation_direction::OperationDirection;
|
||||
pub use pathext::replace_env_in_path;
|
||||
pub use pathext::resolve_option_hashmap_usize_path;
|
||||
pub use pathext::PathExt;
|
||||
pub use pathext::ResolvedPathBuf;
|
||||
pub use rect::Rect;
|
||||
|
||||
pub mod animation;
|
||||
@@ -45,8 +44,6 @@ pub mod operation_direction;
|
||||
pub mod pathext;
|
||||
pub mod rect;
|
||||
|
||||
// serde_as must be before derive
|
||||
#[serde_with::serde_as]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, Display)]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
#[serde(tag = "type", content = "content")]
|
||||
@@ -108,7 +105,7 @@ pub enum SocketMessage {
|
||||
AdjustWorkspacePadding(Sizing, i32),
|
||||
ChangeLayout(DefaultLayout),
|
||||
CycleLayout(CycleDirection),
|
||||
ChangeLayoutCustom(#[serde_as(as = "ResolvedPathBuf")] PathBuf),
|
||||
ChangeLayoutCustom(PathBuf),
|
||||
FlipLayout(Axis),
|
||||
ToggleWorkspaceWindowContainerBehaviour,
|
||||
ToggleWorkspaceFloatOverride,
|
||||
@@ -126,8 +123,8 @@ pub enum SocketMessage {
|
||||
RetileWithResizeDimensions,
|
||||
QuickSave,
|
||||
QuickLoad,
|
||||
Save(#[serde_as(as = "ResolvedPathBuf")] PathBuf),
|
||||
Load(#[serde_as(as = "ResolvedPathBuf")] PathBuf),
|
||||
Save(PathBuf),
|
||||
Load(PathBuf),
|
||||
CycleFocusMonitor(CycleDirection),
|
||||
CycleFocusWorkspace(CycleDirection),
|
||||
CycleFocusEmptyWorkspace(CycleDirection),
|
||||
@@ -150,28 +147,23 @@ pub enum SocketMessage {
|
||||
WorkspaceName(usize, usize, String),
|
||||
WorkspaceLayout(usize, usize, DefaultLayout),
|
||||
NamedWorkspaceLayout(String, DefaultLayout),
|
||||
WorkspaceLayoutCustom(usize, usize, #[serde_as(as = "ResolvedPathBuf")] PathBuf),
|
||||
NamedWorkspaceLayoutCustom(String, #[serde_as(as = "ResolvedPathBuf")] PathBuf),
|
||||
WorkspaceLayoutCustom(usize, usize, PathBuf),
|
||||
NamedWorkspaceLayoutCustom(String, PathBuf),
|
||||
WorkspaceLayoutRule(usize, usize, usize, DefaultLayout),
|
||||
NamedWorkspaceLayoutRule(String, usize, DefaultLayout),
|
||||
WorkspaceLayoutCustomRule(
|
||||
usize,
|
||||
usize,
|
||||
usize,
|
||||
#[serde_as(as = "ResolvedPathBuf")] PathBuf,
|
||||
),
|
||||
NamedWorkspaceLayoutCustomRule(String, usize, #[serde_as(as = "ResolvedPathBuf")] PathBuf),
|
||||
WorkspaceLayoutCustomRule(usize, usize, usize, PathBuf),
|
||||
NamedWorkspaceLayoutCustomRule(String, usize, PathBuf),
|
||||
ClearWorkspaceLayoutRules(usize, usize),
|
||||
ClearNamedWorkspaceLayoutRules(String),
|
||||
ToggleWorkspaceLayer,
|
||||
// Configuration
|
||||
ReloadConfiguration,
|
||||
ReplaceConfiguration(#[serde_as(as = "ResolvedPathBuf")] PathBuf),
|
||||
ReloadStaticConfiguration(#[serde_as(as = "ResolvedPathBuf")] PathBuf),
|
||||
ReplaceConfiguration(PathBuf),
|
||||
ReloadStaticConfiguration(PathBuf),
|
||||
WatchConfiguration(bool),
|
||||
CompleteConfiguration,
|
||||
AltFocusHack(bool),
|
||||
Theme(Box<KomorebiTheme>),
|
||||
Theme(KomorebiTheme),
|
||||
Animation(bool, Option<AnimationPrefix>),
|
||||
AnimationDuration(u64, Option<AnimationPrefix>),
|
||||
AnimationFps(u64),
|
||||
@@ -210,9 +202,6 @@ pub enum SocketMessage {
|
||||
ClearNamedWorkspaceRules(String),
|
||||
ClearAllWorkspaceRules,
|
||||
EnforceWorkspaceRules,
|
||||
SessionFloatRule,
|
||||
SessionFloatRules,
|
||||
ClearSessionFloatRules,
|
||||
#[serde(alias = "FloatRule")]
|
||||
IgnoreRule(ApplicationIdentifier, String),
|
||||
ManageRule(ApplicationIdentifier, String),
|
||||
@@ -340,8 +329,6 @@ pub enum StateQuery {
|
||||
FocusedContainerIndex,
|
||||
FocusedWindowIndex,
|
||||
FocusedWorkspaceName,
|
||||
FocusedWorkspaceLayout,
|
||||
Version,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
@@ -377,21 +364,6 @@ pub struct WindowManagementBehaviour {
|
||||
/// that can be later toggled to tiled, when false it will default to
|
||||
/// `current_behaviour` again.
|
||||
pub float_override: bool,
|
||||
/// Determines if a new window should be spawned floating when on the floating layer and the
|
||||
/// floating layer behaviour is set to float. This value is always calculated when checking for
|
||||
/// the management behaviour on a specific workspace.
|
||||
pub floating_layer_override: bool,
|
||||
/// The floating layer behaviour to be used if the float override is being used
|
||||
pub floating_layer_behaviour: FloatingLayerBehaviour,
|
||||
/// The `Placement` to be used when toggling a window to float
|
||||
pub toggle_float_placement: Placement,
|
||||
/// The `Placement` to be used when spawning a window on the floating layer with the
|
||||
/// `FloatingLayerBehaviour` set to `FloatingLayerBehaviour::Float`
|
||||
pub floating_layer_placement: Placement,
|
||||
/// The `Placement` to be used when spawning a window with float override active
|
||||
pub float_override_placement: Placement,
|
||||
/// The `Placement` to be used when spawning a window that matches a 'floating_applications' rule
|
||||
pub float_rule_placement: Placement,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
@@ -411,59 +383,17 @@ pub enum WindowContainerBehaviour {
|
||||
)]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub enum FloatingLayerBehaviour {
|
||||
/// Tile new windows (unless they match a float rule or float override is active)
|
||||
/// Tile new windows (unless they match a float rule)
|
||||
#[default]
|
||||
Tile,
|
||||
/// Float new windows
|
||||
Float,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Clone, Copy, Debug, Default, Serialize, Deserialize, Display, EnumString, ValueEnum, PartialEq,
|
||||
)]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub enum Placement {
|
||||
/// Does not change the size or position of the window
|
||||
#[default]
|
||||
None,
|
||||
/// Center the window without changing the size
|
||||
Center,
|
||||
/// Center the window and resize it according to the `AspectRatio`
|
||||
CenterAndResize,
|
||||
}
|
||||
|
||||
impl FloatingLayerBehaviour {
|
||||
pub fn should_float(&self) -> bool {
|
||||
match self {
|
||||
FloatingLayerBehaviour::Tile => false,
|
||||
FloatingLayerBehaviour::Float => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Placement {
|
||||
pub fn should_center(&self) -> bool {
|
||||
match self {
|
||||
Placement::None => false,
|
||||
Placement::Center | Placement::CenterAndResize => true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn should_resize(&self) -> bool {
|
||||
match self {
|
||||
Placement::None | Placement::Center => false,
|
||||
Placement::CenterAndResize => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize, Display, EnumString, ValueEnum,
|
||||
)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, Display, EnumString, ValueEnum)]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub enum MoveBehaviour {
|
||||
/// Swap the window container with the window container at the edge of the adjacent monitor
|
||||
#[default]
|
||||
Swap,
|
||||
/// Insert the window container into the focused workspace on the adjacent monitor
|
||||
Insert,
|
||||
@@ -471,22 +401,19 @@ pub enum MoveBehaviour {
|
||||
NoOp,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Clone, Copy, Debug, Default, Serialize, Deserialize, Display, EnumString, ValueEnum, PartialEq,
|
||||
)]
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString, ValueEnum, PartialEq)]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub enum CrossBoundaryBehaviour {
|
||||
/// Attempt to perform actions across a workspace boundary
|
||||
Workspace,
|
||||
/// Attempt to perform actions across a monitor boundary
|
||||
#[default]
|
||||
Monitor,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, Display, EnumString, ValueEnum, PartialEq)]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub enum HidingBehaviour {
|
||||
/// END OF LIFE FEATURE: Use the SW_HIDE flag to hide windows when switching workspaces (has issues with Electron apps)
|
||||
/// Use the SW_HIDE flag to hide windows when switching workspaces (has issues with Electron apps)
|
||||
Hide,
|
||||
/// Use the SW_MINIMIZE flag to hide windows when switching workspaces (has issues with frequent workspace switching)
|
||||
Minimize,
|
||||
@@ -494,13 +421,10 @@ pub enum HidingBehaviour {
|
||||
Cloak,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize, Display, EnumString, ValueEnum,
|
||||
)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, Display, EnumString, ValueEnum)]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub enum OperationBehaviour {
|
||||
/// Process komorebic commands on temporarily unmanaged/floated windows
|
||||
#[default]
|
||||
Op,
|
||||
/// Ignore komorebic commands on temporarily unmanaged/floated windows
|
||||
NoOp,
|
||||
@@ -529,38 +453,45 @@ impl Sizing {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Clone, Copy, Debug, Default, Serialize, Deserialize, Display, EnumString, ValueEnum, PartialEq,
|
||||
)]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub enum WindowHandlingBehaviour {
|
||||
#[default]
|
||||
Sync,
|
||||
Async,
|
||||
}
|
||||
pub fn resolve_home_path<P: AsRef<Path>>(path: P) -> Result<PathBuf> {
|
||||
let mut resolved_path = PathBuf::new();
|
||||
let mut resolved = false;
|
||||
for c in path.as_ref().components() {
|
||||
match c {
|
||||
std::path::Component::Normal(c)
|
||||
if (c == "~" || c == "$Env:USERPROFILE" || c == "$HOME") && !resolved =>
|
||||
{
|
||||
let home = dirs::home_dir().ok_or_else(|| anyhow!("there is no home directory"))?;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
resolved_path.extend(home.components());
|
||||
resolved = true;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserializes() {
|
||||
// Set a variable for testing
|
||||
std::env::set_var("VAR", "VALUE");
|
||||
std::path::Component::Normal(c) if (c == "$Env:KOMOREBI_CONFIG_HOME") && !resolved => {
|
||||
let komorebi_config_home =
|
||||
PathBuf::from(std::env::var("KOMOREBI_CONFIG_HOME").ok().ok_or_else(|| {
|
||||
anyhow!("there is no KOMOREBI_CONFIG_HOME environment variable set")
|
||||
})?);
|
||||
|
||||
let json = r#"{"type":"WorkspaceLayoutCustomRule","content":[0,0,0,"/path/%VAR%/d"]}"#;
|
||||
let message: SocketMessage = serde_json::from_str(json).unwrap();
|
||||
resolved_path.extend(komorebi_config_home.components());
|
||||
resolved = true;
|
||||
}
|
||||
|
||||
let SocketMessage::WorkspaceLayoutCustomRule(
|
||||
_workspace_index,
|
||||
_workspace_number,
|
||||
_monitor_index,
|
||||
path,
|
||||
) = message
|
||||
else {
|
||||
panic!("Expected WorkspaceLayoutCustomRule");
|
||||
};
|
||||
|
||||
assert_eq!(path, PathBuf::from("/path/VALUE/d"));
|
||||
_ => resolved_path.push(c),
|
||||
}
|
||||
}
|
||||
|
||||
let parent = resolved_path
|
||||
.parent()
|
||||
.ok_or_else(|| anyhow!("cannot parse parent directory"))?;
|
||||
|
||||
Ok(if parent.is_dir() {
|
||||
let file = resolved_path
|
||||
.components()
|
||||
.last()
|
||||
.ok_or_else(|| anyhow!("cannot parse filename"))?;
|
||||
dunce::canonicalize(parent)?.join(file)
|
||||
} else {
|
||||
resolved_path
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,192 +1,48 @@
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
use std::ffi::OsStr;
|
||||
use std::path::Component;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
/// Path extension trait
|
||||
pub trait PathExt {
|
||||
/// Resolve environment variables components in a path.
|
||||
///
|
||||
/// Resolves the follwing formats:
|
||||
/// - CMD: `%variable%`
|
||||
/// - PowerShell: `$Env:variable`
|
||||
/// - Bash: `$variable`.
|
||||
fn replace_env(&self) -> PathBuf;
|
||||
}
|
||||
|
||||
/// Blanket implementation for all types that can be converted to a `Path`.
|
||||
impl<P: AsRef<Path>> PathExt for P {
|
||||
impl PathExt for PathBuf {
|
||||
fn replace_env(&self) -> PathBuf {
|
||||
let mut out = PathBuf::new();
|
||||
let mut result = PathBuf::new();
|
||||
|
||||
for c in self.as_ref().components() {
|
||||
match c {
|
||||
Component::Normal(mut c) => {
|
||||
// Special case for ~ and $HOME, replace with $Env:USERPROFILE
|
||||
if c == OsStr::new("~") || c.eq_ignore_ascii_case("$HOME") {
|
||||
c = OsStr::new("$Env:USERPROFILE");
|
||||
}
|
||||
|
||||
let bytes = c.as_encoded_bytes();
|
||||
|
||||
// %LOCALAPPDATA%
|
||||
let var = if bytes[0] == b'%' && bytes[bytes.len() - 1] == b'%' {
|
||||
Some(&bytes[1..bytes.len() - 1])
|
||||
} else {
|
||||
// prefix length is 5 for $Env: and 1 for $
|
||||
// so we take the minimum of 5 and the length of the bytes
|
||||
let prefix = &bytes[..5.min(bytes.len())];
|
||||
let prefix = unsafe { OsStr::from_encoded_bytes_unchecked(prefix) };
|
||||
|
||||
// $Env:LOCALAPPDATA
|
||||
if prefix.eq_ignore_ascii_case("$Env:") {
|
||||
Some(&bytes[5..])
|
||||
} else if bytes[0] == b'$' {
|
||||
// $LOCALAPPDATA
|
||||
Some(&bytes[1..])
|
||||
for component in self.components() {
|
||||
match component {
|
||||
Component::Normal(segment) => {
|
||||
// Check if it starts with `$` or `$Env:`
|
||||
if let Some(stripped_segment) = segment.to_string_lossy().strip_prefix('$') {
|
||||
let var_name = if let Some(env_name) = stripped_segment.strip_prefix("Env:")
|
||||
{
|
||||
// Extract the variable name after `$Env:`
|
||||
env_name
|
||||
} else if stripped_segment == "HOME" {
|
||||
// Special case for `$HOME`
|
||||
"USERPROFILE"
|
||||
} else {
|
||||
// not a variable
|
||||
None
|
||||
}
|
||||
};
|
||||
// Extract the variable name after `$`
|
||||
stripped_segment
|
||||
};
|
||||
|
||||
// if component is a variable, get the value from the environment
|
||||
if let Some(var) = var {
|
||||
let var = unsafe { OsStr::from_encoded_bytes_unchecked(var) };
|
||||
if let Some(value) = env::var_os(var) {
|
||||
out.push(value);
|
||||
continue;
|
||||
if let Ok(value) = env::var(var_name) {
|
||||
result.push(&value); // Replace with the value
|
||||
} else {
|
||||
result.push(segment); // Keep as-is if variable is not found
|
||||
}
|
||||
} else {
|
||||
result.push(segment); // Keep as-is if not an environment variable
|
||||
}
|
||||
|
||||
// if not a variable, or a value couldn't be obtained from environemnt
|
||||
// then push the component as is
|
||||
out.push(c);
|
||||
}
|
||||
|
||||
// other components are pushed as is
|
||||
_ => out.push(c),
|
||||
_ => {
|
||||
// Add other components (e.g., root, parent) as-is
|
||||
result.push(component.as_os_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
/// Replace environment variables in a path. This is a wrapper around
|
||||
/// [`PathExt::replace_env`] to be used in Clap arguments parsing.
|
||||
pub fn replace_env_in_path(input: &str) -> Result<PathBuf, std::convert::Infallible> {
|
||||
Ok(input.replace_env())
|
||||
}
|
||||
|
||||
/// A wrapper around [`PathBuf`] that has a custom [Deserialize] implementation
|
||||
/// that uses [`PathExt::replace_env`] to resolve environment variables
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ResolvedPathBuf(PathBuf);
|
||||
|
||||
impl ResolvedPathBuf {
|
||||
/// Create a new [`ResolvedPathBuf`] from a [`PathBuf`]
|
||||
pub fn new(path: PathBuf) -> Self {
|
||||
Self(path.replace_env())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ResolvedPathBuf> for PathBuf {
|
||||
fn from(path: ResolvedPathBuf) -> Self {
|
||||
path.0
|
||||
}
|
||||
}
|
||||
|
||||
impl serde_with::SerializeAs<PathBuf> for ResolvedPathBuf {
|
||||
fn serialize_as<S>(path: &PathBuf, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
path.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> serde_with::DeserializeAs<'de, PathBuf> for ResolvedPathBuf {
|
||||
fn deserialize_as<D>(deserializer: D) -> Result<PathBuf, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
let path = PathBuf::deserialize(deserializer)?;
|
||||
Ok(path.replace_env())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "schemars")]
|
||||
impl serde_with::schemars_0_8::JsonSchemaAs<PathBuf> for ResolvedPathBuf {
|
||||
fn schema_name() -> String {
|
||||
"PathBuf".to_owned()
|
||||
}
|
||||
|
||||
fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
|
||||
<PathBuf as schemars::JsonSchema>::json_schema(gen)
|
||||
}
|
||||
}
|
||||
|
||||
/// Custom deserializer for [`Option<HashMap<usize, PathBuf>>`] that uses
|
||||
/// [`PathExt::replace_env`] to resolve environment variables in the paths.
|
||||
///
|
||||
/// This is used in `WorkspaceConfig` struct because we can't use
|
||||
/// #[serde_with::serde_as] as it doesn't handle [`Option<HashMap<usize, ResolvedPathBuf>>`]
|
||||
/// quite well and generated compiler errors that can't be fixed because of Rust's orphan rule.
|
||||
pub fn resolve_option_hashmap_usize_path<'de, D>(
|
||||
deserializer: D,
|
||||
) -> Result<Option<HashMap<usize, PathBuf>>, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
let map = Option::<HashMap<usize, PathBuf>>::deserialize(deserializer)?;
|
||||
Ok(map.map(|map| map.into_iter().map(|(k, v)| (k, v.replace_env())).collect()))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
// helper functions
|
||||
fn expected<P: AsRef<Path>>(p: P) -> PathBuf {
|
||||
// Ensure that the path is using the correct path separator for the OS.
|
||||
p.as_ref().components().collect::<PathBuf>()
|
||||
}
|
||||
|
||||
fn resolve<P: AsRef<Path>>(p: P) -> PathBuf {
|
||||
p.replace_env()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolves_env_vars() {
|
||||
// Set a variable for testing
|
||||
std::env::set_var("VAR", "VALUE");
|
||||
|
||||
// %VAR% format
|
||||
assert_eq!(resolve("/path/%VAR%/d"), expected("/path/VALUE/d"));
|
||||
// $env:VAR format
|
||||
assert_eq!(resolve("/path/$env:VAR/d"), expected("/path/VALUE/d"));
|
||||
// $VAR format
|
||||
assert_eq!(resolve("/path/$VAR/d"), expected("/path/VALUE/d"));
|
||||
|
||||
// non-existent variable
|
||||
assert_eq!(resolve("/path/%ASD%/to/d"), expected("/path/%ASD%/to/d"));
|
||||
assert_eq!(
|
||||
resolve("/path/$env:ASD/to/d"),
|
||||
expected("/path/$env:ASD/to/d")
|
||||
);
|
||||
assert_eq!(resolve("/path/$ASD/to/d"), expected("/path/$ASD/to/d"));
|
||||
|
||||
// Set a $env:USERPROFILE variable for testing
|
||||
std::env::set_var("USERPROFILE", "C:\\Users\\user");
|
||||
|
||||
// ~ and $HOME should be replaced with $Env:USERPROFILE
|
||||
assert_eq!(resolve("~"), expected("C:\\Users\\user"));
|
||||
assert_eq!(resolve("$HOME"), expected("C:\\Users\\user"));
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ pub mod border_manager;
|
||||
pub mod com;
|
||||
#[macro_use]
|
||||
pub mod ring;
|
||||
pub mod colour;
|
||||
pub mod container;
|
||||
pub mod core;
|
||||
pub mod focus_manager;
|
||||
@@ -46,8 +47,8 @@ use std::sync::atomic::AtomicU64;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub use colour::*;
|
||||
pub use core::*;
|
||||
pub use komorebi_themes::colour::*;
|
||||
pub use process_command::*;
|
||||
pub use process_event::*;
|
||||
pub use static_config::*;
|
||||
@@ -63,7 +64,6 @@ use crate::core::config_generation::MatchingRule;
|
||||
use crate::core::config_generation::MatchingStrategy;
|
||||
use crate::core::config_generation::WorkspaceMatchingRule;
|
||||
use color_eyre::Result;
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use os_info::Version;
|
||||
use parking_lot::Mutex;
|
||||
use parking_lot::RwLock;
|
||||
@@ -159,15 +159,7 @@ lazy_static! {
|
||||
matching_strategy: Option::from(MatchingStrategy::Equals),
|
||||
})
|
||||
]));
|
||||
static ref SESSION_FLOATING_APPLICATIONS: Arc<Mutex<Vec<MatchingRule>>> = Arc::new(Mutex::new(Vec::new()));
|
||||
static ref FLOATING_APPLICATIONS: Arc<Mutex<Vec<MatchingRule>>> = Arc::new(Mutex::new(vec![
|
||||
MatchingRule::Simple(IdWithIdentifier {
|
||||
kind: ApplicationIdentifier::Exe,
|
||||
id: String::from("komorebi-shortcuts.exe"),
|
||||
matching_strategy: Option::from(MatchingStrategy::Equals),
|
||||
})
|
||||
|
||||
]));
|
||||
static ref FLOATING_APPLICATIONS: Arc<Mutex<Vec<MatchingRule>>> = Arc::new(Mutex::new(Vec::new()));
|
||||
static ref PERMAIGNORE_CLASSES: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![
|
||||
"Chrome_RenderWidgetHostHWND".to_string(),
|
||||
]));
|
||||
@@ -196,16 +188,15 @@ lazy_static! {
|
||||
Arc::new(Mutex::new(HidingBehaviour::Cloak));
|
||||
pub static ref HOME_DIR: PathBuf = {
|
||||
std::env::var("KOMOREBI_CONFIG_HOME").map_or_else(|_| dirs::home_dir().expect("there is no home directory"), |home_path| {
|
||||
let home = home_path.replace_env();
|
||||
let home = PathBuf::from(&home_path);
|
||||
|
||||
assert!(
|
||||
home.is_dir(),
|
||||
"$Env:KOMOREBI_CONFIG_HOME is set to '{}', which is not a valid directory",
|
||||
home_path
|
||||
);
|
||||
|
||||
|
||||
home
|
||||
if home.as_path().is_dir() {
|
||||
home
|
||||
} else {
|
||||
panic!(
|
||||
"$Env:KOMOREBI_CONFIG_HOME is set to '{home_path}', which is not a valid directory",
|
||||
);
|
||||
}
|
||||
})
|
||||
};
|
||||
pub static ref DATA_DIR: PathBuf = dirs::data_local_dir().expect("there is no local data directory").join("komorebi");
|
||||
@@ -235,8 +226,6 @@ lazy_static! {
|
||||
Arc::new(Mutex::new(HashMap::new()));
|
||||
|
||||
static ref FLOATING_WINDOW_TOGGLE_ASPECT_RATIO: Arc<Mutex<AspectRatio>> = Arc::new(Mutex::new(AspectRatio::Predefined(PredefinedAspectRatio::Widescreen)));
|
||||
|
||||
static ref CURRENT_VIRTUAL_DESKTOP: Arc<Mutex<Option<Vec<u8>>>> = Arc::new(Mutex::new(None));
|
||||
}
|
||||
|
||||
pub static DEFAULT_WORKSPACE_PADDING: AtomicI32 = AtomicI32::new(10);
|
||||
@@ -250,11 +239,6 @@ pub static REMOVE_TITLEBARS: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
pub static SLOW_APPLICATION_COMPENSATION_TIME: AtomicU64 = AtomicU64::new(20);
|
||||
|
||||
pub static WINDOW_HANDLING_BEHAVIOUR: AtomicCell<WindowHandlingBehaviour> =
|
||||
AtomicCell::new(WindowHandlingBehaviour::Sync);
|
||||
|
||||
shadow_rs::shadow!(build);
|
||||
|
||||
#[must_use]
|
||||
pub fn current_virtual_desktop() -> Option<Vec<u8>> {
|
||||
let hkcu = RegKey::predef(HKEY_CURRENT_USER);
|
||||
@@ -306,14 +290,6 @@ pub enum NotificationEvent {
|
||||
WindowManager(WindowManagerEvent),
|
||||
Socket(SocketMessage),
|
||||
Monitor(MonitorNotification),
|
||||
VirtualDesktop(VirtualDesktopNotification),
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub enum VirtualDesktopNotification {
|
||||
EnteredAssociatedVirtualDesktop,
|
||||
LeftAssociatedVirtualDesktop,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
|
||||
@@ -17,13 +17,11 @@ use std::time::Duration;
|
||||
|
||||
use clap::Parser;
|
||||
use clap::ValueEnum;
|
||||
use color_eyre::eyre::anyhow;
|
||||
use color_eyre::Result;
|
||||
use crossbeam_utils::Backoff;
|
||||
use komorebi::animation::AnimationEngine;
|
||||
use komorebi::animation::ANIMATION_ENABLED_GLOBAL;
|
||||
use komorebi::animation::ANIMATION_ENABLED_PER_ANIMATION;
|
||||
use komorebi::replace_env_in_path;
|
||||
#[cfg(feature = "deadlock_detection")]
|
||||
use parking_lot::deadlock;
|
||||
use parking_lot::Mutex;
|
||||
@@ -58,6 +56,8 @@ use komorebi::HOME_DIR;
|
||||
use komorebi::INITIAL_CONFIGURATION_LOADED;
|
||||
use komorebi::SESSION_ID;
|
||||
|
||||
shadow_rs::shadow!(build);
|
||||
|
||||
fn setup(log_level: LogLevel) -> Result<(WorkerGuard, WorkerGuard)> {
|
||||
if std::env::var("RUST_LIB_BACKTRACE").is_err() {
|
||||
std::env::set_var("RUST_LIB_BACKTRACE", "1");
|
||||
@@ -165,7 +165,7 @@ enum LogLevel {
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
#[clap(author, about, version = komorebi::build::CLAP_LONG_VERSION)]
|
||||
#[clap(author, about, version = build::CLAP_LONG_VERSION)]
|
||||
struct Opts {
|
||||
/// Allow the use of komorebi's custom focus-follows-mouse implementation
|
||||
#[clap(short, long = "ffm")]
|
||||
@@ -178,7 +178,6 @@ struct Opts {
|
||||
tcp_port: Option<usize>,
|
||||
/// Path to a static configuration JSON file
|
||||
#[clap(short, long)]
|
||||
#[clap(value_parser = replace_env_in_path)]
|
||||
config: Option<PathBuf>,
|
||||
/// Do not attempt to auto-apply a dumped state temp file from a previously running instance of komorebi
|
||||
#[clap(long)]
|
||||
@@ -194,28 +193,8 @@ fn main() -> Result<()> {
|
||||
let opts: Opts = Opts::parse();
|
||||
CUSTOM_FFM.store(opts.focus_follows_mouse, Ordering::SeqCst);
|
||||
|
||||
let mut set_foreground_window_retries = 5;
|
||||
let mut set_foreground_window_succeeded = false;
|
||||
|
||||
let process_id = WindowsApi::current_process_id();
|
||||
while set_foreground_window_retries > 0 && !set_foreground_window_succeeded {
|
||||
match WindowsApi::allow_set_foreground_window(process_id) {
|
||||
Ok(_) => {
|
||||
set_foreground_window_succeeded = true;
|
||||
}
|
||||
Err(error) => {
|
||||
tracing::error!("{error}");
|
||||
set_foreground_window_retries -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
if set_foreground_window_retries == 0 {
|
||||
return Err(anyhow!(
|
||||
"failed call to AllowSetForegroundWindow after 5 retries"
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
WindowsApi::allow_set_foreground_window(process_id)?;
|
||||
WindowsApi::set_process_dpi_awareness_context()?;
|
||||
|
||||
let session_id = WindowsApi::process_id_to_session_id()?;
|
||||
|
||||
@@ -12,21 +12,15 @@ use getset::Setters;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::border_manager::BORDER_ENABLED;
|
||||
use crate::border_manager::BORDER_OFFSET;
|
||||
use crate::border_manager::BORDER_WIDTH;
|
||||
use crate::core::Rect;
|
||||
|
||||
use crate::container::Container;
|
||||
use crate::ring::Ring;
|
||||
use crate::workspace::Workspace;
|
||||
use crate::workspace::WorkspaceGlobals;
|
||||
use crate::workspace::WorkspaceLayer;
|
||||
use crate::DefaultLayout;
|
||||
use crate::FloatingLayerBehaviour;
|
||||
use crate::Layout;
|
||||
use crate::OperationDirection;
|
||||
use crate::Wallpaper;
|
||||
use crate::WindowsApi;
|
||||
use crate::DEFAULT_CONTAINER_PADDING;
|
||||
use crate::DEFAULT_WORKSPACE_PADDING;
|
||||
@@ -66,10 +60,6 @@ pub struct Monitor {
|
||||
pub container_padding: Option<i32>,
|
||||
#[getset(get_copy = "pub", set = "pub")]
|
||||
pub workspace_padding: Option<i32>,
|
||||
#[getset(get = "pub", get_mut = "pub", set = "pub")]
|
||||
pub wallpaper: Option<Wallpaper>,
|
||||
#[getset(get_copy = "pub", set = "pub")]
|
||||
pub floating_layer_behaviour: Option<FloatingLayerBehaviour>,
|
||||
}
|
||||
|
||||
impl_ring_elements!(Monitor, Workspace);
|
||||
@@ -125,8 +115,6 @@ pub fn new(
|
||||
workspace_names: HashMap::default(),
|
||||
container_padding: None,
|
||||
workspace_padding: None,
|
||||
wallpaper: None,
|
||||
floating_layer_behaviour: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,8 +156,6 @@ impl Monitor {
|
||||
workspace_names: Default::default(),
|
||||
container_padding: None,
|
||||
workspace_padding: None,
|
||||
wallpaper: None,
|
||||
floating_layer_behaviour: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,23 +165,11 @@ impl Monitor {
|
||||
.unwrap_or(None)
|
||||
}
|
||||
|
||||
pub fn focused_workspace_layout(&self) -> Option<Layout> {
|
||||
self.focused_workspace().and_then(|workspace| {
|
||||
if *workspace.tile() {
|
||||
Some(workspace.layout().clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn load_focused_workspace(&mut self, mouse_follows_focus: bool) -> Result<()> {
|
||||
let focused_idx = self.focused_workspace_idx();
|
||||
let hmonitor = self.id();
|
||||
let monitor_wp = self.wallpaper.clone();
|
||||
for (i, workspace) in self.workspaces_mut().iter_mut().enumerate() {
|
||||
if i == focused_idx {
|
||||
workspace.restore(mouse_follows_focus, hmonitor, &monitor_wp)?;
|
||||
workspace.restore(mouse_follows_focus)?;
|
||||
} else {
|
||||
workspace.hide(None);
|
||||
}
|
||||
@@ -212,34 +186,18 @@ impl Monitor {
|
||||
let workspace_padding = self
|
||||
.workspace_padding()
|
||||
.or(Some(DEFAULT_WORKSPACE_PADDING.load(Ordering::SeqCst)));
|
||||
let (border_width, border_offset) = {
|
||||
let border_enabled = BORDER_ENABLED.load(Ordering::SeqCst);
|
||||
if border_enabled {
|
||||
let border_width = BORDER_WIDTH.load(Ordering::SeqCst);
|
||||
let border_offset = BORDER_OFFSET.load(Ordering::SeqCst);
|
||||
(border_width, border_offset)
|
||||
} else {
|
||||
(0, 0)
|
||||
}
|
||||
};
|
||||
let work_area = *self.work_area_size();
|
||||
let work_area_offset = self.work_area_offset.or(offset);
|
||||
let offset = self.work_area_offset.or(offset);
|
||||
let window_based_work_area_offset = self.window_based_work_area_offset();
|
||||
let window_based_work_area_offset_limit = self.window_based_work_area_offset_limit();
|
||||
let floating_layer_behaviour = self.floating_layer_behaviour();
|
||||
let limit = self.window_based_work_area_offset_limit();
|
||||
|
||||
for workspace in self.workspaces_mut() {
|
||||
workspace.globals = WorkspaceGlobals {
|
||||
container_padding,
|
||||
workspace_padding,
|
||||
border_width,
|
||||
border_offset,
|
||||
work_area,
|
||||
work_area_offset,
|
||||
window_based_work_area_offset,
|
||||
window_based_work_area_offset_limit,
|
||||
floating_layer_behaviour,
|
||||
}
|
||||
workspace.globals_mut().container_padding = container_padding;
|
||||
workspace.globals_mut().workspace_padding = workspace_padding;
|
||||
workspace.globals_mut().work_area = work_area;
|
||||
workspace.globals_mut().work_area_offset = offset;
|
||||
workspace.globals_mut().window_based_work_area_offset = window_based_work_area_offset;
|
||||
workspace.globals_mut().window_based_work_area_offset_limit = limit;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,34 +209,18 @@ impl Monitor {
|
||||
let workspace_padding = self
|
||||
.workspace_padding()
|
||||
.or(Some(DEFAULT_WORKSPACE_PADDING.load(Ordering::SeqCst)));
|
||||
let (border_width, border_offset) = {
|
||||
let border_enabled = BORDER_ENABLED.load(Ordering::SeqCst);
|
||||
if border_enabled {
|
||||
let border_width = BORDER_WIDTH.load(Ordering::SeqCst);
|
||||
let border_offset = BORDER_OFFSET.load(Ordering::SeqCst);
|
||||
(border_width, border_offset)
|
||||
} else {
|
||||
(0, 0)
|
||||
}
|
||||
};
|
||||
let work_area = *self.work_area_size();
|
||||
let work_area_offset = self.work_area_offset.or(offset);
|
||||
let offset = self.work_area_offset.or(offset);
|
||||
let window_based_work_area_offset = self.window_based_work_area_offset();
|
||||
let window_based_work_area_offset_limit = self.window_based_work_area_offset_limit();
|
||||
let floating_layer_behaviour = self.floating_layer_behaviour();
|
||||
let limit = self.window_based_work_area_offset_limit();
|
||||
|
||||
if let Some(workspace) = self.workspaces_mut().get_mut(workspace_idx) {
|
||||
workspace.globals = WorkspaceGlobals {
|
||||
container_padding,
|
||||
workspace_padding,
|
||||
border_width,
|
||||
border_offset,
|
||||
work_area,
|
||||
work_area_offset,
|
||||
window_based_work_area_offset,
|
||||
window_based_work_area_offset_limit,
|
||||
floating_layer_behaviour,
|
||||
}
|
||||
workspace.globals_mut().container_padding = container_padding;
|
||||
workspace.globals_mut().workspace_padding = workspace_padding;
|
||||
workspace.globals_mut().work_area = work_area;
|
||||
workspace.globals_mut().work_area_offset = offset;
|
||||
workspace.globals_mut().window_based_work_area_offset = window_based_work_area_offset;
|
||||
workspace.globals_mut().window_based_work_area_offset_limit = limit;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -458,18 +400,6 @@ impl Monitor {
|
||||
Some(workspace) => workspace,
|
||||
};
|
||||
|
||||
if target_workspace.monocle_container().is_some() {
|
||||
for container in target_workspace.containers_mut() {
|
||||
container.restore();
|
||||
}
|
||||
|
||||
for window in target_workspace.floating_windows_mut() {
|
||||
window.restore();
|
||||
}
|
||||
|
||||
target_workspace.reintegrate_monocle_container()?;
|
||||
}
|
||||
|
||||
target_workspace.set_layer(WorkspaceLayer::Tiling);
|
||||
|
||||
if let Some(direction) = direction {
|
||||
@@ -673,117 +603,4 @@ mod tests {
|
||||
// Should be the last workspace index: 1
|
||||
assert_eq!(new_workspace_index, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_move_container_to_workspace() {
|
||||
let mut m = Monitor::new(
|
||||
0,
|
||||
Rect::default(),
|
||||
Rect::default(),
|
||||
"TestMonitor".to_string(),
|
||||
"TestDevice".to_string(),
|
||||
"TestDeviceID".to_string(),
|
||||
Some("TestMonitorID".to_string()),
|
||||
);
|
||||
|
||||
let new_workspace_index = m.new_workspace_idx();
|
||||
assert_eq!(new_workspace_index, 1);
|
||||
|
||||
{
|
||||
// Create workspace 1 and add 3 containers
|
||||
let workspace = m.focused_workspace_mut().unwrap();
|
||||
for _ in 0..3 {
|
||||
let container = Container::default();
|
||||
workspace.add_container_to_back(container);
|
||||
}
|
||||
|
||||
// Should have 3 containers in workspace 1
|
||||
assert_eq!(m.focused_workspace().unwrap().containers().len(), 3);
|
||||
}
|
||||
|
||||
// Create and focus workspace 2
|
||||
m.focus_workspace(new_workspace_index).unwrap();
|
||||
|
||||
// Focus workspace 1
|
||||
m.focus_workspace(0).unwrap();
|
||||
|
||||
// Move container to workspace 2
|
||||
m.move_container_to_workspace(1, true, None).unwrap();
|
||||
|
||||
// Should be focused on workspace 2
|
||||
assert_eq!(m.focused_workspace_idx(), 1);
|
||||
|
||||
// Workspace 2 should have 1 container now
|
||||
assert_eq!(m.focused_workspace().unwrap().containers().len(), 1);
|
||||
|
||||
// Move to workspace 1
|
||||
m.focus_workspace(0).unwrap();
|
||||
|
||||
// Workspace 1 should have 2 containers
|
||||
assert_eq!(m.focused_workspace().unwrap().containers().len(), 2);
|
||||
|
||||
// Move a another container from workspace 1 to workspace 2 without following
|
||||
m.move_container_to_workspace(1, false, None).unwrap();
|
||||
|
||||
// Should have 1 container
|
||||
assert_eq!(m.focused_workspace().unwrap().containers().len(), 1);
|
||||
|
||||
// Should still be focused on workspace 1
|
||||
assert_eq!(m.focused_workspace_idx(), 0);
|
||||
|
||||
// Switch to workspace 2
|
||||
m.focus_workspace(1).unwrap();
|
||||
|
||||
// Workspace 2 should now have 2 containers
|
||||
assert_eq!(m.focused_workspace().unwrap().containers().len(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ensure_workspace_count_workspace_contains_two_workspaces() {
|
||||
let mut m = Monitor::new(
|
||||
0,
|
||||
Rect::default(),
|
||||
Rect::default(),
|
||||
"TestMonitor".to_string(),
|
||||
"TestDevice".to_string(),
|
||||
"TestDeviceID".to_string(),
|
||||
Some("TestMonitorID".to_string()),
|
||||
);
|
||||
|
||||
// Create and focus another workspace
|
||||
let new_workspace_index = m.new_workspace_idx();
|
||||
m.focus_workspace(new_workspace_index).unwrap();
|
||||
|
||||
// Should have 2 workspaces now
|
||||
assert_eq!(m.workspaces().len(), 2, "Monitor should have 2 workspaces");
|
||||
|
||||
// Ensure the monitor has at least 5 workspaces
|
||||
m.ensure_workspace_count(5);
|
||||
|
||||
// Monitor should have 5 workspaces
|
||||
assert_eq!(m.workspaces().len(), 5, "Monitor should have 5 workspaces");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ensure_workspace_count_only_default_workspace() {
|
||||
let mut m = Monitor::new(
|
||||
0,
|
||||
Rect::default(),
|
||||
Rect::default(),
|
||||
"TestMonitor".to_string(),
|
||||
"TestDevice".to_string(),
|
||||
"TestDeviceID".to_string(),
|
||||
Some("TestMonitorID".to_string()),
|
||||
);
|
||||
|
||||
// Ensure the monitor has at least 5 workspaces
|
||||
m.ensure_workspace_count(5);
|
||||
|
||||
// Monitor should have 5 workspaces
|
||||
assert_eq!(m.workspaces().len(), 5, "Monitor should have 5 workspaces");
|
||||
|
||||
// Try to call the ensure workspace count again to ensure it doesn't change
|
||||
m.ensure_workspace_count(3);
|
||||
assert_eq!(m.workspaces().len(), 5, "Monitor should have 5 workspaces");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -553,8 +553,6 @@ where
|
||||
workspace_names: cached.workspace_names.clone(),
|
||||
container_padding: cached.container_padding,
|
||||
workspace_padding: cached.workspace_padding,
|
||||
wallpaper: cached.wallpaper.clone(),
|
||||
floating_layer_behaviour: cached.floating_layer_behaviour,
|
||||
};
|
||||
|
||||
let focused_workspace_idx = m.focused_workspace_idx();
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use color_eyre::eyre::anyhow;
|
||||
use color_eyre::eyre::OptionExt;
|
||||
use color_eyre::Result;
|
||||
use komorebi_themes::colour::Rgb;
|
||||
use miow::pipe::connect;
|
||||
use net2::TcpStreamExt;
|
||||
use parking_lot::Mutex;
|
||||
@@ -20,18 +19,9 @@ use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use uds_windows::UnixStream;
|
||||
|
||||
use crate::animation::ANIMATION_DURATION_GLOBAL;
|
||||
use crate::animation::ANIMATION_DURATION_PER_ANIMATION;
|
||||
use crate::animation::ANIMATION_ENABLED_GLOBAL;
|
||||
use crate::animation::ANIMATION_ENABLED_PER_ANIMATION;
|
||||
use crate::animation::ANIMATION_FPS;
|
||||
use crate::animation::ANIMATION_STYLE_GLOBAL;
|
||||
use crate::animation::ANIMATION_STYLE_PER_ANIMATION;
|
||||
use crate::border_manager;
|
||||
use crate::border_manager::IMPLEMENTATION;
|
||||
use crate::border_manager::STYLE;
|
||||
use crate::build;
|
||||
use crate::config_generation::WorkspaceMatchingRule;
|
||||
use crate::core::config_generation::IdWithIdentifier;
|
||||
use crate::core::config_generation::MatchingRule;
|
||||
use crate::core::config_generation::MatchingStrategy;
|
||||
@@ -48,6 +38,16 @@ use crate::core::SocketMessage;
|
||||
use crate::core::StateQuery;
|
||||
use crate::core::WindowContainerBehaviour;
|
||||
use crate::core::WindowKind;
|
||||
|
||||
use crate::animation::ANIMATION_DURATION_GLOBAL;
|
||||
use crate::animation::ANIMATION_ENABLED_GLOBAL;
|
||||
use crate::animation::ANIMATION_FPS;
|
||||
use crate::animation::ANIMATION_STYLE_GLOBAL;
|
||||
use crate::border_manager;
|
||||
use crate::border_manager::IMPLEMENTATION;
|
||||
use crate::border_manager::STYLE;
|
||||
use crate::colour::Rgb;
|
||||
use crate::config_generation::WorkspaceMatchingRule;
|
||||
use crate::current_virtual_desktop;
|
||||
use crate::monitor::MonitorInformation;
|
||||
use crate::notify_subscribers;
|
||||
@@ -72,7 +72,6 @@ use crate::State;
|
||||
use crate::CUSTOM_FFM;
|
||||
use crate::DATA_DIR;
|
||||
use crate::DISPLAY_INDEX_PREFERENCES;
|
||||
use crate::FLOATING_APPLICATIONS;
|
||||
use crate::HIDING_BEHAVIOUR;
|
||||
use crate::IGNORE_IDENTIFIERS;
|
||||
use crate::INITIAL_CONFIGURATION_LOADED;
|
||||
@@ -82,7 +81,6 @@ use crate::MONITOR_INDEX_PREFERENCES;
|
||||
use crate::NO_TITLEBAR;
|
||||
use crate::OBJECT_NAME_CHANGE_ON_LAUNCH;
|
||||
use crate::REMOVE_TITLEBARS;
|
||||
use crate::SESSION_FLOATING_APPLICATIONS;
|
||||
use crate::SUBSCRIPTION_PIPES;
|
||||
use crate::SUBSCRIPTION_SOCKETS;
|
||||
use crate::SUBSCRIPTION_SOCKET_OPTIONS;
|
||||
@@ -226,7 +224,6 @@ impl WindowManager {
|
||||
_ => {}
|
||||
};
|
||||
|
||||
let mut force_update_borders = false;
|
||||
match message {
|
||||
SocketMessage::Promote => self.promote_container_to_front()?,
|
||||
SocketMessage::PromoteFocus => self.promote_focus_to_front()?,
|
||||
@@ -236,37 +233,31 @@ impl WindowManager {
|
||||
}
|
||||
SocketMessage::EagerFocus(ref exe) => {
|
||||
let focused_monitor_idx = self.focused_monitor_idx();
|
||||
let focused_workspace_idx = self.focused_workspace_idx()?;
|
||||
|
||||
let mut window_location = None;
|
||||
let mut monitor_to_focus = None;
|
||||
let mut needs_workspace_loading = false;
|
||||
let mut monitor_workspace_indices = None;
|
||||
|
||||
'search: for (monitor_idx, monitor) in self.monitors_mut().iter_mut().enumerate() {
|
||||
'search: for (monitor_idx, monitor) in self.monitors().iter().enumerate() {
|
||||
for (workspace_idx, workspace) in monitor.workspaces().iter().enumerate() {
|
||||
if let Some(location) = workspace.location_from_exe(exe) {
|
||||
window_location = Some(location);
|
||||
|
||||
if monitor_idx != focused_monitor_idx {
|
||||
monitor_to_focus = Some(monitor_idx);
|
||||
}
|
||||
|
||||
// Focus workspace if it is not already the focused one, without
|
||||
// loading it so that we don't give focus to the wrong window, we will
|
||||
// load it later after focusing the wanted window
|
||||
let focused_ws_idx = monitor.focused_workspace_idx();
|
||||
if focused_ws_idx != workspace_idx {
|
||||
monitor.set_last_focused_workspace(Option::from(focused_ws_idx));
|
||||
monitor.focus_workspace(workspace_idx)?;
|
||||
needs_workspace_loading = true;
|
||||
}
|
||||
|
||||
monitor_workspace_indices = Some((monitor_idx, workspace_idx));
|
||||
break 'search;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(monitor_idx) = monitor_to_focus {
|
||||
self.focus_monitor(monitor_idx)?;
|
||||
if let Some((monitor_idx, workspace_idx)) = monitor_workspace_indices {
|
||||
if monitor_idx != focused_monitor_idx {
|
||||
self.focus_monitor(monitor_idx)?;
|
||||
let focused_ws_idx = self.focused_workspace_idx()?;
|
||||
if focused_ws_idx != workspace_idx {
|
||||
self.focus_workspace(workspace_idx)?;
|
||||
}
|
||||
} else if workspace_idx != focused_workspace_idx {
|
||||
self.focus_workspace(workspace_idx)?;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(location) = window_location {
|
||||
@@ -299,13 +290,6 @@ impl WindowManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if needs_workspace_loading {
|
||||
let mouse_follows_focus = self.mouse_follows_focus;
|
||||
if let Some(monitor) = self.focused_monitor_mut() {
|
||||
monitor.load_focused_workspace(mouse_follows_focus)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
SocketMessage::FocusWindow(direction) => {
|
||||
@@ -411,7 +395,7 @@ impl WindowManager {
|
||||
workspace.locked_containers.remove(&container_idx);
|
||||
}
|
||||
SocketMessage::ToggleLock => self.toggle_lock()?,
|
||||
SocketMessage::ToggleFloat => self.toggle_float(false)?,
|
||||
SocketMessage::ToggleFloat => self.toggle_float()?,
|
||||
SocketMessage::ToggleMonocle => self.toggle_monocle()?,
|
||||
SocketMessage::ToggleMaximize => self.toggle_maximize()?,
|
||||
SocketMessage::ContainerPadding(monitor_idx, workspace_idx, size) => {
|
||||
@@ -558,53 +542,6 @@ impl WindowManager {
|
||||
}));
|
||||
}
|
||||
}
|
||||
SocketMessage::SessionFloatRule => {
|
||||
let foreground_window = WindowsApi::foreground_window()?;
|
||||
let window = Window::from(foreground_window);
|
||||
if let (Ok(exe), Ok(title), Ok(class)) =
|
||||
(window.exe(), window.title(), window.class())
|
||||
{
|
||||
let rule = MatchingRule::Composite(vec![
|
||||
IdWithIdentifier {
|
||||
kind: ApplicationIdentifier::Exe,
|
||||
id: exe,
|
||||
matching_strategy: Option::from(MatchingStrategy::Equals),
|
||||
},
|
||||
IdWithIdentifier {
|
||||
kind: ApplicationIdentifier::Title,
|
||||
id: title,
|
||||
matching_strategy: Option::from(MatchingStrategy::Equals),
|
||||
},
|
||||
IdWithIdentifier {
|
||||
kind: ApplicationIdentifier::Class,
|
||||
id: class,
|
||||
matching_strategy: Option::from(MatchingStrategy::Equals),
|
||||
},
|
||||
]);
|
||||
|
||||
let mut floating_applications = FLOATING_APPLICATIONS.lock();
|
||||
floating_applications.push(rule.clone());
|
||||
let mut session_floating_applications = SESSION_FLOATING_APPLICATIONS.lock();
|
||||
session_floating_applications.push(rule.clone());
|
||||
|
||||
self.toggle_float(true)?;
|
||||
}
|
||||
}
|
||||
SocketMessage::SessionFloatRules => {
|
||||
let session_floating_applications = SESSION_FLOATING_APPLICATIONS.lock();
|
||||
let rules = match serde_json::to_string_pretty(&*session_floating_applications) {
|
||||
Ok(rules) => rules,
|
||||
Err(error) => error.to_string(),
|
||||
};
|
||||
|
||||
reply.write_all(rules.as_bytes())?;
|
||||
}
|
||||
SocketMessage::ClearSessionFloatRules => {
|
||||
let mut floating_applications = FLOATING_APPLICATIONS.lock();
|
||||
let mut session_floating_applications = SESSION_FLOATING_APPLICATIONS.lock();
|
||||
floating_applications.retain(|r| !session_floating_applications.contains(r));
|
||||
session_floating_applications.clear()
|
||||
}
|
||||
SocketMessage::IgnoreRule(identifier, ref id) => {
|
||||
let mut ignore_identifiers = IGNORE_IDENTIFIERS.lock();
|
||||
|
||||
@@ -924,12 +861,10 @@ impl WindowManager {
|
||||
}
|
||||
SocketMessage::Retile => {
|
||||
border_manager::destroy_all_borders()?;
|
||||
force_update_borders = true;
|
||||
self.retile_all(false)?
|
||||
}
|
||||
SocketMessage::RetileWithResizeDimensions => {
|
||||
border_manager::destroy_all_borders()?;
|
||||
force_update_borders = true;
|
||||
self.retile_all(true)?
|
||||
}
|
||||
SocketMessage::FlipLayout(layout_flip) => self.flip_layout(layout_flip)?,
|
||||
@@ -1296,12 +1231,6 @@ impl WindowManager {
|
||||
window.lower()?;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(container) = workspace.monocle_container() {
|
||||
if let Some(window) = container.focused_window() {
|
||||
window.lower()?;
|
||||
}
|
||||
}
|
||||
}
|
||||
WorkspaceLayer::Floating => {
|
||||
workspace.set_layer(WorkspaceLayer::Tiling);
|
||||
@@ -1448,20 +1377,6 @@ impl WindowManager {
|
||||
.focused_workspace_name()
|
||||
.unwrap_or_else(|| focused_monitor.focused_workspace_idx().to_string())
|
||||
}
|
||||
StateQuery::Version => build::RUST_VERSION.to_string(),
|
||||
StateQuery::FocusedWorkspaceLayout => {
|
||||
let focused_monitor = self
|
||||
.focused_monitor()
|
||||
.ok_or_else(|| anyhow!("there is no monitor"))?;
|
||||
|
||||
focused_monitor.focused_workspace_layout().map_or_else(
|
||||
|| "None".to_string(),
|
||||
|layout| match layout {
|
||||
Layout::Default(default_layout) => default_layout.to_string(),
|
||||
Layout::Custom(_) => "Custom".to_string(),
|
||||
},
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
reply.write_all(response.as_bytes())?;
|
||||
@@ -1671,7 +1586,6 @@ impl WindowManager {
|
||||
}
|
||||
SocketMessage::ReloadConfiguration => {
|
||||
Self::reload_configuration();
|
||||
force_update_borders = true;
|
||||
}
|
||||
SocketMessage::ReplaceConfiguration(ref config) => {
|
||||
// Check that this is a valid static config file first
|
||||
@@ -1700,78 +1614,15 @@ impl WindowManager {
|
||||
|
||||
// Set self to the new wm instance
|
||||
*self = wm;
|
||||
|
||||
// check if there are any bars
|
||||
let mut system = sysinfo::System::new_all();
|
||||
system.refresh_processes(sysinfo::ProcessesToUpdate::All, true);
|
||||
|
||||
let has_bar = system
|
||||
.processes_by_name("komorebi-bar.exe".as_ref())
|
||||
.next()
|
||||
.is_some();
|
||||
|
||||
// stop bar(s)
|
||||
if has_bar {
|
||||
let script = r"
|
||||
Stop-Process -Name:komorebi-bar -ErrorAction SilentlyContinue
|
||||
";
|
||||
match powershell_script::run(script) {
|
||||
Ok(_) => {
|
||||
println!("{script}");
|
||||
|
||||
// start new bar(s)
|
||||
let mut config = StaticConfig::read(config)?;
|
||||
if let Some(display_bar_configurations) =
|
||||
&mut config.bar_configurations
|
||||
{
|
||||
for config_file_path in &mut *display_bar_configurations {
|
||||
let script = r#"Start-Process "komorebi-bar" '"--config" "CONFIGFILE"' -WindowStyle hidden"#
|
||||
.replace("CONFIGFILE", &config_file_path.to_string_lossy());
|
||||
|
||||
match powershell_script::run(&script) {
|
||||
Ok(_) => {
|
||||
println!("{script}");
|
||||
}
|
||||
Err(error) => {
|
||||
println!("Error: {error}");
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let script = r"
|
||||
if (!(Get-Process komorebi-bar -ErrorAction SilentlyContinue))
|
||||
{
|
||||
Start-Process komorebi-bar -WindowStyle hidden
|
||||
}
|
||||
";
|
||||
match powershell_script::run(script) {
|
||||
Ok(_) => {
|
||||
println!("{script}");
|
||||
}
|
||||
Err(error) => {
|
||||
println!("Error: {error}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(error) => {
|
||||
println!("Error: {error}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
force_update_borders = true;
|
||||
}
|
||||
}
|
||||
SocketMessage::ReloadStaticConfiguration(ref pathbuf) => {
|
||||
self.reload_static_configuration(pathbuf)?;
|
||||
force_update_borders = true;
|
||||
}
|
||||
SocketMessage::CompleteConfiguration => {
|
||||
if !INITIAL_CONFIGURATION_LOADED.load(Ordering::SeqCst) {
|
||||
INITIAL_CONFIGURATION_LOADED.store(true, Ordering::SeqCst);
|
||||
self.update_focused_workspace(false, false)?;
|
||||
force_update_borders = true;
|
||||
}
|
||||
}
|
||||
SocketMessage::WatchConfiguration(enable) => {
|
||||
@@ -2029,8 +1880,6 @@ if (!(Get-Process komorebi-bar -ErrorAction SilentlyContinue))
|
||||
self.remove_all_accents()?;
|
||||
}
|
||||
}
|
||||
} else if matches!(IMPLEMENTATION.load(), BorderImplementation::Komorebi) {
|
||||
force_update_borders = true;
|
||||
}
|
||||
}
|
||||
SocketMessage::BorderImplementation(implementation) => {
|
||||
@@ -2043,49 +1892,44 @@ if (!(Get-Process komorebi-bar -ErrorAction SilentlyContinue))
|
||||
match IMPLEMENTATION.load() {
|
||||
BorderImplementation::Komorebi => {
|
||||
self.remove_all_accents()?;
|
||||
force_update_borders = true;
|
||||
}
|
||||
BorderImplementation::Windows => {
|
||||
border_manager::destroy_all_borders()?;
|
||||
}
|
||||
}
|
||||
|
||||
border_manager::send_notification(None);
|
||||
}
|
||||
}
|
||||
SocketMessage::BorderColour(kind, r, g, b) => {
|
||||
match kind {
|
||||
WindowKind::Single => {
|
||||
border_manager::FOCUSED.store(Rgb::new(r, g, b).into(), Ordering::SeqCst);
|
||||
}
|
||||
WindowKind::Stack => {
|
||||
border_manager::STACK.store(Rgb::new(r, g, b).into(), Ordering::SeqCst);
|
||||
}
|
||||
WindowKind::Monocle => {
|
||||
border_manager::MONOCLE.store(Rgb::new(r, g, b).into(), Ordering::SeqCst);
|
||||
}
|
||||
WindowKind::Unfocused => {
|
||||
border_manager::UNFOCUSED.store(Rgb::new(r, g, b).into(), Ordering::SeqCst);
|
||||
}
|
||||
WindowKind::UnfocusedLocked => {
|
||||
border_manager::UNFOCUSED_LOCKED
|
||||
.store(Rgb::new(r, g, b).into(), Ordering::SeqCst);
|
||||
}
|
||||
WindowKind::Floating => {
|
||||
border_manager::FLOATING.store(Rgb::new(r, g, b).into(), Ordering::SeqCst);
|
||||
}
|
||||
SocketMessage::BorderColour(kind, r, g, b) => match kind {
|
||||
WindowKind::Single => {
|
||||
border_manager::FOCUSED.store(Rgb::new(r, g, b).into(), Ordering::SeqCst);
|
||||
}
|
||||
force_update_borders = true;
|
||||
}
|
||||
WindowKind::Stack => {
|
||||
border_manager::STACK.store(Rgb::new(r, g, b).into(), Ordering::SeqCst);
|
||||
}
|
||||
WindowKind::Monocle => {
|
||||
border_manager::MONOCLE.store(Rgb::new(r, g, b).into(), Ordering::SeqCst);
|
||||
}
|
||||
WindowKind::Unfocused => {
|
||||
border_manager::UNFOCUSED.store(Rgb::new(r, g, b).into(), Ordering::SeqCst);
|
||||
}
|
||||
WindowKind::UnfocusedLocked => {
|
||||
border_manager::UNFOCUSED_LOCKED
|
||||
.store(Rgb::new(r, g, b).into(), Ordering::SeqCst);
|
||||
}
|
||||
WindowKind::Floating => {
|
||||
border_manager::FLOATING.store(Rgb::new(r, g, b).into(), Ordering::SeqCst);
|
||||
}
|
||||
},
|
||||
SocketMessage::BorderStyle(style) => {
|
||||
STYLE.store(style);
|
||||
force_update_borders = true;
|
||||
}
|
||||
SocketMessage::BorderWidth(width) => {
|
||||
border_manager::BORDER_WIDTH.store(width, Ordering::SeqCst);
|
||||
force_update_borders = true;
|
||||
}
|
||||
SocketMessage::BorderOffset(offset) => {
|
||||
border_manager::BORDER_OFFSET.store(offset, Ordering::SeqCst);
|
||||
force_update_borders = true;
|
||||
}
|
||||
SocketMessage::Animation(enable, prefix) => match prefix {
|
||||
Some(prefix) => {
|
||||
@@ -2247,8 +2091,8 @@ if (!(Get-Process komorebi-bar -ErrorAction SilentlyContinue))
|
||||
|
||||
reply.write_all(schema.as_bytes())?;
|
||||
}
|
||||
SocketMessage::Theme(ref theme) => {
|
||||
theme_manager::send_notification(*theme.clone());
|
||||
SocketMessage::Theme(theme) => {
|
||||
theme_manager::send_notification(theme);
|
||||
}
|
||||
// Deprecated commands
|
||||
SocketMessage::AltFocusHack(_)
|
||||
@@ -2266,11 +2110,7 @@ if (!(Get-Process komorebi-bar -ErrorAction SilentlyContinue))
|
||||
initial_state.has_been_modified(self.as_ref()),
|
||||
)?;
|
||||
|
||||
if force_update_borders {
|
||||
border_manager::send_force_update();
|
||||
} else {
|
||||
border_manager::send_notification(None);
|
||||
}
|
||||
border_manager::send_notification(None);
|
||||
transparency_manager::send_notification();
|
||||
stackbar_manager::send_notification();
|
||||
|
||||
|
||||
@@ -28,9 +28,7 @@ use crate::workspace::WorkspaceLayer;
|
||||
use crate::Notification;
|
||||
use crate::NotificationEvent;
|
||||
use crate::State;
|
||||
use crate::VirtualDesktopNotification;
|
||||
use crate::Window;
|
||||
use crate::CURRENT_VIRTUAL_DESKTOP;
|
||||
use crate::FLOATING_APPLICATIONS;
|
||||
use crate::HIDDEN_HWNDS;
|
||||
use crate::REGEX_IDENTIFIERS;
|
||||
@@ -118,62 +116,15 @@ impl WindowManager {
|
||||
}
|
||||
}
|
||||
|
||||
let mut last_known_virtual_desktop_id = CURRENT_VIRTUAL_DESKTOP.lock();
|
||||
|
||||
if let Some(virtual_desktop_id) = &self.virtual_desktop_id {
|
||||
let latest_virtual_desktop_id = current_virtual_desktop();
|
||||
if let Some(id) = latest_virtual_desktop_id {
|
||||
// if we are on the vd associated with komorebi
|
||||
let should_retile = id == *virtual_desktop_id
|
||||
// and we came from a vd not associated with komorebi
|
||||
&& (*last_known_virtual_desktop_id).clone().unwrap_or_default() != id;
|
||||
|
||||
*last_known_virtual_desktop_id = Some(id.clone());
|
||||
if let Some(id) = current_virtual_desktop() {
|
||||
if id != *virtual_desktop_id {
|
||||
tracing::info!(
|
||||
"ignoring events and commands while not on virtual desktop {:?}",
|
||||
virtual_desktop_id
|
||||
);
|
||||
|
||||
// TODO: when returning from another VD to the VD associated with komorebi
|
||||
// if borders are enabled, they will not be drawn again until the user interacts
|
||||
// with the workspace or forces a retile
|
||||
border_manager::destroy_all_borders()?;
|
||||
|
||||
// to be consumed by integrating gui applications like bars to know
|
||||
// when to hide visual components which don't make sense when not on
|
||||
// komorebi's associated virtual desktop
|
||||
tracing::debug!("notifying subscribers that we have left komorebi's associated virtual desktop");
|
||||
notify_subscribers(
|
||||
Notification {
|
||||
event: NotificationEvent::VirtualDesktop(
|
||||
VirtualDesktopNotification::LeftAssociatedVirtualDesktop,
|
||||
),
|
||||
state: self.as_ref().into(),
|
||||
},
|
||||
true,
|
||||
)?;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if should_retile {
|
||||
self.retile_all(true)?;
|
||||
|
||||
// to be consumed by integrating gui applications like bars to know
|
||||
// when to show visual components associated with komorebi's virtual
|
||||
// desktop
|
||||
tracing::debug!("notifying subscribers that we are back on komorebi's associated virtual desktop");
|
||||
notify_subscribers(
|
||||
Notification {
|
||||
event: NotificationEvent::VirtualDesktop(
|
||||
VirtualDesktopNotification::EnteredAssociatedVirtualDesktop,
|
||||
),
|
||||
state: self.as_ref().into(),
|
||||
},
|
||||
true,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -339,13 +290,14 @@ impl WindowManager {
|
||||
WindowManagerEvent::Show(_, window)
|
||||
| WindowManagerEvent::Manage(window)
|
||||
| WindowManagerEvent::Uncloak(_, window) => {
|
||||
if matches!(event, WindowManagerEvent::Uncloak(_, _)) && self.uncloak_to_ignore >= 1
|
||||
if matches!(event, WindowManagerEvent::Uncloak(_, _))
|
||||
&& self.uncloack_to_ignore >= 1
|
||||
{
|
||||
tracing::info!("ignoring uncloak after monocle move by mouse across monitors");
|
||||
self.uncloak_to_ignore = self.uncloak_to_ignore.saturating_sub(1);
|
||||
self.uncloack_to_ignore = self.uncloack_to_ignore.saturating_sub(1);
|
||||
} else {
|
||||
let focused_monitor_idx = self.focused_monitor_idx();
|
||||
let focused_workspace_idx =
|
||||
let mut focused_monitor_idx = self.focused_monitor_idx();
|
||||
let mut focused_workspace_idx =
|
||||
self.focused_workspace_idx_for_monitor_idx(focused_monitor_idx)?;
|
||||
|
||||
let mut needs_reconciliation = None;
|
||||
@@ -394,14 +346,25 @@ impl WindowManager {
|
||||
}
|
||||
|
||||
if proceed {
|
||||
let behaviour = self.window_management_behaviour(
|
||||
if matches!(event, WindowManagerEvent::Show(_, _)) {
|
||||
let initial_monitor_idx = initial_state.monitors.focused_idx();
|
||||
if focused_monitor_idx != initial_monitor_idx {
|
||||
tracing::info!("assuming focused monitor index should be {initial_monitor_idx} for WindowManagerEvent::Show");
|
||||
self.focus_monitor(initial_monitor_idx)?;
|
||||
}
|
||||
|
||||
focused_monitor_idx = self.focused_monitor_idx();
|
||||
focused_workspace_idx =
|
||||
self.focused_workspace_idx_for_monitor_idx(focused_monitor_idx)?;
|
||||
}
|
||||
|
||||
let mut behaviour = self.window_management_behaviour(
|
||||
focused_monitor_idx,
|
||||
focused_workspace_idx,
|
||||
);
|
||||
let workspace = self.focused_workspace_mut()?;
|
||||
let workspace_contains_window = workspace.contains_window(window.hwnd);
|
||||
let monocle_container = workspace.monocle_container().clone();
|
||||
let mut workspace_layer = *workspace.layer();
|
||||
|
||||
if !workspace_contains_window && needs_reconciliation.is_none() {
|
||||
let floating_applications = FLOATING_APPLICATIONS.lock();
|
||||
@@ -425,33 +388,23 @@ impl WindowManager {
|
||||
}
|
||||
}
|
||||
|
||||
if behaviour.float_override
|
||||
|| behaviour.floating_layer_override
|
||||
|| (should_float && !matches!(event, WindowManagerEvent::Manage(_)))
|
||||
{
|
||||
let placement = if behaviour.floating_layer_override {
|
||||
// Floating layer override placement
|
||||
behaviour.floating_layer_placement
|
||||
} else if behaviour.float_override {
|
||||
// Float override placement
|
||||
behaviour.float_override_placement
|
||||
} else {
|
||||
// Float rule placement
|
||||
behaviour.float_rule_placement
|
||||
};
|
||||
// Center floating windows according to the proper placement if not
|
||||
// on a floating workspace
|
||||
behaviour.float_override = behaviour.float_override
|
||||
|| (should_float
|
||||
&& !matches!(event, WindowManagerEvent::Manage(_)));
|
||||
|
||||
if behaviour.float_override {
|
||||
// Center floating windows if we are already on the `Floating`
|
||||
// layer and the window doesn't match a `floating_windows` rule and
|
||||
// the workspace is not a floating workspace
|
||||
let center_spawned_floats =
|
||||
placement.should_center() && workspace.tile;
|
||||
matches!(workspace.layer, WorkspaceLayer::Floating)
|
||||
&& !should_float
|
||||
&& workspace.tile;
|
||||
workspace.floating_windows_mut().push_back(window);
|
||||
workspace.set_layer(WorkspaceLayer::Floating);
|
||||
workspace_layer = *workspace.layer();
|
||||
if center_spawned_floats {
|
||||
let mut floating_window = window;
|
||||
floating_window.center(
|
||||
&workspace.globals().work_area,
|
||||
placement.should_resize(),
|
||||
)?;
|
||||
floating_window.center(&workspace.globals().work_area)?;
|
||||
}
|
||||
self.update_focused_workspace(false, false)?;
|
||||
} else {
|
||||
@@ -459,7 +412,6 @@ impl WindowManager {
|
||||
WindowContainerBehaviour::Create => {
|
||||
workspace.new_container_for_window(window);
|
||||
workspace.set_layer(WorkspaceLayer::Tiling);
|
||||
workspace_layer = *workspace.layer();
|
||||
self.update_focused_workspace(false, false)?;
|
||||
}
|
||||
WindowContainerBehaviour::Append => {
|
||||
@@ -470,7 +422,6 @@ impl WindowManager {
|
||||
})?
|
||||
.add_window(window);
|
||||
workspace.set_layer(WorkspaceLayer::Tiling);
|
||||
workspace_layer = *workspace.layer();
|
||||
self.update_focused_workspace(true, false)?;
|
||||
stackbar_manager::send_notification();
|
||||
}
|
||||
@@ -503,10 +454,7 @@ impl WindowManager {
|
||||
}
|
||||
}
|
||||
|
||||
if !monocle_window_event
|
||||
&& monocle_container.is_some()
|
||||
&& matches!(workspace_layer, WorkspaceLayer::Tiling)
|
||||
{
|
||||
if !monocle_window_event && monocle_container.is_some() {
|
||||
window.hide();
|
||||
}
|
||||
}
|
||||
@@ -773,7 +721,7 @@ impl WindowManager {
|
||||
|
||||
// If we unmanaged a window, it shouldn't be immediately hidden behind managed windows
|
||||
if let WindowManagerEvent::Unmanage(mut window) = event {
|
||||
window.center(&self.focused_monitor_work_area()?, true)?;
|
||||
window.center(&self.focused_monitor_work_area()?)?;
|
||||
}
|
||||
|
||||
// Update list of known_hwnds and their monitor/workspace index pair
|
||||
|
||||
@@ -3,13 +3,11 @@
|
||||
use crate::border_manager;
|
||||
use crate::notify_subscribers;
|
||||
use crate::winevent::WinEvent;
|
||||
use crate::HidingBehaviour;
|
||||
use crate::NotificationEvent;
|
||||
use crate::Window;
|
||||
use crate::WindowManager;
|
||||
use crate::WindowManagerEvent;
|
||||
use crate::DATA_DIR;
|
||||
use crate::HIDING_BEHAVIOUR;
|
||||
|
||||
use crossbeam_channel::Receiver;
|
||||
use crossbeam_channel::Sender;
|
||||
@@ -171,7 +169,6 @@ fn find_orphans() -> color_eyre::Result<()> {
|
||||
|
||||
loop {
|
||||
std::thread::sleep(Duration::from_millis(20));
|
||||
let hiding_behaviour = *HIDING_BEHAVIOUR.lock();
|
||||
|
||||
let mut cache = HWNDS_CACHE.lock();
|
||||
let mut orphan_hwnds = HashMap::new();
|
||||
@@ -180,18 +177,13 @@ fn find_orphans() -> color_eyre::Result<()> {
|
||||
let window = Window::from(*hwnd);
|
||||
|
||||
if !window.is_window()
|
||||
|| (
|
||||
// This one is a hack because WINWORD.EXE is an absolute trainwreck of an app
|
||||
// when multiple docs are open, it keeps open an invisible window, with WS_EX_LAYERED
|
||||
// (A STYLE THAT THE REGULAR WINDOWS NEED IN ORDER TO BE MANAGED!) when one of the
|
||||
// docs is closed
|
||||
//
|
||||
// I hate every single person who worked on Microsoft Office 365, especially Word
|
||||
!window.is_visible()
|
||||
// We cannot execute this lovely hack if the user is using HidingBehaviour::Hide because
|
||||
// it will result in legitimate hidden, non-visible windows being yeeted from the state
|
||||
&& !matches!(hiding_behaviour, HidingBehaviour::Hide)
|
||||
)
|
||||
// This one is a hack because WINWORD.EXE is an absolute trainwreck of an app
|
||||
// when multiple docs are open, it keeps open an invisible window, with WS_EX_LAYERED
|
||||
// (A STYLE THAT THE REGULAR WINDOWS NEED IN ORDER TO BE MANAGED!) when one of the
|
||||
// docs is closed
|
||||
//
|
||||
// I hate every single person who worked on Microsoft Office 365, especially Word
|
||||
|| !window.is_visible()
|
||||
{
|
||||
orphan_hwnds.insert(window.hwnd, (*m_idx, *w_idx));
|
||||
}
|
||||
|
||||
@@ -180,9 +180,7 @@ impl Stackbar {
|
||||
layout.top -= workspace_specific_offset + STACKBAR_TAB_HEIGHT.load_consume();
|
||||
layout.left -= workspace_specific_offset;
|
||||
|
||||
// Async causes the stackbar to disappear or flicker because we modify it right after,
|
||||
// so we have to do a synchronous call
|
||||
WindowsApi::position_window(self.hwnd, &layout, false, false)?;
|
||||
WindowsApi::position_window(self.hwnd, &layout, false)?;
|
||||
|
||||
unsafe {
|
||||
let hdc = GetDC(Option::from(self.hwnd()));
|
||||
|
||||
@@ -13,12 +13,14 @@ use crate::border_manager;
|
||||
use crate::border_manager::ZOrder;
|
||||
use crate::border_manager::IMPLEMENTATION;
|
||||
use crate::border_manager::STYLE;
|
||||
use crate::colour::Colour;
|
||||
use crate::config_generation::WorkspaceMatchingRule;
|
||||
use crate::core::config_generation::ApplicationConfiguration;
|
||||
use crate::core::config_generation::ApplicationConfigurationGenerator;
|
||||
use crate::core::config_generation::ApplicationOptions;
|
||||
use crate::core::config_generation::MatchingRule;
|
||||
use crate::core::config_generation::MatchingStrategy;
|
||||
use crate::core::resolve_home_path;
|
||||
use crate::core::AnimationStyle;
|
||||
use crate::core::BorderImplementation;
|
||||
use crate::core::BorderStyle;
|
||||
@@ -38,7 +40,6 @@ use crate::current_virtual_desktop;
|
||||
use crate::monitor;
|
||||
use crate::monitor::Monitor;
|
||||
use crate::monitor_reconciliator;
|
||||
use crate::resolve_option_hashmap_usize_path;
|
||||
use crate::ring::Ring;
|
||||
use crate::stackbar_manager::STACKBAR_FOCUSED_TEXT_COLOUR;
|
||||
use crate::stackbar_manager::STACKBAR_FONT_FAMILY;
|
||||
@@ -60,10 +61,7 @@ use crate::AspectRatio;
|
||||
use crate::Axis;
|
||||
use crate::CrossBoundaryBehaviour;
|
||||
use crate::FloatingLayerBehaviour;
|
||||
use crate::Placement;
|
||||
use crate::PredefinedAspectRatio;
|
||||
use crate::ResolvedPathBuf;
|
||||
use crate::WindowHandlingBehaviour;
|
||||
use crate::DATA_DIR;
|
||||
use crate::DEFAULT_CONTAINER_PADDING;
|
||||
use crate::DEFAULT_WORKSPACE_PADDING;
|
||||
@@ -84,13 +82,11 @@ use crate::SLOW_APPLICATION_IDENTIFIERS;
|
||||
use crate::TRANSPARENCY_BLACKLIST;
|
||||
use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS;
|
||||
use crate::WINDOWS_11;
|
||||
use crate::WINDOW_HANDLING_BEHAVIOUR;
|
||||
use crate::WORKSPACE_MATCHING_RULES;
|
||||
use color_eyre::Result;
|
||||
use crossbeam_channel::Receiver;
|
||||
use hotwatch::EventKind;
|
||||
use hotwatch::Hotwatch;
|
||||
use komorebi_themes::colour::Colour;
|
||||
use parking_lot::Mutex;
|
||||
use regex::Regex;
|
||||
use serde::Deserialize;
|
||||
@@ -123,66 +119,8 @@ pub struct BorderColours {
|
||||
/// Border colour when the container is unfocused
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub unfocused: Option<Colour>,
|
||||
/// Border colour when the container is unfocused and locked
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub unfocused_locked: Option<Colour>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub struct ThemeOptions {
|
||||
/// Specify Light or Dark variant for theme generation (default: Dark)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub theme_variant: Option<komorebi_themes::ThemeVariant>,
|
||||
/// Border colour when the container contains a single window (default: Base0D)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub single_border: Option<komorebi_themes::Base16Value>,
|
||||
/// Border colour when the container contains multiple windows (default: Base0B)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub stack_border: Option<komorebi_themes::Base16Value>,
|
||||
/// Border colour when the container is in monocle mode (default: Base0F)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub monocle_border: Option<komorebi_themes::Base16Value>,
|
||||
/// Border colour when the window is floating (default: Base09)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub floating_border: Option<komorebi_themes::Base16Value>,
|
||||
/// Border colour when the container is unfocused (default: Base01)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub unfocused_border: Option<komorebi_themes::Base16Value>,
|
||||
/// Border colour when the container is unfocused and locked (default: Base08)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub unfocused_locked_border: Option<komorebi_themes::Base16Value>,
|
||||
/// Stackbar focused tab text colour (default: Base0B)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub stackbar_focused_text: Option<komorebi_themes::Base16Value>,
|
||||
/// Stackbar unfocused tab text colour (default: Base05)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub stackbar_unfocused_text: Option<komorebi_themes::Base16Value>,
|
||||
/// Stackbar tab background colour (default: Base01)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub stackbar_background: Option<komorebi_themes::Base16Value>,
|
||||
/// Komorebi status bar accent (default: Base0D)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub bar_accent: Option<komorebi_themes::Base16Value>,
|
||||
}
|
||||
|
||||
#[serde_with::serde_as]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub struct Wallpaper {
|
||||
/// Path to the wallpaper image file
|
||||
#[serde_as(as = "ResolvedPathBuf")]
|
||||
pub path: PathBuf,
|
||||
/// Generate and apply Base16 theme for this wallpaper (default: true)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub generate_theme: Option<bool>,
|
||||
/// Specify Light or Dark variant for theme generation (default: Dark)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub theme_options: Option<ThemeOptions>,
|
||||
}
|
||||
|
||||
// serde_as must be before derive
|
||||
#[serde_with::serde_as]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub struct WorkspaceConfig {
|
||||
@@ -193,14 +131,12 @@ pub struct WorkspaceConfig {
|
||||
pub layout: Option<DefaultLayout>,
|
||||
/// END OF LIFE FEATURE: Custom Layout (default: None)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde_as(as = "Option<ResolvedPathBuf>")]
|
||||
pub custom_layout: Option<PathBuf>,
|
||||
/// Layout rules in the format of threshold => layout (default: None)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub layout_rules: Option<HashMap<usize, DefaultLayout>>,
|
||||
/// END OF LIFE FEATURE: Custom layout rules (default: None)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(deserialize_with = "resolve_option_hashmap_usize_path", default)]
|
||||
pub custom_layout_rules: Option<HashMap<usize, PathBuf>>,
|
||||
/// Container padding (default: global)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
@@ -232,9 +168,6 @@ pub struct WorkspaceConfig {
|
||||
/// Determine what happens to a new window when the Floating workspace layer is active (default: Tile)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub floating_layer_behaviour: Option<FloatingLayerBehaviour>,
|
||||
/// Specify a wallpaper for this workspace
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub wallpaper: Option<Wallpaper>,
|
||||
}
|
||||
|
||||
impl From<&Workspace> for WorkspaceConfig {
|
||||
@@ -310,8 +243,7 @@ impl From<&Workspace> for WorkspaceConfig {
|
||||
window_container_behaviour_rules: Option::from(window_container_behaviour_rules),
|
||||
float_override: *value.float_override(),
|
||||
layout_flip: value.layout_flip(),
|
||||
floating_layer_behaviour: value.floating_layer_behaviour(),
|
||||
wallpaper: None,
|
||||
floating_layer_behaviour: Option::from(*value.floating_layer_behaviour()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -336,12 +268,6 @@ pub struct MonitorConfig {
|
||||
/// Workspace padding (default: global)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub workspace_padding: Option<i32>,
|
||||
/// Specify a wallpaper for this monitor
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub wallpaper: Option<Wallpaper>,
|
||||
/// Determine what happens to a new window when the Floating workspace layer is active (default: Tile)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub floating_layer_behaviour: Option<FloatingLayerBehaviour>,
|
||||
}
|
||||
|
||||
impl From<&Monitor> for MonitorConfig {
|
||||
@@ -377,27 +303,23 @@ impl From<&Monitor> for MonitorConfig {
|
||||
window_based_work_area_offset_limit: Some(value.window_based_work_area_offset_limit()),
|
||||
container_padding,
|
||||
workspace_padding,
|
||||
wallpaper: value.wallpaper().clone(),
|
||||
floating_layer_behaviour: value.floating_layer_behaviour(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[serde_with::serde_as]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
#[serde(untagged)]
|
||||
pub enum AppSpecificConfigurationPath {
|
||||
/// A single applications.json file
|
||||
Single(#[serde_as(as = "ResolvedPathBuf")] PathBuf),
|
||||
Single(PathBuf),
|
||||
/// Multiple applications.json files
|
||||
Multiple(#[serde_as(as = "Vec<ResolvedPathBuf>")] Vec<PathBuf>),
|
||||
Multiple(Vec<PathBuf>),
|
||||
}
|
||||
|
||||
#[serde_with::serde_as]
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
/// The `komorebi.json` static configuration file reference for `v0.1.37`
|
||||
/// The `komorebi.json` static configuration file reference for `v0.1.36`
|
||||
pub struct StaticConfig {
|
||||
/// DEPRECATED from v0.1.22: no longer required
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
@@ -418,25 +340,6 @@ pub struct StaticConfig {
|
||||
/// (default: false)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub float_override: Option<bool>,
|
||||
/// Determines what happens on a new window when on the `FloatingLayer`
|
||||
/// (default: Tile)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub floating_layer_behaviour: Option<FloatingLayerBehaviour>,
|
||||
/// Determines the placement of a new window when toggling to float (default: CenterAndResize)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub toggle_float_placement: Option<Placement>,
|
||||
/// Determines the `Placement` to be used when spawning a window on the floating layer with the
|
||||
/// `FloatingLayerBehaviour` set to `FloatingLayerBehaviour::Float` (default: Center)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub floating_layer_placement: Option<Placement>,
|
||||
/// Determines the `Placement` to be used when spawning a window with float override active
|
||||
/// (default: None)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub float_override_placement: Option<Placement>,
|
||||
/// Determines the `Placement` to be used when spawning a window that matches a
|
||||
/// 'floating_applications' rule (default: None)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub float_rule_placement: Option<Placement>,
|
||||
/// Determine what happens when a window is moved across a monitor boundary (default: Swap)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub cross_monitor_move_behaviour: Option<MoveBehaviour>,
|
||||
@@ -463,7 +366,7 @@ pub struct StaticConfig {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(alias = "active_window_border_offset")]
|
||||
pub border_offset: Option<i32>,
|
||||
/// Display an active window border (default: true)
|
||||
/// Display an active window border (default: false)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(alias = "active_window_border")]
|
||||
pub border: Option<bool>,
|
||||
@@ -554,7 +457,6 @@ pub struct StaticConfig {
|
||||
/// Komorebi status bar configuration files for multiple instances on different monitors
|
||||
// this option is a little special because it is only consumed by komorebic
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde_as(as = "Option<Vec<ResolvedPathBuf>>")]
|
||||
pub bar_configurations: Option<Vec<PathBuf>>,
|
||||
/// HEAVILY DISCOURAGED: Identify applications for which komorebi should forcibly remove title bars
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
@@ -562,9 +464,6 @@ pub struct StaticConfig {
|
||||
/// Aspect ratio to resize with when toggling floating mode for a window
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub floating_window_aspect_ratio: Option<AspectRatio>,
|
||||
/// Which Windows API behaviour to use when manipulating windows (default: Sync)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub window_handling_behaviour: Option<WindowHandlingBehaviour>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
@@ -583,7 +482,7 @@ pub struct AnimationsConfig {
|
||||
pub fps: Option<u64>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
#[serde(tag = "palette")]
|
||||
pub enum KomorebiTheme {
|
||||
@@ -657,41 +556,6 @@ pub enum KomorebiTheme {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
bar_accent: Option<komorebi_themes::Base16Value>,
|
||||
},
|
||||
/// A custom Base16 theme
|
||||
Custom {
|
||||
/// Colours of the custom Base16 theme palette
|
||||
colours: Box<komorebi_themes::Base16ColourPalette>,
|
||||
/// Border colour when the container contains a single window (default: Base0D)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
single_border: Option<komorebi_themes::Base16Value>,
|
||||
/// Border colour when the container contains multiple windows (default: Base0B)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
stack_border: Option<komorebi_themes::Base16Value>,
|
||||
/// Border colour when the container is in monocle mode (default: Base0F)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
monocle_border: Option<komorebi_themes::Base16Value>,
|
||||
/// Border colour when the window is floating (default: Base09)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
floating_border: Option<komorebi_themes::Base16Value>,
|
||||
/// Border colour when the container is unfocused (default: Base01)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
unfocused_border: Option<komorebi_themes::Base16Value>,
|
||||
/// Border colour when the container is unfocused and locked (default: Base08)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
unfocused_locked_border: Option<komorebi_themes::Base16Value>,
|
||||
/// Stackbar focused tab text colour (default: Base0B)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
stackbar_focused_text: Option<komorebi_themes::Base16Value>,
|
||||
/// Stackbar unfocused tab text colour (default: Base05)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
stackbar_unfocused_text: Option<komorebi_themes::Base16Value>,
|
||||
/// Stackbar tab background colour (default: Base01)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
stackbar_background: Option<komorebi_themes::Base16Value>,
|
||||
/// Komorebi status bar accent (default: Base0D)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
bar_accent: Option<komorebi_themes::Base16Value>,
|
||||
},
|
||||
}
|
||||
|
||||
impl StaticConfig {
|
||||
@@ -836,9 +700,6 @@ impl From<&WindowManager> for StaticConfig {
|
||||
unfocused: Option::from(Colour::from(
|
||||
border_manager::UNFOCUSED.load(Ordering::SeqCst),
|
||||
)),
|
||||
unfocused_locked: Option::from(Colour::from(
|
||||
border_manager::UNFOCUSED_LOCKED.load(Ordering::SeqCst),
|
||||
)),
|
||||
})
|
||||
};
|
||||
|
||||
@@ -849,21 +710,6 @@ impl From<&WindowManager> for StaticConfig {
|
||||
value.window_management_behaviour.current_behaviour,
|
||||
),
|
||||
float_override: Option::from(value.window_management_behaviour.float_override),
|
||||
floating_layer_behaviour: Option::from(
|
||||
value.window_management_behaviour.floating_layer_behaviour,
|
||||
),
|
||||
toggle_float_placement: Option::from(
|
||||
value.window_management_behaviour.toggle_float_placement,
|
||||
),
|
||||
floating_layer_placement: Option::from(
|
||||
value.window_management_behaviour.floating_layer_placement,
|
||||
),
|
||||
float_override_placement: Option::from(
|
||||
value.window_management_behaviour.float_override_placement,
|
||||
),
|
||||
float_rule_placement: Option::from(
|
||||
value.window_management_behaviour.float_rule_placement,
|
||||
),
|
||||
cross_monitor_move_behaviour: Option::from(value.cross_monitor_move_behaviour),
|
||||
cross_boundary_behaviour: Option::from(value.cross_boundary_behaviour),
|
||||
unmanaged_window_operation_behaviour: Option::from(
|
||||
@@ -926,7 +772,6 @@ impl From<&WindowManager> for StaticConfig {
|
||||
bar_configurations: None,
|
||||
remove_titlebar_applications: Option::from(NO_TITLEBAR.lock().clone()),
|
||||
floating_window_aspect_ratio: Option::from(*FLOATING_WINDOW_TOGGLE_ASPECT_RATIO.lock()),
|
||||
window_handling_behaviour: Option::from(WINDOW_HANDLING_BEHAVIOUR.load()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1011,7 +856,10 @@ impl StaticConfig {
|
||||
|
||||
border_manager::BORDER_WIDTH.store(self.border_width.unwrap_or(8), Ordering::SeqCst);
|
||||
border_manager::BORDER_OFFSET.store(self.border_offset.unwrap_or(-1), Ordering::SeqCst);
|
||||
border_manager::BORDER_ENABLED.store(self.border.unwrap_or(true), Ordering::SeqCst);
|
||||
|
||||
if let Some(enabled) = &self.border {
|
||||
border_manager::BORDER_ENABLED.store(*enabled, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
if let Some(colours) = &self.border_colours {
|
||||
if let Some(single) = colours.single {
|
||||
@@ -1033,11 +881,6 @@ impl StaticConfig {
|
||||
if let Some(unfocused) = colours.unfocused {
|
||||
border_manager::UNFOCUSED.store(u32::from(unfocused), Ordering::SeqCst);
|
||||
}
|
||||
|
||||
if let Some(unfocused_locked) = colours.unfocused_locked {
|
||||
border_manager::UNFOCUSED_LOCKED
|
||||
.store(u32::from(unfocused_locked), Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
STYLE.store(self.border_style.unwrap_or_default());
|
||||
@@ -1178,7 +1021,7 @@ impl StaticConfig {
|
||||
}
|
||||
|
||||
if let Some(theme) = &self.theme {
|
||||
theme_manager::send_notification(theme.clone());
|
||||
theme_manager::send_notification(*theme);
|
||||
}
|
||||
|
||||
if let Some(path) = &self.app_specific_configuration_path {
|
||||
@@ -1214,10 +1057,6 @@ impl StaticConfig {
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(behaviour) = self.window_handling_behaviour {
|
||||
WINDOW_HANDLING_BEHAVIOUR.store(behaviour);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1227,7 +1066,44 @@ impl StaticConfig {
|
||||
|
||||
pub fn read(path: &PathBuf) -> Result<Self> {
|
||||
let content = std::fs::read_to_string(path)?;
|
||||
serde_json::from_str(&content).map_err(Into::into)
|
||||
let mut value: Self = serde_json::from_str(&content)?;
|
||||
|
||||
if let Some(path) = &mut value.app_specific_configuration_path {
|
||||
match path {
|
||||
AppSpecificConfigurationPath::Single(path) => {
|
||||
*path = resolve_home_path(&*path)?;
|
||||
}
|
||||
AppSpecificConfigurationPath::Multiple(paths) => {
|
||||
for path in paths {
|
||||
*path = resolve_home_path(&*path)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(monitors) = &mut value.monitors {
|
||||
for m in monitors {
|
||||
for w in &mut m.workspaces {
|
||||
if let Some(path) = &mut w.custom_layout {
|
||||
*path = resolve_home_path(&*path)?;
|
||||
}
|
||||
|
||||
if let Some(map) = &mut w.custom_layout_rules {
|
||||
for path in map.values_mut() {
|
||||
*path = resolve_home_path(&*path)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(bar_configurations) = &mut value.bar_configurations {
|
||||
for path in bar_configurations {
|
||||
*path = resolve_home_path(&*path)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
@@ -1272,16 +1148,6 @@ impl StaticConfig {
|
||||
.window_container_behaviour
|
||||
.unwrap_or(WindowContainerBehaviour::Create),
|
||||
float_override: value.float_override.unwrap_or_default(),
|
||||
floating_layer_override: false, // this value is always automatically calculated
|
||||
floating_layer_behaviour: FloatingLayerBehaviour::default(),
|
||||
toggle_float_placement: value
|
||||
.toggle_float_placement
|
||||
.unwrap_or(Placement::CenterAndResize),
|
||||
floating_layer_placement: value
|
||||
.floating_layer_placement
|
||||
.unwrap_or(Placement::Center),
|
||||
float_override_placement: value.float_override_placement.unwrap_or(Placement::None),
|
||||
float_rule_placement: value.float_rule_placement.unwrap_or(Placement::None),
|
||||
},
|
||||
cross_monitor_move_behaviour: value
|
||||
.cross_monitor_move_behaviour
|
||||
@@ -1299,7 +1165,7 @@ impl StaticConfig {
|
||||
has_pending_raise_op: false,
|
||||
pending_move_op: Arc::new(None),
|
||||
already_moved_window_handles: Arc::new(Mutex::new(HashSet::new())),
|
||||
uncloak_to_ignore: 0,
|
||||
uncloack_to_ignore: 0,
|
||||
known_hwnds: HashMap::new(),
|
||||
};
|
||||
|
||||
@@ -1390,8 +1256,6 @@ impl StaticConfig {
|
||||
);
|
||||
monitor.set_container_padding(monitor_config.container_padding);
|
||||
monitor.set_workspace_padding(monitor_config.workspace_padding);
|
||||
monitor.set_wallpaper(monitor_config.wallpaper.clone());
|
||||
monitor.set_floating_layer_behaviour(monitor_config.floating_layer_behaviour);
|
||||
|
||||
monitor.update_workspaces_globals(offset);
|
||||
for (j, ws) in monitor.workspaces_mut().iter_mut().enumerate() {
|
||||
@@ -1477,7 +1341,6 @@ impl StaticConfig {
|
||||
);
|
||||
m.set_container_padding(monitor_config.container_padding);
|
||||
m.set_workspace_padding(monitor_config.workspace_padding);
|
||||
m.set_floating_layer_behaviour(monitor_config.floating_layer_behaviour);
|
||||
|
||||
m.update_workspaces_globals(offset);
|
||||
|
||||
@@ -1564,8 +1427,6 @@ impl StaticConfig {
|
||||
);
|
||||
monitor.set_container_padding(monitor_config.container_padding);
|
||||
monitor.set_workspace_padding(monitor_config.workspace_padding);
|
||||
monitor.set_wallpaper(monitor_config.wallpaper.clone());
|
||||
monitor.set_floating_layer_behaviour(monitor_config.floating_layer_behaviour);
|
||||
|
||||
monitor.update_workspaces_globals(offset);
|
||||
|
||||
@@ -1652,7 +1513,6 @@ impl StaticConfig {
|
||||
);
|
||||
m.set_container_padding(monitor_config.container_padding);
|
||||
m.set_workspace_padding(monitor_config.workspace_padding);
|
||||
m.set_floating_layer_behaviour(monitor_config.floating_layer_behaviour);
|
||||
|
||||
m.update_workspaces_globals(offset);
|
||||
|
||||
@@ -1669,32 +1529,41 @@ impl StaticConfig {
|
||||
|
||||
wm.enforce_workspace_rules()?;
|
||||
|
||||
border_manager::BORDER_ENABLED.store(value.border.unwrap_or(true), Ordering::SeqCst);
|
||||
wm.window_management_behaviour.current_behaviour =
|
||||
value.window_container_behaviour.unwrap_or_default();
|
||||
wm.window_management_behaviour.float_override = value.float_override.unwrap_or_default();
|
||||
wm.window_management_behaviour.floating_layer_behaviour =
|
||||
value.floating_layer_behaviour.unwrap_or_default();
|
||||
wm.window_management_behaviour.toggle_float_placement = value
|
||||
.toggle_float_placement
|
||||
.unwrap_or(Placement::CenterAndResize);
|
||||
wm.window_management_behaviour.floating_layer_placement =
|
||||
value.floating_layer_placement.unwrap_or(Placement::Center);
|
||||
wm.window_management_behaviour.float_override_placement =
|
||||
value.float_override_placement.unwrap_or(Placement::None);
|
||||
wm.window_management_behaviour.float_rule_placement =
|
||||
value.float_rule_placement.unwrap_or(Placement::None);
|
||||
wm.cross_monitor_move_behaviour = value.cross_monitor_move_behaviour.unwrap_or_default();
|
||||
wm.cross_boundary_behaviour = value.cross_boundary_behaviour.unwrap_or_default();
|
||||
wm.unmanaged_window_operation_behaviour = value
|
||||
.unmanaged_window_operation_behaviour
|
||||
.unwrap_or_default();
|
||||
wm.resize_delta = value.resize_delta.unwrap_or(50);
|
||||
wm.mouse_follows_focus = value.mouse_follows_focus.unwrap_or(true);
|
||||
wm.work_area_offset = value.global_work_area_offset;
|
||||
wm.focus_follows_mouse = value.focus_follows_mouse;
|
||||
if let Some(enabled) = value.border {
|
||||
border_manager::BORDER_ENABLED.store(enabled, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
match wm.focus_follows_mouse {
|
||||
if let Some(val) = value.window_container_behaviour {
|
||||
wm.window_management_behaviour.current_behaviour = val;
|
||||
}
|
||||
|
||||
if let Some(val) = value.float_override {
|
||||
wm.window_management_behaviour.float_override = val;
|
||||
}
|
||||
|
||||
if let Some(val) = value.cross_monitor_move_behaviour {
|
||||
wm.cross_monitor_move_behaviour = val;
|
||||
}
|
||||
|
||||
if let Some(val) = value.cross_boundary_behaviour {
|
||||
wm.cross_boundary_behaviour = val;
|
||||
}
|
||||
|
||||
if let Some(val) = value.unmanaged_window_operation_behaviour {
|
||||
wm.unmanaged_window_operation_behaviour = val;
|
||||
}
|
||||
|
||||
if let Some(val) = value.resize_delta {
|
||||
wm.resize_delta = val;
|
||||
}
|
||||
|
||||
if let Some(val) = value.mouse_follows_focus {
|
||||
wm.mouse_follows_focus = val;
|
||||
}
|
||||
|
||||
wm.work_area_offset = value.global_work_area_offset;
|
||||
|
||||
match value.focus_follows_mouse {
|
||||
None => WindowsApi::disable_focus_follows_mouse()?,
|
||||
Some(FocusFollowsMouseImplementation::Windows) => {
|
||||
WindowsApi::enable_focus_follows_mouse()?;
|
||||
@@ -1702,12 +1571,12 @@ impl StaticConfig {
|
||||
Some(FocusFollowsMouseImplementation::Komorebi) => {}
|
||||
};
|
||||
|
||||
wm.focus_follows_mouse = value.focus_follows_mouse;
|
||||
|
||||
let monitor_count = wm.monitors().len();
|
||||
|
||||
for i in 0..monitor_count {
|
||||
wm.update_focused_workspace_by_monitor_idx(i)?;
|
||||
let ws_idx = wm.focused_workspace_idx_for_monitor_idx(i)?;
|
||||
wm.apply_wallpaper_for_monitor_workspace(i, ws_idx)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -1796,6 +1665,7 @@ fn handle_asc_file(
|
||||
Some(ext) => match ext.to_string_lossy().to_string().as_str() {
|
||||
"yaml" => {
|
||||
tracing::info!("loading applications.yaml from: {}", path.display());
|
||||
let path = resolve_home_path(path)?;
|
||||
let content = std::fs::read_to_string(path)?;
|
||||
let asc = ApplicationConfigurationGenerator::load(&content)?;
|
||||
|
||||
@@ -1844,7 +1714,8 @@ fn handle_asc_file(
|
||||
}
|
||||
"json" => {
|
||||
tracing::info!("loading applications.json from: {}", path.display());
|
||||
let mut asc = ApplicationSpecificConfiguration::load(path)?;
|
||||
let path = resolve_home_path(path)?;
|
||||
let mut asc = ApplicationSpecificConfiguration::load(&path)?;
|
||||
|
||||
for entry in asc.values_mut() {
|
||||
match entry {
|
||||
@@ -1906,10 +1777,7 @@ fn handle_asc_file(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::StaticConfig;
|
||||
use crate::WorkspaceConfig;
|
||||
|
||||
#[test]
|
||||
fn backwards_compat() {
|
||||
@@ -1938,40 +1806,4 @@ mod tests {
|
||||
StaticConfig::read_raw(&config).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_custom_layout_rules() {
|
||||
// set an environment variable for testing
|
||||
std::env::set_var("VAR", "VALUE");
|
||||
|
||||
let config = r#"
|
||||
{
|
||||
"name": "Test",
|
||||
"custom_layout_rules": {
|
||||
"1": "path/to/dir",
|
||||
"2": "path/to/%VAR%"
|
||||
}
|
||||
}
|
||||
"#;
|
||||
let config = serde_json::from_str::<WorkspaceConfig>(config).unwrap();
|
||||
|
||||
let custom_layout_rules = config.custom_layout_rules.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
custom_layout_rules.get(&1).unwrap(),
|
||||
&PathBuf::from("path/to/dir")
|
||||
);
|
||||
assert_eq!(
|
||||
custom_layout_rules.get(&2).unwrap(),
|
||||
&PathBuf::from("path/to/VALUE")
|
||||
);
|
||||
|
||||
let config = r#"
|
||||
{
|
||||
"name": "Test",
|
||||
}
|
||||
"#;
|
||||
let config = serde_json::from_str::<WorkspaceConfig>(config).unwrap();
|
||||
assert_eq!(config.custom_layout_rules, None);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,12 +5,11 @@ use crate::stackbar_manager;
|
||||
use crate::stackbar_manager::STACKBAR_FOCUSED_TEXT_COLOUR;
|
||||
use crate::stackbar_manager::STACKBAR_TAB_BACKGROUND_COLOUR;
|
||||
use crate::stackbar_manager::STACKBAR_UNFOCUSED_TEXT_COLOUR;
|
||||
use crate::Colour;
|
||||
use crate::KomorebiTheme;
|
||||
use crossbeam_channel::Receiver;
|
||||
use crossbeam_channel::Sender;
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use komorebi_themes::colour::Colour;
|
||||
use komorebi_themes::Base16Wrapper;
|
||||
use std::ops::Deref;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::OnceLock;
|
||||
@@ -158,100 +157,39 @@ pub fn handle_notifications() -> color_eyre::Result<()> {
|
||||
} => {
|
||||
let single_border = single_border
|
||||
.unwrap_or(komorebi_themes::Base16Value::Base0D)
|
||||
.color32(Base16Wrapper::Base16(*name));
|
||||
.color32(*name);
|
||||
|
||||
let stack_border = stack_border
|
||||
.unwrap_or(komorebi_themes::Base16Value::Base0B)
|
||||
.color32(Base16Wrapper::Base16(*name));
|
||||
.color32(*name);
|
||||
|
||||
let monocle_border = monocle_border
|
||||
.unwrap_or(komorebi_themes::Base16Value::Base0F)
|
||||
.color32(Base16Wrapper::Base16(*name));
|
||||
.color32(*name);
|
||||
|
||||
let unfocused_border = unfocused_border
|
||||
.unwrap_or(komorebi_themes::Base16Value::Base01)
|
||||
.color32(Base16Wrapper::Base16(*name));
|
||||
.color32(*name);
|
||||
|
||||
let unfocused_locked_border = unfocused_locked_border
|
||||
.unwrap_or(komorebi_themes::Base16Value::Base08)
|
||||
.color32(Base16Wrapper::Base16(*name));
|
||||
.color32(*name);
|
||||
|
||||
let floating_border = floating_border
|
||||
.unwrap_or(komorebi_themes::Base16Value::Base09)
|
||||
.color32(Base16Wrapper::Base16(*name));
|
||||
.color32(*name);
|
||||
|
||||
let stackbar_focused_text = stackbar_focused_text
|
||||
.unwrap_or(komorebi_themes::Base16Value::Base0B)
|
||||
.color32(Base16Wrapper::Base16(*name));
|
||||
.color32(*name);
|
||||
|
||||
let stackbar_unfocused_text = stackbar_unfocused_text
|
||||
.unwrap_or(komorebi_themes::Base16Value::Base05)
|
||||
.color32(Base16Wrapper::Base16(*name));
|
||||
.color32(*name);
|
||||
|
||||
let stackbar_background = stackbar_background
|
||||
.unwrap_or(komorebi_themes::Base16Value::Base01)
|
||||
.color32(Base16Wrapper::Base16(*name));
|
||||
|
||||
(
|
||||
single_border,
|
||||
stack_border,
|
||||
monocle_border,
|
||||
floating_border,
|
||||
unfocused_border,
|
||||
unfocused_locked_border,
|
||||
stackbar_focused_text,
|
||||
stackbar_unfocused_text,
|
||||
stackbar_background,
|
||||
)
|
||||
}
|
||||
KomorebiTheme::Custom {
|
||||
colours,
|
||||
single_border,
|
||||
stack_border,
|
||||
monocle_border,
|
||||
floating_border,
|
||||
unfocused_border,
|
||||
unfocused_locked_border,
|
||||
stackbar_focused_text,
|
||||
stackbar_unfocused_text,
|
||||
stackbar_background,
|
||||
..
|
||||
} => {
|
||||
let single_border = single_border
|
||||
.unwrap_or(komorebi_themes::Base16Value::Base0D)
|
||||
.color32(Base16Wrapper::Custom(colours.clone()));
|
||||
|
||||
let stack_border = stack_border
|
||||
.unwrap_or(komorebi_themes::Base16Value::Base0B)
|
||||
.color32(Base16Wrapper::Custom(colours.clone()));
|
||||
|
||||
let monocle_border = monocle_border
|
||||
.unwrap_or(komorebi_themes::Base16Value::Base0F)
|
||||
.color32(Base16Wrapper::Custom(colours.clone()));
|
||||
|
||||
let unfocused_border = unfocused_border
|
||||
.unwrap_or(komorebi_themes::Base16Value::Base01)
|
||||
.color32(Base16Wrapper::Custom(colours.clone()));
|
||||
|
||||
let unfocused_locked_border = unfocused_locked_border
|
||||
.unwrap_or(komorebi_themes::Base16Value::Base08)
|
||||
.color32(Base16Wrapper::Custom(colours.clone()));
|
||||
|
||||
let floating_border = floating_border
|
||||
.unwrap_or(komorebi_themes::Base16Value::Base09)
|
||||
.color32(Base16Wrapper::Custom(colours.clone()));
|
||||
|
||||
let stackbar_focused_text = stackbar_focused_text
|
||||
.unwrap_or(komorebi_themes::Base16Value::Base0B)
|
||||
.color32(Base16Wrapper::Custom(colours.clone()));
|
||||
|
||||
let stackbar_unfocused_text = stackbar_unfocused_text
|
||||
.unwrap_or(komorebi_themes::Base16Value::Base05)
|
||||
.color32(Base16Wrapper::Custom(colours.clone()));
|
||||
|
||||
let stackbar_background = stackbar_background
|
||||
.unwrap_or(komorebi_themes::Base16Value::Base01)
|
||||
.color32(Base16Wrapper::Custom(colours.clone()));
|
||||
.color32(*name);
|
||||
|
||||
(
|
||||
single_border,
|
||||
@@ -295,7 +233,7 @@ pub fn handle_notifications() -> color_eyre::Result<()> {
|
||||
|
||||
CURRENT_THEME.store(Some(notification.0));
|
||||
|
||||
border_manager::send_force_update();
|
||||
border_manager::send_notification(None);
|
||||
stackbar_manager::send_notification();
|
||||
}
|
||||
|
||||
|
||||
@@ -203,8 +203,8 @@ impl RenderDispatcher for MovementRenderDispatcher {
|
||||
fn render(&self, progress: f64) -> Result<()> {
|
||||
let new_rect = self.start_rect.lerp(self.target_rect, progress, self.style);
|
||||
|
||||
// we don't check WINDOW_HANDLING_BEHAVIOUR here because animations
|
||||
// are always run on a separate thread
|
||||
// using MoveWindow because it runs faster than SetWindowPos
|
||||
// so animation have more fps and feel smoother
|
||||
WindowsApi::move_window(self.hwnd, &new_rect, false)?;
|
||||
WindowsApi::invalidate_rect(self.hwnd, None, false);
|
||||
|
||||
@@ -212,9 +212,7 @@ impl RenderDispatcher for MovementRenderDispatcher {
|
||||
}
|
||||
|
||||
fn post_render(&self) -> Result<()> {
|
||||
// we don't add the async_window_pos flag here because animations
|
||||
// are always run on a separate thread
|
||||
WindowsApi::position_window(self.hwnd, &self.target_rect, self.top, false)?;
|
||||
WindowsApi::position_window(self.hwnd, &self.target_rect, self.top)?;
|
||||
if ANIMATION_MANAGER
|
||||
.lock()
|
||||
.count_in_progress(MovementRenderDispatcher::PREFIX)
|
||||
@@ -413,18 +411,12 @@ impl Window {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn center(&mut self, work_area: &Rect, resize: bool) -> Result<()> {
|
||||
let (target_width, target_height) = if resize {
|
||||
let (aspect_ratio_width, aspect_ratio_height) = FLOATING_WINDOW_TOGGLE_ASPECT_RATIO
|
||||
.lock()
|
||||
.width_and_height();
|
||||
let target_height = work_area.bottom / 2;
|
||||
let target_width = (target_height * aspect_ratio_width) / aspect_ratio_height;
|
||||
(target_width, target_height)
|
||||
} else {
|
||||
let current_rect = WindowsApi::window_rect(self.hwnd)?;
|
||||
(current_rect.right, current_rect.bottom)
|
||||
};
|
||||
pub fn center(&mut self, work_area: &Rect) -> Result<()> {
|
||||
let (aspect_ratio_width, aspect_ratio_height) = FLOATING_WINDOW_TOGGLE_ASPECT_RATIO
|
||||
.lock()
|
||||
.width_and_height();
|
||||
let target_height = work_area.bottom / 2;
|
||||
let target_width = (target_height * aspect_ratio_width) / aspect_ratio_height;
|
||||
|
||||
let x = work_area.left + ((work_area.right - target_width) / 2);
|
||||
let y = work_area.top + ((work_area.bottom - target_height) / 2);
|
||||
@@ -469,7 +461,7 @@ impl Window {
|
||||
|
||||
AnimationEngine::animate(render_dispatcher, duration)
|
||||
} else {
|
||||
WindowsApi::position_window(self.hwnd, layout, top, true)
|
||||
WindowsApi::position_window(self.hwnd, layout, top)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -80,6 +80,7 @@ use crate::workspace::WorkspaceLayer;
|
||||
use crate::BorderColours;
|
||||
use crate::Colour;
|
||||
use crate::CrossBoundaryBehaviour;
|
||||
use crate::FloatingLayerBehaviour;
|
||||
use crate::Rgb;
|
||||
use crate::CUSTOM_FFM;
|
||||
use crate::DATA_DIR;
|
||||
@@ -120,7 +121,7 @@ pub struct WindowManager {
|
||||
pub has_pending_raise_op: bool,
|
||||
pub pending_move_op: Arc<Option<(usize, usize, isize)>>,
|
||||
pub already_moved_window_handles: Arc<Mutex<HashSet<isize>>>,
|
||||
pub uncloak_to_ignore: usize,
|
||||
pub uncloack_to_ignore: usize,
|
||||
/// Maps each known window hwnd to the (monitor, workspace) index pair managing it
|
||||
pub known_hwnds: HashMap<isize, (usize, usize)>,
|
||||
}
|
||||
@@ -247,9 +248,6 @@ impl Default for GlobalState {
|
||||
unfocused: Option::from(Colour::Rgb(Rgb::from(
|
||||
border_manager::UNFOCUSED.load(Ordering::SeqCst),
|
||||
))),
|
||||
unfocused_locked: Option::from(Colour::Rgb(Rgb::from(
|
||||
border_manager::UNFOCUSED_LOCKED.load(Ordering::SeqCst),
|
||||
))),
|
||||
},
|
||||
border_style: STYLE.load(),
|
||||
border_offset: border_manager::BORDER_OFFSET.load(Ordering::SeqCst),
|
||||
@@ -347,7 +345,6 @@ impl From<&WindowManager> for State {
|
||||
floating_layer_behaviour: workspace.floating_layer_behaviour,
|
||||
globals: workspace.globals,
|
||||
locked_containers: workspace.locked_containers.clone(),
|
||||
wallpaper: workspace.wallpaper.clone(),
|
||||
workspace_config: None,
|
||||
})
|
||||
.collect::<VecDeque<_>>();
|
||||
@@ -358,8 +355,6 @@ impl From<&WindowManager> for State {
|
||||
workspace_names: monitor.workspace_names.clone(),
|
||||
container_padding: monitor.container_padding,
|
||||
workspace_padding: monitor.workspace_padding,
|
||||
wallpaper: monitor.wallpaper.clone(),
|
||||
floating_layer_behaviour: monitor.floating_layer_behaviour,
|
||||
})
|
||||
.collect::<VecDeque<_>>();
|
||||
stripped_monitors.focus(wm.monitors.focused_idx());
|
||||
@@ -438,7 +433,7 @@ impl WindowManager {
|
||||
work_area_offset: None,
|
||||
window_management_behaviour: WindowManagementBehaviour::default(),
|
||||
cross_monitor_move_behaviour: MoveBehaviour::Swap,
|
||||
cross_boundary_behaviour: CrossBoundaryBehaviour::Monitor,
|
||||
cross_boundary_behaviour: CrossBoundaryBehaviour::Workspace,
|
||||
unmanaged_window_operation_behaviour: OperationBehaviour::Op,
|
||||
resize_delta: 50,
|
||||
focus_follows_mouse: None,
|
||||
@@ -447,7 +442,7 @@ impl WindowManager {
|
||||
has_pending_raise_op: false,
|
||||
pending_move_op: Arc::new(None),
|
||||
already_moved_window_handles: Arc::new(Mutex::new(HashSet::new())),
|
||||
uncloak_to_ignore: 0,
|
||||
uncloack_to_ignore: 0,
|
||||
known_hwnds: HashMap::new(),
|
||||
})
|
||||
}
|
||||
@@ -645,40 +640,24 @@ impl WindowManager {
|
||||
self.window_management_behaviour.current_behaviour
|
||||
};
|
||||
|
||||
let float_override = if let Some(float_override) = workspace.float_override() {
|
||||
let mut float_override = if let Some(float_override) = workspace.float_override() {
|
||||
*float_override
|
||||
} else {
|
||||
self.window_management_behaviour.float_override
|
||||
};
|
||||
|
||||
let floating_layer_behaviour =
|
||||
if let Some(behaviour) = workspace.floating_layer_behaviour() {
|
||||
behaviour
|
||||
} else {
|
||||
monitor
|
||||
.floating_layer_behaviour()
|
||||
.unwrap_or(self.window_management_behaviour.floating_layer_behaviour)
|
||||
};
|
||||
|
||||
// If the workspace layer is `Floating` and the floating layer behaviour should
|
||||
// float then change floating_layer_override to true so that new windows spawn
|
||||
// as floating
|
||||
let floating_layer_override = matches!(workspace.layer, WorkspaceLayer::Floating)
|
||||
&& floating_layer_behaviour.should_float();
|
||||
// If the workspace layer is `Floating` and the floating layer behaviour is `Float`,
|
||||
// then consider it as if it had float override so that new windows spawn as floating
|
||||
float_override = float_override
|
||||
|| (matches!(workspace.layer, WorkspaceLayer::Floating)
|
||||
&& matches!(
|
||||
workspace.floating_layer_behaviour,
|
||||
FloatingLayerBehaviour::Float
|
||||
));
|
||||
|
||||
return WindowManagementBehaviour {
|
||||
current_behaviour,
|
||||
float_override,
|
||||
floating_layer_override,
|
||||
floating_layer_behaviour,
|
||||
toggle_float_placement: self.window_management_behaviour.toggle_float_placement,
|
||||
floating_layer_placement: self
|
||||
.window_management_behaviour
|
||||
.floating_layer_placement,
|
||||
float_override_placement: self
|
||||
.window_management_behaviour
|
||||
.float_override_placement,
|
||||
float_rule_placement: self.window_management_behaviour.float_rule_placement,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -686,12 +665,6 @@ impl WindowManager {
|
||||
WindowManagementBehaviour {
|
||||
current_behaviour: WindowContainerBehaviour::Create,
|
||||
float_override: self.window_management_behaviour.float_override,
|
||||
floating_layer_override: self.window_management_behaviour.floating_layer_override,
|
||||
floating_layer_behaviour: self.window_management_behaviour.floating_layer_behaviour,
|
||||
toggle_float_placement: self.window_management_behaviour.toggle_float_placement,
|
||||
floating_layer_placement: self.window_management_behaviour.floating_layer_placement,
|
||||
float_override_placement: self.window_management_behaviour.float_override_placement,
|
||||
float_rule_placement: self.window_management_behaviour.float_rule_placement,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1037,8 +1010,6 @@ impl WindowManager {
|
||||
let focused_workspace_idx = monitor.focused_workspace_idx();
|
||||
monitor.update_workspace_globals(focused_workspace_idx, offset);
|
||||
|
||||
let hmonitor = monitor.id();
|
||||
let monitor_wp = monitor.wallpaper.clone();
|
||||
let workspace = monitor
|
||||
.focused_workspace_mut()
|
||||
.ok_or_else(|| anyhow!("there is no workspace"))?;
|
||||
@@ -1050,12 +1021,6 @@ impl WindowManager {
|
||||
}
|
||||
}
|
||||
|
||||
if workspace.wallpaper().is_some() || monitor_wp.is_some() {
|
||||
if let Err(error) = workspace.apply_wallpaper(hmonitor, &monitor_wp) {
|
||||
tracing::error!("failed to apply wallpaper: {}", error);
|
||||
}
|
||||
}
|
||||
|
||||
workspace.update()?;
|
||||
}
|
||||
|
||||
@@ -1234,7 +1199,7 @@ impl WindowManager {
|
||||
// That workspace reconciliation would focus the window on the origin monitor.
|
||||
// So we need to ignore the uncloak events produced by the origin workspace
|
||||
// restore to avoid that issue.
|
||||
self.uncloak_to_ignore = uncloack_amount;
|
||||
self.uncloack_to_ignore = uncloack_amount;
|
||||
}
|
||||
} else if origin_workspace
|
||||
.maximized_window()
|
||||
@@ -1517,7 +1482,7 @@ impl WindowManager {
|
||||
}
|
||||
}
|
||||
|
||||
WindowsApi::position_window(window.hwnd, &rect, false, true)?;
|
||||
WindowsApi::position_window(window.hwnd, &rect, false)?;
|
||||
if mouse_follows_focus {
|
||||
WindowsApi::center_cursor_in_rect(&rect)?;
|
||||
}
|
||||
@@ -1739,30 +1704,6 @@ impl WindowManager {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check for an existing wallpaper definition on the workspace/monitor index pair and apply it
|
||||
/// if it exists
|
||||
#[tracing::instrument(skip(self))]
|
||||
pub fn apply_wallpaper_for_monitor_workspace(
|
||||
&mut self,
|
||||
monitor_idx: usize,
|
||||
workspace_idx: usize,
|
||||
) -> Result<()> {
|
||||
let monitor = self
|
||||
.monitors_mut()
|
||||
.get_mut(monitor_idx)
|
||||
.ok_or_else(|| anyhow!("there is no monitor"))?;
|
||||
|
||||
let hmonitor = monitor.id();
|
||||
let monitor_wp = monitor.wallpaper.clone();
|
||||
|
||||
let workspace = monitor
|
||||
.workspaces()
|
||||
.get(workspace_idx)
|
||||
.ok_or_else(|| anyhow!("there is no workspace"))?;
|
||||
|
||||
workspace.apply_wallpaper(hmonitor, &monitor_wp)
|
||||
}
|
||||
|
||||
pub fn update_focused_workspace_by_monitor_idx(&mut self, idx: usize) -> Result<()> {
|
||||
let offset = self.work_area_offset;
|
||||
|
||||
@@ -1923,18 +1864,6 @@ impl WindowManager {
|
||||
.focused_workspace_mut()
|
||||
.ok_or_else(|| anyhow!("there is no focused workspace on target monitor"))?;
|
||||
|
||||
if target_workspace.monocle_container().is_some() {
|
||||
for container in target_workspace.containers_mut() {
|
||||
container.restore();
|
||||
}
|
||||
|
||||
for window in target_workspace.floating_windows_mut() {
|
||||
window.restore();
|
||||
}
|
||||
|
||||
target_workspace.reintegrate_monocle_container()?;
|
||||
}
|
||||
|
||||
if let Some(window) = floating_window {
|
||||
target_workspace.floating_windows_mut().push_back(window);
|
||||
target_workspace.set_layer(WorkspaceLayer::Floating);
|
||||
@@ -2533,7 +2462,7 @@ impl WindowManager {
|
||||
}
|
||||
}
|
||||
|
||||
WindowsApi::position_window(window.hwnd, &rect, false, true)?;
|
||||
WindowsApi::position_window(window.hwnd, &rect, false)?;
|
||||
if mouse_follows_focus {
|
||||
WindowsApi::center_cursor_in_rect(&rect)?;
|
||||
}
|
||||
@@ -3128,7 +3057,7 @@ impl WindowManager {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
pub fn toggle_float(&mut self, force_float: bool) -> Result<()> {
|
||||
pub fn toggle_float(&mut self) -> Result<()> {
|
||||
let hwnd = WindowsApi::foreground_window()?;
|
||||
let workspace = self.focused_workspace_mut()?;
|
||||
|
||||
@@ -3140,7 +3069,7 @@ impl WindowManager {
|
||||
}
|
||||
}
|
||||
|
||||
if is_floating_window && !force_float {
|
||||
if is_floating_window {
|
||||
workspace.set_layer(WorkspaceLayer::Tiling);
|
||||
self.unfloat_window()?;
|
||||
} else {
|
||||
@@ -3171,8 +3100,6 @@ impl WindowManager {
|
||||
|
||||
let work_area = self.focused_monitor_work_area()?;
|
||||
|
||||
let toggle_float_placement = self.window_management_behaviour.toggle_float_placement;
|
||||
|
||||
let workspace = self.focused_workspace_mut()?;
|
||||
workspace.new_floating_window()?;
|
||||
|
||||
@@ -3181,9 +3108,7 @@ impl WindowManager {
|
||||
.back_mut()
|
||||
.ok_or_else(|| anyhow!("there is no floating window"))?;
|
||||
|
||||
if toggle_float_placement.should_center() {
|
||||
window.center(&work_area, toggle_float_placement.should_resize())?;
|
||||
}
|
||||
window.center(&work_area)?;
|
||||
window.focus(self.mouse_follows_focus)?;
|
||||
|
||||
Ok(())
|
||||
@@ -5055,534 +4980,4 @@ mod tests {
|
||||
assert!(*workspace.tile());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_toggle_lock() {
|
||||
let (mut wm, _context) = setup_window_manager();
|
||||
|
||||
{
|
||||
// Add monitor with default workspace to
|
||||
let mut m = monitor::new(
|
||||
0,
|
||||
Rect::default(),
|
||||
Rect::default(),
|
||||
"TestMonitor".to_string(),
|
||||
"TestDevice".to_string(),
|
||||
"TestDeviceID".to_string(),
|
||||
Some("TestMonitorID".to_string()),
|
||||
);
|
||||
|
||||
let workspace = m.focused_workspace_mut().unwrap();
|
||||
|
||||
// Create containers to add to the workspace
|
||||
for _ in 0..3 {
|
||||
let container = Container::default();
|
||||
workspace.add_container_to_back(container);
|
||||
}
|
||||
|
||||
wm.monitors_mut().push_back(m);
|
||||
}
|
||||
|
||||
{
|
||||
// Ensure container 2 is not locked
|
||||
let workspace = wm.focused_workspace_mut().unwrap();
|
||||
assert!(!workspace.locked_containers().contains(&2));
|
||||
}
|
||||
|
||||
// Toggle lock on focused container
|
||||
wm.toggle_lock().unwrap();
|
||||
|
||||
{
|
||||
// Ensure container 2 is locked
|
||||
let workspace = wm.focused_workspace_mut().unwrap();
|
||||
assert!(workspace.locked_containers().contains(&2));
|
||||
}
|
||||
|
||||
// Toggle lock on focused container
|
||||
wm.toggle_lock().unwrap();
|
||||
|
||||
{
|
||||
// Ensure container 2 is not locked
|
||||
let workspace = wm.focused_workspace_mut().unwrap();
|
||||
assert!(!workspace.locked_containers().contains(&2));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_float_window() {
|
||||
let (mut wm, _context) = setup_window_manager();
|
||||
|
||||
{
|
||||
// Create a monitor
|
||||
let mut m = monitor::new(
|
||||
0,
|
||||
Rect::default(),
|
||||
Rect::default(),
|
||||
"TestMonitor".to_string(),
|
||||
"TestDevice".to_string(),
|
||||
"TestDeviceID".to_string(),
|
||||
Some("TestMonitorID".to_string()),
|
||||
);
|
||||
|
||||
// Create a container
|
||||
let mut container = Container::default();
|
||||
|
||||
// Add three windows to the container
|
||||
for i in 0..3 {
|
||||
container.windows_mut().push_back(Window::from(i));
|
||||
}
|
||||
|
||||
// Should have 3 windows in the container
|
||||
assert_eq!(container.windows().len(), 3);
|
||||
|
||||
// Add the container to the workspace
|
||||
let workspace = m.focused_workspace_mut().unwrap();
|
||||
workspace.add_container_to_back(container);
|
||||
|
||||
// Add monitor to the window manager
|
||||
wm.monitors_mut().push_back(m);
|
||||
}
|
||||
|
||||
// Add focused window to floating window list
|
||||
wm.float_window().ok();
|
||||
|
||||
{
|
||||
let workspace = wm.focused_workspace().unwrap();
|
||||
let floating_windows = workspace.floating_windows();
|
||||
let container = workspace.focused_container().unwrap();
|
||||
|
||||
// Hwnd 0 should be added to floating_windows
|
||||
assert_eq!(floating_windows[0].hwnd, 0);
|
||||
|
||||
// Should have a length of 1
|
||||
assert_eq!(floating_windows.len(), 1);
|
||||
|
||||
// Should have 2 windows in the container
|
||||
assert_eq!(container.windows().len(), 2);
|
||||
|
||||
// Should be focused on window 1
|
||||
assert_eq!(container.focused_window(), Some(&Window { hwnd: 1 }));
|
||||
}
|
||||
|
||||
// Add focused window to floating window list
|
||||
wm.float_window().ok();
|
||||
|
||||
{
|
||||
let workspace = wm.focused_workspace().unwrap();
|
||||
let floating_windows = workspace.floating_windows();
|
||||
let container = workspace.focused_container().unwrap();
|
||||
|
||||
// Hwnd 1 should be added to floating_windows
|
||||
assert_eq!(floating_windows[1].hwnd, 1);
|
||||
|
||||
// Should have a length of 2
|
||||
assert_eq!(floating_windows.len(), 2);
|
||||
|
||||
// Should have 1 window in the container
|
||||
assert_eq!(container.windows().len(), 1);
|
||||
|
||||
// Should be focused on window 2
|
||||
assert_eq!(container.focused_window(), Some(&Window { hwnd: 2 }));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_maximize_and_unmaximize_window() {
|
||||
let (mut wm, _context) = setup_window_manager();
|
||||
|
||||
{
|
||||
// Create a monitor
|
||||
let mut m = monitor::new(
|
||||
0,
|
||||
Rect::default(),
|
||||
Rect::default(),
|
||||
"TestMonitor".to_string(),
|
||||
"TestDevice".to_string(),
|
||||
"TestDeviceID".to_string(),
|
||||
Some("TestMonitorID".to_string()),
|
||||
);
|
||||
|
||||
// Create a container
|
||||
let mut container = Container::default();
|
||||
|
||||
// Add three windows to the container
|
||||
for i in 0..3 {
|
||||
container.windows_mut().push_back(Window::from(i));
|
||||
}
|
||||
|
||||
// Should have 3 windows in the container
|
||||
assert_eq!(container.windows().len(), 3);
|
||||
|
||||
// Add the container to the workspace
|
||||
let workspace = m.focused_workspace_mut().unwrap();
|
||||
workspace.add_container_to_back(container);
|
||||
|
||||
// Add monitor to the window manager
|
||||
wm.monitors_mut().push_back(m);
|
||||
}
|
||||
|
||||
{
|
||||
// No windows should be maximized
|
||||
let workspace = wm.focused_workspace().unwrap();
|
||||
let maximized_window = workspace.maximized_window();
|
||||
assert_eq!(*maximized_window, None);
|
||||
}
|
||||
|
||||
// Maximize the focused window
|
||||
wm.maximize_window().ok();
|
||||
|
||||
{
|
||||
// Window 0 should be maximized
|
||||
let workspace = wm.focused_workspace().unwrap();
|
||||
let maximized_window = workspace.maximized_window();
|
||||
assert_eq!(*maximized_window, Some(Window::from(0)));
|
||||
}
|
||||
|
||||
wm.unmaximize_window().ok();
|
||||
|
||||
{
|
||||
// No windows should be maximized
|
||||
let workspace = wm.focused_workspace().unwrap();
|
||||
let maximized_window = workspace.maximized_window();
|
||||
assert_eq!(*maximized_window, None);
|
||||
}
|
||||
|
||||
// Focus container at index 1
|
||||
wm.focused_workspace_mut().unwrap().focus_container(1);
|
||||
|
||||
{
|
||||
// Focus the window at index 1
|
||||
let container = wm.focused_container_mut().unwrap();
|
||||
container.focus_window(1);
|
||||
}
|
||||
|
||||
// Maximize the focused window
|
||||
wm.maximize_window().ok();
|
||||
|
||||
{
|
||||
// Window 2 should be maximized
|
||||
let workspace = wm.focused_workspace().unwrap();
|
||||
let maximized_window = workspace.maximized_window();
|
||||
assert_eq!(*maximized_window, Some(Window::from(2)));
|
||||
}
|
||||
|
||||
wm.unmaximize_window().ok();
|
||||
|
||||
{
|
||||
// No windows should be maximized
|
||||
let workspace = wm.focused_workspace().unwrap();
|
||||
let maximized_window = workspace.maximized_window();
|
||||
assert_eq!(*maximized_window, None);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_toggle_maximize() {
|
||||
let (mut wm, _context) = setup_window_manager();
|
||||
|
||||
{
|
||||
// Create a monitor
|
||||
let mut m = monitor::new(
|
||||
0,
|
||||
Rect::default(),
|
||||
Rect::default(),
|
||||
"TestMonitor".to_string(),
|
||||
"TestDevice".to_string(),
|
||||
"TestDeviceID".to_string(),
|
||||
Some("TestMonitorID".to_string()),
|
||||
);
|
||||
|
||||
// Create a container
|
||||
let mut container = Container::default();
|
||||
|
||||
// Add three windows to the container
|
||||
for i in 0..3 {
|
||||
container.windows_mut().push_back(Window::from(i));
|
||||
}
|
||||
|
||||
// Should have 3 windows in the container
|
||||
assert_eq!(container.windows().len(), 3);
|
||||
|
||||
// Add the container to the workspace
|
||||
let workspace = m.focused_workspace_mut().unwrap();
|
||||
workspace.add_container_to_back(container);
|
||||
|
||||
// Add monitor to the window manager
|
||||
wm.monitors_mut().push_back(m);
|
||||
}
|
||||
|
||||
// Toggle maximize on
|
||||
wm.toggle_maximize().ok();
|
||||
|
||||
{
|
||||
// Window 0 should be maximized
|
||||
let workspace = wm.focused_workspace().unwrap();
|
||||
let maximized_window = workspace.maximized_window();
|
||||
assert_eq!(*maximized_window, Some(Window::from(0)));
|
||||
}
|
||||
|
||||
// Toggle maximize off
|
||||
wm.toggle_maximize().ok();
|
||||
|
||||
{
|
||||
// No windows should be maximized
|
||||
let workspace = wm.focused_workspace().unwrap();
|
||||
let maximized_window = workspace.maximized_window();
|
||||
assert_eq!(*maximized_window, None);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_monocle_on_and_monocle_off() {
|
||||
let (mut wm, _context) = setup_window_manager();
|
||||
|
||||
{
|
||||
// Create a monitor
|
||||
let mut m = monitor::new(
|
||||
0,
|
||||
Rect::default(),
|
||||
Rect::default(),
|
||||
"TestMonitor".to_string(),
|
||||
"TestDevice".to_string(),
|
||||
"TestDeviceID".to_string(),
|
||||
Some("TestMonitorID".to_string()),
|
||||
);
|
||||
|
||||
// Create a container
|
||||
let mut container = Container::default();
|
||||
|
||||
// Add a window to the container
|
||||
container.windows_mut().push_back(Window::from(1));
|
||||
|
||||
// Should have 1 window in the container
|
||||
assert_eq!(container.windows().len(), 1);
|
||||
|
||||
// Add the container to the workspace
|
||||
let workspace = m.focused_workspace_mut().unwrap();
|
||||
workspace.add_container_to_back(container);
|
||||
|
||||
// Add monitor to the window manager
|
||||
wm.monitors_mut().push_back(m);
|
||||
}
|
||||
|
||||
// Move container to monocle container
|
||||
wm.monocle_on().ok();
|
||||
|
||||
{
|
||||
// Container should be a monocle container
|
||||
let monocle_container = wm
|
||||
.focused_workspace()
|
||||
.unwrap()
|
||||
.monocle_container()
|
||||
.as_ref()
|
||||
.unwrap();
|
||||
assert_eq!(monocle_container.windows().len(), 1);
|
||||
assert_eq!(monocle_container.windows()[0].hwnd, 1);
|
||||
}
|
||||
|
||||
{
|
||||
// Should not have any containers
|
||||
let container = wm.focused_workspace().unwrap();
|
||||
assert_eq!(container.containers().len(), 0);
|
||||
}
|
||||
|
||||
// Move monocle container to regular container
|
||||
wm.monocle_off().ok();
|
||||
|
||||
{
|
||||
// Should have 1 container in the workspace
|
||||
let container = wm.focused_workspace().unwrap();
|
||||
assert_eq!(container.containers().len(), 1);
|
||||
assert_eq!(container.containers()[0].windows()[0].hwnd, 1);
|
||||
}
|
||||
|
||||
{
|
||||
// No windows should be in the monocle container
|
||||
let monocle_container = wm.focused_workspace().unwrap().monocle_container();
|
||||
assert_eq!(*monocle_container, None);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_toggle_monocle() {
|
||||
let (mut wm, _context) = setup_window_manager();
|
||||
|
||||
{
|
||||
// Create a monitor
|
||||
let mut m = monitor::new(
|
||||
0,
|
||||
Rect::default(),
|
||||
Rect::default(),
|
||||
"TestMonitor".to_string(),
|
||||
"TestDevice".to_string(),
|
||||
"TestDeviceID".to_string(),
|
||||
Some("TestMonitorID".to_string()),
|
||||
);
|
||||
|
||||
// Create a container
|
||||
let mut container = Container::default();
|
||||
|
||||
// Add a window to the container
|
||||
container.windows_mut().push_back(Window::from(1));
|
||||
|
||||
// Should have 1 window in the container
|
||||
assert_eq!(container.windows().len(), 1);
|
||||
|
||||
// Add the container to the workspace
|
||||
let workspace = m.focused_workspace_mut().unwrap();
|
||||
workspace.add_container_to_back(container);
|
||||
|
||||
// Add monitor to the window manager
|
||||
wm.monitors_mut().push_back(m);
|
||||
}
|
||||
|
||||
// Toggle monocle on
|
||||
wm.toggle_monocle().ok();
|
||||
|
||||
{
|
||||
// Container should be a monocle container
|
||||
let monocle_container = wm
|
||||
.focused_workspace()
|
||||
.unwrap()
|
||||
.monocle_container()
|
||||
.as_ref()
|
||||
.unwrap();
|
||||
assert_eq!(monocle_container.windows().len(), 1);
|
||||
assert_eq!(monocle_container.windows()[0].hwnd, 1);
|
||||
}
|
||||
|
||||
{
|
||||
// Should not have any containers
|
||||
let container = wm.focused_workspace().unwrap();
|
||||
assert_eq!(container.containers().len(), 0);
|
||||
}
|
||||
|
||||
// Toggle monocle off
|
||||
wm.toggle_monocle().ok();
|
||||
|
||||
{
|
||||
// Should have 1 container in the workspace
|
||||
let container = wm.focused_workspace().unwrap();
|
||||
assert_eq!(container.containers().len(), 1);
|
||||
assert_eq!(container.containers()[0].windows()[0].hwnd, 1);
|
||||
}
|
||||
|
||||
{
|
||||
// No windows should be in the monocle container
|
||||
let monocle_container = wm.focused_workspace().unwrap().monocle_container();
|
||||
assert_eq!(*monocle_container, None);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ensure_named_workspace_for_monitor() {
|
||||
let (mut wm, _context) = setup_window_manager();
|
||||
|
||||
{
|
||||
// Create a monitor
|
||||
let m = monitor::new(
|
||||
0,
|
||||
Rect::default(),
|
||||
Rect::default(),
|
||||
"TestMonitor".to_string(),
|
||||
"TestDevice".to_string(),
|
||||
"TestDeviceID".to_string(),
|
||||
Some("TestMonitorID".to_string()),
|
||||
);
|
||||
|
||||
// Add the monitor to the window manager
|
||||
wm.monitors_mut().push_back(m);
|
||||
}
|
||||
|
||||
{
|
||||
// Create a monitor
|
||||
let m = monitor::new(
|
||||
1,
|
||||
Rect::default(),
|
||||
Rect::default(),
|
||||
"TestMonitor1".to_string(),
|
||||
"TestDevice1".to_string(),
|
||||
"TestDeviceID1".to_string(),
|
||||
Some("TestMonitorID1".to_string()),
|
||||
);
|
||||
|
||||
// Add the monitor to the window manager
|
||||
wm.monitors_mut().push_back(m);
|
||||
}
|
||||
|
||||
// Workspace names list
|
||||
let mut workspace_names = vec!["Workspace".to_string(), "Workspace1".to_string()];
|
||||
|
||||
// Ensure workspaces for monitor 1
|
||||
wm.ensure_named_workspaces_for_monitor(1, &workspace_names)
|
||||
.ok();
|
||||
|
||||
{
|
||||
// Monitor 1 should have 2 workspaces with names "Workspace" and "Workspace1"
|
||||
let monitor = wm.monitors().get(1).unwrap();
|
||||
let workspaces = monitor.workspaces();
|
||||
assert_eq!(workspaces.len(), workspace_names.len());
|
||||
for (i, workspace) in workspaces.iter().enumerate() {
|
||||
assert_eq!(workspace.name(), &Some(workspace_names[i].clone()));
|
||||
}
|
||||
}
|
||||
|
||||
// Add more workspaces to list
|
||||
workspace_names.push("Workspace2".to_string());
|
||||
workspace_names.push("Workspace3".to_string());
|
||||
|
||||
// Ensure workspaces for monitor 0
|
||||
wm.ensure_named_workspaces_for_monitor(0, &workspace_names)
|
||||
.ok();
|
||||
|
||||
{
|
||||
// Monitor 0 should have 4 workspaces with names "Workspace", "Workspace1",
|
||||
// "Workspace2" and "Workspace3"
|
||||
let monitor = wm.monitors().front().unwrap();
|
||||
let workspaces = monitor.workspaces();
|
||||
assert_eq!(workspaces.len(), workspace_names.len());
|
||||
for (i, workspace) in workspaces.iter().enumerate() {
|
||||
assert_eq!(workspace.name(), &Some(workspace_names[i].clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_window_handle_to_move_based_on_workspace_rule() {
|
||||
let (wm, _context) = setup_window_manager();
|
||||
|
||||
// Mock Data representing a window and its workspace/movement details
|
||||
let window_title = String::from("TestWindow");
|
||||
let hwnd = 12345;
|
||||
let origin_monitor_idx = 0;
|
||||
let origin_workspace_idx = 0;
|
||||
let target_monitor_idx = 2;
|
||||
let target_workspace_idx = 3;
|
||||
let floating = false;
|
||||
|
||||
// Empty vector to hold workspace rule enforcement operations
|
||||
let mut to_move: Vec<EnforceWorkspaceRuleOp> = Vec::new();
|
||||
|
||||
// Call the function to add a window movement operation based on workspace rules
|
||||
wm.add_window_handle_to_move_based_on_workspace_rule(
|
||||
&window_title,
|
||||
hwnd,
|
||||
origin_monitor_idx,
|
||||
origin_workspace_idx,
|
||||
target_monitor_idx,
|
||||
target_workspace_idx,
|
||||
floating,
|
||||
&mut to_move,
|
||||
);
|
||||
|
||||
// Verify that the vector contains the expected operation with the correct values
|
||||
assert_eq!(to_move.len(), 1);
|
||||
let op = &to_move[0];
|
||||
assert_eq!(op.hwnd, hwnd); // 12345
|
||||
assert_eq!(op.origin_monitor_idx, origin_monitor_idx); // 0
|
||||
assert_eq!(op.origin_workspace_idx, origin_workspace_idx); // 0
|
||||
assert_eq!(op.target_monitor_idx, target_monitor_idx); // 2
|
||||
assert_eq!(op.target_workspace_idx, target_workspace_idx); // 3
|
||||
assert_eq!(op.floating, floating); // false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
use color_eyre::eyre::anyhow;
|
||||
use color_eyre::eyre::bail;
|
||||
use color_eyre::eyre::Error;
|
||||
use color_eyre::Result;
|
||||
use core::ffi::c_void;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::VecDeque;
|
||||
use std::convert::TryFrom;
|
||||
use std::mem::size_of;
|
||||
use std::path::Path;
|
||||
|
||||
use color_eyre::eyre::anyhow;
|
||||
use color_eyre::eyre::bail;
|
||||
use color_eyre::eyre::Error;
|
||||
use color_eyre::Result;
|
||||
use windows::core::Result as WindowsCrateResult;
|
||||
use windows::core::PCWSTR;
|
||||
use windows::core::PWSTR;
|
||||
@@ -47,8 +47,6 @@ use windows::Win32::Graphics::Gdi::HMONITOR;
|
||||
use windows::Win32::Graphics::Gdi::MONITORENUMPROC;
|
||||
use windows::Win32::Graphics::Gdi::MONITORINFOEXW;
|
||||
use windows::Win32::Graphics::Gdi::MONITOR_DEFAULTTONEAREST;
|
||||
use windows::Win32::System::Com::CoCreateInstance;
|
||||
use windows::Win32::System::Com::CLSCTX_ALL;
|
||||
use windows::Win32::System::LibraryLoader::GetModuleHandleW;
|
||||
use windows::Win32::System::Power::RegisterPowerSettingNotification;
|
||||
use windows::Win32::System::Power::HPOWERNOTIFY;
|
||||
@@ -74,9 +72,6 @@ use windows::Win32::UI::Input::KeyboardAndMouse::MOUSEEVENTF_LEFTUP;
|
||||
use windows::Win32::UI::Input::KeyboardAndMouse::MOUSEINPUT;
|
||||
use windows::Win32::UI::Input::KeyboardAndMouse::VK_LBUTTON;
|
||||
use windows::Win32::UI::Input::KeyboardAndMouse::VK_MENU;
|
||||
use windows::Win32::UI::Shell::DesktopWallpaper;
|
||||
use windows::Win32::UI::Shell::IDesktopWallpaper;
|
||||
use windows::Win32::UI::Shell::DWPOS_FILL;
|
||||
use windows::Win32::UI::WindowsAndMessaging::AllowSetForegroundWindow;
|
||||
use windows::Win32::UI::WindowsAndMessaging::BringWindowToTop;
|
||||
use windows::Win32::UI::WindowsAndMessaging::CreateWindowExW;
|
||||
@@ -106,7 +101,6 @@ use windows::Win32::UI::WindowsAndMessaging::SetLayeredWindowAttributes;
|
||||
use windows::Win32::UI::WindowsAndMessaging::SetWindowLongPtrW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::SetWindowPos;
|
||||
use windows::Win32::UI::WindowsAndMessaging::ShowWindow;
|
||||
use windows::Win32::UI::WindowsAndMessaging::ShowWindowAsync;
|
||||
use windows::Win32::UI::WindowsAndMessaging::SystemParametersInfoW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WindowFromPoint;
|
||||
use windows::Win32::UI::WindowsAndMessaging::CW_USEDEFAULT;
|
||||
@@ -126,7 +120,6 @@ use windows::Win32::UI::WindowsAndMessaging::SPI_GETACTIVEWINDOWTRACKING;
|
||||
use windows::Win32::UI::WindowsAndMessaging::SPI_GETFOREGROUNDLOCKTIMEOUT;
|
||||
use windows::Win32::UI::WindowsAndMessaging::SPI_SETACTIVEWINDOWTRACKING;
|
||||
use windows::Win32::UI::WindowsAndMessaging::SPI_SETFOREGROUNDLOCKTIMEOUT;
|
||||
use windows::Win32::UI::WindowsAndMessaging::SWP_ASYNCWINDOWPOS;
|
||||
use windows::Win32::UI::WindowsAndMessaging::SWP_NOMOVE;
|
||||
use windows::Win32::UI::WindowsAndMessaging::SWP_NOSIZE;
|
||||
use windows::Win32::UI::WindowsAndMessaging::SWP_SHOWWINDOW;
|
||||
@@ -148,7 +141,6 @@ use windows::Win32::UI::WindowsAndMessaging::WS_EX_TOPMOST;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WS_POPUP;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WS_SYSMENU;
|
||||
use windows_core::BOOL;
|
||||
use windows_core::HSTRING;
|
||||
|
||||
use crate::core::Rect;
|
||||
|
||||
@@ -159,12 +151,10 @@ use crate::ring::Ring;
|
||||
use crate::set_window_position::SetWindowPosition;
|
||||
use crate::windows_callbacks;
|
||||
use crate::Window;
|
||||
use crate::WindowHandlingBehaviour;
|
||||
use crate::WindowManager;
|
||||
use crate::DISPLAY_INDEX_PREFERENCES;
|
||||
use crate::DUPLICATE_MONITOR_SERIAL_IDS;
|
||||
use crate::MONITOR_INDEX_PREFERENCES;
|
||||
use crate::WINDOW_HANDLING_BEHAVIOUR;
|
||||
|
||||
macro_rules! as_ptr {
|
||||
($value:expr) => {
|
||||
@@ -477,12 +467,7 @@ impl WindowsApi {
|
||||
/// position window resizes the target window to the given layout, adjusting
|
||||
/// the layout to account for any window shadow borders (the window painted
|
||||
/// region will match layout on completion).
|
||||
pub fn position_window(
|
||||
hwnd: isize,
|
||||
layout: &Rect,
|
||||
top: bool,
|
||||
with_async_window_pos: bool,
|
||||
) -> Result<()> {
|
||||
pub fn position_window(hwnd: isize, layout: &Rect, top: bool) -> Result<()> {
|
||||
let hwnd = HWND(as_ptr!(hwnd));
|
||||
|
||||
let mut flags = SetWindowPosition::NO_ACTIVATE
|
||||
@@ -493,19 +478,6 @@ impl WindowsApi {
|
||||
// If the request is to place the window on top, then HWND_TOP will take
|
||||
// effect, otherwise pass NO_Z_ORDER that will cause set_window_pos to
|
||||
// ignore the z-order paramter.
|
||||
|
||||
// By default SetWindowPos waits for target window's WindowProc thread
|
||||
// to process the message, so we have to use ASYNC_WINDOW_POS to avoid
|
||||
// blocking our thread in case the target window is not responding.
|
||||
if with_async_window_pos
|
||||
&& matches!(
|
||||
WINDOW_HANDLING_BEHAVIOUR.load(),
|
||||
WindowHandlingBehaviour::Async
|
||||
)
|
||||
{
|
||||
flags |= SetWindowPosition::ASYNC_WINDOW_POS;
|
||||
}
|
||||
|
||||
if !top {
|
||||
flags |= SetWindowPosition::NO_Z_ORDER;
|
||||
}
|
||||
@@ -540,18 +512,11 @@ impl WindowsApi {
|
||||
/// Raise the window to the top of the Z order, but do not activate or focus
|
||||
/// it. Use raise_and_focus_window to activate and focus a window.
|
||||
pub fn raise_window(hwnd: isize) -> Result<()> {
|
||||
let mut flags = SetWindowPosition::NO_MOVE
|
||||
let flags = SetWindowPosition::NO_MOVE
|
||||
| SetWindowPosition::NO_SIZE
|
||||
| SetWindowPosition::NO_ACTIVATE
|
||||
| SetWindowPosition::SHOW_WINDOW;
|
||||
|
||||
if matches!(
|
||||
WINDOW_HANDLING_BEHAVIOUR.load(),
|
||||
WindowHandlingBehaviour::Async
|
||||
) {
|
||||
flags |= SetWindowPosition::ASYNC_WINDOW_POS;
|
||||
}
|
||||
|
||||
let position = HWND_TOP;
|
||||
Self::set_window_pos(
|
||||
HWND(as_ptr!(hwnd)),
|
||||
@@ -564,18 +529,11 @@ impl WindowsApi {
|
||||
/// Lower the window to the bottom of the Z order, but do not activate or focus
|
||||
/// it.
|
||||
pub fn lower_window(hwnd: isize) -> Result<()> {
|
||||
let mut flags = SetWindowPosition::NO_MOVE
|
||||
let flags = SetWindowPosition::NO_MOVE
|
||||
| SetWindowPosition::NO_SIZE
|
||||
| SetWindowPosition::NO_ACTIVATE
|
||||
| SetWindowPosition::SHOW_WINDOW;
|
||||
|
||||
if matches!(
|
||||
WINDOW_HANDLING_BEHAVIOUR.load(),
|
||||
WindowHandlingBehaviour::Async
|
||||
) {
|
||||
flags |= SetWindowPosition::ASYNC_WINDOW_POS;
|
||||
}
|
||||
|
||||
let position = HWND_BOTTOM;
|
||||
Self::set_window_pos(
|
||||
HWND(as_ptr!(hwnd)),
|
||||
@@ -586,17 +544,12 @@ impl WindowsApi {
|
||||
}
|
||||
|
||||
pub fn set_border_pos(hwnd: isize, layout: &Rect, position: isize) -> Result<()> {
|
||||
let mut flags = SetWindowPosition::NO_SEND_CHANGING
|
||||
| SetWindowPosition::NO_ACTIVATE
|
||||
| SetWindowPosition::NO_REDRAW
|
||||
| SetWindowPosition::SHOW_WINDOW;
|
||||
|
||||
if matches!(
|
||||
WINDOW_HANDLING_BEHAVIOUR.load(),
|
||||
WindowHandlingBehaviour::Async
|
||||
) {
|
||||
flags |= SetWindowPosition::ASYNC_WINDOW_POS;
|
||||
}
|
||||
let flags = {
|
||||
SetWindowPosition::NO_SEND_CHANGING
|
||||
| SetWindowPosition::NO_ACTIVATE
|
||||
| SetWindowPosition::NO_REDRAW
|
||||
| SetWindowPosition::SHOW_WINDOW
|
||||
};
|
||||
|
||||
Self::set_window_pos(
|
||||
HWND(as_ptr!(hwnd)),
|
||||
@@ -622,7 +575,6 @@ impl WindowsApi {
|
||||
.process()
|
||||
}
|
||||
|
||||
/// move_windows calls MoveWindow, but cannot be called with async window pos, so it might hang
|
||||
pub fn move_window(hwnd: isize, layout: &Rect, repaint: bool) -> Result<()> {
|
||||
let hwnd = HWND(as_ptr!(hwnd));
|
||||
|
||||
@@ -640,18 +592,9 @@ impl WindowsApi {
|
||||
// BOOL is returned but does not signify whether or not the operation was succesful
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow
|
||||
// TODO: error handling
|
||||
if matches!(
|
||||
WINDOW_HANDLING_BEHAVIOUR.load(),
|
||||
WindowHandlingBehaviour::Async
|
||||
) {
|
||||
unsafe {
|
||||
let _ = ShowWindowAsync(HWND(as_ptr!(hwnd)), command);
|
||||
};
|
||||
} else {
|
||||
unsafe {
|
||||
let _ = ShowWindow(HWND(as_ptr!(hwnd)), command);
|
||||
};
|
||||
}
|
||||
unsafe {
|
||||
let _ = ShowWindow(HWND(as_ptr!(hwnd)), command);
|
||||
};
|
||||
}
|
||||
|
||||
pub fn minimize_window(hwnd: isize) {
|
||||
@@ -707,7 +650,7 @@ impl WindowsApi {
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | SWP_ASYNCWINDOWPOS,
|
||||
SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW,
|
||||
)
|
||||
.process();
|
||||
SetForegroundWindow(HWND(as_ptr!(hwnd)))
|
||||
@@ -990,7 +933,7 @@ impl WindowsApi {
|
||||
pub fn exe(handle: HANDLE) -> Result<String> {
|
||||
Ok(Self::exe_path(handle)?
|
||||
.split('\\')
|
||||
.next_back()
|
||||
.last()
|
||||
.ok_or_else(|| anyhow!("there is no last element"))?
|
||||
.to_string())
|
||||
}
|
||||
@@ -1059,16 +1002,6 @@ impl WindowsApi {
|
||||
Ok(ex_info)
|
||||
}
|
||||
|
||||
pub fn monitor_device_path(hmonitor: isize) -> Option<String> {
|
||||
for display in win32_display_data::connected_displays_all().flatten() {
|
||||
if display.hmonitor == hmonitor {
|
||||
return Some(display.device_path.clone());
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn monitor(hmonitor: isize) -> Result<Monitor> {
|
||||
for mut display in win32_display_data::connected_displays_all().flatten() {
|
||||
if display.hmonitor == hmonitor {
|
||||
@@ -1412,47 +1345,4 @@ impl WindowsApi {
|
||||
pub fn wts_register_session_notification(hwnd: isize) -> Result<()> {
|
||||
unsafe { WTSRegisterSessionNotification(HWND(as_ptr!(hwnd)), 1) }.process()
|
||||
}
|
||||
|
||||
pub fn set_wallpaper(path: &Path, hmonitor: isize) -> Result<()> {
|
||||
let path = path.canonicalize()?;
|
||||
|
||||
let wallpaper: IDesktopWallpaper =
|
||||
unsafe { CoCreateInstance(&DesktopWallpaper, None, CLSCTX_ALL)? };
|
||||
|
||||
let wallpaper_path = HSTRING::from(path.to_str().unwrap_or_default());
|
||||
unsafe {
|
||||
wallpaper.SetPosition(DWPOS_FILL)?;
|
||||
}
|
||||
|
||||
let monitor_id = if let Some(path) = Self::monitor_device_path(hmonitor) {
|
||||
PCWSTR::from_raw(HSTRING::from(path).as_ptr())
|
||||
} else {
|
||||
PCWSTR::null()
|
||||
};
|
||||
|
||||
// Set the wallpaper
|
||||
unsafe {
|
||||
wallpaper.SetWallpaper(monitor_id, PCWSTR::from_raw(wallpaper_path.as_ptr()))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_wallpaper(hmonitor: isize) -> Result<String> {
|
||||
let wallpaper: IDesktopWallpaper =
|
||||
unsafe { CoCreateInstance(&DesktopWallpaper, None, CLSCTX_ALL)? };
|
||||
|
||||
let monitor_id = if let Some(path) = Self::monitor_device_path(hmonitor) {
|
||||
PCWSTR::from_raw(HSTRING::from(path).as_ptr())
|
||||
} else {
|
||||
PCWSTR::null()
|
||||
};
|
||||
|
||||
// Set the wallpaper
|
||||
unsafe {
|
||||
wallpaper
|
||||
.GetWallpaper(monitor_id)
|
||||
.and_then(|pwstr| pwstr.to_string().map_err(|e| e.into()))
|
||||
}
|
||||
.process()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
use std::collections::BTreeSet;
|
||||
use std::collections::VecDeque;
|
||||
use std::ffi::OsStr;
|
||||
use std::fmt::Display;
|
||||
use std::fmt::Formatter;
|
||||
use std::io::Write;
|
||||
use std::num::NonZeroUsize;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use color_eyre::eyre::anyhow;
|
||||
use color_eyre::Result;
|
||||
use getset::CopyGetters;
|
||||
use getset::Getters;
|
||||
use getset::MutGetters;
|
||||
use getset::Setters;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::border_manager;
|
||||
use crate::container::Container;
|
||||
use crate::core::Axis;
|
||||
use crate::core::CustomLayout;
|
||||
use crate::core::CycleDirection;
|
||||
@@ -16,6 +22,11 @@ use crate::core::DefaultLayout;
|
||||
use crate::core::Layout;
|
||||
use crate::core::OperationDirection;
|
||||
use crate::core::Rect;
|
||||
use crate::FloatingLayerBehaviour;
|
||||
|
||||
use crate::border_manager::BORDER_OFFSET;
|
||||
use crate::border_manager::BORDER_WIDTH;
|
||||
use crate::container::Container;
|
||||
use crate::locked_deque::LockedDeque;
|
||||
use crate::ring::Ring;
|
||||
use crate::should_act;
|
||||
@@ -25,28 +36,13 @@ use crate::static_config::WorkspaceConfig;
|
||||
use crate::window::Window;
|
||||
use crate::window::WindowDetails;
|
||||
use crate::windows_api::WindowsApi;
|
||||
use crate::FloatingLayerBehaviour;
|
||||
use crate::KomorebiTheme;
|
||||
use crate::SocketMessage;
|
||||
use crate::Wallpaper;
|
||||
use crate::WindowContainerBehaviour;
|
||||
use crate::DATA_DIR;
|
||||
use crate::DEFAULT_CONTAINER_PADDING;
|
||||
use crate::DEFAULT_WORKSPACE_PADDING;
|
||||
use crate::INITIAL_CONFIGURATION_LOADED;
|
||||
use crate::NO_TITLEBAR;
|
||||
use crate::REGEX_IDENTIFIERS;
|
||||
use crate::REMOVE_TITLEBARS;
|
||||
use color_eyre::eyre::anyhow;
|
||||
use color_eyre::Result;
|
||||
use getset::CopyGetters;
|
||||
use getset::Getters;
|
||||
use getset::MutGetters;
|
||||
use getset::Setters;
|
||||
use komorebi_themes::Base16ColourPalette;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use uds_windows::UnixStream;
|
||||
|
||||
#[allow(clippy::struct_field_names)]
|
||||
#[derive(
|
||||
@@ -92,17 +88,14 @@ pub struct Workspace {
|
||||
pub window_container_behaviour_rules: Option<Vec<(usize, WindowContainerBehaviour)>>,
|
||||
#[getset(get = "pub", get_mut = "pub", set = "pub")]
|
||||
pub float_override: Option<bool>,
|
||||
#[serde(skip)]
|
||||
#[getset(get = "pub", get_mut = "pub", set = "pub")]
|
||||
pub globals: WorkspaceGlobals,
|
||||
#[getset(get = "pub", get_mut = "pub", set = "pub")]
|
||||
pub layer: WorkspaceLayer,
|
||||
#[getset(get_copy = "pub", get_mut = "pub", set = "pub")]
|
||||
pub floating_layer_behaviour: Option<FloatingLayerBehaviour>,
|
||||
#[getset(get = "pub", get_mut = "pub", set = "pub")]
|
||||
pub floating_layer_behaviour: FloatingLayerBehaviour,
|
||||
#[getset(get = "pub", get_mut = "pub", set = "pub")]
|
||||
pub locked_containers: BTreeSet<usize>,
|
||||
#[getset(get = "pub", get_mut = "pub", set = "pub")]
|
||||
pub wallpaper: Option<Wallpaper>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[getset(get = "pub", set = "pub")]
|
||||
pub workspace_config: Option<WorkspaceConfig>,
|
||||
@@ -155,7 +148,6 @@ impl Default for Workspace {
|
||||
globals: Default::default(),
|
||||
workspace_config: None,
|
||||
locked_containers: Default::default(),
|
||||
wallpaper: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -186,13 +178,10 @@ pub enum WorkspaceWindowLocation {
|
||||
pub struct WorkspaceGlobals {
|
||||
pub container_padding: Option<i32>,
|
||||
pub workspace_padding: Option<i32>,
|
||||
pub border_width: i32,
|
||||
pub border_offset: i32,
|
||||
pub work_area: Rect,
|
||||
pub work_area_offset: Option<Rect>,
|
||||
pub window_based_work_area_offset: Option<Rect>,
|
||||
pub window_based_work_area_offset_limit: isize,
|
||||
pub floating_layer_behaviour: Option<FloatingLayerBehaviour>,
|
||||
}
|
||||
|
||||
impl Workspace {
|
||||
@@ -265,8 +254,7 @@ impl Workspace {
|
||||
|
||||
self.set_float_override(config.float_override);
|
||||
self.set_layout_flip(config.layout_flip);
|
||||
self.set_floating_layer_behaviour(config.floating_layer_behaviour);
|
||||
self.set_wallpaper(config.wallpaper.clone());
|
||||
self.set_floating_layer_behaviour(config.floating_layer_behaviour.unwrap_or_default());
|
||||
|
||||
self.set_workspace_config(Some(config.clone()));
|
||||
|
||||
@@ -303,133 +291,12 @@ impl Workspace {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_wallpaper(&self, hmonitor: isize, monitor_wp: &Option<Wallpaper>) -> Result<()> {
|
||||
if let Some(wallpaper) = self.wallpaper.as_ref().or(monitor_wp.as_ref()) {
|
||||
if let Err(error) = WindowsApi::set_wallpaper(&wallpaper.path, hmonitor) {
|
||||
tracing::error!("failed to set wallpaper: {error}");
|
||||
}
|
||||
|
||||
if wallpaper.generate_theme.unwrap_or(true) {
|
||||
let variant = wallpaper
|
||||
.theme_options
|
||||
.as_ref()
|
||||
.and_then(|t| t.theme_variant)
|
||||
.unwrap_or_default();
|
||||
|
||||
let cached_palette = DATA_DIR.join(format!(
|
||||
"{}.base16.{variant}.json",
|
||||
wallpaper
|
||||
.path
|
||||
.file_name()
|
||||
.unwrap_or(OsStr::new("tmp"))
|
||||
.to_string_lossy()
|
||||
));
|
||||
|
||||
let mut base16_palette = None;
|
||||
|
||||
if cached_palette.is_file() {
|
||||
tracing::info!(
|
||||
"colour palette for wallpaper {} found in cache",
|
||||
cached_palette.display()
|
||||
);
|
||||
|
||||
// this code is VERY slow on debug builds - should only be a one-time issue when loading
|
||||
// an uncached wallpaper
|
||||
if let Ok(palette) = serde_json::from_str::<Base16ColourPalette>(
|
||||
&std::fs::read_to_string(&cached_palette)?,
|
||||
) {
|
||||
base16_palette = Some(palette);
|
||||
}
|
||||
};
|
||||
|
||||
if base16_palette.is_none() {
|
||||
base16_palette =
|
||||
komorebi_themes::generate_base16_palette(&wallpaper.path, variant).ok();
|
||||
|
||||
std::fs::write(
|
||||
&cached_palette,
|
||||
serde_json::to_string_pretty(&base16_palette)?,
|
||||
)?;
|
||||
|
||||
tracing::info!(
|
||||
"colour palette for wallpaper {} cached",
|
||||
cached_palette.display()
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(palette) = base16_palette {
|
||||
let komorebi_theme = KomorebiTheme::Custom {
|
||||
colours: Box::new(palette),
|
||||
single_border: wallpaper
|
||||
.theme_options
|
||||
.as_ref()
|
||||
.and_then(|o| o.single_border),
|
||||
stack_border: wallpaper
|
||||
.theme_options
|
||||
.as_ref()
|
||||
.and_then(|o| o.stack_border),
|
||||
monocle_border: wallpaper
|
||||
.theme_options
|
||||
.as_ref()
|
||||
.and_then(|o| o.monocle_border),
|
||||
floating_border: wallpaper
|
||||
.theme_options
|
||||
.as_ref()
|
||||
.and_then(|o| o.floating_border),
|
||||
unfocused_border: wallpaper
|
||||
.theme_options
|
||||
.as_ref()
|
||||
.and_then(|o| o.unfocused_border),
|
||||
unfocused_locked_border: wallpaper
|
||||
.theme_options
|
||||
.as_ref()
|
||||
.and_then(|o| o.unfocused_locked_border),
|
||||
stackbar_focused_text: wallpaper
|
||||
.theme_options
|
||||
.as_ref()
|
||||
.and_then(|o| o.stackbar_focused_text),
|
||||
stackbar_unfocused_text: wallpaper
|
||||
.theme_options
|
||||
.as_ref()
|
||||
.and_then(|o| o.stackbar_unfocused_text),
|
||||
stackbar_background: wallpaper
|
||||
.theme_options
|
||||
.as_ref()
|
||||
.and_then(|o| o.stackbar_background),
|
||||
bar_accent: wallpaper.theme_options.as_ref().and_then(|o| o.bar_accent),
|
||||
};
|
||||
|
||||
let bytes = SocketMessage::Theme(Box::new(komorebi_theme)).as_bytes()?;
|
||||
|
||||
let socket = DATA_DIR.join("komorebi.sock");
|
||||
match UnixStream::connect(socket) {
|
||||
Ok(mut stream) => {
|
||||
if let Err(error) = stream.write_all(&bytes) {
|
||||
tracing::error!("failed to send theme update message: {error}")
|
||||
}
|
||||
}
|
||||
Err(error) => {
|
||||
tracing::error!("{error}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn restore(
|
||||
&mut self,
|
||||
mouse_follows_focus: bool,
|
||||
hmonitor: isize,
|
||||
monitor_wp: &Option<Wallpaper>,
|
||||
) -> Result<()> {
|
||||
pub fn restore(&mut self, mouse_follows_focus: bool) -> Result<()> {
|
||||
if let Some(container) = self.monocle_container() {
|
||||
if let Some(window) = container.focused_window() {
|
||||
container.restore();
|
||||
window.focus(mouse_follows_focus)?;
|
||||
return self.apply_wallpaper(hmonitor, monitor_wp);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -473,7 +340,7 @@ impl Workspace {
|
||||
floating_window.focus(mouse_follows_focus)?;
|
||||
}
|
||||
|
||||
self.apply_wallpaper(hmonitor, monitor_wp)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update(&mut self) -> Result<()> {
|
||||
@@ -481,9 +348,6 @@ impl Workspace {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// make sure we are never holding on to empty containers
|
||||
self.containers_mut().retain(|c| !c.windows().is_empty());
|
||||
|
||||
let container_padding = self
|
||||
.container_padding()
|
||||
.or(self.globals().container_padding)
|
||||
@@ -492,8 +356,6 @@ impl Workspace {
|
||||
.workspace_padding()
|
||||
.or(self.globals().workspace_padding)
|
||||
.unwrap_or_default();
|
||||
let border_width = self.globals().border_width;
|
||||
let border_offset = self.globals().border_offset;
|
||||
let work_area = self.globals().work_area;
|
||||
let work_area_offset = self.globals().work_area_offset;
|
||||
let window_based_work_area_offset = self.globals().window_based_work_area_offset;
|
||||
@@ -566,8 +428,12 @@ impl Workspace {
|
||||
if let Some(container) = self.monocle_container_mut() {
|
||||
if let Some(window) = container.focused_window_mut() {
|
||||
adjusted_work_area.add_padding(container_padding);
|
||||
adjusted_work_area.add_padding(border_offset);
|
||||
adjusted_work_area.add_padding(border_width);
|
||||
{
|
||||
let border_offset = BORDER_OFFSET.load(Ordering::SeqCst);
|
||||
adjusted_work_area.add_padding(border_offset);
|
||||
let width = BORDER_WIDTH.load(Ordering::SeqCst);
|
||||
adjusted_work_area.add_padding(width);
|
||||
}
|
||||
window.set_position(&adjusted_work_area, true)?;
|
||||
};
|
||||
} else if let Some(window) = self.maximized_window_mut() {
|
||||
@@ -595,8 +461,13 @@ impl Workspace {
|
||||
let window_count = container.windows().len();
|
||||
|
||||
if let Some(layout) = layouts.get_mut(i) {
|
||||
layout.add_padding(border_offset);
|
||||
layout.add_padding(border_width);
|
||||
{
|
||||
let border_offset = BORDER_OFFSET.load(Ordering::SeqCst);
|
||||
layout.add_padding(border_offset);
|
||||
|
||||
let width = BORDER_WIDTH.load(Ordering::SeqCst);
|
||||
layout.add_padding(width);
|
||||
}
|
||||
|
||||
if stackbar_manager::should_have_stackbar(window_count) {
|
||||
let tab_height = STACKBAR_TAB_HEIGHT.load(Ordering::SeqCst);
|
||||
@@ -660,6 +531,84 @@ impl Workspace {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn reap_orphans(&mut self) -> Result<(usize, usize)> {
|
||||
let mut hwnds = vec![];
|
||||
let mut floating_hwnds = vec![];
|
||||
let mut remove_monocle = false;
|
||||
let mut remove_maximized = false;
|
||||
|
||||
if let Some(monocle) = &self.monocle_container {
|
||||
let window_count = monocle.windows().len();
|
||||
let mut orphan_count = 0;
|
||||
for window in monocle.windows() {
|
||||
if !window.is_window() {
|
||||
hwnds.push(window.hwnd);
|
||||
orphan_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
remove_monocle = orphan_count == window_count;
|
||||
}
|
||||
|
||||
if let Some(window) = &self.maximized_window {
|
||||
if !window.is_window() {
|
||||
hwnds.push(window.hwnd);
|
||||
remove_maximized = true;
|
||||
}
|
||||
}
|
||||
|
||||
for window in self.visible_windows().into_iter().flatten() {
|
||||
if !window.is_window()
|
||||
// This one is a hack because WINWORD.EXE is an absolute trainwreck of an app
|
||||
// when multiple docs are open, it keeps open an invisible window, with WS_EX_LAYERED
|
||||
// (A STYLE THAT THE REGULAR WINDOWS NEED IN ORDER TO BE MANAGED!) when one of the
|
||||
// docs is closed
|
||||
//
|
||||
// I hate every single person who worked on Microsoft Office 365, especially Word
|
||||
|| !window.is_visible()
|
||||
{
|
||||
hwnds.push(window.hwnd);
|
||||
}
|
||||
}
|
||||
|
||||
for window in self.floating_windows() {
|
||||
if !window.is_window() {
|
||||
floating_hwnds.push(window.hwnd);
|
||||
}
|
||||
}
|
||||
|
||||
for hwnd in &hwnds {
|
||||
tracing::debug!("reaping hwnd: {}", hwnd);
|
||||
self.remove_window(*hwnd)?;
|
||||
}
|
||||
|
||||
for hwnd in &floating_hwnds {
|
||||
tracing::debug!("reaping floating hwnd: {}", hwnd);
|
||||
self.floating_windows_mut()
|
||||
.retain(|w| !floating_hwnds.contains(&w.hwnd));
|
||||
}
|
||||
|
||||
let mut container_ids = vec![];
|
||||
for container in self.containers() {
|
||||
if container.windows().is_empty() {
|
||||
container_ids.push(container.id().clone());
|
||||
}
|
||||
}
|
||||
|
||||
self.containers_mut()
|
||||
.retain(|c| !container_ids.contains(c.id()));
|
||||
|
||||
if remove_monocle {
|
||||
self.set_monocle_container(None);
|
||||
}
|
||||
|
||||
if remove_maximized {
|
||||
self.set_maximized_window(None);
|
||||
}
|
||||
|
||||
Ok((hwnds.len() + floating_hwnds.len(), container_ids.len()))
|
||||
}
|
||||
|
||||
pub fn container_for_window(&self, hwnd: isize) -> Option<&Container> {
|
||||
self.containers().get(self.container_idx_for_window(hwnd)?)
|
||||
}
|
||||
@@ -1698,6 +1647,7 @@ impl Workspace {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use crate::container::Container;
|
||||
use crate::Window;
|
||||
use std::collections::BTreeSet;
|
||||
@@ -2410,53 +2360,4 @@ mod tests {
|
||||
let floating_windows = workspace.floating_windows_mut();
|
||||
assert!(floating_windows.contains(&Window { hwnd: 0 }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_visible_windows() {
|
||||
let mut workspace = Workspace::default();
|
||||
|
||||
{
|
||||
// Create and add a default Container with 2 windows
|
||||
let mut container = Container::default();
|
||||
container.windows_mut().push_back(Window::from(100));
|
||||
container.windows_mut().push_back(Window::from(200));
|
||||
workspace.add_container_to_back(container);
|
||||
}
|
||||
|
||||
{
|
||||
// visible_windows should return None and 100
|
||||
let visible_windows = workspace.visible_windows();
|
||||
assert_eq!(visible_windows.len(), 2);
|
||||
assert!(visible_windows[0].is_none());
|
||||
assert_eq!(visible_windows[1].unwrap().hwnd, 100);
|
||||
}
|
||||
|
||||
{
|
||||
// Create and add a default Container with 1 window
|
||||
let mut container = Container::default();
|
||||
container.windows_mut().push_back(Window::from(300));
|
||||
workspace.add_container_to_back(container);
|
||||
}
|
||||
|
||||
{
|
||||
// visible_windows should return None, 100, and 300
|
||||
let visible_windows = workspace.visible_windows();
|
||||
assert_eq!(visible_windows.len(), 3);
|
||||
assert!(visible_windows[0].is_none());
|
||||
assert_eq!(visible_windows[1].unwrap().hwnd, 100);
|
||||
assert_eq!(visible_windows[2].unwrap().hwnd, 300);
|
||||
}
|
||||
|
||||
// Maximize window 200
|
||||
workspace.set_maximized_window(Some(Window { hwnd: 200 }));
|
||||
|
||||
{
|
||||
// visible_windows should return 200, 100, and 300
|
||||
let visible_windows = workspace.visible_windows();
|
||||
assert_eq!(visible_windows.len(), 3);
|
||||
assert_eq!(visible_windows[0].unwrap().hwnd, 200);
|
||||
assert_eq!(visible_windows[1].unwrap().hwnd, 100);
|
||||
assert_eq!(visible_windows[2].unwrap().hwnd, 300);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "komorebic-no-console"
|
||||
version = "0.1.37"
|
||||
version = "0.1.36"
|
||||
description = "The command-line interface (without a console) for Komorebi, a tiling window manager for Windows"
|
||||
repository = "https://github.com/LGUG2Z/komorebi"
|
||||
edition = "2021"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "komorebic"
|
||||
version = "0.1.37"
|
||||
version = "0.1.36"
|
||||
description = "The command-line interface for Komorebi, a tiling window manager for Windows"
|
||||
repository = "https://github.com/LGUG2Z/komorebi"
|
||||
edition = "2021"
|
||||
@@ -36,7 +36,7 @@ shadow-rs = { workspace = true }
|
||||
|
||||
[features]
|
||||
default = ["schemars"]
|
||||
schemars = ["dep:schemars", "komorebi-client/schemars"]
|
||||
schemars = ["dep:schemars"]
|
||||
|
||||
[lints.rust]
|
||||
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(FALSE)'] }
|
||||
@@ -2,13 +2,12 @@
|
||||
#![allow(clippy::missing_errors_doc, clippy::doc_markdown)]
|
||||
|
||||
use chrono::Utc;
|
||||
use komorebi_client::replace_env_in_path;
|
||||
use komorebi_client::PathExt;
|
||||
use std::fs::File;
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::BufRead;
|
||||
use std::io::BufReader;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
@@ -23,6 +22,7 @@ use color_eyre::eyre::bail;
|
||||
use color_eyre::Result;
|
||||
use dirs::data_local_dir;
|
||||
use fs_tail::TailedFile;
|
||||
use komorebi_client::resolve_home_path;
|
||||
use komorebi_client::send_message;
|
||||
use komorebi_client::send_query;
|
||||
use komorebi_client::AppSpecificConfigurationPath;
|
||||
@@ -64,7 +64,8 @@ lazy_static! {
|
||||
std::env::var("KOMOREBI_CONFIG_HOME").map_or_else(
|
||||
|_| dirs::home_dir().expect("there is no home directory"),
|
||||
|home_path| {
|
||||
let home = home_path.replace_env();
|
||||
let home = PathBuf::from(&home_path);
|
||||
|
||||
if home.as_path().is_dir() {
|
||||
HAS_CUSTOM_CONFIG_HOME.store(true, Ordering::SeqCst);
|
||||
home
|
||||
@@ -87,12 +88,12 @@ lazy_static! {
|
||||
.join(".config")
|
||||
},
|
||||
|home_path| {
|
||||
let whkd_config_home = home_path.replace_env();
|
||||
let whkd_config_home = PathBuf::from(&home_path);
|
||||
|
||||
assert!(
|
||||
whkd_config_home.is_dir(),
|
||||
whkd_config_home.as_path().is_dir(),
|
||||
"$Env:WHKD_CONFIG_HOME is set to '{}', which is not a valid directory",
|
||||
home_path
|
||||
whkd_config_home.to_string_lossy()
|
||||
);
|
||||
|
||||
whkd_config_home
|
||||
@@ -298,7 +299,6 @@ pub struct WorkspaceCustomLayout {
|
||||
workspace: usize,
|
||||
|
||||
/// JSON or YAML file from which the custom layout definition should be loaded
|
||||
#[clap(value_parser = replace_env_in_path)]
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
@@ -308,7 +308,6 @@ pub struct NamedWorkspaceCustomLayout {
|
||||
workspace: String,
|
||||
|
||||
/// JSON or YAML file from which the custom layout definition should be loaded
|
||||
#[clap(value_parser = replace_env_in_path)]
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
@@ -351,7 +350,6 @@ pub struct WorkspaceCustomLayoutRule {
|
||||
at_container_count: usize,
|
||||
|
||||
/// JSON or YAML file from which the custom layout definition should be loaded
|
||||
#[clap(value_parser = replace_env_in_path)]
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
@@ -364,7 +362,6 @@ pub struct NamedWorkspaceCustomLayoutRule {
|
||||
at_container_count: usize,
|
||||
|
||||
/// JSON or YAML file from which the custom layout definition should be loaded
|
||||
#[clap(value_parser = replace_env_in_path)]
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
@@ -773,7 +770,6 @@ struct Start {
|
||||
ffm: bool,
|
||||
/// Path to a static configuration JSON file
|
||||
#[clap(short, long)]
|
||||
#[clap(value_parser = replace_env_in_path)]
|
||||
config: Option<PathBuf>,
|
||||
/// Wait for 'komorebic complete-configuration' to be sent before processing events
|
||||
#[clap(short, long)]
|
||||
@@ -836,21 +832,18 @@ struct Kill {
|
||||
#[derive(Parser)]
|
||||
struct SaveResize {
|
||||
/// File to which the resize layout dimensions should be saved
|
||||
#[clap(value_parser = replace_env_in_path)]
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
struct LoadResize {
|
||||
/// File from which the resize layout dimensions should be loaded
|
||||
#[clap(value_parser = replace_env_in_path)]
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
struct LoadCustomLayout {
|
||||
/// JSON or YAML file from which the custom layout definition should be loaded
|
||||
#[clap(value_parser = replace_env_in_path)]
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
@@ -881,34 +874,28 @@ struct UnsubscribePipe {
|
||||
#[derive(Parser)]
|
||||
struct AhkAppSpecificConfiguration {
|
||||
/// YAML file from which the application-specific configurations should be loaded
|
||||
#[clap(value_parser = replace_env_in_path)]
|
||||
path: PathBuf,
|
||||
/// Optional YAML file of overrides to apply over the first file
|
||||
#[clap(value_parser = replace_env_in_path)]
|
||||
override_path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
struct PwshAppSpecificConfiguration {
|
||||
/// YAML file from which the application-specific configurations should be loaded
|
||||
#[clap(value_parser = replace_env_in_path)]
|
||||
path: PathBuf,
|
||||
/// Optional YAML file of overrides to apply over the first file
|
||||
#[clap(value_parser = replace_env_in_path)]
|
||||
override_path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
struct FormatAppSpecificConfiguration {
|
||||
/// YAML file from which the application-specific configurations should be loaded
|
||||
#[clap(value_parser = replace_env_in_path)]
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
struct ConvertAppSpecificConfiguration {
|
||||
/// YAML file from which the application-specific configurations should be loaded
|
||||
#[clap(value_parser = replace_env_in_path)]
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
@@ -922,7 +909,6 @@ struct AltFocusHack {
|
||||
struct EnableAutostart {
|
||||
/// Path to a static configuration JSON file
|
||||
#[clap(action, short, long)]
|
||||
#[clap(value_parser = replace_env_in_path)]
|
||||
config: Option<PathBuf>,
|
||||
/// Enable komorebi's custom focus-follows-mouse implementation
|
||||
#[clap(hide = true)]
|
||||
@@ -946,14 +932,12 @@ struct EnableAutostart {
|
||||
struct Check {
|
||||
/// Path to a static configuration JSON file
|
||||
#[clap(action, short, long)]
|
||||
#[clap(value_parser = replace_env_in_path)]
|
||||
komorebi_config: Option<PathBuf>,
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
struct ReplaceConfiguration {
|
||||
/// Static configuration JSON file from which the configuration should be loaded
|
||||
#[clap(value_parser = replace_env_in_path)]
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
@@ -994,17 +978,12 @@ enum SubCommand {
|
||||
/// Show the path to whkdrc
|
||||
#[clap(alias = "whkd")]
|
||||
Whkdrc,
|
||||
/// Show the path to komorebi's data directory in %LOCALAPPDATA%
|
||||
#[clap(alias = "datadir")]
|
||||
DataDirectory,
|
||||
/// Show a JSON representation of the current window manager state
|
||||
State,
|
||||
/// Show a JSON representation of the current global state
|
||||
GlobalState,
|
||||
/// Launch the komorebi-gui debugging tool
|
||||
Gui,
|
||||
/// Toggle the komorebi-shortcuts helper
|
||||
ToggleShortcuts,
|
||||
/// Show a JSON representation of visible windows
|
||||
VisibleWindows,
|
||||
/// Show information about connected monitors
|
||||
@@ -1341,12 +1320,6 @@ enum SubCommand {
|
||||
/// Set the operation behaviour when the focused window is not managed
|
||||
#[clap(arg_required_else_help = true)]
|
||||
UnmanagedWindowOperationBehaviour(UnmanagedWindowOperationBehaviour),
|
||||
/// Add a rule to float the foreground window for the rest of this session
|
||||
SessionFloatRule,
|
||||
/// Show all session float rules
|
||||
SessionFloatRules,
|
||||
/// Clear all session float rules
|
||||
ClearSessionFloatRules,
|
||||
/// Add a rule to ignore the specified application
|
||||
#[clap(arg_required_else_help = true)]
|
||||
#[clap(alias = "float-rule")]
|
||||
@@ -1695,7 +1668,7 @@ fn main() -> Result<()> {
|
||||
println!("Application specific configuration file path has not been set. Try running 'komorebic fetch-asc'\n");
|
||||
}
|
||||
Some(AppSpecificConfigurationPath::Single(path)) => {
|
||||
if !path.exists() {
|
||||
if !Path::exists(Path::new(&path)) {
|
||||
println!("Application specific configuration file path '{}' does not exist. Try running 'komorebic fetch-asc'\n", path.display());
|
||||
}
|
||||
}
|
||||
@@ -1708,7 +1681,8 @@ fn main() -> Result<()> {
|
||||
// errors
|
||||
let _ = serde_json::from_str::<StaticConfig>(&config_source)?;
|
||||
|
||||
let raw = std::fs::read_to_string(static_config)?;
|
||||
let path = resolve_home_path(static_config)?;
|
||||
let raw = std::fs::read_to_string(path)?;
|
||||
StaticConfig::aliases(&raw);
|
||||
StaticConfig::deprecated(&raw);
|
||||
StaticConfig::end_of_life(&raw);
|
||||
@@ -1780,12 +1754,6 @@ fn main() -> Result<()> {
|
||||
println!("{}", whkdrc.display());
|
||||
}
|
||||
}
|
||||
SubCommand::DataDirectory => {
|
||||
let dir = &*DATA_DIR;
|
||||
if dir.exists() {
|
||||
println!("{}", dir.display());
|
||||
}
|
||||
}
|
||||
SubCommand::Log => {
|
||||
let timestamp = Utc::now().format("%Y-%m-%d").to_string();
|
||||
let color_log = std::env::temp_dir().join(format!("komorebi.log.{timestamp}"));
|
||||
@@ -2011,13 +1979,13 @@ fn main() -> Result<()> {
|
||||
send_message(&SocketMessage::WorkspaceLayoutCustom(
|
||||
arg.monitor,
|
||||
arg.workspace,
|
||||
arg.path,
|
||||
resolve_home_path(arg.path)?,
|
||||
))?;
|
||||
}
|
||||
SubCommand::NamedWorkspaceCustomLayout(arg) => {
|
||||
send_message(&SocketMessage::NamedWorkspaceLayoutCustom(
|
||||
arg.workspace,
|
||||
arg.path,
|
||||
resolve_home_path(arg.path)?,
|
||||
))?;
|
||||
}
|
||||
SubCommand::WorkspaceLayoutRule(arg) => {
|
||||
@@ -2040,14 +2008,14 @@ fn main() -> Result<()> {
|
||||
arg.monitor,
|
||||
arg.workspace,
|
||||
arg.at_container_count,
|
||||
arg.path,
|
||||
resolve_home_path(arg.path)?,
|
||||
))?;
|
||||
}
|
||||
SubCommand::NamedWorkspaceCustomLayoutRule(arg) => {
|
||||
send_message(&SocketMessage::NamedWorkspaceLayoutCustomRule(
|
||||
arg.workspace,
|
||||
arg.at_container_count,
|
||||
arg.path,
|
||||
resolve_home_path(arg.path)?,
|
||||
))?;
|
||||
}
|
||||
SubCommand::ClearWorkspaceLayoutRules(arg) => {
|
||||
@@ -2120,12 +2088,13 @@ fn main() -> Result<()> {
|
||||
|
||||
let mut flags = vec![];
|
||||
if let Some(config) = &arg.config {
|
||||
if !config.is_file() {
|
||||
bail!("could not find file: {}", config.display());
|
||||
let path = resolve_home_path(config)?;
|
||||
if !path.is_file() {
|
||||
bail!("could not find file: {}", path.display());
|
||||
}
|
||||
|
||||
let config = dunce::simplified(config);
|
||||
flags.push(format!("'--config=\"{}\"'", config.display()));
|
||||
// we don't need to replace UNC prefix here as `resolve_home_path` already did
|
||||
flags.push(format!("'--config=\"{}\"'", path.display()));
|
||||
}
|
||||
|
||||
if arg.ffm {
|
||||
@@ -2144,12 +2113,17 @@ fn main() -> Result<()> {
|
||||
flags.push("'--clean-state'".to_string());
|
||||
}
|
||||
|
||||
let exec = exec.unwrap_or("komorebi.exe");
|
||||
let script = if flags.is_empty() {
|
||||
format!("Start-Process '{exec}' -WindowStyle hidden",)
|
||||
format!(
|
||||
"Start-Process '{}' -WindowStyle hidden",
|
||||
exec.unwrap_or("komorebi.exe")
|
||||
)
|
||||
} else {
|
||||
let argument_list = flags.join(",");
|
||||
format!("Start-Process '{exec}' -ArgumentList {argument_list} -WindowStyle hidden",)
|
||||
format!(
|
||||
"Start-Process '{}' -ArgumentList {argument_list} -WindowStyle hidden",
|
||||
exec.unwrap_or("komorebi.exe")
|
||||
)
|
||||
};
|
||||
|
||||
let mut system = sysinfo::System::new_all();
|
||||
@@ -2192,8 +2166,9 @@ fn main() -> Result<()> {
|
||||
if !running {
|
||||
println!("\nRunning komorebi.exe directly for detailed error output\n");
|
||||
if let Some(config) = arg.config {
|
||||
let path = resolve_home_path(config)?;
|
||||
if let Ok(output) = Command::new("komorebi.exe")
|
||||
.arg(format!("'--config=\"{}\"'", config.display()))
|
||||
.arg(format!("'--config=\"{}\"'", path.display()))
|
||||
.output()
|
||||
{
|
||||
println!("{}", String::from_utf8(output.stderr)?);
|
||||
@@ -2243,20 +2218,25 @@ if (!(Get-Process whkd -ErrorAction SilentlyContinue))
|
||||
}
|
||||
}
|
||||
|
||||
let static_config = arg.config.clone().or_else(|| {
|
||||
let komorebi_json = HOME_DIR.join("komorebi.json");
|
||||
komorebi_json.is_file().then_some(komorebi_json)
|
||||
});
|
||||
let static_config = arg.config.clone().map_or_else(
|
||||
|| {
|
||||
let komorebi_json = HOME_DIR.join("komorebi.json");
|
||||
if komorebi_json.is_file() {
|
||||
Option::from(komorebi_json)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
Option::from,
|
||||
);
|
||||
|
||||
if arg.bar {
|
||||
if let Some(config) = &static_config {
|
||||
let mut config = StaticConfig::read(config)?;
|
||||
if let Some(display_bar_configurations) = &mut config.bar_configurations {
|
||||
for config_file_path in &mut *display_bar_configurations {
|
||||
let script = format!(
|
||||
r#"Start-Process "komorebi-bar" '"--config" "{}"' -WindowStyle hidden"#,
|
||||
config_file_path.to_string_lossy()
|
||||
);
|
||||
let script = r#"Start-Process "komorebi-bar" '"--config" "CONFIGFILE"' -WindowStyle hidden"#
|
||||
.replace("CONFIGFILE", &config_file_path.to_string_lossy());
|
||||
|
||||
match powershell_script::run(&script) {
|
||||
Ok(_) => {
|
||||
@@ -2318,13 +2298,21 @@ if (!(Get-Process masir -ErrorAction SilentlyContinue))
|
||||
println!("\n# Documentation");
|
||||
println!("* Read the docs https://lgug2z.github.io/komorebi - Quickly search through all komorebic commands");
|
||||
|
||||
let bar_config = arg.config.or_else(|| {
|
||||
let bar_json = HOME_DIR.join("komorebi.bar.json");
|
||||
bar_json.is_file().then_some(bar_json)
|
||||
});
|
||||
let bar_config = arg.config.map_or_else(
|
||||
|| {
|
||||
let bar_json = HOME_DIR.join("komorebi.bar.json");
|
||||
if bar_json.is_file() {
|
||||
Option::from(bar_json)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
Option::from,
|
||||
);
|
||||
|
||||
if let Some(config) = &static_config {
|
||||
let raw = std::fs::read_to_string(config)?;
|
||||
let path = resolve_home_path(config)?;
|
||||
let raw = std::fs::read_to_string(path)?;
|
||||
StaticConfig::aliases(&raw);
|
||||
StaticConfig::deprecated(&raw);
|
||||
StaticConfig::end_of_life(&raw);
|
||||
@@ -2538,15 +2526,6 @@ if (Get-Command Get-CimInstance -ErrorAction SilentlyContinue) {
|
||||
}
|
||||
}
|
||||
}
|
||||
SubCommand::SessionFloatRule => {
|
||||
send_message(&SocketMessage::SessionFloatRule)?;
|
||||
}
|
||||
SubCommand::SessionFloatRules => {
|
||||
print_query(&SocketMessage::SessionFloatRules);
|
||||
}
|
||||
SubCommand::ClearSessionFloatRules => {
|
||||
send_message(&SocketMessage::ClearSessionFloatRules)?;
|
||||
}
|
||||
SubCommand::IgnoreRule(arg) => {
|
||||
send_message(&SocketMessage::IgnoreRule(arg.identifier, arg.id))?;
|
||||
}
|
||||
@@ -2626,7 +2605,9 @@ if (Get-Command Get-CimInstance -ErrorAction SilentlyContinue) {
|
||||
send_message(&SocketMessage::CycleLayout(arg.cycle_direction))?;
|
||||
}
|
||||
SubCommand::LoadCustomLayout(arg) => {
|
||||
send_message(&SocketMessage::ChangeLayoutCustom(arg.path))?;
|
||||
send_message(&SocketMessage::ChangeLayoutCustom(resolve_home_path(
|
||||
arg.path,
|
||||
)?))?;
|
||||
}
|
||||
SubCommand::FlipLayout(arg) => {
|
||||
send_message(&SocketMessage::FlipLayout(arg.axis))?;
|
||||
@@ -2715,15 +2696,6 @@ if (Get-Command Get-CimInstance -ErrorAction SilentlyContinue) {
|
||||
SubCommand::Gui => {
|
||||
Command::new("komorebi-gui").spawn()?;
|
||||
}
|
||||
SubCommand::ToggleShortcuts => {
|
||||
let output = Command::new("taskkill")
|
||||
.args(["/F", "/IM", "komorebi-shortcuts.exe"])
|
||||
.output()?;
|
||||
|
||||
if !output.status.success() {
|
||||
Command::new("komorebi-shortcuts.exe").spawn()?;
|
||||
}
|
||||
}
|
||||
SubCommand::VisibleWindows => {
|
||||
print_query(&SocketMessage::VisibleWindows);
|
||||
}
|
||||
@@ -2812,10 +2784,10 @@ if (Get-Command Get-CimInstance -ErrorAction SilentlyContinue) {
|
||||
send_message(&SocketMessage::QuickLoad)?;
|
||||
}
|
||||
SubCommand::SaveResize(arg) => {
|
||||
send_message(&SocketMessage::Save(arg.path))?;
|
||||
send_message(&SocketMessage::Save(resolve_home_path(arg.path)?))?;
|
||||
}
|
||||
SubCommand::LoadResize(arg) => {
|
||||
send_message(&SocketMessage::Load(arg.path))?;
|
||||
send_message(&SocketMessage::Load(resolve_home_path(arg.path)?))?;
|
||||
}
|
||||
SubCommand::SubscribeSocket(arg) => {
|
||||
send_message(&SocketMessage::AddSubscriberSocket(arg.socket))?;
|
||||
@@ -2927,9 +2899,9 @@ if (Get-Command Get-CimInstance -ErrorAction SilentlyContinue) {
|
||||
))?;
|
||||
}
|
||||
SubCommand::AhkAppSpecificConfiguration(arg) => {
|
||||
let content = std::fs::read_to_string(arg.path)?;
|
||||
let content = std::fs::read_to_string(resolve_home_path(arg.path)?)?;
|
||||
let lines = if let Some(override_path) = arg.override_path {
|
||||
let override_content = std::fs::read_to_string(override_path)?;
|
||||
let override_content = std::fs::read_to_string(resolve_home_path(override_path)?)?;
|
||||
|
||||
ApplicationConfigurationGenerator::generate_ahk(
|
||||
&content,
|
||||
@@ -2954,9 +2926,9 @@ if (Get-Command Get-CimInstance -ErrorAction SilentlyContinue) {
|
||||
);
|
||||
}
|
||||
SubCommand::PwshAppSpecificConfiguration(arg) => {
|
||||
let content = std::fs::read_to_string(arg.path)?;
|
||||
let content = std::fs::read_to_string(resolve_home_path(arg.path)?)?;
|
||||
let lines = if let Some(override_path) = arg.override_path {
|
||||
let override_content = std::fs::read_to_string(override_path)?;
|
||||
let override_content = std::fs::read_to_string(resolve_home_path(override_path)?)?;
|
||||
|
||||
ApplicationConfigurationGenerator::generate_pwsh(
|
||||
&content,
|
||||
@@ -2981,21 +2953,23 @@ if (Get-Command Get-CimInstance -ErrorAction SilentlyContinue) {
|
||||
);
|
||||
}
|
||||
SubCommand::ConvertAppSpecificConfiguration(arg) => {
|
||||
let content = std::fs::read_to_string(arg.path)?;
|
||||
let file_path = resolve_home_path(arg.path)?;
|
||||
let content = std::fs::read_to_string(&file_path)?;
|
||||
let mut asc = ApplicationConfigurationGenerator::load(&content)?;
|
||||
asc.sort_by(|a, b| a.name.cmp(&b.name));
|
||||
let v2 = ApplicationSpecificConfiguration::from(asc);
|
||||
println!("{}", serde_json::to_string_pretty(&v2)?);
|
||||
}
|
||||
SubCommand::FormatAppSpecificConfiguration(arg) => {
|
||||
let content = std::fs::read_to_string(&arg.path)?;
|
||||
let file_path = resolve_home_path(arg.path)?;
|
||||
let content = std::fs::read_to_string(&file_path)?;
|
||||
let formatted_content = ApplicationConfigurationGenerator::format(&content)?;
|
||||
|
||||
let mut file = OpenOptions::new()
|
||||
.write(true)
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.open(arg.path)?;
|
||||
.open(file_path)?;
|
||||
|
||||
file.write_all(formatted_content.as_bytes())?;
|
||||
|
||||
|
||||
69
mkdocs.yml
69
mkdocs.yml
@@ -49,40 +49,37 @@ plugins:
|
||||
|
||||
nav:
|
||||
- Komorebi:
|
||||
- About:
|
||||
- index.md
|
||||
- design.md
|
||||
- Getting started:
|
||||
- Installation: installation.md
|
||||
- Example configurations: example-configurations.md
|
||||
- Troubleshooting: troubleshooting.md
|
||||
- Usage:
|
||||
- usage/focusing-windows.md
|
||||
- usage/moving-windows.md
|
||||
- usage/stacking-windows.md
|
||||
- usage/focusing-workspaces.md
|
||||
- usage/moving-windows-across-workspaces.md
|
||||
- Komorebi Configuration:
|
||||
- Schema: https://komorebi.lgug2z.com/schema
|
||||
- Komorebi Bar Configuration:
|
||||
- Schema: https://komorebi-bar.lgug2z.com/schema
|
||||
- Common workflows:
|
||||
- common-workflows/komorebi-config-home.md
|
||||
- common-workflows/autostart.md
|
||||
- common-workflows/animations.md
|
||||
- common-workflows/autohotkey.md
|
||||
- common-workflows/borders.md
|
||||
- common-workflows/stackbar.md
|
||||
- common-workflows/remove-gaps.md
|
||||
- common-workflows/ignore-windows.md
|
||||
- common-workflows/force-manage-windows.md
|
||||
- common-workflows/floating-applications.md
|
||||
- common-workflows/tray-and-multi-window-applications.md
|
||||
- common-workflows/mouse-follows-focus.md
|
||||
- common-workflows/dynamic-layout-switching.md
|
||||
- common-workflows/set-display-index.md
|
||||
- common-workflows/multiple-bar-instances.md
|
||||
- common-workflows/multi-monitor-setup.md
|
||||
- index.md
|
||||
- Design: design.md
|
||||
- Getting started:
|
||||
- Installation: installation.md
|
||||
- Example configurations: example-configurations.md
|
||||
- Troubleshooting: troubleshooting.md
|
||||
- Usage:
|
||||
- usage/focusing-windows.md
|
||||
- usage/moving-windows.md
|
||||
- usage/stacking-windows.md
|
||||
- usage/focusing-workspaces.md
|
||||
- usage/moving-windows-across-workspaces.md
|
||||
- Common workflows:
|
||||
- common-workflows/komorebi-config-home.md
|
||||
- common-workflows/autostart.md
|
||||
- common-workflows/animations.md
|
||||
- common-workflows/autohotkey.md
|
||||
- common-workflows/borders.md
|
||||
- common-workflows/stackbar.md
|
||||
- common-workflows/remove-gaps.md
|
||||
- common-workflows/ignore-windows.md
|
||||
- common-workflows/force-manage-windows.md
|
||||
- common-workflows/floating-applications.md
|
||||
- common-workflows/tray-and-multi-window-applications.md
|
||||
- common-workflows/mouse-follows-focus.md
|
||||
- common-workflows/dynamic-layout-switching.md
|
||||
- common-workflows/set-display-index.md
|
||||
- common-workflows/multiple-bar-instances.md
|
||||
- common-workflows/multi-monitor-setup.md
|
||||
- Configuration reference: https://komorebi.lgug2z.com/schema
|
||||
- Bar reference: https://komorebi-bar.lgug2z.com/schema
|
||||
- CLI reference:
|
||||
- cli/quickstart.md
|
||||
- cli/start.md
|
||||
@@ -92,7 +89,6 @@ nav:
|
||||
- cli/configuration.md
|
||||
- cli/bar-configuration.md
|
||||
- cli/whkdrc.md
|
||||
- cli/data-directory.md
|
||||
- cli/state.md
|
||||
- cli/global-state.md
|
||||
- cli/gui.md
|
||||
@@ -209,9 +205,6 @@ nav:
|
||||
- cli/cross-monitor-move-behaviour.md
|
||||
- cli/toggle-cross-monitor-move-behaviour.md
|
||||
- cli/unmanaged-window-operation-behaviour.md
|
||||
- cli/session-float-rule.md
|
||||
- cli/session-float-rules.md
|
||||
- cli/clear-session-float-rules.md
|
||||
- cli/ignore-rule.md
|
||||
- cli/manage-rule.md
|
||||
- cli/initial-workspace-rule.md
|
||||
|
||||
1624
schema.bar.json
1624
schema.bar.json
File diff suppressed because it is too large
Load Diff
1765
schema.json
1765
schema.json
File diff suppressed because it is too large
Load Diff
@@ -101,9 +101,6 @@
|
||||
<Component Id='binary4' Guid='*'>
|
||||
<File Id='exe4' Name='komorebi-bar.exe' DiskId='1' Source='$(var.CargoTargetBinDir)\komorebi-bar.exe' KeyPath='yes' />
|
||||
</Component>
|
||||
<Component Id='binary5' Guid='*'>
|
||||
<File Id='exe5' Name='komorebi-shortcuts.exe' DiskId='1' Source='$(var.CargoTargetBinDir)\komorebi-shortcuts.exe' KeyPath='yes' />
|
||||
</Component>
|
||||
</Directory>
|
||||
</Directory>
|
||||
</Directory>
|
||||
@@ -126,8 +123,6 @@
|
||||
|
||||
<ComponentRef Id='binary4' />
|
||||
|
||||
<ComponentRef Id='binary5' />
|
||||
|
||||
<Feature Id='Environment' Title='PATH Environment Variable' Description='Add the install location of the [ProductName] executable to the PATH system environment variable. This allows the [ProductName] executable to be called from any location.' Level='1' Absent='allow'>
|
||||
<ComponentRef Id='Path' />
|
||||
</Feature>
|
||||
|
||||
Reference in New Issue
Block a user