kk/src/display/body.rs

284 lines
7.1 KiB
Rust

use std::io::{Stdout, Write};
use termion::{clear, cursor, raw::RawTerminal};
use super::{
compose::{Component, Components, HorizontalAlignment, Text},
frame::Frame,
theme::ColorSet,
Environment, Event, Page,
};
type Result<T> = std::result::Result<T, anyhow::Error>;
#[derive(Debug, Clone)]
pub enum Body {
Echo,
Signin(SigninPage),
}
impl Body {
fn echo(
env: Environment,
screen: &mut RawTerminal<Stdout>,
event: Event,
) -> Result<()> {
let echo_comps =
Components::break_into(format!("Event: {}", event))
.nextline_after_text(
0,
HorizontalAlignment::Left,
env.frame.inner_size().0,
);
write!(screen, "{}", env.frame.make(echo_comps))?;
screen.flush()?;
Ok(())
}
fn echo_init(
&self,
env: Environment,
screen: &mut RawTerminal<Stdout>,
) -> Result<()> {
write!(
screen,
"{hide}{clear}{fr}{cursor}",
clear = clear::All,
hide = cursor::Hide,
fr = env.frame.frame_str(env.theme.colors.primary),
cursor = env.frame.goto(0, 0),
)?;
screen.flush()?;
Ok(())
}
}
impl Page for Body {
fn receive_event(
&mut self,
env: Environment,
screen: &mut RawTerminal<Stdout>,
event: Event,
) -> Result<()> {
match self {
Body::Signin(b) => b.receive_event(env, screen, event)?,
Body::Echo => Body::echo(env, screen, event)?,
};
Ok(())
}
fn init(
&mut self,
env: Environment,
screen: &mut RawTerminal<Stdout>,
) -> Result<()> {
match self {
Body::Echo => self.echo_init(env, screen),
Body::Signin(signin) => signin.init(env, screen),
}
}
}
#[derive(Debug, Clone, Default)]
pub struct SigninPage {
hostname: String,
cursor: SigninCursorLocation,
frame: Option<Frame>,
}
#[derive(Debug, Clone)]
enum SigninCursorLocation {
Hostname,
Ok,
}
impl Default for SigninCursorLocation {
fn default() -> Self {
Self::Hostname
}
}
impl SigninPage {
#[inline]
fn next(&mut self) {
self.cursor = match self.cursor {
SigninCursorLocation::Hostname => {
SigninCursorLocation::Ok
}
SigninCursorLocation::Ok => {
SigninCursorLocation::Hostname
}
}
}
#[inline]
fn previous(&mut self) {
// Don't tell them this neat trick
self.next()
}
fn draw(
&self,
highlight: ColorSet,
normal: ColorSet,
) -> Components {
const HEADER_Y: u16 = 1;
const HOSTNAME_Y: u16 = 3;
const OK_Y: u16 = 5;
let frame = self.frame.unwrap();
let (w, _) = frame.inner_size();
let exp_w = w / 3;
let (hostname_theme, ok_theme) = match self.cursor {
SigninCursorLocation::Hostname => (highlight, normal),
SigninCursorLocation::Ok => (normal, highlight),
};
vec![
frame
.prefix_centered_goto(
vec![Component::String(Text::Normal(
"login kk".into(),
))]
.into(),
HEADER_Y,
)
.0,
frame
.prefix_centered_goto(
self.field(
hostname_theme,
normal,
exp_w,
"hostname",
&self.hostname,
),
HOSTNAME_Y,
)
.0,
vec![Component::Theme(ok_theme)],
HorizontalAlignment::Center
.align(Text::Normal("[ok]".into()), w, OK_Y)
.0,
vec![Component::Theme(normal)],
]
.into_iter()
.flatten()
.collect::<Vec<Component>>()
.into()
}
#[inline]
fn field<C>(
&self,
color: ColorSet,
normal: ColorSet,
exp_width: u16,
field: C,
field_content: C,
) -> Components
where
C: Into<String>,
{
let (field, field_content) =
(field.into(), field_content.into());
let budget_for_content = exp_width - field.len() as u16 - 3;
vec![
Component::String((field + &": ").into()),
Component::Theme(color),
Component::String(Text::LimitedOrPadded(
field_content,
'_',
budget_for_content,
)),
Component::Theme(normal),
]
.into()
}
#[inline]
fn char(&mut self, c: char) {
match self.cursor {
SigninCursorLocation::Hostname => {
self.hostname.push(c);
}
SigninCursorLocation::Ok => {}
}
}
}
impl Page for SigninPage {
fn receive_event(
&mut self,
env: Environment,
screen: &mut RawTerminal<Stdout>,
event: Event,
) -> Result<()> {
match event {
Event::Key(key) => match key {
termion::event::Key::Char(c) => self.char(c),
termion::event::Key::Up => self.next(),
termion::event::Key::Down => self.previous(),
termion::event::Key::Backspace => {
if let SigninCursorLocation::Hostname =
self.cursor
{
self.hostname.pop();
}
}
termion::event::Key::Delete => {}
termion::event::Key::Left => {}
termion::event::Key::Right => {}
//
_ => {}
},
}
let fr = self.frame.unwrap();
write!(
screen,
"{}{}{}",
env.primary_frame(),
fr.frame_str(env.theme.colors.subwin),
fr.make(self.draw(
env.theme.colors.highlight,
env.theme.colors.subwin
))
)?;
screen.flush()?;
Ok(())
}
fn init(
&mut self,
env: Environment,
screen: &mut RawTerminal<Stdout>,
) -> Result<()> {
self.frame = Some(env.frame.sub(
env.theme.colors.subframe,
super::frame::FrameSize::ByPercent(50, 50),
2,
));
write!(
screen,
"{theme}{clear}{frame}{subframe}{content}{hide_cursor}",
theme = env.theme.frame(),
clear = clear::All,
frame = env.frame.frame_str(env.theme.colors.primary),
subframe = self
.frame
.unwrap()
.frame_str(env.theme.colors.subwin),
hide_cursor = cursor::Hide,
content = self.frame.unwrap().make(self.draw(
env.theme.colors.highlight,
env.theme.colors.subwin
))
)?;
screen.flush()?;
Ok(())
}
}