mirror of
https://github.com/arabianq/pipewire-soundpad.git
synced 2026-04-28 06:21:23 +00:00
fix: hotkeys setting from pwsp-gui (#56)
* refactor: do not overwrite incorrect hotkeys config * fix: hotkeys not saved via pwsp-gui
This commit is contained in:
committed by
GitHub
parent
cb56cb3a04
commit
42c0170044
+19
-1
@@ -70,8 +70,10 @@ enum Actions {
|
|||||||
},
|
},
|
||||||
/// Play a sound by hotkey slot name
|
/// Play a sound by hotkey slot name
|
||||||
PlayHotkey { slot: String },
|
PlayHotkey { slot: String },
|
||||||
/// Remove a hotkey slot
|
/// Remove the hotkey slot
|
||||||
ClearHotkey { slot: String },
|
ClearHotkey { slot: String },
|
||||||
|
/// Clear the key chord for a hotkey slot
|
||||||
|
ClearHotkeyKey { slot: String },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Subcommand, Debug)]
|
#[derive(Subcommand, Debug)]
|
||||||
@@ -135,6 +137,12 @@ enum SetCommands {
|
|||||||
Hotkey { slot: String, file_path: PathBuf },
|
Hotkey { slot: String, file_path: PathBuf },
|
||||||
/// Set the key chord for a hotkey slot (e.g. "Ctrl+Alt+1")
|
/// Set the key chord for a hotkey slot (e.g. "Ctrl+Alt+1")
|
||||||
HotkeyKey { slot: String, key_chord: String },
|
HotkeyKey { slot: String, key_chord: String },
|
||||||
|
/// Atomically set the action and key chord for a hotkey slot
|
||||||
|
HotkeyActionAndKey {
|
||||||
|
slot: String,
|
||||||
|
action: String,
|
||||||
|
key_chord: String,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
@@ -158,6 +166,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
Actions::ToggleLoop { id } => Request::toggle_loop(id),
|
Actions::ToggleLoop { id } => Request::toggle_loop(id),
|
||||||
Actions::PlayHotkey { slot } => Request::play_hotkey(&slot),
|
Actions::PlayHotkey { slot } => Request::play_hotkey(&slot),
|
||||||
Actions::ClearHotkey { slot } => Request::clear_hotkey(&slot),
|
Actions::ClearHotkey { slot } => Request::clear_hotkey(&slot),
|
||||||
|
Actions::ClearHotkeyKey { slot } => Request::clear_hotkey_key(&slot),
|
||||||
},
|
},
|
||||||
Commands::Get { parameter } => match parameter {
|
Commands::Get { parameter } => match parameter {
|
||||||
GetCommands::IsPaused => Request::get_is_paused(),
|
GetCommands::IsPaused => Request::get_is_paused(),
|
||||||
@@ -183,6 +192,15 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
SetCommands::HotkeyKey { slot, key_chord } => {
|
SetCommands::HotkeyKey { slot, key_chord } => {
|
||||||
Request::set_hotkey_key(&slot, &key_chord)
|
Request::set_hotkey_key(&slot, &key_chord)
|
||||||
}
|
}
|
||||||
|
SetCommands::HotkeyActionAndKey {
|
||||||
|
slot,
|
||||||
|
action,
|
||||||
|
key_chord,
|
||||||
|
} => Request::set_hotkey_action_and_key(
|
||||||
|
&slot,
|
||||||
|
&serde_json::from_str::<Request>(&action)?,
|
||||||
|
&key_chord,
|
||||||
|
),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+8
-3
@@ -221,16 +221,21 @@ impl SoundpadGui {
|
|||||||
.to_string_lossy()
|
.to_string_lossy()
|
||||||
.to_string();
|
.to_string();
|
||||||
let action = Request::play(&file_path.to_string_lossy(), false);
|
let action = Request::play(&file_path.to_string_lossy(), false);
|
||||||
make_request_async(Request::set_hotkey_action(&slot_name, &action));
|
|
||||||
make_request_async(Request::set_hotkey_key(&slot_name, &chord));
|
make_request_async(Request::set_hotkey_action_and_key(
|
||||||
|
&slot_name, &action, &chord,
|
||||||
|
));
|
||||||
|
|
||||||
self.app_state
|
self.app_state
|
||||||
.hotkey_config
|
.hotkey_config
|
||||||
.set_slot(slot_name.clone(), action);
|
.set_slot(slot_name.clone(), action);
|
||||||
self.app_state
|
self.app_state
|
||||||
.hotkey_config
|
.hotkey_config
|
||||||
.set_key_chord(&slot_name, Some(chord));
|
.set_key_chord(&slot_name, Some(chord.clone()));
|
||||||
}
|
}
|
||||||
self.app_state.hotkey_capture_active = false;
|
self.app_state.hotkey_capture_active = false;
|
||||||
|
self.app_state.assigning_hotkey_slot = None;
|
||||||
|
self.app_state.assigning_hotkey_for_file = None;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
+66
-20
@@ -99,22 +99,28 @@ pub struct SetHotkeyCommand {
|
|||||||
pub file_path: Option<PathBuf>,
|
pub file_path: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct SetHotkeyActionCommand {
|
||||||
|
pub slot: Option<String>,
|
||||||
|
pub action: Option<Request>,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct SetHotkeyKeyCommand {
|
pub struct SetHotkeyKeyCommand {
|
||||||
pub slot: Option<String>,
|
pub slot: Option<String>,
|
||||||
pub key_chord: Option<String>,
|
pub key_chord: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ClearHotkeyCommand {
|
pub struct SetHotkeyActionAndKeyCommand {
|
||||||
pub slot: Option<String>,
|
pub slot: Option<String>,
|
||||||
|
pub action: Option<Request>,
|
||||||
|
pub key_chord: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PlayHotkeyCommand {
|
pub struct PlayHotkeyCommand {
|
||||||
pub slot: Option<String>,
|
pub slot: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SetHotkeyActionCommand {
|
pub struct ClearHotkeyCommand {
|
||||||
pub slot: Option<String>,
|
pub slot: Option<String>,
|
||||||
pub action: Option<Request>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ClearHotkeyKeyCommand {
|
pub struct ClearHotkeyKeyCommand {
|
||||||
@@ -553,6 +559,30 @@ impl Executable for SetHotkeyCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl Executable for SetHotkeyActionCommand {
|
||||||
|
async fn execute(&self) -> Response {
|
||||||
|
let Some(slot) = &self.slot else {
|
||||||
|
return Response::new(false, "Missing slot name");
|
||||||
|
};
|
||||||
|
let Some(action) = &self.action else {
|
||||||
|
return Response::new(false, "Missing or invalid action");
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut config = match HotkeyConfig::load() {
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(err) => return Response::new(false, format!("Failed to load hotkeys: {}", err)),
|
||||||
|
};
|
||||||
|
|
||||||
|
config.set_slot(slot.clone(), action.clone());
|
||||||
|
|
||||||
|
match config.save() {
|
||||||
|
Ok(_) => Response::new(true, format!("Hotkey slot '{}' set", slot)),
|
||||||
|
Err(err) => Response::new(false, format!("Failed to save hotkeys: {}", err)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Executable for SetHotkeyKeyCommand {
|
impl Executable for SetHotkeyKeyCommand {
|
||||||
async fn execute(&self) -> Response {
|
async fn execute(&self) -> Response {
|
||||||
@@ -583,24 +613,41 @@ impl Executable for SetHotkeyKeyCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Executable for ClearHotkeyCommand {
|
impl Executable for SetHotkeyActionAndKeyCommand {
|
||||||
async fn execute(&self) -> Response {
|
async fn execute(&self) -> Response {
|
||||||
let Some(slot) = &self.slot else {
|
let Some(slot) = &self.slot else {
|
||||||
return Response::new(false, "Missing slot name");
|
return Response::new(false, "Missing slot name");
|
||||||
};
|
};
|
||||||
|
let Some(action) = &self.action else {
|
||||||
|
return Response::new(false, "Missing or invalid action");
|
||||||
|
};
|
||||||
|
let Some(key_chord) = &self.key_chord else {
|
||||||
|
return Response::new(false, "Missing key chord");
|
||||||
|
};
|
||||||
|
|
||||||
let mut config = match HotkeyConfig::load() {
|
let mut config = match HotkeyConfig::load() {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(err) => return Response::new(false, format!("Failed to load hotkeys: {}", err)),
|
Err(err) => return Response::new(false, format!("Failed to load hotkeys: {}", err)),
|
||||||
};
|
};
|
||||||
|
|
||||||
if config.remove_slot(slot) {
|
// Set the action and then the key chord
|
||||||
match config.save() {
|
config.set_slot(slot.clone(), action.clone());
|
||||||
Ok(_) => Response::new(true, format!("Hotkey slot '{}' cleared", slot)),
|
if !config.set_key_chord(slot, Some(key_chord.clone())) {
|
||||||
Err(err) => Response::new(false, format!("Failed to save hotkeys: {}", err)),
|
return Response::new(
|
||||||
}
|
false,
|
||||||
} else {
|
format!("Slot '{}' not found after setting action", slot),
|
||||||
Response::new(false, format!("Slot '{}' not found", slot))
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
match config.save() {
|
||||||
|
Ok(_) => Response::new(
|
||||||
|
true,
|
||||||
|
format!(
|
||||||
|
"Hotkey slot '{}' set with action and key chord '{}'",
|
||||||
|
slot, key_chord
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Err(err) => Response::new(false, format!("Failed to save hotkeys: {}", err)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -632,25 +679,24 @@ impl Executable for PlayHotkeyCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Executable for SetHotkeyActionCommand {
|
impl Executable for ClearHotkeyCommand {
|
||||||
async fn execute(&self) -> Response {
|
async fn execute(&self) -> Response {
|
||||||
let Some(slot) = &self.slot else {
|
let Some(slot) = &self.slot else {
|
||||||
return Response::new(false, "Missing slot name");
|
return Response::new(false, "Missing slot name");
|
||||||
};
|
};
|
||||||
let Some(action) = &self.action else {
|
|
||||||
return Response::new(false, "Missing or invalid action");
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut config = match HotkeyConfig::load() {
|
let mut config = match HotkeyConfig::load() {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(err) => return Response::new(false, format!("Failed to load hotkeys: {}", err)),
|
Err(err) => return Response::new(false, format!("Failed to load hotkeys: {}", err)),
|
||||||
};
|
};
|
||||||
|
|
||||||
config.set_slot(slot.clone(), action.clone());
|
if config.remove_slot(slot) {
|
||||||
|
match config.save() {
|
||||||
match config.save() {
|
Ok(_) => Response::new(true, format!("Hotkey slot '{}' cleared", slot)),
|
||||||
Ok(_) => Response::new(true, format!("Hotkey slot '{}' set", slot)),
|
Err(err) => Response::new(false, format!("Failed to save hotkeys: {}", err)),
|
||||||
Err(err) => Response::new(false, format!("Failed to save hotkeys: {}", err)),
|
}
|
||||||
|
} else {
|
||||||
|
Response::new(false, format!("Slot '{}' not found", slot))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-2
@@ -98,7 +98,6 @@ impl GuiConfig {
|
|||||||
pub struct HotkeySlot {
|
pub struct HotkeySlot {
|
||||||
pub slot: String,
|
pub slot: String,
|
||||||
pub action: Request,
|
pub action: Request,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub key_chord: Option<String>,
|
pub key_chord: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,7 +120,7 @@ impl HotkeyConfig {
|
|||||||
let bytes = fs::read(&path)?;
|
let bytes = fs::read(&path)?;
|
||||||
match serde_json::from_slice::<HotkeyConfig>(&bytes) {
|
match serde_json::from_slice::<HotkeyConfig>(&bytes) {
|
||||||
Ok(config) => Ok(config),
|
Ok(config) => Ok(config),
|
||||||
Err(_) => Ok(HotkeyConfig::default()),
|
Err(e) => Err(e.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -208,6 +208,18 @@ impl Request {
|
|||||||
pub fn clear_hotkey_key(slot: &str) -> Self {
|
pub fn clear_hotkey_key(slot: &str) -> Self {
|
||||||
Request::new("clear_hotkey_key", vec![("slot", slot)])
|
Request::new("clear_hotkey_key", vec![("slot", slot)])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_hotkey_action_and_key(slot: &str, action: &Request, key_chord: &str) -> Self {
|
||||||
|
let action_json = serde_json::to_string(action).unwrap_or_default();
|
||||||
|
Request::new(
|
||||||
|
"set_hotkey_action_and_key",
|
||||||
|
vec![
|
||||||
|
("slot", slot),
|
||||||
|
("action", &action_json),
|
||||||
|
("key_chord", key_chord),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
|
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
|
||||||
|
|||||||
@@ -106,6 +106,19 @@ pub fn parse_command(request: &Request) -> Option<Box<dyn Executable + Send>> {
|
|||||||
let slot = request.args.get("slot").cloned();
|
let slot = request.args.get("slot").cloned();
|
||||||
Some(Box::new(ClearHotkeyKeyCommand { slot }))
|
Some(Box::new(ClearHotkeyKeyCommand { slot }))
|
||||||
}
|
}
|
||||||
|
"set_hotkey_action_and_key" => {
|
||||||
|
let slot = request.args.get("slot").cloned();
|
||||||
|
let action = request
|
||||||
|
.args
|
||||||
|
.get("action")
|
||||||
|
.and_then(|s| serde_json::from_str::<Request>(s).ok());
|
||||||
|
let key_chord = request.args.get("key_chord").cloned();
|
||||||
|
Some(Box::new(SetHotkeyActionAndKeyCommand {
|
||||||
|
slot,
|
||||||
|
action,
|
||||||
|
key_chord,
|
||||||
|
}))
|
||||||
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user