|
|
|
|
@@ -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
|
|
|
|
|
|