Update toss to version with separate widthdb

This commit is contained in:
Joscha 2022-09-26 17:27:47 +02:00
parent 5ed0cd5f3f
commit 61a9cc10f1
8 changed files with 101 additions and 94 deletions

2
Cargo.lock generated
View file

@ -1214,7 +1214,7 @@ dependencies = [
[[package]]
name = "toss"
version = "0.1.0"
source = "git+https://github.com/Garmelon/toss.git?rev=24fd0050fbfdd72ac2f03029148370b362291777#24fd0050fbfdd72ac2f03029148370b362291777"
source = "git+https://github.com/Garmelon/toss.git?rev=06aefd562bd66f5564f7c1ea73a4959f51be74e7#06aefd562bd66f5564f7c1ea73a4959f51be74e7"
dependencies = [
"crossterm",
"unicode-linebreak",

View file

@ -41,7 +41,7 @@ rev = "bdb17db5bf4796696d6375168abb76920ab11b87"
[dependencies.toss]
git = "https://github.com/Garmelon/toss.git"
rev = "24fd0050fbfdd72ac2f03029148370b362291777"
rev = "06aefd562bd66f5564f7c1ea73a4959f51be74e7"
# [patch."https://github.com/Garmelon/toss.git"]
# toss = { path = "../toss/" }

View file

@ -1,5 +1,3 @@
// TODO Reduce need to pass Terminal when only width db is needed
mod chat;
mod euph;
mod input;

View file

@ -75,7 +75,8 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
frame: &mut Frame,
indent: usize,
) -> Block<BlockId<M::Id>> {
let (widget, cursor_row) = widgets::editor::<M>(frame, indent, &context.nick, &self.editor);
let (widget, cursor_row) =
widgets::editor::<M>(frame.widthdb(), indent, &context.nick, &self.editor);
let cursor_row = cursor_row as i32;
Block::new(frame, BlockId::Cursor, widget).focus(cursor_row..cursor_row + 1)
}

View file

@ -3,8 +3,8 @@ mod seen;
mod time;
use crossterm::style::{ContentStyle, Stylize};
use toss::frame::Frame;
use toss::styled::Styled;
use toss::widthdb::WidthDb;
use super::super::ChatMsg;
use crate::store::Msg;
@ -111,14 +111,14 @@ pub fn msg_placeholder(
}
pub fn editor<M: ChatMsg>(
frame: &mut Frame,
widthdb: &mut WidthDb,
indent: usize,
nick: &str,
editor: &EditorState,
) -> (BoxedWidget, usize) {
let (nick, content) = M::edit(nick, &editor.text());
let editor = editor.widget().highlight(|_| content);
let cursor_row = editor.cursor_row(frame);
let cursor_row = editor.cursor_row(widthdb);
let widget = HJoin::new(vec![
Segment::new(seen::widget(true)),

View file

@ -98,10 +98,10 @@ pub fn handle_editor_input_event(
InputEvent::Key(crate::ui::input::KeyEvent {
code: crossterm::event::KeyCode::Enter,
..
}) if char_filter('\n') => editor.insert_char(terminal.frame(), '\n'),
}) if char_filter('\n') => editor.insert_char(terminal.widthdb(), '\n'),
// Editing
key!(Char ch) if char_filter(*ch) => editor.insert_char(terminal.frame(), *ch),
key!(Char ch) if char_filter(*ch) => editor.insert_char(terminal.widthdb(), *ch),
key!(Paste str) => {
// It seems that when pasting, '\n' are converted into '\r' for some
// reason. I don't really know why, or at what point this happens.
@ -109,25 +109,25 @@ pub fn handle_editor_input_event(
// decided to mirror that behaviour.
let str = str.replace('\r', "\n");
if str.chars().all(char_filter) {
editor.insert_str(terminal.frame(), &str);
editor.insert_str(terminal.widthdb(), &str);
} else {
return false;
}
}
key!(Ctrl + 'h') | key!(Backspace) => editor.backspace(terminal.frame()),
key!(Ctrl + 'h') | key!(Backspace) => editor.backspace(terminal.widthdb()),
key!(Ctrl + 'd') | key!(Delete) => editor.delete(),
key!(Ctrl + 'l') => editor.clear(),
// TODO Key bindings to delete words
// Cursor movement
key!(Ctrl + 'b') | key!(Left) => editor.move_cursor_left(terminal.frame()),
key!(Ctrl + 'f') | key!(Right) => editor.move_cursor_right(terminal.frame()),
key!(Alt + 'b') | key!(Ctrl + Left) => editor.move_cursor_left_a_word(terminal.frame()),
key!(Alt + 'f') | key!(Ctrl + Right) => editor.move_cursor_right_a_word(terminal.frame()),
key!(Ctrl + 'a') | key!(Home) => editor.move_cursor_to_start_of_line(terminal.frame()),
key!(Ctrl + 'e') | key!(End) => editor.move_cursor_to_end_of_line(terminal.frame()),
key!(Up) => editor.move_cursor_up(terminal.frame()),
key!(Down) => editor.move_cursor_down(terminal.frame()),
key!(Ctrl + 'b') | key!(Left) => editor.move_cursor_left(terminal.widthdb()),
key!(Ctrl + 'f') | key!(Right) => editor.move_cursor_right(terminal.widthdb()),
key!(Alt + 'b') | key!(Ctrl + Left) => editor.move_cursor_left_a_word(terminal.widthdb()),
key!(Alt + 'f') | key!(Ctrl + Right) => editor.move_cursor_right_a_word(terminal.widthdb()),
key!(Ctrl + 'a') | key!(Home) => editor.move_cursor_to_start_of_line(terminal.widthdb()),
key!(Ctrl + 'e') | key!(End) => editor.move_cursor_to_end_of_line(terminal.widthdb()),
key!(Up) => editor.move_cursor_up(terminal.widthdb()),
key!(Down) => editor.move_cursor_down(terminal.widthdb()),
_ => return false,
}

View file

@ -7,6 +7,7 @@ use parking_lot::{FairMutex, Mutex};
use toss::frame::{Frame, Pos, Size};
use toss::styled::Styled;
use toss::terminal::Terminal;
use toss::widthdb::WidthDb;
use unicode_segmentation::UnicodeSegmentation;
use crate::ui::util;
@ -14,10 +15,10 @@ use crate::ui::util;
use super::text::Text;
use super::Widget;
/// Like [`Frame::wrap`] but includes a final break index if the text ends with
/// a newline.
fn wrap(frame: &mut Frame, text: &str, width: usize) -> Vec<usize> {
let mut breaks = frame.wrap(text, width);
/// Like [`WidthDb::wrap`] but includes a final break index if the text ends
/// with a newline.
fn wrap(widthdb: &mut WidthDb, text: &str, width: usize) -> Vec<usize> {
let mut breaks = widthdb.wrap(text, width);
if text.ends_with('\n') {
breaks.push(text.len())
}
@ -125,8 +126,8 @@ impl InnerEditorState {
result
}
fn cursor_col(&self, frame: &mut Frame, line_start: usize) -> usize {
frame.width(&self.text[line_start..self.idx])
fn cursor_col(&self, widthdb: &mut WidthDb, line_start: usize) -> usize {
widthdb.width(&self.text[line_start..self.idx])
}
fn line(&self, line: usize) -> (usize, usize) {
@ -139,7 +140,7 @@ impl InnerEditorState {
.expect("line exists")
}
fn move_cursor_to_line_col(&mut self, frame: &mut Frame, line: usize, col: usize) {
fn move_cursor_to_line_col(&mut self, widthdb: &mut WidthDb, line: usize, col: usize) {
let (start, end) = self.line(line);
let line = &self.text[start..end];
@ -147,7 +148,7 @@ impl InnerEditorState {
for (gi, g) in line.grapheme_indices(true) {
self.idx = start + gi;
if col > width {
width += frame.grapheme_width(g, width) as usize;
width += widthdb.grapheme_width(g, width) as usize;
} else {
return;
}
@ -158,10 +159,10 @@ impl InnerEditorState {
}
}
fn record_cursor_col(&mut self, frame: &mut Frame) {
fn record_cursor_col(&mut self, widthdb: &mut WidthDb) {
let boundaries = self.line_boundaries();
let (_, start, _) = self.cursor_line(&boundaries);
self.col = self.cursor_col(frame, start);
self.col = self.cursor_col(widthdb, start);
}
/////////////
@ -174,36 +175,36 @@ impl InnerEditorState {
self.col = 0;
}
fn set_text(&mut self, frame: &mut Frame, text: String) {
fn set_text(&mut self, widthdb: &mut WidthDb, text: String) {
self.text = text;
self.move_cursor_to_grapheme_boundary();
self.record_cursor_col(frame);
self.record_cursor_col(widthdb);
}
/// Insert a character at the current cursor position and move the cursor
/// accordingly.
fn insert_char(&mut self, frame: &mut Frame, ch: char) {
fn insert_char(&mut self, widthdb: &mut WidthDb, ch: char) {
self.text.insert(self.idx, ch);
self.idx += ch.len_utf8();
self.record_cursor_col(frame);
self.record_cursor_col(widthdb);
}
/// Insert a string at the current cursor position and move the cursor
/// accordingly.
fn insert_str(&mut self, frame: &mut Frame, str: &str) {
fn insert_str(&mut self, widthdb: &mut WidthDb, str: &str) {
self.text.insert_str(self.idx, str);
self.idx += str.len();
self.record_cursor_col(frame);
self.record_cursor_col(widthdb);
}
/// Delete the grapheme before the cursor position.
fn backspace(&mut self, frame: &mut Frame) {
fn backspace(&mut self, widthdb: &mut WidthDb) {
let boundaries = self.grapheme_boundaries();
for (start, end) in boundaries.iter().zip(boundaries.iter().skip(1)) {
if *end == self.idx {
self.text.replace_range(start..end, "");
self.idx = *start;
self.record_cursor_col(frame);
self.record_cursor_col(widthdb);
break;
}
}
@ -224,29 +225,29 @@ impl InnerEditorState {
// Cursor movement //
/////////////////////
fn move_cursor_left(&mut self, frame: &mut Frame) {
fn move_cursor_left(&mut self, widthdb: &mut WidthDb) {
let boundaries = self.grapheme_boundaries();
for (start, end) in boundaries.iter().zip(boundaries.iter().skip(1)) {
if *end == self.idx {
self.idx = *start;
self.record_cursor_col(frame);
self.record_cursor_col(widthdb);
break;
}
}
}
fn move_cursor_right(&mut self, frame: &mut Frame) {
fn move_cursor_right(&mut self, widthdb: &mut WidthDb) {
let boundaries = self.grapheme_boundaries();
for (start, end) in boundaries.iter().zip(boundaries.iter().skip(1)) {
if *start == self.idx {
self.idx = *end;
self.record_cursor_col(frame);
self.record_cursor_col(widthdb);
break;
}
}
}
fn move_cursor_left_a_word(&mut self, frame: &mut Frame) {
fn move_cursor_left_a_word(&mut self, widthdb: &mut WidthDb) {
let boundaries = self.grapheme_boundaries();
let mut encountered_word = false;
for (start, end) in boundaries.iter().zip(boundaries.iter().skip(1)).rev() {
@ -261,10 +262,10 @@ impl InnerEditorState {
self.idx = *start;
}
}
self.record_cursor_col(frame);
self.record_cursor_col(widthdb);
}
fn move_cursor_right_a_word(&mut self, frame: &mut Frame) {
fn move_cursor_right_a_word(&mut self, widthdb: &mut WidthDb) {
let boundaries = self.grapheme_boundaries();
let mut encountered_word = false;
for (start, end) in boundaries.iter().zip(boundaries.iter().skip(1)) {
@ -279,32 +280,32 @@ impl InnerEditorState {
self.idx = *end;
}
}
self.record_cursor_col(frame);
self.record_cursor_col(widthdb);
}
fn move_cursor_to_start_of_line(&mut self, frame: &mut Frame) {
fn move_cursor_to_start_of_line(&mut self, widthdb: &mut WidthDb) {
let boundaries = self.line_boundaries();
let (line, _, _) = self.cursor_line(&boundaries);
self.move_cursor_to_line_col(frame, line, 0);
self.record_cursor_col(frame);
self.move_cursor_to_line_col(widthdb, line, 0);
self.record_cursor_col(widthdb);
}
fn move_cursor_to_end_of_line(&mut self, frame: &mut Frame) {
fn move_cursor_to_end_of_line(&mut self, widthdb: &mut WidthDb) {
let boundaries = self.line_boundaries();
let (line, _, _) = self.cursor_line(&boundaries);
self.move_cursor_to_line_col(frame, line, usize::MAX);
self.record_cursor_col(frame);
self.move_cursor_to_line_col(widthdb, line, usize::MAX);
self.record_cursor_col(widthdb);
}
fn move_cursor_up(&mut self, frame: &mut Frame) {
fn move_cursor_up(&mut self, widthdb: &mut WidthDb) {
let boundaries = self.line_boundaries();
let (line, _, _) = self.cursor_line(&boundaries);
if line > 0 {
self.move_cursor_to_line_col(frame, line - 1, self.col);
self.move_cursor_to_line_col(widthdb, line - 1, self.col);
}
}
fn move_cursor_down(&mut self, frame: &mut Frame) {
fn move_cursor_down(&mut self, widthdb: &mut WidthDb) {
let boundaries = self.line_boundaries();
// There's always at least one line, and always at least two line
@ -313,7 +314,7 @@ impl InnerEditorState {
let (line, _, _) = self.cursor_line(&boundaries);
if line + 1 < amount_of_lines {
self.move_cursor_to_line_col(frame, line + 1, self.col);
self.move_cursor_to_line_col(widthdb, line + 1, self.col);
}
}
}
@ -350,21 +351,21 @@ impl EditorState {
self.0.lock().clear();
}
pub fn set_text(&self, frame: &mut Frame, text: String) {
self.0.lock().set_text(frame, text);
pub fn set_text(&self, widthdb: &mut WidthDb, text: String) {
self.0.lock().set_text(widthdb, text);
}
pub fn insert_char(&self, frame: &mut Frame, ch: char) {
self.0.lock().insert_char(frame, ch);
pub fn insert_char(&self, widthdb: &mut WidthDb, ch: char) {
self.0.lock().insert_char(widthdb, ch);
}
pub fn insert_str(&self, frame: &mut Frame, str: &str) {
self.0.lock().insert_str(frame, str);
pub fn insert_str(&self, widthdb: &mut WidthDb, str: &str) {
self.0.lock().insert_str(widthdb, str);
}
/// Delete the grapheme before the cursor position.
pub fn backspace(&self, frame: &mut Frame) {
self.0.lock().backspace(frame);
pub fn backspace(&self, widthdb: &mut WidthDb) {
self.0.lock().backspace(widthdb);
}
/// Delete the grapheme after the cursor position.
@ -372,36 +373,36 @@ impl EditorState {
self.0.lock().delete();
}
pub fn move_cursor_left(&self, frame: &mut Frame) {
self.0.lock().move_cursor_left(frame);
pub fn move_cursor_left(&self, widthdb: &mut WidthDb) {
self.0.lock().move_cursor_left(widthdb);
}
pub fn move_cursor_right(&self, frame: &mut Frame) {
self.0.lock().move_cursor_right(frame);
pub fn move_cursor_right(&self, widthdb: &mut WidthDb) {
self.0.lock().move_cursor_right(widthdb);
}
pub fn move_cursor_left_a_word(&self, frame: &mut Frame) {
self.0.lock().move_cursor_left_a_word(frame);
pub fn move_cursor_left_a_word(&self, widthdb: &mut WidthDb) {
self.0.lock().move_cursor_left_a_word(widthdb);
}
pub fn move_cursor_right_a_word(&self, frame: &mut Frame) {
self.0.lock().move_cursor_right_a_word(frame);
pub fn move_cursor_right_a_word(&self, widthdb: &mut WidthDb) {
self.0.lock().move_cursor_right_a_word(widthdb);
}
pub fn move_cursor_to_start_of_line(&self, frame: &mut Frame) {
self.0.lock().move_cursor_to_start_of_line(frame);
pub fn move_cursor_to_start_of_line(&self, widthdb: &mut WidthDb) {
self.0.lock().move_cursor_to_start_of_line(widthdb);
}
pub fn move_cursor_to_end_of_line(&self, frame: &mut Frame) {
self.0.lock().move_cursor_to_end_of_line(frame);
pub fn move_cursor_to_end_of_line(&self, widthdb: &mut WidthDb) {
self.0.lock().move_cursor_to_end_of_line(widthdb);
}
pub fn move_cursor_up(&self, frame: &mut Frame) {
self.0.lock().move_cursor_up(frame);
pub fn move_cursor_up(&self, widthdb: &mut WidthDb) {
self.0.lock().move_cursor_up(widthdb);
}
pub fn move_cursor_down(&self, frame: &mut Frame) {
self.0.lock().move_cursor_down(frame);
pub fn move_cursor_down(&self, widthdb: &mut WidthDb) {
self.0.lock().move_cursor_down(widthdb);
}
pub fn edit_externally(
@ -422,9 +423,9 @@ impl EditorState {
// Some editors like vim add a trailing newline that would look out
// of place in cove's editor. To intentionally add a trailing
// newline, simply add two in-editor.
guard.set_text(terminal.frame(), text.to_string());
guard.set_text(terminal.widthdb(), text.to_string());
} else {
guard.set_text(terminal.frame(), text);
guard.set_text(terminal.widthdb(), text);
}
Ok(())
@ -484,10 +485,10 @@ impl Editor {
(row, line_idx)
}
pub fn cursor_row(&self, frame: &mut Frame) -> usize {
pub fn cursor_row(&self, widthdb: &mut WidthDb) -> usize {
let width = self.state.lock().last_width;
let text_width = (width - 1) as usize;
let indices = wrap(frame, self.text.text(), text_width);
let indices = wrap(widthdb, self.text.text(), text_width);
let (row, _) = Self::wrapped_cursor(self.idx, &indices);
row
}
@ -506,14 +507,16 @@ impl Widget for Editor {
return size;
}
let widthdb = frame.widthdb();
let max_width = max_width.map(|w| w as usize).unwrap_or(usize::MAX).max(1);
let max_text_width = max_width - 1;
let indices = wrap(frame, self.text.text(), max_text_width);
let indices = wrap(widthdb, self.text.text(), max_text_width);
let lines = self.text.clone().split_at_indices(&indices);
let min_width = lines
.iter()
.map(|l| frame.width(l.text().trim_end()))
.map(|l| widthdb.width(l.text().trim_end()))
.max()
.unwrap_or(0)
+ 1;
@ -532,15 +535,18 @@ impl Widget for Editor {
return;
}
let width = frame.size().width.max(1);
let size = frame.size();
let widthdb = frame.widthdb();
let width = size.width.max(1);
let text_width = (width - 1) as usize;
let indices = wrap(frame, self.text.text(), text_width);
let indices = wrap(widthdb, self.text.text(), text_width);
let lines = self.text.split_at_indices(&indices);
// Determine cursor position now while we still have the lines.
let cursor_pos = if self.focus {
let (cursor_row, cursor_line_idx) = Self::wrapped_cursor(self.idx, &indices);
let cursor_col = frame.width(lines[cursor_row].text().split_at(cursor_line_idx).0);
let cursor_col = widthdb.width(lines[cursor_row].text().split_at(cursor_line_idx).0);
let cursor_col = cursor_col.min(text_width);
Some(Pos::new(cursor_col as i32, cursor_row as i32))
} else {

View file

@ -1,6 +1,7 @@
use async_trait::async_trait;
use toss::frame::{Frame, Pos, Size};
use toss::styled::Styled;
use toss::widthdb::WidthDb;
use super::Widget;
@ -23,14 +24,14 @@ impl Text {
self
}
fn wrapped(&self, frame: &mut Frame, max_width: Option<u16>) -> Vec<Styled> {
fn wrapped(&self, widthdb: &mut WidthDb, max_width: Option<u16>) -> Vec<Styled> {
let max_width = if self.wrap {
max_width.map(|w| w as usize).unwrap_or(usize::MAX)
} else {
usize::MAX
};
let indices = frame.wrap(self.styled.text(), max_width);
let indices = widthdb.wrap(self.styled.text(), max_width);
self.styled.clone().split_at_indices(&indices)
}
}
@ -38,10 +39,11 @@ impl Text {
#[async_trait]
impl Widget for Text {
fn size(&self, frame: &mut Frame, max_width: Option<u16>, _max_height: Option<u16>) -> Size {
let lines = self.wrapped(frame, max_width);
let lines = self.wrapped(frame.widthdb(), max_width);
let widthdb = frame.widthdb();
let min_width = lines
.iter()
.map(|l| frame.width(l.text().trim_end()))
.map(|l| widthdb.width(l.text().trim_end()))
.max()
.unwrap_or(0);
let min_height = lines.len();
@ -51,7 +53,7 @@ impl Widget for Text {
async fn render(self: Box<Self>, frame: &mut Frame) {
let size = frame.size();
for (i, line) in self
.wrapped(frame, Some(size.width))
.wrapped(frame.widthdb(), Some(size.width))
.into_iter()
.enumerate()
{