From 76b1d4f3459d17f004cd4691174c0727623f31be Mon Sep 17 00:00:00 2001 From: Tarasov Aleksandr <55220741+arabianq@users.noreply.github.com> Date: Sat, 25 Apr 2026 15:44:50 +0300 Subject: [PATCH] fix(gui): footer and hotkeys table are no longer clipped because of long filenames (#74) * fix: truncate file button text in draw function so footer is no clipped * fix(gui): fix hotkeys table clipping with egui_extras::TableBuilder fully reworked hotkeys page * deps: update flatpak cargo-sources.json --- Cargo.lock | 124 +++++++++++ Cargo.toml | 1 + packages/flatpak/cargo-sources.json | 169 +++++++++++++++ src/gui/draw.rs | 312 +++++++++++++++------------- 4 files changed, 460 insertions(+), 146 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1c7c609..670e651 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1022,6 +1022,20 @@ dependencies = [ "web-time", ] +[[package]] +name = "egui_extras" +version = "0.34.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bfc6870c68d3f254e33aca8200095d422e09edacb0f365f79fe23a5ba10963" +dependencies = [ + "ahash", + "egui", + "enum-map", + "log", + "mime_guess2", + "profiling", +] + [[package]] name = "egui_glow" version = "0.34.1" @@ -1079,6 +1093,26 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" +[[package]] +name = "enum-map" +version = "2.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6866f3bfdf8207509a033af1a75a7b08abda06bbaaeae6669323fd5a097df2e9" +dependencies = [ + "enum-map-derive", +] + +[[package]] +name = "enum-map-derive" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "enumflags2" version = "0.7.12" @@ -2060,6 +2094,24 @@ dependencies = [ "autocfg", ] +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess2" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1706dc14a2e140dec0a7a07109d9a3d5890b81e85bd6c60b906b249a77adf0ca" +dependencies = [ + "mime", + "phf", + "phf_shared", + "unicase", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -2721,6 +2773,50 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_macros", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_macros" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn", + "unicase", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", + "unicase", +] + [[package]] name = "pin-project" version = "1.1.11" @@ -2921,6 +3017,7 @@ dependencies = [ "eframe", "egui", "egui_dnd", + "egui_extras", "egui_material_icons", "evdev", "itertools 0.14.0", @@ -2981,6 +3078,21 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" +[[package]] +name = "rand" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + [[package]] name = "raw-window-handle" version = "0.6.2" @@ -3326,6 +3438,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c82d449ab1bccfeec125893c6875008206f038d4eb8a09e1e10caf86f44d574e" +[[package]] +name = "siphasher" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" + [[package]] name = "skrifa" version = "0.40.0" @@ -3907,6 +4025,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "unicase" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" + [[package]] name = "unicode-ident" version = "1.0.24" diff --git a/Cargo.toml b/Cargo.toml index ce71cb0..44a6915 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,6 +50,7 @@ eframe = { version = "0.34.1", default-features = false, features = [ "x11", "wayland", ] } +egui_extras = "0.34.1" egui_material_icons = "0.6.0" egui_dnd = "0.15.0" diff --git a/packages/flatpak/cargo-sources.json b/packages/flatpak/cargo-sources.json index 8dd8b45..f0806df 100644 --- a/packages/flatpak/cargo-sources.json +++ b/packages/flatpak/cargo-sources.json @@ -1273,6 +1273,19 @@ "dest": "cargo/vendor/egui_dnd-0.15.0", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/egui_extras/egui_extras-0.34.1.crate", + "sha256": "62bfc6870c68d3f254e33aca8200095d422e09edacb0f365f79fe23a5ba10963", + "dest": "cargo/vendor/egui_extras-0.34.1" + }, + { + "type": "inline", + "contents": "{\"package\": \"62bfc6870c68d3f254e33aca8200095d422e09edacb0f365f79fe23a5ba10963\", \"files\": {}}", + "dest": "cargo/vendor/egui_extras-0.34.1", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -1351,6 +1364,32 @@ "dest": "cargo/vendor/endi-1.1.1", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/enum-map/enum-map-2.7.3.crate", + "sha256": "6866f3bfdf8207509a033af1a75a7b08abda06bbaaeae6669323fd5a097df2e9", + "dest": "cargo/vendor/enum-map-2.7.3" + }, + { + "type": "inline", + "contents": "{\"package\": \"6866f3bfdf8207509a033af1a75a7b08abda06bbaaeae6669323fd5a097df2e9\", \"files\": {}}", + "dest": "cargo/vendor/enum-map-2.7.3", + "dest-filename": ".cargo-checksum.json" + }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/enum-map-derive/enum-map-derive-0.17.0.crate", + "sha256": "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb", + "dest": "cargo/vendor/enum-map-derive-0.17.0" + }, + { + "type": "inline", + "contents": "{\"package\": \"f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb\", \"files\": {}}", + "dest": "cargo/vendor/enum-map-derive-0.17.0", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -2677,6 +2716,32 @@ "dest": "cargo/vendor/memoffset-0.9.1", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/mime/mime-0.3.17.crate", + "sha256": "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a", + "dest": "cargo/vendor/mime-0.3.17" + }, + { + "type": "inline", + "contents": "{\"package\": \"6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a\", \"files\": {}}", + "dest": "cargo/vendor/mime-0.3.17", + "dest-filename": ".cargo-checksum.json" + }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/mime_guess2/mime_guess2-2.3.1.crate", + "sha256": "1706dc14a2e140dec0a7a07109d9a3d5890b81e85bd6c60b906b249a77adf0ca", + "dest": "cargo/vendor/mime_guess2-2.3.1" + }, + { + "type": "inline", + "contents": "{\"package\": \"1706dc14a2e140dec0a7a07109d9a3d5890b81e85bd6c60b906b249a77adf0ca\", \"files\": {}}", + "dest": "cargo/vendor/mime_guess2-2.3.1", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -3457,6 +3522,58 @@ "dest": "cargo/vendor/percent-encoding-2.3.2", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/phf/phf-0.11.3.crate", + "sha256": "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078", + "dest": "cargo/vendor/phf-0.11.3" + }, + { + "type": "inline", + "contents": "{\"package\": \"1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078\", \"files\": {}}", + "dest": "cargo/vendor/phf-0.11.3", + "dest-filename": ".cargo-checksum.json" + }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/phf_generator/phf_generator-0.11.3.crate", + "sha256": "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d", + "dest": "cargo/vendor/phf_generator-0.11.3" + }, + { + "type": "inline", + "contents": "{\"package\": \"3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d\", \"files\": {}}", + "dest": "cargo/vendor/phf_generator-0.11.3", + "dest-filename": ".cargo-checksum.json" + }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/phf_macros/phf_macros-0.11.3.crate", + "sha256": "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216", + "dest": "cargo/vendor/phf_macros-0.11.3" + }, + { + "type": "inline", + "contents": "{\"package\": \"f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216\", \"files\": {}}", + "dest": "cargo/vendor/phf_macros-0.11.3", + "dest-filename": ".cargo-checksum.json" + }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/phf_shared/phf_shared-0.11.3.crate", + "sha256": "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5", + "dest": "cargo/vendor/phf_shared-0.11.3" + }, + { + "type": "inline", + "contents": "{\"package\": \"67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5\", \"files\": {}}", + "dest": "cargo/vendor/phf_shared-0.11.3", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -3808,6 +3925,32 @@ "dest": "cargo/vendor/radium-0.7.0", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/rand/rand-0.8.6.crate", + "sha256": "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a", + "dest": "cargo/vendor/rand-0.8.6" + }, + { + "type": "inline", + "contents": "{\"package\": \"5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a\", \"files\": {}}", + "dest": "cargo/vendor/rand-0.8.6", + "dest-filename": ".cargo-checksum.json" + }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/rand_core/rand_core-0.6.4.crate", + "sha256": "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c", + "dest": "cargo/vendor/rand_core-0.6.4" + }, + { + "type": "inline", + "contents": "{\"package\": \"ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c\", \"files\": {}}", + "dest": "cargo/vendor/rand_core-0.6.4", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -4302,6 +4445,19 @@ "dest": "cargo/vendor/simple-easing-1.0.2", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/siphasher/siphasher-1.0.2.crate", + "sha256": "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e", + "dest": "cargo/vendor/siphasher-1.0.2" + }, + { + "type": "inline", + "contents": "{\"package\": \"b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e\", \"files\": {}}", + "dest": "cargo/vendor/siphasher-1.0.2", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -4978,6 +5134,19 @@ "dest": "cargo/vendor/uds_windows-1.2.1", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/unicase/unicase-2.9.0.crate", + "sha256": "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142", + "dest": "cargo/vendor/unicase-2.9.0" + }, + { + "type": "inline", + "contents": "{\"package\": \"dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142\", \"files\": {}}", + "dest": "cargo/vendor/unicase-2.9.0", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", diff --git a/src/gui/draw.rs b/src/gui/draw.rs index 9d39016..425d90c 100644 --- a/src/gui/draw.rs +++ b/src/gui/draw.rs @@ -1,9 +1,10 @@ use crate::gui::SoundpadGui; use egui::{ - Align, AtomExt, Button, CollapsingHeader, Color32, ComboBox, CursorIcon, FontFamily, Grid, - Label, Layout, RichText, ScrollArea, Sense, Slider, TextEdit, Ui, Vec2, + Align, AtomExt, Button, CollapsingHeader, Color32, ComboBox, CursorIcon, FontFamily, Label, + Layout, RichText, ScrollArea, Sense, Slider, TextEdit, Ui, Vec2, }; use egui_dnd::dnd; +use egui_extras::{Column, TableBuilder}; use egui_material_icons::icons::*; use pwsp::types::socket::Request; use pwsp::types::{audio_player::TrackInfo, gui::AppState}; @@ -133,26 +134,24 @@ impl SoundpadGui { } pub fn draw_hotkeys(&mut self, ui: &mut Ui) { - let area_size = ui.available_size(); ui.vertical(|ui| { - ui.set_min_width(area_size.x); - ui.set_min_height(area_size.y); ui.spacing_mut().item_spacing.y = 5.0; - // Header - ui.horizontal_top(|ui| { + // --- Header --- + ui.horizontal(|ui| { let back_button = Button::new(ICON_ARROW_BACK).frame(false); if ui.add(back_button).clicked() { self.app_state.show_hotkeys = false; } - ui.add_space(ui.available_width() / 2.0 - 40.0); - ui.label(RichText::new("Hotkeys").color(Color32::WHITE).monospace()); + ui.vertical_centered(|ui| { + ui.label(RichText::new("Hotkeys").color(Color32::WHITE).monospace()); + }); }); ui.separator(); - // Search and Add Command + // --- Search and Add Command --- ui.horizontal(|ui| { ui.menu_button(format!("{} Add Command", ICON_ADD.codepoint), |ui| { let mut selected_cmd = None; @@ -185,10 +184,10 @@ impl SoundpadGui { ui.add_space(10.0); - ui.add_sized( - [ui.available_width(), 22.0], + ui.add( TextEdit::singleline(&mut self.app_state.hotkey_search_query) - .hint_text("Search hotkeys..."), + .hint_text("Search hotkeys...") + .desired_width(f32::INFINITY), ); }); @@ -200,165 +199,186 @@ impl SoundpadGui { conflicts.into_iter().flat_map(|(a, b)| [a, b]).collect(); let search = self.app_state.hotkey_search_query.to_lowercase(); - - // Slots table let mut action: Option = None; - let area_size = ui.available_size(); - ScrollArea::vertical().show(ui, |ui| { - ui.set_min_width(area_size.x); - Grid::new("hotkeys_grid") - .striped(true) - .num_columns(4) - .max_col_width(area_size.x) - .min_col_width(area_size.x / 4.0) - .spacing([40.0, 10.0]) - .show(ui, |ui| { - // Table header + let slots: Vec<_> = self + .app_state + .hotkey_config + .slots + .iter() + .filter(|s| { + if search.is_empty() { + return true; + } + s.slot.to_lowercase().contains(&search) + || format!("{:?}", s.action).to_lowercase().contains(&search) + || s.key_chord + .as_deref() + .unwrap_or("") + .to_lowercase() + .contains(&search) + }) + .cloned() + .collect(); + + let available_width = ui.available_width(); + let col_width = (available_width / 4.0).max(80.0); + + TableBuilder::new(ui) + .striped(true) + .column(Column::exact(col_width).clip(true)) // Slot + .column(Column::exact(col_width).clip(true)) // Sound / Action name + .column(Column::exact(col_width).clip(true)) // Key Chord + .column(Column::exact(col_width).clip(true)) // Actions + .header(30.0, |mut header| { + header.col(|ui| { ui.label( RichText::new("Slot") .strong() .monospace() .color(Color32::LIGHT_GRAY), ); + }); + header.col(|ui| { ui.label( RichText::new("Sound") .strong() .monospace() .color(Color32::LIGHT_GRAY), ); + }); + header.col(|ui| { ui.label( RichText::new("Key Chord") .strong() .monospace() .color(Color32::LIGHT_GRAY), ); + }); + header.col(|ui| { ui.label( RichText::new("Actions") .strong() .monospace() .color(Color32::LIGHT_GRAY), ); - ui.end_row(); - - let slots: Vec<_> = self - .app_state - .hotkey_config - .slots - .iter() - .filter(|s| { - if search.is_empty() { - return true; - } - s.slot.to_lowercase().contains(&search) - || format!("{:?}", s.action).to_lowercase().contains(&search) - || s.key_chord - .as_deref() - .unwrap_or("") - .to_lowercase() - .contains(&search) - }) - .cloned() - .collect(); - - for slot in &slots { - ui.horizontal(|ui| { - // Conflict badge - if conflict_slots.contains(slot.slot.as_str()) { - ui.label( - RichText::new(ICON_WARNING.codepoint) - .color(Color32::from_rgb(255, 165, 0)), - ) - .on_hover_text("Key chord conflict"); - } - - // Slot name - let slot_text = RichText::new(&slot.slot).monospace(); - ui.label(slot_text); - }); - - // Action description - let action_name = match slot.action.name.as_str() { - "play" => { - if let Some(file_path_str) = slot.action.args.get("file_path") { - Path::new(file_path_str) - .file_name() - .unwrap_or_default() - .to_string_lossy() - .to_string() - } else { - "Play".to_string() - } - } - "toggle_pause" => "Toggle Pause".to_string(), - "pause" => "Pause Playback".to_string(), - "resume" => "Resume Playback".to_string(), - "stop" => "Stop Playback".to_string(), - "toggle_loop" => "Toggle Loop".to_string(), - other => other.to_string(), - }; - ui.add(Label::new(RichText::new(action_name).monospace()).truncate()); - - // Key chord - let chord_text = slot.key_chord.as_deref().unwrap_or("(none)"); - ui.label(RichText::new(chord_text).monospace().color( - if slot.key_chord.is_some() { - Color32::from_rgb(100, 200, 100) - } else { - Color32::GRAY - }, - )); - - ui.horizontal(|ui| { - // Delete button - if ui - .add(Button::new(ICON_DELETE).frame(false)) - .on_hover_text("Remove slot") - .clicked() - { - action = Some(HotkeyAction::Remove(slot.slot.clone())); - } - - // Set key chord button - if ui - .add(Button::new(ICON_KEYBOARD).frame(false)) - .on_hover_text("Set key chord") - .clicked() - { - action = Some(HotkeyAction::Capture(slot.slot.clone())); - } - - // Clear key chord - if slot.key_chord.is_some() - && ui - .add(Button::new(ICON_BACKSPACE).frame(false)) - .on_hover_text("Clear key chord") - .clicked() - { - action = Some(HotkeyAction::ClearChord(slot.slot.clone())); - } - - // Play button - if ui - .add(Button::new(ICON_PLAY_ARROW).frame(false)) - .on_hover_text("Play") - .clicked() - { - action = Some(HotkeyAction::Play(slot.slot.clone())); - } - }); - ui.end_row(); - } - - if slots.is_empty() { - ui.label("No hotkey slots configured."); - ui.label(""); - ui.label(""); - ui.label(""); - ui.end_row(); - } }); - }); + }) + .body(|mut body| { + if slots.is_empty() { + body.row(30.0, |mut row| { + row.col(|_| {}); + row.col(|ui| { + ui.label( + RichText::new("No hotkey slots configured.") + .color(Color32::GRAY), + ); + }); + row.col(|_| {}); + row.col(|_| {}); + }); + return; + } + + for slot in &slots { + body.row(30.0, |mut row| { + // Column 1: Slot + row.col(|ui| { + ui.horizontal(|ui| { + if conflict_slots.contains(slot.slot.as_str()) { + ui.label( + RichText::new(ICON_WARNING.codepoint) + .color(Color32::from_rgb(255, 165, 0)), + ) + .on_hover_text("Key chord conflict"); + } + ui.add( + Label::new(RichText::new(&slot.slot).monospace()) + .truncate(), + ); + }); + }); + + // Column 2: Sound / Action name + row.col(|ui| { + let action_name = match slot.action.name.as_str() { + "play" => { + if let Some(file_path_str) = + slot.action.args.get("file_path") + { + Path::new(file_path_str) + .file_name() + .unwrap_or_default() + .to_string_lossy() + .to_string() + } else { + "Play".to_string() + } + } + "toggle_pause" => "Toggle Pause".to_string(), + "pause" => "Pause Playback".to_string(), + "resume" => "Resume Playback".to_string(), + "stop" => "Stop Playback".to_string(), + "toggle_loop" => "Toggle Loop".to_string(), + other => other.to_string(), + }; + ui.add( + Label::new(RichText::new(action_name).monospace()).truncate(), + ); + }); + + // Column 3: Key Chord + row.col(|ui| { + let chord_text = slot.key_chord.as_deref().unwrap_or("(none)"); + ui.add( + Label::new(RichText::new(chord_text).monospace().color( + if slot.key_chord.is_some() { + Color32::from_rgb(100, 200, 100) + } else { + Color32::GRAY + }, + )) + .truncate(), + ); + }); + + // Column 4: Actions + row.col(|ui| { + ui.horizontal(|ui| { + if ui + .add(Button::new(ICON_DELETE).frame(false)) + .on_hover_text("Remove slot") + .clicked() + { + action = Some(HotkeyAction::Remove(slot.slot.clone())); + } + if ui + .add(Button::new(ICON_KEYBOARD).frame(false)) + .on_hover_text("Set key chord") + .clicked() + { + action = Some(HotkeyAction::Capture(slot.slot.clone())); + } + if slot.key_chord.is_some() + && ui + .add(Button::new(ICON_BACKSPACE).frame(false)) + .on_hover_text("Clear key chord") + .clicked() + { + action = Some(HotkeyAction::ClearChord(slot.slot.clone())); + } + if ui + .add(Button::new(ICON_PLAY_ARROW).frame(false)) + .on_hover_text("Play") + .clicked() + { + action = Some(HotkeyAction::Play(slot.slot.clone())); + } + }); + }); + }); + } + }); if let Some(action) = action { match action { @@ -731,7 +751,7 @@ impl SoundpadGui { file_button_text = file_button_text.color(Color32::WHITE); } - let file_button = Button::new(file_button_text).frame(false); + let file_button = Button::new(file_button_text).frame(false).truncate(); let file_button_response = ui.add(file_button); if file_button_response.clicked() { ui.input(|i| {