My personal-knowledge-system, with deeply integrated task tracking and long term goal planning capabilities.

feat/tui: Component trait #6

closed opened by suri.codes targeting main from tui
Labels

None yet.

assignee

None yet.

Participants 1
AT URI
at://did:plc:nxdjlcnomw44wvso6myag2e6/sh.tangled.repo.pull/3mgl5nipbjj22
+154 -24
Diff #0
+2
.harper-dictionary.txt
··· 1 + Implementers 1 2 Multi-Device 2 3 PKM 4 + clippy 3 5 eyre
+5 -5
flake.lock
··· 22 22 }, 23 23 "nixpkgs": { 24 24 "locked": { 25 - "lastModified": 1772542754, 26 - "narHash": "sha256-WGV2hy+VIeQsYXpsLjdr4GvHv5eECMISX1zKLTedhdg=", 27 - "rev": "8c809a146a140c5c8806f13399592dbcb1bb5dc4", 28 - "revCount": 957538, 25 + "lastModified": 1772624091, 26 + "narHash": "sha256-QKyJ0QGWBn6r0invrMAK8dmJoBYWoOWy7lN+UHzW1jc=", 27 + "rev": "80bdc1e5ce51f56b19791b52b2901187931f5353", 28 + "revCount": 958232, 29 29 "type": "tarball", 30 - "url": "https://api.flakehub.com/f/pinned/NixOS/nixpkgs/0.1.957538%2Brev-8c809a146a140c5c8806f13399592dbcb1bb5dc4/019cb563-81c1-72cf-aec2-46679a968011/source.tar.gz" 30 + "url": "https://api.flakehub.com/f/pinned/NixOS/nixpkgs/0.1.958232%2Brev-80bdc1e5ce51f56b19791b52b2901187931f5353/019cbcff-0413-7b59-87be-b2cea58a4043/source.tar.gz" 31 31 }, 32 32 "original": { 33 33 "type": "tarball",
+120
src/components/mod.rs
··· 1 + use crossterm::event::{KeyEvent, MouseEvent}; 2 + use ratatui::{ 3 + Frame, 4 + layout::{Rect, Size}, 5 + }; 6 + use tokio::sync::mpsc::UnboundedSender; 7 + 8 + use crate::{config::Config, signal::Signal, tui::Event}; 9 + 10 + /// `Component` is a trait that represents a visual and interactive element of the user interface. 11 + /// 12 + /// Implementers of this trait can be registered with the main application loop and will be able to 13 + /// receive events, update state, and be rendered on the screen. 14 + #[expect(dead_code)] 15 + pub trait Component { 16 + /// Register a signal handler that can send signals for processing if necessary. 17 + /// 18 + /// # Arguments 19 + /// 20 + /// * `tx` - An unbounded sender that can send signals. 21 + /// 22 + /// # Returns 23 + /// 24 + /// * [`color_eyre::Result<()>`] - An Ok result or an error. 25 + fn register_signal_handler(&mut self, tx: UnboundedSender<Signal>) -> color_eyre::Result<()> { 26 + let _ = tx; // to appease clippy 27 + Ok(()) 28 + } 29 + /// Register a configuration handler that provides configuration settings if necessary. 30 + /// 31 + /// # Arguments 32 + /// 33 + /// * `config` - Configuration settings. 34 + /// 35 + /// # Returns 36 + /// 37 + /// * [`color_eyre::Result<()>`] - An Ok result or an error. 38 + fn register_config_handler(&mut self, config: Config) -> color_eyre::Result<()> { 39 + let _ = config; // to appease clippy 40 + Ok(()) 41 + } 42 + /// Initialize the component with a specified area if necessary. 43 + /// 44 + /// # Arguments 45 + /// 46 + /// * `area` - Rectangular area to initialize the component within. 47 + /// 48 + /// # Returns 49 + /// 50 + /// * [`color_eyre::Result<()>`] - An Ok result or an error. 51 + fn init(&mut self, area: Size) -> color_eyre::Result<()> { 52 + let _ = area; // to appease clippy 53 + Ok(()) 54 + } 55 + /// Handle incoming events and produce signals if necessary. 56 + /// 57 + /// # Arguments 58 + /// 59 + /// * `event` - An optional event to be processed. 60 + /// 61 + /// # Returns 62 + /// 63 + /// * [`color_eyre::Result<Option<signal>>`] - A signal to be processed or none. 64 + fn handle_events(&mut self, event: Option<Event>) -> color_eyre::Result<Option<Signal>> { 65 + let signal = match event { 66 + Some(Event::Key(key_event)) => self.handle_key_event(key_event)?, 67 + Some(Event::Mouse(mouse_event)) => self.handle_mouse_event(mouse_event)?, 68 + _ => None, 69 + }; 70 + Ok(signal) 71 + } 72 + /// Handle key events and produce signals if necessary. 73 + /// 74 + /// # Arguments 75 + /// 76 + /// * `key` - A key event to be processed. 77 + /// 78 + /// # Returns 79 + /// 80 + /// * [`color_eyre::Result<Option<signal>>`] - A signal to be processed or none. 81 + fn handle_key_event(&mut self, key: KeyEvent) -> color_eyre::Result<Option<Signal>> { 82 + let _ = key; // to appease clippy 83 + Ok(None) 84 + } 85 + /// Handle mouse events and produce signals if necessary. 86 + /// 87 + /// # Arguments 88 + /// 89 + /// * `mouse` - A mouse event to be processed. 90 + /// 91 + /// # Returns 92 + /// 93 + /// * [`color_eyre::Result<Option<signal>>`] - A signal to be processed or none. 94 + fn handle_mouse_event(&mut self, mouse: MouseEvent) -> color_eyre::Result<Option<Signal>> { 95 + let _ = mouse; // to appease clippy 96 + Ok(None) 97 + } 98 + /// Update the state of the component based on a received signal. (REQUIRED) 99 + /// 100 + /// # Arguments 101 + /// 102 + /// * `signal` - A signal that may modify the state of the component. 103 + /// 104 + /// # Returns 105 + /// 106 + /// * [`color_eyre::Result<Option<signal>>`] - A signal to be processed or none. 107 + fn update(&mut self, signal: Signal) -> color_eyre::Result<Option<Signal>>; 108 + 109 + /// Render the component on the screen. (REQUIRED) 110 + /// 111 + /// # Arguments 112 + /// 113 + /// * `f` - A frame used for rendering. 114 + /// * `area` - The area in which the component should be drawn. 115 + /// 116 + /// # Returns 117 + /// 118 + /// * [`color_eyre::Result<()>`] - An Ok result or an error. 119 + fn draw(&mut self, frame: &mut Frame, area: Rect) -> color_eyre::Result<()>; 120 + }
+19 -16
src/config.rs
··· 1 1 use directories::ProjectDirs; 2 2 use lazy_static::lazy_static; 3 3 use serde::Deserialize; 4 - use std::{env, path::PathBuf}; 4 + use std::{env, path::PathBuf, sync::LazyLock}; 5 5 6 - lazy_static! { 7 - /// Filaments 8 - pub static ref PROJECT_NAME: String = env!("CARGO_CRATE_NAME").to_uppercase().to_string(); 9 - /// Data folder override if user has manually set FILAMENTS_DATA to a directory. 10 - pub static ref DATA_FOLDER: Option<PathBuf> = 11 - env::var(format!("{}_DATA", PROJECT_NAME.clone())) 12 - .ok() 13 - .map(PathBuf::from); 14 - /// Config folder override if user has manually set FILAMENTS_CONFIG to a directory. 15 - pub static ref CONFIG_FOLDER: Option<PathBuf> = 16 - env::var(format!("{}_CONFIG", PROJECT_NAME.clone())) 17 - .ok() 18 - .map(PathBuf::from); 19 - } 6 + // #[expect(dead_code)] 7 + static PROJECT_NAME: LazyLock<String> = LazyLock::new(|| env!("CARGO_CRATE_NAME").to_uppercase()); 8 + 9 + #[expect(dead_code)] 10 + static DATA_FOLDER: LazyLock<Option<PathBuf>> = LazyLock::new(|| { 11 + env::var(format!("{}_DATA", PROJECT_NAME.clone())) 12 + .ok() 13 + .map(PathBuf::from) 14 + }); 15 + #[expect(dead_code)] 16 + static CONFIG_FOLDER: LazyLock<Option<PathBuf>> = LazyLock::new(|| { 17 + env::var(format!("{}_CONFIG", PROJECT_NAME.clone())) 18 + .ok() 19 + .map(PathBuf::from) 20 + }); 20 21 21 22 /// The App Config and Data locations. 22 23 #[derive(Clone, Debug, Deserialize, Default)] 23 - 24 + #[expect(dead_code)] 24 25 pub struct AppDirs { 25 26 #[serde(default)] 26 27 pub data_dir: PathBuf, ··· 29 30 } 30 31 31 32 /// Configuration for the App 33 + #[expect(dead_code)] 32 34 pub struct Config { 33 35 pub app_dirs: AppDirs, // pub data_dir: PathBuf, 34 36 // pub keybindings: KeyBindings, ··· 36 38 // pub styles: Styles, 37 39 } 38 40 41 + #[expect(dead_code)] 39 42 impl Config { 40 43 pub fn new() -> Self { 41 44 todo!()
+1 -1
src/errors.rs
··· 3 3 /// Additionally the panic handler prints different information 4 4 /// based on debug / release builds. 5 5 pub fn init() -> color_eyre::Result<()> { 6 - let (panic_hook, eyre_hook) = color_eyre::config::HookBuilder::default() 6 + let (_, eyre_hook) = color_eyre::config::HookBuilder::default() 7 7 .panic_section(format!( 8 8 "This is a bug. Please report it at {}", 9 9 env!("CARGO_PKG_REPOSITORY")
+1
src/main.rs
··· 2 2 //! My (suri.codes) personal-knowledge-system, with deeply integrated task tracking and long term goal planning capabilities. 3 3 //! 4 4 5 + mod components; 5 6 mod config; 6 7 mod errors; 7 8 mod signal;
+6 -2
src/tui.rs
··· 13 13 }, 14 14 terminal::{EnterAlternateScreen, LeaveAlternateScreen, enable_raw_mode}, 15 15 }; 16 + use futures::{FutureExt as _, StreamExt as _}; 16 17 use ratatui::{Terminal, prelude::CrosstermBackend}; 17 18 use tokio::{ 18 19 sync::mpsc::{self, UnboundedReceiver, UnboundedSender}, ··· 23 24 use tracing::error; 24 25 25 26 /// Events processed by the whole application. 27 + #[expect(dead_code)] 26 28 pub enum Event { 27 29 /// Application initialized 28 30 Init, ··· 74 76 pub paste_enabled: bool, 75 77 } 76 78 79 + #[expect(dead_code)] 77 80 impl Tui { 78 81 /// Creates a new TUI. 79 82 pub fn new() -> Result<Self> { ··· 150 153 .expect("Tui::event_loop: Failed to send init event."); 151 154 loop { 152 155 let event = tokio::select! { 153 - _ = cancellation_token.cancelled() => { 156 + () = cancellation_token.cancelled() => { 154 157 break; 155 158 } 156 159 _ = tick_interval.tick() => Event::Tick, ··· 160 163 // we only care about press down events, 161 164 // not doing anything related to up / down keypresses 162 165 CrosstermEvent::Key(key) if key.kind == KeyEventKind::Press => Event::Key(key), 166 + CrosstermEvent::Key(_) => continue, 167 + 163 168 CrosstermEvent::Mouse(mouse) => Event::Mouse(mouse), 164 169 CrosstermEvent::Resize(x, y) => Event::Resize(x, y), 165 170 CrosstermEvent::FocusLost => {Event::FocusLost }, 166 171 CrosstermEvent::FocusGained => {Event::FocusGained }, 167 172 CrosstermEvent::Paste(s)=> {Event::Paste(s)}, 168 - _ => continue, 169 173 170 174 } 171 175 Some(Err(_)) => Event::Error,

History

1 round 0 comments
sign up or login to add to the discussion
suri.codes submitted #0
1 commit
expand
feat/tui: Component trait
1/2 failed, 1/2 success
expand
expand 0 comments
closed without merging