Skip to content

Commit

Permalink
feat: target configuration for action keymaps (#539)
Browse files Browse the repository at this point in the history
  • Loading branch information
juliamertz authored Aug 18, 2024
1 parent 98d8123 commit 463a16c
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 38 deletions.
5 changes: 4 additions & 1 deletion docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -286,16 +286,19 @@ key_sequence = "q"

## Actions

Actions are located in the same `keymap.toml` file as keymaps. An action can be triggered by a key sequence that is not bound to any command. Once the mapped key sequence is pressed, the corresponding action will be triggered **on the currently selected item**. For example,
Actions are located in the same `keymap.toml` file as keymaps. An action can be triggered by a key sequence that is not bound to any command. Once the mapped key sequence is pressed, the corresponding action will be triggered. By default actions will act upon the currently selected item, you can change this behaviour by setting the `target` field for a keymap to either `PlayingTrack` or `SelectedItem`.
a list of actions can be found [here](../README.md#actions).

For example,

```toml
[[actions]]
action = "GoToArtist"
key_sequence = "g A"
[[actions]]
action = "GoToAlbum"
key_sequence = "g B"
target = "PlayingTrack"
[[actions]]
action="ToggleLiked"
key_sequence="C-l"
Expand Down
9 changes: 8 additions & 1 deletion spotify_player/src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,16 @@ pub enum ActionContext {
PlaylistFolder(PlaylistFolder),
}

#[derive(Debug, PartialEq, Clone, Deserialize, Default, Copy)]
pub enum ActionTarget {
PlayingTrack,
#[default]
SelectedItem,
}

pub enum CommandOrAction {
Command(Command),
Action(Action),
Action(Action, ActionTarget),
}

impl From<Track> for ActionContext {
Expand Down
15 changes: 10 additions & 5 deletions spotify_player/src/config/keymap.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{
command::{Action, Command, CommandOrAction},
command::{Action, ActionTarget, Command, CommandOrAction},
key::{Key, KeySequence},
};
use anyhow::Result;
Expand All @@ -25,6 +25,8 @@ pub struct Keymap {
/// A keymap that triggers an `Action` when a key sequence is pressed
pub struct ActionMap {
pub key_sequence: KeySequence,
#[serde(default)]
pub target: ActionTarget,
pub action: Action,
}

Expand Down Expand Up @@ -401,11 +403,14 @@ impl KeymapConfig {
}

/// finds an action from a mapped key sequence
pub fn find_action_from_key_sequence(&self, key_sequence: &KeySequence) -> Option<Action> {
pub fn find_action_from_key_sequence(
&self,
key_sequence: &KeySequence,
) -> Option<(Action, ActionTarget)> {
self.actions
.iter()
.find(|&action| action.key_sequence == *key_sequence)
.map(|action| action.action)
.map(|action| (action.action, action.target))
}

/// finds a command or action from a mapped key sequence
Expand All @@ -416,8 +421,8 @@ impl KeymapConfig {
if let Some(command) = self.find_command_from_key_sequence(key_sequence) {
return Some(CommandOrAction::Command(command));
}
if let Some(action) = self.find_action_from_key_sequence(key_sequence) {
return Some(CommandOrAction::Action(action));
if let Some((action, target)) = self.find_action_from_key_sequence(key_sequence) {
return Some(CommandOrAction::Action(action, target));
}
None
}
Expand Down
92 changes: 79 additions & 13 deletions spotify_player/src/event/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use crate::{
client::{ClientRequest, PlayerRequest},
command::{self, construct_artist_actions, Action, ActionContext, Command},
command::{
self, construct_artist_actions, Action, ActionContext, ActionTarget, Command,
CommandOrAction,
},
config,
key::{Key, KeySequence},
state::*,
Expand Down Expand Up @@ -103,10 +106,15 @@ fn handle_key_event(
}
};

// if the key sequence is not handled, let the global command handler handle it
// if the key sequence is not handled, let the global handler handle it
let handled = if !handled {
match keymap_config.find_command_from_key_sequence(&key_sequence) {
Some(command) => handle_global_command(command, client_pub, state, &mut ui)?,
match keymap_config.find_command_or_action_from_key_sequence(&key_sequence) {
Some(CommandOrAction::Action(action, target)) => {
handle_global_action(action, target, client_pub, state, &mut ui)?
}
Some(CommandOrAction::Command(command)) => {
handle_global_command(command, client_pub, state, &mut ui)?
}
None => false,
}
} else {
Expand All @@ -129,7 +137,7 @@ pub fn handle_action_in_context(
client_pub: &flume::Sender<ClientRequest>,
data: &DataReadGuard,
ui: &mut UIStateGuard,
) -> Result<()> {
) -> Result<bool> {
match context {
ActionContext::Track(track) => match action {
Action::GoToAlbum => {
Expand All @@ -142,19 +150,24 @@ pub fn handle_action_in_context(
context_page_type: ContextPageType::Browsing(context_id),
state: None,
});
return Ok(true);
}
Ok(false)
}
Action::GoToArtist => {
handle_go_to_artist(track.artists, ui);
Ok(true)
}
Action::AddToQueue => {
client_pub.send(ClientRequest::AddTrackToQueue(track.id))?;
ui.popup = None;
Ok(true)
}
Action::CopyLink => {
let track_url = format!("https://open.spotify.com/track/{}", track.id.id());
execute_copy_command(track_url)?;
ui.popup = None;
Ok(true)
}
Action::AddToPlaylist => {
client_pub.send(ClientRequest::GetUserPlaylists)?;
Expand All @@ -165,6 +178,7 @@ pub fn handle_action_in_context(
},
ListState::default(),
));
Ok(true)
}
Action::ToggleLiked => {
if data.user_data.is_liked_track(&track) {
Expand All @@ -173,14 +187,17 @@ pub fn handle_action_in_context(
client_pub.send(ClientRequest::AddToLibrary(Item::Track(track)))?;
}
ui.popup = None;
Ok(true)
}
Action::AddToLiked => {
client_pub.send(ClientRequest::AddToLibrary(Item::Track(track)))?;
ui.popup = None;
Ok(true)
}
Action::DeleteFromLiked => {
client_pub.send(ClientRequest::DeleteFromLibrary(ItemId::Track(track.id)))?;
ui.popup = None;
Ok(true)
}
Action::GoToRadio => {
let uri = track.id.uri();
Expand All @@ -190,8 +207,12 @@ pub fn handle_action_in_context(
seed_uri: uri,
seed_name: name,
})?;
Ok(true)
}
Action::ShowActionsOnArtist => {
handle_show_actions_on_artist(track.artists, data, ui);
Ok(true)
}
Action::ShowActionsOnArtist => handle_show_actions_on_artist(track.artists, data, ui),
Action::ShowActionsOnAlbum => {
if let Some(album) = track.album {
let context = ActionContext::Album(album.clone());
Expand All @@ -202,7 +223,9 @@ pub fn handle_action_in_context(
)),
ListState::default(),
));
return Ok(true);
}
Ok(false)
}
Action::DeleteFromPlaylist => {
if let PageState::Context {
Expand All @@ -216,12 +239,14 @@ pub fn handle_action_in_context(
))?;
}
ui.popup = None;
Ok(true)
}
_ => {}
_ => Ok(false),
},
ActionContext::Album(album) => match action {
Action::GoToArtist => {
handle_go_to_artist(album.artists, ui);
Ok(true)
}
Action::GoToRadio => {
let uri = album.id.uri();
Expand All @@ -231,42 +256,51 @@ pub fn handle_action_in_context(
seed_uri: uri,
seed_name: name,
})?;
Ok(true)
}
Action::ShowActionsOnArtist => {
handle_show_actions_on_artist(album.artists, data, ui);
Ok(true)
}
Action::AddToLibrary => {
client_pub.send(ClientRequest::AddToLibrary(Item::Album(album)))?;
ui.popup = None;
Ok(true)
}
Action::DeleteFromLibrary => {
client_pub.send(ClientRequest::DeleteFromLibrary(ItemId::Album(album.id)))?;
ui.popup = None;
Ok(true)
}
Action::CopyLink => {
let album_url = format!("https://open.spotify.com/album/{}", album.id.id());
execute_copy_command(album_url)?;
ui.popup = None;
Ok(true)
}
Action::AddToQueue => {
client_pub.send(ClientRequest::AddAlbumToQueue(album.id))?;
ui.popup = None;
Ok(true)
}
_ => {}
_ => Ok(false),
},
ActionContext::Artist(artist) => match action {
Action::Follow => {
client_pub.send(ClientRequest::AddToLibrary(Item::Artist(artist)))?;
ui.popup = None;
Ok(true)
}
Action::Unfollow => {
client_pub.send(ClientRequest::DeleteFromLibrary(ItemId::Artist(artist.id)))?;
ui.popup = None;
Ok(true)
}
Action::CopyLink => {
let artist_url = format!("https://open.spotify.com/artist/{}", artist.id.id());
execute_copy_command(artist_url)?;
ui.popup = None;
Ok(true)
}
Action::GoToRadio => {
let uri = artist.id.uri();
Expand All @@ -276,13 +310,15 @@ pub fn handle_action_in_context(
seed_uri: uri,
seed_name: name,
})?;
Ok(true)
}
_ => {}
_ => Ok(false),
},
ActionContext::Playlist(playlist) => match action {
Action::AddToLibrary => {
client_pub.send(ClientRequest::AddToLibrary(Item::Playlist(playlist)))?;
ui.popup = None;
Ok(true)
}
Action::GoToRadio => {
let uri = playlist.id.uri();
Expand All @@ -292,26 +328,27 @@ pub fn handle_action_in_context(
seed_uri: uri,
seed_name: name,
})?;
Ok(true)
}
Action::CopyLink => {
let playlist_url =
format!("https://open.spotify.com/playlist/{}", playlist.id.id());
execute_copy_command(playlist_url)?;
ui.popup = None;
Ok(true)
}
Action::DeleteFromLibrary => {
client_pub.send(ClientRequest::DeleteFromLibrary(ItemId::Playlist(
playlist.id,
)))?;
ui.popup = None;
Ok(true)
}
_ => {}
_ => Ok(false),
},
// TODO: support actions for playlist folders
ActionContext::PlaylistFolder(_) => {}
ActionContext::PlaylistFolder(_) => Ok(false),
}

Ok(())
}

fn handle_go_to_artist(artists: Vec<Artist>, ui: &mut UIStateGuard) {
Expand Down Expand Up @@ -351,6 +388,35 @@ fn handle_show_actions_on_artist(
}
}

/// Handle a global action, currently this is only used to target
/// the currently playing song instead of the selection.
fn handle_global_action(
action: Action,
target: ActionTarget,
client_pub: &flume::Sender<ClientRequest>,
state: &SharedState,
ui: &mut UIStateGuard,
) -> Result<bool> {
if target == ActionTarget::PlayingTrack {
let player = state.player.read();
let data = state.data.read();

if let Some(currently_playing) = player.current_playing_track() {
if let Some(track) = Track::try_from_full_track(currently_playing.clone()) {
return handle_action_in_context(
action,
ActionContext::Track(track),
client_pub,
&data,
ui,
);
}
}
};

Ok(false)
}

/// Handle a global command that is not specific to any page/popup
fn handle_global_command(
command: Command,
Expand Down
Loading

0 comments on commit 463a16c

Please sign in to comment.