semi-working text
This commit is contained in:
parent
ce9d1e6684
commit
546f3628c7
@ -244,6 +244,7 @@ fn Rect? GlyphIterator.next(&self)
|
|||||||
if (self.anchor == TOP_LEFT) {
|
if (self.anchor == TOP_LEFT) {
|
||||||
self.o.x = self.bounds.x;
|
self.o.x = self.bounds.x;
|
||||||
self.o.y += self.line_height + self.line_gap;
|
self.o.y += self.line_height + self.line_gap;
|
||||||
|
self.line_idx++;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case self.cp == '\t':
|
case self.cp == '\t':
|
||||||
@ -254,10 +255,11 @@ fn Rect? GlyphIterator.next(&self)
|
|||||||
b = self.gp.bounds().off(self.o);
|
b = self.gp.bounds().off(self.o);
|
||||||
b.y += self.baseline;
|
b.y += self.baseline;
|
||||||
if (self.anchor == TOP_LEFT) {
|
if (self.anchor == TOP_LEFT) {
|
||||||
if (self.o.x == self.bounds.x) self.bounds.x -= self.gp.ox;
|
//if (self.o.x == self.bounds.x) self.bounds.x -= self.gp.ox;
|
||||||
if (self.reflow && b.bottom_right().x > self.bounds.bottom_right().x) {
|
if (self.reflow && b.bottom_right().x > self.bounds.bottom_right().x) {
|
||||||
self.o.x = self.bounds.x - self.gp.ox;
|
self.o.x = self.bounds.x - self.gp.ox;
|
||||||
self.o.y += self.line_height + self.line_gap;
|
self.o.y += self.line_height + self.line_gap;
|
||||||
|
self.line_idx++;
|
||||||
b = self.gp.bounds().off(self.o);
|
b = self.gp.bounds().off(self.o);
|
||||||
b.y += self.baseline;
|
b.y += self.baseline;
|
||||||
}
|
}
|
||||||
@ -286,7 +288,11 @@ fn bool GlyphIterator.has_next(&self)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usz GlyphIterator.current_offset(&self) => self.lines[self.line_idx].start + self.line_off;
|
fn usz GlyphIterator.current_offset(&self)
|
||||||
|
{
|
||||||
|
if (self.anchor == TOP_LEFT) return self.line_off;
|
||||||
|
return self.lines[self.line_idx].start + self.line_off;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// layout a string inside a bounding box, following the given alignment (anchor).
|
// layout a string inside a bounding box, following the given alignment (anchor).
|
||||||
@ -315,6 +321,7 @@ fn void? Ctx.layout_string(&ctx, String text, Rect bounds, Anchor anchor, int z_
|
|||||||
// ctx.dbg_rect(str_bounds.off(bounds.position()));
|
// ctx.dbg_rect(str_bounds.off(bounds.position()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------- //
|
// ---------------------------------------------------------------------------------- //
|
||||||
// CURSOR AND MOUSE //
|
// CURSOR AND MOUSE //
|
||||||
// ---------------------------------------------------------------------------------- //
|
// ---------------------------------------------------------------------------------- //
|
||||||
@ -435,6 +442,55 @@ fn usz? Ctx.hit_test_string(&ctx, String text, Rect bounds, Anchor anchor, Point
|
|||||||
return text.len;
|
return text.len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: implement a function `layout_string_with_selection` to avoid iterating over the string twice
|
||||||
|
fn void? Ctx.draw_string_selection(&ctx, String text, Rect bounds, Anchor anchor, usz start, usz end, int z_index, Color hue, bool reflow = false)
|
||||||
|
{
|
||||||
|
if (text == "") return;
|
||||||
|
if (bounds.w <= 0 || bounds.h <= 0) return;
|
||||||
|
|
||||||
|
if (start > end) @swap(start, end);
|
||||||
|
|
||||||
|
// Ensure start < end
|
||||||
|
if (start > end) {
|
||||||
|
usz temp = start;
|
||||||
|
start = end;
|
||||||
|
end = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.push_scissor(bounds, z_index)!;
|
||||||
|
|
||||||
|
Font* font = &ctx.font;
|
||||||
|
|
||||||
|
GlyphIterator gi;
|
||||||
|
gi.init(tmem, text, bounds, font, anchor, reflow, TAB_SIZE)!;
|
||||||
|
|
||||||
|
Rect sel_rect = { .h = gi.line_height }; // selection rect
|
||||||
|
isz sel_line = -1; // selection line
|
||||||
|
while (gi.has_next()) {
|
||||||
|
Rect b = gi.next()!;
|
||||||
|
usz off = gi.current_offset()-1;
|
||||||
|
isz line = gi.line_idx;
|
||||||
|
bool in_selection = start <= off && off <= end;
|
||||||
|
|
||||||
|
if (in_selection && line != sel_line) {
|
||||||
|
if (sel_line != -1) {
|
||||||
|
ctx.push_rect(sel_rect, z_index, &&{.bg = hue})!;
|
||||||
|
}
|
||||||
|
sel_rect = {.x = gi.o.x - b.w, .y = gi.o.y, .w = 0, .h = gi.line_height};
|
||||||
|
sel_line = line;
|
||||||
|
}
|
||||||
|
if (in_selection) {
|
||||||
|
sel_rect.w = gi.o.x - sel_rect.x;
|
||||||
|
if (gi.cp == '\n') sel_rect.w += gi.space_width;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (off > end) break;
|
||||||
|
}
|
||||||
|
ctx.push_rect(sel_rect, z_index, &&{.bg = hue})!;
|
||||||
|
|
||||||
|
ctx.reset_scissor(z_index)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------- //
|
// ---------------------------------------------------------------------------------- //
|
||||||
// TEXT MEASUREMENT //
|
// TEXT MEASUREMENT //
|
||||||
|
@ -7,6 +7,8 @@ struct TextEdit {
|
|||||||
char[] buffer;
|
char[] buffer;
|
||||||
usz chars;
|
usz chars;
|
||||||
usz cursor;
|
usz cursor;
|
||||||
|
usz sel_start, sel_end;
|
||||||
|
bool selection;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn String TextEdit.to_string(&te) => (String)te.buffer[:te.chars];
|
fn String TextEdit.to_string(&te) => (String)te.buffer[:te.chars];
|
||||||
@ -38,11 +40,15 @@ fn bool Ctx.text_edit(&ctx, TextEdit* te)
|
|||||||
|
|
||||||
// handle modkeys
|
// handle modkeys
|
||||||
if (te.chars) {
|
if (te.chars) {
|
||||||
|
// if not already in selection update the selection start/end to the cursor positon
|
||||||
|
if (!te.selection) {
|
||||||
|
te.sel_start = te.sel_end = te.cursor;
|
||||||
|
}
|
||||||
|
|
||||||
// handle backspace and delete
|
// handle backspace and delete
|
||||||
if (mod.bkspc) {
|
if (mod.bkspc) {
|
||||||
if (te.cursor > 0) {
|
if (te.cursor > 0) {
|
||||||
// TODO: only delete until punctuation
|
usz how_many = te.how_many_bw(mod);
|
||||||
usz how_many = mod & KMOD_CTRL ? te.until_cursor().prev_word_off() : te.until_cursor().prev_char_off();
|
|
||||||
te.buffer[te.cursor-how_many : after] = te.buffer[te.cursor : after];
|
te.buffer[te.cursor-how_many : after] = te.buffer[te.cursor : after];
|
||||||
te.cursor -= how_many;
|
te.cursor -= how_many;
|
||||||
te.chars -= how_many;
|
te.chars -= how_many;
|
||||||
@ -51,7 +57,7 @@ fn bool Ctx.text_edit(&ctx, TextEdit* te)
|
|||||||
}
|
}
|
||||||
if (mod.del) {
|
if (mod.del) {
|
||||||
if (after > 0 && te.cursor < te.chars) {
|
if (after > 0 && te.cursor < te.chars) {
|
||||||
usz how_many = mod & KMOD_CTRL ? te.from_cursor().next_word_off() : te.from_cursor().next_char_off();
|
usz how_many = te.how_many_fw(mod);
|
||||||
te.buffer[te.cursor : after] = te.buffer[te.cursor+how_many : after];
|
te.buffer[te.cursor : after] = te.buffer[te.cursor+how_many : after];
|
||||||
te.chars -= how_many;
|
te.chars -= how_many;
|
||||||
after -= how_many;
|
after -= how_many;
|
||||||
@ -62,16 +68,31 @@ fn bool Ctx.text_edit(&ctx, TextEdit* te)
|
|||||||
// handle arrow keys
|
// handle arrow keys
|
||||||
if (mod.left) {
|
if (mod.left) {
|
||||||
if (te.cursor > 0) {
|
if (te.cursor > 0) {
|
||||||
usz how_many = mod & KMOD_CTRL ? te.until_cursor().prev_word_off() : te.until_cursor().prev_char_off();
|
usz how_many = te.how_many_bw(mod);
|
||||||
te.cursor -= how_many;
|
te.cursor -= how_many;
|
||||||
after += how_many;
|
after += how_many;
|
||||||
|
|
||||||
|
if (mod & KMOD_SHIFT) {
|
||||||
|
te.selection = true;
|
||||||
|
if (!te.selection) te.sel_start = te.cursor;
|
||||||
|
te.sel_end = te.cursor;
|
||||||
|
} else {
|
||||||
|
te.selection = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (mod.right) {
|
if (mod.right) {
|
||||||
if (after > 0) {
|
if (after > 0) {
|
||||||
usz how_many = mod & KMOD_CTRL ? te.from_cursor().next_word_off() : te.from_cursor().next_char_off();
|
usz how_many = te.how_many_fw(mod);
|
||||||
te.cursor += how_many;
|
te.cursor += how_many;
|
||||||
after -= how_many;
|
after -= how_many;
|
||||||
|
|
||||||
|
if (mod & KMOD_SHIFT) {
|
||||||
|
te.sel_end = te.cursor-1;
|
||||||
|
te.selection = true;
|
||||||
|
} else {
|
||||||
|
te.selection = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// FIXME: this still doesn't work
|
// FIXME: this still doesn't work
|
||||||
@ -103,10 +124,36 @@ fn bool Ctx.text_edit(&ctx, TextEdit* te)
|
|||||||
// TODO: mod.end
|
// TODO: mod.end
|
||||||
// TODO: selection with shift+arrows
|
// TODO: selection with shift+arrows
|
||||||
}
|
}
|
||||||
|
println("cursor: ", te.cursor, " start: ", te.sel_start, " end: ", te.sel_end);
|
||||||
|
|
||||||
return free == 0;
|
return free == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn usz TextEdit.how_many_fw(&te, ModKeys mod)
|
||||||
|
{
|
||||||
|
if (mod & KMOD_CTRL) {
|
||||||
|
return te.from_cursor().next_word_off();
|
||||||
|
} else if (te.selection && !(mod & KMOD_SHIFT)) {
|
||||||
|
// FIXME: +1 here is not correct, should use next_char_off()
|
||||||
|
return te.sel_start > te.sel_end ? te.sel_end - te.sel_start + 1 : te.from_cursor().next_char_off();
|
||||||
|
} else {
|
||||||
|
return te.from_cursor().next_char_off();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// how many characters to jump backwards (left arrow)
|
||||||
|
fn usz TextEdit.how_many_bw(&te, ModKeys mod)
|
||||||
|
{
|
||||||
|
if (mod & KMOD_CTRL) {
|
||||||
|
return te.until_cursor().prev_word_off();
|
||||||
|
} else if (te.selection && !(mod & KMOD_SHIFT)){
|
||||||
|
// FIXME: +1 here is not correct, should use prev_char_off()
|
||||||
|
return te.sel_start < te.sel_end ? te.sel_end - te.sel_start + 1 : te.until_cursor().prev_char_off();
|
||||||
|
} else {
|
||||||
|
return te.until_cursor().prev_char_off();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
macro isz char[].next_char_off(b) => grapheme::next_character_break_utf8(b.ptr, b.len);
|
macro isz char[].next_char_off(b) => grapheme::next_character_break_utf8(b.ptr, b.len);
|
||||||
macro isz char[].next_word_off(b) => grapheme::next_word_break_utf8(b.ptr, b.len);
|
macro isz char[].next_word_off(b) => grapheme::next_word_break_utf8(b.ptr, b.len);
|
||||||
|
|
||||||
|
@ -71,19 +71,23 @@ fn ElemEvents? Ctx.text_box_id(&ctx, Id id, Size w, Size h, TextEdit* te, Anchor
|
|||||||
ctx.text_edit(elem.text.te);
|
ctx.text_edit(elem.text.te);
|
||||||
}
|
}
|
||||||
|
|
||||||
// draw the box
|
|
||||||
|
|
||||||
Rect bg_bounds = elem.bounds.pad(style.margin);
|
Rect bg_bounds = elem.bounds.pad(style.margin);
|
||||||
Rect text_bounds = elem.bounds.pad(elem.layout.content_offset);
|
Rect text_bounds = elem.bounds.pad(elem.layout.content_offset);
|
||||||
ctx.push_rect(bg_bounds, parent.z_index, style)!;
|
ctx.push_rect(bg_bounds, parent.z_index, style)!;
|
||||||
ctx.layout_string(elem.text.te.to_string(), text_bounds, text_alignment, parent.z_index, style.fg, reflow)!;
|
String s = elem.text.te.to_string();
|
||||||
|
if (elem.text.te.selection) {
|
||||||
|
usz start = elem.text.te.sel_start;
|
||||||
|
usz end = elem.text.te.sel_end;
|
||||||
|
ctx.draw_string_selection(s, text_bounds, text_alignment, start, end, parent.z_index, style.accent, reflow)!;
|
||||||
|
}
|
||||||
|
ctx.layout_string(s, text_bounds, text_alignment, parent.z_index, style.fg, reflow)!;
|
||||||
|
|
||||||
// draw the cursor if the element has focus
|
// draw the cursor if the element has focus
|
||||||
if (elem.events.has_focus) {
|
if (elem.events.has_focus) {
|
||||||
if (elem.events.mouse_press) {
|
if (elem.events.mouse_press) {
|
||||||
elem.text.te.cursor = ctx.hit_test_string(elem.text.te.to_string(), text_bounds, text_alignment, ctx.input.mouse.pos, reflow)!;
|
elem.text.te.cursor = ctx.hit_test_string(s, text_bounds, text_alignment, ctx.input.mouse.pos, reflow)!;
|
||||||
}
|
}
|
||||||
Rect cur = ctx.get_cursor_position(elem.text.te.to_string(), text_bounds, text_alignment, elem.text.te.cursor, reflow)!;
|
Rect cur = ctx.get_cursor_position(s, text_bounds, text_alignment, elem.text.te.cursor, reflow)!;
|
||||||
cur.w = 2;
|
cur.w = 2;
|
||||||
ctx.push_rect(cur, parent.z_index, &&(Style){.bg = style.fg})!;
|
ctx.push_rect(cur, parent.z_index, &&(Style){.bg = style.fg})!;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user