Skip to content

Commit

Permalink
fix: Fix performance dropping rapidly after selecting a text for some…
Browse files Browse the repository at this point in the history
… time (#624)

* fix: Take the cursor position and selections the moment  it is measure instead of received by `use_editable` to avoid unnecessary measurements

* fix: Only take out the cursor and selection when it is the correct cursor id
  • Loading branch information
marc2332 authored Jun 1, 2024
1 parent 18d3e16 commit f27206b
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 156 deletions.
4 changes: 2 additions & 2 deletions crates/core/src/dom/doms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ impl FreyaDOM {
let node = rdom.get(*node_id);
let layout_node = layout.get(*node_id);
if let Some((node, layout_node)) = node.zip(layout_node) {
measure_paragraph(&node, layout_node, true, scale_factor);
measure_paragraph(&node, layout_node, scale_factor);
}
}
}
Expand All @@ -232,7 +232,7 @@ impl FreyaDOM {
let layout_node = layout.get(*node_id);

if let Some((node, layout_node)) = node.zip(layout_node) {
measure_paragraph(&node, layout_node, true, scale_factor);
measure_paragraph(&node, layout_node, scale_factor);
}
}
}
Expand Down
175 changes: 83 additions & 92 deletions crates/core/src/dom/paragraph_utils.rs
Original file line number Diff line number Diff line change
@@ -1,92 +1,83 @@
use freya_native_core::real_dom::NodeImmutable;

use freya_common::{CachedParagraph, CursorLayoutResponse};
use freya_node_state::{CursorReference, CursorSettings};
use std::ops::Mul;

use torin::prelude::*;

use crate::dom::DioxusNode;

pub fn measure_paragraph(
node: &DioxusNode,
layout_node: &LayoutNode,
is_editable: bool,
scale_factor: f32,
) {
let paragraph = &layout_node
.data
.as_ref()
.unwrap()
.get::<CachedParagraph>()
.unwrap()
.0;
let scale_factors = scale_factor as f64;

if is_editable {
if let Some((cursor_ref, id, cursor_position, cursor_selections)) =
get_cursor_reference(node)
{
if let Some(cursor_position) = cursor_position {
// Calculate the new cursor position
let char_position = paragraph.get_glyph_position_at_coordinate(
cursor_position.mul(scale_factors).to_i32().to_tuple(),
);

// Notify the cursor reference listener
cursor_ref
.cursor_sender
.send(CursorLayoutResponse::CursorPosition {
position: char_position.position as usize,
id,
})
.ok();
}

if let Some((origin, dist)) = cursor_selections {
// Calculate the start of the highlighting
let origin_char = paragraph.get_glyph_position_at_coordinate(
origin.mul(scale_factors).to_i32().to_tuple(),
);
// Calculate the end of the highlighting
let dist_char = paragraph
.get_glyph_position_at_coordinate(dist.mul(scale_factors).to_i32().to_tuple());

cursor_ref
.cursor_sender
.send(CursorLayoutResponse::TextSelection {
from: origin_char.position as usize,
to: dist_char.position as usize,
id,
})
.ok();
}
}
}
}

/// Get the info related to a cursor reference
#[allow(clippy::type_complexity)]
fn get_cursor_reference(
node: &DioxusNode,
) -> Option<(
CursorReference,
usize,
Option<CursorPoint>,
Option<(CursorPoint, CursorPoint)>,
)> {
let cursor_settings = node.get::<CursorSettings>().unwrap();

let cursor_ref = cursor_settings.cursor_ref.clone()?;
let cursor_id = cursor_settings.cursor_id?;

let current_cursor_id = { *cursor_ref.cursor_id.lock().unwrap().as_ref()? };
let cursor_selections = *cursor_ref.cursor_selections.lock().unwrap();
let cursor_position = *cursor_ref.cursor_position.lock().unwrap();

if current_cursor_id == cursor_id {
Some((cursor_ref, cursor_id, cursor_position, cursor_selections))
} else {
None
}
}
use freya_native_core::real_dom::NodeImmutable;

use freya_common::{CachedParagraph, CursorLayoutResponse};
use freya_node_state::{CursorReference, CursorSettings};
use std::ops::Mul;

use torin::prelude::*;

use crate::dom::DioxusNode;

