This commit is contained in:
Aitor Moreno
2026-02-03 13:31:35 +01:00
parent 1a9ffaaccc
commit e8794c3854
6 changed files with 73 additions and 44 deletions

View File

@@ -187,6 +187,8 @@
alt? (kbd/alt? event)
meta? (kbd/meta? event)
hovering? (some? @hover)
native-event (unchecked-get event "nativeEvent")
off-pt (dom/get-offset-position native-event)
raw-pt (dom/get-client-position event)
pt (uwvv/point->viewport raw-pt)]
(st/emit! (mse/->MouseEvent :click ctrl? shift? alt? meta?))
@@ -208,9 +210,7 @@
(when (and (= :text (:type hover-shape))
(features/active-feature? @st/state "text-editor-wasm/v1")
wasm.wasm/context-initialized?)
(let [raw-pt (dom/get-client-position event)]
;; FIXME
(wasm.api/text-editor-set-cursor-from-point (.-x raw-pt) (.-y raw-pt))))))
(wasm.api/text-editor-set-cursor-from-point (.-x off-pt) (.-y off-pt)))))
(when (and @z?
(not @space?)
@@ -381,7 +381,9 @@
(let [last-position (mf/use-var nil)]
(mf/use-fn
(fn [event]
(let [raw-pt (dom/get-client-position event)
(let [native-event (unchecked-get event "nativeEvent")
off-pt (dom/get-offset-position native-event)
raw-pt (dom/get-client-position event)
pt (uwvv/point->viewport raw-pt)
;; We calculate the delta because Safari's MouseEvent.movementX/Y drop
@@ -390,6 +392,8 @@
(gpt/subtract raw-pt @last-position)
(gpt/point 0 0))]
(wasm.api/text-editor-testing-coords (.-x off-pt) (.-y off-pt))
(rx/push! move-stream pt)
(reset! last-position raw-pt)
(st/emit! (mse/->PointerEvent :delta delta

View File

@@ -83,6 +83,7 @@
;; Re-export public text editor functions
(def text-editor-start text-editor/text-editor-start)
(def text-editor-stop text-editor/text-editor-stop)
(def text-editor-testing-coords text-editor/text-editor-testing-coords)
(def text-editor-set-cursor-from-point text-editor/text-editor-set-cursor-from-point)
(def text-editor-is-active? text-editor/text-editor-is-active?)
(def text-editor-sync-content text-editor/text-editor-sync-content)
@@ -1110,7 +1111,7 @@
(defn- set-objects-async
"Asynchronously process shapes in chunks, yielding to the browser between chunks.
Returns a promise that resolves when all shapes are processed.
Renders a preview only periodically during loading to show progress,
then does a full tile-based render at the end."
[shapes render-callback]

View File

@@ -22,6 +22,11 @@
(aget buffer 2)
(aget buffer 3)))))
(defn text-editor-testing-coords
[x y]
(when wasm/context-initialized?
(h/call wasm/internal-module "_text_editor_testing_coords" x y)))
(defn text-editor-set-cursor-from-point
[x y]
(when wasm/context-initialized?

View File

@@ -76,3 +76,4 @@ export function getFills(fillStyle) {
const [color, opacity] = getColor(fillStyle);
return `[["^ ","~:fill-color","${color}","~:fill-opacity",${opacity}]]`;
}

View File

@@ -41,7 +41,11 @@ fn render_cursor(canvas: &Canvas, editor_state: &TextEditorState, text_content:
paint.set_color(editor_state.theme.cursor_color);
paint.set_anti_alias(true);
let shape_matrix = shape.get_matrix();
canvas.save();
canvas.concat(&shape_matrix);
canvas.draw_rect(rect, &paint);
canvas.restore();
}
fn render_selection(
@@ -95,8 +99,6 @@ fn calculate_cursor_rect(
return None;
}
let selrect = shape.selrect();
let mut y_offset = vertical_align_offset(shape, &layout_paragraphs);
for (idx, laid_out_para) in layout_paragraphs.iter().enumerate() {
if idx == cursor.paragraph {
@@ -153,8 +155,8 @@ fn calculate_cursor_rect(
};
return Some(Rect::from_xywh(
selrect.x() + cursor_x,
selrect.y() + y_offset,
cursor_x,
y_offset,
editor_state.theme.cursor_width,
cursor_height,
));

View File

@@ -1,5 +1,3 @@
use std::io::Cursor;
use crate::math::{Matrix, Point, Rect};
use crate::mem;
use crate::shapes::{Paragraph, Shape, TextContent, Type, VerticalAlign};
@@ -119,6 +117,37 @@ pub extern "C" fn text_editor_poll_event() -> u8 {
// SELECTION MANAGEMENT
// ============================================================================
fn get_shape_relative_point(point: Point, view_matrix: Matrix, shape_matrix: Matrix) -> Option<Point> {
let Some(inv_view_matrix) = view_matrix.invert() else {
return None;
};
let Some(inv_shape_matrix) = shape_matrix.invert() else {
return None;
};
let transform_matrix: Matrix = Matrix::concat(&inv_shape_matrix, &inv_view_matrix);
let shape_relative_point = transform_matrix.map_point(point);
Some(shape_relative_point)
}
#[no_mangle]
pub extern "C" fn text_editor_testing_coords(x: f32, y: f32) {
with_state_mut!(state, {
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 {
return;
};
let Some(shape) = state.shapes.get(&shape_id) else {
return;
};
let shape_matrix = shape.get_matrix();
let Some(shape_rel_point) = get_shape_relative_point(point, view_matrix, shape_matrix) else {
return;
};
// println!("testing_coords::shape_rel_point {:?}", shape_rel_point);
});
}
#[no_mangle]
pub extern "C" fn text_editor_set_cursor_from_point(x: f32, y: f32) {
with_state_mut!(state, {
@@ -126,44 +155,26 @@ pub extern "C" fn text_editor_set_cursor_from_point(x: f32, y: f32) {
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 {
return;
};
let (shape_matrix, view_matrix, selrect, vertical_align) = {
let Some(shape) = state.shapes.get(&shape_id) else {
return;
};
(
shape.get_concatenated_matrix(&state.shapes),
state.render_state.viewbox.get_matrix(),
shape.selrect(),
shape.vertical_align(),
)
let Some(shape) = state.shapes.get(&shape_id) else {
return;
};
let Some(inv_view_matrix) = view_matrix.invert() else {
let shape_matrix = shape.get_matrix();
let Some(shape_rel_point) = get_shape_relative_point(point, view_matrix, shape_matrix) else {
return;
};
let Some(inv_shape_matrix) = shape_matrix.invert() else {
let Type::Text(text_content) = &shape.shape_type else {
return;
};
let mut matrix = Matrix::new_identity();
matrix.post_concat(&inv_view_matrix);
matrix.post_concat(&inv_shape_matrix);
let mapped_point = matrix.map_point(Point::new(x, y));
let Some(shape) = state.shapes.get_mut(&shape_id) else {
return;
};
let Type::Text(text_content) = &mut shape.shape_type else {
return;
};
println!("cursor_from_point::shape_rel_point {:?}", shape_rel_point);
/*
if text_content.layout.paragraphs.is_empty() && !text_content.paragraphs().is_empty() {
let bounds = text_content.bounds;
text_content.update_layout(bounds);
@@ -171,7 +182,7 @@ pub extern "C" fn text_editor_set_cursor_from_point(x: f32, y: f32) {
// Calculate vertical alignment offset (same as in render/text_editor.rs)
let layout_paragraphs: Vec<_> = text_content.layout.paragraphs.iter().flatten().collect();
let total_height: f32 = layout_paragraphs.iter().map(|p| p.height()).sum();
let _total_height: f32 = layout_paragraphs.iter().map(|p| p.height()).sum();
let vertical_offset = match vertical_align {
crate::shapes::VerticalAlign::Center => (selrect.height() - total_height) / 2.0,
crate::shapes::VerticalAlign::Bottom => selrect.height() - total_height,
@@ -184,8 +195,9 @@ pub extern "C" fn text_editor_set_cursor_from_point(x: f32, y: f32) {
mapped_point.x - selrect.x(),
mapped_point.y - selrect.y() - vertical_offset,
);
if let Some(position) = text_content.get_caret_position_at(&adjusted_point) {
*/
if let Some(position) = text_content.get_caret_position_at(&shape_rel_point) {
println!("cursor_from_point::position {:?}", position);
state.text_editor_state.set_caret_from_position(position);
}
});
@@ -218,13 +230,15 @@ pub extern "C" fn text_editor_extend_selection_to_point(x: f32, y: f32) {
return;
};
/*
let Some(inv_shape_matrix) = shape_matrix.invert() else {
return;
};
*/
let mut matrix = Matrix::new_identity();
matrix.post_concat(&inv_view_matrix);
matrix.post_concat(&inv_shape_matrix);
matrix.post_concat(&shape_matrix);
let mapped_point = matrix.map_point(Point::new(x, y));
@@ -243,7 +257,8 @@ pub extern "C" fn text_editor_extend_selection_to_point(x: f32, y: f32) {
// Calculate vertical alignment offset (same as in render/text_editor.rs)
let layout_paragraphs: Vec<_> = text_content.layout.paragraphs.iter().flatten().collect();
let total_height: f32 = layout_paragraphs.iter().map(|p| p.height()).sum();
let _total_height: f32 = layout_paragraphs.iter().map(|p| p.height()).sum();
/*
let vertical_offset = match vertical_align {
crate::shapes::VerticalAlign::Center => (selrect.height() - total_height) / 2.0,
crate::shapes::VerticalAlign::Bottom => selrect.height() - total_height,
@@ -255,8 +270,9 @@ pub extern "C" fn text_editor_extend_selection_to_point(x: f32, y: f32) {
mapped_point.x - selrect.x(),
mapped_point.y - selrect.y() - vertical_offset,
);
*/
if let Some(position) = text_content.get_caret_position_at(&adjusted_point) {
if let Some(position) = text_content.get_caret_position_at(&mapped_point) {
state
.text_editor_state
.extend_selection_from_position(position);