Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
7de508b
feat: Improved text editing support
marc2332 May 26, 2024
1fecea5
some improvements
marc2332 May 26, 2024
888865e
Clean up and simplify
marc2332 May 26, 2024
8dcd1ab
cleanup and fixes
marc2332 May 26, 2024
a30d2f1
cleanup
marc2332 May 26, 2024
4447bdc
Merge branch 'main' into feat/improved-text-editing-support
marc2332 May 28, 2024
ccc8c89
use chars len instead of bytes len in EditorHistory
marc2332 May 28, 2024
599f244
fix: Move cursor accordingly
marc2332 May 28, 2024
2f578ee
chore: Clean up
marc2332 May 31, 2024
aeb840c
Merge branch 'main' into feat/improved-text-editing-support
marc2332 May 31, 2024
c09b6c3
chore: Clean up
marc2332 May 31, 2024
1284c11
chore: Add test
marc2332 Jun 1, 2024
22a19fb
chore: Update tests
marc2332 Jun 1, 2024
0e8e223
chore: Update tests
marc2332 Jun 1, 2024
a25a8d8
chore: Update tests
marc2332 Jun 1, 2024
8960441
chore: Update tests
marc2332 Jun 1, 2024
4ad0059
chore: Update tests
marc2332 Jun 1, 2024
e707cd2
chore: Update tests
marc2332 Jun 1, 2024
9fdc35f
chore: Update tests
marc2332 Jun 1, 2024
8ecb2d3
chore: Update tests
marc2332 Jun 1, 2024
94d4fac
chore: Update tests
marc2332 Jun 1, 2024
5b3c593
chore: Update tests
marc2332 Jun 1, 2024
af6fff5
chore: Update tests
marc2332 Jun 1, 2024
1668456
chore: Update tests
marc2332 Jun 1, 2024
883e43a
chore: Update tests
marc2332 Jun 1, 2024
bf7a2ed
chore: Update tests
marc2332 Jun 1, 2024
c0b9bfb
chore: Update tests
marc2332 Jun 1, 2024
2f4d1a3
chore: Clean up
marc2332 Jun 1, 2024
b05f881
chore: Clean up
marc2332 Jun 1, 2024
fd34300
chore: Clean up
marc2332 Jun 1, 2024
2da0314
chore: Clean up
marc2332 Jun 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/components/src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ pub fn Input(
let (background, cursor_char) = if focus.is_focused() {
(
theme.hover_background,
editable.editor().read().cursor_pos().to_string(),
editable.editor().read().visible_cursor_pos().to_string(),
)
} else {
(theme.background, "none".to_string())
Expand Down
1 change: 1 addition & 0 deletions crates/engine/src/skia.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pub use skia_safe::{
path::ArcSize,
rrect::Corner,
runtime_effect::Uniform,
surfaces::raster_n32_premul,
svg,
textlayout::{
paragraph::GlyphClusterInfo, Decoration, FontCollection, FontFeature, LineMetrics,
Expand Down
10 changes: 5 additions & 5 deletions crates/hooks/src/editor_history.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,10 @@ impl EditorHistory {
let idx_end = match last_change {
HistoryChange::Remove { idx, text } => {
rope.insert(*idx, text);
idx + text.len()
idx + text.chars().count()
}
HistoryChange::InsertChar { idx, .. } => {
rope.remove(*idx..*idx + 1);
HistoryChange::InsertChar { idx, char: ch } => {
rope.remove(*idx..*idx + ch.len_utf8());
*idx
}
HistoryChange::InsertText { idx, text } => {
Expand All @@ -85,7 +85,7 @@ impl EditorHistory {
if let Some(next_change) = next_change {
let idx_end = match next_change {
HistoryChange::Remove { idx, text } => {
rope.remove(*idx..idx + text.len());
rope.remove(*idx..idx + text.chars().count());
*idx
}
HistoryChange::InsertChar { idx, char: ch } => {
Expand All @@ -94,7 +94,7 @@ impl EditorHistory {
}
HistoryChange::InsertText { idx, text, .. } => {
rope.insert(*idx, text);
idx + text.len()
idx + text.chars().count()
}
};
self.current_change += 1;
Expand Down
41 changes: 26 additions & 15 deletions crates/hooks/src/rope_editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,14 @@ impl TextEditor for RopeEditor {
self.rope.line_to_char(line_idx)
}

fn utf16_cu_to_char(&self, utf16_cu_idx: usize) -> usize {
self.rope.utf16_cu_to_char(utf16_cu_idx)
}

fn char_to_utf16_cu(&self, idx: usize) -> usize {
self.rope.char_to_utf16_cu(idx)
}

fn line(&self, line_idx: usize) -> Option<Line<'_>> {
let line = self.rope.get_line(line_idx);

Expand Down Expand Up @@ -145,43 +153,46 @@ impl TextEditor for RopeEditor {
return Some((0, len));
}

match selected_from_row.cmp(&selected_to_row) {
let highlights = match selected_from_row.cmp(&selected_to_row) {
// Selection direction is from bottom -> top
Ordering::Greater => {
if selected_from_row == editor_id {
// Starting line
return Some((0, selected_from_col_idx));
Some((0, selected_from_col_idx))
} else if selected_to_row == editor_id {
// Ending line
let len = self.line(selected_to_row).unwrap().len_chars();
return Some((selected_to_col_idx, len));
Some((selected_to_col_idx, len))
} else {
None
}
}
// Selection direction is from top -> bottom
Ordering::Less => {
if selected_from_row == editor_id {
// Starting line
let len = self.line(selected_from_row).unwrap().len_chars();
return Some((selected_from_col_idx, len));
Some((selected_from_col_idx, len))
} else if selected_to_row == editor_id {
// Ending line
return Some((0, selected_to_col_idx));
Some((0, selected_to_col_idx))
} else {
None
}
}
Ordering::Equal => {
Ordering::Equal if selected_from_row == editor_id => {
// Starting and endline line are the same
if selected_from_row == editor_id {
return Some((
selected_from - editor_row_idx,
selected_to - editor_row_idx,
));
}
Some((selected_from - editor_row_idx, selected_to - editor_row_idx))
}
}
_ => None,
};

None
highlights.map(|(from, to)| (self.char_to_utf16_cu(from), self.char_to_utf16_cu(to)))
} else {
Some((selected_from, selected_to))
Some((
self.char_to_utf16_cu(selected_from),
self.char_to_utf16_cu(selected_to),
))
}
}

Expand Down
40 changes: 28 additions & 12 deletions crates/hooks/src/text_editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ impl Line<'_> {
self.text.chars().filter(|c| c != &'\r').count()
}

/// Get the length of the line
pub fn utf16_len_chars(&self) -> usize {
self.text.encode_utf16().count()
}

/// Get the text of the line
fn as_str(&self) -> &str {
&self.text
Expand Down Expand Up @@ -110,6 +115,10 @@ pub trait TextEditor {
/// Get the first char from the given line
fn line_to_char(&self, line_idx: usize) -> usize;

fn utf16_cu_to_char(&self, utf16_cu_idx: usize) -> usize;

fn char_to_utf16_cu(&self, idx: usize) -> usize;

/// Get a line from the text
fn line(&self, line_idx: usize) -> Option<Line<'_>>;

Expand All @@ -132,6 +141,11 @@ pub trait TextEditor {
self.cursor().col()
}

/// Get the visible cursor position
fn visible_cursor_col(&self) -> usize {
self.char_to_utf16_cu(self.cursor_col())
}

/// Move the cursor 1 line down
fn cursor_down(&mut self) {
let new_row = self.cursor_row() + 1;
Expand Down Expand Up @@ -162,6 +176,12 @@ pub trait TextEditor {
line_begining + self.cursor_col()
}

/// Get the cursor position
fn visible_cursor_pos(&self) -> usize {
let line_begining = self.char_to_utf16_cu(self.line_to_char(self.cursor_row()));
line_begining + self.char_to_utf16_cu(self.cursor_col())
}

/// Set the cursor position
fn set_cursor_pos(&mut self, pos: usize) {
let row = self.char_to_line(pos);
Expand Down Expand Up @@ -450,21 +470,17 @@ pub trait TextEditor {

_ => {
if let Ok(ch) = character.parse::<char>() {
// https://github.com/marc2332/freya/issues/461
if !ch.is_ascii_control() && ch.len_utf8() <= 2 {
// Inserts a character
let char_idx =
self.line_to_char(self.cursor_row()) + self.cursor_col();
self.insert(character, char_idx);
self.cursor_right();

event.insert(TextEvent::TEXT_CHANGED);
}
} else if character.is_ascii() {
// Inserts a character
let char_idx = self.line_to_char(self.cursor_row()) + self.cursor_col();
self.insert_char(ch, char_idx);
self.cursor_right();

event.insert(TextEvent::TEXT_CHANGED);
} else {
// Inserts a text
let char_idx = self.line_to_char(self.cursor_row()) + self.cursor_col();
self.insert(character, char_idx);
self.set_cursor_pos(char_idx + character.len());
self.set_cursor_pos(char_idx + character.chars().count());

event.insert(TextEvent::TEXT_CHANGED);
}
Expand Down
30 changes: 23 additions & 7 deletions crates/hooks/src/use_editable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,23 +191,34 @@ pub fn use_editable(initializer: impl Fn() -> EditableConfig, mode: EditableMode

let new_cursor_row = match mode {
EditableMode::MultipleLinesSingleEditor => {
text_editor.char_to_line(position)
text_editor.char_to_line(text_editor.utf16_cu_to_char(position))
}
EditableMode::SingleLineMultipleEditors => id,
};

let new_cursor_col = match mode {
EditableMode::MultipleLinesSingleEditor => {
position - text_editor.line_to_char(new_cursor_row)
EditableMode::MultipleLinesSingleEditor => text_editor
.utf16_cu_to_char(
position
- text_editor.char_to_utf16_cu(
text_editor.line_to_char(new_cursor_row),
),
),
EditableMode::SingleLineMultipleEditors => {
text_editor.utf16_cu_to_char(position)
}
EditableMode::SingleLineMultipleEditors => position,
};

let new_current_line = text_editor.line(new_cursor_row).unwrap();

// Use the line length as new column if the clicked column surpases the length
let new_cursor = if new_cursor_col >= new_current_line.len_chars() {
(new_current_line.len_chars(), new_cursor_row)
let new_cursor = if new_cursor_col >= new_current_line.utf16_len_chars()
{
(
text_editor
.utf16_cu_to_char(new_current_line.utf16_len_chars()),
new_cursor_row,
)
} else {
(new_cursor_col, new_cursor_row)
};
Expand All @@ -224,7 +235,12 @@ pub fn use_editable(initializer: impl Fn() -> EditableConfig, mode: EditableMode
}
// Update the text selections calculated by the layout
CursorLayoutResponse::TextSelection { from, to, id } => {
editor.write().highlight_text(from, to, id);
let mut text_editor = editor.write();
let (from, to) = (
text_editor.utf16_cu_to_char(from),
text_editor.utf16_cu_to_char(to),
);
text_editor.highlight_text(from, to, id);
cursor_reference.set_cursor_selections(None);
}
}
Expand Down
Loading