Create very basic text input
This commit is contained in:
parent
95d1f3295f
commit
28c3241fd9
4 changed files with 81 additions and 106 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -202,6 +202,7 @@ dependencies = [
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-tungstenite",
|
"tokio-tungstenite",
|
||||||
"tui",
|
"tui",
|
||||||
|
"unicode-width",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
||||||
|
|
@ -18,3 +18,4 @@ tokio-tungstenite = { version = "0.16.1", features = [
|
||||||
"rustls-tls-native-roots",
|
"rustls-tls-native-roots",
|
||||||
] }
|
] }
|
||||||
tui = "0.17.0"
|
tui = "0.17.0"
|
||||||
|
unicode-width = "0.1.9"
|
||||||
|
|
|
||||||
|
|
@ -2,124 +2,48 @@ mod config;
|
||||||
mod never;
|
mod never;
|
||||||
mod replies;
|
mod replies;
|
||||||
mod room;
|
mod room;
|
||||||
|
mod textline;
|
||||||
|
|
||||||
use std::io::{self, Stdout};
|
use std::io::{self, Stdout};
|
||||||
|
|
||||||
use config::Config;
|
use crossterm::event::{DisableMouseCapture, EnableMouseCapture, Event, KeyCode};
|
||||||
use crossterm::event::{DisableMouseCapture, EnableMouseCapture};
|
|
||||||
use crossterm::execute;
|
use crossterm::execute;
|
||||||
use crossterm::terminal::{EnterAlternateScreen, LeaveAlternateScreen};
|
use crossterm::terminal::{EnterAlternateScreen, LeaveAlternateScreen};
|
||||||
use palette::rgb::Rgb;
|
use textline::{TextLine, TextLineState};
|
||||||
use palette::{FromColor, Hsl, Srgb};
|
use tui::backend::{Backend, CrosstermBackend};
|
||||||
use tui::backend::CrosstermBackend;
|
use tui::{Frame, Terminal};
|
||||||
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;
|
|
||||||
|
|
||||||
async fn run(terminal: &mut Terminal<CrosstermBackend<Stdout>>) -> anyhow::Result<()> {
|
#[derive(Debug, Default)]
|
||||||
terminal.draw(|f| {
|
struct Ui {
|
||||||
let hchunks = Layout::default()
|
text: TextLineState,
|
||||||
.direction(Direction::Horizontal)
|
}
|
||||||
.constraints([
|
|
||||||
Constraint::Length(20),
|
|
||||||
Constraint::Length(2),
|
|
||||||
Constraint::Min(0),
|
|
||||||
Constraint::Length(2),
|
|
||||||
Constraint::Length(20),
|
|
||||||
])
|
|
||||||
.split(f.size());
|
|
||||||
|
|
||||||
// Borders
|
impl Ui {
|
||||||
f.render_widget(Block::default().borders(Borders::LEFT), hchunks[1]);
|
fn draw<B: Backend>(&mut self, f: &mut Frame<B>) {
|
||||||
f.render_widget(Block::default().borders(Borders::LEFT), hchunks[3]);
|
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));
|
fn run(terminal: &mut Terminal<CrosstermBackend<Stdout>>) -> anyhow::Result<()> {
|
||||||
f.render_stateful_widget(
|
let mut ui = Ui::default();
|
||||||
List::new(vec![
|
loop {
|
||||||
ListItem::new(Span::styled(
|
terminal.draw(|f| ui.draw(f))?;
|
||||||
"Cove",
|
|
||||||
Style::default().add_modifier(Modifier::BOLD),
|
let event = crossterm::event::read()?;
|
||||||
)),
|
|
||||||
ListItem::new(Span::styled("&dunno", room_style)),
|
if let Event::Key(k) = event {
|
||||||
ListItem::new(Span::styled("&test", room_style)),
|
if k.code == KeyCode::Esc {
|
||||||
ListItem::new(" "),
|
break;
|
||||||
ListItem::new(Span::styled(
|
}
|
||||||
"Euphoria",
|
}
|
||||||
Style::default().add_modifier(Modifier::BOLD),
|
|
||||||
)),
|
ui.text.process_input(event);
|
||||||
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]);
|
|
||||||
|
|
||||||
// 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))
|
|
||||||
}
|
}
|
||||||
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();
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
fn main() -> anyhow::Result<()> {
|
||||||
async fn main() -> anyhow::Result<()> {
|
|
||||||
let config = Config::load();
|
|
||||||
|
|
||||||
let mut terminal = Terminal::new(CrosstermBackend::new(io::stdout()))?;
|
let mut terminal = Terminal::new(CrosstermBackend::new(io::stdout()))?;
|
||||||
|
|
||||||
crossterm::terminal::enable_raw_mode()?;
|
crossterm::terminal::enable_raw_mode()?;
|
||||||
|
|
@ -130,7 +54,7 @@ async fn main() -> anyhow::Result<()> {
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Defer error handling so the terminal always gets restored properly
|
// 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()?;
|
crossterm::terminal::disable_raw_mode()?;
|
||||||
execute!(
|
execute!(
|
||||||
|
|
|
||||||
49
cove-tui/src/textline.rs
Normal file
49
cove-tui/src/textline.rs
Normal 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);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue