Better event handling and structure
This commit is contained in:
parent
f7943884cb
commit
a58caed9e9
7 changed files with 101 additions and 39 deletions
|
|
@ -2,6 +2,7 @@ mod action;
|
|||
mod ui;
|
||||
mod terminal;
|
||||
mod state;
|
||||
mod theme;
|
||||
|
||||
use crate::{
|
||||
terminal::{Terminal, TerminalEvent, Color},
|
||||
|
|
|
|||
19
src/state.rs
19
src/state.rs
|
|
@ -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(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
43
src/theme.rs
Normal 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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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]),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue