From 34b4870a252a95c605af935a9d8f036d2e5867af Mon Sep 17 00:00:00 2001 From: Cleboost Date: Mon, 30 Jun 2025 21:09:01 +0200 Subject: [PATCH] Add Windows system tray support and close-to-tray option Introduces a Windows-specific system tray integration with icon, context menu, and playback controls. Adds a 'close to tray' option to preferences and config, and updates the delegate to handle tray events and clean app shutdown. Also adds new selectors for tray and playback toggle commands. --- Cargo.lock | 174 +++++++++------- psst-gui/Cargo.toml | 3 + psst-gui/src/cmd.rs | 3 + psst-gui/src/controller/playback.rs | 4 + psst-gui/src/data/config.rs | 4 + psst-gui/src/delegate.rs | 152 ++++++++++++-- psst-gui/src/delegate/windows_tray.rs | 272 ++++++++++++++++++++++++++ psst-gui/src/ui/preferences.rs | 8 + 8 files changed, 538 insertions(+), 82 deletions(-) create mode 100644 psst-gui/src/delegate/windows_tray.rs diff --git a/Cargo.lock b/Cargo.lock index 866b7f3b..1dc352e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,7 +23,7 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "cipher", "cpufeatures", ] @@ -57,7 +57,7 @@ checksum = "ed7572b7ba83a31e20d1b48970ee402d2e3e0537dcfe0a3ff4d6eb7508617d43" dependencies = [ "alsa-sys", "bitflags 2.9.0", - "cfg-if", + "cfg-if 1.0.0", "libc", ] @@ -227,7 +227,7 @@ checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" dependencies = [ "async-lock 2.8.0", "autocfg", - "cfg-if", + "cfg-if 1.0.0", "concurrent-queue", "futures-lite 1.13.0", "log", @@ -246,7 +246,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" dependencies = [ "async-lock 3.4.0", - "cfg-if", + "cfg-if 1.0.0", "concurrent-queue", "futures-io", "futures-lite 2.6.0", @@ -288,7 +288,7 @@ dependencies = [ "async-lock 2.8.0", "async-signal", "blocking", - "cfg-if", + "cfg-if 1.0.0", "event-listener 3.1.0", "futures-lite 1.13.0", "rustix 0.38.44", @@ -315,7 +315,7 @@ dependencies = [ "async-io 2.4.0", "async-lock 3.4.0", "atomic-waker", - "cfg-if", + "cfg-if 1.0.0", "futures-core", "futures-io", "rustix 0.38.44", @@ -377,7 +377,7 @@ version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "716b7ed619475af650836aa562422e724b807201acd10046298d6efa22b558a3" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "dbus", "libc", "log", @@ -400,7 +400,7 @@ dependencies = [ "anyhow", "arrayvec", "log", - "nom", + "nom 7.1.3", "num-rational", "v_frame", ] @@ -421,7 +421,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" dependencies = [ "addr2line", - "cfg-if", + "cfg-if 1.0.0", "libc", "miniz_oxide", "object", @@ -455,20 +455,22 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bindgen" -version = "0.70.1" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" +checksum = "99de13bb6361e01e493b3db7928085dcc474b7ba4f5481818e53a89d76b8393f" dependencies = [ - "bitflags 2.9.0", + "bitflags 1.3.2", "cexpr", + "cfg-if 0.1.10", "clang-sys", - "itertools 0.13.0", + "lazy_static", + "lazycell", + "peeking_take_while", "proc-macro2", "quote", "regex", "rustc-hash", - "shlex", - "syn 2.0.101", + "shlex 0.1.1", ] [[package]] @@ -612,7 +614,7 @@ checksum = "8691782945451c1c383942c4874dbe63814f61cb57ef773cda2972682b7bb3c0" dependencies = [ "jobserver", "libc", - "shlex", + "shlex 1.3.0", ] [[package]] @@ -623,11 +625,11 @@ checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" [[package]] name = "cexpr" -version = "0.6.0" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +checksum = "fce5b5fb86b0c57c20c834c1b412fd09c77c8a59b9473f86272709e78874cd1d" dependencies = [ - "nom", + "nom 4.2.3", ] [[package]] @@ -651,6 +653,12 @@ dependencies = [ "target-lexicon", ] +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + [[package]] name = "cfg-if" version = "1.0.0" @@ -690,9 +698,9 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.8.1" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +checksum = "81de550971c976f176130da4b2978d3b524eaa0fd9ac31f3ceb5ae1231fb4853" dependencies = [ "glob", "libc", @@ -775,7 +783,7 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "wasm-bindgen", ] @@ -793,7 +801,7 @@ checksum = "03a5d7b21829bc7b4bf4754a978a241ae54ea55a40f92bb20216e54096f4b951" dependencies = [ "percent-encoding", "time 0.2.27", - "version_check", + "version_check 0.9.5", ] [[package]] @@ -804,7 +812,7 @@ checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" dependencies = [ "percent-encoding", "time 0.3.41", - "version_check", + "version_check 0.9.5", ] [[package]] @@ -906,9 +914,9 @@ dependencies = [ [[package]] name = "coreaudio-sys" -version = "0.2.16" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ce857aa0b77d77287acc1ac3e37a05a8c95a2af3647d23b15f263bdaeb7562b" +checksum = "17f73df0f29f4c3c374854f076c47dc018f19acaa63538880dba0937ad4fa8d7" dependencies = [ "bindgen", ] @@ -951,7 +959,7 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", ] [[package]] @@ -1102,7 +1110,7 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf36e65a80337bea855cd4ef9b8401ffce06a7baedf2e85ec467b1ac3f6e82b6" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "dirs-sys-next", ] @@ -1214,7 +1222,7 @@ dependencies = [ "bitflags 1.3.2", "block", "cairo-rs", - "cfg-if", + "cfg-if 1.0.0", "cocoa", "core-graphics", "foreign-types", @@ -1262,7 +1270,7 @@ version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", ] [[package]] @@ -1678,7 +1686,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", - "version_check", + "version_check 0.9.5", ] [[package]] @@ -1687,7 +1695,7 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", @@ -1700,7 +1708,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", "r-efi", "wasi 0.14.2+wasi-0.2.4", @@ -2161,7 +2169,7 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "crunchy", ] @@ -2332,7 +2340,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.4.10", + "socket2 0.5.9", "tokio", "tower-service", "tracing", @@ -2539,7 +2547,7 @@ dependencies = [ "serde", "sized-chunks", "typenum", - "version_check", + "version_check 0.9.5", ] [[package]] @@ -2629,7 +2637,7 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "js-sys", "wasm-bindgen", "web-sys", @@ -2716,15 +2724,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.14.0" @@ -2788,7 +2787,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" dependencies = [ "cesu8", - "cfg-if", + "cfg-if 1.0.0", "combine", "jni-sys", "log", @@ -2854,6 +2853,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "lebe" version = "0.5.2" @@ -2887,12 +2892,12 @@ dependencies = [ [[package]] name = "libloading" -version = "0.8.6" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +checksum = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753" dependencies = [ - "cfg-if", - "windows-targets 0.52.6", + "cc", + "winapi", ] [[package]] @@ -3045,7 +3050,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "rayon", ] @@ -3163,11 +3168,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" dependencies = [ "bitflags 1.3.2", - "cfg-if", + "cfg-if 1.0.0", "libc", "memoffset 0.7.1", ] +[[package]] +name = "nom" +version = "4.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" +dependencies = [ + "memchr", + "version_check 0.1.5", +] + [[package]] name = "nom" version = "7.1.3" @@ -3466,7 +3481,7 @@ version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", "redox_syscall", "smallvec", @@ -3485,6 +3500,12 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + [[package]] name = "percent-encoding" version = "2.3.1" @@ -3562,7 +3583,7 @@ checksum = "9dd8497cc0bcfecb1e14e027428c5e3eaf9af6e14761176e1212006d8bdba387" dependencies = [ "cairo-rs", "cairo-sys-rs", - "cfg-if", + "cfg-if 1.0.0", "core-graphics", "piet", "piet-cairo", @@ -3675,7 +3696,7 @@ checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" dependencies = [ "autocfg", "bitflags 1.3.2", - "cfg-if", + "cfg-if 1.0.0", "concurrent-queue", "libc", "log", @@ -3689,7 +3710,7 @@ version = "3.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "concurrent-queue", "hermit-abi 0.4.0", "pin-project-lite", @@ -3769,7 +3790,7 @@ dependencies = [ "proc-macro2", "quote", "syn 1.0.109", - "version_check", + "version_check 0.9.5", ] [[package]] @@ -3780,7 +3801,7 @@ checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ "proc-macro2", "quote", - "version_check", + "version_check 0.9.5", ] [[package]] @@ -3909,6 +3930,7 @@ dependencies = [ "time-humanize", "ureq 3.0.11", "url", + "winapi", "winres", ] @@ -4063,7 +4085,7 @@ dependencies = [ "av1-grain", "bitstream-io", "built", - "cfg-if", + "cfg-if 1.0.0", "interpolate_name", "itertools 0.12.1", "libc", @@ -4262,7 +4284,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", - "cfg-if", + "cfg-if 1.0.0", "getrandom 0.2.16", "libc", "untrusted 0.9.0", @@ -4610,7 +4632,7 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "cpufeatures", "digest", ] @@ -4630,7 +4652,7 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "cpufeatures", "digest", ] @@ -4657,7 +4679,7 @@ version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "cpufeatures", "digest", ] @@ -4680,6 +4702,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shlex" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" + [[package]] name = "shlex" version = "1.3.0" @@ -4809,7 +4837,7 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff" dependencies = [ - "version_check", + "version_check 0.9.5", ] [[package]] @@ -5131,7 +5159,7 @@ version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "once_cell", ] @@ -5166,7 +5194,7 @@ dependencies = [ "standback", "stdweb", "time-macros 0.1.1", - "version_check", + "version_check 0.9.5", "winapi", ] @@ -5673,6 +5701,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" +[[package]] +name = "version_check" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" + [[package]] name = "version_check" version = "0.9.5" @@ -5725,7 +5759,7 @@ version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "once_cell", "rustversion", "wasm-bindgen-macro", @@ -5751,7 +5785,7 @@ version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "js-sys", "once_cell", "wasm-bindgen", @@ -6269,7 +6303,7 @@ version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "windows-sys 0.48.0", ] diff --git a/psst-gui/Cargo.toml b/psst-gui/Cargo.toml index 769d5572..95179324 100644 --- a/psst-gui/Cargo.toml +++ b/psst-gui/Cargo.toml @@ -57,6 +57,9 @@ rustfm-scrobble = "1.1.1" winres = { version = "0.1.12" } image = { version = "0.25.6" } +[target.'cfg(windows)'.dependencies] +winapi = { version = "0.3", features = ["winuser", "shellapi", "commctrl", "libloaderapi", "processthreadsapi"] } + [package.metadata.bundle] name = "Psst" identifier = "com.jpochyla.psst" diff --git a/psst-gui/src/cmd.rs b/psst-gui/src/cmd.rs index 9e897e79..8b4b24b9 100644 --- a/psst-gui/src/cmd.rs +++ b/psst-gui/src/cmd.rs @@ -17,6 +17,8 @@ pub const SHOW_MAIN: Selector = Selector::new("app.show-main"); pub const SHOW_ACCOUNT_SETUP: Selector = Selector::new("app.show-initial"); pub const CLOSE_ALL_WINDOWS: Selector = Selector::new("app.close-all-windows"); pub const QUIT_APP_WITH_SAVE: Selector = Selector::new("app.quit-with-save"); +pub const QUIT_APP_CLEAN: Selector = Selector::new("app.quit-clean"); +pub const TRAY_REMOVED: Selector = Selector::new("app.tray-removed"); pub const SET_FOCUS: Selector = Selector::new("app.set-focus"); pub const COPY: Selector = Selector::new("app.copy-to-clipboard"); pub const GO_TO_URL: Selector = Selector::new("app.go-to-url"); @@ -51,6 +53,7 @@ pub const PLAY_TRACKS: Selector = Selector::new("app.play-track pub const PLAY_PREVIOUS: Selector = Selector::new("app.play-previous"); pub const PLAY_PAUSE: Selector = Selector::new("app.play-pause"); pub const PLAY_RESUME: Selector = Selector::new("app.play-resume"); +pub const PLAY_PAUSE_TOGGLE: Selector = Selector::new("app.play-pause-toggle"); pub const PLAY_NEXT: Selector = Selector::new("app.play-next"); pub const PLAY_STOP: Selector = Selector::new("app.play-stop"); pub const ADD_TO_QUEUE: Selector<(QueueEntry, PlaybackItem)> = Selector::new("app.add-to-queue"); diff --git a/psst-gui/src/controller/playback.rs b/psst-gui/src/controller/playback.rs index 9913b9a1..65e6f55e 100644 --- a/psst-gui/src/controller/playback.rs +++ b/psst-gui/src/controller/playback.rs @@ -506,6 +506,10 @@ where self.resume(); ctx.set_handled(); } + Event::Command(cmd) if cmd.is(cmd::PLAY_PAUSE_TOGGLE) => { + self.pause_or_resume(); + ctx.set_handled(); + } Event::Command(cmd) if cmd.is(cmd::PLAY_PREVIOUS) => { self.previous(); ctx.set_handled(); diff --git a/psst-gui/src/data/config.rs b/psst-gui/src/data/config.rs index fc96b839..d5527f99 100644 --- a/psst-gui/src/data/config.rs +++ b/psst-gui/src/data/config.rs @@ -116,6 +116,8 @@ pub struct Config { pub last_route: Option