Overhaul events system + add url_manager
This commit is contained in:
parent
9d23304feb
commit
f638f4bd1e
56
src/app.rs
56
src/app.rs
@ -9,6 +9,7 @@ use crate::component::Component;
|
|||||||
use crate::components;
|
use crate::components;
|
||||||
use crate::keys::key_commands::KeyCommand;
|
use crate::keys::key_commands::KeyCommand;
|
||||||
use crate::tui;
|
use crate::tui;
|
||||||
|
|
||||||
pub struct App {
|
pub struct App {
|
||||||
pub tui: tui::Tui,
|
pub tui: tui::Tui,
|
||||||
pub tick_rate: Duration,
|
pub tick_rate: Duration,
|
||||||
@ -37,8 +38,16 @@ impl App {
|
|||||||
key_commands: self.key_commands.clone(),
|
key_commands: self.key_commands.clone(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let status_bar = components::status::StatusBar::default();
|
let status_bar = components::status::StatusBar {
|
||||||
self.components = vec![Box::new(global_keys), Box::new(status_bar)];
|
message: "Press '?' to show the help menu".to_string(),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let url_manager = components::url_manager::UrlManager::default();
|
||||||
|
self.components = vec![
|
||||||
|
Box::new(global_keys),
|
||||||
|
Box::new(status_bar),
|
||||||
|
Box::new(url_manager),
|
||||||
|
];
|
||||||
|
|
||||||
for component in &mut self.components {
|
for component in &mut self.components {
|
||||||
component.init()?;
|
component.init()?;
|
||||||
@ -67,16 +76,7 @@ impl App {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let Some(event) = event {
|
if let Some(event) = event {
|
||||||
let mut actions: Vec<AppAction> = vec![];
|
self.handle_event(event)?;
|
||||||
for component in &mut self.components {
|
|
||||||
if let Some(action) = component.handle_event(event.clone())? {
|
|
||||||
actions.push(action);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for action in actions {
|
|
||||||
self.handle_action(action)?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.should_quit {
|
if self.should_quit {
|
||||||
@ -94,10 +94,28 @@ impl App {
|
|||||||
|
|
||||||
// status bar
|
// status bar
|
||||||
let _ = self.components[1].render(frame, layout[1]);
|
let _ = self.components[1].render(frame, layout[1]);
|
||||||
|
|
||||||
// global_keys
|
// global_keys
|
||||||
let _ = self.components[0].render(frame, frame.size());
|
let _ = self.components[0].render(frame, frame.size());
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
self.update()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&mut self) -> Result<()> {
|
||||||
|
let mut events: Vec<AppEvent> = vec![];
|
||||||
|
for component in &mut self.components {
|
||||||
|
if let Some(event) = component.update() {
|
||||||
|
events.push(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for event in events {
|
||||||
|
self.handle_event(event)?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,4 +137,18 @@ impl App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_event(&mut self, event: AppEvent) -> Result<()> {
|
||||||
|
let mut actions: Vec<AppAction> = vec![];
|
||||||
|
for component in &mut self.components {
|
||||||
|
if let Some(action) = component.handle_event(event.clone()) {
|
||||||
|
actions.push(action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for action in actions {
|
||||||
|
self.handle_action(action)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ use std::fmt;
|
|||||||
|
|
||||||
#[derive(Default, Clone, Debug)]
|
#[derive(Default, Clone, Debug)]
|
||||||
pub enum AppAction {
|
pub enum AppAction {
|
||||||
StatusBarGetInput(String),
|
|
||||||
StatusBarSetMessage(String),
|
StatusBarSetMessage(String),
|
||||||
StatusBarSetError(String),
|
StatusBarSetError(String),
|
||||||
OpenUrl,
|
OpenUrl,
|
||||||
|
@ -6,6 +6,5 @@ pub enum AppEvent {
|
|||||||
Key(KeyEvent),
|
Key(KeyEvent),
|
||||||
Mouse(MouseEvent),
|
Mouse(MouseEvent),
|
||||||
|
|
||||||
StatusBarInput(String),
|
|
||||||
OpenUrl(Url),
|
OpenUrl(Url),
|
||||||
}
|
}
|
||||||
|
@ -14,32 +14,29 @@ pub trait Component {
|
|||||||
fn handle_action(&mut self, action: AppAction) {}
|
fn handle_action(&mut self, action: AppAction) {}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
fn handle_event(&mut self, event: AppEvent) -> Result<Option<AppAction>> {
|
fn handle_event(&mut self, event: AppEvent) -> Option<AppAction> {
|
||||||
match event {
|
match event {
|
||||||
AppEvent::Key(key_event) => Ok(self.handle_key_event(key_event)?),
|
AppEvent::Key(key_event) => self.handle_key_event(key_event),
|
||||||
AppEvent::Mouse(mouse_event) => {
|
AppEvent::Mouse(mouse_event) => {
|
||||||
Ok(self.handle_mouse_event(mouse_event)?)
|
self.handle_mouse_event(mouse_event)
|
||||||
}
|
}
|
||||||
_ => Ok(None),
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
fn handle_key_event(&mut self, key: KeyEvent) -> Result<Option<AppAction>> {
|
fn handle_key_event(&mut self, key: KeyEvent) -> Option<AppAction> {
|
||||||
Ok(None)
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
fn handle_mouse_event(
|
fn handle_mouse_event(&mut self, mouse: MouseEvent) -> Option<AppAction> {
|
||||||
&mut self,
|
None
|
||||||
mouse: MouseEvent,
|
|
||||||
) -> Result<Option<AppAction>> {
|
|
||||||
Ok(None)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
fn update(&mut self, action: AppAction) -> Result<Option<AppAction>> {
|
fn update(&mut self) -> Option<AppEvent> {
|
||||||
Ok(None)
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(&mut self, frame: &mut Frame, rect: Rect) -> Result<()>;
|
fn render(&mut self, frame: &mut Frame, rect: Rect) -> Result<()>;
|
||||||
|
@ -58,20 +58,17 @@ impl Component for GlobalKeys {
|
|||||||
self.scroll_state = self.scroll_state.position(self.scroll);
|
self.scroll_state = self.scroll_state.position(self.scroll);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_key_event(
|
fn handle_key_event(&mut self, key: KeyEvent) -> Option<AppAction> {
|
||||||
&mut self,
|
|
||||||
key: KeyEvent,
|
|
||||||
) -> eyre::Result<Option<AppAction>> {
|
|
||||||
if key.kind == KeyEventKind::Press {
|
if key.kind == KeyEventKind::Press {
|
||||||
let key_event = serialize_key_event(key);
|
let key_event = serialize_key_event(key);
|
||||||
for key_command in &mut self.key_commands {
|
for key_command in &mut self.key_commands {
|
||||||
if key_command.key_code == key_event {
|
if key_command.key_code == key_event {
|
||||||
return Ok(Some(key_command.action.clone()));
|
return Some(key_command.action.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(None)
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(&mut self, frame: &mut Frame, rect: Rect) -> eyre::Result<()> {
|
fn render(&mut self, frame: &mut Frame, rect: Rect) -> eyre::Result<()> {
|
||||||
|
@ -1,2 +1,3 @@
|
|||||||
pub mod global_keys;
|
pub mod global_keys;
|
||||||
pub mod status;
|
pub mod status;
|
||||||
|
pub mod url_manager;
|
||||||
|
@ -2,29 +2,31 @@ use ratatui::prelude::*;
|
|||||||
use ratatui::widgets::*;
|
use ratatui::widgets::*;
|
||||||
|
|
||||||
use crate::app_action::AppAction;
|
use crate::app_action::AppAction;
|
||||||
|
use crate::app_event::AppEvent;
|
||||||
use crate::component::Component;
|
use crate::component::Component;
|
||||||
use crate::keys::key_commands::serialize_key_event;
|
use crate::keys::key_commands::serialize_key_event;
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
#[derive(Default, Clone)]
|
||||||
pub struct StatusBar {
|
pub struct StatusBar {
|
||||||
message: String,
|
pub message: String,
|
||||||
current_key: String,
|
pub current_key: String,
|
||||||
error: bool,
|
pub error: bool,
|
||||||
|
pub url_to_open: Option<url::Url>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Component for StatusBar {
|
impl Component for StatusBar {
|
||||||
fn handle_key_event(
|
fn handle_key_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
key: crossterm::event::KeyEvent,
|
key: crossterm::event::KeyEvent,
|
||||||
) -> eyre::Result<Option<AppAction>> {
|
) -> Option<AppAction> {
|
||||||
let key_str = serialize_key_event(key);
|
let key_str = serialize_key_event(key);
|
||||||
self.current_key = key_str;
|
self.current_key = key_str;
|
||||||
|
|
||||||
Ok(None)
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_action(&mut self, action: crate::app_action::AppAction) {
|
fn handle_action(&mut self, action: crate::app_action::AppAction) {
|
||||||
match action {
|
match action.clone() {
|
||||||
AppAction::StatusBarSetMessage(message) => {
|
AppAction::StatusBarSetMessage(message) => {
|
||||||
self.error = false;
|
self.error = false;
|
||||||
self.message = message;
|
self.message = message;
|
||||||
@ -33,14 +35,24 @@ impl Component for StatusBar {
|
|||||||
self.error = true;
|
self.error = true;
|
||||||
self.message = message;
|
self.message = message;
|
||||||
}
|
}
|
||||||
AppAction::StatusBarGetInput(_prompt) => todo!(),
|
AppAction::OpenUrl => {
|
||||||
_ => {
|
self.url_to_open =
|
||||||
self.current_key += " ";
|
Some(url::Url::parse("molerat://example.com").unwrap());
|
||||||
self.current_key += &action.to_string();
|
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update(&mut self) -> Option<AppEvent> {
|
||||||
|
if let Some(url) = &self.url_to_open {
|
||||||
|
let event = AppEvent::OpenUrl(url.clone());
|
||||||
|
self.url_to_open = None;
|
||||||
|
return Some(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn render(
|
fn render(
|
||||||
&mut self,
|
&mut self,
|
||||||
frame: &mut ratatui::prelude::Frame,
|
frame: &mut ratatui::prelude::Frame,
|
||||||
|
34
src/components/url_manager.rs
Normal file
34
src/components/url_manager.rs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
use url::Url;
|
||||||
|
|
||||||
|
use crate::app_action::AppAction;
|
||||||
|
use crate::app_event::AppEvent;
|
||||||
|
use crate::component::Component;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct UrlManager {
|
||||||
|
url: Option<Url>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for UrlManager {
|
||||||
|
fn handle_event(&mut self, event: AppEvent) -> Option<AppAction> {
|
||||||
|
match event {
|
||||||
|
AppEvent::OpenUrl(url) => {
|
||||||
|
self.url = Some(url.clone());
|
||||||
|
return Some(AppAction::StatusBarSetMessage(format!(
|
||||||
|
"Opening {}",
|
||||||
|
url.as_str()
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(
|
||||||
|
&mut self,
|
||||||
|
_frame: &mut ratatui::prelude::Frame,
|
||||||
|
_rect: ratatui::prelude::Rect,
|
||||||
|
) -> eyre::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
@ -21,7 +21,6 @@ fn main() -> Result<()> {
|
|||||||
description: "Open new link".to_string(),
|
description: "Open new link".to_string(),
|
||||||
action: AppAction::OpenUrl,
|
action: AppAction::OpenUrl,
|
||||||
},
|
},
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
KeyCommand {
|
KeyCommand {
|
||||||
key_code: "g".to_string(),
|
key_code: "g".to_string(),
|
||||||
@ -50,9 +49,9 @@ fn main() -> Result<()> {
|
|||||||
},
|
},
|
||||||
KeyCommand {
|
KeyCommand {
|
||||||
key_code: "?".to_string(),
|
key_code: "?".to_string(),
|
||||||
description: "Show help menu".to_string(),
|
description: "Toggle help menu".to_string(),
|
||||||
action: AppAction::ShowHelpMenu
|
action: AppAction::ShowHelpMenu,
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
app.key_commands.append(&mut key_commands);
|
app.key_commands.append(&mut key_commands);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user