mirror of
https://github.com/arabianq/pipewire-soundpad.git
synced 2026-04-28 06:21:23 +00:00
feat(gui): now directories can be reordered using drag and drop
This commit is contained in:
Generated
+61
-1
@@ -182,7 +182,7 @@ dependencies = [
|
|||||||
"bitflags 2.10.0",
|
"bitflags 2.10.0",
|
||||||
"cexpr",
|
"cexpr",
|
||||||
"clang-sys",
|
"clang-sys",
|
||||||
"itertools",
|
"itertools 0.13.0",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"regex",
|
"regex",
|
||||||
@@ -461,6 +461,16 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "concat-idents"
|
||||||
|
version = "1.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f76990911f2267d837d9d0ad060aa63aaad170af40904b29461734c339030d4d"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "concurrent-queue"
|
name = "concurrent-queue"
|
||||||
version = "2.5.0"
|
version = "2.5.0"
|
||||||
@@ -810,6 +820,29 @@ dependencies = [
|
|||||||
"winit",
|
"winit",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "egui_animation"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a3db554dd3784f469d804f7dc25d1b14a2e00f1608d7af60218ccbced720a6e8"
|
||||||
|
dependencies = [
|
||||||
|
"egui",
|
||||||
|
"hello_egui_utils",
|
||||||
|
"simple-easing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "egui_dnd"
|
||||||
|
version = "0.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f535b8df7ca89f781954feaa505899c8955b550074ecafcaa3c040f67aec3d46"
|
||||||
|
dependencies = [
|
||||||
|
"egui",
|
||||||
|
"egui_animation",
|
||||||
|
"simple-easing",
|
||||||
|
"web-time",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "egui_glow"
|
name = "egui_glow"
|
||||||
version = "0.33.3"
|
version = "0.33.3"
|
||||||
@@ -1180,6 +1213,16 @@ version = "0.5.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hello_egui_utils"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f5d09c2c7f3aa61624b1bec320be9029f30e06769f92e39ac99a6d6e01024ae8"
|
||||||
|
dependencies = [
|
||||||
|
"concat-idents",
|
||||||
|
"egui",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
@@ -1327,6 +1370,15 @@ dependencies = [
|
|||||||
"either",
|
"either",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.17"
|
version = "1.0.17"
|
||||||
@@ -2247,7 +2299,9 @@ dependencies = [
|
|||||||
"dirs",
|
"dirs",
|
||||||
"eframe",
|
"eframe",
|
||||||
"egui",
|
"egui",
|
||||||
|
"egui_dnd",
|
||||||
"egui_material_icons",
|
"egui_material_icons",
|
||||||
|
"itertools 0.14.0",
|
||||||
"pipewire",
|
"pipewire",
|
||||||
"rfd",
|
"rfd",
|
||||||
"rodio",
|
"rodio",
|
||||||
@@ -2569,6 +2623,12 @@ version = "0.3.8"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2"
|
checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "simple-easing"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "832ddd7df0d98d6fd93b973c330b7c8e0742d5cb8f1afc7dea89dba4d2531aa1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slab"
|
name = "slab"
|
||||||
version = "0.4.11"
|
version = "0.4.11"
|
||||||
|
|||||||
+3
-1
@@ -20,6 +20,7 @@ serde_json = "1.0.149"
|
|||||||
|
|
||||||
clap = { version = "4.5.54", default-features = false, features = ["std", "suggestions", "help", "usage", "error-context", "derive"] }
|
clap = { version = "4.5.54", default-features = false, features = ["std", "suggestions", "help", "usage", "error-context", "derive"] }
|
||||||
dirs = "6.0.0"
|
dirs = "6.0.0"
|
||||||
|
itertools = "0.14.0"
|
||||||
|
|
||||||
rodio = { version = "0.21.1", default-features = false, features = ["symphonia-all", "playback"] }
|
rodio = { version = "0.21.1", default-features = false, features = ["symphonia-all", "playback"] }
|
||||||
pipewire = "0.9.2"
|
pipewire = "0.9.2"
|
||||||
@@ -28,6 +29,7 @@ rfd = { version = "0.17.2", default-features = false, features = ["xdg-portal"]}
|
|||||||
egui = { version = "0.33.3", default-features = false, features = ["default_fonts", "rayon"] }
|
egui = { version = "0.33.3", default-features = false, features = ["default_fonts", "rayon"] }
|
||||||
eframe = { version = "0.33.3", default-features = false, features = ["default_fonts", "glow", "x11", "wayland"] }
|
eframe = { version = "0.33.3", default-features = false, features = ["default_fonts", "glow", "x11", "wayland"] }
|
||||||
egui_material_icons = "0.5.0"
|
egui_material_icons = "0.5.0"
|
||||||
|
egui_dnd = "0.14.0"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "pwsp-daemon"
|
name = "pwsp-daemon"
|
||||||
@@ -56,4 +58,4 @@ assets = [
|
|||||||
["assets/pwsp-gui.desktop", "usr/share/applications/pwsp.desktop", "644"],
|
["assets/pwsp-gui.desktop", "usr/share/applications/pwsp.desktop", "644"],
|
||||||
["assets/icon.png", "usr/share/icons/hicolor/256x256/apps/pwsp.png", "644"],
|
["assets/icon.png", "usr/share/icons/hicolor/256x256/apps/pwsp.png", "644"],
|
||||||
["assets/pwsp-daemon.service", "usr/lib/systemd/user/pwsp-daemon.service", "644"],
|
["assets/pwsp-daemon.service", "usr/lib/systemd/user/pwsp-daemon.service", "644"],
|
||||||
]
|
]
|
||||||
|
|||||||
+13
-7
@@ -3,10 +3,11 @@ use egui::{
|
|||||||
Align, AtomExt, Button, CollapsingHeader, Color32, ComboBox, CursorIcon, FontFamily, Label,
|
Align, AtomExt, Button, CollapsingHeader, Color32, ComboBox, CursorIcon, FontFamily, Label,
|
||||||
Layout, RichText, ScrollArea, Sense, Slider, TextEdit, Ui, Vec2,
|
Layout, RichText, ScrollArea, Sense, Slider, TextEdit, Ui, Vec2,
|
||||||
};
|
};
|
||||||
|
use egui_dnd::dnd;
|
||||||
use egui_material_icons::icons;
|
use egui_material_icons::icons;
|
||||||
use pwsp::types::audio_player::TrackInfo;
|
use pwsp::types::audio_player::TrackInfo;
|
||||||
use pwsp::utils::gui::format_time_pair;
|
use pwsp::utils::gui::format_time_pair;
|
||||||
use std::{error::Error, path::PathBuf, time::Instant};
|
use std::{error::Error, time::Instant};
|
||||||
|
|
||||||
use pwsp::types::gui::AppState;
|
use pwsp::types::gui::AppState;
|
||||||
|
|
||||||
@@ -301,10 +302,14 @@ impl SoundpadGui {
|
|||||||
ScrollArea::vertical().id_salt(0).show(ui, |ui| {
|
ScrollArea::vertical().id_salt(0).show(ui, |ui| {
|
||||||
ui.set_min_width(area_size.x);
|
ui.set_min_width(area_size.x);
|
||||||
|
|
||||||
let mut dirs: Vec<PathBuf> = self.app_state.dirs.iter().cloned().collect();
|
let mut dirs = self.app_state.dirs.clone();
|
||||||
dirs.sort();
|
|
||||||
for path in dirs.iter() {
|
dnd(ui, "dnd_directories").show_vec(&mut dirs, |ui, item, handle, _state| {
|
||||||
|
let path = item.clone();
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
|
handle.ui(ui, |ui| {
|
||||||
|
ui.label(icons::ICON_DRAG_INDICATOR);
|
||||||
|
});
|
||||||
let name = path
|
let name = path
|
||||||
.file_name()
|
.file_name()
|
||||||
.map(|s| s.to_string_lossy().to_string())
|
.map(|s| s.to_string_lossy().to_string())
|
||||||
@@ -312,7 +317,7 @@ impl SoundpadGui {
|
|||||||
|
|
||||||
let mut dir_button_text = RichText::new(name.clone());
|
let mut dir_button_text = RichText::new(name.clone());
|
||||||
if let Some(current_dir) = &self.app_state.current_dir {
|
if let Some(current_dir) = &self.app_state.current_dir {
|
||||||
if current_dir.eq(path) {
|
if current_dir.eq(&path) {
|
||||||
dir_button_text = dir_button_text.color(Color32::WHITE);
|
dir_button_text = dir_button_text.color(Color32::WHITE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -322,7 +327,7 @@ impl SoundpadGui {
|
|||||||
|
|
||||||
let dir_button_response = ui.add(dir_button);
|
let dir_button_response = ui.add(dir_button);
|
||||||
if dir_button_response.clicked() {
|
if dir_button_response.clicked() {
|
||||||
self.open_dir(path);
|
self.open_dir(&path);
|
||||||
}
|
}
|
||||||
|
|
||||||
let delete_dir_button = Button::new(icons::ICON_DELETE).frame(false);
|
let delete_dir_button = Button::new(icons::ICON_DELETE).frame(false);
|
||||||
@@ -332,7 +337,8 @@ impl SoundpadGui {
|
|||||||
self.remove_dir(&path.clone());
|
self.remove_dir(&path.clone());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
|
self.app_state.dirs = dirs;
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
let add_dirs_button = Button::new(icons::ICON_ADD).frame(false);
|
let add_dirs_button = Button::new(icons::ICON_ADD).frame(false);
|
||||||
|
|||||||
+4
-2
@@ -4,6 +4,7 @@ mod update;
|
|||||||
|
|
||||||
use eframe::{HardwareAcceleration, NativeOptions, icon_data::from_png_bytes, run_native};
|
use eframe::{HardwareAcceleration, NativeOptions, icon_data::from_png_bytes, run_native};
|
||||||
use egui::{Context, Vec2, ViewportBuilder};
|
use egui::{Context, Vec2, ViewportBuilder};
|
||||||
|
use itertools::Itertools;
|
||||||
use pwsp::{
|
use pwsp::{
|
||||||
types::{
|
types::{
|
||||||
audio_player::PlayerState,
|
audio_player::PlayerState,
|
||||||
@@ -87,15 +88,16 @@ impl SoundpadGui {
|
|||||||
let file_dialog = FileDialog::new();
|
let file_dialog = FileDialog::new();
|
||||||
if let Some(paths) = file_dialog.pick_folders() {
|
if let Some(paths) = file_dialog.pick_folders() {
|
||||||
for path in paths {
|
for path in paths {
|
||||||
self.app_state.dirs.insert(path);
|
self.app_state.dirs.push(path);
|
||||||
}
|
}
|
||||||
|
self.app_state.dirs = self.app_state.dirs.iter().unique().cloned().collect();
|
||||||
self.config.dirs = self.app_state.dirs.clone();
|
self.config.dirs = self.app_state.dirs.clone();
|
||||||
self.config.save_to_file().ok();
|
self.config.save_to_file().ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_dir(&mut self, path: &PathBuf) {
|
pub fn remove_dir(&mut self, path: &PathBuf) {
|
||||||
self.app_state.dirs.remove(path);
|
self.app_state.dirs.retain(|x| x != path);
|
||||||
if let Some(current_dir) = &self.app_state.current_dir
|
if let Some(current_dir) = &self.app_state.current_dir
|
||||||
&& current_dir == path
|
&& current_dir == path
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,6 +9,13 @@ use std::time::{Duration, Instant};
|
|||||||
|
|
||||||
impl App for SoundpadGui {
|
impl App for SoundpadGui {
|
||||||
fn update(&mut self, ctx: &Context, _frame: &mut EFrame) {
|
fn update(&mut self, ctx: &Context, _frame: &mut EFrame) {
|
||||||
|
// Save directories if changed
|
||||||
|
if !self.config.dirs.eq(&self.app_state.dirs) {
|
||||||
|
self.config.dirs = self.app_state.dirs.clone();
|
||||||
|
self.config.save_to_file().ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seek and volume requests
|
||||||
let mut seek_requests = vec![];
|
let mut seek_requests = vec![];
|
||||||
let mut volume_requests = vec![];
|
let mut volume_requests = vec![];
|
||||||
|
|
||||||
@@ -57,11 +64,13 @@ impl App for SoundpadGui {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sync audio player state
|
||||||
{
|
{
|
||||||
let guard = self.audio_player_state_shared.lock().unwrap();
|
let guard = self.audio_player_state_shared.lock().unwrap();
|
||||||
self.audio_player_state = guard.clone();
|
self.audio_player_state = guard.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle scale factor changes
|
||||||
let old_scale_factor = self.config.scale_factor;
|
let old_scale_factor = self.config.scale_factor;
|
||||||
let new_scale_factor = ctx.zoom_factor().clamp(0.5, 2.0);
|
let new_scale_factor = ctx.zoom_factor().clamp(0.5, 2.0);
|
||||||
|
|
||||||
@@ -72,8 +81,10 @@ impl App for SoundpadGui {
|
|||||||
self.config.save_to_file().ok();
|
self.config.save_to_file().ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle input
|
||||||
self.handle_input(ctx);
|
self.handle_input(ctx);
|
||||||
|
|
||||||
|
// Draw UI
|
||||||
CentralPanel::default().show(ctx, |ui| {
|
CentralPanel::default().show(ctx, |ui| {
|
||||||
if !self.audio_player_state.is_daemon_running {
|
if !self.audio_player_state.is_daemon_running {
|
||||||
self.draw_waiting_for_daemon(ui);
|
self.draw_waiting_for_daemon(ui);
|
||||||
@@ -88,6 +99,7 @@ impl App for SoundpadGui {
|
|||||||
self.draw(ui).ok();
|
self.draw(ui).ok();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Request repaint
|
||||||
ctx.request_repaint_after_secs(1.0 / 60.0);
|
ctx.request_repaint_after_secs(1.0 / 60.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -1,6 +1,6 @@
|
|||||||
use crate::utils::config::get_config_path;
|
use crate::utils::config::get_config_path;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{collections::HashSet, error::Error, fs, path::PathBuf};
|
use std::{error::Error, fs, path::PathBuf};
|
||||||
|
|
||||||
#[derive(Default, Clone, Serialize, Deserialize)]
|
#[derive(Default, Clone, Serialize, Deserialize)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
@@ -41,7 +41,7 @@ pub struct GuiConfig {
|
|||||||
pub save_scale_factor: bool,
|
pub save_scale_factor: bool,
|
||||||
pub pause_on_exit: bool,
|
pub pause_on_exit: bool,
|
||||||
|
|
||||||
pub dirs: HashSet<PathBuf>,
|
pub dirs: Vec<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for GuiConfig {
|
impl Default for GuiConfig {
|
||||||
@@ -55,7 +55,7 @@ impl Default for GuiConfig {
|
|||||||
save_scale_factor: false,
|
save_scale_factor: false,
|
||||||
pause_on_exit: false,
|
pause_on_exit: false,
|
||||||
|
|
||||||
dirs: HashSet::default(),
|
dirs: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -37,7 +37,7 @@ pub struct AppState {
|
|||||||
pub ignore_volume_update_until: Option<Instant>,
|
pub ignore_volume_update_until: Option<Instant>,
|
||||||
|
|
||||||
pub current_dir: Option<PathBuf>,
|
pub current_dir: Option<PathBuf>,
|
||||||
pub dirs: HashSet<PathBuf>,
|
pub dirs: Vec<PathBuf>,
|
||||||
|
|
||||||
pub selected_file: Option<PathBuf>,
|
pub selected_file: Option<PathBuf>,
|
||||||
pub files: HashSet<PathBuf>,
|
pub files: HashSet<PathBuf>,
|
||||||
|
|||||||
Reference in New Issue
Block a user