pub fn measure_paragraph(node: &DioxusNode, layout_node: &LayoutNode, scale_factor: f32) {
let paragraph = &layout_node
.data
.as_ref()
.unwrap()
.get::<CachedParagraph>()
.unwrap()
.0;

let scale_factors = scale_factor as f64;

if let Some((cursor_ref, id, cursor_position, cursor_selections)) = get_cursor_reference(node) {
if let Some(cursor_position) = cursor_position {
// Calculate the new cursor position
let char_position = paragraph.get_glyph_position_at_coordinate(
cursor_position.mul(scale_factors).to_i32().to_tuple(),
);

// Notify the cursor reference listener
cursor_ref
.cursor_sender
.send(CursorLayoutResponse::CursorPosition {
position: char_position.position as usize,
id,
})
.ok();
}

if let Some((origin, dist)) = cursor_selections {
// Calculate the start of the highlighting
let origin_char = paragraph
.get_glyph_position_at_coordinate(origin.mul(scale_factors).to_i32().to_tuple());
// Calculate the end of the highlighting
let dist_char = paragraph
.get_glyph_position_at_coordinate(dist.mul(scale_factors).to_i32().to_tuple());

cursor_ref
.cursor_sender
.send(CursorLayoutResponse::TextSelection {
from: origin_char.position as usize,
to: dist_char.position as usize,
id,
})
.ok();
}
}
}

/// Get the info related to a cursor reference
#[allow(clippy::type_complexity)]
fn get_cursor_reference(
node: &DioxusNode,
) -> Option<(
CursorReference,
usize,
Option<CursorPoint>,
Option<(CursorPoint, CursorPoint)>,
)> {
let cursor_settings = node.get::<CursorSettings>().unwrap();

let cursor_ref = cursor_settings.cursor_ref.clone()?;
let cursor_id = cursor_settings.cursor_id?;

let current_cursor_id = *cursor_ref.cursor_id.lock().unwrap().as_ref()?;

if current_cursor_id == cursor_id {
let cursor_selections = cursor_ref.cursor_selections.lock().unwrap().take();
let cursor_position = cursor_ref.cursor_position.lock().unwrap().take();
Some((cursor_ref, cursor_id, cursor_position, cursor_selections))
} else {
None
}
}
112 changes: 51 additions & 61 deletions crates/hooks/src/use_editable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ use std::{
};

use dioxus_core::{prelude::spawn, use_hook, AttributeValue};
use dioxus_hooks::to_owned;
use dioxus_sdk::clipboard::use_clipboard;
use dioxus_signals::{Readable, Signal, Writable};
use freya_common::{CursorLayoutResponse, EventMessage};
Expand Down Expand Up @@ -180,70 +179,61 @@ pub fn use_editable(initializer: impl Fn() -> EditableConfig, mode: EditableMode
cursor_selections: Arc::new(Mutex::new(None)),
};

spawn({
to_owned![cursor_reference];
async move {
while let Some(message) = cursor_receiver.recv().await {
match message {
// Update the cursor position calculated by the layout
CursorLayoutResponse::CursorPosition { position, id } => {
let mut text_editor = editor.write();
spawn(async move {
while let Some(message) = cursor_receiver.recv().await {
match message {
// Update the cursor position calculated by the layout
CursorLayoutResponse::CursorPosition { position, id } => {
let mut text_editor = editor.write();

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

let new_cursor_col = match mode {
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)
}
};

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.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)
};

// Only update if it's actually different
if text_editor.cursor().as_tuple() != new_cursor {
text_editor.cursor_mut().set_col(new_cursor.0);
text_editor.cursor_mut().set_row(new_cursor.1);
text_editor.unhighlight();
let new_cursor_row = match mode {
EditableMode::MultipleLinesSingleEditor => {
text_editor.char_to_line(text_editor.utf16_cu_to_char(position))
}

// Remove the current calcutions so the layout engine doesn't try to calculate again
cursor_reference.set_cursor_position(None);
}
// Update the text selections calculated by the layout
CursorLayoutResponse::TextSelection { 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);
EditableMode::SingleLineMultipleEditors => id,
};

let new_cursor_col = match mode {
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)
}
};

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.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)
};

// Only update if it's actually different
if text_editor.cursor().as_tuple() != new_cursor {
text_editor.cursor_mut().set_col(new_cursor.0);
text_editor.cursor_mut().set_row(new_cursor.1);
text_editor.unhighlight();
}
}
// Update the text selections calculated by the layout
CursorLayoutResponse::TextSelection { 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);
}
}
}
});
Expand Down
2 changes: 1 addition & 1 deletion examples/simple_editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
use freya::prelude::*;

fn main() {
launch(app);
launch_with_props(app, "Simple editor", (900.0, 650.0));
}

fn app() -> Element {
Expand Down

0 comments on commit f27206b

Please sign in to comment.