Create very basic text input

This commit is contained in:
Joscha 2022-02-21 01:49:20 +01:00
parent 95d1f3295f
commit 28c3241fd9
4 changed files with 81 additions and 106 deletions

1
Cargo.lock generated
View file

@ -202,6 +202,7 @@ dependencies = [
"tokio",
"tokio-tungstenite",
"tui",
"unicode-width",
]
[[package]]

View file

@ -18,3 +18,4 @@ tokio-tungstenite = { version = "0.16.1", features = [
"rustls-tls-native-roots",
] }
tui = "0.17.0"
unicode-width = "0.1.9"

View file

@ -2,124 +2,48 @@ mod config;
mod never;
mod replies;
mod room;
mod textline;
use std::io::{self, Stdout};
use config::Config;
use crossterm::event::{DisableMouseCapture, EnableMouseCapture};
use crossterm::event::{DisableMouseCapture, EnableMouseCapture, Event, KeyCode};
use crossterm::execute;
use crossterm::terminal::{EnterAlternateScreen, LeaveAlternateScreen};
use palette::rgb::Rgb;
use palette::{FromColor, Hsl, Srgb};
use tui::backend::CrosstermBackend;
use tui::layout::{Constraint, Direction, Layout};
use tui::style::{Color, Modifier, Style};
use tui::text::{Span, Spans};
use tui::widgets::{Block, Borders, List, ListItem, ListState, Paragraph};
use tui::Terminal;
use textline::{TextLine, TextLineState};
use tui::backend::{Backend, CrosstermBackend};
use tui::{Frame, Terminal};
async fn run(terminal: &mut Terminal<CrosstermBackend<Stdout>>) -> anyhow::Result<()> {
terminal.draw(|f| {
let hchunks = Layout::default()
.direction(Direction::Horizontal)
.constraints([
Constraint::Length(20),
Constraint::Length(2),
Constraint::Min(0),
Constraint::Length(2),
Constraint::Length(20),
])
.split(f.size());
#[derive(Debug, Default)]
struct Ui {
text: TextLineState,
}
// Borders
f.render_widget(Block::default().borders(Borders::LEFT), hchunks[1]);
f.render_widget(Block::default().borders(Borders::LEFT), hchunks[3]);
impl Ui {
fn draw<B: Backend>(&mut self, f: &mut Frame<B>) {
f.render_stateful_widget(TextLine, f.size(), &mut self.text);
self.text.set_cursor(f, f.size());
}
}
// Room list
let room_style = Style::default().fg(Color::LightBlue);
let mut state = ListState::default();
// state.select(Some(1));
f.render_stateful_widget(
List::new(vec![
ListItem::new(Span::styled(
"Cove",
Style::default().add_modifier(Modifier::BOLD),
)),
ListItem::new(Span::styled("&dunno", room_style)),
ListItem::new(Span::styled("&test", room_style)),
ListItem::new(" "),
ListItem::new(Span::styled(
"Euphoria",
Style::default().add_modifier(Modifier::BOLD),
)),
ListItem::new(Span::styled("&xkcd", room_style)),
ListItem::new(Span::styled("&music", room_style)),
ListItem::new(Span::styled("&bots", room_style)),
ListItem::new(" "),
ListItem::new(Span::styled(
"Instant",
Style::default().add_modifier(Modifier::BOLD),
)),
ListItem::new(Span::styled("&welcome", room_style)),
]),
// .highlight_style(Style::default().add_modifier(Modifier::BOLD))
// .highlight_symbol(">"),
hchunks[0],
&mut state,
);
// f.render_widget(Paragraph::new("foo"), hchunks[0]);
fn run(terminal: &mut Terminal<CrosstermBackend<Stdout>>) -> anyhow::Result<()> {
let mut ui = Ui::default();
loop {
terminal.draw(|f| ui.draw(f))?;
// Nick list
let nchunks = Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Length(1), Constraint::Min(0)])
.split(hchunks[4]);
f.render_widget(
Paragraph::new(Spans::from(vec![
Span::styled("Users", Style::default().add_modifier(Modifier::BOLD)),
Span::raw(" "),
Span::styled("(13)", Style::default().fg(Color::Gray)),
])),
nchunks[0],
);
fn userstyle(r: u8, g: u8, b: u8) -> Style {
let rgb = Srgb::new(r, g, b).into_format::<f32>();
let mut hsl = Hsl::from_color(rgb);
hsl.saturation = 1.0;
hsl.lightness = 0.7;
let rgb = Rgb::from_color(hsl).into_format::<u8>();
Style::default().fg(Color::Rgb(rgb.red, rgb.green, rgb.blue))
let event = crossterm::event::read()?;
if let Event::Key(k) = event {
if k.code == KeyCode::Esc {
break;
}
}
f.render_widget(
List::new([
ListItem::new(Span::styled("TerryTvType", userstyle(192, 242, 238))),
ListItem::new(Span::styled("r*4", userstyle(192, 211, 242))),
ListItem::new(Span::styled("Swedish", userstyle(192, 242, 207))),
ListItem::new(Span::styled("Garmy", userstyle(242, 225, 192))),
ListItem::new(Span::styled("SRP", userstyle(242, 219, 192))),
ListItem::new(Span::styled("C", userstyle(192, 218, 242))),
ListItem::new(Span::styled("fill", userstyle(192, 197, 242))),
ListItem::new(Span::styled("ohnezo", userstyle(242, 203, 192))),
ListItem::new(Span::styled("Sumärzru", userstyle(242, 223, 192))),
ListItem::new(Span::styled("SuperGeek", userstyle(192, 242, 203))),
ListItem::new(Span::styled("certainlyhominid", userstyle(192, 242, 209))),
ListItem::new(Span::styled("Plugh", userstyle(192, 242, 215))),
ListItem::new(Span::styled(
"🎼\u{fe0e}🎷🎷🎷🎼\u{fe0e}",
userstyle(242, 192, 192),
)),
]),
nchunks[1],
);
})?;
let _ = crossterm::event::read();
ui.text.process_input(event);
}
Ok(())
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let config = Config::load();
fn main() -> anyhow::Result<()> {
let mut terminal = Terminal::new(CrosstermBackend::new(io::stdout()))?;
crossterm::terminal::enable_raw_mode()?;
@ -130,7 +54,7 @@ async fn main() -> anyhow::Result<()> {
)?;
// Defer error handling so the terminal always gets restored properly
let result = run(&mut terminal).await;
let result = run(&mut terminal);
crossterm::terminal::disable_raw_mode()?;
execute!(

49
cove-tui/src/textline.rs Normal file
View file

@ -0,0 +1,49 @@
use std::cmp;
use crossterm::event::{Event, KeyCode};
use tui::backend::Backend;
use tui::buffer::Buffer;
use tui::layout::Rect;
use tui::widgets::{Paragraph, StatefulWidget, Widget};
use tui::Frame;
use unicode_width::UnicodeWidthStr;
/// A simple single-line text box.
pub struct TextLine;
impl StatefulWidget for TextLine {
type State = TextLineState;
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
Paragraph::new(&state.content as &str).render(area, buf);
// Paragraph::new("foo").render(area, buf);
}
}
/// State for [`TextLine`].
#[derive(Debug, Default)]
pub struct TextLineState {
content: String,
}
impl TextLineState {
pub fn set_cursor<B: Backend>(&self, f: &mut Frame<B>, area: Rect) {
let x = area.x + (self.content.width() as u16);
let x = cmp::min(x, area.x + area.width);
f.set_cursor(x, area.y);
}
pub fn process_input(&mut self, event: Event) {
if let Event::Key(k) = event {
match k.code {
KeyCode::Backspace => {
self.content.pop();
}
KeyCode::Char(c) => {
self.content.push(c);
}
_ => {}
}
}
}
}