From ec15ed6f9fb7a24b1c26921d7028c11dca7cec35 Mon Sep 17 00:00:00 2001 From: hippoz <10706925-hippoz@users.noreply.gitlab.com> Date: Thu, 31 Aug 2023 23:27:48 +0300 Subject: [PATCH] add better centering to box layout --- example/{build.sh => build} | 0 example/main.c | 68 +++++++++++++++++++++++++++++-------- src/box-layout-node.c | 17 +++++++--- src/box-layout-node.h | 2 +- src/main.c | 11 +++++- src/node.c | 3 ++ src/node.h | 1 + src/text-node.c | 5 ++- src/window.c | 2 +- 9 files changed, 85 insertions(+), 24 deletions(-) rename example/{build.sh => build} (100%) mode change 100644 => 100755 diff --git a/example/build.sh b/example/build old mode 100644 new mode 100755 similarity index 100% rename from example/build.sh rename to example/build diff --git a/example/main.c b/example/main.c index 65c688b..8c0dddf 100644 --- a/example/main.c +++ b/example/main.c @@ -4,6 +4,7 @@ typedef struct AppState { PangoFontDescription *desc; + PangoFontDescription *bold_desc; } AppState; UINode *main_view_scaffold(UINode *root, AppState *state) @@ -12,24 +13,60 @@ UINode *main_view_scaffold(UINode *root, AppState *state) background_node_new(root, UISlate50, 0); UIBoxLayoutNode *layout = box_layout_new(root, UI_DIRECTION_VERTICAL); box_layout_set_margins(layout, 36); - layout->gap = 12; - layout->justify_secondary_dimension = UI_BOX_LAYOUT_JUSTIFY_CENTER; + layout->gap = 26; } { - UINode *input = (UINode*)text_input_new(root); - background_node_new(input, UISlate200, 10); - input->width_policy = UI_SIZE_POLICY_STATIC; - input->height_policy = UI_SIZE_POLICY_GROW; - input->rect.w = 38; + UINode *input_container; - UIBoxLayoutNode *layout = box_layout_new(input, UI_DIRECTION_HORIZONTAL); - layout->justify_secondary_dimension = UI_BOX_LAYOUT_JUSTIFY_CENTER; - layout->margin_left = 0; + { + input_container = node_new(root, "input_container"); + input_container->width_policy = UI_SIZE_POLICY_GROW; + UIBoxLayoutNode *layout = box_layout_new(input_container, UI_DIRECTION_HORIZONTAL); + layout->gap = 8; + layout->justify_secondary = UI_BOX_LAYOUT_JUSTIFY_CENTER; + } - UINode *input_text_container = node_new(input, "input_text_container"); - UITextNode *input_text = text_node_new(input_text_container, state->desc, UINeutral950, NULL); - ((UITextInputNode*)input)->text_node = input_text; + { + UINode *input = (UINode*)text_input_new(input_container); + background_node_new(input, UISlate100, 10); + input->width_policy = UI_SIZE_POLICY_GROW; + input->height_policy = UI_SIZE_POLICY_STATIC; + input->rect.h = 42; + + UIBoxLayoutNode *layout = box_layout_new(input, UI_DIRECTION_HORIZONTAL); + layout->justify_secondary = UI_BOX_LAYOUT_JUSTIFY_CENTER; + layout->margin_left = 6; + + UINode *input_text_container = node_new(input, "input_text_container"); + UITextNode *input_text = text_node_new(input_text_container, state->desc, UINeutral950, NULL); + ((UITextInputNode*)input)->text_node = input_text; + } + + { + UINode *add_button = node_new(input_container, "button"); + add_button->width_policy = UI_SIZE_POLICY_STATIC; + add_button->height_policy = UI_SIZE_POLICY_GROW; + add_button->rect.w = 42; + state_background_node_new(add_button, UIIndigo500, UIIndigo600, UIIndigo700, 8.0); + UIBoxLayoutNode *layout = box_layout_new(add_button, UI_DIRECTION_HORIZONTAL); + layout->justify_secondary = UI_BOX_LAYOUT_JUSTIFY_CENTER; + box_layout_set_margins(layout, 8); + UINode *text_container = node_new(add_button, "text_container"); + text_node_new(text_container, state->desc, UINeutral50, "+"); + } + } + + { + UINode *item = node_new(root, "item"); + item->width_policy = UI_SIZE_POLICY_GROW; + box_layout_new(item, UI_DIRECTION_HORIZONTAL)->justify_secondary = UI_BOX_LAYOUT_JUSTIFY_CENTER; + UINode *item_text_container = node_new(item, "item_text_container"); + text_node_new(item_text_container, state->bold_desc, UINeutral950, "Test"); + UINode *separator = node_new(item, "separator"); + separator->width_policy = UI_SIZE_POLICY_GROW; + UINode *remove_button = node_new(item, "remove_button"); + text_node_new(remove_button, state->desc, UINeutral700, "remove"); } return root; @@ -43,8 +80,9 @@ int main(void) return EXIT_FAILURE; } - AppState state ={ - .desc = pango_font_description_from_string("Roboto:size=16") + AppState state = { + .desc = pango_font_description_from_string("Roboto 12"), + .bold_desc = pango_font_description_from_string("Roboto Demi-Bold 12") }; UINode *root = node_new(NULL, "[root]"); diff --git a/src/box-layout-node.c b/src/box-layout-node.c index 513c736..419e5df 100644 --- a/src/box-layout-node.c +++ b/src/box-layout-node.c @@ -75,12 +75,14 @@ int box_layout_handle(UINode *component, enum UIEvent ev, size_t d, void *p) if (ev == UI_EVENT_GET_EXTENTS) { ((UIRect*)p)->w = is_horizontal ? size : maximum_secondary_position; ((UIRect*)p)->h = is_vertical ? size : maximum_secondary_position; + return UI_NODE_COMPUTED_EXTENTS_OK; } - // we can distribute growing layout children only if we have a fixed sized policy in the layout primary direction bool is_fixed_for_primary_direction = (is_horizontal && node->width_policy != UI_SIZE_POLICY_COMPUTED) || (is_vertical && node->height_policy != UI_SIZE_POLICY_COMPUTED); double primary_direction = is_horizontal ? node->rect.w - margin_left - margin_right : node->rect.h - margin_top - margin_bottom; + + // we can distribute growing layout children only if we have a fixed sized policy in the layout primary direction bool distributes_growing_widgets = growing_widgets && is_fixed_for_primary_direction; double size_per_growing_widget = 0; // growing widgets are evenly sized among the remaining free space @@ -93,6 +95,9 @@ int box_layout_handle(UINode *component, enum UIEvent ev, size_t d, void *p) } } + // if we are centered on the primary direction and we are fixed for the primary direction, we can center the elements on the primary direction + double primary_direction_start = !growing_widgets && is_fixed_for_primary_direction && box_layout_node->justify_primary == UI_BOX_LAYOUT_JUSTIFY_CENTER ? (primary_direction - size) / 2 : 0; + bool is_fixed_for_secondary_direction = (is_horizontal && node->height_policy != UI_SIZE_POLICY_COMPUTED) || (is_vertical && node->width_policy != UI_SIZE_POLICY_COMPUTED); double secondary_direction = is_horizontal ? node->rect.h - margin_top - margin_bottom : node->rect.w - margin_left - margin_right; // if our layout secondary direction is fixed, we set the maximum known secondary position to it @@ -100,8 +105,8 @@ int box_layout_handle(UINode *component, enum UIEvent ev, size_t d, void *p) maximum_secondary_position = secondary_direction; } - double x = margin_left; - double y = margin_top; + double x = is_horizontal && primary_direction_start ? primary_direction_start : margin_left; + double y = is_vertical && primary_direction_start ? primary_direction_start : margin_top; current_index = 0; for (int i = 0; i < node->nodes_count; i++) { UINode *current = node->nodes[i]; @@ -118,7 +123,7 @@ int box_layout_handle(UINode *component, enum UIEvent ev, size_t d, void *p) x += gap; } current->rect.x = x; - if (box_layout_node->justify_secondary_dimension == UI_BOX_LAYOUT_JUSTIFY_CENTER) { + if (box_layout_node->justify_secondary == UI_BOX_LAYOUT_JUSTIFY_CENTER) { current->rect.y = margin_top + (maximum_secondary_position - current->rect.h) / 2; } else { current->rect.y = y; @@ -134,7 +139,7 @@ int box_layout_handle(UINode *component, enum UIEvent ev, size_t d, void *p) if (current_index) { y += gap; } - if (box_layout_node->justify_secondary_dimension == UI_BOX_LAYOUT_JUSTIFY_CENTER) { + if (box_layout_node->justify_secondary == UI_BOX_LAYOUT_JUSTIFY_CENTER) { current->rect.x = margin_left + (maximum_secondary_position - current->rect.w) / 2; } else { current->rect.x = x; @@ -167,6 +172,8 @@ UIBoxLayoutNode *box_layout_new(UINode *parent, enum UIDirection direction) n->node.text = "box_layout"; n->node.handle_proto = box_layout_handle; n->direction = direction; + n->justify_primary = UI_BOX_LAYOUT_JUSTIFY_START; + n->justify_secondary = UI_BOX_LAYOUT_JUSTIFY_START; n->margin_top = 0; n->margin_left = 0; n->margin_bottom = 0; diff --git a/src/box-layout-node.h b/src/box-layout-node.h index a8fc793..745a62d 100644 --- a/src/box-layout-node.h +++ b/src/box-layout-node.h @@ -11,7 +11,7 @@ enum UIBoxLayoutJustify { typedef struct UIBoxLayoutNode { UINode node; enum UIDirection direction; - enum UIBoxLayoutJustify justify_secondary_dimension; + enum UIBoxLayoutJustify justify_secondary, justify_primary; double margin_top, margin_left, margin_bottom, margin_right, gap; } UIBoxLayoutNode; diff --git a/src/main.c b/src/main.c index db56cb9..18e3b3b 100644 --- a/src/main.c +++ b/src/main.c @@ -85,8 +85,17 @@ int app_handle(struct UINode *node, void *data, int event_type, size_t d, void * /* increment button */ { UINode *button = node_new(node, "button"); + button->width_policy = UI_SIZE_POLICY_STATIC; + button->height_policy = UI_SIZE_POLICY_STATIC; + button->rect.w = 48; + button->rect.h = 48; + button->flags |= UI_NODE_CONSUMES_HITS; + UIBoxLayoutNode *layout = box_layout_new(button, UI_DIRECTION_HORIZONTAL); + layout->justify_primary = UI_BOX_LAYOUT_JUSTIFY_CENTER; + layout->justify_secondary = UI_BOX_LAYOUT_JUSTIFY_CENTER; state_background_node_new(button, UIPurple600, UIPurple700, UIPurple800, 6.0); - text_node_new(button, state->font, UINeutral50, "add"); + UINode *text_container = node_new(button, "text_container"); + text_node_new(text_container, state->font, UINeutral50, "+"); dispatcher_new(button, UI_EVENT_UNPRESSED, INCREMENT_COUNT, state, app_handle); } diff --git a/src/node.c b/src/node.c index 80abf2b..fb430e2 100644 --- a/src/node.c +++ b/src/node.c @@ -97,6 +97,9 @@ UINode *node_by_point(UINode *root, double x, double y) for (int i = 0; i < root->nodes_count; i++) { UINode *node = root->nodes[i]; if (ui_rect_contains_point(&node->rect, x - node->window_rel_x, y - node->window_rel_y) && !(node->flags & UI_NODE_DISABLED)) { + if (node->flags & UI_NODE_CONSUMES_HITS) { + return node; + } return node_by_point(node, x, y); } } diff --git a/src/node.h b/src/node.h index d1521a9..83fb42b 100644 --- a/src/node.h +++ b/src/node.h @@ -70,6 +70,7 @@ typedef struct UINodeDispatchBucket { #define UI_NODE_COMPONENT (1 << 0) #define UI_NODE_RETAINS_PRESS (1 << 1) #define UI_NODE_DISABLED (1 << 2) +#define UI_NODE_CONSUMES_HITS (1 << 3) #define UI_NODE_DISPATCH_BUCKETS (7) typedef struct UINode { diff --git a/src/text-node.c b/src/text-node.c index e1cd663..03637b8 100644 --- a/src/text-node.c +++ b/src/text-node.c @@ -28,7 +28,10 @@ UITextNode *text_node_new(UINode *parent, PangoFontDescription *desc, UIRGBA col void text_node_set_text(UITextNode *text_node, char *text) { text_node->_pending_text = text; - text_node->node.cached_computed_extents = false; + if (text_node->node.parent) { + // TODO: do this better + text_node->node.parent->cached_computed_extents = false; + } } int text_node_handle(UINode *node, enum UIEvent ev, size_t d, void *p) diff --git a/src/window.c b/src/window.c index 33b20ef..329cd23 100644 --- a/src/window.c +++ b/src/window.c @@ -676,7 +676,7 @@ int window_turn(UIWindow *window) end_clock(); - dump_summary(stdout); + //dump_summary(stdout); } return 0; }