diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 4af0e09b2..d40ed931e 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -56,7 +56,6 @@ jobs: cargo doc --features docs,simd,standalone,zstd --no-deps -p nih_plug -p nih_plug_derive - -p nih_plug_egui -p nih_plug_iced -p nih_plug_vizia -p nih_plug_xtask diff --git a/Cargo.lock b/Cargo.lock index 81c28648b..d0990f4f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -84,7 +84,7 @@ dependencies = [ "accesskit_macos", "accesskit_unix", "accesskit_windows", - "winit 0.28.7", + "winit", ] [[package]] @@ -187,27 +187,6 @@ dependencies = [ "num_enum 0.6.1", ] -[[package]] -name = "android-activity" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" -dependencies = [ - "android-properties", - "bitflags 2.9.0", - "cc", - "cesu8", - "jni", - "jni-sys", - "libc", - "log", - "ndk 0.9.0", - "ndk-context", - "ndk-sys 0.6.0+11769913", - "num_enum 0.7.3", - "thiserror 1.0.69", -] - [[package]] name = "android-properties" version = "0.2.2" @@ -941,20 +920,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "981520c98f422fcc584dc1a95c334e6953900b9106bc47a9839b81790009eb21" -[[package]] -name = "calloop" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" -dependencies = [ - "bitflags 2.9.0", - "log", - "polling 3.7.4", - "rustix 0.38.44", - "slab", - "thiserror 1.0.69", -] - [[package]] name = "camino" version = "1.1.9" @@ -1258,19 +1223,6 @@ dependencies = [ "x11-clipboard 0.7.1", ] -[[package]] -name = "copypasta" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deb85422867ca93da58b7f95fb5c0c10f6183ed6e1ef8841568968a896d3a858" -dependencies = [ - "clipboard-win", - "objc", - "objc-foundation", - "objc_id", - "x11-clipboard 0.9.3", -] - [[package]] name = "core-foundation" version = "0.9.4" @@ -1310,19 +1262,6 @@ dependencies = [ "libc", ] -[[package]] -name = "core-graphics" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" -dependencies = [ - "bitflags 1.3.2", - "core-foundation 0.9.4", - "core-graphics-types 0.1.3", - "foreign-types 0.5.0", - "libc", -] - [[package]] name = "core-graphics" version = "0.24.0" @@ -1597,12 +1536,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" -[[package]] -name = "cursor-icon" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" - [[package]] name = "dasp_sample" version = "0.11.0" @@ -1659,7 +1592,7 @@ dependencies = [ "atomic_float", "nih_plug", "nih_plug_vizia", - "open 3.2.0", + "open", "realfft", "semver", "triple_buffer", @@ -1727,12 +1660,6 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" -[[package]] -name = "dpi" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" - [[package]] name = "drm" version = "0.12.0" @@ -1799,79 +1726,12 @@ dependencies = [ "wio", ] -[[package]] -name = "ecolor" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "878e9005799dd739e5d5d89ff7480491c12d0af571d44399bcaefa1ee172dd76" -dependencies = [ - "bytemuck", - "emath", -] - -[[package]] -name = "egui" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d2768eaa6d5c80a6e2a008da1f0e062dff3c83eb2b28605ea2d0732d46e74d6" -dependencies = [ - "ahash", - "bitflags 2.9.0", - "emath", - "epaint", - "nohash-hasher", - "profiling", -] - -[[package]] -name = "egui-baseview" -version = "0.5.0" -source = "git+https://github.com/BillyDM/egui-baseview.git?rev=ec70c3fe6b2f070dcacbc22924431edbe24bd1c0#ec70c3fe6b2f070dcacbc22924431edbe24bd1c0" -dependencies = [ - "baseview 0.1.0 (git+https://github.com/RustAudio/baseview.git?rev=9a0b42c09d712777b2edb4c5e0cb6baf21e988f0)", - "copypasta 0.10.1", - "egui", - "egui_glow", - "keyboard-types", - "log", - "open 5.3.2", - "raw-window-handle 0.5.2", - "thiserror 2.0.11", -] - -[[package]] -name = "egui_glow" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a53e2374a964c3c793cb0b8ead81bca631f24974bc0b747d1a5622f4e39fdd0" -dependencies = [ - "ahash", - "bytemuck", - "egui", - "glow 0.16.0", - "log", - "memoffset 0.9.1", - "profiling", - "wasm-bindgen", - "web-sys", - "winit 0.30.9", -] - [[package]] name = "either" version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7914353092ddf589ad78f25c5c1c21b7f80b0ff8621e7c814c3485b5306da9d" -[[package]] -name = "emath" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55b7b6be5ad1d247f11738b0e4699d9c20005ed366f2c29f5ec1f8e1de180bc2" -dependencies = [ - "bytemuck", -] - [[package]] name = "enumflags2" version = "0.7.11" @@ -1893,30 +1753,6 @@ dependencies = [ "syn 2.0.98", ] -[[package]] -name = "epaint" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "275b665a7b9611d8317485187e5458750850f9e64604d3c58434bb3fc1d22915" -dependencies = [ - "ab_glyph", - "ahash", - "bytemuck", - "ecolor", - "emath", - "epaint_default_fonts", - "nohash-hasher", - "parking_lot 0.12.3", - "profiling", - "rayon", -] - -[[package]] -name = "epaint_default_fonts" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9343d356d7cac894dacafc161b4654e0881301097bdf32a122ed503d97cb94b6" - [[package]] name = "equivalent" version = "1.0.2" @@ -2351,15 +2187,6 @@ dependencies = [ "parking_lot 0.12.3", ] -[[package]] -name = "gain_gui_egui" -version = "0.1.0" -dependencies = [ - "atomic_float", - "nih_plug", - "nih_plug_egui", -] - [[package]] name = "gain_gui_iced" version = "0.1.0" @@ -2574,7 +2401,7 @@ dependencies = [ "cfg_aliases 0.1.1", "glutin", "raw-window-handle 0.5.2", - "winit 0.28.7", + "winit", ] [[package]] @@ -2970,25 +2797,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "is-docker" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3" -dependencies = [ - "once_cell", -] - -[[package]] -name = "is-wsl" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5" -dependencies = [ - "is-docker", - "once_cell", -] - [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -3489,21 +3297,6 @@ dependencies = [ "thiserror 1.0.69", ] -[[package]] -name = "ndk" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" -dependencies = [ - "bitflags 2.9.0", - "jni-sys", - "log", - "ndk-sys 0.6.0+11769913", - "num_enum 0.7.3", - "raw-window-handle 0.6.2", - "thiserror 1.0.69", -] - [[package]] name = "ndk-context" version = "0.1.1" @@ -3528,15 +3321,6 @@ dependencies = [ "jni-sys", ] -[[package]] -name = "ndk-sys" -version = "0.6.0+11769913" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" -dependencies = [ - "jni-sys", -] - [[package]] name = "nih_log" version = "0.3.1" @@ -3604,19 +3388,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "nih_plug_egui" -version = "0.0.0" -dependencies = [ - "baseview 0.1.0 (git+https://github.com/RustAudio/baseview.git?rev=9a0b42c09d712777b2edb4c5e0cb6baf21e988f0)", - "crossbeam", - "egui-baseview", - "nih_plug", - "parking_lot 0.12.3", - "raw-window-handle 0.5.2", - "serde", -] - [[package]] name = "nih_plug_iced" version = "0.0.0" @@ -3692,12 +3463,6 @@ dependencies = [ "memoffset 0.7.1", ] -[[package]] -name = "nohash-hasher" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" - [[package]] name = "nom" version = "7.1.3" @@ -3888,82 +3653,6 @@ dependencies = [ "objc2-encode 4.1.0", ] -[[package]] -name = "objc2-app-kit" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" -dependencies = [ - "bitflags 2.9.0", - "block2 0.5.1", - "libc", - "objc2 0.5.2", - "objc2-core-data", - "objc2-core-image", - "objc2-foundation", - "objc2-quartz-core", -] - -[[package]] -name = "objc2-cloud-kit" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" -dependencies = [ - "bitflags 2.9.0", - "block2 0.5.1", - "objc2 0.5.2", - "objc2-core-location", - "objc2-foundation", -] - -[[package]] -name = "objc2-contacts" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" -dependencies = [ - "block2 0.5.1", - "objc2 0.5.2", - "objc2-foundation", -] - -[[package]] -name = "objc2-core-data" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" -dependencies = [ - "bitflags 2.9.0", - "block2 0.5.1", - "objc2 0.5.2", - "objc2-foundation", -] - -[[package]] -name = "objc2-core-image" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" -dependencies = [ - "block2 0.5.1", - "objc2 0.5.2", - "objc2-foundation", - "objc2-metal", -] - -[[package]] -name = "objc2-core-location" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" -dependencies = [ - "block2 0.5.1", - "objc2 0.5.2", - "objc2-contacts", - "objc2-foundation", -] - [[package]] name = "objc2-encode" version = "2.0.0-pre.2" @@ -3987,23 +3676,10 @@ checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ "bitflags 2.9.0", "block2 0.5.1", - "dispatch", "libc", "objc2 0.5.2", ] -[[package]] -name = "objc2-link-presentation" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" -dependencies = [ - "block2 0.5.1", - "objc2 0.5.2", - "objc2-app-kit", - "objc2-foundation", -] - [[package]] name = "objc2-metal" version = "0.2.2" @@ -4029,61 +3705,6 @@ dependencies = [ "objc2-metal", ] -[[package]] -name = "objc2-symbols" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc" -dependencies = [ - "objc2 0.5.2", - "objc2-foundation", -] - -[[package]] -name = "objc2-ui-kit" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" -dependencies = [ - "bitflags 2.9.0", - "block2 0.5.1", - "objc2 0.5.2", - "objc2-cloud-kit", - "objc2-core-data", - "objc2-core-image", - "objc2-core-location", - "objc2-foundation", - "objc2-link-presentation", - "objc2-quartz-core", - "objc2-symbols", - "objc2-uniform-type-identifiers", - "objc2-user-notifications", -] - -[[package]] -name = "objc2-uniform-type-identifiers" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" -dependencies = [ - "block2 0.5.1", - "objc2 0.5.2", - "objc2-foundation", -] - -[[package]] -name = "objc2-user-notifications" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" -dependencies = [ - "bitflags 2.9.0", - "block2 0.5.1", - "objc2 0.5.2", - "objc2-core-location", - "objc2-foundation", -] - [[package]] name = "objc_id" version = "0.1.1" @@ -4141,17 +3762,6 @@ dependencies = [ "windows-sys 0.42.0", ] -[[package]] -name = "open" -version = "5.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2483562e62ea94312f3576a7aca397306df7990b8d89033e18766744377ef95" -dependencies = [ - "is-wsl", - "libc", - "pathdiff", -] - [[package]] name = "orbclient" version = "0.3.48" @@ -4395,26 +4005,6 @@ dependencies = [ "siphasher", ] -[[package]] -name = "pin-project" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfe2e71e1471fe07709406bf725f710b02927c9c54b2b5b2ec0e8087d97c327d" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6e859e6e5bd50440ab63c47e3ebabc90f26251f7c73c3d3e837b74a1cc3fa67" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.98", -] - [[package]] name = "pin-project-lite" version = "0.2.16" @@ -4806,15 +4396,6 @@ dependencies = [ "bitflags 1.3.2", ] -[[package]] -name = "redox_syscall" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "redox_syscall" version = "0.5.9" @@ -5289,15 +4870,6 @@ dependencies = [ "futures-lite 1.13.0", ] -[[package]] -name = "smol_str" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead" -dependencies = [ - "serde", -] - [[package]] name = "socket2" version = "0.4.10" @@ -5352,7 +4924,7 @@ dependencies = [ "crossbeam", "nih_plug", "nih_plug_vizia", - "open 3.2.0", + "open", "realfft", "serde", "triple_buffer", @@ -6031,7 +5603,7 @@ dependencies = [ "wasm-bindgen", "web-sys", "winapi", - "winit 0.28.7", + "winit", ] [[package]] @@ -6277,16 +5849,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "web-time" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - [[package]] name = "wgpu" version = "25.0.0" @@ -6672,15 +6234,6 @@ dependencies = [ "windows-targets 0.48.5", ] -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets 0.52.6", -] - [[package]] name = "windows-sys" version = "0.59.0" @@ -6874,7 +6427,7 @@ version = "0.28.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9596d90b45384f5281384ab204224876e8e8bf7d58366d9b795ad99aa9894b94" dependencies = [ - "android-activity 0.4.3", + "android-activity", "bitflags 1.3.2", "cfg_aliases 0.1.1", "core-foundation 0.9.4", @@ -6898,50 +6451,6 @@ dependencies = [ "x11-dl", ] -[[package]] -name = "winit" -version = "0.30.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a809eacf18c8eca8b6635091543f02a5a06ddf3dad846398795460e6e0ae3cc0" -dependencies = [ - "android-activity 0.6.0", - "atomic-waker", - "bitflags 2.9.0", - "block2 0.5.1", - "bytemuck", - "calloop", - "cfg_aliases 0.2.1", - "concurrent-queue", - "core-foundation 0.9.4", - "core-graphics 0.23.2", - "cursor-icon", - "dpi", - "js-sys", - "libc", - "ndk 0.9.0", - "objc2 0.5.2", - "objc2-app-kit", - "objc2-foundation", - "objc2-ui-kit", - "orbclient", - "percent-encoding", - "pin-project", - "raw-window-handle 0.6.2", - "redox_syscall 0.4.1", - "rustix 0.38.44", - "smol_str", - "tracing", - "unicode-segmentation", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "web-time", - "windows-sys 0.52.0", - "x11-dl", - "x11rb 0.13.1", - "xkbcommon-dl", -] - [[package]] name = "winnow" version = "0.5.40" @@ -7006,16 +6515,6 @@ dependencies = [ "x11rb 0.10.1", ] -[[package]] -name = "x11-clipboard" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "662d74b3d77e396b8e5beb00b9cad6a9eccf40b2ef68cc858784b14c41d535a3" -dependencies = [ - "libc", - "x11rb 0.13.1", -] - [[package]] name = "x11-dl" version = "2.21.0" @@ -7049,8 +6548,6 @@ dependencies = [ "as-raw-xcb-connection", "gethostname 0.4.3", "libc", - "libloading 0.8.6", - "once_cell", "rustix 0.38.44", "x11rb-protocol 0.13.1", ] @@ -7124,25 +6621,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a67300977d3dc3f8034dae89778f502b6ba20b269527b3223ba59c0cf393bb8a" -[[package]] -name = "xkbcommon-dl" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" -dependencies = [ - "bitflags 2.9.0", - "dlib", - "log", - "once_cell", - "xkeysym", -] - -[[package]] -name = "xkeysym" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" - [[package]] name = "xml-rs" version = "0.8.25" diff --git a/Cargo.toml b/Cargo.toml index 6b70605d9..a3d7e6f64 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,6 @@ repository = "https://github.com/robbert-vdh/nih-plug" resolver = "2" members = [ "nih_plug_derive", - "nih_plug_egui", "nih_plug_iced", "nih_plug_vizia", "nih_plug_xtask", @@ -26,7 +25,6 @@ members = [ "plugins/examples/byo_gui_softbuffer", "plugins/examples/byo_gui_wgpu", "plugins/examples/gain", - "plugins/examples/gain_gui_egui", "plugins/examples/gain_gui_iced", "plugins/examples/gain_gui_vizia", "plugins/examples/midi_inverter", diff --git a/README.md b/README.md index 2969287ad..0434b52ba 100644 --- a/README.md +++ b/README.md @@ -136,9 +136,11 @@ Scroll down for more information on the underlying plugin framework. - Optional support for compressing the human readable JSON state files using [Zstandard](https://en.wikipedia.org/wiki/Zstd). - Comes with adapters for popular Rust GUI frameworks as well as some basic - widgets for them that integrate with NIH-plug's parameter system. Currently - there's support for [egui](nih_plug_egui), [iced](nih_plug_iced) and - [VIZIA](nih_plug_vizia). + widgets for them that integrate with NIH-plug's parameter system. + - Currently supported GUI frameworks include: + - [VIZIA](nih_plug_vizia) + - [iced](nih_plug_iced) + - [egui](https://codeberg.org/BillyDM/egui-baseview) (3rd party) - A simple and safe API for state saving and restoring from the editor is provided by the framework if you want to do your own internal preset management. @@ -198,10 +200,10 @@ examples. off a couple other parts of the API, like support for storing arbitrary serializable state. - **gain-gui** is the same plugin as gain, but with a GUI to control the - parameter and a digital peak meter. Comes in three exciting flavors: - [egui](plugins/examples/gain_gui_egui), - [iced](plugins/examples/gain_gui_iced), and - [VIZIA](plugins/examples/gain_gui_vizia). + parameter and a digital peak meter. Comes in two exciting flavors: + [VIZIA](plugins/examples/gain_gui_vizia) and + [iced](plugins/examples/gain_gui_iced). (Third party examples exist for + [egui](https://codeberg.org/BillyDM/egui-baseview/src/branch/main/nih_plug_egui).) There are also examples for making custom GUIs with [OpenGL](plugins/examples/byo_gui_gl), [wgpu](plugins/examples/byo_gui_wgpu), diff --git a/nih_plug_egui/Cargo.toml b/nih_plug_egui/Cargo.toml deleted file mode 100644 index 124c3b321..000000000 --- a/nih_plug_egui/Cargo.toml +++ /dev/null @@ -1,26 +0,0 @@ -[package] -name = "nih_plug_egui" -version = "0.0.0" -edition = "2021" -authors = ["Robbert van der Helm "] -license = "ISC" - -description = "An adapter to use egui GUIs with NIH-plug" - -[features] -default = ["opengl", "default_fonts"] -# `nih_plug_egui` always uses OpenGL since egui's wgpu backend is still unstable -# depending on the platform -opengl = ["egui-baseview/opengl"] -default_fonts = ["egui-baseview/default_fonts"] -rayon = ["egui-baseview/rayon"] - -[dependencies] -nih_plug = { path = "..", default-features = false } -raw-window-handle = "0.5" -baseview = { git = "https://github.com/RustAudio/baseview.git", rev = "9a0b42c09d712777b2edb4c5e0cb6baf21e988f0" } -crossbeam = "0.8" -egui-baseview = { git = "https://github.com/BillyDM/egui-baseview.git", rev = "ec70c3fe6b2f070dcacbc22924431edbe24bd1c0" } -parking_lot = "0.12" -# To make the state persistable -serde = { version = "1.0", features = ["derive"] } diff --git a/nih_plug_egui/README.md b/nih_plug_egui/README.md deleted file mode 100644 index 1a43ac62d..000000000 --- a/nih_plug_egui/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# NIH-plug: egui support - -This provides an adapter to create [egui](https://github.com/emilk/egui) based -GUIs with NIH-plug through -[egui-baseview](https://github.com/BillyDM/egui-baseview). - -Consider using [`nih_plug_iced`](../nih_plug_iced) or -[`nih_plug_vizia`](../nih_plug_vizia) instead. diff --git a/nih_plug_egui/src/editor.rs b/nih_plug_egui/src/editor.rs deleted file mode 100644 index d53393790..000000000 --- a/nih_plug_egui/src/editor.rs +++ /dev/null @@ -1,194 +0,0 @@ -//! An [`Editor`] implementation for egui. - -use crate::egui::Vec2; -use crate::egui::ViewportCommand; -use crate::EguiState; -use baseview::gl::GlConfig; -use baseview::PhySize; -use baseview::{Size, WindowHandle, WindowOpenOptions, WindowScalePolicy}; -use crossbeam::atomic::AtomicCell; -use egui_baseview::egui::Context; -use egui_baseview::EguiWindow; -use nih_plug::prelude::{Editor, GuiContext, ParamSetter, ParentWindowHandle}; -use parking_lot::RwLock; -use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; -use std::sync::atomic::Ordering; -use std::sync::Arc; - -/// An [`Editor`] implementation that calls an egui draw loop. -pub(crate) struct EguiEditor { - pub(crate) egui_state: Arc, - /// The plugin's state. This is kept in between editor openenings. - pub(crate) user_state: Arc>, - - /// The user's build function. Applied once at the start of the application. - pub(crate) build: Arc, - /// The user's update function. - pub(crate) update: Arc, - - /// The scaling factor reported by the host, if any. On macOS this will never be set and we - /// should use the system scaling factor instead. - pub(crate) scaling_factor: AtomicCell>, -} - -/// This version of `baseview` uses a different version of `raw_window_handle than NIH-plug, so we -/// need to adapt it ourselves. -struct ParentWindowHandleAdapter(nih_plug::editor::ParentWindowHandle); - -unsafe impl HasRawWindowHandle for ParentWindowHandleAdapter { - fn raw_window_handle(&self) -> RawWindowHandle { - match self.0 { - ParentWindowHandle::X11Window(window) => { - let mut handle = raw_window_handle::XcbWindowHandle::empty(); - handle.window = window; - RawWindowHandle::Xcb(handle) - } - ParentWindowHandle::AppKitNsView(ns_view) => { - let mut handle = raw_window_handle::AppKitWindowHandle::empty(); - handle.ns_view = ns_view; - RawWindowHandle::AppKit(handle) - } - ParentWindowHandle::Win32Hwnd(hwnd) => { - let mut handle = raw_window_handle::Win32WindowHandle::empty(); - handle.hwnd = hwnd; - RawWindowHandle::Win32(handle) - } - } - } -} - -impl Editor for EguiEditor -where - T: 'static + Send + Sync, -{ - fn spawn( - &self, - parent: ParentWindowHandle, - context: Arc, - ) -> Box { - let build = self.build.clone(); - let update = self.update.clone(); - let state = self.user_state.clone(); - let egui_state = self.egui_state.clone(); - - let (unscaled_width, unscaled_height) = self.egui_state.size(); - let scaling_factor = self.scaling_factor.load(); - let window = EguiWindow::open_parented( - &ParentWindowHandleAdapter(parent), - WindowOpenOptions { - title: String::from("egui window"), - // Baseview should be doing the DPI scaling for us - size: Size::new(unscaled_width as f64, unscaled_height as f64), - // NOTE: For some reason passing 1.0 here causes the UI to be scaled on macOS but - // not the mouse events. - scale: scaling_factor - .map(|factor| WindowScalePolicy::ScaleFactor(factor as f64)) - .unwrap_or(WindowScalePolicy::SystemScaleFactor), - - #[cfg(feature = "opengl")] - gl_config: Some(GlConfig { - version: (3, 2), - red_bits: 8, - blue_bits: 8, - green_bits: 8, - alpha_bits: 8, - depth_bits: 24, - stencil_bits: 8, - samples: None, - srgb: true, - double_buffer: true, - vsync: true, - ..Default::default() - }), - }, - Default::default(), - state, - move |egui_ctx, _queue, state| build(egui_ctx, &mut state.write()), - move |egui_ctx, queue, state| { - let setter = ParamSetter::new(context.as_ref()); - - // If the window was requested to resize - if let Some(new_size) = egui_state.requested_size.swap(None) { - // Ask the plugin host to resize to self.size() - if context.request_resize() { - // Resize the content of egui window - queue.resize(PhySize::new(new_size.0, new_size.1)); - egui_ctx.send_viewport_cmd(ViewportCommand::InnerSize(Vec2::new( - new_size.0 as f32, - new_size.1 as f32, - ))); - - // Update the state - egui_state.size.store(new_size); - } - } - - // For now, just always redraw. Most plugin GUIs have meters, and those almost always - // need a redraw. Later we can try to be a bit more sophisticated about this. Without - // this we would also have a blank GUI when it gets first opened because most DAWs open - // their GUI while the window is still unmapped. - egui_ctx.request_repaint(); - (update)(egui_ctx, &setter, &mut state.write()); - }, - ); - - self.egui_state.open.store(true, Ordering::Release); - Box::new(EguiEditorHandle { - egui_state: self.egui_state.clone(), - window, - }) - } - - /// Size of the editor window - fn size(&self) -> (u32, u32) { - let new_size = self.egui_state.requested_size.load(); - // This method will be used to ask the host for new size. - // If the editor is currently being resized and new size hasn't been consumed and set yet, return new requested size. - if let Some(new_size) = new_size { - new_size - } else { - self.egui_state.size() - } - } - - fn set_scale_factor(&self, factor: f32) -> bool { - // If the editor is currently open then the host must not change the current HiDPI scale as - // we don't have a way to handle that. Ableton Live does this. - if self.egui_state.is_open() { - return false; - } - - self.scaling_factor.store(Some(factor)); - true - } - - fn param_value_changed(&self, _id: &str, _normalized_value: f32) { - // As mentioned above, for now we'll always force a redraw to allow meter widgets to work - // correctly. In the future we can use an `Arc` and only force a redraw when - // that boolean is set. - } - - fn param_modulation_changed(&self, _id: &str, _modulation_offset: f32) {} - - fn param_values_changed(&self) { - // Same - } -} - -/// The window handle used for [`EguiEditor`]. -struct EguiEditorHandle { - egui_state: Arc, - window: WindowHandle, -} - -/// The window handle enum stored within 'WindowHandle' contains raw pointers. Is there a way around -/// having this requirement? -unsafe impl Send for EguiEditorHandle {} - -impl Drop for EguiEditorHandle { - fn drop(&mut self) { - self.egui_state.open.store(false, Ordering::Release); - // XXX: This should automatically happen when the handle gets dropped, but apparently not - self.window.close(); - } -} diff --git a/nih_plug_egui/src/lib.rs b/nih_plug_egui/src/lib.rs deleted file mode 100644 index f268e670d..000000000 --- a/nih_plug_egui/src/lib.rs +++ /dev/null @@ -1,119 +0,0 @@ -//! [egui](https://github.com/emilk/egui) editor support for NIH plug. -//! -//! TODO: Proper usage example, for now check out the gain_gui example - -// See the comment in the main `nih_plug` crate -#![allow(clippy::type_complexity)] - -use crossbeam::atomic::AtomicCell; -use egui::Context; -use nih_plug::params::persist::PersistentField; -use nih_plug::prelude::{Editor, ParamSetter}; -use parking_lot::RwLock; -use serde::{Deserialize, Serialize}; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::Arc; - -#[cfg(not(feature = "opengl"))] -compile_error!("There's currently no software rendering support for egui"); - -/// Re-export for convenience. -pub use egui_baseview::egui; - -mod editor; -pub mod resizable_window; -pub mod widgets; - -/// Create an [`Editor`] instance using an [`egui`][::egui] GUI. Using the user state parameter is -/// optional, but it can be useful for keeping track of some temporary GUI-only settings. See the -/// `gui_gain` example for more information on how to use this. The [`EguiState`] passed to this -/// function contains the GUI's intitial size, and this is kept in sync whenever the GUI gets -/// resized. You can also use this to know if the GUI is open, so you can avoid performing -/// potentially expensive calculations while the GUI is not open. If you want this size to be -/// persisted when restoring a plugin instance, then you can store it in a `#[persist = "key"]` -/// field on your parameters struct. -/// -/// See [`EguiState::from_size()`]. -pub fn create_egui_editor( - egui_state: Arc, - user_state: T, - build: B, - update: U, -) -> Option> -where - T: 'static + Send + Sync, - B: Fn(&Context, &mut T) + 'static + Send + Sync, - U: Fn(&Context, &ParamSetter, &mut T) + 'static + Send + Sync, -{ - Some(Box::new(editor::EguiEditor { - egui_state, - user_state: Arc::new(RwLock::new(user_state)), - build: Arc::new(build), - update: Arc::new(update), - - // TODO: We can't get the size of the window when baseview does its own scaling, so if the - // host does not set a scale factor on Windows or Linux we should just use a factor of - // 1. That may make the GUI tiny but it also prevents it from getting cut off. - #[cfg(target_os = "macos")] - scaling_factor: AtomicCell::new(None), - #[cfg(not(target_os = "macos"))] - scaling_factor: AtomicCell::new(Some(1.0)), - })) -} - -/// State for an `nih_plug_egui` editor. -#[derive(Debug, Serialize, Deserialize)] -pub struct EguiState { - /// The window's size in logical pixels before applying `scale_factor`. - #[serde(with = "nih_plug::params::persist::serialize_atomic_cell")] - size: AtomicCell<(u32, u32)>, - - /// The new size of the window, if it was requested to resize by the GUI. - #[serde(skip)] - requested_size: AtomicCell>, - - /// Whether the editor's window is currently open. - #[serde(skip)] - open: AtomicBool, -} - -impl<'a> PersistentField<'a, EguiState> for Arc { - fn set(&self, new_value: EguiState) { - self.size.store(new_value.size.load()); - } - - fn map(&self, f: F) -> R - where - F: Fn(&EguiState) -> R, - { - f(self) - } -} - -impl EguiState { - /// Initialize the GUI's state. This value can be passed to [`create_egui_editor()`]. The window - /// size is in logical pixels, so before it is multiplied by the DPI scaling factor. - pub fn from_size(width: u32, height: u32) -> Arc { - Arc::new(EguiState { - size: AtomicCell::new((width, height)), - requested_size: Default::default(), - open: AtomicBool::new(false), - }) - } - - /// Returns a `(width, height)` pair for the current size of the GUI in logical pixels. - pub fn size(&self) -> (u32, u32) { - self.size.load() - } - - /// Whether the GUI is currently visible. - // Called `is_open()` instead of `open()` to avoid the ambiguity. - pub fn is_open(&self) -> bool { - self.open.load(Ordering::Acquire) - } - - /// Set the new size that will be used to resize the window if the host allows. - fn set_requested_size(&self, new_size: (u32, u32)) { - self.requested_size.store(Some(new_size)); - } -} diff --git a/nih_plug_egui/src/resizable_window.rs b/nih_plug_egui/src/resizable_window.rs deleted file mode 100644 index b7e918a73..000000000 --- a/nih_plug_egui/src/resizable_window.rs +++ /dev/null @@ -1,81 +0,0 @@ -//! Resizable window wrapper for Egui editor. - -use egui_baseview::egui::emath::GuiRounding; -use egui_baseview::egui::{InnerResponse, UiBuilder}; - -use crate::egui::{pos2, CentralPanel, Context, Id, Rect, Response, Sense, Ui, Vec2}; -use crate::EguiState; - -/// Adds a corner to the plugin window that can be dragged in order to resize it. -/// Resizing happens through plugin API, hence a custom implementation is needed. -pub struct ResizableWindow { - id: Id, - min_size: Vec2, -} - -impl ResizableWindow { - pub fn new(id_source: impl std::hash::Hash) -> Self { - Self { - id: Id::new(id_source), - min_size: Vec2::splat(16.0), - } - } - - /// Won't shrink to smaller than this - #[inline] - pub fn min_size(mut self, min_size: impl Into) -> Self { - self.min_size = min_size.into(); - self - } - - pub fn show( - self, - context: &Context, - egui_state: &EguiState, - add_contents: impl FnOnce(&mut Ui) -> R, - ) -> InnerResponse { - CentralPanel::default().show(context, move |ui| { - let ui_rect = ui.clip_rect(); - let mut content_ui = - ui.new_child(UiBuilder::new().max_rect(ui_rect).layout(*ui.layout())); - - let ret = add_contents(&mut content_ui); - - let corner_size = Vec2::splat(ui.visuals().resize_corner_size); - let corner_rect = Rect::from_min_size(ui_rect.max - corner_size, corner_size); - - let corner_response = ui.interact(corner_rect, self.id.with("corner"), Sense::drag()); - - if let Some(pointer_pos) = corner_response.interact_pointer_pos() { - let desired_size = (pointer_pos - ui_rect.min + 0.5 * corner_response.rect.size()) - .max(self.min_size); - - if corner_response.dragged() { - egui_state.set_requested_size(( - desired_size.x.round() as u32, - desired_size.y.round() as u32, - )); - } - } - - paint_resize_corner(&content_ui, &corner_response); - - ret - }) - } -} - -pub fn paint_resize_corner(ui: &Ui, response: &Response) { - let stroke = ui.style().interact(response).fg_stroke; - - let painter = ui.painter(); - let rect = response.rect.translate(-Vec2::splat(2.0)); // move away from the corner - let cp = rect.max.round_to_pixels(painter.pixels_per_point()); - - let mut w = 2.0; - - while w <= rect.width() && w <= rect.height() { - painter.line_segment([pos2(cp.x - w, cp.y), pos2(cp.x, cp.y - w)], stroke); - w += 4.0; - } -} diff --git a/nih_plug_egui/src/widgets.rs b/nih_plug_egui/src/widgets.rs deleted file mode 100644 index 70b12e5cc..000000000 --- a/nih_plug_egui/src/widgets.rs +++ /dev/null @@ -1,12 +0,0 @@ -//! Custom egui widgets for displaying parameter values. -//! -//! # Note -//! -//! None of these widgets are finalized, and their sizes or looks can change at any point. Feel free -//! to copy the widgets and modify them to your personal taste. - -pub mod generic_ui; -mod param_slider; -pub mod util; - -pub use param_slider::ParamSlider; diff --git a/nih_plug_egui/src/widgets/generic_ui.rs b/nih_plug_egui/src/widgets/generic_ui.rs deleted file mode 100644 index 1d77b9302..000000000 --- a/nih_plug_egui/src/widgets/generic_ui.rs +++ /dev/null @@ -1,72 +0,0 @@ -//! A simple generic UI widget that renders all parameters in a [`Params`] object as a scrollable -//! list of sliders and labels. - -use std::sync::Arc; - -use egui_baseview::egui::{self, TextStyle, Ui, Vec2}; -use nih_plug::prelude::{Param, ParamFlags, ParamPtr, ParamSetter, Params}; - -use super::ParamSlider; - -/// A widget that can be used to create a generic UI with. This is used in conjuction with empty -/// structs to emulate existential types. -pub trait ParamWidget { - fn add_widget(&self, ui: &mut Ui, param: &P, setter: &ParamSetter); - - /// The same as [`add_widget()`][Self::add_widget()], but for a `ParamPtr`. - /// - /// # Safety - /// - /// Undefined behavior of the `ParamPtr` does not point to a valid parameter. - unsafe fn add_widget_raw(&self, ui: &mut Ui, param: &ParamPtr, setter: &ParamSetter) { - match param { - ParamPtr::FloatParam(p) => self.add_widget(ui, &**p, setter), - ParamPtr::IntParam(p) => self.add_widget(ui, &**p, setter), - ParamPtr::BoolParam(p) => self.add_widget(ui, &**p, setter), - ParamPtr::EnumParam(p) => self.add_widget(ui, &**p, setter), - } - } -} - -/// Create a generic UI using [`ParamSlider`]s. -pub struct GenericSlider; - -/// Create a scrollable generic UI using the specified widget. Takes up all the remaining vertical -/// space. -pub fn create( - ui: &mut Ui, - params: Arc, - setter: &ParamSetter, - widget: impl ParamWidget, -) { - let padding = Vec2::splat(ui.text_style_height(&TextStyle::Body) * 0.2); - egui::containers::ScrollArea::vertical() - // Take up all remaining space, use a wrapper container to adjust how much space that is - .auto_shrink([false, false]) - .show(ui, |ui| { - let mut first_widget = true; - for (_, param_ptr, _) in params.param_map().into_iter() { - let flags = unsafe { param_ptr.flags() }; - if flags.contains(ParamFlags::HIDE_IN_GENERIC_UI) { - continue; - } - - // This list looks weird without a little padding - if !first_widget { - ui.allocate_space(padding); - } - - ui.label(unsafe { param_ptr.name() }); - unsafe { widget.add_widget_raw(ui, ¶m_ptr, setter) }; - - first_widget = false; - } - }); -} - -impl ParamWidget for GenericSlider { - fn add_widget(&self, ui: &mut Ui, param: &P, setter: &ParamSetter) { - // Make these sliders a bit wider, else they look a bit odd - ui.add(ParamSlider::for_param(param, setter).with_width(100.0)); - } -} diff --git a/nih_plug_egui/src/widgets/param_slider.rs b/nih_plug_egui/src/widgets/param_slider.rs deleted file mode 100644 index 38b1214d0..000000000 --- a/nih_plug_egui/src/widgets/param_slider.rs +++ /dev/null @@ -1,357 +0,0 @@ -use std::sync::{Arc, LazyLock}; - -use egui_baseview::egui::emath::GuiRounding; -use egui_baseview::egui::{ - self, emath, vec2, Key, Response, Sense, Stroke, TextEdit, TextStyle, Ui, Vec2, Widget, - WidgetText, -}; -use nih_plug::prelude::{Param, ParamSetter}; -use parking_lot::Mutex; - -use super::util; - -/// When shift+dragging a parameter, one pixel dragged corresponds to this much change in the -/// noramlized parameter. -const GRANULAR_DRAG_MULTIPLIER: f32 = 0.0015; - -static DRAG_NORMALIZED_START_VALUE_MEMORY_ID: LazyLock = - LazyLock::new(|| egui::Id::new((file!(), 0))); -static DRAG_AMOUNT_MEMORY_ID: LazyLock = LazyLock::new(|| egui::Id::new((file!(), 1))); -static VALUE_ENTRY_MEMORY_ID: LazyLock = LazyLock::new(|| egui::Id::new((file!(), 2))); - -/// A slider widget similar to [`egui::widgets::Slider`] that knows about NIH-plug parameters ranges -/// and can get values for it. The slider supports double click and control click to reset, -/// shift+drag for granular dragging, text value entry by clicking on the value text. -/// -/// TODO: Vertical orientation -/// TODO: Check below for more input methods that should be added -/// TODO: Decouple the logic from the drawing so we can also do things like nobs without having to -/// repeat everything -/// TODO: Add WidgetInfo annotations for accessibility -#[must_use = "You should put this widget in an ui with `ui.add(widget);`"] -pub struct ParamSlider<'a, P: Param> { - param: &'a P, - setter: &'a ParamSetter<'a>, - - draw_value: bool, - slider_width: Option, - - /// Will be set in the `ui()` function so we can request keyboard input focus on Alt+click. - keyboard_focus_id: Option, -} - -impl<'a, P: Param> ParamSlider<'a, P> { - /// Create a new slider for a parameter. Use the other methods to modify the slider before - /// passing it to [`Ui::add()`]. - pub fn for_param(param: &'a P, setter: &'a ParamSetter<'a>) -> Self { - Self { - param, - setter, - - draw_value: true, - slider_width: None, - - keyboard_focus_id: None, - } - } - - /// Don't draw the text slider's current value after the slider. - pub fn without_value(mut self) -> Self { - self.draw_value = false; - self - } - - /// Set a custom width for the slider. - pub fn with_width(mut self, width: f32) -> Self { - self.slider_width = Some(width); - self - } - - fn plain_value(&self) -> P::Plain { - self.param.modulated_plain_value() - } - - fn normalized_value(&self) -> f32 { - self.param.modulated_normalized_value() - } - - fn string_value(&self) -> String { - self.param.to_string() - } - - /// Enable the keyboard entry part of the widget. - fn begin_keyboard_entry(&self, ui: &Ui) { - ui.memory_mut(|mem| mem.request_focus(self.keyboard_focus_id.unwrap())); - - // Always initialize the field to the current value, that seems nicer than having to - // being typing from scratch - let value_entry_mutex = ui.memory_mut(|mem| { - mem.data - .get_temp_mut_or_default::>>(*VALUE_ENTRY_MEMORY_ID) - .clone() - }); - *value_entry_mutex.lock() = self.string_value(); - } - - fn keyboard_entry_active(&self, ui: &Ui) -> bool { - ui.memory(|mem| mem.has_focus(self.keyboard_focus_id.unwrap())) - } - - fn begin_drag(&self) { - self.setter.begin_set_parameter(self.param); - } - - fn set_normalized_value(&self, normalized: f32) { - // This snaps to the nearest plain value if the parameter is stepped in some way. - // TODO: As an optimization, we could add a `const CONTINUOUS: bool` to the parameter to - // avoid this normalized->plain->normalized conversion for parameters that don't need - // it - let value = self.param.preview_plain(normalized); - if value != self.plain_value() { - self.setter.set_parameter(self.param, value); - } - } - - /// Begin and end drag still need to be called when using this. Returns `false` if the string - /// could no tbe parsed. - fn set_from_string(&self, string: &str) -> bool { - match self.param.string_to_normalized_value(string) { - Some(normalized_value) => { - self.set_normalized_value(normalized_value); - true - } - None => false, - } - } - - /// Begin and end drag still need to be called when using this.. - fn reset_param(&self) { - self.setter - .set_parameter(self.param, self.param.default_plain_value()); - } - - fn granular_drag(&self, ui: &Ui, drag_delta: Vec2) { - // Remember the intial position when we started with the granular drag. This value gets - // reset whenever we have a normal itneraction with the slider. - let start_value = if Self::get_drag_amount_memory(ui) == 0.0 { - Self::set_drag_normalized_start_value_memory(ui, self.normalized_value()); - self.normalized_value() - } else { - Self::get_drag_normalized_start_value_memory(ui) - }; - - let total_drag_distance = drag_delta.x + Self::get_drag_amount_memory(ui); - Self::set_drag_amount_memory(ui, total_drag_distance); - - self.set_normalized_value( - (start_value + (total_drag_distance * GRANULAR_DRAG_MULTIPLIER)).clamp(0.0, 1.0), - ); - } - - fn end_drag(&self) { - self.setter.end_set_parameter(self.param); - } - - fn get_drag_normalized_start_value_memory(ui: &Ui) -> f32 { - ui.memory(|mem| { - mem.data - .get_temp(*DRAG_NORMALIZED_START_VALUE_MEMORY_ID) - .unwrap_or(0.5) - }) - } - - fn set_drag_normalized_start_value_memory(ui: &Ui, amount: f32) { - ui.memory_mut(|mem| { - mem.data - .insert_temp(*DRAG_NORMALIZED_START_VALUE_MEMORY_ID, amount) - }); - } - - fn get_drag_amount_memory(ui: &Ui) -> f32 { - ui.memory(|mem| mem.data.get_temp(*DRAG_AMOUNT_MEMORY_ID).unwrap_or(0.0)) - } - - fn set_drag_amount_memory(ui: &Ui, amount: f32) { - ui.memory_mut(|mem| mem.data.insert_temp(*DRAG_AMOUNT_MEMORY_ID, amount)); - } - - fn slider_ui(&self, ui: &Ui, response: &mut Response) { - // Handle user input - // TODO: Optionally (since it can be annoying) add scrolling behind a builder option - if response.drag_started() { - // When beginning a drag or dragging normally, reset the memory used to keep track of - // our granular drag - self.begin_drag(); - Self::set_drag_amount_memory(ui, 0.0); - } - if let Some(click_pos) = response.interact_pointer_pos() { - if ui.input(|i| i.modifiers.command) { - // Like double clicking, Ctrl+Click should reset the parameter - self.reset_param(); - response.mark_changed(); - // // FIXME: This releases the focus again when you release the mouse button without - // // moving the mouse a bit for some reason - // } else if ui.input().modifiers.alt && self.draw_value { - // // Allow typing in the value on an Alt+Click. Right now this is shown as part of the - // // value field, so it only makes sense when we're drawing that. - // self.begin_keyboard_entry(ui); - } else if ui.input(|i| i.modifiers.shift) { - // And shift dragging should switch to a more granulra input method - self.granular_drag(ui, response.drag_delta()); - response.mark_changed(); - } else { - let proportion = - emath::remap_clamp(click_pos.x, response.rect.x_range(), 0.0..=1.0) as f64; - self.set_normalized_value(proportion as f32); - response.mark_changed(); - Self::set_drag_amount_memory(ui, 0.0); - } - } - if response.double_clicked() { - self.reset_param(); - response.mark_changed(); - } - if response.drag_stopped() { - self.end_drag(); - } - - // And finally draw the thing - if ui.is_rect_visible(response.rect) { - // We'll do a flat widget with background -> filled foreground -> slight border - ui.painter() - .rect_filled(response.rect, 0.0, ui.visuals().widgets.inactive.bg_fill); - - let filled_proportion = self.normalized_value(); - if filled_proportion > 0.0 { - let mut filled_rect = response.rect; - filled_rect.set_width(response.rect.width() * filled_proportion); - let filled_bg = if response.dragged() { - util::add_hsv(ui.visuals().selection.bg_fill, 0.0, -0.1, 0.1) - } else { - ui.visuals().selection.bg_fill - }; - ui.painter().rect_filled(filled_rect, 0.0, filled_bg); - } - - ui.painter().rect_stroke( - response.rect, - 0.0, - Stroke::new(1.0, ui.visuals().widgets.active.bg_fill), - egui::StrokeKind::Middle, - ); - } - } - - fn value_ui(&self, ui: &mut Ui) { - let visuals = ui.visuals().widgets.inactive; - let should_draw_frame = ui.visuals().button_frame; - let padding = ui.spacing().button_padding; - - // Either show the parameter's label, or show a text entry field if the parameter's label - // has been clicked on - let keyboard_focus_id = self.keyboard_focus_id.unwrap(); - if self.keyboard_entry_active(ui) { - let value_entry_mutex = ui.memory_mut(|mem| { - mem.data - .get_temp_mut_or_default::>>(*VALUE_ENTRY_MEMORY_ID) - .clone() - }); - let mut value_entry = value_entry_mutex.lock(); - - ui.add( - TextEdit::singleline(&mut *value_entry) - .id(keyboard_focus_id) - .font(TextStyle::Monospace), - ); - if ui.input(|i| i.key_pressed(Key::Escape)) { - // Cancel when pressing escape - ui.memory_mut(|mem| mem.surrender_focus(keyboard_focus_id)); - } else if ui.input(|i| i.key_pressed(Key::Enter)) { - // And try to set the value by string when pressing enter - self.begin_drag(); - self.set_from_string(&value_entry); - self.end_drag(); - - ui.memory_mut(|mem| mem.surrender_focus(keyboard_focus_id)); - } - } else { - let text = WidgetText::from(self.string_value()).into_galley( - ui, - None, - ui.available_width() - (padding.x * 2.0), - TextStyle::Button, - ); - - let response = ui.allocate_response(text.size() + (padding * 2.0), Sense::click()); - if response.clicked() { - self.begin_keyboard_entry(ui); - } - - if ui.is_rect_visible(response.rect) { - if should_draw_frame { - let fill = visuals.bg_fill; - let stroke = visuals.bg_stroke; - ui.painter().rect( - response.rect.expand(visuals.expansion), - visuals.corner_radius, - fill, - stroke, - egui::StrokeKind::Middle, - ); - } - - let text_pos = ui - .layout() - .align_size_within_rect(text.size(), response.rect.shrink2(padding)) - .min; - - ui.painter().add(egui::epaint::TextShape::new( - text_pos, - text, - visuals.fg_stroke.color, - )); - } - } - } -} - -impl Widget for ParamSlider<'_, P> { - fn ui(mut self, ui: &mut Ui) -> Response { - let slider_width = self - .slider_width - .unwrap_or_else(|| ui.spacing().slider_width); - - ui.horizontal(|ui| { - // Allocate space, but add some padding on the top and bottom to make it look a bit slimmer. - let height = ui - .text_style_height(&TextStyle::Body) - .max(ui.spacing().interact_size.y * 0.8); - let slider_height = (height * 0.8).round_to_pixels(ui.painter().pixels_per_point()); - let mut response = ui - .vertical(|ui| { - ui.allocate_space(vec2(slider_width, (height - slider_height) / 2.0)); - let response = ui.allocate_response( - vec2(slider_width, slider_height), - Sense::click_and_drag(), - ); - let (kb_edit_id, _) = - ui.allocate_space(vec2(slider_width, (height - slider_height) / 2.0)); - // Allocate an automatic ID for keeping track of keyboard focus state - // FIXME: There doesn't seem to be a way to generate IDs in the public API, not sure how - // you're supposed to do this - self.keyboard_focus_id = Some(kb_edit_id); - - response - }) - .inner; - - self.slider_ui(ui, &mut response); - if self.draw_value { - self.value_ui(ui); - } - - response - }) - .inner - } -} diff --git a/nih_plug_egui/src/widgets/util.rs b/nih_plug_egui/src/widgets/util.rs deleted file mode 100644 index f58cb4320..000000000 --- a/nih_plug_egui/src/widgets/util.rs +++ /dev/null @@ -1,21 +0,0 @@ -//! Utilities for creating these widgets. - -use egui_baseview::egui::{self, Color32}; - -/// Additively modify the hue, saturation, and lightness [0, 1] values of a color. -pub fn add_hsv(color: Color32, h: f32, s: f32, v: f32) -> Color32 { - let mut hsv = egui::epaint::Hsva::from(color); - hsv.h += h; - hsv.s += s; - hsv.v += v; - hsv.into() -} - -/// Multiplicatively modify the hue, saturation, and lightness [0, 1] values of a color. -pub fn scale_hsv(color: Color32, h: f32, s: f32, v: f32) -> Color32 { - let mut hsv = egui::epaint::Hsva::from(color); - hsv.h *= h; - hsv.s *= s; - hsv.v *= v; - hsv.into() -} diff --git a/plugins/examples/gain_gui_egui/Cargo.toml b/plugins/examples/gain_gui_egui/Cargo.toml deleted file mode 100644 index 7b9f9ef79..000000000 --- a/plugins/examples/gain_gui_egui/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "gain_gui_egui" -version = "0.1.0" -edition = "2021" -authors = ["Robbert van der Helm "] -license = "ISC" - -description = "A simple gain plugin with an egui GUI" - -[lib] -# The `lib` artifact is needed for the standalone target -crate-type = ["cdylib", "lib"] - -[dependencies] -nih_plug = { path = "../../../", features = ["assert_process_allocs", "standalone"] } -nih_plug_egui = { path = "../../../nih_plug_egui" } - -atomic_float = "0.1" diff --git a/plugins/examples/gain_gui_egui/src/lib.rs b/plugins/examples/gain_gui_egui/src/lib.rs deleted file mode 100644 index a89d55f64..000000000 --- a/plugins/examples/gain_gui_egui/src/lib.rs +++ /dev/null @@ -1,245 +0,0 @@ -use nih_plug::prelude::*; -use nih_plug_egui::{ - create_egui_editor, - egui::{self, Vec2}, - resizable_window::ResizableWindow, - widgets, EguiState, -}; -use std::sync::Arc; - -/// The time it takes for the peak meter to decay by 12 dB after switching to complete silence. -const PEAK_METER_DECAY_MS: f64 = 150.0; - -/// This is mostly identical to the gain example, minus some fluff, and with a GUI. -pub struct Gain { - params: Arc, - - /// Needed to normalize the peak meter's response based on the sample rate. - peak_meter_decay_weight: f32, - /// The current data for the peak meter. This is stored as an [`Arc`] so we can share it between - /// the GUI and the audio processing parts. If you have more state to share, then it's a good - /// idea to put all of that in a struct behind a single `Arc`. - /// - /// This is stored as voltage gain. - peak_meter: Arc, -} - -#[derive(Params)] -pub struct GainParams { - /// The editor state, saved together with the parameter state so the custom scaling can be - /// restored. - #[persist = "editor-state"] - editor_state: Arc, - - #[id = "gain"] - pub gain: FloatParam, - - // TODO: Remove this parameter when we're done implementing the widgets - #[id = "foobar"] - pub some_int: IntParam, -} - -impl Default for Gain { - fn default() -> Self { - Self { - params: Arc::new(GainParams::default()), - - peak_meter_decay_weight: 1.0, - peak_meter: Arc::new(AtomicF32::new(util::MINUS_INFINITY_DB)), - } - } -} - -impl Default for GainParams { - fn default() -> Self { - Self { - editor_state: EguiState::from_size(300, 180), - - // See the main gain example for more details - gain: FloatParam::new( - "Gain", - util::db_to_gain(0.0), - FloatRange::Skewed { - min: util::db_to_gain(-30.0), - max: util::db_to_gain(30.0), - factor: FloatRange::gain_skew_factor(-30.0, 30.0), - }, - ) - .with_smoother(SmoothingStyle::Logarithmic(50.0)) - .with_unit(" dB") - .with_value_to_string(formatters::v2s_f32_gain_to_db(2)) - .with_string_to_value(formatters::s2v_f32_gain_to_db()), - some_int: IntParam::new("Something", 3, IntRange::Linear { min: 0, max: 3 }), - } - } -} - -impl Plugin for Gain { - const NAME: &'static str = "Gain GUI (egui)"; - const VENDOR: &'static str = "Moist Plugins GmbH"; - const URL: &'static str = "https://youtu.be/dQw4w9WgXcQ"; - const EMAIL: &'static str = "info@example.com"; - - const VERSION: &'static str = env!("CARGO_PKG_VERSION"); - - const AUDIO_IO_LAYOUTS: &'static [AudioIOLayout] = &[ - AudioIOLayout { - main_input_channels: NonZeroU32::new(2), - main_output_channels: NonZeroU32::new(2), - ..AudioIOLayout::const_default() - }, - AudioIOLayout { - main_input_channels: NonZeroU32::new(1), - main_output_channels: NonZeroU32::new(1), - ..AudioIOLayout::const_default() - }, - ]; - - const SAMPLE_ACCURATE_AUTOMATION: bool = true; - - type SysExMessage = (); - type BackgroundTask = (); - - fn params(&self) -> Arc { - self.params.clone() - } - - fn editor(&mut self, _async_executor: AsyncExecutor) -> Option> { - let params = self.params.clone(); - let peak_meter = self.peak_meter.clone(); - let egui_state = params.editor_state.clone(); - create_egui_editor( - self.params.editor_state.clone(), - (), - |_, _| {}, - move |egui_ctx, setter, _state| { - ResizableWindow::new("res-wind") - .min_size(Vec2::new(128.0, 128.0)) - .show(egui_ctx, egui_state.as_ref(), |ui| { - // NOTE: See `plugins/diopser/src/editor.rs` for an example using the generic UI widget - - // This is a fancy widget that can get all the information it needs to properly - // display and modify the parameter from the parametr itself - // It's not yet fully implemented, as the text is missing. - ui.label("Some random integer"); - ui.add(widgets::ParamSlider::for_param(¶ms.some_int, setter)); - - ui.label("Gain"); - ui.add(widgets::ParamSlider::for_param(¶ms.gain, setter)); - - ui.label( - "Also gain, but with a lame widget. Can't even render the value correctly!", - ); - // This is a simple naieve version of a parameter slider that's not aware of how - // the parameters work - ui.add( - egui::widgets::Slider::from_get_set(-30.0..=30.0, |new_value| { - match new_value { - Some(new_value_db) => { - let new_value = util::gain_to_db(new_value_db as f32); - - setter.begin_set_parameter(¶ms.gain); - setter.set_parameter(¶ms.gain, new_value); - setter.end_set_parameter(¶ms.gain); - - new_value_db - } - None => util::gain_to_db(params.gain.value()) as f64, - } - }) - .suffix(" dB"), - ); - - // TODO: Add a proper custom widget instead of reusing a progress bar - let peak_meter = - util::gain_to_db(peak_meter.load(std::sync::atomic::Ordering::Relaxed)); - let peak_meter_text = if peak_meter > util::MINUS_INFINITY_DB { - format!("{peak_meter:.1} dBFS") - } else { - String::from("-inf dBFS") - }; - - let peak_meter_normalized = (peak_meter + 60.0) / 60.0; - ui.allocate_space(egui::Vec2::splat(2.0)); - ui.add( - egui::widgets::ProgressBar::new(peak_meter_normalized) - .text(peak_meter_text), - ); - }); - }, - ) - } - - fn initialize( - &mut self, - _audio_io_layout: &AudioIOLayout, - buffer_config: &BufferConfig, - _context: &mut impl InitContext, - ) -> bool { - // After `PEAK_METER_DECAY_MS` milliseconds of pure silence, the peak meter's value should - // have dropped by 12 dB - self.peak_meter_decay_weight = 0.25f64 - .powf((buffer_config.sample_rate as f64 * PEAK_METER_DECAY_MS / 1000.0).recip()) - as f32; - - true - } - - fn process( - &mut self, - buffer: &mut Buffer, - _aux: &mut AuxiliaryBuffers, - _context: &mut impl ProcessContext, - ) -> ProcessStatus { - for channel_samples in buffer.iter_samples() { - let mut amplitude = 0.0; - let num_samples = channel_samples.len(); - - let gain = self.params.gain.smoothed.next(); - for sample in channel_samples { - *sample *= gain; - amplitude += *sample; - } - - // To save resources, a plugin can (and probably should!) only perform expensive - // calculations that are only displayed on the GUI while the GUI is open - if self.params.editor_state.is_open() { - amplitude = (amplitude / num_samples as f32).abs(); - let current_peak_meter = self.peak_meter.load(std::sync::atomic::Ordering::Relaxed); - let new_peak_meter = if amplitude > current_peak_meter { - amplitude - } else { - current_peak_meter * self.peak_meter_decay_weight - + amplitude * (1.0 - self.peak_meter_decay_weight) - }; - - self.peak_meter - .store(new_peak_meter, std::sync::atomic::Ordering::Relaxed) - } - } - - ProcessStatus::Normal - } -} - -impl ClapPlugin for Gain { - const CLAP_ID: &'static str = "com.moist-plugins-gmbh-egui.gain-gui"; - const CLAP_DESCRIPTION: Option<&'static str> = Some("A smoothed gain parameter example plugin"); - const CLAP_MANUAL_URL: Option<&'static str> = Some(Self::URL); - const CLAP_SUPPORT_URL: Option<&'static str> = None; - const CLAP_FEATURES: &'static [ClapFeature] = &[ - ClapFeature::AudioEffect, - ClapFeature::Stereo, - ClapFeature::Mono, - ClapFeature::Utility, - ]; -} - -impl Vst3Plugin for Gain { - const VST3_CLASS_ID: [u8; 16] = *b"GainGuiYeahBoyyy"; - const VST3_SUBCATEGORIES: &'static [Vst3SubCategory] = - &[Vst3SubCategory::Fx, Vst3SubCategory::Tools]; -} - -nih_export_clap!(Gain); -nih_export_vst3!(Gain); diff --git a/plugins/examples/gain_gui_egui/src/main.rs b/plugins/examples/gain_gui_egui/src/main.rs deleted file mode 100644 index 85a264666..000000000 --- a/plugins/examples/gain_gui_egui/src/main.rs +++ /dev/null @@ -1,7 +0,0 @@ -use nih_plug::prelude::*; - -use gain_gui_egui::Gain; - -fn main() { - nih_export_standalone::(); -}