Krolyxon
Commit cf9c69a

rmptuisrcscreensmod.rs

164 lines · 4.4 KB · Rust

1//! The screens. Each is a self-contained component that renders itself,
2//! interprets actions, and reacts to server events.
3
4mod browser;
5mod library;
6mod lyrics;
7mod playlists;
8mod queue;
9mod tag_editor;
10mod visualizer;
11
12use std::sync::Arc;
13
14use crossterm::event::MouseEvent;
15use ratatui::{Frame, layout::Rect};
16use tokio::sync::watch;
17
18pub use browser::BrowserScreen;
19pub use library::LibraryScreen;
20pub use lyrics::{LyricsScreen, draw_lyrics};
21pub use playlists::PlaylistsScreen;
22pub use queue::QueueScreen;
23pub use tag_editor::TagEditorScreen;
24pub use visualizer::VisualizerScreen;
25
26use crate::{
27 action::{Action, ScreenId},
28 config::{Config, Theme},
29 lyrics::LyricsState,
30 mpd::{MpdEvent, MpdHandle},
31 popups::Popup,
32 state::MpdState,
33 tags::{TagField, TagWrite},
34 visualizer::Spectrum,
35};
36
37/// What a screen or popup may read, plus a queue of effects for the app.
38pub struct Ctx<'a> {
39 pub config: &'a Config,
40 pub mpd: &'a MpdState,
41 pub client: &'a MpdHandle,
42 pub lyrics: &'a LyricsState,
43 pub effects: Vec<Effect>,
44}
45
46impl<'a> Ctx<'a> {
47 pub fn new(
48 config: &'a Config,
49 mpd: &'a MpdState,
50 client: &'a MpdHandle,
51 lyrics: &'a LyricsState,
52 ) -> Self {
53 Self {
54 config,
55 mpd,
56 client,
57 lyrics,
58 effects: Vec::new(),
59 }
60 }
61
62 pub fn theme(&self) -> &'a Theme {
63 &self.config.theme
64 }
65
66 pub fn notify(&mut self, text: impl Into<String>) {
67 self.effects.push(Effect::Notify {
68 text: text.into(),
69 error: false,
70 });
71 }
72
73 pub fn error(&mut self, text: impl Into<String>) {
74 self.effects.push(Effect::Notify {
75 text: text.into(),
76 error: true,
77 });
78 }
79
80 pub fn popup(&mut self, popup: impl Popup + 'static) {
81 self.effects.push(Effect::Popup(Box::new(popup)));
82 }
83}
84
85/// Requests from screens and popups that only the app can fulfil.
86pub enum Effect {
87 Notify { text: String, error: bool },
88 Popup(Box<dyn Popup>),
89 Message(ScreenId, ScreenMessage),
90 WriteTags(Vec<TagWrite>),
91 ReloadLyrics,
92}
93
94/// Data a popup hands back to a screen.
95#[derive(Clone, Debug)]
96pub enum ScreenMessage {
97 TagValue { field: TagField, value: String },
98}
99
100pub trait Screen {
101 fn draw(&mut self, frame: &mut Frame, area: Rect, ctx: &Ctx);
102
103 /// Returns whether the action was handled.
104 fn handle_action(&mut self, action: Action, ctx: &mut Ctx) -> bool;
105
106 /// Called for every server event, whether or not the screen is visible.
107 fn on_mpd(&mut self, _event: &MpdEvent, _ctx: &mut Ctx) {}
108
109 /// Called when the screen becomes visible.
110 fn on_enter(&mut self, _ctx: &mut Ctx) {}
111
112 fn on_message(&mut self, _message: ScreenMessage, _ctx: &mut Ctx) {}
113
114 fn on_mouse(&mut self, _event: MouseEvent, _ctx: &mut Ctx) {}
115
116 /// Searches the focused list. Returns `false` if there is nothing to
117 /// search on this screen.
118 fn search(&mut self, _query: &str, _ctx: &Ctx) -> bool {
119 false
120 }
121}
122
123/// All screens, owned by the app.
124pub struct Screens {
125 pub queue: QueueScreen,
126 pub browser: BrowserScreen,
127 pub playlists: PlaylistsScreen,
128 pub library: LibraryScreen,
129 pub tag_editor: TagEditorScreen,
130 pub visualizer: VisualizerScreen,
131 pub lyrics: LyricsScreen,
132}
133
134impl Screens {
135 pub fn new(config: &Config, spectrum: watch::Receiver<Arc<Spectrum>>) -> Self {
136 Self {
137 queue: QueueScreen::new(config),
138 browser: BrowserScreen::default(),
139 playlists: PlaylistsScreen::default(),
140 library: LibraryScreen::default(),
141 tag_editor: TagEditorScreen::default(),
142 visualizer: VisualizerScreen::new(spectrum),
143 lyrics: LyricsScreen::default(),
144 }
145 }
146
147 pub fn get_mut(&mut self, id: ScreenId) -> &mut dyn Screen {
148 match id {
149 ScreenId::Queue => &mut self.queue,
150 ScreenId::Browser => &mut self.browser,
151 ScreenId::Playlists => &mut self.playlists,
152 ScreenId::Library => &mut self.library,
153 ScreenId::TagEditor => &mut self.tag_editor,
154 ScreenId::Visualizer => &mut self.visualizer,
155 ScreenId::Lyrics => &mut self.lyrics,
156 }
157 }
158
159 pub fn for_each(&mut self, mut f: impl FnMut(&mut dyn Screen)) {
160 for &id in ScreenId::ALL {
161 f(self.get_mut(id));
162 }
163 }
164}