From 21f385636db73888c3206ef588f5d3a24c29f959 Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Thu, 19 Aug 2021 15:45:24 +0200 Subject: [PATCH 1/5] first draft of kb shortcut in config --- Cargo.lock | 37 +++++++++++++------- psst-gui/Cargo.toml | 7 ++-- psst-gui/src/controller/playback.rs | 8 ++--- psst-gui/src/data/config.rs | 24 +++++++++++++ psst-gui/src/data/kbshortcut.rs | 53 +++++++++++++++++++++++++++++ psst-gui/src/data/mod.rs | 2 ++ 6 files changed, 112 insertions(+), 19 deletions(-) create mode 100644 psst-gui/src/data/kbshortcut.rs diff --git a/Cargo.lock b/Cargo.lock index a0fd0f3c..6bd1823f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -489,7 +489,7 @@ checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" [[package]] name = "druid" version = "0.7.0" -source = "git+https://github.com/jpochyla/druid?branch=psst#30aa0baab6f9801aba16db48793d97a23839856a" +source = "git+https://github.com/JuliDi/druid?branch=psst#a31679cdc6e0d4bebb96f5c50970ff9afa254851" dependencies = [ "console_error_panic_hook", "druid-derive", @@ -511,7 +511,7 @@ dependencies = [ [[package]] name = "druid-derive" version = "0.4.0" -source = "git+https://github.com/jpochyla/druid?branch=psst#30aa0baab6f9801aba16db48793d97a23839856a" +source = "git+https://github.com/JuliDi/druid?branch=psst#a31679cdc6e0d4bebb96f5c50970ff9afa254851" dependencies = [ "proc-macro2", "quote", @@ -521,7 +521,7 @@ dependencies = [ [[package]] name = "druid-shell" version = "0.7.0" -source = "git+https://github.com/jpochyla/druid?branch=psst#30aa0baab6f9801aba16db48793d97a23839856a" +source = "git+https://github.com/JuliDi/druid?branch=psst#a31679cdc6e0d4bebb96f5c50970ff9afa254851" dependencies = [ "anyhow", "bitflags", @@ -542,7 +542,7 @@ dependencies = [ "image", "instant", "js-sys", - "keyboard-types", + "keyboard-types 0.5.0", "kurbo", "lazy_static", "objc", @@ -1148,6 +1148,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a989afac88279b0482f402d234b5fbd405bf1ad051308595b58de4e6de22346b" dependencies = [ "bitflags", + "serde", + "unicode-segmentation", +] + +[[package]] +name = "keyboard-types" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5168b1079eb8dc457c80f263c1f521578e458c1b21ae42328f65117738d31396" +dependencies = [ + "bitflags", + "serde", + "unicode-segmentation", ] [[package]] @@ -1221,9 +1234,9 @@ checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" [[package]] name = "memchr" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" [[package]] name = "miniaudio" @@ -1668,6 +1681,7 @@ dependencies = [ "env_logger", "fs_extra", "itertools 0.10.1", + "keyboard-types 0.6.1", "log", "lru-cache", "once_cell", @@ -2003,8 +2017,7 @@ dependencies = [ [[package]] name = "souvlaki" version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5834fb7421062570b409e49ecebee68d303866c8ba1c3f81138a82e8eba64081" +source = "git+https://github.com/Sinono3/souvlaki#ab184816861bf2d691a1cf30db834cad383f70d6" dependencies = [ "block", "cocoa", @@ -2292,18 +2305,18 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9ff14f98b1a4b289c6248a023c1c2fa1491062964e9fed67ab29c4e4da4a052" +checksum = "2ca517f43f0fb96e0c3072ed5c275fe5eece87e8cb52f4a77b69226d3b1c9df8" dependencies = [ "lazy_static", ] [[package]] name = "tracing-subscriber" -version = "0.2.19" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab69019741fca4d98be3c62d2b75254528b5432233fd8a4d2739fec20278de48" +checksum = "b9cbe87a2fa7e35900ce5de20220a582a9483a7063811defce79d7cbd59d4cfe" dependencies = [ "ansi_term", "sharded-slab", diff --git a/psst-gui/Cargo.toml b/psst-gui/Cargo.toml index 593f03f4..293a4dd4 100644 --- a/psst-gui/Cargo.toml +++ b/psst-gui/Cargo.toml @@ -12,8 +12,9 @@ psst-core = { path = "../psst-core" } chrono = { version = "0.4", features = ["serde"] } crossbeam-channel = "0.5" -druid-shell = { git = "https://github.com/jpochyla/druid", branch = "psst", features = ["raw-win-handle"] } -druid = { git = "https://github.com/jpochyla/druid", branch = "psst", features = ["im", "image", "jpeg", "png", "serde"] } +druid-shell = { git = "https://github.com/JuliDi/druid", branch = "psst", features = ["raw-win-handle"] } +druid = { git = "https://github.com/JuliDi/druid", branch = "psst", features = ["im", "image", "jpeg", "png", "serde"] } +keyboard-types = { version = "0.6.1", features = ["serde"] } env_logger = "0.8" fs_extra = "1.2" itertools = "0.10" @@ -26,7 +27,7 @@ rand = "0.8" raw-window-handle = "0.3" serde = { version = "1.0", features = ["derive", "rc"] } serde_json = "1.0" -souvlaki = "0.4" +souvlaki = { git = "https://github.com/Sinono3/souvlaki" } ureq = { version = "2.1", features = ["json", "socks-proxy"] } url = "2.2" diff --git a/psst-gui/src/controller/playback.rs b/psst-gui/src/controller/playback.rs index cbab06e2..eaa8676e 100644 --- a/psst-gui/src/controller/playback.rs +++ b/psst-gui/src/controller/playback.rs @@ -24,8 +24,8 @@ use souvlaki::{ use crate::{ cmd, data::{ - AppState, Config, Playback, PlaybackOrigin, PlaybackState, QueueBehavior, QueuedTrack, - TrackId, + AppState, Config, KbShortcut, Playback, PlaybackOrigin, PlaybackState, QueueBehavior, + QueuedTrack, TrackId, }, }; @@ -406,11 +406,11 @@ where self.previous(); ctx.set_handled(); } - Event::KeyDown(key) if key.key == KbKey::Character("+".to_string()) => { + Event::KeyDown(key) if data.config.shortcuts.volume_increase.matches(key) => { data.playback.volume = (data.playback.volume + 0.1).min(1.0); ctx.set_handled(); } - Event::KeyDown(key) if key.key == KbKey::Character("-".to_string()) => { + Event::KeyDown(key) if data.config.shortcuts.volume_decrease.matches(key) => { data.playback.volume = (data.playback.volume - 0.1).max(0.0); ctx.set_handled(); } diff --git a/psst-gui/src/data/config.rs b/psst-gui/src/data/config.rs index 70c065d0..f491707b 100644 --- a/psst-gui/src/data/config.rs +++ b/psst-gui/src/data/config.rs @@ -11,6 +11,7 @@ use psst_core::{ use serde::{Deserialize, Serialize}; use super::Promise; +use crate::data::{KbShortcut, ToKbShortcut}; #[derive(Clone, Debug, Data, Lens)] pub struct Preferences { @@ -72,6 +73,7 @@ pub struct Config { pub audio_quality: AudioQuality, pub theme: Theme, pub volume: f64, + pub shortcuts: KbShortcuts, } impl Default for Config { @@ -81,6 +83,7 @@ impl Default for Config { audio_quality: Default::default(), theme: Default::default(), volume: 1.0, + shortcuts: KbShortcuts::default(), } } } @@ -193,3 +196,24 @@ impl Default for Theme { Self::Light } } + +#[derive(Clone, Debug, Eq, PartialEq, Data, Serialize, Deserialize)] +pub struct KbShortcuts { + pub play_resume: KbShortcut, + pub volume_increase: KbShortcut, + pub volume_decrease: KbShortcut, + pub next_song: KbShortcut, + pub previous_song: KbShortcut, +} + +impl Default for KbShortcuts { + fn default() -> Self { + Self { + play_resume: " ".to_string().to_kbshortcut().unwrap(), + volume_increase: "+".to_string().to_kbshortcut().unwrap(), + volume_decrease: "-".to_string().to_kbshortcut().unwrap(), + next_song: "ArrowRight".to_string().to_kbshortcut().unwrap(), + previous_song: "ArrowLeft".to_string().to_kbshortcut().unwrap(), + } + } +} diff --git a/psst-gui/src/data/kbshortcut.rs b/psst-gui/src/data/kbshortcut.rs new file mode 100644 index 00000000..50818e36 --- /dev/null +++ b/psst-gui/src/data/kbshortcut.rs @@ -0,0 +1,53 @@ +use druid::Data; +use druid_shell::{Code, KbKey, KeyEvent}; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub enum KbShortcut { + Key(KbKey), + Code(Code), +} + +impl Data for KbShortcut { + fn same(&self, other: &Self) -> bool { + self == other + } +} + +impl KbShortcut { + pub fn matches(&self, event: &KeyEvent) -> bool { + match self { + KbShortcut::Key(key) => &event.key == key, + KbShortcut::Code(code) => &event.code == code, + } + } +} + +pub trait ToKbShortcut { + fn to_kbshortcut(&self) -> Result; +} + +impl ToKbShortcut for String { + fn to_kbshortcut(&self) -> Result { + if let Ok(code) = kb_code_from_str(&self) { + Ok(KbShortcut::Code(code)) + } else if self.len() == 1 { + Ok(KbShortcut::Key(KbKey::Character(self.clone()))) + } else { + Err(()) + } + } +} + +fn kb_code_from_str(s: &str) -> Result { + match s { + "NumpadAdd" => Ok(Code::NumpadAdd), + "Minus" => Ok(Code::Minus), + " " | "Space" => Ok(Code::Space), + "ArrowRight" => Ok(Code::ArrowRight), + "ArrowLeft" => Ok(Code::ArrowLeft), + "ArrowUp" => Ok(Code::ArrowUp), + "ArrowDown" => Ok(Code::ArrowDown), + _ => Err(()), + } +} diff --git a/psst-gui/src/data/mod.rs b/psst-gui/src/data/mod.rs index c3559b2c..079ea776 100644 --- a/psst-gui/src/data/mod.rs +++ b/psst-gui/src/data/mod.rs @@ -3,6 +3,7 @@ mod artist; mod config; mod ctx; mod id; +mod kbshortcut; mod nav; mod playback; mod playlist; @@ -26,6 +27,7 @@ pub use crate::data::{ artist::{Artist, ArtistAlbums, ArtistDetail, ArtistLink, ArtistTracks}, config::{AudioQuality, Authentication, Config, Preferences, PreferencesTab, Theme}, ctx::Ctx, + kbshortcut::{KbShortcut, ToKbShortcut}, nav::{Nav, SpotifyUrl}, playback::{ NowPlaying, Playback, PlaybackOrigin, PlaybackPayload, PlaybackState, QueueBehavior, From 89f57dd811c4d89a8091a1f854c3f00bdb5b78a6 Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Fri, 20 Aug 2021 08:26:01 +0200 Subject: [PATCH 2/5] change kbshortcuts to be stored as string for compativility with text box --- psst-gui/src/controller/playback.rs | 4 +- psst-gui/src/data/config.rs | 28 +++++----- psst-gui/src/data/kbshortcut.rs | 79 ++++++++++++++++++++++------- psst-gui/src/data/mod.rs | 8 ++- psst-gui/src/ui/preferences.rs | 43 +++++++++++++++- 5 files changed, 126 insertions(+), 36 deletions(-) diff --git a/psst-gui/src/controller/playback.rs b/psst-gui/src/controller/playback.rs index eaa8676e..180dfde7 100644 --- a/psst-gui/src/controller/playback.rs +++ b/psst-gui/src/controller/playback.rs @@ -406,11 +406,11 @@ where self.previous(); ctx.set_handled(); } - Event::KeyDown(key) if data.config.shortcuts.volume_increase.matches(key) => { + Event::KeyDown(key) if matches(key, data.shortcuts.volume_increase) => { data.playback.volume = (data.playback.volume + 0.1).min(1.0); ctx.set_handled(); } - Event::KeyDown(key) if data.config.shortcuts.volume_decrease.matches(key) => { + Event::KeyDown(key) if data.shortcuts.volume_decrease().matches(key) => { data.playback.volume = (data.playback.volume - 0.1).max(0.0); ctx.set_handled(); } diff --git a/psst-gui/src/data/config.rs b/psst-gui/src/data/config.rs index f491707b..c99133b3 100644 --- a/psst-gui/src/data/config.rs +++ b/psst-gui/src/data/config.rs @@ -11,7 +11,8 @@ use psst_core::{ use serde::{Deserialize, Serialize}; use super::Promise; -use crate::data::{KbShortcut, ToKbShortcut}; +use crate::data::KbShortcut; +use std::str::FromStr; #[derive(Clone, Debug, Data, Lens)] pub struct Preferences { @@ -35,6 +36,7 @@ impl Preferences { pub enum PreferencesTab { General, Cache, + Shortcuts, } #[derive(Clone, Debug, Data, Lens)] @@ -73,7 +75,6 @@ pub struct Config { pub audio_quality: AudioQuality, pub theme: Theme, pub volume: f64, - pub shortcuts: KbShortcuts, } impl Default for Config { @@ -83,7 +84,6 @@ impl Default for Config { audio_quality: Default::default(), theme: Default::default(), volume: 1.0, - shortcuts: KbShortcuts::default(), } } } @@ -197,23 +197,23 @@ impl Default for Theme { } } -#[derive(Clone, Debug, Eq, PartialEq, Data, Serialize, Deserialize)] +#[derive(Clone, Debug, Eq, PartialEq, Data, Lens, Serialize, Deserialize)] pub struct KbShortcuts { - pub play_resume: KbShortcut, - pub volume_increase: KbShortcut, - pub volume_decrease: KbShortcut, - pub next_song: KbShortcut, - pub previous_song: KbShortcut, + pub play_resume: String, + pub volume_increase: String, + pub volume_decrease: String, + pub nextsong: String, + pub previous_song: String, } impl Default for KbShortcuts { fn default() -> Self { Self { - play_resume: " ".to_string().to_kbshortcut().unwrap(), - volume_increase: "+".to_string().to_kbshortcut().unwrap(), - volume_decrease: "-".to_string().to_kbshortcut().unwrap(), - next_song: "ArrowRight".to_string().to_kbshortcut().unwrap(), - previous_song: "ArrowLeft".to_string().to_kbshortcut().unwrap(), + play_resume: " ".to_string(), + volume_increase: "+".to_string(), + volume_decrease: "-".to_string(), + nextsong: "ArrowRight".to_string(), + previous_song: "ArrowLeft".to_string(), } } } diff --git a/psst-gui/src/data/kbshortcut.rs b/psst-gui/src/data/kbshortcut.rs index 50818e36..18554730 100644 --- a/psst-gui/src/data/kbshortcut.rs +++ b/psst-gui/src/data/kbshortcut.rs @@ -1,6 +1,8 @@ -use druid::Data; +use druid::{Data, Lens}; use druid_shell::{Code, KbKey, KeyEvent}; use serde::{Deserialize, Serialize}; +use std::fmt; +use std::str::FromStr; #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] pub enum KbShortcut { @@ -23,22 +25,6 @@ impl KbShortcut { } } -pub trait ToKbShortcut { - fn to_kbshortcut(&self) -> Result; -} - -impl ToKbShortcut for String { - fn to_kbshortcut(&self) -> Result { - if let Ok(code) = kb_code_from_str(&self) { - Ok(KbShortcut::Code(code)) - } else if self.len() == 1 { - Ok(KbShortcut::Key(KbKey::Character(self.clone()))) - } else { - Err(()) - } - } -} - fn kb_code_from_str(s: &str) -> Result { match s { "NumpadAdd" => Ok(Code::NumpadAdd), @@ -51,3 +37,62 @@ fn kb_code_from_str(s: &str) -> Result { _ => Err(()), } } + +impl fmt::Display for KbShortcut { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + KbShortcut::Key(key) => { + write!(f, "{}", key.to_string()) + } + KbShortcut::Code(code) => { + write!(f, "{}", code.to_string()) + } + } + } +} + +#[derive(Debug, Clone)] +struct ParseShortcutError; + +// Generation of an error is completely separate from how it is displayed. +// There's no need to be concerned about cluttering complex logic with the display style. +// +// Note that we don't store any extra info about the errors. This means we can't state +// which string failed to parse without modifying our types to carry that information. +impl fmt::Display for ParseShortcutError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Error parsing shortcut!") + } +} + +impl FromStr for KbShortcut { + type Err = ParseShortcutError; + + fn from_str(s: &str) -> Result { + if let Ok(code) = kb_code_from_str(s) { + Ok(KbShortcut::Code(code)) + } else if s.len() == 1 { + Ok(KbShortcut::Key(KbKey::Character(s.to_string()))) + } else { + Err(ParseShortcutError) + } + } +} + +struct ShortcutLens; + +impl Lens for ShortcutLens { + fn with V>(&self, data: &KbShortcut, f: F) -> V { + f(&data.to_string()) + } + + fn with_mut V>(&self, data: &mut KbShortcut, f: F) -> V { + let mut shortcut_as_string = &data.to_string(); + f(&mut shortcut_as_string) + } +} + +pub fn matches(key: KeyEvent, str: String) -> bool { + // Make KbShortcut from str, match key.code and key.key to the result and return if it matched + // on error return false? +} diff --git a/psst-gui/src/data/mod.rs b/psst-gui/src/data/mod.rs index 079ea776..49950371 100644 --- a/psst-gui/src/data/mod.rs +++ b/psst-gui/src/data/mod.rs @@ -25,9 +25,11 @@ use psst_core::session::SessionService; pub use crate::data::{ album::{Album, AlbumDetail, AlbumLink, AlbumType, Copyright, CopyrightType}, artist::{Artist, ArtistAlbums, ArtistDetail, ArtistLink, ArtistTracks}, - config::{AudioQuality, Authentication, Config, Preferences, PreferencesTab, Theme}, + config::{ + AudioQuality, Authentication, Config, KbShortcuts, Preferences, PreferencesTab, Theme, + }, ctx::Ctx, - kbshortcut::{KbShortcut, ToKbShortcut}, + kbshortcut::KbShortcut, nav::{Nav, SpotifyUrl}, playback::{ NowPlaying, Playback, PlaybackOrigin, PlaybackPayload, PlaybackState, QueueBehavior, @@ -53,6 +55,7 @@ pub struct AppState { pub route: Nav, pub history: Vector