Compare commits

...

3 Commits

5 changed files with 78 additions and 34 deletions

View File

@ -309,9 +309,18 @@ fn Rect? Ctx.layout_string(&ctx, String text, Rect bounds, Anchor anchor, int z_
short line_height = (short)font.line_height(); short line_height = (short)font.line_height();
Rect cursor_rect = {.h = line_height}; Rect cursor_rect = {.h = line_height};
if (text == "" || bounds.w <= 0 || bounds.h <= 0) return cursor_rect; if (bounds.w <= 0 || bounds.h <= 0) return cursor_rect;
ctx.push_scissor(bounds, z_index)!; ctx.push_scissor(bounds, z_index)!;
if (text == "") {
// when text is empty but we need to draw a cursor use a visually empty string
if (cursor >= 0) {
text = "\f";
} else {
return cursor_rect;
}
}
Id texture_id = font.id; Id texture_id = font.id;
short baseline = (short)font.ascender; short baseline = (short)font.ascender;
short line_gap = (short)font.linegap; short line_gap = (short)font.linegap;
@ -356,6 +365,7 @@ fn Rect? Ctx.layout_string(&ctx, String text, Rect bounds, Anchor anchor, int z_
switch { switch {
case cp == '\n': case cp == '\n':
off += x;
break ITER; break ITER;
case cp == '\t': case cp == '\t':
o.x += tab_width; o.x += tab_width;
@ -377,28 +387,42 @@ fn Rect? Ctx.layout_string(&ctx, String text, Rect bounds, Anchor anchor, int z_
} }
} }
line_end = off; line_end = off;
if (line_end == line_start) break; if (line_end == line_start) unreachable("something went wrong in measuring the line");
// with the line width calculate the right origin and layout the line // with the line width calculate the right origin and layout the line
origin.x = bounds.x; origin.x = bounds.x;
short next_line_x = bounds.x; // the x coordinate of the origin if the line_width is zero
switch (anchor) { switch (anchor) {
case TOP_LEFT: nextcase; case TOP_LEFT: nextcase;
case LEFT: nextcase; case LEFT: nextcase;
case BOTTOM_LEFT: case BOTTOM_LEFT:
// TODO: we didn't need to measure the line width with this alignment // TODO: we didn't need to measure the line width with this alignment
origin.x += 0; origin.x += 0;
next_line_x += 0;
case TOP: nextcase; case TOP: nextcase;
case CENTER: nextcase; case CENTER: nextcase;
case BOTTOM: case BOTTOM:
origin.x += (short)(bounds.w - line_width)/2+1; origin.x += (short)(bounds.w - line_width)/2+1;
next_line_x += bounds.w/2;
case TOP_RIGHT: nextcase; case TOP_RIGHT: nextcase;
case RIGHT: nextcase; case RIGHT: nextcase;
case BOTTOM_RIGHT: case BOTTOM_RIGHT:
origin.x += (short)(bounds.w - line_width); origin.x += (short)(bounds.w - line_width);
next_line_x += bounds.w;
} }
cursor_rect.x = origin.x; // reset the cursor to the line
cursor_rect.y = origin.y; if (line_start <= cursor && cursor <= line_end) {
cursor_rect.x = origin.x;
cursor_rect.y = origin.y;
cursor_rect.w = 0;
if (cursor && text[cursor-1] == '\n') {
cursor_rect.x = next_line_x;
cursor_rect.y += line_height;
}
}
Point line_origin = origin;
// see the fixme when measuring the height // see the fixme when measuring the height
//ctx.push_rect({.x = origin.x,.y=origin.y,.w=(short)line_width,.h=(short)string_height}, z_index, &&(Style){.bg=0xff000042u.@to_rgba()})!; //ctx.push_rect({.x = origin.x,.y=origin.y,.w=(short)line_width,.h=(short)string_height}, z_index, &&(Style){.bg=0xff000042u.@to_rgba()})!;
@ -407,7 +431,6 @@ fn Rect? Ctx.layout_string(&ctx, String text, Rect bounds, Anchor anchor, int z_
for (usz x; (cp = str_to_codepoint(line[off..], &x)) != 0 && off < line.len; off += x) { for (usz x; (cp = str_to_codepoint(line[off..], &x)) != 0 && off < line.len; off += x) {
Glyph* gp = font.get_glyph(cp)!; Glyph* gp = font.get_glyph(cp)!;
// update the text bounds
switch { switch {
case cp == '\t': case cp == '\t':
origin.x += tab_width; origin.x += tab_width;
@ -429,15 +452,16 @@ fn Rect? Ctx.layout_string(&ctx, String text, Rect bounds, Anchor anchor, int z_
ctx.push_sprite(b, uv, texture_id, z_index, hue)!; ctx.push_sprite(b, uv, texture_id, z_index, hue)!;
//ctx.push_rect(b, z_index, &&(Style){.bg=0x0000ff66u.@to_rgba()})!; //ctx.push_rect(b, z_index, &&(Style){.bg=0x0000ff66u.@to_rgba()})!;
if (line_start + off < cursor) {
cursor_rect.x = origin.x + gp.adv;
cursor_rect.y = origin.y;
cursor_rect.w = gp.adv;
}
origin.x += gp.adv; origin.x += gp.adv;
} }
if (line_start + off < cursor && text[cursor-1] != '\n') {
cursor_rect.x = origin.x;
cursor_rect.y = origin.y;
cursor_rect.w = gp.adv;
}
} }
// done with the line // done with the line
line_start = line_end; line_start = line_end;
origin.y += line_height + line_gap; origin.y += line_height + line_gap;

View File

@ -31,8 +31,12 @@ struct Layout {
struct origin { struct origin {
short x, y; short x, y;
} }
LayoutDirection dir; // false: the element is laid out according to the parent
Anchor anchor; // true: the element is laid out separate from all other children and the relative position to
// the parent is the .origin field
bool absolute;
LayoutDirection dir; // the direction the children are laid out
Anchor anchor; // how the children are positioned
Rect content_offset; // combined effect of margin, border and padding Rect content_offset; // combined effect of margin, border and padding
} }
@ -112,6 +116,9 @@ fn void update_parent_size(Elem* child, Elem* parent)
Layout* cl = &child.layout; Layout* cl = &child.layout;
Layout* pl = &parent.layout; Layout* pl = &parent.layout;
// if the element has absolute position do not update the parent
if (cl.absolute) return;
Point child_size = cl.get_dimensions(); Point child_size = cl.get_dimensions();
switch (pl.dir) { switch (pl.dir) {
@ -164,6 +171,9 @@ fn void resolve_dimensions(Elem* e, Elem* p)
e.bounds.w = elem_dimensions.x; e.bounds.w = elem_dimensions.x;
e.bounds.h = elem_dimensions.y; e.bounds.h = elem_dimensions.y;
// if the element has absolute position do not update the parent
if (el.absolute) return;
switch (pl.dir) { switch (pl.dir) {
case ROW: case ROW:
if (!el.w.@is_grow()) pl.occupied += e.bounds.w; if (!el.w.@is_grow()) pl.occupied += e.bounds.w;
@ -209,6 +219,13 @@ fn void resolve_placement(Elem* c, Elem* p)
.y = p.bounds.y + pl.origin.y + pl.content_offset.y, .y = p.bounds.y + pl.origin.y + pl.content_offset.y,
}; };
// if the element has absolute position assign the origin and do not update the parent
if (cl.absolute) {
c.bounds.x = p.bounds.x + pl.content_offset.x + cl.origin.x;
c.bounds.y = p.bounds.x + pl.content_offset.x + cl.origin.y;
return;
}
switch (pl.anchor) { switch (pl.anchor) {
case TOP_LEFT: case TOP_LEFT:
c.bounds.x = off.x; c.bounds.x = off.x;

View File

@ -25,12 +25,13 @@ macro Ctx.@div(&ctx,
Size width = @grow, Size height = @grow, Size width = @grow, Size height = @grow,
LayoutDirection dir = ROW, LayoutDirection dir = ROW,
Anchor anchor = TOP_LEFT, Anchor anchor = TOP_LEFT,
bool absolute = false, Point off = {},
bool scroll_x = false, bool scroll_y = false, bool scroll_x = false, bool scroll_y = false,
...; ...;
@body() @body()
) )
{ {
ctx.div_begin(width, height, dir, anchor, scroll_x, scroll_y, $vasplat)!; ctx.div_begin(width, height, dir, anchor, absolute, off, scroll_x, scroll_y, $vasplat)!;
@body(); @body();
return ctx.div_end()!; return ctx.div_end()!;
} }
@ -39,11 +40,12 @@ macro Ctx.div_begin(&ctx,
Size width = @grow(), Size height = @grow(), Size width = @grow(), Size height = @grow(),
LayoutDirection dir = ROW, LayoutDirection dir = ROW,
Anchor anchor = TOP_LEFT, Anchor anchor = TOP_LEFT,
bool absolute = false, Point off = {},
bool scroll_x = false, bool scroll_y = false, bool scroll_x = false, bool scroll_y = false,
... ...
) )
{ {
return ctx.div_begin_id(@compute_id($vasplat), width, height, dir, anchor, scroll_x, scroll_y); return ctx.div_begin_id(@compute_id($vasplat), width, height, dir, anchor, absolute, off, scroll_x, scroll_y);
} }
fn void? Ctx.div_begin_id(&ctx, fn void? Ctx.div_begin_id(&ctx,
@ -51,6 +53,7 @@ fn void? Ctx.div_begin_id(&ctx,
Size width, Size height, Size width, Size height,
LayoutDirection dir, LayoutDirection dir,
Anchor anchor, Anchor anchor,
bool absolute, Point off,
bool scroll_x, bool scroll_y bool scroll_x, bool scroll_y
) )
{ {
@ -75,7 +78,12 @@ fn void? Ctx.div_begin_id(&ctx,
.dir = dir, .dir = dir,
.anchor = anchor, .anchor = anchor,
.content_offset = style.margin + style.border + style.padding, .content_offset = style.margin + style.border + style.padding,
.absolute = absolute,
}; };
if (absolute) {
elem.layout.origin.x = off.x;
elem.layout.origin.y = off.y;
}
ctx.push_rect(elem.bounds.pad(style.margin), elem.div.z_index, style)!; ctx.push_rect(elem.bounds.pad(style.margin), elem.div.z_index, style)!;

View File

@ -76,14 +76,11 @@ fn ElemEvents? Ctx.text_box_id(&ctx, Id id, Size w, Size h, TextEdit* te)
cur = ctx.layout_string(elem.text.te.to_string(), text_bounds, TOP_LEFT, parent.div.z_index, style.fg, elem.text.te.cursor)!; cur = ctx.layout_string(elem.text.te.to_string(), text_bounds, TOP_LEFT, parent.div.z_index, style.fg, elem.text.te.cursor)!;
// draw the cursor if the element has focus // draw the cursor if the element has focus
cur.w = 2;
if (elem.events.has_focus) { if (elem.events.has_focus) {
cur.w = 2; ctx.push_scissor(text_bounds, parent.div.z_index)!;
// FIXME: gross hack for when text is empty
if (cur.x == 0 && cur.y == 0) {
cur.x = text_bounds.x;
cur.y = text_bounds.y;
}
ctx.push_rect(cur, parent.div.z_index, &&(Style){.bg = style.fg})!; ctx.push_rect(cur, parent.div.z_index, &&(Style){.bg = style.fg})!;
ctx.reset_scissor(parent.div.z_index)!;
} }
return elem.events; return elem.events;
} }

View File

@ -52,6 +52,9 @@ const char[*] FS_PATH = "resources/shaders/compiled/ugui.frag.spv";
const char[*] STYLESHEET_PATH = "resources/style.css"; const char[*] STYLESHEET_PATH = "resources/style.css";
const bool LIMIT_FPS = false;
const bool VSYNC = false;
fn int main(String[] args) fn int main(String[] args)
{ {
ArenaAllocator arena; ArenaAllocator arena;
@ -64,7 +67,7 @@ fn int main(String[] args)
defer ui.free(); defer ui.free();
ren::Renderer ren; ren::Renderer ren;
ren.init("Ugui Test", 800, 600, true); ren.init("Ugui Test", 800, 600, VSYNC);
defer ren.free(); defer ren.free();
ui.input_window_size(800, 600)!!; ui.input_window_size(800, 600)!!;
@ -216,15 +219,10 @@ $endswitch
TimeStats dts = draw_times.get_stats(); TimeStats dts = draw_times.get_stats();
TimeStats uts = ui_times.get_stats(); TimeStats uts = ui_times.get_stats();
/* ui.@div(ugui::@fit(), ugui::@fit(), COLUMN, TOP_LEFT, true) {
ui.layout_set_floating()!!; ui.text(string::tformat("frame %d, fps = %.2f", frame, fps))!!;
ui.@div({0, ui.height-150, -300, 150}) { ui.text(string::tformat("ui avg: %s\ndraw avg: %s\nTOT: %s", uts.avg, dts.avg, uts.avg+dts.avg))!!;
ui.layout_set_column()!!;
ui.text_unbounded(string::tformat("frame %d, fps = %.2f", frame, fps))!!;
ui.text_unbounded(string::tformat("ui avg: %s\ndraw avg: %s\nTOT: %s", uts.avg, dts.avg, uts.avg+dts.avg))!!;
ui.text_unbounded(string::tformat("%s %s", mod.lctrl, (String)ui.input.keyboard.text[:ui.input.keyboard.text_len]))!!;
}!!; }!!;
*/
ui.frame_end()!!; ui.frame_end()!!;
/* End UI Handling */ /* End UI Handling */
@ -243,8 +241,8 @@ $endswitch
/* End Drawing */ /* End Drawing */
// wait for the next event, timeout after 100ms // wait for the next event, timeout after 100ms
int timeout = LIMIT_FPS ? (int)(100.0-sleep_clock.mark().to_ms()-0.5) : 0;
sdl::wait_event_timeout(&e, (int)(100.0-sleep_clock.mark().to_ms()-0.5)); sdl::wait_event_timeout(&e, timeout);
fps = 1.0 / fps_clock.mark().to_sec(); fps = 1.0 / fps_clock.mark().to_sec();
frame++; frame++;
@ -356,7 +354,7 @@ fn void calculator(ugui::Ctx* ui, TextEdit* te)
case "9": case "9":
buffer[len++] = ui.get_keys()[0]; buffer[len++] = ui.get_keys()[0];
case "\n": case "\n":
eval = true; eval = len != 0;
case "c": case "c":
len = 0; len = 0;
case "d": case "d":