diff --git a/src/core/text/edit.rs b/src/core/text/edit.rs index 9546e62..25dc26b 100644 --- a/src/core/text/edit.rs +++ b/src/core/text/edit.rs @@ -1,7 +1,7 @@ use std::ops::{Deref, DerefMut}; use crate::prelude::*; -use cosmic_text::{Affinity, Attrs, Cursor, FontSystem, Motion}; +use cosmic_text::{Affinity, Attrs, Cursor, FontSystem, LayoutRun, Motion}; use unicode_segmentation::UnicodeSegmentation; use winit::{ event::KeyEvent, @@ -53,38 +53,16 @@ impl Widget for TextEdit { TextSelection::Span { start, end } => { let (start, end) = sort_cursors(*start, *end); let line_height = self.attrs.line_height; - let mut highlight = |offset, width: f32| { + for (top_left, width) in iter_layout_lines(&self.buf, &start, &end) { painter.primitive_within( RectPrimitive::color(Color::SKY), vec2(width, line_height) .align(Align::TOP_LEFT) - .offset(offset) + .offset(top_left) .within(®ion), ); - }; - if let (Some(start_offset), Some(end_offset)) = - (cursor_pos(&start, &self.buf), cursor_pos(&end, &self.buf)) - { - let mut iter = self.buf.layout_runs().skip(start.line); - if start.line == end.line { - highlight(start_offset, end_offset.x - start_offset.x); - } else { - let first = iter.next().unwrap(); - highlight(start_offset, first.line_w - start_offset.x); - for i in (start.line + 1)..end.line { - let line = iter.next().unwrap(); - let offset = vec2(0.0, i as f32 * line_height); - highlight(offset, line.line_w); - } - let offset = vec2(0.0, end.line as f32 * line_height); - highlight(offset, end_offset.x); - } - // painter.primitive_within( - // RectPrimitive::color(Color::WHITE), - // size.align(Align::TOP_LEFT) - // .offset(end_offset) - // .within(®ion), - // ); + } + if let Some(end_offset) = cursor_pos(&end, &self.buf) { let size = vec2(1, self.attrs.line_height); painter.primitive_within( RectPrimitive::color(Color::WHITE), @@ -106,7 +84,67 @@ impl Widget for TextEdit { } } +/// provides top left + width +fn iter_layout_lines( + buf: &TextBuffer, + start: &Cursor, + end: &Cursor, +) -> impl Iterator { + gen { + let mut iter = buf.layout_runs(); + for line in iter.by_ref() { + if line.line_i == start.line + && let Some(start_x) = index_x(&line, start.index) + { + if start.line == end.line + && let Some(end_x) = index_x(&line, end.index) + { + yield (vec2(start_x, line.line_top), end_x - start_x); + return; + } + yield (vec2(start_x, line.line_top), line.line_w - start_x); + break; + } + } + for line in iter { + if line.line_i == end.line + && let Some(end_x) = index_x(&line, end.index) + { + yield (vec2(0.0, line.line_top), end_x); + return; + } + yield (vec2(0.0, line.line_top), line.line_w); + } + } +} + /// copied & modified from fn found in Editor in cosmic_text +/// returns x pos of a (non layout) index within an layout run +fn index_x(run: &LayoutRun, index: usize) -> Option { + for glyph in run.glyphs.iter() { + if index == glyph.start { + return Some(glyph.x); + } else if index > glyph.start && 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 < index { + before += 1; + } + total += 1; + } + + let offset = glyph.w * (before as f32) / (total as f32); + return Some(glyph.x + offset); + } + } + None +} + +/// returns top of line segment where cursor should visually select fn cursor_pos(cursor: &Cursor, buf: &TextBuffer) -> Option { let mut prev = None; for run in buf @@ -115,25 +153,8 @@ fn cursor_pos(cursor: &Cursor, buf: &TextBuffer) -> Option { .take_while(|r| r.line_i == cursor.line) { prev = Some(vec2(run.line_w, run.line_top)); - for glyph in run.glyphs.iter() { - if cursor.index == glyph.start { - return Some(vec2(glyph.x, run.line_top)); - } 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(vec2(glyph.x + offset, run.line_top)); - } + if let Some(pos) = index_x(&run, cursor.index) { + return Some(vec2(pos, run.line_top)); } } prev diff --git a/src/lib.rs b/src/lib.rs index 89b8a85..cb1e028 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,7 @@ #![feature(const_cmp)] #![feature(const_destruct)] #![feature(portable_simd)] +#![feature(gen_blocks)] pub mod core; pub mod layout;