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 = std::result::Result; #[derive(Debug, Clone)] pub enum Body { Echo, Signin(SigninPage), } impl Body { fn echo( env: Environment, screen: &mut RawTerminal, 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, ) -> 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, 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, ) -> 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, } #[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::>() .into() } #[inline] fn field( &self, color: ColorSet, normal: ColorSet, exp_width: u16, field: C, field_content: C, ) -> Components where C: Into, { 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, 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, ) -> 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(()) } }