From 60bec17d36aa2fd1bddb3ffd4413bbfe867b6716 Mon Sep 17 00:00:00 2001 From: Alessandro Mauri Date: Thu, 4 Dec 2025 19:44:46 +0100 Subject: [PATCH] resizeable divs --- TODO | 4 ++ src/layout.c3 | 14 ++++++- src/widgets/div.c3 | 93 +++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 101 insertions(+), 10 deletions(-) diff --git a/TODO b/TODO index d9bcb08..f2fa0fa 100644 --- a/TODO +++ b/TODO @@ -44,6 +44,9 @@ content, the background color is applied starting from the border. Right now push_rect() offsets the background rect by both border and padding [x] Investigate why the debug pointer (cyan rectangle) disappears... +[ ] Selectable divs +[ ] Selectable text +[ ] Copy buffer ## Layout @@ -71,6 +74,7 @@ [x] Use containing_rect() in position_element() to skip some computing and semplify the function [x] Rename position_element() to layout_element() [x] Make functions to mark rows/columns as full, to fix the calculator demo +[ ] Grids ## Input diff --git a/src/layout.c3 b/src/layout.c3 index d22363d..f3834c9 100644 --- a/src/layout.c3 +++ b/src/layout.c3 @@ -20,6 +20,18 @@ enum Anchor { CENTER } +bitstruct ResizeDirection : char { + bool x : 0; + bool y : 1; +} + +bitstruct ResizeAnchor : char { + bool top : 0; + bool bottom : 1; + bool left : 2; + bool right : 3; +} + struct Layout { Size w, h; // size of the CONTENT, does not include margin, border and padding struct children { // the children size includes the children's margin/border/pading @@ -39,7 +51,7 @@ struct Layout { Rect content_offset; // combined effect of margin, border and padding } -// Returns the width and height of a @FIT() element based on it's wanted size (min/max) +// Returns the width and height of a @fit() element based on it's wanted size (min/max) // and the content size, this function is used to both update the parent's children size and // give the dimensions of a fit element // TODO: test and cleanup this function diff --git a/src/widgets/div.c3 b/src/widgets/div.c3 index f7e723b..0c12a14 100644 --- a/src/widgets/div.c3 +++ b/src/widgets/div.c3 @@ -15,6 +15,10 @@ struct ElemDiv { bool on; float value; } + ResizeAnchor resize_anchor; + ResizeAnchor resize_now; + ResizeDirection resized; // the element has been manually resized + Point resize_size; } @@ -39,7 +43,6 @@ macro Ctx.@column(&ctx, Anchor anchor = TOP_LEFT, ...; @body()) }!; } - // useful macro to start and end a div, capturing the trailing block macro Ctx.@div(&ctx, Size width = @grow, Size height = @grow, @@ -47,25 +50,26 @@ macro Ctx.@div(&ctx, Anchor anchor = TOP_LEFT, bool absolute = false, Point off = {}, bool scroll_x = false, bool scroll_y = false, + ResizeAnchor resize = {}, ...; @body() ) { - ctx.div_begin(width, height, dir, anchor, absolute, off, scroll_x, scroll_y, $vasplat)!; + ctx.div_begin(width, height, dir, anchor, absolute, off, scroll_x, scroll_y, resize, $vasplat)!; @body(); return ctx.div_end()!; } macro Ctx.div_begin(&ctx, Size width = @grow(), Size height = @grow(), - LayoutDirection dir = ROW, - Anchor anchor = TOP_LEFT, - bool absolute = false, Point off = {}, - bool scroll_x = false, bool scroll_y = false, + LayoutDirection dir, Anchor anchor, + bool absolute, Point off, + bool scroll_x, bool scroll_y, + ResizeAnchor resize, ... ) { - return ctx.div_begin_id(@compute_id($vasplat), width, height, dir, anchor, absolute, off, scroll_x, scroll_y); + return ctx.div_begin_id(@compute_id($vasplat), width, height, dir, anchor, absolute, off, scroll_x, scroll_y, resize); } fn void? Ctx.div_begin_id(&ctx, @@ -74,7 +78,8 @@ fn void? Ctx.div_begin_id(&ctx, LayoutDirection dir, Anchor anchor, bool absolute, Point off, - bool scroll_x, bool scroll_y + bool scroll_x, bool scroll_y, + ResizeAnchor resize ) { id = ctx.gen_id(id)!; @@ -87,7 +92,21 @@ 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){}) { + // if the element was not resized yet then the size is as-specified + // if the element was resized the size is the same as the last frame + if (elem.div.resized.x == true) { + width = @exact(elem.div.resize_size.x); + } + if (elem.div.resized.y == true) { + height = @exact(elem.div.resize_size.y); + } + } + // update layout with correct info elem.layout = { .w = width, @@ -161,6 +180,63 @@ fn Id? Ctx.div_end(&ctx) elem.div.scroll_x.value = 0; } + // check resize action + /* + * top border + * +-------------------------------------------+ + * | +---------------------------------------+ | + * | | | | + * | | | | + * | | | | + * | | | | + * left | | | | right + * border | | | | border + * | | | | + * | | | | + * | | | | + * | | | | + * | +---------------------------------------+ | + * +-------------------------------------------+ + * bottom border + */ + if (elem.div.resize_anchor != (ResizeAnchor){}) { + Rect b = elem.bounds; + Rect s = style.border + style.margin + style.padding; + Rect b_l = {.x = b.x, .y = b.y, .w = s.x, .h = b.h}; + 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) { + if (elem.div.resize_anchor.right && m.in_rect(b_r)) elem.div.resize_now.right = true; + if (elem.div.resize_anchor.left && m.in_rect(b_l)) elem.div.resize_now.left = true; + if (elem.div.resize_anchor.top && m.in_rect(b_t)) elem.div.resize_now.top = true; + if (elem.div.resize_anchor.bottom && m.in_rect(b_b)) elem.div.resize_now.bottom = true; + } else if (ctx.is_mouse_released(BTN_ANY)) { + elem.div.resize_now = {}; + } + + if (elem.div.resize_now.right == true) { + elem.div.resized.x = true; + elem.div.resize_size.x = content_bounds.w - (elem.bounds.x + elem.bounds.w - m.x); + } + if (elem.div.resize_now.bottom == true) { + elem.div.resized.y = true; + elem.div.resize_size.y = content_bounds.h - (elem.bounds.y + elem.bounds.h - m.y); + } + if (elem.div.resize_now.left == true) { + elem.div.resized.x = true; + elem.div.resize_size.x = content_bounds.w - (elem.bounds.x - m.x); + } + if (elem.div.resize_now.top == true) { + 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 ctx.active_div = ctx.tree.parentof(ctx.active_div)!; Elem* parent = ctx.get_parent()!; @@ -247,6 +323,5 @@ fn bool? Ctx.popup_begin_id(&ctx, } // TODO: check active - // TODO: check resizeable return true; }