add better centering to box layout
This commit is contained in:
parent
023c876641
commit
ec15ed6f9f
9 changed files with 85 additions and 24 deletions
0
example/build.sh → example/build
Normal file → Executable file
0
example/build.sh → example/build
Normal file → Executable file
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
typedef struct AppState {
|
typedef struct AppState {
|
||||||
PangoFontDescription *desc;
|
PangoFontDescription *desc;
|
||||||
|
PangoFontDescription *bold_desc;
|
||||||
} AppState;
|
} AppState;
|
||||||
|
|
||||||
UINode *main_view_scaffold(UINode *root, AppState *state)
|
UINode *main_view_scaffold(UINode *root, AppState *state)
|
||||||
|
@ -12,26 +13,62 @@ UINode *main_view_scaffold(UINode *root, AppState *state)
|
||||||
background_node_new(root, UISlate50, 0);
|
background_node_new(root, UISlate50, 0);
|
||||||
UIBoxLayoutNode *layout = box_layout_new(root, UI_DIRECTION_VERTICAL);
|
UIBoxLayoutNode *layout = box_layout_new(root, UI_DIRECTION_VERTICAL);
|
||||||
box_layout_set_margins(layout, 36);
|
box_layout_set_margins(layout, 36);
|
||||||
layout->gap = 12;
|
layout->gap = 26;
|
||||||
layout->justify_secondary_dimension = UI_BOX_LAYOUT_JUSTIFY_CENTER;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
UINode *input = (UINode*)text_input_new(root);
|
UINode *input_container;
|
||||||
background_node_new(input, UISlate200, 10);
|
|
||||||
input->width_policy = UI_SIZE_POLICY_STATIC;
|
{
|
||||||
input->height_policy = UI_SIZE_POLICY_GROW;
|
input_container = node_new(root, "input_container");
|
||||||
input->rect.w = 38;
|
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 = (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);
|
UIBoxLayoutNode *layout = box_layout_new(input, UI_DIRECTION_HORIZONTAL);
|
||||||
layout->justify_secondary_dimension = UI_BOX_LAYOUT_JUSTIFY_CENTER;
|
layout->justify_secondary = UI_BOX_LAYOUT_JUSTIFY_CENTER;
|
||||||
layout->margin_left = 0;
|
layout->margin_left = 6;
|
||||||
|
|
||||||
UINode *input_text_container = node_new(input, "input_text_container");
|
UINode *input_text_container = node_new(input, "input_text_container");
|
||||||
UITextNode *input_text = text_node_new(input_text_container, state->desc, UINeutral950, NULL);
|
UITextNode *input_text = text_node_new(input_text_container, state->desc, UINeutral950, NULL);
|
||||||
((UITextInputNode*)input)->text_node = input_text;
|
((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;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,8 +80,9 @@ int main(void)
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
AppState state ={
|
AppState state = {
|
||||||
.desc = pango_font_description_from_string("Roboto:size=16")
|
.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]");
|
UINode *root = node_new(NULL, "[root]");
|
||||||
|
|
|
@ -75,12 +75,14 @@ int box_layout_handle(UINode *component, enum UIEvent ev, size_t d, void *p)
|
||||||
if (ev == UI_EVENT_GET_EXTENTS) {
|
if (ev == UI_EVENT_GET_EXTENTS) {
|
||||||
((UIRect*)p)->w = is_horizontal ? size : maximum_secondary_position;
|
((UIRect*)p)->w = is_horizontal ? size : maximum_secondary_position;
|
||||||
((UIRect*)p)->h = is_vertical ? size : maximum_secondary_position;
|
((UIRect*)p)->h = is_vertical ? size : maximum_secondary_position;
|
||||||
|
|
||||||
return UI_NODE_COMPUTED_EXTENTS_OK;
|
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);
|
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;
|
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;
|
bool distributes_growing_widgets = growing_widgets && is_fixed_for_primary_direction;
|
||||||
double size_per_growing_widget = 0;
|
double size_per_growing_widget = 0;
|
||||||
// growing widgets are evenly sized among the remaining free space
|
// 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);
|
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;
|
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
|
// 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;
|
maximum_secondary_position = secondary_direction;
|
||||||
}
|
}
|
||||||
|
|
||||||
double x = margin_left;
|
double x = is_horizontal && primary_direction_start ? primary_direction_start : margin_left;
|
||||||
double y = margin_top;
|
double y = is_vertical && primary_direction_start ? primary_direction_start : margin_top;
|
||||||
current_index = 0;
|
current_index = 0;
|
||||||
for (int i = 0; i < node->nodes_count; i++) {
|
for (int i = 0; i < node->nodes_count; i++) {
|
||||||
UINode *current = node->nodes[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;
|
x += gap;
|
||||||
}
|
}
|
||||||
current->rect.x = x;
|
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;
|
current->rect.y = margin_top + (maximum_secondary_position - current->rect.h) / 2;
|
||||||
} else {
|
} else {
|
||||||
current->rect.y = y;
|
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) {
|
if (current_index) {
|
||||||
y += gap;
|
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;
|
current->rect.x = margin_left + (maximum_secondary_position - current->rect.w) / 2;
|
||||||
} else {
|
} else {
|
||||||
current->rect.x = x;
|
current->rect.x = x;
|
||||||
|
@ -167,6 +172,8 @@ UIBoxLayoutNode *box_layout_new(UINode *parent, enum UIDirection direction)
|
||||||
n->node.text = "box_layout";
|
n->node.text = "box_layout";
|
||||||
n->node.handle_proto = box_layout_handle;
|
n->node.handle_proto = box_layout_handle;
|
||||||
n->direction = direction;
|
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_top = 0;
|
||||||
n->margin_left = 0;
|
n->margin_left = 0;
|
||||||
n->margin_bottom = 0;
|
n->margin_bottom = 0;
|
||||||
|
|
|
@ -11,7 +11,7 @@ enum UIBoxLayoutJustify {
|
||||||
typedef struct UIBoxLayoutNode {
|
typedef struct UIBoxLayoutNode {
|
||||||
UINode node;
|
UINode node;
|
||||||
enum UIDirection direction;
|
enum UIDirection direction;
|
||||||
enum UIBoxLayoutJustify justify_secondary_dimension;
|
enum UIBoxLayoutJustify justify_secondary, justify_primary;
|
||||||
double margin_top, margin_left, margin_bottom, margin_right, gap;
|
double margin_top, margin_left, margin_bottom, margin_right, gap;
|
||||||
} UIBoxLayoutNode;
|
} UIBoxLayoutNode;
|
||||||
|
|
||||||
|
|
11
src/main.c
11
src/main.c
|
@ -85,8 +85,17 @@ int app_handle(struct UINode *node, void *data, int event_type, size_t d, void *
|
||||||
/* increment button */
|
/* increment button */
|
||||||
{
|
{
|
||||||
UINode *button = node_new(node, "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);
|
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);
|
dispatcher_new(button, UI_EVENT_UNPRESSED, INCREMENT_COUNT, state, app_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -97,6 +97,9 @@ UINode *node_by_point(UINode *root, double x, double y)
|
||||||
for (int i = 0; i < root->nodes_count; i++) {
|
for (int i = 0; i < root->nodes_count; i++) {
|
||||||
UINode *node = root->nodes[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 (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);
|
return node_by_point(node, x, y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,6 +70,7 @@ typedef struct UINodeDispatchBucket {
|
||||||
#define UI_NODE_COMPONENT (1 << 0)
|
#define UI_NODE_COMPONENT (1 << 0)
|
||||||
#define UI_NODE_RETAINS_PRESS (1 << 1)
|
#define UI_NODE_RETAINS_PRESS (1 << 1)
|
||||||
#define UI_NODE_DISABLED (1 << 2)
|
#define UI_NODE_DISABLED (1 << 2)
|
||||||
|
#define UI_NODE_CONSUMES_HITS (1 << 3)
|
||||||
|
|
||||||
#define UI_NODE_DISPATCH_BUCKETS (7)
|
#define UI_NODE_DISPATCH_BUCKETS (7)
|
||||||
typedef struct UINode {
|
typedef struct UINode {
|
||||||
|
|
|
@ -28,7 +28,10 @@ UITextNode *text_node_new(UINode *parent, PangoFontDescription *desc, UIRGBA col
|
||||||
void text_node_set_text(UITextNode *text_node, char *text)
|
void text_node_set_text(UITextNode *text_node, char *text)
|
||||||
{
|
{
|
||||||
text_node->_pending_text = 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)
|
int text_node_handle(UINode *node, enum UIEvent ev, size_t d, void *p)
|
||||||
|
|
|
@ -676,7 +676,7 @@ int window_turn(UIWindow *window)
|
||||||
|
|
||||||
end_clock();
|
end_clock();
|
||||||
|
|
||||||
dump_summary(stdout);
|
//dump_summary(stdout);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue