1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
pub mod browse;
pub mod doc_meta;
pub mod editor;
pub mod overlay_host;
pub mod panel_set;
pub mod preferences;
pub mod start;
use async_trait::async_trait;
use ratatui::Frame;
use kimun_core::nfs::VaultPath;
use crate::components::event_state::EventState;
use crate::components::events::{AppEvent, AppTx, InputEvent};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ScreenKind {
Start,
Browse,
Editor,
Preferences,
}
#[async_trait]
pub trait AppScreen: Send {
/// Called once when the screen mounts. Send `AppEvent`s through `tx` to
/// trigger navigation (e.g. `StartScreen` checking whether a vault exists).
async fn on_enter(&mut self, _tx: &AppTx) {}
/// Handle an input event. Send events through `tx` for navigation or quit.
/// Returns whether this screen consumed the event.
fn handle_input(&mut self, event: &InputEvent, tx: &AppTx) -> EventState;
fn render(&mut self, f: &mut Frame);
/// Handle an application-level event owned by this screen. Events the
/// screen does not recognize are silently ignored. The default
/// implementation handles nothing.
async fn handle_app_message(&mut self, _msg: AppEvent, _tx: &AppTx) {}
/// Try to open `path` within this screen (e.g. load a note into the
/// buffer, or navigate a sidebar). Return `Some(path)` if the screen
/// does not handle it, in which case the main loop switches to an
/// appropriate screen. Default: not handled.
/// `emphasis` carries the originating query's needles when the open came
/// from a query result; only the editor screen uses it (spec §5.1) — it
/// is dropped when the open reroutes to a screen switch.
async fn try_open_path(
&mut self,
path: VaultPath,
_emphasis: Option<Vec<String>>,
_tx: &AppTx,
) -> Option<VaultPath> {
Some(path)
}
fn get_kind(&self) -> ScreenKind;
/// Called once just before the screen is removed from the app (quit or screen transition).
/// Default implementation is a no-op.
async fn on_exit(&mut self, _tx: &AppTx) {}
}
#[cfg(test)]
mod tests {
use tokio::sync::mpsc::unbounded_channel;
use std::sync::{Arc, RwLock};
use super::*;
use crate::app_screen::preferences::PreferencesScreen;
use crate::settings::AppSettings;
fn shared_defaults() -> crate::settings::SharedSettings {
Arc::new(RwLock::new(AppSettings::default()))
}
#[tokio::test]
async fn on_exit_default_is_noop() {
let (tx, _rx) = unbounded_channel::<AppEvent>();
let mut screen = PreferencesScreen::new(shared_defaults());
screen.on_exit(&tx).await; // must compile and not panic
}
}