mirror of
https://github.com/penpot/penpot.git
synced 2026-05-10 10:38:17 +00:00
♻️ Refactor text editor state (#9379)
This commit is contained in:
parent
c5f2ffab69
commit
bc0f081371
@ -18,6 +18,7 @@ use std::collections::HashMap;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use crate::error::{Error, Result};
|
||||
use crate::state::TextEditorState;
|
||||
use macros::wasm_error;
|
||||
use math::{Bounds, Matrix};
|
||||
use mem::SerializableResult;
|
||||
@ -28,6 +29,15 @@ use utils::uuid_from_u32_quartet;
|
||||
use uuid::Uuid;
|
||||
|
||||
pub(crate) static mut STATE: Option<Box<State>> = None;
|
||||
pub(crate) static mut TEXT_EDITOR_STATE: *mut TextEditorState = std::ptr::null_mut();
|
||||
|
||||
#[inline(always)]
|
||||
pub fn get_text_editor_state() -> &'static mut TextEditorState {
|
||||
unsafe {
|
||||
debug_assert!(!TEXT_EDITOR_STATE.is_null(), "Text Editor state is null");
|
||||
&mut *TEXT_EDITOR_STATE
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: These with_state* macros should be using our CriticalError instead of expect.
|
||||
// But to do that, we need to not use them at domain-level (i.e. in business logic), just
|
||||
@ -107,6 +117,7 @@ pub extern "C" fn init(width: i32, height: i32) -> Result<()> {
|
||||
let state_box = Box::new(State::try_new(width, height)?);
|
||||
unsafe {
|
||||
STATE = Some(state_box);
|
||||
TEXT_EDITOR_STATE = Box::into_raw(Box::new(TextEditorState::new()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -199,6 +199,12 @@ pub struct TextContentLayout {
|
||||
cached_extrect: Cell<Option<CachedExtrect>>,
|
||||
}
|
||||
|
||||
impl Default for TextContentLayout {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for TextContentLayout {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
|
||||
@ -21,7 +21,6 @@ use crate::shapes::modifiers::grid_layout::grid_cell_data;
|
||||
/// must not be shared between different Web Workers.
|
||||
pub(crate) struct State {
|
||||
pub render_state: RenderState,
|
||||
pub text_editor_state: TextEditorState,
|
||||
pub current_id: Option<Uuid>,
|
||||
pub current_browser: u8,
|
||||
pub shapes: ShapesPool,
|
||||
@ -34,7 +33,7 @@ impl State {
|
||||
pub fn try_new(width: i32, height: i32) -> Result<Self> {
|
||||
Ok(State {
|
||||
render_state: RenderState::try_new(width, height)?,
|
||||
text_editor_state: TextEditorState::new(),
|
||||
// text_editor_state: TextEditorState::new(),
|
||||
current_id: None,
|
||||
current_browser: 0,
|
||||
shapes: ShapesPool::new(),
|
||||
@ -79,16 +78,6 @@ impl State {
|
||||
&self.render_state
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn text_editor_state_mut(&mut self) -> &mut TextEditorState {
|
||||
&mut self.text_editor_state
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn text_editor_state(&self) -> &TextEditorState {
|
||||
&self.text_editor_state
|
||||
}
|
||||
|
||||
pub fn render_from_cache(&mut self) {
|
||||
self.render_state.render_from_cache(&self.shapes);
|
||||
}
|
||||
|
||||
@ -392,3 +392,9 @@ impl ShapesPoolImpl {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ShapesPoolImpl {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
@ -255,6 +255,12 @@ impl TextEditorStyles {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for TextEditorStyles {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TextEditorTheme {
|
||||
pub selection_color: Color,
|
||||
pub cursor_color: Color,
|
||||
@ -319,6 +325,12 @@ impl TextComposition {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for TextComposition {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TextEditorState {
|
||||
pub theme: TextEditorTheme,
|
||||
pub selection: TextSelection,
|
||||
@ -880,6 +892,12 @@ impl TextEditorState {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for TextEditorState {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
fn is_word_char(c: char) -> bool {
|
||||
c.is_alphanumeric() || c == '_'
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
use macros::{wasm_error, ToJs};
|
||||
|
||||
use crate::get_text_editor_state;
|
||||
use crate::math::{Matrix, Point, Rect};
|
||||
use crate::mem;
|
||||
use crate::render::text_editor as text_editor_render;
|
||||
@ -33,12 +34,10 @@ pub enum CursorDirection {
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn text_editor_apply_theme(selection_color: u32, cursor_color: u32) {
|
||||
with_state_mut!(state, {
|
||||
// NOTE: In the future could be interesting to fill al this data from
|
||||
// a structure pointer.
|
||||
state.text_editor_state.theme.selection_color = Color::new(selection_color);
|
||||
state.text_editor_state.theme.cursor_color = Color::new(cursor_color);
|
||||
})
|
||||
// NOTE: In the future could be interesting to fill al this data from
|
||||
// a structure pointer.
|
||||
get_text_editor_state().theme.selection_color = Color::new(selection_color);
|
||||
get_text_editor_state().theme.cursor_color = Color::new(cursor_color);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@ -54,74 +53,66 @@ pub extern "C" fn text_editor_focus(a: u32, b: u32, c: u32, d: u32) -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
state.text_editor_state.focus(shape_id);
|
||||
get_text_editor_state().focus(shape_id);
|
||||
true
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn text_editor_blur() -> bool {
|
||||
with_state_mut!(state, {
|
||||
if !state.text_editor_state.has_focus {
|
||||
return false;
|
||||
}
|
||||
state.text_editor_state.blur();
|
||||
true
|
||||
})
|
||||
if !get_text_editor_state().has_focus {
|
||||
return false;
|
||||
}
|
||||
get_text_editor_state().blur();
|
||||
true
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn text_editor_dispose() -> bool {
|
||||
with_state_mut!(state, {
|
||||
state.text_editor_state.dispose();
|
||||
true
|
||||
})
|
||||
get_text_editor_state().dispose();
|
||||
true
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn text_editor_has_selection() -> bool {
|
||||
with_state!(state, { state.text_editor_state.selection.is_selection() })
|
||||
get_text_editor_state().selection.is_selection()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn text_editor_has_focus() -> bool {
|
||||
with_state!(state, { state.text_editor_state.has_focus })
|
||||
get_text_editor_state().has_focus
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn text_editor_has_focus_with_id(a: u32, b: u32, c: u32, d: u32) -> bool {
|
||||
with_state!(state, {
|
||||
let shape_id = uuid_from_u32_quartet(a, b, c, d);
|
||||
let Some(active_shape_id) = state.text_editor_state.active_shape_id else {
|
||||
return false;
|
||||
};
|
||||
state.text_editor_state.has_focus && active_shape_id == shape_id
|
||||
})
|
||||
let shape_id = uuid_from_u32_quartet(a, b, c, d);
|
||||
let Some(active_shape_id) = get_text_editor_state().active_shape_id else {
|
||||
return false;
|
||||
};
|
||||
get_text_editor_state().has_focus && active_shape_id == shape_id
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn text_editor_get_active_shape_id(buffer_ptr: *mut u32) {
|
||||
with_state!(state, {
|
||||
if let Some(shape_id) = state.text_editor_state.active_shape_id {
|
||||
let (a, b, c, d) = uuid_to_u32_quartet(&shape_id);
|
||||
unsafe {
|
||||
*buffer_ptr = a;
|
||||
*buffer_ptr.add(1) = b;
|
||||
*buffer_ptr.add(2) = c;
|
||||
*buffer_ptr.add(3) = d;
|
||||
}
|
||||
if let Some(shape_id) = get_text_editor_state().active_shape_id {
|
||||
let (a, b, c, d) = uuid_to_u32_quartet(&shape_id);
|
||||
unsafe {
|
||||
*buffer_ptr = a;
|
||||
*buffer_ptr.add(1) = b;
|
||||
*buffer_ptr.add(2) = c;
|
||||
*buffer_ptr.add(3) = d;
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn text_editor_select_all() -> bool {
|
||||
with_state_mut!(state, {
|
||||
if !state.text_editor_state.has_focus {
|
||||
if !get_text_editor_state().has_focus {
|
||||
return false;
|
||||
}
|
||||
|
||||
let Some(shape_id) = state.text_editor_state.active_shape_id else {
|
||||
let Some(shape_id) = get_text_editor_state().active_shape_id else {
|
||||
return false;
|
||||
};
|
||||
|
||||
@ -132,18 +123,18 @@ pub extern "C" fn text_editor_select_all() -> bool {
|
||||
let Type::Text(text_content) = &shape.shape_type else {
|
||||
return false;
|
||||
};
|
||||
state.text_editor_state.select_all(text_content)
|
||||
get_text_editor_state().select_all(text_content)
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn text_editor_select_word_boundary(x: f32, y: f32) {
|
||||
with_state_mut!(state, {
|
||||
if !state.text_editor_state.has_focus {
|
||||
if !get_text_editor_state().has_focus {
|
||||
return;
|
||||
}
|
||||
|
||||
let Some(shape_id) = state.text_editor_state.active_shape_id else {
|
||||
let Some(shape_id) = get_text_editor_state().active_shape_id else {
|
||||
return;
|
||||
};
|
||||
|
||||
@ -157,16 +148,14 @@ pub extern "C" fn text_editor_select_word_boundary(x: f32, y: f32) {
|
||||
|
||||
let point = Point::new(x, y);
|
||||
if let Some(position) = text_content.get_caret_position_from_shape_coords(&point) {
|
||||
state
|
||||
.text_editor_state
|
||||
.select_word_boundary(text_content, &position);
|
||||
get_text_editor_state().select_word_boundary(text_content, &position);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn text_editor_poll_event() -> u8 {
|
||||
with_state_mut!(state, { state.text_editor_state.poll_event() as u8 })
|
||||
get_text_editor_state().poll_event() as u8
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
@ -176,10 +165,10 @@ pub extern "C" fn text_editor_poll_event() -> u8 {
|
||||
#[no_mangle]
|
||||
pub extern "C" fn text_editor_pointer_down(x: f32, y: f32) {
|
||||
with_state_mut!(state, {
|
||||
if !state.text_editor_state.has_focus {
|
||||
if !get_text_editor_state().has_focus {
|
||||
return;
|
||||
}
|
||||
let Some(shape_id) = state.text_editor_state.active_shape_id else {
|
||||
let Some(shape_id) = get_text_editor_state().active_shape_id else {
|
||||
return;
|
||||
};
|
||||
let Some(shape) = state.shapes.get(&shape_id) else {
|
||||
@ -189,10 +178,10 @@ pub extern "C" fn text_editor_pointer_down(x: f32, y: f32) {
|
||||
return;
|
||||
};
|
||||
let point = Point::new(x, y);
|
||||
state.text_editor_state.start_pointer_selection();
|
||||
get_text_editor_state().start_pointer_selection();
|
||||
if let Some(position) = text_content.get_caret_position_from_shape_coords(&point) {
|
||||
state.text_editor_state.set_caret_from_position(&position);
|
||||
state.text_editor_state.update_styles(text_content);
|
||||
get_text_editor_state().set_caret_from_position(&position);
|
||||
get_text_editor_state().update_styles(text_content);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -200,12 +189,12 @@ pub extern "C" fn text_editor_pointer_down(x: f32, y: f32) {
|
||||
#[no_mangle]
|
||||
pub extern "C" fn text_editor_pointer_move(x: f32, y: f32) {
|
||||
with_state_mut!(state, {
|
||||
if !state.text_editor_state.has_focus {
|
||||
if !get_text_editor_state().has_focus {
|
||||
return;
|
||||
}
|
||||
|
||||
let point = Point::new(x, y);
|
||||
let Some(shape_id) = state.text_editor_state.active_shape_id else {
|
||||
let Some(shape_id) = get_text_editor_state().active_shape_id else {
|
||||
return;
|
||||
};
|
||||
|
||||
@ -213,7 +202,7 @@ pub extern "C" fn text_editor_pointer_move(x: f32, y: f32) {
|
||||
return;
|
||||
};
|
||||
|
||||
if !state.text_editor_state.is_pointer_selection_active {
|
||||
if !get_text_editor_state().is_pointer_selection_active {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -222,13 +211,11 @@ pub extern "C" fn text_editor_pointer_move(x: f32, y: f32) {
|
||||
};
|
||||
|
||||
if let Some(position) = text_content.get_caret_position_from_shape_coords(&point) {
|
||||
state
|
||||
.text_editor_state
|
||||
.extend_selection_from_position(&position);
|
||||
get_text_editor_state().extend_selection_from_position(&position);
|
||||
// We need this flag to prevent handling the click behavior
|
||||
// just after a pointerup event.
|
||||
state.text_editor_state.is_click_event_skipped = true;
|
||||
state.text_editor_state.update_styles(text_content);
|
||||
get_text_editor_state().is_click_event_skipped = true;
|
||||
get_text_editor_state().update_styles(text_content);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -236,29 +223,27 @@ pub extern "C" fn text_editor_pointer_move(x: f32, y: f32) {
|
||||
#[no_mangle]
|
||||
pub extern "C" fn text_editor_pointer_up(x: f32, y: f32) {
|
||||
with_state_mut!(state, {
|
||||
if !state.text_editor_state.has_focus {
|
||||
if !get_text_editor_state().has_focus {
|
||||
return;
|
||||
}
|
||||
let point = Point::new(x, y);
|
||||
let Some(shape_id) = state.text_editor_state.active_shape_id else {
|
||||
let Some(shape_id) = get_text_editor_state().active_shape_id else {
|
||||
return;
|
||||
};
|
||||
let Some(shape) = state.shapes.get(&shape_id) else {
|
||||
return;
|
||||
};
|
||||
if !state.text_editor_state.is_pointer_selection_active {
|
||||
if !get_text_editor_state().is_pointer_selection_active {
|
||||
return;
|
||||
}
|
||||
let Type::Text(text_content) = &shape.shape_type else {
|
||||
return;
|
||||
};
|
||||
if let Some(position) = text_content.get_caret_position_from_shape_coords(&point) {
|
||||
state
|
||||
.text_editor_state
|
||||
.extend_selection_from_position(&position);
|
||||
state.text_editor_state.update_styles(text_content);
|
||||
get_text_editor_state().extend_selection_from_position(&position);
|
||||
get_text_editor_state().update_styles(text_content);
|
||||
}
|
||||
state.text_editor_state.stop_pointer_selection();
|
||||
get_text_editor_state().stop_pointer_selection();
|
||||
});
|
||||
}
|
||||
|
||||
@ -267,17 +252,17 @@ pub extern "C" fn text_editor_set_cursor_from_offset(x: f32, y: f32) {
|
||||
with_state_mut!(state, {
|
||||
// We need this flag to prevent handling the click behavior
|
||||
// just after a pointerup event.
|
||||
if state.text_editor_state.is_click_event_skipped {
|
||||
state.text_editor_state.is_click_event_skipped = false;
|
||||
if get_text_editor_state().is_click_event_skipped {
|
||||
get_text_editor_state().is_click_event_skipped = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if !state.text_editor_state.has_focus {
|
||||
if !get_text_editor_state().has_focus {
|
||||
return;
|
||||
}
|
||||
|
||||
let point = Point::new(x, y);
|
||||
let Some(shape_id) = state.text_editor_state.active_shape_id else {
|
||||
let Some(shape_id) = get_text_editor_state().active_shape_id else {
|
||||
return;
|
||||
};
|
||||
|
||||
@ -290,7 +275,7 @@ pub extern "C" fn text_editor_set_cursor_from_offset(x: f32, y: f32) {
|
||||
};
|
||||
|
||||
if let Some(position) = text_content.get_caret_position_from_shape_coords(&point) {
|
||||
state.text_editor_state.set_caret_from_position(&position);
|
||||
get_text_editor_state().set_caret_from_position(&position);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -298,13 +283,13 @@ pub extern "C" fn text_editor_set_cursor_from_offset(x: f32, y: f32) {
|
||||
#[no_mangle]
|
||||
pub extern "C" fn text_editor_set_cursor_from_point(x: f32, y: f32) {
|
||||
with_state_mut!(state, {
|
||||
if !state.text_editor_state.has_focus {
|
||||
if !get_text_editor_state().has_focus {
|
||||
return;
|
||||
}
|
||||
|
||||
let view_matrix: Matrix = state.render_state.viewbox.get_matrix();
|
||||
let point = Point::new(x, y);
|
||||
let Some(shape_id) = state.text_editor_state.active_shape_id else {
|
||||
let Some(shape_id) = get_text_editor_state().active_shape_id else {
|
||||
return;
|
||||
};
|
||||
let Some(shape) = state.shapes.get(&shape_id) else {
|
||||
@ -317,7 +302,7 @@ pub extern "C" fn text_editor_set_cursor_from_point(x: f32, y: f32) {
|
||||
if let Some(position) =
|
||||
text_content.get_caret_position_from_screen_coords(&point, &view_matrix, &shape_matrix)
|
||||
{
|
||||
state.text_editor_state.set_caret_from_position(&position);
|
||||
get_text_editor_state().set_caret_from_position(&position);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -329,13 +314,10 @@ pub extern "C" fn text_editor_set_cursor_from_point(x: f32, y: f32) {
|
||||
#[no_mangle]
|
||||
#[wasm_error]
|
||||
pub extern "C" fn text_editor_composition_start() -> Result<()> {
|
||||
with_state_mut!(state, {
|
||||
if !state.text_editor_state.has_focus {
|
||||
return Ok(());
|
||||
}
|
||||
state.text_editor_state.composition.start();
|
||||
});
|
||||
|
||||
if !get_text_editor_state().has_focus {
|
||||
return Ok(());
|
||||
}
|
||||
get_text_editor_state().composition.start();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -349,11 +331,11 @@ pub extern "C" fn text_editor_composition_end() -> Result<()> {
|
||||
};
|
||||
|
||||
with_state_mut!(state, {
|
||||
if !state.text_editor_state.has_focus {
|
||||
if !get_text_editor_state().has_focus {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let Some(shape_id) = state.text_editor_state.active_shape_id else {
|
||||
let Some(shape_id) = get_text_editor_state().active_shape_id else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
@ -365,35 +347,30 @@ pub extern "C" fn text_editor_composition_end() -> Result<()> {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
state.text_editor_state.composition.update(&text);
|
||||
get_text_editor_state().composition.update(&text);
|
||||
|
||||
let selection = state
|
||||
.text_editor_state
|
||||
let selection = get_text_editor_state()
|
||||
.composition
|
||||
.get_selection(&state.text_editor_state.selection);
|
||||
.get_selection(&get_text_editor_state().selection);
|
||||
text_helpers::delete_selection_range(text_content, &selection);
|
||||
|
||||
let cursor = state.text_editor_state.selection.focus;
|
||||
let cursor = get_text_editor_state().selection.focus;
|
||||
if let Some(new_cursor) =
|
||||
text_helpers::insert_text_with_newlines(text_content, &cursor, &text)
|
||||
{
|
||||
state.text_editor_state.selection.set_caret(new_cursor);
|
||||
get_text_editor_state().selection.set_caret(new_cursor);
|
||||
}
|
||||
|
||||
text_content.layout.paragraphs.clear();
|
||||
text_content.layout.paragraph_builders.clear();
|
||||
|
||||
state.text_editor_state.reset_blink();
|
||||
state
|
||||
.text_editor_state
|
||||
.push_event(crate::state::TextEditorEvent::ContentChanged);
|
||||
state
|
||||
.text_editor_state
|
||||
.push_event(crate::state::TextEditorEvent::NeedsLayout);
|
||||
get_text_editor_state().reset_blink();
|
||||
get_text_editor_state().push_event(crate::state::TextEditorEvent::ContentChanged);
|
||||
get_text_editor_state().push_event(crate::state::TextEditorEvent::NeedsLayout);
|
||||
|
||||
state.render_state.mark_touched(shape_id);
|
||||
|
||||
state.text_editor_state.composition.end();
|
||||
get_text_editor_state().composition.end();
|
||||
});
|
||||
|
||||
crate::mem::free_bytes()?;
|
||||
@ -410,11 +387,11 @@ pub extern "C" fn text_editor_composition_update() -> Result<()> {
|
||||
};
|
||||
|
||||
with_state_mut!(state, {
|
||||
if !state.text_editor_state.has_focus {
|
||||
if !get_text_editor_state().has_focus {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let Some(shape_id) = state.text_editor_state.active_shape_id else {
|
||||
let Some(shape_id) = get_text_editor_state().active_shape_id else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
@ -426,27 +403,22 @@ pub extern "C" fn text_editor_composition_update() -> Result<()> {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
state.text_editor_state.composition.update(&text);
|
||||
get_text_editor_state().composition.update(&text);
|
||||
|
||||
let selection = state
|
||||
.text_editor_state
|
||||
let selection = get_text_editor_state()
|
||||
.composition
|
||||
.get_selection(&state.text_editor_state.selection);
|
||||
.get_selection(&get_text_editor_state().selection);
|
||||
text_helpers::delete_selection_range(text_content, &selection);
|
||||
|
||||
let cursor = state.text_editor_state.selection.focus;
|
||||
let cursor = get_text_editor_state().selection.focus;
|
||||
text_helpers::insert_text_with_newlines(text_content, &cursor, &text);
|
||||
|
||||
text_content.layout.paragraphs.clear();
|
||||
text_content.layout.paragraph_builders.clear();
|
||||
|
||||
state.text_editor_state.reset_blink();
|
||||
state
|
||||
.text_editor_state
|
||||
.push_event(crate::state::TextEditorEvent::ContentChanged);
|
||||
state
|
||||
.text_editor_state
|
||||
.push_event(crate::state::TextEditorEvent::NeedsLayout);
|
||||
get_text_editor_state().reset_blink();
|
||||
get_text_editor_state().push_event(crate::state::TextEditorEvent::ContentChanged);
|
||||
get_text_editor_state().push_event(crate::state::TextEditorEvent::NeedsLayout);
|
||||
|
||||
state.render_state.mark_touched(shape_id);
|
||||
});
|
||||
@ -458,10 +430,8 @@ pub extern "C" fn text_editor_composition_update() -> Result<()> {
|
||||
#[no_mangle]
|
||||
#[wasm_error]
|
||||
pub extern "C" fn text_editor_toggle_overtype_mode() -> Result<()> {
|
||||
with_state_mut!(state, {
|
||||
state.text_editor_state.toggle_overtype_mode();
|
||||
Ok(())
|
||||
})
|
||||
get_text_editor_state().toggle_overtype_mode();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// FIXME: Review if all the return Ok(()) should be Err instead.
|
||||
@ -475,11 +445,11 @@ pub extern "C" fn text_editor_insert_text() -> Result<()> {
|
||||
};
|
||||
|
||||
with_state_mut!(state, {
|
||||
if !state.text_editor_state.has_focus {
|
||||
if !get_text_editor_state().has_focus {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let Some(shape_id) = state.text_editor_state.active_shape_id else {
|
||||
let Some(shape_id) = get_text_editor_state().active_shape_id else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
@ -491,37 +461,33 @@ pub extern "C" fn text_editor_insert_text() -> Result<()> {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let selection = state.text_editor_state.selection;
|
||||
let selection = get_text_editor_state().selection;
|
||||
|
||||
if selection.is_selection() {
|
||||
text_helpers::delete_selection_range(text_content, &selection);
|
||||
let start = selection.start();
|
||||
state.text_editor_state.selection.set_caret(start);
|
||||
get_text_editor_state().selection.set_caret(start);
|
||||
}
|
||||
|
||||
let cursor = state.text_editor_state.selection.focus;
|
||||
if !state.text_editor_state.is_overtype_mode {
|
||||
let cursor = get_text_editor_state().selection.focus;
|
||||
if !get_text_editor_state().is_overtype_mode {
|
||||
if let Some(new_cursor) =
|
||||
text_helpers::insert_text_with_newlines(text_content, &cursor, &text)
|
||||
{
|
||||
state.text_editor_state.selection.set_caret(new_cursor);
|
||||
get_text_editor_state().selection.set_caret(new_cursor);
|
||||
}
|
||||
} else if let Some(new_cursor) =
|
||||
text_helpers::replace_text_with_newlines(text_content, &cursor, &text)
|
||||
{
|
||||
state.text_editor_state.selection.set_caret(new_cursor);
|
||||
get_text_editor_state().selection.set_caret(new_cursor);
|
||||
}
|
||||
|
||||
text_content.layout.paragraphs.clear();
|
||||
text_content.layout.paragraph_builders.clear();
|
||||
|
||||
state.text_editor_state.reset_blink();
|
||||
state
|
||||
.text_editor_state
|
||||
.push_event(TextEditorEvent::ContentChanged);
|
||||
state
|
||||
.text_editor_state
|
||||
.push_event(TextEditorEvent::NeedsLayout);
|
||||
get_text_editor_state().reset_blink();
|
||||
get_text_editor_state().push_event(TextEditorEvent::ContentChanged);
|
||||
get_text_editor_state().push_event(TextEditorEvent::NeedsLayout);
|
||||
|
||||
state.render_state.mark_touched(shape_id);
|
||||
});
|
||||
@ -533,11 +499,11 @@ pub extern "C" fn text_editor_insert_text() -> Result<()> {
|
||||
#[no_mangle]
|
||||
pub extern "C" fn text_editor_delete_backward(word_boundary: bool) {
|
||||
with_state_mut!(state, {
|
||||
if !state.text_editor_state.has_focus {
|
||||
if !get_text_editor_state().has_focus {
|
||||
return;
|
||||
}
|
||||
|
||||
let Some(shape_id) = state.text_editor_state.active_shape_id else {
|
||||
let Some(shape_id) = get_text_editor_state().active_shape_id else {
|
||||
return;
|
||||
};
|
||||
|
||||
@ -549,9 +515,7 @@ pub extern "C" fn text_editor_delete_backward(word_boundary: bool) {
|
||||
return;
|
||||
};
|
||||
|
||||
state
|
||||
.text_editor_state
|
||||
.delete_backward(text_content, word_boundary);
|
||||
get_text_editor_state().delete_backward(text_content, word_boundary);
|
||||
state.render_state.mark_touched(shape_id);
|
||||
});
|
||||
}
|
||||
@ -559,11 +523,11 @@ pub extern "C" fn text_editor_delete_backward(word_boundary: bool) {
|
||||
#[no_mangle]
|
||||
pub extern "C" fn text_editor_delete_forward(word_boundary: bool) {
|
||||
with_state_mut!(state, {
|
||||
if !state.text_editor_state.has_focus {
|
||||
if !get_text_editor_state().has_focus {
|
||||
return;
|
||||
}
|
||||
|
||||
let Some(shape_id) = state.text_editor_state.active_shape_id else {
|
||||
let Some(shape_id) = get_text_editor_state().active_shape_id else {
|
||||
return;
|
||||
};
|
||||
|
||||
@ -575,9 +539,7 @@ pub extern "C" fn text_editor_delete_forward(word_boundary: bool) {
|
||||
return;
|
||||
};
|
||||
|
||||
state
|
||||
.text_editor_state
|
||||
.delete_forward(text_content, word_boundary);
|
||||
get_text_editor_state().delete_forward(text_content, word_boundary);
|
||||
state.render_state.mark_touched(shape_id);
|
||||
});
|
||||
}
|
||||
@ -585,11 +547,11 @@ pub extern "C" fn text_editor_delete_forward(word_boundary: bool) {
|
||||
#[no_mangle]
|
||||
pub extern "C" fn text_editor_insert_paragraph() {
|
||||
with_state_mut!(state, {
|
||||
if !state.text_editor_state.has_focus {
|
||||
if !get_text_editor_state().has_focus {
|
||||
return;
|
||||
}
|
||||
|
||||
let Some(shape_id) = state.text_editor_state.active_shape_id else {
|
||||
let Some(shape_id) = get_text_editor_state().active_shape_id else {
|
||||
return;
|
||||
};
|
||||
|
||||
@ -601,7 +563,7 @@ pub extern "C" fn text_editor_insert_paragraph() {
|
||||
return;
|
||||
};
|
||||
|
||||
state.text_editor_state.insert_paragraph(text_content);
|
||||
get_text_editor_state().insert_paragraph(text_content);
|
||||
state.render_state.mark_touched(shape_id);
|
||||
});
|
||||
}
|
||||
@ -617,11 +579,11 @@ pub extern "C" fn text_editor_move_cursor(
|
||||
extend_selection: bool,
|
||||
) {
|
||||
with_state_mut!(state, {
|
||||
if !state.text_editor_state.has_focus {
|
||||
if !get_text_editor_state().has_focus {
|
||||
return;
|
||||
}
|
||||
|
||||
let Some(shape_id) = state.text_editor_state.active_shape_id else {
|
||||
let Some(shape_id) = get_text_editor_state().active_shape_id else {
|
||||
return;
|
||||
};
|
||||
|
||||
@ -633,7 +595,7 @@ pub extern "C" fn text_editor_move_cursor(
|
||||
return;
|
||||
};
|
||||
|
||||
state.text_editor_state.move_cursor(
|
||||
get_text_editor_state().move_cursor(
|
||||
text_content,
|
||||
direction,
|
||||
word_boundary,
|
||||
@ -649,11 +611,11 @@ pub extern "C" fn text_editor_move_cursor(
|
||||
#[no_mangle]
|
||||
pub extern "C" fn text_editor_get_cursor_rect() -> *mut u8 {
|
||||
with_state_mut!(state, {
|
||||
if !state.text_editor_state.has_focus || !state.text_editor_state.cursor_visible {
|
||||
if !get_text_editor_state().has_focus || !get_text_editor_state().cursor_visible {
|
||||
return std::ptr::null_mut();
|
||||
}
|
||||
|
||||
let Some(shape_id) = state.text_editor_state.active_shape_id else {
|
||||
let Some(shape_id) = get_text_editor_state().active_shape_id else {
|
||||
return std::ptr::null_mut();
|
||||
};
|
||||
|
||||
@ -665,7 +627,7 @@ pub extern "C" fn text_editor_get_cursor_rect() -> *mut u8 {
|
||||
return std::ptr::null_mut();
|
||||
};
|
||||
|
||||
let cursor = &state.text_editor_state.selection.focus;
|
||||
let cursor = &get_text_editor_state().selection.focus;
|
||||
|
||||
if let Some(rect) = get_cursor_rect(text_content, cursor, shape) {
|
||||
let mut bytes = vec![0u8; 16];
|
||||
@ -683,11 +645,11 @@ pub extern "C" fn text_editor_get_cursor_rect() -> *mut u8 {
|
||||
#[no_mangle]
|
||||
pub extern "C" fn text_editor_get_current_styles() -> *mut u8 {
|
||||
with_state_mut!(state, {
|
||||
if !state.text_editor_state.has_focus {
|
||||
if !get_text_editor_state().has_focus {
|
||||
return std::ptr::null_mut();
|
||||
}
|
||||
|
||||
let Some(shape_id) = state.text_editor_state.active_shape_id else {
|
||||
let Some(shape_id) = get_text_editor_state().active_shape_id else {
|
||||
return std::ptr::null_mut();
|
||||
};
|
||||
|
||||
@ -699,7 +661,7 @@ pub extern "C" fn text_editor_get_current_styles() -> *mut u8 {
|
||||
return std::ptr::null_mut();
|
||||
};
|
||||
|
||||
let styles = &state.text_editor_state.current_styles;
|
||||
let styles = &get_text_editor_state().current_styles;
|
||||
|
||||
let vertical_align = match styles.vertical_align {
|
||||
VerticalAlign::Top => 0_u32,
|
||||
@ -851,15 +813,15 @@ pub extern "C" fn text_editor_get_current_styles() -> *mut u8 {
|
||||
#[no_mangle]
|
||||
pub extern "C" fn text_editor_get_selection_rects() -> *mut u8 {
|
||||
with_state_mut!(state, {
|
||||
if !state.text_editor_state.has_focus {
|
||||
if !get_text_editor_state().has_focus {
|
||||
return std::ptr::null_mut();
|
||||
}
|
||||
|
||||
if state.text_editor_state.selection.is_collapsed() {
|
||||
if get_text_editor_state().selection.is_collapsed() {
|
||||
return std::ptr::null_mut();
|
||||
}
|
||||
|
||||
let Some(shape_id) = state.text_editor_state.active_shape_id else {
|
||||
let Some(shape_id) = get_text_editor_state().active_shape_id else {
|
||||
return std::ptr::null_mut();
|
||||
};
|
||||
|
||||
@ -871,7 +833,7 @@ pub extern "C" fn text_editor_get_selection_rects() -> *mut u8 {
|
||||
return std::ptr::null_mut();
|
||||
};
|
||||
|
||||
let selection = &state.text_editor_state.selection;
|
||||
let selection = &get_text_editor_state().selection;
|
||||
let rects = get_selection_rects(text_content, selection, shape);
|
||||
if rects.is_empty() {
|
||||
return std::ptr::null_mut();
|
||||
@ -891,15 +853,13 @@ pub extern "C" fn text_editor_get_selection_rects() -> *mut u8 {
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn text_editor_update_blink(timestamp_ms: f32) {
|
||||
with_state_mut!(state, {
|
||||
state.text_editor_state.update_blink(timestamp_ms);
|
||||
});
|
||||
get_text_editor_state().update_blink(timestamp_ms);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn text_editor_render_overlay() {
|
||||
with_state_mut!(state, {
|
||||
let Some(shape_id) = state.text_editor_state.active_shape_id else {
|
||||
let Some(shape_id) = get_text_editor_state().active_shape_id else {
|
||||
return;
|
||||
};
|
||||
|
||||
@ -926,7 +886,7 @@ pub extern "C" fn text_editor_render_overlay() {
|
||||
canvas,
|
||||
&viewbox,
|
||||
&state.render_state.options,
|
||||
&state.text_editor_state,
|
||||
get_text_editor_state(),
|
||||
shape,
|
||||
);
|
||||
state.render_state.flush_and_submit();
|
||||
@ -936,11 +896,11 @@ pub extern "C" fn text_editor_render_overlay() {
|
||||
#[no_mangle]
|
||||
pub extern "C" fn text_editor_export_content() -> *mut u8 {
|
||||
with_state!(state, {
|
||||
if !state.text_editor_state.has_focus {
|
||||
if !get_text_editor_state().has_focus {
|
||||
return std::ptr::null_mut();
|
||||
}
|
||||
|
||||
let Some(shape_id) = state.text_editor_state.active_shape_id else {
|
||||
let Some(shape_id) = get_text_editor_state().active_shape_id else {
|
||||
return std::ptr::null_mut();
|
||||
};
|
||||
|
||||
@ -979,10 +939,10 @@ pub extern "C" fn text_editor_export_content() -> *mut u8 {
|
||||
pub extern "C" fn text_editor_export_selection() -> *mut u8 {
|
||||
use std::ptr;
|
||||
with_state!(state, {
|
||||
if !state.text_editor_state.has_focus {
|
||||
if !get_text_editor_state().has_focus {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
let Some(shape_id) = state.text_editor_state.active_shape_id else {
|
||||
let Some(shape_id) = get_text_editor_state().active_shape_id else {
|
||||
return ptr::null_mut();
|
||||
};
|
||||
let Some(shape) = state.shapes.get(&shape_id) else {
|
||||
@ -991,7 +951,7 @@ pub extern "C" fn text_editor_export_selection() -> *mut u8 {
|
||||
let Type::Text(text_content) = &shape.shape_type else {
|
||||
return ptr::null_mut();
|
||||
};
|
||||
let selection = &state.text_editor_state.selection;
|
||||
let selection = &get_text_editor_state().selection;
|
||||
let start = selection.start();
|
||||
let end = selection.end();
|
||||
let paragraphs = text_content.paragraphs();
|
||||
@ -1055,19 +1015,17 @@ pub extern "C" fn text_editor_export_selection() -> *mut u8 {
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn text_editor_get_selection(buffer_ptr: *mut u32) -> bool {
|
||||
with_state!(state, {
|
||||
if !state.text_editor_state.selection.is_selection() {
|
||||
return false;
|
||||
}
|
||||
let sel = &state.text_editor_state.selection;
|
||||
unsafe {
|
||||
*buffer_ptr = sel.anchor.paragraph as u32;
|
||||
*buffer_ptr.add(1) = sel.anchor.offset as u32;
|
||||
*buffer_ptr.add(2) = sel.focus.paragraph as u32;
|
||||
*buffer_ptr.add(3) = sel.focus.offset as u32;
|
||||
}
|
||||
true
|
||||
})
|
||||
if !get_text_editor_state().selection.is_selection() {
|
||||
return false;
|
||||
}
|
||||
let sel = &get_text_editor_state().selection;
|
||||
unsafe {
|
||||
*buffer_ptr = sel.anchor.paragraph as u32;
|
||||
*buffer_ptr.add(1) = sel.anchor.offset as u32;
|
||||
*buffer_ptr.add(2) = sel.focus.paragraph as u32;
|
||||
*buffer_ptr.add(3) = sel.focus.offset as u32;
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user