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 ui;
|
||||||
mod terminal;
|
mod terminal;
|
||||||
mod state;
|
mod state;
|
||||||
|
mod theme;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
terminal::{Terminal, TerminalEvent, Color},
|
terminal::{Terminal, TerminalEvent, Color},
|
||||||
|
|
|
||||||
19
src/state.rs
19
src/state.rs
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
|
theme,
|
||||||
ui::{self, Resp, Element as _},
|
ui::{self, Resp, Element as _},
|
||||||
Action, Event, Args, Error, Color,
|
Action, Event, Args, Error, Color,
|
||||||
};
|
};
|
||||||
|
|
@ -41,24 +42,10 @@ pub enum Activity {
|
||||||
Console(Console),
|
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 struct State {
|
||||||
pub activities: HopSlotMap<ActivityId, Activity>,
|
pub activities: HopSlotMap<ActivityId, Activity>,
|
||||||
pub tick: u64,
|
pub tick: u64,
|
||||||
pub theme: Theme,
|
pub theme: theme::Theme,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<Args> for State {
|
impl TryFrom<Args> for State {
|
||||||
|
|
@ -67,7 +54,7 @@ impl TryFrom<Args> for State {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
activities: HopSlotMap::default(),
|
activities: HopSlotMap::default(),
|
||||||
tick: 0,
|
tick: 0,
|
||||||
theme: Theme::default(),
|
theme: theme::Theme::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use super::Error;
|
use crate::{theme, Error};
|
||||||
|
|
||||||
pub use crossterm::{
|
pub use crossterm::{
|
||||||
cursor::SetCursorStyle as CursorStyle,
|
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 {
|
pub fn with_fg(&mut self, fg: Color) -> Rect {
|
||||||
Rect { fg, bg: self.bg, origin: self.origin, size: self.size, fb: self.fb }
|
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 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 row in 0..self.size()[1] {
|
||||||
for col in 0..self.size()[0] {
|
for col in 0..self.size()[0] {
|
||||||
let cell = Cell { c, fg: self.fg, bg: self.bg };
|
let cell = Cell { c, fg: self.fg, bg: self.bg };
|
||||||
if let Some(c) = self.get_mut([col, row]) { *c = cell; }
|
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() {
|
for (idx, c) in text.into_iter().enumerate() {
|
||||||
if origin[0] + idx >= self.size()[0] {
|
if origin[0] + idx >= self.size()[0] {
|
||||||
break;
|
break;
|
||||||
|
|
@ -101,15 +118,15 @@ impl<'a> Rect<'a> {
|
||||||
if let Some(c) = self.get_mut([origin[0] + idx, origin[1]]) { *c = cell; }
|
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.fb.cursor = Some((
|
||||||
[self.origin[0] + cursor[0] as u16, self.origin[1] + cursor[1] as u16],
|
[self.origin[0] + cursor[0] as u16, self.origin[1] + cursor[1] as u16],
|
||||||
style,
|
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::*;
|
use super::*;
|
||||||
|
|
||||||
pub struct Panes;
|
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 {
|
impl Visual for Prompt {
|
||||||
fn render(&self, state: &State, frame: &mut Rect) {
|
fn render(&self, state: &State, frame: &mut Rect) {
|
||||||
frame
|
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));
|
.with(|f| self.input.render(state, f));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -87,7 +85,7 @@ impl Visual for Show {
|
||||||
let lines = self.label.lines().count();
|
let lines = self.label.lines().count();
|
||||||
self.label.render(
|
self.label.render(
|
||||||
state,
|
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();
|
let lines = self.label.lines().count();
|
||||||
self.label.render(
|
self.label.render(
|
||||||
state,
|
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 {
|
let action = loop {
|
||||||
task_idx = match task_idx.checked_sub(1) {
|
task_idx = match task_idx.checked_sub(1) {
|
||||||
Some(task_idx) => task_idx,
|
Some(task_idx) => task_idx,
|
||||||
None => break event.to_action(|e| if e.is_prompt() {
|
None => break match self.panes.handle(event) {
|
||||||
Some(Action::OpenPrompt)
|
Ok(resp) => resp.action,
|
||||||
} else if e.is_cancel() {
|
Err(event) => event.to_action(|e| if e.is_prompt() {
|
||||||
Some(Action::Cancel)
|
Some(Action::OpenPrompt)
|
||||||
} else {
|
} else if e.is_cancel() {
|
||||||
None
|
Some(Action::Cancel)
|
||||||
}),
|
} else {
|
||||||
|
None
|
||||||
|
}),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let res = match &mut self.tasks[task_idx] {
|
let res = match &mut self.tasks[task_idx] {
|
||||||
|
|
@ -83,20 +86,24 @@ impl Element<CanEnd> for Root {
|
||||||
impl Visual for Root {
|
impl Visual for Root {
|
||||||
fn render(&self, state: &State, frame: &mut Rect) {
|
fn render(&self, state: &State, frame: &mut Rect) {
|
||||||
frame
|
frame
|
||||||
.with_bg(Color::Black)
|
|
||||||
.fill(' ');
|
.fill(' ');
|
||||||
|
|
||||||
// Display status bar
|
// Display status bar
|
||||||
frame
|
frame
|
||||||
.rect([0, frame.size()[1].saturating_sub(1)], [frame.size()[0], 1])
|
.rect([0, frame.size()[1].saturating_sub(3)], [frame.size()[0], 3])
|
||||||
.with_bg(state.theme.status_bg)
|
.fill(' ')
|
||||||
.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() {
|
if let Some(task) = self.tasks.last() {
|
||||||
match task {
|
match task {
|
||||||
Task::Prompt(p) => p.render(state, frame),
|
|
||||||
Task::Show(s) => s.render(state, frame),
|
Task::Show(s) => s.render(state, frame),
|
||||||
Task::Confirm(c) => c.render(state, frame),
|
Task::Confirm(c) => c.render(state, frame),
|
||||||
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue