diff --git a/gui/gui.c b/gui/gui.c index 2cef826..2686fb0 100644 --- a/gui/gui.c +++ b/gui/gui.c @@ -96,6 +96,17 @@ static inline int point_in_rect(int x, int y, GUI_Rectangle rect) && (x <= rect.x1) && (y <= rect.y1); } +static inline int point_in_rect_with_offset( + int x, int y, + GUI_Rectangle rect, + int x_offset, int y_offset) +{ + return + (x >= (rect.x0+x_offset)) + && (y >= (rect.y0+y_offset)) + && (x <= (rect.x1+x_offset)) + && (y <= (rect.y1+y_offset)); +} typedef enum { @@ -225,6 +236,10 @@ typedef struct { GUI_Draw_Command *first_draw_command; int num_draw_commands; Pixel_Buffer *pixel_buffer; + bool should_draw; + // TODO(Zelaven): Offsets like for the subtrees. + int offset_x; + int offset_y; } GUI_Layer; @@ -355,7 +370,14 @@ void fff(GUI_Context *context) } #endif +#define GUIWITH_INNER2(token) defer ## token +#define GUIWITH_INNER(token) GUIWITH_INNER2(token) +#define GUIWITH(storage_variable, TYPE, value_to_use)\ +for(struct {TYPE temp; int i;} GUIWITH_INNER(__LINE__) = {.temp=(storage_variable), .i = ((storage_variable) = (value_to_use), 0)}; !GUIWITH_INNER(__LINE__).i; GUIWITH_INNER(__LINE__).i += 1, ((storage_variable) = GUIWITH_INNER(__LINE__).temp)) +#define GUI_ON_LAYER(context, layer) \ + (layer)->should_draw = true;\ + GUIWITH(context->current_layer, GUI_Layer*, layer) // --- @@ -364,7 +386,7 @@ void fff(GUI_Context *context) // TODO(Zelaven): The mouse coordinates are in the context, so no need to pass // them as parameters. #undef ENABLE_DEBUG -#define ENABLE_DEBUG 0 +#define ENABLE_DEBUG 1 void gui_apply_input( GUI_Context *context, int mouse_x, @@ -380,12 +402,15 @@ void gui_apply_input( { GUI_Layer *top_layer = &context->top_layer; GUI_Subtree *root_subtree = (GUI_Subtree*)top_layer->subtree_rdic.root; - if(root_subtree != NULL) + if(top_layer->should_draw && root_subtree != NULL) { GUI_Node *top_layer_root = (GUI_Node*)root_subtree->rdic.root; if(top_layer_root != NULL) - if(point_in_rect(mouse_x, mouse_y, top_layer_root->rect)) + if(point_in_rect_with_offset( + mouse_x, mouse_y, + top_layer_root->rect, + top_layer->offset_x, top_layer->offset_y)) { hit_layer = &context->top_layer; hit_root_subtree = root_subtree; @@ -579,45 +604,24 @@ GUI_Subtree_Reference gui_get_subtree( } -#undef ENABLE_DEBUG -#define ENABLE_DEBUG 0 -// TODO(Zelaven): Make this cause redrawing of root on parameter change. -// Maybe an explicit parameter so it can be based on received events? -// Such as expose events, resize events, etc. -// May also be a good idea because struct-comparing styles each frame wouldn't -// be good for performance, so a force-redraw bool sounds like an always-good -// option. That way you can force-dirty the root when you make a style change, -// but it could also be added at other granularity. -// Or should it be something that is done by just setting -// ref.node->dirty = true? -// That is one option, specifying the dirty flag as a part of the API itself. -// I think I like that option. -GUI_Node_Reference gui_context_start_frame( +GUI_Node_Reference gui_context_prepare_layer( GUI_Context *context, + GUI_Layer *layer, GUI_Node_Reference last_root_reference, int width, int height, GUI_Layout_Direction layout_direction, GUI_Style *style) { - //context->frame_current_node = context->root; - context->num_draw_commands = 0; - memory_arena_reset(context->draw_command_arena); - - DEBUG("%s: %d %d %d %d\n", __func__, - context->mouse_x, context->mouse_y, - context->mouse_pressed, context->mouse_down); - - context->current_layer = &context->background_layer; - - GUI_Subtree_Reference root_subtree = context->current_layer->root_subtree; + layer->should_draw = false; + GUI_Subtree_Reference root_subtree = layer->root_subtree; //root_subtree = gui_get_subtree(context, root_subtree); root_subtree.rdic_ref = rdic_context_start_frame( - &context->current_layer->subtree_rdic, root_subtree.rdic_ref); + &layer->subtree_rdic, root_subtree.rdic_ref); if(root_subtree.node == NULL) { return (GUI_Node_Reference){0}; } - context->current_layer->root_subtree = root_subtree; + layer->root_subtree = root_subtree; root_subtree.node->rdic.freelist = context->node_freelist; //static GUI_Subtree background_subtree = {0}; @@ -638,6 +642,7 @@ GUI_Node_Reference gui_context_start_frame( //RDIC_Node_Reference new_root_reference = rdic_context_start_frame( // &background_subtree.rdic, // last_root_reference); + GUI_Node *root = (GUI_Node*)new_root_reference.node; root->dirty = false; if(!gui_node_references_equal(last_root_reference, new_root_reference)) { @@ -647,8 +652,6 @@ GUI_Node_Reference gui_context_start_frame( root->dirty = true; } - gui_apply_input(context, context->mouse_x, context->mouse_y); - root->debug_string = GUI_STRING("root"); root->layout_direction = layout_direction; root->semantic_size[GUI_AXIS2_X] = (GUI_Size) @@ -659,6 +662,46 @@ GUI_Node_Reference gui_context_start_frame( return new_root_reference; } + +#undef ENABLE_DEBUG +#define ENABLE_DEBUG 0 +// TODO(Zelaven): Make this cause redrawing of root on parameter change. +// Maybe an explicit parameter so it can be based on received events? +// Such as expose events, resize events, etc. +// May also be a good idea because struct-comparing styles each frame wouldn't +// be good for performance, so a force-redraw bool sounds like an always-good +// option. That way you can force-dirty the root when you make a style change, +// but it could also be added at other granularity. +// Or should it be something that is done by just setting +// ref.node->dirty = true? +// That is one option, specifying the dirty flag as a part of the API itself. +// I think I like that option. +GUI_Node_Reference gui_context_start_frame( + GUI_Context *context, + GUI_Node_Reference last_root_reference, + int width, int height, + GUI_Layout_Direction layout_direction, + GUI_Style *style) +{ + //context->frame_current_node = context->root; + context->num_draw_commands = 0; + memory_arena_reset(context->draw_command_arena); + + DEBUG("%s: %d %d %d %d\n", __func__, + context->mouse_x, context->mouse_y, + context->mouse_pressed, context->mouse_down); + + context->current_layer = &context->background_layer; + GUI_Node_Reference new_root_reference = gui_context_prepare_layer( + context, context->current_layer, last_root_reference, + width, height, layout_direction, style); + + context->current_layer->should_draw = true; + + gui_apply_input(context, context->mouse_x, context->mouse_y); + + return new_root_reference; +} void gui_context_finish_frame( GUI_Context *context) { @@ -1028,6 +1071,32 @@ bool gui_dumb_button( #undef ENABLE_DEBUG #define ENABLE_DEBUG 0 +bool gui_mouseover_box( + GUI_Context *context, + GUI_Node_Reference *last_reference, + GUI_String text, + GUI_Style *style, + GUI_Size size[static 2]) +{ + GUI_Node_Reference new_reference = gui_get_node( + context, *last_reference, GUI_LAYOUT_NONE, style, size); + GUI_Node *node = new_reference.node; + node->debug_string = GUI_STRING("gui_mouseover_box"); + node->text_string = text; + + bool new_ref_is_top = gui_node_references_equal( + new_reference, context->top_node_under_cursor); + DEBUG(" %p\n | %p\n | %p\n", + context->top_node_under_cursor.node, + last_reference->node, new_reference.node); + bool hovered = new_ref_is_top; + + *last_reference = new_reference; + return hovered; +} + +#undef ENABLE_DEBUG +#define ENABLE_DEBUG 1 bool gui_slider( GUI_Context *context, GUI_Node_Reference *last_reference, @@ -1053,10 +1122,10 @@ bool gui_slider( new_reference, context->top_node_under_cursor); bool inner_ref_is_top = gui_node_references_equal( new_inner_reference, context->top_node_under_cursor); - DEBUG("%p\n | %p %p\n | %p %p\n", + /*DEBUG(" %p\n | %p %p\n | %p %p\n", context->top_node_under_cursor.node, last_reference->node, inner_box_reference->node, - new_reference.node, new_inner_reference.node); + new_reference.node, new_inner_reference.node);*/ if(new_ref_is_top || inner_ref_is_top) { DEBUG("\n%s: reference is the top node under cursor.\n", __func__); @@ -1814,6 +1883,14 @@ void test_gui__subtree( full_gui_rectangle.y1-full_gui_rectangle.y0, GUI_LAYOUT_VERTICAL, &root_style); + static GUI_Node_Reference top_root = {0}; + top_root = gui_context_prepare_layer( + context, &context->top_layer, top_root, + context->top_layer.pixel_buffer->width, + context->top_layer.pixel_buffer->height, + GUI_LAYOUT_HORIZONTAL, &outer_style); + context->top_layer.offset_x = 50; + context->top_layer.offset_y = 250; static GUI_Node_Reference top = {0}; @@ -1841,7 +1918,7 @@ void test_gui__subtree( static GUI_Node_Reference dirty_button = {0}; static GUI_Style button_style = {0x99,0x99,0x99, 0x00222222,2,0}; static GUI_Size button_size[2] = { - {GUI_SIZERULE_PIXELS, 200, 100}, + {GUI_SIZERULE_PIXELS, 170, 100}, {GUI_SIZERULE_PIXELS, 40, 100}}; if(gui_dumb_button( context, &dirty_button, GUI_STRING("Dirty middle"), @@ -1857,6 +1934,31 @@ void test_gui__subtree( set_pixels = true; printf("Setting pixels.\n"); } + static bool tooltip_toggle = false; + static GUI_Node_Reference dirty_button3 = {0}; + if(gui_dumb_button( + context, &dirty_button3, GUI_STRING("Hover for tooltip"), + &button_style, button_size)) + { + tooltip_toggle = !tooltip_toggle; + } + static GUI_Node_Reference dirty_button4 = {0}; + if( + gui_mouseover_box( + context, &dirty_button4, GUI_STRING("Hover for tooltip"), + &button_style, button_size) + || tooltip_toggle) + { + GUI_ON_LAYER(context, &context->top_layer) + { + static GUI_Size full_size[2] = { + {GUI_SIZERULE_PERCENTOFPARENT, 100, 100}, + {GUI_SIZERULE_PERCENTOFPARENT, 100, 100}}; + static GUI_Node_Reference top_layer_block = {0}; + top_layer_block = gui_dumb_block( + context, top_layer_block, &element_style, full_size); + } + } } gui_pop_parent(context); // middle. middle = gui_dumb_block(context, middle, &outer_style, middle_size); @@ -3611,6 +3713,30 @@ void gui_layout_and_draw(GUI_Context *context) } } +void gui_compose_layers( + GUI_Context *context, + Pixel_Buffer *target_buffer) +{ + // TODO(Zelaven): Make the layers in the context iteratable. + GUI_Layer *layers[] = { + &context->background_layer, + &context->top_layer, + }; + for( + size_t i = 0; + i < ARRAYLENGTH(layers); + i++) + { + GUI_Layer *layer = layers[i]; + if(layer->should_draw) { + gui_draw_image_direct( + target_buffer, + layer->pixel_buffer, + layer->offset_x, + layer->offset_y); + } + } +} #include // usleep. @@ -3678,12 +3804,27 @@ int main(void) RDIC_Freelist subtree_freelist = {&g_gui_subtree_freelist[0].rdic_node}; context.subtree_freelist = &subtree_freelist; + Pixel_Buffer background_pixel_buffer = (Pixel_Buffer){ + .pixels = malloc(xwindow.pixel_buffer.width*xwindow.pixel_buffer.height*sizeof(GUI_Color)), + .width = xwindow.pixel_buffer.width, + .height = xwindow.pixel_buffer.height, + }; + Pixel_Buffer top_pixel_buffer = (Pixel_Buffer){ + .pixels = malloc(300*200*sizeof(GUI_Color)), + .width = 300, + .height = 200, + }; + assert(background_pixel_buffer.pixels); + assert(top_pixel_buffer.pixels); + context.background_layer = (GUI_Layer){ .subtree_rdic.freelist = &subtree_freelist, - .pixel_buffer = &xwindow.pixel_buffer, + //.pixel_buffer = &xwindow.pixel_buffer, + .pixel_buffer = &background_pixel_buffer, }; context.top_layer = (GUI_Layer){ .subtree_rdic.freelist = &subtree_freelist, + .pixel_buffer = &top_pixel_buffer, }; #endif @@ -3692,10 +3833,16 @@ int main(void) fill_pixel_buffer( xwindow.pixel_buffer.pixels, - xwindow.width, - xwindow.height, - 0, - 0); + xwindow.width, xwindow.height, + 0, 0); + fill_pixel_buffer( + background_pixel_buffer.pixels, + background_pixel_buffer.width, background_pixel_buffer.height, + 0, 0); + fill_pixel_buffer( + top_pixel_buffer.pixels, + top_pixel_buffer.width, top_pixel_buffer.height, + 0, 0); GUI_Rectangle screen_rectangle = { .x0 = 0, .y0 = 0, .x1 = xwindow.width, .y1 = xwindow.height, @@ -3798,7 +3945,7 @@ int main(void) //gui_layout_nodes(&context); #if 1 gui_layout_and_draw(&context); - SET_PIXEL(&xwindow.pixel_buffer, 239, 259, 0x00ff0000); + gui_compose_layers(&context, &xwindow.pixel_buffer); #else for( GUI_Subtree *current = (GUI_Subtree*)context.background_layer.subtree_rdic.root;