add better centering to box layout

This commit is contained in:
hippoz 2023-08-31 23:27:48 +03:00
parent 023c876641
commit ec15ed6f9f
Signed by: hippoz
GPG key ID: 56C4E02A85F2FBED
9 changed files with 85 additions and 24 deletions

0
example/build.sh → example/build Normal file → Executable file
View file

View file

@ -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]");

View file

@ -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;

View file

@ -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;

View file

@ -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);
}

View file

@ -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);
}
}

View file

@ -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 {

View file

@ -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)

View file

@ -676,7 +676,7 @@ int window_turn(UIWindow *window)
end_clock();
dump_summary(stdout);
//dump_summary(stdout);
}
return 0;
}