Better event handling and structure

This commit is contained in:
Joshua Barretto 2024-03-04 16:51:57 +00:00
parent f7943884cb
commit a58caed9e9
7 changed files with 101 additions and 39 deletions

View file

@ -2,6 +2,7 @@ mod action;
mod ui;
mod terminal;
mod state;
mod theme;
use crate::{
terminal::{Terminal, TerminalEvent, Color},

View file

@ -1,4 +1,5 @@
use crate::{
theme,
ui::{self, Resp, Element as _},
Action, Event, Args, Error, Color,
};
@ -41,24 +42,10 @@ pub enum Activity {
Console(Console),
}
pub struct Theme {
pub ui_bg: Color,
pub status_bg: Color,
}
impl Default for Theme {
fn default() -> Self {
Self {
ui_bg: Color::AnsiValue(235),
status_bg: Color::AnsiValue(23),
}
}
}
pub struct State {
pub activities: HopSlotMap<ActivityId, Activity>,
pub tick: u64,
pub theme: Theme,
pub theme: theme::Theme,
}
impl TryFrom<Args> for State {
@ -67,7 +54,7 @@ impl TryFrom<Args> for State {
Ok(Self {
activities: HopSlotMap::default(),
tick: 0,
theme: Theme::default(),
theme: theme::Theme::default(),
})
}
}

View file

@ -1,4 +1,4 @@
use super::Error;
use crate::{theme, Error};
pub use crossterm::{
cursor::SetCursorStyle as CursorStyle,
@ -72,6 +72,23 @@ impl<'a> Rect<'a> {
}
}
pub fn with_border(&mut self, theme: &theme::BorderTheme) -> Rect {
let edge = self.size().map(|e| e.saturating_sub(1));
for col in 0..edge[0] {
self.get_mut([col, 0]).map(|c| c.c = theme.top);
self.get_mut([col, edge[1]]).map(|c| c.c = theme.bottom);
}
for row in 0..edge[1] {
self.get_mut([0, row]).map(|c| c.c = theme.left);
self.get_mut([edge[0], row]).map(|c| c.c = theme.right);
}
self.get_mut([0, 0]).map(|c| c.c = theme.top_left);
self.get_mut([edge[0], 0]).map(|c| c.c = theme.top_right);
self.get_mut([0, edge[1]]).map(|c| c.c = theme.bottom_left);
self.get_mut([edge[0], edge[1]]).map(|c| c.c = theme.bottom_right);
self.rect([1, 1], self.size().map(|e| e.saturating_sub(2)))
}
pub fn with_fg(&mut self, fg: Color) -> Rect {
Rect { fg, bg: self.bg, origin: self.origin, size: self.size, fb: self.fb }
}
@ -82,17 +99,17 @@ impl<'a> Rect<'a> {
pub fn size(&self) -> [usize; 2] { self.size.map(|e| e as usize) }
pub fn fill(&mut self, c: char) -> &mut Self {
pub fn fill(&mut self, c: char) -> Rect {
for row in 0..self.size()[1] {
for col in 0..self.size()[0] {
let cell = Cell { c, fg: self.fg, bg: self.bg };
if let Some(c) = self.get_mut([col, row]) { *c = cell; }
}
}
self
self.rect([0, 0], self.size())
}
pub fn text<C: Borrow<char>>(&mut self, origin: [usize; 2], text: impl IntoIterator<Item = C>) -> &mut Self {
pub fn text<C: Borrow<char>>(&mut self, origin: [usize; 2], text: impl IntoIterator<Item = C>) -> Rect {
for (idx, c) in text.into_iter().enumerate() {
if origin[0] + idx >= self.size()[0] {
break;
@ -101,15 +118,15 @@ impl<'a> Rect<'a> {
if let Some(c) = self.get_mut([origin[0] + idx, origin[1]]) { *c = cell; }
}
}
self
self.rect([0, 0], self.size())
}
pub fn set_cursor(&mut self, cursor: [usize; 2], style: CursorStyle) -> &mut Self {
pub fn set_cursor(&mut self, cursor: [usize; 2], style: CursorStyle) -> Rect {
self.fb.cursor = Some((
[self.origin[0] + cursor[0] as u16, self.origin[1] + cursor[1] as u16],
style,
));
self
self.rect([0, 0], self.size())
}
}

43
src/theme.rs Normal file
View file

@ -0,0 +1,43 @@
use crate::Color;
pub struct BorderTheme {
pub left: char,
pub right: char,
pub top: char,
pub bottom: char,
pub top_left: char,
pub top_right: char,
pub bottom_left: char,
pub bottom_right: char,
}
impl Default for BorderTheme {
fn default() -> Self {
Self {
left: '',
right: '',
top: '',
bottom: '',
top_left: '',
top_right: '',
bottom_left: '',
bottom_right: '',
}
}
}
pub struct Theme {
pub ui_bg: Color,
pub status_bg: Color,
pub border: BorderTheme,
}
impl Default for Theme {
fn default() -> Self {
Self {
ui_bg: Color::AnsiValue(235),
status_bg: Color::AnsiValue(23),
border: BorderTheme::default(),
}
}
}

View file

@ -1,3 +1,12 @@
use super::*;
pub struct Panes;
impl Element for Panes {
fn handle(&mut self, event: Event) -> Result<Resp, Event> {
match event.to_action(|e| None) {
//Some(_) => todo!(),
_ => Err(event),
}
}
}

View file

@ -59,8 +59,6 @@ impl Element<CanEnd> for Prompt {
impl Visual for Prompt {
fn render(&self, state: &State, frame: &mut Rect) {
frame
.rect([0, frame.size()[1].saturating_sub(1)], [frame.size()[0], 1])
.with_bg(state.theme.status_bg)
.with(|f| self.input.render(state, f));
}
}
@ -87,7 +85,7 @@ impl Visual for Show {
let lines = self.label.lines().count();
self.label.render(
state,
&mut frame.rect([0, frame.size()[1].saturating_sub(1 + lines)], [frame.size()[0], lines]),
&mut frame.rect([0, frame.size()[1].saturating_sub(3 + lines)], [frame.size()[0], lines]),
);
}
}
@ -118,7 +116,7 @@ impl Visual for Confirm {
let lines = self.label.lines().count();
self.label.render(
state,
&mut frame.rect([0, frame.size()[1].saturating_sub(1 + lines)], [frame.size()[0], lines]),
&mut frame.rect([0, frame.size()[1].saturating_sub(3 + lines)], [frame.size()[0], lines]),
);
}
}

View file

@ -29,13 +29,16 @@ impl Element<CanEnd> for Root {
let action = loop {
task_idx = match task_idx.checked_sub(1) {
Some(task_idx) => task_idx,
None => break event.to_action(|e| if e.is_prompt() {
Some(Action::OpenPrompt)
} else if e.is_cancel() {
Some(Action::Cancel)
} else {
None
}),
None => break match self.panes.handle(event) {
Ok(resp) => resp.action,
Err(event) => event.to_action(|e| if e.is_prompt() {
Some(Action::OpenPrompt)
} else if e.is_cancel() {
Some(Action::Cancel)
} else {
None
}),
},
};
let res = match &mut self.tasks[task_idx] {
@ -83,20 +86,24 @@ impl Element<CanEnd> for Root {
impl Visual for Root {
fn render(&self, state: &State, frame: &mut Rect) {
frame
.with_bg(Color::Black)
.fill(' ');
// Display status bar
frame
.rect([0, frame.size()[1].saturating_sub(1)], [frame.size()[0], 1])
.with_bg(state.theme.status_bg)
.fill(' ');
.rect([0, frame.size()[1].saturating_sub(3)], [frame.size()[0], 3])
.fill(' ')
.with_border(&state.theme.border)
.with(|frame| {
if let Some(Task::Prompt(p)) = self.tasks.last() {
p.render(state, frame);
}
});
if let Some(task) = self.tasks.last() {
match task {
Task::Prompt(p) => p.render(state, frame),
Task::Show(s) => s.render(state, frame),
Task::Confirm(c) => c.render(state, frame),
_ => {},
}
}
}