fix wrapping text selection
This commit is contained in:
@@ -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<Item = (Vec2, f32)> {
|
||||
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<f32> {
|
||||
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<Vec2> {
|
||||
let mut prev = None;
|
||||
for run in buf
|
||||
@@ -115,25 +153,8 @@ fn cursor_pos(cursor: &Cursor, buf: &TextBuffer) -> Option<Vec2> {
|
||||
.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
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#![feature(const_cmp)]
|
||||
#![feature(const_destruct)]
|
||||
#![feature(portable_simd)]
|
||||
#![feature(gen_blocks)]
|
||||
|
||||
pub mod core;
|
||||
pub mod layout;
|
||||
|
||||
Reference in New Issue
Block a user