Skip to content

Commit

Permalink
Implemented mouse Click and Drag events
Browse files Browse the repository at this point in the history
  • Loading branch information
LeoRiether committed Feb 11, 2024
1 parent 8c96f94 commit 30c73aa
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 26 deletions.
1 change: 1 addition & 0 deletions tori/src/events/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub enum Action {
Play(String),
RefreshSongs,
RefreshPlaylists,
SelectAndMaybePlaySong(usize),
SelectSong(usize),
SelectPlaylist(usize),

Expand Down
2 changes: 1 addition & 1 deletion tori/src/rect_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ pub trait RectOps {

impl RectOps for Rect {
fn contains(&self, x: u16, y: u16) -> bool {
x >= self.left() && x <= self.right() && y >= self.top() && y <= self.bottom()
x >= self.left() && x < self.right() && y >= self.top() && y < self.bottom()
}

fn split_top(&self, n: u16) -> (Rect, Rect) {
Expand Down
1 change: 1 addition & 0 deletions tori/src/ui/eventful_widget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ where
/// An event a widget can receive
pub enum UIEvent {
Click(Rect),
Drag(Rect),
}

/// A widget that registers event listeners
Expand Down
53 changes: 36 additions & 17 deletions tori/src/ui/list.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::{ui::Scrollbar, events::Action};
use crate::{events::Action, ui::Scrollbar};

use super::{on, EventfulWidget, Listener, UIEvent};
use crossterm::event::Event;
use std::mem;
use tui::{
prelude::*,
Expand All @@ -19,7 +20,8 @@ pub struct List<'a, const C: usize> {
state: TableState,
items: Vec<[String; C]>,
help_message: String,
click_event: Option<Box<dyn Fn(usize) -> Action>>,
on_click: Option<Box<dyn Fn(usize) -> Action + Send + Sync + 'static>>,
on_drag: Option<Box<dyn Fn(usize) -> Action + Send + Sync + 'static>>,
}

impl<'a, const C: usize> EventfulWidget<Action> for List<'a, C> {
Expand Down Expand Up @@ -71,19 +73,31 @@ impl<'a, const C: usize> EventfulWidget<Action> for List<'a, C> {
}

// Event listeners
if let Some(click_event) = &self.click_event {
let offset = self.state.offset();
let height = std::cmp::max(area.height as usize - 2, items_len);
for i in offset..offset + height {
let event = click_event(i);
let rect = Rect {
x: area.x,
y: area.y + 1 + i as u16,
width: area.width - 2,
height: 1,
};
l.push(on(UIEvent::Click(rect), move |_| event.clone()));
}
// NOTE: please don't call `render()` more than once with the same `List`, it'll `take()`
// the click_event the first time and mess it up the second
let offset = self.state.offset();
let mut inner_area = area.inner(&Margin {
horizontal: 1,
vertical: 1,
});
inner_area.height = std::cmp::min(area.height - 2, items_len as u16);
if let Some(on_click) = self.on_click.take() {
l.push(on(UIEvent::Click(inner_area), move |ev| {
if let Event::Mouse(ev) = ev {
let index = ev.row as usize + offset - inner_area.y as usize;
return on_click(index);
}
Action::Rerender // no-op?
}));
}
if let Some(on_drag) = self.on_drag.take() {
l.push(on(UIEvent::Drag(inner_area), move |ev| {
if let Event::Mouse(ev) = ev {
let index = ev.row as usize + offset - inner_area.y as usize;
return on_drag(index);
}
Action::Rerender // no-op?
}));
}
}
}
Expand Down Expand Up @@ -133,8 +147,13 @@ impl<'a, const C: usize> List<'a, C> {
self
}

pub fn click_event(mut self, action: impl Fn(usize) -> Action + 'static) -> Self {
self.click_event = Some(Box::new(action));
pub fn on_click(mut self, action: impl Fn(usize) -> Action + Send + Sync + 'static) -> Self {
self.on_click = Some(Box::new(action));
self
}

pub fn on_drag(mut self, action: impl Fn(usize) -> Action + Send + Sync + 'static) -> Self {
self.on_drag = Some(Box::new(action));
self
}
}
6 changes: 4 additions & 2 deletions tori/src/ui/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@ fn playlists_pane(
.border_style(border_style)
.borders(Borders::ALL & !Borders::RIGHT)
.highlight_style(Style::default().bg(Color::LightBlue).fg(Color::Black))
.click_event(Action::SelectPlaylist);
.on_click(Action::SelectPlaylist)
.on_drag(Action::SelectPlaylist);
list.render(area, buf, l);
screen.shown_playlists.state = list.get_state();
}
Expand Down Expand Up @@ -166,7 +167,8 @@ fn songs_pane(
.borders(Borders::ALL)
.highlight_style(Style::default().bg(Color::Yellow).fg(Color::Black))
.highlight_symbol(" ◇")
.click_event(Action::SelectSong);
.on_click(Action::SelectAndMaybePlaySong)
.on_drag(Action::SelectSong);
list.render(area, buf, l);
screen.shown_songs.state = list.get_state();
}
31 changes: 28 additions & 3 deletions tori/src/update/browse_screen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,14 @@ pub fn browse_screen_action(
Action::Command(cmd) => return browse_screen_command(state, screen, tx, cmd),

ScrollDown => match &screen.focus {
Focus::Playlists => screen.shown_playlists.select_next(),
Focus::Songs => screen.shown_songs.select_next(),
Focus::Playlists => {
screen.shown_playlists.select_next();
screen.refresh_playlists()?;
}
Focus::Songs => {
screen.shown_songs.select_next();
screen.refresh_songs()?;
}
Focus::PlaylistsFilter(_) | Focus::SongsFilter(_) => {}
},
ScrollUp => match &screen.focus {
Expand All @@ -50,10 +56,29 @@ pub fn browse_screen_action(
}
RefreshSongs => screen.refresh_songs()?,
RefreshPlaylists => screen.refresh_playlists()?,
SelectSong(i) => screen.shown_songs.select(Some(i)),
SelectAndMaybePlaySong(i) => {
if screen.shown_songs.selected_item() == Some(i) {
// Double click => Play song
if let Some(song) = screen.selected_song() {
state.player.play(&song.path)?;
}
} else {
// Select i-th song
return Ok(Some(Action::SelectSong(i)));
}
}
SelectSong(i) => {
screen.shown_songs.select(Some(i));
if let Focus::Playlists | Focus::PlaylistsFilter(_) = screen.focus {
screen.focus = Focus::Songs;
}
}
SelectPlaylist(i) => {
screen.shown_playlists.select(Some(i));
screen.refresh_songs()?;
if let Focus::Songs | Focus::SongsFilter(_) = screen.focus {
screen.focus = Focus::Playlists;
}
}

_ => {}
Expand Down
29 changes: 26 additions & 3 deletions tori/src/update/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ use crate::{
input::InputResponse,
m3u::playlist_management,
player::Player,
rect_ops::RectOps,
state::{browse_screen::Focus, Screen, State},
ui::UIEvent,
};
use crossterm::event::{Event as TermEvent, KeyEvent, KeyEventKind, MouseEventKind};

Expand All @@ -35,9 +37,29 @@ pub fn handle_event(state: &mut State<'_>, tx: Tx, ev: TermEvent) -> Result<Opti
},
},
TermEvent::Mouse(mouse) => match mouse.kind {
MouseEventKind::Down(_) => todo!("Clicks are yet to be implemented!"),
MouseEventKind::Up(_) => todo!("Clickups are yet to be implemented!"),
MouseEventKind::Drag(_) => todo!("Dragging is yet to be implemented!"),
MouseEventKind::Down(_) => {
for listener in &state.listeners {
if let UIEvent::Click(rect) = listener.event {
if rect.contains(mouse.column, mouse.row) {
let ev = ev.clone();
tx.send((listener.emitter)(ev))?;
}
}
}
None
}
MouseEventKind::Drag(_) => {
for listener in &state.listeners {
if let UIEvent::Drag(rect) = listener.event {
if rect.contains(mouse.column, mouse.row) {
let ev = ev.clone();
tx.send((listener.emitter)(ev))?;
}
}
}
None
}
MouseEventKind::Up(_) => None,
MouseEventKind::ScrollDown => Some(Action::ScrollDown),
MouseEventKind::ScrollUp => Some(Action::ScrollUp),
MouseEventKind::Moved | MouseEventKind::ScrollLeft | MouseEventKind::ScrollRight => {
Expand Down Expand Up @@ -67,6 +89,7 @@ pub fn update(state: &mut State<'_>, tx: Tx, act: Action) -> Result<Option<Actio
| SongAdded { .. }
| RefreshSongs
| RefreshPlaylists
| SelectAndMaybePlaySong(_)
| SelectSong(_)
| SelectPlaylist(_) => return screen_action(state, tx, act),

Expand Down

0 comments on commit 30c73aa

Please sign in to comment.