From a3a6262e5d654bf6d110f18e6b7b8a31a764e64f Mon Sep 17 00:00:00 2001 From: Pavel Strakhov Date: Sat, 8 Jun 2024 13:16:28 +0100 Subject: [PATCH] Add Edit::cursor_position --- examples/editor/src/main.rs | 8 +- src/edit/editor.rs | 141 ++++++++++++++++++++---------------- src/edit/mod.rs | 3 + src/edit/syntect.rs | 4 + src/edit/vi.rs | 4 + 5 files changed, 95 insertions(+), 65 deletions(-) diff --git a/examples/editor/src/main.rs b/examples/editor/src/main.rs index 6afeec1..e0477bc 100644 --- a/examples/editor/src/main.rs +++ b/examples/editor/src/main.rs @@ -7,7 +7,7 @@ use cosmic_text::{ use std::{env, fs, num::NonZeroU32, rc::Rc, slice}; use tiny_skia::{Paint, PixmapMut, Rect, Transform}; use winit::{ - dpi::PhysicalPosition, + dpi::{PhysicalPosition, PhysicalSize}, event::{ElementState, Event, KeyEvent, MouseButton, MouseScrollDelta, WindowEvent}, event_loop::{ControlFlow, EventLoop}, keyboard::{Key, NamedKey}, @@ -130,6 +130,12 @@ fn main() { None, ); }); + if let Some((x, y)) = editor.cursor_position() { + window.set_ime_cursor_area( + PhysicalPosition::new(x, y), + PhysicalSize::new(20, 20), + ); + } // Draw scrollbar { diff --git a/src/edit/editor.rs b/src/edit/editor.rs index cdd2a68..dd1bfff 100644 --- a/src/edit/editor.rs +++ b/src/edit/editor.rs @@ -12,7 +12,7 @@ use unicode_segmentation::UnicodeSegmentation; use crate::Color; use crate::{ Action, Attrs, AttrsList, BorrowedWithFontSystem, BufferLine, BufferRef, Change, ChangeItem, - Cursor, Edit, FontSystem, Selection, Shaping, + Cursor, Edit, FontSystem, LayoutRun, Selection, Shaping, }; /// A wrapper of [`Buffer`] for easy editing @@ -27,6 +27,72 @@ pub struct Editor<'buffer> { change: Option, } +fn cursor_glyph_opt(cursor: &Cursor, run: &LayoutRun) -> Option<(usize, f32)> { + if cursor.line == run.line_i { + for (glyph_i, glyph) in run.glyphs.iter().enumerate() { + if cursor.index == glyph.start { + return Some((glyph_i, 0.0)); + } else if cursor.index > glyph.start && cursor.index < glyph.end { + // Guess x offset based on characters + let mut before = 0; + let mut total = 0; + + let cluster = &run.text[glyph.start..glyph.end]; + for (i, _) in cluster.grapheme_indices(true) { + if glyph.start + i < cursor.index { + before += 1; + } + total += 1; + } + + let offset = glyph.w * (before as f32) / (total as f32); + return Some((glyph_i, offset)); + } + } + match run.glyphs.last() { + Some(glyph) => { + if cursor.index == glyph.end { + return Some((run.glyphs.len(), 0.0)); + } + } + None => { + return Some((0, 0.0)); + } + } + } + None +} + +fn cursor_position(cursor: &Cursor, run: &LayoutRun) -> Option<(i32, i32)> { + let (cursor_glyph, cursor_glyph_offset) = cursor_glyph_opt(cursor, run)?; + let x = match run.glyphs.get(cursor_glyph) { + Some(glyph) => { + // Start of detected glyph + if glyph.level.is_rtl() { + (glyph.x + glyph.w - cursor_glyph_offset) as i32 + } else { + (glyph.x + cursor_glyph_offset) as i32 + } + } + None => match run.glyphs.last() { + Some(glyph) => { + // End of last glyph + if glyph.level.is_rtl() { + glyph.x as i32 + } else { + (glyph.x + glyph.w) as i32 + } + } + None => { + // Start of empty line + 0 + } + }, + }; + + Some((x, run.line_top as i32)) +} + impl<'buffer> Editor<'buffer> { /// Create a new [`Editor`] with the provided [`Buffer`] pub fn new(buffer: impl Into>) -> Self { @@ -63,42 +129,6 @@ impl<'buffer> Editor<'buffer> { let line_top = run.line_top; let line_height = run.line_height; - let cursor_glyph_opt = |cursor: &Cursor| -> Option<(usize, f32)> { - if cursor.line == line_i { - for (glyph_i, glyph) in run.glyphs.iter().enumerate() { - if cursor.index == glyph.start { - return Some((glyph_i, 0.0)); - } else if cursor.index > glyph.start && cursor.index < glyph.end { - // Guess x offset based on characters - let mut before = 0; - let mut total = 0; - - let cluster = &run.text[glyph.start..glyph.end]; - for (i, _) in cluster.grapheme_indices(true) { - if glyph.start + i < cursor.index { - before += 1; - } - total += 1; - } - - let offset = glyph.w * (before as f32) / (total as f32); - return Some((glyph_i, offset)); - } - } - match run.glyphs.last() { - Some(glyph) => { - if cursor.index == glyph.end { - return Some((run.glyphs.len(), 0.0)); - } - } - None => { - return Some((0, 0.0)); - } - } - } - None - }; - // Highlight selection if let Some((start, end)) = selection_bounds { if line_i >= start.line && line_i <= end.line { @@ -161,33 +191,8 @@ impl<'buffer> Editor<'buffer> { } // Draw cursor - if let Some((cursor_glyph, cursor_glyph_offset)) = cursor_glyph_opt(&self.cursor) { - let x = match run.glyphs.get(cursor_glyph) { - Some(glyph) => { - // Start of detected glyph - if glyph.level.is_rtl() { - (glyph.x + glyph.w - cursor_glyph_offset) as i32 - } else { - (glyph.x + cursor_glyph_offset) as i32 - } - } - None => match run.glyphs.last() { - Some(glyph) => { - // End of last glyph - if glyph.level.is_rtl() { - glyph.x as i32 - } else { - (glyph.x + glyph.w) as i32 - } - } - None => { - // Start of empty line - 0 - } - }, - }; - - f(x, line_top as i32, 1, line_height as u32, cursor_color); + if let Some((x, y)) = cursor_position(&self.cursor, &run) { + f(x, y, 1, line_height as u32, cursor_color); } for glyph in run.glyphs.iter() { @@ -883,6 +888,14 @@ impl<'buffer> Edit<'buffer> for Editor<'buffer> { */ } } + + fn cursor_position(&self) -> Option<(i32, i32)> { + self.with_buffer(|buffer| { + buffer + .layout_runs() + .find_map(|run| cursor_position(&self.cursor, &run)) + }) + } } impl<'font_system, 'buffer> BorrowedWithFontSystem<'font_system, Editor<'buffer>> { diff --git a/src/edit/mod.rs b/src/edit/mod.rs index 61a8f44..c070831 100644 --- a/src/edit/mod.rs +++ b/src/edit/mod.rs @@ -319,6 +319,9 @@ pub trait Edit<'buffer> { /// Perform an [Action] on the editor fn action(&mut self, font_system: &mut FontSystem, action: Action); + + /// Get X and Y position of the top left corner of the cursor + fn cursor_position(&self) -> Option<(i32, i32)>; } impl<'font_system, 'buffer, E: Edit<'buffer>> BorrowedWithFontSystem<'font_system, E> { diff --git a/src/edit/syntect.rs b/src/edit/syntect.rs index d63c0e5..ebd0b88 100644 --- a/src/edit/syntect.rs +++ b/src/edit/syntect.rs @@ -413,6 +413,10 @@ impl<'syntax_system, 'buffer> Edit<'buffer> for SyntaxEditor<'syntax_system, 'bu fn action(&mut self, font_system: &mut FontSystem, action: Action) { self.editor.action(font_system, action); } + + fn cursor_position(&self) -> Option<(i32, i32)> { + self.editor.cursor_position() + } } impl<'font_system, 'syntax_system, 'buffer> diff --git a/src/edit/vi.rs b/src/edit/vi.rs index 334540c..641a028 100644 --- a/src/edit/vi.rs +++ b/src/edit/vi.rs @@ -1157,6 +1157,10 @@ impl<'syntax_system, 'buffer> Edit<'buffer> for ViEditor<'syntax_system, 'buffer editor.action(font_system, action); }); } + + fn cursor_position(&self) -> Option<(i32, i32)> { + self.editor.cursor_position() + } } impl<'font_system, 'syntax_system, 'buffer>