move cursor with mouse
This commit is contained in:
parent
e3c0bac9ca
commit
05a6d4803e
@ -77,8 +77,6 @@ fn void? Ctx.layout_string(&ctx, String text, Rect bounds, Anchor anchor, int z_
|
||||
// TODO: Following improvements
|
||||
// [ ] implement a macro to fetch and layout each character, this can be used to reduce code
|
||||
// repetition both here and in measure_string
|
||||
// [ ] the cursor position can be determined by a separate function like get_cursor_position()
|
||||
// this way the function can terminate early if the cursor position is found
|
||||
// [ ] implement a function hit_test_string() to get the character position at point, this can
|
||||
// be used to implement mouse interactions, like cursor movement and selection
|
||||
<*
|
||||
@ -281,6 +279,9 @@ fn void? Ctx.layout_string_topleft(&ctx, String text, Rect bounds, int z_index,
|
||||
// ---------------------------------------------------------------------------------- //
|
||||
|
||||
|
||||
// TODO: get_cursor_position and hit_test_string can be implemented with a glyph_iterator that
|
||||
// returns the position and offset in the string of each glyph
|
||||
|
||||
fn Rect? Ctx.get_cursor_position(&ctx, String text, Rect bounds, Anchor anchor, usz cursor, bool reflow = false)
|
||||
{
|
||||
if (anchor != TOP_LEFT) {
|
||||
@ -348,6 +349,132 @@ fn Rect? Ctx.get_cursor_position(&ctx, String text, Rect bounds, Anchor anchor,
|
||||
return {};
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&in] ctx
|
||||
@param [in] text
|
||||
*>
|
||||
fn usz? Ctx.hit_test_string(&ctx, String text, Rect bounds, Anchor anchor, Point p, bool reflow = false)
|
||||
{
|
||||
if (text == "") return 0;
|
||||
if (bounds.w <= 0 || bounds.h <= 0) return 0;
|
||||
|
||||
Font* font = &ctx.font;
|
||||
Id texture_id = font.id;
|
||||
short line_height = (short)font.line_height();
|
||||
short baseline = (short)font.ascender;
|
||||
short line_gap = (short)font.linegap;
|
||||
short space_width = font.get_glyph(' ').adv!;
|
||||
short tab_width = space_width * TAB_SIZE;
|
||||
|
||||
Point origin = bounds.position();
|
||||
|
||||
LineStack lines;
|
||||
lines.init(tmem, 1);
|
||||
|
||||
Rect str_bounds;
|
||||
|
||||
usz line_start;
|
||||
LineInfo li;
|
||||
Point o;
|
||||
StringIterator ti = text.iterator();
|
||||
for (Codepoint cp; ti.has_next();) {
|
||||
// FIXME: what if the interface changes?
|
||||
cp = ti.next()!;
|
||||
usz off = ti.current;
|
||||
Glyph* gp = font.get_glyph(cp)!;
|
||||
bool push = false;
|
||||
|
||||
switch {
|
||||
case cp == '\n':
|
||||
push = true;
|
||||
case cp == '\t':
|
||||
o.x += tab_width;
|
||||
case ascii::is_cntrl((char)cp):
|
||||
break;
|
||||
default:
|
||||
if (off == line_start) li.first_off = gp.ox;
|
||||
|
||||
Rect b = {
|
||||
.x = o.x + gp.ox,
|
||||
.y = o.y + gp.oy + baseline,
|
||||
.w = gp.w,
|
||||
.h = gp.h,
|
||||
};
|
||||
|
||||
if (reflow && b.x + b.w > bounds.w) {
|
||||
li.width += gp.ox + gp.w;
|
||||
li.height = line_height;
|
||||
push = true;
|
||||
} else {
|
||||
o.x += gp.adv;
|
||||
li.width += gp.adv;
|
||||
li.height = line_height;
|
||||
}
|
||||
}
|
||||
|
||||
if (push) {
|
||||
li.start = line_start;
|
||||
li.end = off;
|
||||
lines.push(li);
|
||||
str_bounds.w = max(str_bounds.w, li.width);
|
||||
str_bounds.h += li.height;
|
||||
|
||||
o.x = 0;
|
||||
o.y += line_height;
|
||||
line_start = off;
|
||||
|
||||
li.height = 0;
|
||||
li.width = 0;
|
||||
}
|
||||
}
|
||||
// FIXME: crap
|
||||
li.start = line_start;
|
||||
li.end = ti.current;
|
||||
lines.push(li);
|
||||
str_bounds.w = max(str_bounds.w, li.width);
|
||||
str_bounds.h += li.height;
|
||||
|
||||
// account for the line gap
|
||||
str_bounds.h += (short)(lines.len() - 1)*line_gap;
|
||||
|
||||
o = bounds.position();
|
||||
o.y += bounds.y_off(str_bounds.h, anchor);
|
||||
foreach (idx, line : lines) {
|
||||
o.x = bounds.x + bounds.x_off(line.width, anchor) - line.first_off;
|
||||
|
||||
StringIterator s = text[line.start:line.end-line.start].iterator();
|
||||
usz prev;
|
||||
for (Codepoint cp; s.has_next(); prev = s.current) {
|
||||
cp = s.next()!;
|
||||
Glyph* gp = font.get_glyph(cp)!;
|
||||
|
||||
switch {
|
||||
case cp == '\n':
|
||||
break;
|
||||
case cp == '\t':
|
||||
o.x += tab_width;
|
||||
case ascii::is_cntrl((char)cp):
|
||||
break;
|
||||
default:
|
||||
Rect b = {
|
||||
.x = o.x + gp.ox,
|
||||
.y = o.y + gp.oy + baseline,
|
||||
.w = gp.w,
|
||||
.h = gp.h,
|
||||
};
|
||||
|
||||
// TODO: skip lines if p is not inside them
|
||||
Rect r = { .x = b.x, .y = b.y - gp.ox, .w = b.w, .h = line.height};
|
||||
if (p.in_rect(r)) return line.start + prev;
|
||||
|
||||
o.x += gp.adv;
|
||||
}
|
||||
}
|
||||
o.y += line.height + line_gap;
|
||||
}
|
||||
return text.len;
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------------- //
|
||||
// TEXT MEASUREMENT //
|
||||
|
@ -80,6 +80,9 @@ fn ElemEvents? Ctx.text_box_id(&ctx, Id id, Size w, Size h, TextEdit* te, Anchor
|
||||
|
||||
// draw the cursor if the element has focus
|
||||
if (elem.events.has_focus) {
|
||||
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)!;
|
||||
}
|
||||
Rect cur = ctx.get_cursor_position(elem.text.te.to_string(), text_bounds, text_alignment, elem.text.te.cursor, reflow)!;
|
||||
cur.w = 2;
|
||||
ctx.push_rect(cur, parent.z_index, &&(Style){.bg = style.fg})!;
|
||||
|
Loading…
x
Reference in New Issue
Block a user