From bafadae1e03152e28183d8a4d8b3db1c473d35e4 Mon Sep 17 00:00:00 2001 From: Alessandro Mauri Date: Sun, 15 Mar 2026 00:07:50 +0100 Subject: [PATCH] scissoring fixes and scroll offset of grow elements --- src/cmd.c3 | 7 +++--- src/layout.c3 | 6 ++--- src/string.c3 | 56 +++++++++++++++++++++------------------------- src/widgets/div.c3 | 24 ++++++++++---------- 4 files changed, 43 insertions(+), 50 deletions(-) diff --git a/src/cmd.c3 b/src/cmd.c3 index 5a14b07..f86a33b 100644 --- a/src/cmd.c3 +++ b/src/cmd.c3 @@ -41,7 +41,7 @@ struct CmdScissor { // command structure struct Cmd (Printable) { CmdType type; - int z_index; + int z_index; union { CmdRect rect; CmdUpdateAtlas update_atlas; @@ -93,7 +93,7 @@ fn void? CmdQueue.sort(&queue) fn usz? Cmd.to_format(Cmd* cmd, Formatter *f) @dynamic { usz ret; - + ret += f.printf("Cmd{ type: %s, z_index: %d, ", cmd.type, cmd.z_index)!; switch (cmd.type) { case CMD_RECT: @@ -111,7 +111,7 @@ fn usz? Cmd.to_format(Cmd* cmd, Formatter *f) @dynamic case CMD_REQ_SKIP_FRAME: ret += f.print("Skip Frame Request")!; } - + ret += f.print("}")!; return ret; } @@ -214,4 +214,3 @@ fn void? Ctx.push_update_atlas(&ctx, Atlas* atlas) } macro Ctx.dbg_rect(&ctx, Rect r, uint c = 0xff000042u) => ctx.push_rect(r, int.max, &&(Style){.bg=c.to_rgba()})!!; - diff --git a/src/layout.c3 b/src/layout.c3 index f3834c9..a4a523c 100644 --- a/src/layout.c3 +++ b/src/layout.c3 @@ -179,7 +179,7 @@ fn void resolve_grow_elements(Elem* e, Elem* p) p.layout.grow_children--; p.layout.occupied += slot; } else if (p.layout.dir == COLUMN) { // grow across the layout axis, inherit width of the parent - e.bounds.w = p.content_space().x; + e.bounds.w = p.content_space().x + p.layout.scroll_offset.x; } } @@ -193,7 +193,7 @@ fn void resolve_grow_elements(Elem* e, Elem* p) p.layout.grow_children--; p.layout.occupied += slot; } else if (p.layout.dir == ROW) { // grow across the layout axis, inherit width of the parent - e.bounds.h = p.content_space().y; + e.bounds.h = p.content_space().y + p.layout.scroll_offset.y; } } } @@ -305,7 +305,7 @@ fn void resolve_placement(Elem* c, Elem* p) // update the parent children_bounds // FIXME: this causes scollbars to flicker in/out during resize because the current frames children_bounds are updated // with the previous' frame child bounds. It would be better to implement another pass during layout. - // FIXME: this long way around to compute the children bounds works and reduces flickering, but it is very ugly + // FIXME: this long way around to compute the children bounds works and reduces flickering, but it is very ugly Rect ncb = c.children_bounds; ncb.x = c.bounds.x; ncb.y = c.bounds.y; diff --git a/src/string.c3 b/src/string.c3 index b5029cd..eaf1e2b 100644 --- a/src/string.c3 +++ b/src/string.c3 @@ -52,7 +52,7 @@ fn short Rect.x_off(Rect bounds, short width, Anchor anchor) case BOTTOM_RIGHT: off = (short)(bounds.w - width); } - + return off; } @@ -112,7 +112,7 @@ fn void? GlyphIterator.init(&self, Allocator allocator, String text, Rect bounds self.populate_lines_stack()!; self.line_off = 0; self.line_idx = 0; - + if (self.lines.len() > 0) { self.o.y += bounds.y_off(self.str_bounds.h, anchor); self.o.x += bounds.x_off(self.lines[0].width, anchor) - self.lines[0].first_off; @@ -152,7 +152,7 @@ fn void? GlyphIterator.populate_lines_stack(&self) Rect b = gp.bounds().off(o); b.y += self.baseline; - + if (self.reflow && b.x + b.w > self.bounds.x + self.bounds.w) { push = true; // roll back this character since it is on the next line @@ -163,7 +163,7 @@ fn void? GlyphIterator.populate_lines_stack(&self) li.width += gp.adv; } } - + if (push) { li.start = line_start; li.end = off; @@ -178,7 +178,7 @@ fn void? GlyphIterator.populate_lines_stack(&self) li.height = 0; li.width = 0; } - + prev_off = off; } if (line_start != ti.current) { @@ -189,7 +189,7 @@ fn void? GlyphIterator.populate_lines_stack(&self) self.str_bounds.w = max(self.str_bounds.w, li.width); self.str_bounds.h += li.height; } - + self.str_bounds.h += (short)(self.lines.len()-1) * self.line_gap; } @@ -207,18 +207,18 @@ fn Rect? GlyphIterator.next(&self) if (self.line_idx >= self.lines.len()) { return NO_MORE_ELEMENT~; } - + LineInfo li = self.lines[self.line_idx]; if (self.line_off >= li.len()) { self.line_idx++; - + if (self.line_idx >= self.lines.len()) { return NO_MORE_ELEMENT~; } - + self.line_off = 0; li = self.lines[self.line_idx]; - + self.o.y += self.line_height + self.line_gap; self.o.x = self.bounds.x + self.bounds.x_off(li.width, self.anchor) - li.first_off; } @@ -232,12 +232,12 @@ fn Rect? GlyphIterator.next(&self) } else { t = self.text[self.line_off..]; } - + usz read = t.len < 4 ? t.len : 4; self.cp = conv::utf8_to_char32(&t[0], &read)!; self.line_off += read; self.gp = self.font.get_glyph(self.cp)!; - + Rect b = {.x = self.o.x, .y = self.o.y}; Point prev_o = self.o; @@ -283,7 +283,7 @@ fn bool GlyphIterator.has_next(&self) if (self.line_idx >= self.lines.len()) { return false; } - + LineInfo li = self.lines[self.line_idx]; if (self.line_idx == self.lines.len() - 1 && self.line_off >= li.len()) { return false; @@ -308,11 +308,10 @@ fn void? Ctx.layout_string(&ctx, String text, Rect bounds, Anchor anchor, int z_ { if (text == "") return; if (bounds.w <= 0 || bounds.h <= 0) return; - ctx.push_scissor(bounds, z_index)!; Font* font = &ctx.font; Id texture_id = font.id; - + GlyphIterator gi; gi.init(tmem, text, bounds, font, anchor, reflow, TAB_SIZE)!; while (gi.has_next()) { @@ -320,9 +319,6 @@ fn void? Ctx.layout_string(&ctx, String text, Rect bounds, Anchor anchor, int z_ Rect uv = gi.gp.uv(); ctx.push_sprite(b, uv, texture_id, z_index, hue)!; } - - ctx.reset_scissor(z_index)!; -// ctx.dbg_rect(str_bounds.off(bounds.position())); } @@ -337,7 +333,7 @@ fn Rect? Ctx.get_cursor_position(&ctx, String text, Rect bounds, Anchor anchor, Font* font = &ctx.font; Id texture_id = font.id; - + if (text == "") text = "\f"; GlyphIterator gi; @@ -382,18 +378,18 @@ fn usz? Ctx.hit_test_string(&ctx, String text, Rect bounds, Anchor anchor, Point if (bounds.w <= 0 || bounds.h <= 0) return 0; Font* font = &ctx.font; - + GlyphIterator gi; gi.init(tmem, text, bounds, font, anchor, reflow, TAB_SIZE)!; - + usz prev_offset = 0; Point prev_o = gi.o; - + while (gi.has_next()) { Point o_before = gi.o; usz offset_before = gi.current_offset(); Rect b = gi.next()!; - + switch { case gi.cp == '\n': // Check if point is on this line before the newline @@ -438,10 +434,10 @@ fn usz? Ctx.hit_test_string(&ctx, String text, Rect bounds, Anchor anchor, Point } } } - + prev_offset = gi.current_offset(); } - + // Point is after all text return text.len; } @@ -460,11 +456,11 @@ fn void? Ctx.draw_string_selection(&ctx, String text, Rect bounds, Anchor anchor 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)!; @@ -487,11 +483,11 @@ fn void? Ctx.draw_string_selection(&ctx, String text, Rect bounds, Anchor anchor 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)!; } @@ -584,5 +580,3 @@ fn TextSize? Ctx.measure_string(&ctx, String text) return ts; } - - diff --git a/src/widgets/div.c3 b/src/widgets/div.c3 index 0c12a14..f0ee7ed 100644 --- a/src/widgets/div.c3 +++ b/src/widgets/div.c3 @@ -93,7 +93,7 @@ fn void? Ctx.div_begin_id(&ctx, elem.div.scroll_x.enabled = scroll_x; elem.div.scroll_y.enabled = scroll_y; elem.div.resize_anchor = resize; - + // check if the div is resizeable bool resized = elem.div.resized && (resize != (ResizeAnchor){}); if (resize != (ResizeAnchor){}) { @@ -124,8 +124,9 @@ fn void? Ctx.div_begin_id(&ctx, ctx.push_rect(elem.bounds.pad(style.margin), elem.z_index, style)!; // update the ctx scissor, it HAS to be after drawing the background - ctx.div_scissor = elem.bounds.pad(elem.layout.content_offset).max({0,0,0,0}); - ctx.push_scissor(ctx.div_scissor, elem.z_index)!; + Rect scissor = elem.bounds.pad(elem.layout.content_offset).max({0,0,0,0}); + ctx.push_scissor(scissor, elem.z_index)!; + ctx.div_scissor = scissor; //elem.events = ctx.get_elem_events(elem); @@ -206,7 +207,7 @@ fn Id? Ctx.div_end(&ctx) Rect b_r = {.x = b.x+b.w-s.w, .y = b.y, .w = s.w, .h = b.h}; Rect b_t = {.x = b.x, .y = b.y, .w = b.w, .h = s.y}; Rect b_b = {.x = b.x, .y = b.y+b.h, .w = b.w, .h = s.h}; - + Rect content_bounds = elem.bounds.pad(elem.layout.content_offset); Point m = ctx.input.mouse.pos; if (elem.events.mouse_hover && elem.events.mouse_press) { @@ -234,7 +235,6 @@ fn Id? Ctx.div_end(&ctx) elem.div.resized.y = true; elem.div.resize_size.y = content_bounds.h - (elem.bounds.y - m.y); } - } // the active_div returns to the parent of the current one @@ -252,7 +252,7 @@ fn Id? Ctx.div_end(&ctx) macro Ctx.@popup(&ctx, bool* state, Point pos, Size width, Size height, - LayoutDirection dir = ROW, Anchor anchor = TOP_LEFT, + LayoutDirection dir = ROW, Anchor anchor = TOP_LEFT, bool scroll_x = false, bool scroll_y = false, ...; @body()) { @@ -265,16 +265,16 @@ macro Ctx.@popup(&ctx, bool* state, macro bool? Ctx.popup_begin(&ctx, Point pos, Size width, Size height, - LayoutDirection dir = ROW, Anchor anchor = TOP_LEFT, + LayoutDirection dir = ROW, Anchor anchor = TOP_LEFT, bool scroll_x = false, bool scroll_y = false, ... ) => ctx.popup_begin_id(@compute_id($vasplat), pos, width, height, dir, anchor, scroll_x, scroll_y); -fn bool? Ctx.popup_begin_id(&ctx, - Id id, - Point pos, +fn bool? Ctx.popup_begin_id(&ctx, + Id id, + Point pos, Size width, Size height, - LayoutDirection dir, Anchor anchor, + LayoutDirection dir, Anchor anchor, bool scroll_x, bool scroll_y ) { @@ -316,7 +316,7 @@ fn bool? Ctx.popup_begin_id(&ctx, ctx.push_scissor(ctx.div_scissor, elem.z_index)!; //elem.events = ctx.get_elem_events(elem); - + // check close condition, mouse release anywhere outside the div bounds if ((ctx.mouse_released() & BTN_ANY) && ctx.input.mouse.pos.outside(elem.bounds)) { return false;