kk/src/display/body.rs

323 lines
8.2 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.windows.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,
token: String,
cursor: SigninCursorLocation,
frame: Option<Frame>,
}
#[derive(Debug, Clone)]
enum SigninCursorLocation {
Hostname,
Token,
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::Token
}
SigninCursorLocation::Token => SigninCursorLocation::Ok,
SigninCursorLocation::Ok => {
SigninCursorLocation::Hostname
}
}
}
#[inline]
fn previous(&mut self) {
self.cursor = match self.cursor {
SigninCursorLocation::Hostname => {
SigninCursorLocation::Ok
}
SigninCursorLocation::Token => {
SigninCursorLocation::Hostname
}
SigninCursorLocation::Ok => SigninCursorLocation::Token,
}
}
fn draw(
&self,
highlight: ColorSet,
normal: ColorSet,
) -> Components {
const HEADER_Y: u16 = 1;
const HOSTNAME_Y: u16 = 3;
const TOKEN_Y: u16 = 4;
const OK_Y: u16 = 6;
let frame = self.frame.unwrap();
let (w, _) = frame.inner_size();
let exp_w = w - (w / 3);
let (hostname_theme, token_theme, ok_theme) = match self
.cursor
{
SigninCursorLocation::Hostname => {
(highlight, normal, normal)
}
SigninCursorLocation::Token => {
(normal, highlight, normal)
}
SigninCursorLocation::Ok => (normal, 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,
frame
.prefix_centered_goto(
self.field(
token_theme,
normal,
exp_w,
"token",
&self.token,
),
TOKEN_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()
}
fn del(&mut self) {
match self.cursor {
SigninCursorLocation::Hostname => {
self.hostname.pop();
}
SigninCursorLocation::Token => {
self.token.pop();
}
SigninCursorLocation::Ok => {}
}
}
#[inline]
fn char(&mut self, c: char) {
if ['\t', '\n', '\r'].contains(&c) {
return;
}
match self.cursor {
SigninCursorLocation::Hostname => {
self.hostname.push(c);
}
SigninCursorLocation::Token => {
self.token.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.previous(),
termion::event::Key::Down => self.next(),
termion::event::Key::Backspace => self.del(),
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.windows.subwin),
fr.make(self.draw(
env.theme.windows.highlight,
env.theme.windows.subwin
))
)?;
screen.flush()?;
Ok(())
}
fn init(
&mut self,
env: Environment,
screen: &mut RawTerminal<Stdout>,
) -> Result<()> {
self.frame = Some(env.frame.sub(
env.theme.frames.inner,
super::frame::FrameSize::ByAbsolute(70, 10),
));
write!(
screen,
"{frame}{subframe}{content}{hide_cursor}",
frame = env.frame.frame_str(env.theme.windows.primary),
subframe = self
.frame
.unwrap()
.frame_str(env.theme.windows.subwin),
hide_cursor = cursor::Hide,
content = self.frame.unwrap().make(self.draw(
env.theme.windows.highlight,
env.theme.windows.subwin
))
)?;
screen.flush()?;
Ok(())
}
}