#include #include #include #include #define DEBUG(...) if(ENABLE_DEBUG) {printf(__VA_ARGS__);} #define ARRAYLENGTH(X) (sizeof(X)/sizeof((X)[0])) bool my_bool = true; int border_thickness = 0; float roundedness = 60; #include "stb_truetype.h" void print_bakedchar(char c, stbtt_bakedchar *b) { printf("%c: %d %d %d %d %.2g %.2g %.2g\n", c, b->x0, b->y0, b->x1, b->y1, b->xoff, b->yoff, b->xadvance); } stbtt_bakedchar g_baked_font[96]; unsigned char *g_font_bitmap; int g_font_bitmap_width = 512; int g_font_bitmap_height = 512; // --- #define arena_allocate_TYPE(arena, TYPE) ((TYPE*)memory_arena_allocate(arena, sizeof(TYPE), _Alignof(TYPE))) #define arena_allocate_draw_command(arena) arena_allocate_TYPE(arena, GUI_Draw_Command) #include "memory_arena.c" #include "memory_arena_linux.c" // --- #define RDIC_IMPLEMENTATION #include "../rdic.h" typedef struct { // Top-left corner. int x0, y0; // Bottom-Right corner. int x1, y1; } GUI_Rectangle; // NOTE(Zelaven): I would have loved to do pascal strings here, but I could not // find any way to pass those as literals in a reasonable way. // I also use int instead of ssize_t or anything else because of the .*s format // specifier being an absolute pain. typedef struct { int length; char *cstring; } GUI_String; #define GUI_STRING(literal) (GUI_String){.length = sizeof(literal)-1, .cstring = literal} typedef uint32_t GUI_Color; typedef struct { GUI_Color *pixels; int width; int height; } Pixel_Buffer; // TODO(Zelaven): Add a generation to this that you can // increment so that the nodes that use a style can know // when up dirty themselves due to style change? typedef struct { // TODO(Zelaven): Replace these with GUI_Color. unsigned char red; unsigned char green; unsigned char blue; GUI_Color border_color; int border_thickness; unsigned char roundedness; } GUI_Style; static inline int point_in_rect(int x, int y, GUI_Rectangle rect) { return (x >= rect.x0) && (y >= rect.y0) && (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 { GUI_SIZERULE_PIXELS, GUI_SIZERULE_TEXTCONTENT, GUI_SIZERULE_PERCENTOFPARENT, GUI_SIZERULE_SUMOFCHILDREN, //GUI_SIZERULE_SHARES, } GUI_Size_Rule; typedef struct GUI_Size { GUI_Size_Rule size_rule; int value; int strictness; } GUI_Size; typedef enum GUI_Axis2 { GUI_AXIS2_X, GUI_AXIS2_Y, GUI_AXIS2_COUNT, } GUI_Axis2; typedef enum { GUI_LAYOUT_NONE, GUI_LAYOUT_HORIZONTAL, GUI_LAYOUT_VERTICAL, } GUI_Layout_Direction; typedef struct GUI_Node { RDIC_Node rdic_node; GUI_Style *style; GUI_String text_string; // Optional. int text_width, text_height; // Optional. Initialized by lazy_init and cached. Pixel_Buffer *image; GUI_String debug_string; // NOTE(Zelaven): Useful for tab-navigation and other non-mouse navigation. GUI_Layout_Direction layout_direction; GUI_Size semantic_size[GUI_AXIS2_COUNT]; // TODO(Zelaven): This needs a better name. // True iff. it (and its children) should generate draw commands. // TODO(Zelaven): Should I make it a flags field, which encodes dirty-reasons? // The "dirty test" would be "dirty != 0", and each bit in the field would be // a reason that other code can use to figure out why a node was dirtied. // Multiple reasons for a decision can all be checked in one go as well. bool dirty; // NOTE(Zelaven): Don't make decisions about visual things based on this. // It will be a frame off, and with dirty-redrawing it will result in the // error persisting on the screen. // All sizing and positioning should be done using semantic sizes and // offsets, and should be handled in offline layouting and positioning. int computed_size[GUI_AXIS2_COUNT]; GUI_Rectangle rect; // NOTE(Zelaven): I have no better name for this. } GUI_Node; #define GUI_NODE_PARENT(node) ((GUI_Node*)(node)->rdic_node.parent) #define GUI_NODE_SIBLING(node) ((GUI_Node*)(node)->rdic_node.sibling) #define GUI_NODE_FIRST_CHILD(node) ((GUI_Node*)(node)->rdic_node.first_child) typedef union GUI_Node_Reference { RDIC_Node_Reference rdic_ref; struct { GUI_Node *node; unsigned int generation; }; } GUI_Node_Reference; #if 0 #define gui_node_references_equal(n, m) rdic_node_references_equal((n).rdic_ref,(m).rdic_ref) #else // NOTE(Zelaven): This was just for debugging with better debug messages. int gui_node_references_equal(GUI_Node_Reference n, GUI_Node_Reference m) { return rdic_node_references_equal(n.rdic_ref, m.rdic_ref); } #endif typedef struct { GUI_Rectangle rectangle; GUI_Color color; GUI_Color border_color; int border_thickness; unsigned char roundedness; GUI_String text; Pixel_Buffer *image; } GUI_Draw_Command; typedef struct GUI_Subtree GUI_Subtree; struct GUI_Subtree { // NOTE(Zelaven): We are maintaining an immediate interface of subtrees too. RDIC_Node rdic_node; RDIC_Context rdic; GUI_Node_Reference clipnode; GUI_Node_Reference relative_to; int flat_offset_x; int flat_offset_y; int offset_relative_node_percentage_x; int offset_relative_node_percentage_y; int offset_own_size_percentage_x; int offset_own_size_percentage_y; }; #define GUI_SUBTREE_PARENT(node) ((GUI_Subtree*)(node)->rdic_node.parent) #define GUI_SUBTREE_SIBLING(node) ((GUI_Subtree*)(node)->rdic_node.sibling) #define GUI_SUBTREE_FIRST_CHILD(node) ((GUI_Subtree*)(node)->rdic_node.first_child) typedef union GUI_Subtree_Reference { RDIC_Node_Reference rdic_ref; struct { GUI_Subtree *node; unsigned int generation; }; } GUI_Subtree_Reference; typedef struct { RDIC_Context subtree_rdic; //GUI_Subtree *first_subtree; //GUI_Subtree *last_subtree; //GUI_Subtree *current_subtree; GUI_Subtree_Reference root_subtree; 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; typedef struct { //RDIC_Context rdic; //GUI_Subtree *first_subtree; //GUI_Subtree *last_subtree; //GUI_Subtree *current_subtree; GUI_Layer background_layer; GUI_Layer top_layer; GUI_Layer *current_layer; GUI_Node_Reference focused_node; GUI_Node_Reference hovered_node; GUI_Node_Reference top_node_under_cursor; int mouse_pressed; int mouse_down; int mouse_x; int mouse_y; // TODO: An allocator for the nodes. RDIC_Freelist *node_freelist; RDIC_Freelist *subtree_freelist; Memory_Arena *draw_command_arena; int num_draw_commands; } GUI_Context; // --- #if 1 void print_node_tree(GUI_Node *node, int level) { for(int i = 0; i < level; i++) { printf("-"); } GUI_String text = node->debug_string; printf(" %.*s", text.length, text.cstring); //printf(" (%p)", node); printf(" (Dirty?: %b)", node->dirty); printf("\n"); GUI_Node *child = GUI_NODE_FIRST_CHILD(node); while(child != NULL) { print_node_tree(child, level+1); child = GUI_NODE_SIBLING(child); } } #endif #if 1 void print_subtree_tree(GUI_Subtree *node, int level, bool print_nodes) { for(int i = 0; i < level; i++) { printf("="); } printf(" (%p)", node); printf("\n"); if(print_nodes) { print_node_tree((GUI_Node*)node->rdic.root, level+1); } GUI_Subtree *child = GUI_SUBTREE_FIRST_CHILD(node); while(child != NULL) { print_subtree_tree(child, level+1, print_nodes); child = GUI_SUBTREE_SIBLING(child); } } #endif // --- // NOTE(Zelaven): This won't be used, but it will stay around as a reference for // the technique. #if 0 #define GUIWITH_INNER2(token) defer ## token #define GUIWITH_INNER(token) GUIWITH_INNER2(token) /* * NOTE(Zelaven): This is a "defer loop" macro, fitted as a "with" construct. * What it is used for is temporarily overwriting a value, keeping the original * value safe in a loop-local variable. * The operation functions by declaring the loop variable of an anonymous struct * type, which contains the temporary variable as well as the actual loop * variable. * The temporary is initialized with the contents of the stored value, which is * then overwritten with the "with" value, and at the end of the loop the * stored value is restored to contain the contents of the temporary. * It is important to note that for this to operate correctly, the temporary * needs to be filled out before the stored value is overwritten, which is * dependent on the ORDER OF THE STRUCT MEMBERS. * This is because the overwriting of the stored value is done in the * initialization of the loop variable part of the struct, and the members are * initialized in member order, not initializer declaration order. * I tested this, but both are arranged in the desired order for both reading * and in the case that it proves to be compiler-dependent. * (Note that it appears to be consistent across optimization flags) */ #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_WITH_REDNESS(context, value) GUIWITH(context->style.red, unsigned char, value) #define GUI_WITH_BLUENESS(context, value) GUIWITH(context->style.blue, unsigned char, value) #define GUI_WITH_GREENNESS(context, value) GUIWITH(context->style.green, unsigned char, value) #define GUI_WITH_STYLE(context, value) GUIWITH(context->style, GUI_Style, value) #define GUI_WITH_BORDER_COLOR(context, value) GUIWITH((context)->style.border_color, GUI_Color, (value)) #define GUI_WITH_BORDER_THICKNESS(context, value) GUIWITH((context)->style.border_thickness, int, (value)) #define GUI_WITH_ROUNDEDNESS(context, value) GUIWITH((context)->style.roundedness, unsigned char, (value)) void fff(GUI_Context *context) { GUI_WITH_REDNESS(context, 222) printf("%d\n", context->style.red); GUI_Style style = (GUI_Style){2,2,2,0,0,0}; GUI_WITH_STYLE(context, style) GUI_WITH_STYLE(context, ((GUI_Style){2,2,2,0,0,0})) printf("%d\n", context->style.red); } #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) // --- // TODO(Zelaven): The mouse coordinates are in the context, so no need to pass // them as parameters. #undef ENABLE_DEBUG #define ENABLE_DEBUG 0 void gui_apply_input( GUI_Context *context, int mouse_x, int mouse_y) { (void)context; (void)mouse_x; (void)mouse_y; // --- Mouse Input --- // NOTE(Zelaven): First hit test against layers. GUI_Layer *hit_layer = &context->background_layer; GUI_Subtree *hit_root_subtree = (GUI_Subtree*)hit_layer->subtree_rdic.root; { GUI_Layer *top_layer = &context->top_layer; GUI_Subtree *root_subtree = (GUI_Subtree*)top_layer->subtree_rdic.root; 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_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; } } } (void)hit_layer; assert(hit_root_subtree != NULL); GUI_Subtree *top_subtree_under_cursor = hit_root_subtree; GUI_Subtree *current_subtree = GUI_SUBTREE_FIRST_CHILD(top_subtree_under_cursor); int mouse_over_subtree = 0; while(current_subtree != NULL) { // TODO(Zelaven): Should actually test against the rect-intersect between // the clipnode and the subtree root. The clipnode is only a maximal bound. mouse_over_subtree = point_in_rect_with_offset( mouse_x, mouse_y, // TODO(Zelaven): Test clipnode valid. current_subtree->clipnode.node->rect, hit_layer->offset_x, hit_layer->offset_y); if(mouse_over_subtree) { top_subtree_under_cursor = current_subtree; current_subtree = GUI_SUBTREE_FIRST_CHILD(current_subtree); } else { current_subtree = GUI_SUBTREE_SIBLING(current_subtree); } } hit_root_subtree = top_subtree_under_cursor; GUI_Node *top_node_under_cursor = (GUI_Node*)hit_root_subtree->rdic.root; GUI_Node *current_node = (GUI_Node*)top_node_under_cursor->rdic_node.first_child; int mouse_over_node = 0; while(current_node != NULL) { mouse_over_node = point_in_rect_with_offset( mouse_x, mouse_y, current_node->rect, hit_layer->offset_x, hit_layer->offset_y); if(mouse_over_node) { top_node_under_cursor = current_node; current_node = (GUI_Node*)current_node->rdic_node.first_child; } else { current_node = (GUI_Node*)current_node->rdic_node.sibling; } } context->top_node_under_cursor.node = top_node_under_cursor; context->top_node_under_cursor.generation = top_node_under_cursor->rdic_node.generation; DEBUG("Top node under cursor: %.*s\n", top_node_under_cursor->debug_string.length, top_node_under_cursor->debug_string.cstring); return; } #if 0 { // --- Mouse Input --- // NOTE(Zelaven): First hit test against layers. GUI_Layer *hit_layer = &context->background_layer; { GUI_Layer *top_layer = &context->top_layer; if(top_layer->first_subtree != NULL) { GUI_Node *top_layer_root = (GUI_Node*)top_layer->first_subtree->rdic.root; if(point_in_rect(mouse_x, mouse_y, top_layer_root->rect)) { hit_layer = &context->top_layer; } } } //GUI_Node *top_node_under_cursor = (GUI_Node*)context->rdic.root; //GUI_Node *top_node_under_cursor = (GUI_Node*)context->first_subtree->rdic.root; GUI_Node *top_node_under_cursor = (GUI_Node*)hit_layer->first_subtree->rdic.root; GUI_Node *current_node = (GUI_Node*)top_node_under_cursor->rdic_node.first_child; int mouse_over_node = 0; while(current_node != NULL) { mouse_over_node = point_in_rect(mouse_x, mouse_y, current_node->rect); if(mouse_over_node) { top_node_under_cursor = current_node; current_node = (GUI_Node*)current_node->rdic_node.first_child; } else { current_node = (GUI_Node*)current_node->rdic_node.sibling; } } context->top_node_under_cursor.node = top_node_under_cursor; context->top_node_under_cursor.generation = top_node_under_cursor->rdic_node.generation; DEBUG("Top node under cursor: %.*s\n", top_node_under_cursor->debug_string.length, top_node_under_cursor->debug_string.cstring); } #endif GUI_Node_Reference gui_get_node( GUI_Context *context, GUI_Node_Reference last_reference, GUI_Layout_Direction direction, GUI_Style *style, GUI_Size size[static 2]) { GUI_Layer *current_layer = context->current_layer; if(current_layer == NULL) { return (GUI_Node_Reference){0}; } GUI_Subtree *current_subtree = //(GUI_Subtree*)current_layer->subtree_rdic.frame_current_node; // TODO(Zelaven): This has to do with me always pushing the newest subtree // as a parent, which honestly doesn't sound ideal to me. I should look // into what what my options are. (GUI_Subtree*)current_layer->subtree_rdic.current_parent; int get_node_flags = 0; GUI_Node_Reference new_reference = { .rdic_ref = rdic_get_node( //&context->current_subtree->rdic, last_reference.rdic, &get_node_flags), ¤t_subtree->rdic, last_reference.rdic_ref, &get_node_flags), }; GUI_Node *node = new_reference.node; node->layout_direction = direction; node->style = style; node->semantic_size[GUI_AXIS2_X] = size[GUI_AXIS2_X]; node->semantic_size[GUI_AXIS2_Y] = size[GUI_AXIS2_Y]; // TODO(Zelaven): // get_node_flags & RDIC_GET_NODE__OUT_OF_FREELIST // ^ This should result in an attempt to allocate more nodes for the freelist. // Then rdic_get_node should be retried. if(get_node_flags & RDIC_GET_NODE__NODES_COLLECTED) { GUI_NODE_PARENT(node)->dirty = true; } if(get_node_flags & RDIC_GET_NODE__NODE_ALLOCATED) { GUI_NODE_PARENT(node)->dirty = true; } node->dirty = (get_node_flags & RDIC_GET_NODE__NODE_ALLOCATED) != 0; return new_reference; } GUI_Subtree_Reference gui_get_subtree( GUI_Context *context, GUI_Subtree_Reference last_reference) { GUI_Layer *current_layer = context->current_layer; if(current_layer == NULL) { return (GUI_Subtree_Reference){0}; } RDIC_Context *subtree_rdic = ¤t_layer->subtree_rdic; RDIC_Node *old_subtree_freelist_head = subtree_rdic->freelist->head; int get_node_flags = 0; GUI_Subtree_Reference new_reference = { .rdic_ref = rdic_get_node( //&context->current_subtree->rdic, last_reference.rdic, &get_node_flags), subtree_rdic, last_reference.rdic_ref, &get_node_flags), }; GUI_Subtree *subtree = new_reference.node; subtree->rdic = (RDIC_Context){0}; subtree->rdic.freelist = context->node_freelist; subtree->relative_to = (GUI_Node_Reference){0}; subtree->flat_offset_x = 0; subtree->flat_offset_y = 0; subtree->offset_relative_node_percentage_x = 0; subtree->offset_relative_node_percentage_y = 0; subtree->offset_own_size_percentage_x = 0; subtree->offset_own_size_percentage_y = 0; // TODO(Zelaven): // get_node_flags & RDIC_GET_NODE__OUT_OF_FREELIST // ^ This should result in an attempt to allocate more nodes for the freelist. // Then rdic_get_node should be retried. if(get_node_flags & RDIC_GET_NODE__NODES_COLLECTED) { // NOTE(Zelaven): Some subtrees were collected, which means that their RDIC // node trees should be collected too. for( RDIC_Node *i = subtree_rdic->freelist->head; i != old_subtree_freelist_head; i = i->sibling) { GUI_Subtree *subtree = (GUI_Subtree*)i; // NOTE(Zelaven): This line falsely triggers -Wstringop-overflow and the // copy into a temporary variable seemingly fixes it. // For this reason I simply opted to use -Wno-stringop-overflow, as I // don't want to have a broken flag enabled. rdic_cull_subtrees(&subtree->rdic, subtree->rdic.root); //RDIC_Context *rdic_context = &subtree->rdic; //rdic_cull_subtrees(rdic_context, subtree->rdic.root); } } #if 0 if(get_node_flags & RDIC_GET_NODE__NODES_COLLECTED) { GUI_NODE_PARENT(node)->dirty = true; } if(get_node_flags & RDIC_GET_NODE__NODE_ALLOCATED) { GUI_NODE_PARENT(node)->dirty = true; } node->dirty = (get_node_flags & RDIC_GET_NODE__NODE_ALLOCATED) != 0; #endif return new_reference; } 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) { 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( &layer->subtree_rdic, root_subtree.rdic_ref); if(root_subtree.node == NULL) { return (GUI_Node_Reference){0}; } layer->root_subtree = root_subtree; root_subtree.node->rdic.freelist = context->node_freelist; //static GUI_Subtree background_subtree = {0}; //background_subtree.rdic.freelist = context->node_freelist; //context->background_layer.first_subtree = &background_subtree; //context->background_layer.last_subtree = &background_subtree; //context->current_subtree = &background_subtree; GUI_Node_Reference new_root_reference = { .rdic_ref = rdic_context_start_frame( //&background_subtree.rdic, last_root_reference.rdic) &root_subtree.node->rdic, last_root_reference.rdic_ref) }; if(new_root_reference.node == NULL) { return (GUI_Node_Reference){0}; } //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)) { root->dirty = true; } if(layout_direction != root->layout_direction) { root->dirty = true; } root->debug_string = GUI_STRING("root"); root->layout_direction = layout_direction; root->semantic_size[GUI_AXIS2_X] = (GUI_Size) {.size_rule = GUI_SIZERULE_PIXELS, .value = width, .strictness = 100,}; root->semantic_size[GUI_AXIS2_Y] = (GUI_Size) {.size_rule = GUI_SIZERULE_PIXELS, .value = height, .strictness = 100,}; root->style = style; 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) { //rdic_context_finish_frame(&context->background_layer.first_subtree->rdic); rdic_context_finish_frame(&context->background_layer.root_subtree.node->rdic); //context->node_freelist = // context->background_layer.first_subtree->rdic.node_freelist; } GUI_Node_Reference gui_push_subtree( GUI_Context *context, GUI_Subtree_Reference subtree, GUI_Node_Reference last_root_reference, GUI_Layout_Direction layout_direction, GUI_Style *style, GUI_Size size[static 2], GUI_Node_Reference relative_to) { RDIC_Context *layer_rdic_context = &context->current_layer->subtree_rdic; rdic_push_parent(layer_rdic_context, subtree.rdic_ref); //context->node_freelist = context->current_subtree->rdic.node_freelist; //subtree->rdic.node_freelist = context->node_freelist; // NOTE(Zelaven): Well, not being able to do this is a problem... //context->last_subtree->next = subtree; //subtree->prev = context->last_subtree; //context->last_subtree = subtree; //subtree->structural_parent = context->current_subtree; //context->current_subtree = subtree; subtree.node->relative_to = relative_to; subtree.node->clipnode = relative_to; GUI_Node_Reference new_root_reference = { .rdic_ref = rdic_context_start_frame( &subtree.node->rdic, last_root_reference.rdic_ref) }; GUI_Node *root = (GUI_Node*)new_root_reference.node; root->dirty = false; if(!gui_node_references_equal(last_root_reference, new_root_reference)) { root->dirty = true; } if(layout_direction != root->layout_direction) { root->dirty = true; } root->debug_string = GUI_STRING("gui_push_subtree"); root->layout_direction = layout_direction; root->semantic_size[GUI_AXIS2_X] = size[GUI_AXIS2_X]; root->semantic_size[GUI_AXIS2_Y] = size[GUI_AXIS2_Y]; root->style = style; return new_root_reference; } void gui_pop_subtree( GUI_Context *context) { //context->node_freelist = context->current_subtree->rdic.node_freelist; //context->current_subtree = context->current_subtree->structural_parent; //context->current_subtree->rdic.node_freelist = context->node_freelist; RDIC_Context *layer_rdic_context = &context->current_layer->subtree_rdic; rdic_pop_parent(layer_rdic_context); } void gui_push_parent( GUI_Context *context, GUI_Node_Reference parent) { GUI_Layer *current_layer = context->current_layer; GUI_Subtree *current_subtree = //(GUI_Subtree*)current_layer->subtree_rdic.frame_current_node; (GUI_Subtree*)current_layer->subtree_rdic.current_parent; rdic_push_parent(¤t_subtree->rdic, parent.rdic_ref); } void gui_pop_parent( GUI_Context *context) { GUI_Layer *current_layer = context->current_layer; GUI_Subtree *current_subtree = //(GUI_Subtree*)current_layer->subtree_rdic.frame_current_node; (GUI_Subtree*)current_layer->subtree_rdic.current_parent; rdic_pop_parent(¤t_subtree->rdic); } // --- void gui_layout_calculate_standalone_sizes( GUI_Node *node, GUI_Axis2 axis) { switch(node->semantic_size[axis].size_rule) { case GUI_SIZERULE_PIXELS: { node->computed_size[axis] = node->semantic_size[axis].value; } break; case GUI_SIZERULE_TEXTCONTENT: { // TODO(Zelaven): Calculate text width. node->computed_size[axis] = 0; } break; default: { //assert(!"Non-standalone size passed to calculate_standalone_sizes."); } break; } if(node->rdic_node.first_child != NULL) { gui_layout_calculate_standalone_sizes(GUI_NODE_FIRST_CHILD(node), axis); } if(node->rdic_node.sibling != NULL) { gui_layout_calculate_standalone_sizes(GUI_NODE_SIBLING(node), axis); } } void gui_layout_calculate_parent_percent_sizes( GUI_Node *node, GUI_Axis2 axis) { if(node->semantic_size[axis].size_rule == GUI_SIZERULE_PERCENTOFPARENT) { int percentage = node->semantic_size[axis].value; node->computed_size[axis] = (GUI_NODE_PARENT(node)->computed_size[axis] * percentage) / 100; } if(node->rdic_node.first_child != NULL) { gui_layout_calculate_parent_percent_sizes(GUI_NODE_FIRST_CHILD(node), axis); } if(node->rdic_node.sibling != NULL) { gui_layout_calculate_parent_percent_sizes(GUI_NODE_SIBLING(node), axis); } } void gui_layout_calculate_screen_rects( GUI_Node *node, int x, int y, GUI_Layout_Direction layout_direction) { int width = node->computed_size[GUI_AXIS2_X]; int height = node->computed_size[GUI_AXIS2_Y]; node->rect = (GUI_Rectangle){x, y, x+width, y+height}; if(node->rdic_node.first_child != NULL) { gui_layout_calculate_screen_rects( GUI_NODE_FIRST_CHILD(node), x, y, node->layout_direction); } if(node->rdic_node.sibling != NULL) { int sibling_x = x; int sibling_y = y; if(layout_direction == GUI_LAYOUT_HORIZONTAL) { sibling_x += width; } if(layout_direction == GUI_LAYOUT_VERTICAL) { sibling_y += height; } gui_layout_calculate_screen_rects( GUI_NODE_SIBLING(node), sibling_x, sibling_y, layout_direction); } } void gui_layout_nodes(GUI_Subtree *subtree) { #if 1 GUI_Node *root = (GUI_Node*)subtree->rdic.root; assert(root != NULL); for(GUI_Axis2 axis = 0; axis < GUI_AXIS2_COUNT; axis++) { gui_layout_calculate_standalone_sizes(root, axis); gui_layout_calculate_parent_percent_sizes(root, axis); } // TODO(Zelaven): Test reference validity. // TODO(Zelaven): This probably can be done once and passed in. int x_offset = 0; int y_offset = 0; GUI_Node *relative_node = (GUI_Node*)subtree->relative_to.node; if(relative_node != NULL){ GUI_Rectangle *relative_rectangle = &relative_node->rect; x_offset = relative_rectangle->x0; y_offset = relative_rectangle->y0; //printf("%.*s: %d, %d\n", relative_node->debug_string.length, relative_node->debug_string.cstring, x_offset, y_offset); int relative_width = relative_rectangle->x1 - relative_rectangle->x0; int relative_height = relative_rectangle->y1 - relative_rectangle->y0; x_offset += (relative_width * subtree->offset_relative_node_percentage_x) / 100; y_offset += (relative_height * subtree->offset_relative_node_percentage_y) / 100; } x_offset += subtree->flat_offset_x; y_offset += subtree->flat_offset_y; x_offset += (root->computed_size[GUI_AXIS2_X] * subtree->offset_own_size_percentage_x) / 100; y_offset += (root->computed_size[GUI_AXIS2_Y] * subtree->offset_own_size_percentage_y) / 100; gui_layout_calculate_screen_rects(root, x_offset, y_offset, GUI_LAYOUT_NONE); #else //GUI_Node *root = (GUI_Node*)context->first_subtree->rdic.root; for(GUI_Axis2 axis = 0; axis < GUI_AXIS2_COUNT; axis++) { gui_layout_calculate_standalone_sizes(root, axis); gui_layout_calculate_parent_percent_sizes(root, axis); } gui_layout_calculate_screen_rects(root, 0, 0, GUI_LAYOUT_NONE); #endif } // --- #undef ENABLE_DEBUG #define ENABLE_DEBUG 0 void gui_generate_draw_commands_inner( //int x_offset, //int y_offset, GUI_Node *node, bool dirty, //TODO(Zelaven): Better name. Memory_Arena *draw_command_arena, int *out_num_draw_commands) { DEBUG("%.*s: dirtiness: %b\n", node->debug_string.length, node->debug_string.cstring, dirty); if(dirty) { node->dirty = true; GUI_Draw_Command *draw_command = arena_allocate_draw_command(draw_command_arena); DEBUG("%d, %d, %d, %x, %d, %u\n", node->style->red, node->style->green, node->style->blue, node->style->border_color, node->style->border_thickness, node->style->roundedness); *draw_command = (GUI_Draw_Command){ .rectangle = node->rect, .color = node->style->red<<16 | node->style->green<<8 | node->style->blue, .border_color = node->style->border_color, .border_thickness = node->style->border_thickness, .roundedness = node->style->roundedness, .text = node->text_string, .image = node->image, }; #if 0 (*draw_command).rectangle.x0 += x_offset; (*draw_command).rectangle.x1 += x_offset; (*draw_command).rectangle.y0 += y_offset; (*draw_command).rectangle.y1 += y_offset; #endif *out_num_draw_commands += 1; } GUI_Node *sibling = GUI_NODE_SIBLING(node); if(sibling != NULL) { GUI_Node *parent = GUI_NODE_PARENT(node); gui_generate_draw_commands_inner( //x_offset, y_offset, sibling, parent->dirty || sibling->dirty, draw_command_arena, out_num_draw_commands); } GUI_Node *first_child = GUI_NODE_FIRST_CHILD(node); if(node->rdic_node.first_child != NULL) { gui_generate_draw_commands_inner( //x_offset, y_offset, first_child, dirty || first_child->dirty, draw_command_arena, out_num_draw_commands); } } void gui_generate_draw_commands( //GUI_Node *root, GUI_Subtree *subtree, Memory_Arena *draw_command_arena, int *out_num_draw_commands) { #if 0 // TODO(Zelaven): Test reference validity. // TODO(Zelaven): This probably can be done once and passed in. int x_offset = 0; int y_offset = 0; GUI_Node *relative_node = (GUI_Node*)subtree->relative_to.node; if(relative_node != NULL){ GUI_Rectangle *relative_rectangle = &relative_node->rect; x_offset = relative_rectangle->x0; y_offset = relative_rectangle->y0; //printf("%.*s: %d, %d\n", relative_node->debug_string.length, relative_node->debug_string.cstring, x_offset, y_offset); int relative_width = relative_rectangle->x1 - relative_rectangle->x0; int relative_height = relative_rectangle->y1 - relative_rectangle->y0; x_offset += (relative_width * subtree->offset_relative_node_percentage_x) / 100; y_offset += (relative_height * subtree->offset_relative_node_percentage_y) / 100; } x_offset += subtree->flat_offset_x; y_offset += subtree->flat_offset_y; GUI_Node *root = (GUI_Node*)subtree->rdic.root; assert(root != NULL); x_offset += (root->computed_size[GUI_AXIS2_X] * subtree->offset_own_size_percentage_x) / 100; y_offset += (root->computed_size[GUI_AXIS2_Y] * subtree->offset_own_size_percentage_y) / 100; #else GUI_Node *root = (GUI_Node*)subtree->rdic.root; assert(root != NULL); #endif assert(draw_command_arena != NULL); assert(out_num_draw_commands != NULL); *out_num_draw_commands = 0; memory_arena_reset(draw_command_arena); bool dirtyness = root->dirty; GUI_Node_Reference clipnode = subtree->clipnode; if(rdic_node_reference_valid(clipnode.rdic_ref)) { dirtyness |= clipnode.node->dirty; } gui_generate_draw_commands_inner( //x_offset, y_offset, root, dirtyness, draw_command_arena, out_num_draw_commands); } // --- GUI_Node_Reference gui_layout( GUI_Context *context, GUI_Node_Reference last_reference, GUI_Layout_Direction direction, GUI_Style *style, GUI_Size size[static 2]) { GUI_Node_Reference new_reference = gui_get_node( context, last_reference, direction, style, size); GUI_Node *node = new_reference.node; node->text_string = (GUI_String){0}; node->debug_string = GUI_STRING("gui_layout"); return new_reference; } // --- GUI_Node_Reference gui_dumb_block( GUI_Context *context, GUI_Node_Reference last_reference, 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->text_string = (GUI_String){0}; node->debug_string = GUI_STRING("gui_dumb_block"); return new_reference; } #undef ENABLE_DEBUG #define ENABLE_DEBUG 0 bool gui_dumb_button( 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 = text; node->text_string = text; bool clicked = false; if(gui_node_references_equal( new_reference, context->top_node_under_cursor)) { DEBUG("%.*s: reference is the top node under cursor.\n", text.length, text.cstring); if(context->mouse_pressed) { DEBUG(" Mouse pressed on node\n"); context->focused_node = new_reference; } else if(!context->mouse_down) { DEBUG(" Mouse released on node\n"); if(gui_node_references_equal(new_reference, context->focused_node)) { DEBUG(" Mouse released over same node as pressed\n"); context->focused_node.node = NULL; clicked = true; } } } else if(gui_node_references_equal(new_reference, context->focused_node) && !context->mouse_down) { context->focused_node.node = NULL; } *last_reference = new_reference; return clicked; } #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 0 bool gui_slider( GUI_Context *context, GUI_Node_Reference *last_reference, GUI_Node_Reference *inner_box_reference, float *value, GUI_Style *background_style, GUI_Style *bar_style, GUI_Size size[static 2]) { GUI_Node_Reference new_reference = gui_get_node( context, *last_reference, GUI_LAYOUT_NONE, background_style, size); GUI_Node *node = new_reference.node; node->debug_string = GUI_STRING("gui_slider"); node->text_string = (GUI_String){0}; // NOTE(Zelaven): We need a handle to this node, but we set its values later. gui_push_parent(context, new_reference); GUI_Node_Reference new_inner_reference = gui_get_node( context, *inner_box_reference, GUI_LAYOUT_NONE, bar_style, size); bool value_changed = false; bool new_ref_is_top = gui_node_references_equal( 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", context->top_node_under_cursor.node, last_reference->node, inner_box_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__); if(context->mouse_pressed) { DEBUG(" Mouse pressed on node\n"); context->focused_node = new_reference; } else if(!context->mouse_down) { DEBUG(" Mouse released on node\n"); if(gui_node_references_equal(new_reference, context->focused_node)) { DEBUG(" Mouse released over same node as pressed\n"); context->focused_node.node = NULL; } } } else if(gui_node_references_equal(new_reference, context->focused_node) && !context->mouse_down) { context->focused_node.node = NULL; } if(gui_node_references_equal(new_reference, context->focused_node)) { int last_frame_width = node->computed_size[GUI_AXIS2_X]; if(last_frame_width == 0) { DEBUG( " last_frame_width is 0." " This shouldn't really happen as the user shouldn't be able to focus" " a node that hasn't been displayed on the screen yet." " Not going to change the slider value."); } else { int offset_x = context->mouse_x - node->rect.x0; float before_value = *value; float new_value = ((float)offset_x) / ((float)last_frame_width); if(new_value < 0.0f) { new_value = 0.0f; } else if(new_value >= 1.0f) { new_value = 1.0f; } *value = new_value; DEBUG(" Value before: %f - Value after: %f\n", before_value, *value); value_changed = (before_value != new_value); } } // NOTE(Zelaven): Modified by input, so handle input first. GUI_Node *inner = new_inner_reference.node; inner->debug_string = GUI_STRING("gui_slider - inner node"); inner->text_string = (GUI_String){0}; assert(*value <= 1.0f); //inner->semantic_size[GUI_AXIS2_X].value = last_frame_width*(*value); inner->semantic_size[GUI_AXIS2_Y] = (GUI_Size){GUI_SIZERULE_PERCENTOFPARENT, 100, 100}; inner->semantic_size[GUI_AXIS2_X].size_rule = GUI_SIZERULE_PERCENTOFPARENT; inner->semantic_size[GUI_AXIS2_X].value = 100 * (*value); gui_pop_parent(context); *last_reference = new_reference; *inner_box_reference = new_inner_reference; if(value_changed) {node->dirty = true;} //node->dirty = true; return value_changed; } #undef ENABLE_DEBUG #define ENABLE_DEBUG 0 bool gui_vertical_slider( GUI_Context *context, GUI_Node_Reference *last_reference, GUI_Node_Reference *inner_box_reference, float *value, GUI_Style *background_style, GUI_Style *bar_style, GUI_Size size[static 2]) { GUI_Node_Reference new_reference = gui_get_node( context, *last_reference, GUI_LAYOUT_NONE, background_style, size); GUI_Node *node = new_reference.node; node->debug_string = GUI_STRING("gui_slider"); node->text_string = (GUI_String){0}; // NOTE(Zelaven): We need a handle to this node, but we set its values later. gui_push_parent(context, new_reference); GUI_Node_Reference new_inner_reference = gui_get_node( context, *inner_box_reference, GUI_LAYOUT_NONE, bar_style, size); bool value_changed = false; bool new_ref_is_top = gui_node_references_equal( 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", context->top_node_under_cursor.node, last_reference->node, inner_box_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__); if(context->mouse_pressed) { DEBUG(" Mouse pressed on node\n"); context->focused_node = new_reference; } else if(!context->mouse_down) { DEBUG(" Mouse released on node\n"); if(gui_node_references_equal(new_reference, context->focused_node)) { DEBUG(" Mouse released over same node as pressed\n"); context->focused_node.node = NULL; } } } else if(gui_node_references_equal(new_reference, context->focused_node) && !context->mouse_down) { context->focused_node.node = NULL; } if(gui_node_references_equal(new_reference, context->focused_node)) { int last_frame_height = node->computed_size[GUI_AXIS2_Y]; if(last_frame_height == 0) { DEBUG( " last_frame_height is 0." " This shouldn't really happen as the user shouldn't be able to focus" " a node that hasn't been displayed on the screen yet." " Not going to change the slider value."); } else { int offset_y = context->mouse_y - node->rect.y0; float before_value = *value; float new_value = ((float)offset_y) / ((float)last_frame_height); if(new_value < 0.0f) { new_value = 0.0f; } else if(new_value >= 1.0f) { new_value = 1.0f; } *value = new_value; DEBUG(" Value before: %f - Value after: %f\n", before_value, *value); value_changed = (before_value != new_value); } } // NOTE(Zelaven): Modified by input, so handle input first. GUI_Node *inner = new_inner_reference.node; inner->debug_string = GUI_STRING("gui_slider - inner node"); inner->text_string = (GUI_String){0}; assert(*value <= 1.0f); inner->semantic_size[GUI_AXIS2_X] = (GUI_Size){GUI_SIZERULE_PERCENTOFPARENT, 100, 100}; inner->semantic_size[GUI_AXIS2_Y].size_rule = GUI_SIZERULE_PERCENTOFPARENT; inner->semantic_size[GUI_AXIS2_Y].value = 100 * (*value); gui_pop_parent(context); *last_reference = new_reference; *inner_box_reference = new_inner_reference; if(value_changed) {node->dirty = true;} return value_changed; } // --- void test_gui( GUI_Context *context, GUI_Rectangle full_gui_rectangle) { static float border_thickness = 0; float last_border_thickness = border_thickness; static GUI_Style root_style = {42, 24, 88, 0,0,0}; static GUI_Style dumb_button_style = {88, 24, 42, 0,0,0}; static GUI_Size dumb_button_size[2] = { {GUI_SIZERULE_PIXELS, 42, 100}, {GUI_SIZERULE_PIXELS, 24, 100}}; static GUI_Style other_dumb_button_style = {24, 42, 88, 0x0000ffff,0,0}; static GUI_Node_Reference root = {0}; root = gui_context_start_frame( context, root, full_gui_rectangle.x1-full_gui_rectangle.x0, full_gui_rectangle.y1-full_gui_rectangle.y0, GUI_LAYOUT_HORIZONTAL, &root_style); static GUI_Node_Reference dumb_button = {0}; if(my_bool) if(gui_dumb_button( context, &dumb_button, GUI_STRING("My Dumb Button"), &dumb_button_style, dumb_button_size)) { printf( "**************\n" "*** BUTTON ***\n" "**************\n"); border_thickness += 1; if(border_thickness > 21) border_thickness = 21; printf("border_thickness: %f\n", border_thickness); my_bool = false; ((GUI_Node*)root.node)->dirty = true; } static GUI_Node_Reference dumb_button2 = {0}; if(gui_dumb_button( context, &dumb_button2, GUI_STRING("My Other Dumb Button"), &other_dumb_button_style, dumb_button_size)) { printf( "**************\n" "*** OTHER ***\n" "**************\n" ); border_thickness -= 1; if(border_thickness < 0) border_thickness = 0; printf("border_thickness: %f\n", border_thickness); my_bool = true; } if(my_bool) { static GUI_Style layout_style = {100, 0, 100,0,0,0}; static GUI_Size layout_size[2] = { {GUI_SIZERULE_PIXELS, 400, 100}, {GUI_SIZERULE_PIXELS, 400, 100}}; static GUI_Node_Reference layout = {0}; layout = gui_layout( context, layout, GUI_LAYOUT_VERTICAL, &layout_style, layout_size); gui_push_parent(context, layout); // static variables can be used for isolated styles that: // 1) are only used within the current scope. // 2) don't need to be changed at runtime. static GUI_Style inner_block_2_style = {200, 253, 235, 0,0,0}; static GUI_Style inner_block_3_style = {235, 253, 200, 0,0,0}; static GUI_Style inner_block_4_style = {0 , 0, 0, 0,0,0}; static GUI_Node_Reference dumb_block2 = {0}; dumb_block2 = gui_dumb_block( context, dumb_block2, &inner_block_2_style, dumb_button_size); static GUI_Node_Reference dumb_block3 = {0}; dumb_block3 = gui_dumb_block( context, dumb_block3, &inner_block_3_style, dumb_button_size); static GUI_Node_Reference dumb_block4 = {0}; dumb_block4 = gui_dumb_block( context, dumb_block4, &inner_block_4_style, dumb_button_size); static GUI_Node_Reference dumb_button3 = {0}; if(gui_dumb_button( context, &dumb_button3, GUI_STRING("Inner Dumb Button"), &dumb_button_style, dumb_button_size)) { printf( "**************\n" "*** INNER ***\n" "**************\n" ); } static GUI_Size dumb_block5_size[2] = { {GUI_SIZERULE_PERCENTOFPARENT, 42, 100}, {GUI_SIZERULE_PERCENTOFPARENT, 24, 100}}; static GUI_Node_Reference dumb_block5 = {0}; dumb_block5 = gui_dumb_block( context, dumb_block5, &inner_block_4_style, dumb_block5_size); gui_pop_parent(context); } static GUI_Style slider_background = {0, 0, 0, 0,0,0}; static GUI_Style slider_bar = {255, 0, 0, 0,0,0}; static GUI_Size slider_size[2] = { {GUI_SIZERULE_PIXELS, 100, 100}, {GUI_SIZERULE_PIXELS, 40, 100}}; static GUI_Node_Reference slider = {0}; static GUI_Node_Reference slider_inner = {0}; static float slider_value = 0.25f; //slider_bar.blue = 255 * slider_value; //slider_value += 0.01f; //if(slider_value >= 1.0f) slider_value = 0.5f; // NOTE(Zelaven): This line is not good because it causes jankyness when using // the slider, and the slider behaves properly without it. // NOTE(Zelaven): This line is actually necessary if the value is writable by // anything other than the slider. If border_thickness is a float variable, // then there is no issue, though. slider_value = ((float)border_thickness) / 21.; bool slider_value_changed = gui_slider( context, &slider, &slider_inner, &slider_value, &slider_background, &slider_bar, slider_size); border_thickness = 21.*slider_value; //printf("Slider value changed?: %b\n", slider_value_changed); ((GUI_Node*)root.node)->dirty |= slider_value_changed; static GUI_Style block_after_layout_style = {99, 99, 99, 0,0,0}; static GUI_Size block_after_layout_size[2] = { {GUI_SIZERULE_PIXELS, 50, 100}, {GUI_SIZERULE_PIXELS, 50, 100}}; static GUI_Node_Reference dumb_block_after_layout = {0}; dumb_block_after_layout = gui_dumb_block( context, dumb_block_after_layout, &block_after_layout_style, block_after_layout_size); static GUI_Style my_style = {100, 100, 0, 0, 3, 0}; static GUI_Size dumb_block_size[2] = { {GUI_SIZERULE_PIXELS, 50, 100}, {GUI_SIZERULE_PIXELS, 100, 100}, }; my_style.border_thickness = border_thickness; static GUI_Node_Reference new_style_gui_dumb_block = {0}; new_style_gui_dumb_block = gui_dumb_block( context, new_style_gui_dumb_block, &my_style, dumb_block_size); other_dumb_button_style.border_thickness = border_thickness; if(border_thickness != last_border_thickness) { ((GUI_Node*)root.node)->dirty = true; } gui_context_finish_frame(context); } // Text field at the top, 4x4 button grid below. // See the nuklear example calculator? void test_gui__calculator( GUI_Context *context, GUI_Rectangle full_gui_rectangle) { (void)full_gui_rectangle; static GUI_Style root_style = {0x44,0x44,0x44, 0x00999999,2,0}; static GUI_Style button_style = {0x33,0x33,0x33, 0x00999999,2,25}; static GUI_Size button_size[2] = { {GUI_SIZERULE_PERCENTOFPARENT, 25, 100}, {GUI_SIZERULE_PERCENTOFPARENT, 100, 100}}; static GUI_Style row_style = {0x44,0x44,0x44, 0,0,0}; static GUI_Size row_size[2] = { {GUI_SIZERULE_PERCENTOFPARENT, 100, 100}, {GUI_SIZERULE_PERCENTOFPARENT, 25, 100}}; static GUI_Node_Reference root = {0}; root = gui_context_start_frame( context, root, //full_gui_rectangle.x1-full_gui_rectangle.x0, //full_gui_rectangle.y1-full_gui_rectangle.y0, 300, 400, GUI_LAYOUT_VERTICAL, &root_style); #if 1 static GUI_Node_Reference rows[4] = {0}; static GUI_Node_Reference buttons[4][4] = {0}; GUI_String button_labels[4][4] = { {GUI_STRING("1"), GUI_STRING("2"), GUI_STRING("3"), GUI_STRING("+")}, {GUI_STRING("4"), GUI_STRING("5"), GUI_STRING("6"), GUI_STRING("-")}, {GUI_STRING("7"), GUI_STRING("8"), GUI_STRING("9"), GUI_STRING("*")}, {GUI_STRING("C"), GUI_STRING("0"), GUI_STRING("="), GUI_STRING("/")}, }; for(size_t row = 0; row < ARRAYLENGTH(rows); row++) { rows[row] = gui_layout( context, rows[row], GUI_LAYOUT_HORIZONTAL, &row_style, row_size); gui_push_parent(context, rows[row]); for(size_t i = 0; i < ARRAYLENGTH(buttons[0]); i++) { if(gui_dumb_button( context, &buttons[row][i], button_labels[row][i], &button_style, button_size)) { GUI_String button_label = button_labels[row][i]; printf("%.*s\n", button_label.length, button_label.cstring); } } gui_pop_parent(context); } #else static GUI_Node_Reference row1 = {0}; row1 = gui_layout2( context, row1, GUI_LAYOUT_HORIZONTAL, &row_style, row_size); gui_push_parent(context, row1); static GUI_Node_Reference row1_buttons[4] = {0}; char *row1_button_labels[4] = {"1", "2", "3", "+"}; for(size_t i = 0; i < ARRAYLENGTH(row1_buttons); i++) { if(gui_dumb_button( context, &row1_buttons[i], row1_button_labels[i], &button_style, button_size)) { printf("%s\n", row1_button_labels[i]); } } gui_pop_parent(context); #endif gui_context_finish_frame(context); } void test_gui__scrollbars( GUI_Context *context, GUI_Rectangle full_gui_rectangle) { (void)full_gui_rectangle; static GUI_Style root_style = {0x44,0x44,0x44, 0x00999999,2,0}; static GUI_Style element_style = {0,0x33,0x33, 0x00009999,2,0}; static GUI_Size element_size[2] = { {GUI_SIZERULE_PERCENTOFPARENT, 100, 100}, {GUI_SIZERULE_PIXELS, 100, 100}}; static GUI_Style outer_style = {0x33,0x33,0x33, 0x00999999,2,0}; static GUI_Size top_bottom_size[2] = { {GUI_SIZERULE_PERCENTOFPARENT, 100, 100}, {GUI_SIZERULE_PERCENTOFPARENT, 25, 100}}; static GUI_Size middle_size[2] = { {GUI_SIZERULE_PERCENTOFPARENT, 100, 100}, {GUI_SIZERULE_PERCENTOFPARENT, 50, 100}}; static GUI_Size side_size[2] = { {GUI_SIZERULE_PERCENTOFPARENT, 25, 100}, {GUI_SIZERULE_PERCENTOFPARENT, 100, 100}}; static GUI_Size center_size[2] = { {GUI_SIZERULE_PERCENTOFPARENT, 50, 100}, {GUI_SIZERULE_PERCENTOFPARENT, 100, 100}}; static GUI_Node_Reference root = {0}; root = gui_context_start_frame( context, root, full_gui_rectangle.x1-full_gui_rectangle.x0, full_gui_rectangle.y1-full_gui_rectangle.y0, //300, 400, GUI_LAYOUT_VERTICAL, &root_style); static GUI_Node_Reference top = {0}; static GUI_Node_Reference middle = {0}; static GUI_Node_Reference bottom = {0}; top = gui_dumb_block(context, top, &outer_style, top_bottom_size); middle = gui_layout( context, middle, GUI_LAYOUT_HORIZONTAL, &outer_style, middle_size); gui_push_parent(context, middle); { static GUI_Node_Reference left = {0}; static GUI_Node_Reference center = {0}; static GUI_Node_Reference right = {0}; left = gui_dumb_block(context, left, &outer_style, side_size); center = gui_dumb_block(context, center, &outer_style, center_size); gui_push_parent(context, center); { static GUI_Node_Reference scrollbox_layout = {0}; static GUI_Size full_size[2] = { {GUI_SIZERULE_PERCENTOFPARENT, 100, 100}, {GUI_SIZERULE_PERCENTOFPARENT, 100, 100}}; scrollbox_layout = gui_layout( context, scrollbox_layout, GUI_LAYOUT_HORIZONTAL, &outer_style, full_size); gui_push_parent(context, scrollbox_layout); { static GUI_Node_Reference scrollregion_layout = {0}; static GUI_Size scrollregion_size[2] = { {GUI_SIZERULE_PERCENTOFPARENT, 90, 100}, {GUI_SIZERULE_PERCENTOFPARENT, 100, 100}}; static GUI_Size scrollbar_size[2] = { {GUI_SIZERULE_PERCENTOFPARENT, 10, 100}, {GUI_SIZERULE_PERCENTOFPARENT, 100, 100}}; #if 0 scrollregion_layout = gui_layout( context, scrollregion_layout, GUI_LAYOUT_VERTICAL, &outer_style, scrollregion_size); gui_push_parent(context, scrollregion_layout); { static GUI_Node_Reference elements[6] = {0}; for(size_t i = 0; i < ARRAYLENGTH(elements); i++) { elements[i] = gui_dumb_block( context, elements[i], &element_style, element_size); } } gui_pop_parent(context); #else scrollregion_layout = gui_dumb_block( context, scrollregion_layout, &outer_style, scrollregion_size); static GUI_Subtree_Reference scrollregion_subtree = {0}; scrollregion_subtree = gui_get_subtree(context, scrollregion_subtree); static GUI_Node_Reference scrollregion_root = {0}; static GUI_Size scrollregion_inner_size[2] = { {GUI_SIZERULE_PERCENTOFPARENT, 100, 100}, //{GUI_SIZERULE_PIXELS, 100, 100}, {GUI_SIZERULE_PIXELS, 880, 100}}; scrollregion_root = gui_push_subtree( context, scrollregion_subtree, scrollregion_root, GUI_LAYOUT_VERTICAL, &outer_style, scrollregion_inner_size, scrollbox_layout); // TODO(Zelaven): Instead of this hack, add to the layout code that it // can set the size of the root relative to the size of its relative // node. scrollregion_root.rdic_ref.node->parent = scrollregion_layout.rdic_ref.node; { static GUI_Node_Reference elements[6] = {0}; for(size_t i = 0; i < ARRAYLENGTH(elements); i++) { elements[i] = gui_dumb_block( context, elements[i], &element_style, element_size); } } gui_pop_subtree(context); #endif #if 0 static GUI_Node_Reference scrollbar_layout = {0}; scrollbar_layout = gui_layout( context, scrollbar_layout, GUI_LAYOUT_VERTICAL, &outer_style, scrollbar_size); float scroll_position = 0.75f; int spacer_total = 90; int spacer_upper = scroll_position * spacer_total; int spacer_lower = spacer_total - spacer_upper; gui_push_parent(context, scrollbar_layout); { static GUI_Size bar_knob_size[2] = { {GUI_SIZERULE_PERCENTOFPARENT, 100, 100}, {GUI_SIZERULE_PERCENTOFPARENT, 10, 100}}; // NOTE(Zelaven): These sizes are unique to each scrollbar. static GUI_Size bar_upper_spacer_size[2] = { {GUI_SIZERULE_PERCENTOFPARENT, 100, 100}, {GUI_SIZERULE_PERCENTOFPARENT, 0, 100}}; static GUI_Size bar_lower_spacer_size[2] = { {GUI_SIZERULE_PERCENTOFPARENT, 100, 100}, {GUI_SIZERULE_PERCENTOFPARENT, 90, 100}}; bar_upper_spacer_size[GUI_AXIS2_Y].value = spacer_upper; bar_lower_spacer_size[GUI_AXIS2_Y].value = spacer_lower; static GUI_Node_Reference scrollbar_upper_spacer = {0}; static GUI_Node_Reference scrollbar_knob = {0}; static GUI_Node_Reference scrollbar_lower_spacer = {0}; scrollbar_upper_spacer = gui_dumb_block( context, scrollbar_upper_spacer, &outer_style, bar_upper_spacer_size); scrollbar_knob = gui_dumb_block( context, scrollbar_knob, &outer_style, bar_knob_size); scrollbar_lower_spacer = gui_dumb_block( context, scrollbar_lower_spacer, &outer_style, bar_lower_spacer_size); } gui_pop_parent(context); // scrollbar_layout. #else static float slider_value = 0.0f; static GUI_Style slider_background = {0, 0, 0, 0,0,0}; static GUI_Style slider_bar = {255, 0, 0, 0,0,0}; static GUI_Node_Reference slider = {0}; static GUI_Node_Reference slider_inner = {0}; gui_vertical_slider(context, &slider, &slider_inner, &slider_value, &slider_background, &slider_bar, scrollbar_size); scrollregion_subtree.node->offset_relative_node_percentage_y = slider_value*100; scrollregion_subtree.node->offset_own_size_percentage_y = slider_value*-100; scrollregion_root.node->dirty = true; //((GUI_Node*)slider.node)->dirty; //printf("Slider value: %f\n", slider_value); #endif } gui_pop_parent(context); // scrollbox_layout. } gui_pop_parent(context); // center. right = gui_dumb_block(context, right, &outer_style, side_size); } gui_pop_parent(context); // middle. bottom = gui_dumb_block(context, bottom, &outer_style, top_bottom_size); gui_context_finish_frame(context); } void test_gui__draw_command_using_sliders( GUI_Context *context, GUI_Rectangle full_gui_rectangle) { //static float border_thickness = 0; static GUI_Style root_style = {42, 24, 88, 0,0,0}; static GUI_Node_Reference root = {0}; root = gui_context_start_frame( context, root, full_gui_rectangle.x1-full_gui_rectangle.x0, full_gui_rectangle.y1-full_gui_rectangle.y0, GUI_LAYOUT_VERTICAL, &root_style); static GUI_Size layout_size[2] = { {GUI_SIZERULE_PERCENTOFPARENT, 100, 100}, {GUI_SIZERULE_PERCENTOFPARENT, 50, 100}}; static GUI_Node_Reference layout = {0}; layout = gui_layout( context, layout, GUI_LAYOUT_VERTICAL, &root_style, layout_size); gui_push_parent(context, layout); { static GUI_Style slider_background = {0, 0, 0, 0,0,0}; static GUI_Style slider_bar = {255, 0, 0, 0,0,0}; static GUI_Size slider_size[2] = { {GUI_SIZERULE_PIXELS, 200, 100}, {GUI_SIZERULE_PIXELS, 40, 100}}; static GUI_Node_Reference slider = {0}; static GUI_Node_Reference slider_inner = {0}; static GUI_Style button_style = {200, 0, 0, 0,5,0}; static GUI_Size button_size[2] = { {GUI_SIZERULE_PIXELS, 100, 100}, {GUI_SIZERULE_PIXELS, 40, 100}}; static GUI_Style slider_spacer_style = {99, 99, 99, 0,0,0}; static GUI_Size slider_spacer_size[2] = { {GUI_SIZERULE_PIXELS, 200, 100}, {GUI_SIZERULE_PIXELS, 5, 100}}; static GUI_Style button_spacer_style = {99, 99, 99, 0,0,0}; static GUI_Size button_spacer_size[2] = { {GUI_SIZERULE_PIXELS, 5, 100}, {GUI_SIZERULE_PIXELS, 40, 100}}; static GUI_Size row_layout_size[2] = { {GUI_SIZERULE_PERCENTOFPARENT, 100, 100}, {GUI_SIZERULE_PIXELS, 40, 100}}; static GUI_Node_Reference row1_layout = {0}; row1_layout = gui_layout( context, row1_layout, GUI_LAYOUT_HORIZONTAL, &root_style, row_layout_size); #if 1 gui_push_parent(context, row1_layout); { static float slider_value = 0.25f; // NOTE(Zelaven): This line is not good because it causes jankyness when // using the slider, and the slider behaves properly without it. // NOTE(Zelaven): This line is actually necessary if the value is writable // by anything other than the slider. If border_thickness is a float // variable, then there is no issue, though. slider_value = ((float)border_thickness) / 51.; gui_slider(context, &slider, &slider_inner, &slider_value, &slider_background, &slider_bar, slider_size); border_thickness = 51.*slider_value; static GUI_Node_Reference spacer1 = {0}; spacer1 = gui_dumb_block( context, spacer1, &button_spacer_style, button_spacer_size); static GUI_Node_Reference button_thickness_down = {0}; if(gui_dumb_button(context, &button_thickness_down, GUI_STRING("-"), &button_style, button_size)) { border_thickness -= 1; printf("-\n"); } static GUI_Node_Reference spacer2 = {0}; spacer2 = gui_dumb_block( context, spacer2, &button_spacer_style, button_spacer_size); static GUI_Node_Reference button_thickness_up = {0}; if(gui_dumb_button(context, &button_thickness_up, GUI_STRING("+"), &button_style, button_size)) { border_thickness += 1; printf("+\n"); } } gui_pop_parent(context); #endif static GUI_Node_Reference slider_spacer = {0}; slider_spacer = gui_dumb_block( context, slider_spacer, &slider_spacer_style, slider_spacer_size); static GUI_Node_Reference slider2 = {0}; static GUI_Node_Reference slider2_inner = {0}; static float slider2_value = 0.25f; slider2_value = ((float)roundedness) / 100.; gui_slider(context, &slider2, &slider2_inner, &slider2_value, &slider_background, &slider_bar, slider_size); roundedness = 100.*slider2_value; } gui_pop_parent(context); gui_context_finish_frame(context); } #if 1 void test_gui__subtree( GUI_Context *context, GUI_Rectangle full_gui_rectangle) { static GUI_Style root_style = {0x44,0x33,0x33, 0x00999999,2,0}; static GUI_Style element_style = {0,0x33,0x33, 0x00009999,2,0}; static GUI_Size element_size[2] = { {GUI_SIZERULE_PERCENTOFPARENT, 100, 100}, {GUI_SIZERULE_PIXELS, 100, 100}}; static GUI_Style outer_style = {0x33,0x33,0x33, 0x00999999,2,0}; static GUI_Size top_bottom_size[2] = { {GUI_SIZERULE_PERCENTOFPARENT, 100, 100}, {GUI_SIZERULE_PERCENTOFPARENT, 25, 100}}; static GUI_Size middle_size[2] = { {GUI_SIZERULE_PERCENTOFPARENT, 100, 100}, {GUI_SIZERULE_PERCENTOFPARENT, 50, 100}}; static GUI_Size test_size[2] = { {GUI_SIZERULE_PIXELS, 200, 100}, {GUI_SIZERULE_PIXELS, 600, 100}}; static GUI_Node_Reference root = {0}; root = gui_context_start_frame( context, root, full_gui_rectangle.x1-full_gui_rectangle.x0, 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 = 70; context->top_layer.offset_y = 20; static GUI_Node_Reference top = {0}; static GUI_Node_Reference middle = {0}; static GUI_Node_Reference bottom = {0}; static GUI_Node_Reference slider = {0}; static float slider_value = 0.25f; bool should_dirty = false; bool set_pixels = false; top = gui_layout( context, top, GUI_LAYOUT_HORIZONTAL, &outer_style, top_bottom_size); gui_push_parent(context, top); { static GUI_Style slider_background = {0, 0, 0, 0,0,0}; static GUI_Style slider_bar = {255, 0, 0, 0,0,0}; static GUI_Size slider_size[2] = { {GUI_SIZERULE_PIXELS, 200, 100}, {GUI_SIZERULE_PIXELS, 40, 100}}; //static GUI_Node_Reference slider = {0}; static GUI_Node_Reference slider_inner = {0}; gui_slider(context, &slider, &slider_inner, &slider_value, &slider_background, &slider_bar, slider_size); 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, 170, 100}, {GUI_SIZERULE_PIXELS, 40, 100}}; if(gui_dumb_button( context, &dirty_button, GUI_STRING("Dirty middle"), &button_style, button_size)) { should_dirty = true; printf("Dirtying middle.\n"); } static GUI_Node_Reference dirty_button2 = {0}; if(gui_dumb_button( context, &dirty_button2, GUI_STRING("Set Pixels"), &button_style, button_size)) { 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("Toggle tooltip"), &button_style, button_size)) { tooltip_toggle = !tooltip_toggle; printf("Toggling tooltip.\n"); } 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}; if(gui_dumb_button( context, &top_layer_block, GUI_STRING("Hi!"), &element_style, full_size)) { printf("Top layer says hi!\n"); } } } } gui_pop_parent(context); // middle. middle = gui_dumb_block(context, middle, &outer_style, middle_size); #if 1 static GUI_Subtree_Reference middle_subtree = {0}; middle_subtree = gui_get_subtree(context, middle_subtree); static GUI_Node_Reference middle_subtree_root = {0}; middle_subtree_root = gui_push_subtree( context, middle_subtree, middle_subtree_root, GUI_LAYOUT_VERTICAL, &element_style, test_size, middle); { static GUI_Color test_image_pixels[10*12] = { 0, ~0, 0, ~0, 0, ~0, 0, ~0, 0, ~0, ~0, 0, ~0, 0, ~0, 0, ~0, 0, ~0, 0, 0, ~0, 0, ~0, 0, ~0, 0, ~0, 0, ~0, ~0, 0, ~0, 0, ~0, 0, ~0, 0, ~0, 0, 0, ~0, 0, ~0, 0, ~0, 0, ~0, 0, ~0, ~0, 0, ~0, 0, ~0, 0, ~0, 0, ~0, 0, 0, ~0, 0, ~0, 0, ~0, 0, ~0, 0, ~0, ~0, 0, ~0, 0, ~0, 0, ~0, 0, ~0, 0, 0, ~0, 0, ~0, 0, ~0, 0, ~0, 0, ~0, ~0, 0, ~0, 0, ~0, 0, ~0, 0, ~0, 0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, }; static Pixel_Buffer test_image = { .pixels = test_image_pixels, .width = 10, .height = 12, }; static GUI_Node_Reference inner_top = {0}; inner_top = gui_dumb_block(context, inner_top, &outer_style, element_size); ((GUI_Node*)inner_top.node)->image = &test_image; static GUI_Node_Reference subtree_button = {0}; if(gui_dumb_button( context, &subtree_button, GUI_STRING("In subtree"), &element_style, element_size)) { printf("Hi from subtree!\n"); } } gui_pop_subtree(context); #endif #if 0 int middle_height = ((GUI_Node*)middle.node)->computed_size[GUI_AXIS2_Y]; int subtree_height = ((GUI_Node*)middle_subtree_root.node)->computed_size[GUI_AXIS2_Y]; int overfill = subtree_height - middle_height; printf("\n%d = %d - %d\n", overfill, subtree_height, middle_height); if(overfill >= 0) { printf("?\n"); middle_subtree.flat_offset_y = -(slider_value * overfill); printf("%d\n", middle_subtree.flat_offset_x); } else { middle_subtree.flat_offset_y = 0; } #else //printf("slider value: %f\n", slider_value); middle_subtree.node->offset_relative_node_percentage_y = slider_value*100; middle_subtree.node->offset_own_size_percentage_y = slider_value*-100; #endif middle_subtree_root.node->dirty = ((GUI_Node*)slider.node)->dirty; bottom = gui_dumb_block(context, bottom, &element_style, top_bottom_size); middle.node->dirty |= should_dirty; if(set_pixels) { #define SET_PIXEL(pixel_buffer, x, y, color) (pixel_buffer)->pixels[(y)*(pixel_buffer)->width + (x)] = (color) Pixel_Buffer *pixbuf = context->background_layer.pixel_buffer; SET_PIXEL(pixbuf, 500, 500, 0x00ff0000); SET_PIXEL(pixbuf, 500, 501, 0); SET_PIXEL(pixbuf, 501, 500, 0); SET_PIXEL(pixbuf, 501, 501, 0); SET_PIXEL(pixbuf, 500, 900, 0x00ff0000); SET_PIXEL(pixbuf, 500, 901, 0); SET_PIXEL(pixbuf, 501, 900, 0); SET_PIXEL(pixbuf, 501, 901, 0); #undef SET_PIXEL } gui_context_finish_frame(context); } #endif void test_gui__tmp( GUI_Context *context, GUI_Rectangle full_gui_rectangle) { static GUI_Style root_style = {0x44,0x33,0x33, 0x00999999,2,0}; static GUI_Style block_style = {0x33,0x33,0x33, 0x00999999,2,0}; static GUI_Size block_size[2] = { {GUI_SIZERULE_PIXELS, 200, 100}, {GUI_SIZERULE_PIXELS, 200, 100}}; static GUI_Node_Reference root = {0}; root = gui_context_start_frame( context, root, full_gui_rectangle.x1-full_gui_rectangle.x0, full_gui_rectangle.y1-full_gui_rectangle.y0, GUI_LAYOUT_VERTICAL, &root_style); static GUI_Node_Reference block1 = {0}; static GUI_Node_Reference empty_layout = {0}; static GUI_Node_Reference block2 = {0}; block1 = gui_dumb_block(context, block1, &block_style, block_size); empty_layout = gui_layout( context, empty_layout, GUI_LAYOUT_HORIZONTAL, &block_style, block_size); gui_push_parent(context, empty_layout); { } gui_pop_parent(context); block2 = gui_dumb_block(context, block2, &block_style, block_size); gui_context_finish_frame(context); } #if 0 void test_gui_layout( GUI_Context *context, GUI_Rectangle full_gui_rectangle) { static RDIC_Node_Reference root = {0}; root = gui_context_start_frame( context, root, full_gui_rectangle.x1-full_gui_rectangle.x0, full_gui_rectangle.y1-full_gui_rectangle.y0, GUI_LAYOUT_HORIZONTAL); static RDIC_Node_Reference dumb_block = {0}; #if 0 dumb_block = gui_dumb_block( context, dumb_block, (GUI_Style){255, 0, 0,0,0,0}, 50, 50); #else if(gui_dumb_button( context, &dumb_block, "BEFORE Button", (GUI_Style){255, 0, 0,0,0,0}, 50, 50)) {printf( "**************\n" "*** BEFORE ***\n" "**************\n");} #endif static RDIC_Node_Reference layout = {0}; layout = gui_layout( context, layout, GUI_LAYOUT_VERTICAL); gui_push_parent(context, layout); static RDIC_Node_Reference dumb_block2 = {0}; #if 0 dumb_block2 = gui_dumb_block( context, dumb_block2, (GUI_Style){0, 255, 0,0,0,0}, 100, 50); #else if(gui_dumb_button( context, &dumb_block2, "INSIDE Button", (GUI_Style){0, 255, 0,0,0,0}, 100, 50)) {printf( "**************\n" "*** INSIDE ***\n" "**************\n");} #endif gui_pop_parent(context); static RDIC_Node_Reference dumb_block3 = {0}; #if 0 dumb_block_after_layout = gui_dumb_block( context, dumb_block3, (GUI_Style){0, 0, 255,0,0,0}, 150, 50); #else if(gui_dumb_button( context, &dumb_block3, "AFTER Button", (GUI_Style){0, 0, 255,0,0,0}, 150, 50)) {printf( "**************\n" "*** AFTER ***\n" "**************\n");} #endif gui_context_finish_frame(context); } #endif // --- // --- // --- #include #include #include #define WINDOW_WIDTH 800 #define WINDOW_HEIGHT 600 typedef struct X11_Window { Display *display; Window root; Visual *visual; Colormap colormap; XWindowAttributes window_attributes; XSetWindowAttributes set_window_attributes; Window window; GC graphics_context; int screen; unsigned int width; unsigned int height; XImage *screen_image; Pixel_Buffer pixel_buffer; } X11_Window; #include #include #include #include noreturn void die(const char *fmt, ...) { va_list args; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); exit(EXIT_FAILURE); } void print_binary_long(long value) { int num_bits = sizeof(long)*8; unsigned long mask = ((unsigned long)1) << (num_bits-1); while(mask != (unsigned long)0) { int bit = (value & mask) != 0; printf("%d", bit); mask >>= 1; } } void fill_pixel_buffer( uint32_t *pixels, unsigned int width, unsigned int height, uint8_t red_offset, uint8_t blue_offset) { for(unsigned int column = 0; column < width; column++) { for(unsigned int row = 0; row < height; row++) { uint8_t red = (uint8_t)((column+ red_offset) & 0xFF); uint8_t blue = (uint8_t)((row +blue_offset) & 0xFF); pixels[column + row*width] = (red<<16) | blue; } } } void draw_window( X11_Window *xwindow) { /* int XPutImage(Display *display, Drawable d, GC gc, XImage *image, int src_x, int src_y, int dest_x, int dest_y, unsigned int width, unsigned int height); */ int error = XPutImage( xwindow->display, xwindow->window, xwindow->graphics_context, xwindow->screen_image, 0, 0, 0, 0, xwindow->width, xwindow->height); assert(error == 0); } typedef unsigned int uint; void draw_rect( uint32_t *pixels, unsigned int width, unsigned int height, uint x0, uint y0, uint x1, uint y1, uint32_t color) { //x0 = x0 < 0 ? 0 : x0; //y0 = y0 < 0 ? 0 : y0; x1 = x1 > width ? width : x1; y1 = y1 > width ? height : y1; for(unsigned int column = x0; column < x1; column++) { for(unsigned int row = y0; row < y1; row++) { pixels[column + row*width] = color; } } } void draw_rect2( Pixel_Buffer *pixel_buffer, GUI_Draw_Command *draw_command) { int width = pixel_buffer->width; int height = pixel_buffer->height; int x0 = draw_command->rectangle.x0; int y0 = draw_command->rectangle.y0; int x1 = draw_command->rectangle.x1; int y1 = draw_command->rectangle.y1; x0 = x0 < 0 ? 0 : x0; y0 = y0 < 0 ? 0 : y0; x1 = x1 > width ? width : x1; y1 = y1 > width ? height : y1; for(int column = x0; column < x1; column++) { for(int row = y0; row < y1; row++) { pixel_buffer->pixels[column + row*width] = draw_command->color; } } } void draw_rect3( Pixel_Buffer *pixel_buffer, GUI_Rectangle rectangle, GUI_Color color) { int width = pixel_buffer->width; int height = pixel_buffer->height; int x0 = rectangle.x0; int y0 = rectangle.y0; int x1 = rectangle.x1; int y1 = rectangle.y1; x0 = x0 < 0 ? 0 : x0; y0 = y0 < 0 ? 0 : y0; x1 = x1 > width ? width : x1; y1 = y1 > height ? height : y1; for(int column = x0; column < x1; column++) { for(int row = y0; row < y1; row++) { pixel_buffer->pixels[column + row*width] = color; } } } #define SET_PIXEL(pixel_buffer, x, y, color) (pixel_buffer)->pixels[(y)*(pixel_buffer)->width + (x)] = (color) #define GET_PIXEL(pixel_buffer, x, y) (pixel_buffer)->pixels[(y)*(pixel_buffer)->width + (x)] void test_draw_horizontal_pixel_line( Pixel_Buffer *pixel_buffer, int x0, int x1, int y, GUI_Color color) { for(int i = x0; i < x1; i++) { SET_PIXEL(pixel_buffer, i, y, color); } } void test_draw_vertical_pixel_line( Pixel_Buffer *pixel_buffer, GUI_Color color, int x, int y0, int y1) { for(int i = y0; i < y1; i++) { SET_PIXEL(pixel_buffer, x, i, color); } } // NOTE(Zelaven): Based on the technique and code for drawing circles // showcased by Casey here: // https://www.computerenhance.com/p/efficient-dda-circle-outlines void gui_draw_rounded_rect( Pixel_Buffer *pixel_buffer, GUI_Draw_Command *draw_command) { int roundedness = draw_command->roundedness; // NOTE(Zelaven): Roundedness is a function of width and height, whichever // is smaller. int width = draw_command->rectangle.x1 - draw_command->rectangle.x0; int height = draw_command->rectangle.y1 - draw_command->rectangle.y0; short r = 0; { int shortest_dimension = width < height ? width : height; r = (shortest_dimension * roundedness) / 200; } int Cx = draw_command->rectangle.x0+r; int Cy = draw_command->rectangle.y0+r; int R = r; int R2, X, Y, dY, dX, D; // TODO(Zelaven): Remove this static limit. assert(R < 500); // NOTE(Zelaven): Both of these are used to cache calculations. // NOTE(Zelaven): This one is also used to prevent redrawing the same pixels. int Y_to_X_map[500]; int Y_to_X_map2[500]; #if 0 for(size_t i = 0; i < ARRAYLENGTH(Y_to_X_map); i++) { Y_to_X_map[i] = -1; Y_to_X_map2[i] = -1; } #endif //printf("\n\n"); R2 = R+R; Y = R; X = 0; dX = -2; dY = R2+R2 - 4; D = R2 - 1; while(X <= Y) { //printf("X=%d, Y=%d\n", X, Y); //setPixel(Cx - X, Cy - Y); Y_to_X_map[Y] = X; //setPixel(Cx - X, Cy + Y); //setPixel(Cx + X, Cy - Y); //setPixel(Cx + X, Cy + Y); //setPixel(Cx - Y, Cy - X); Y_to_X_map2[X] = Y; //setPixel(Cx - Y, Cy + X); //setPixel(Cx + Y, Cy - X); //setPixel(Cx + Y, Cy + X); D += dX; dX -= 4; ++X; // NOTE(casey): Branchless version int Mask = (D >> 31); D += dY & Mask; dY -= 4 & Mask; Y += Mask; } //SET_PIXEL(pixel_buffer, draw_command->rectangle.x0, draw_command->rectangle.y0, draw_command->border_color); //SET_PIXEL(pixel_buffer, draw_command->rectangle.x1-1, draw_command->rectangle.y1-1, draw_command->border_color); // top. int top_y0 = Cy - R; int top_y1 = Cy - Y; //int top_height = top_y1 - top_y0; for(int y = top_y0; y < top_y1; y++) { int Y_ = Cy - y; int X_ = Y_to_X_map[Y_]; //printf("X_: %d\n", X_); test_draw_horizontal_pixel_line( pixel_buffer, Cx - X_, draw_command->rectangle.x1 - R + X_, y, draw_command->color); } // lower. int lower_y0 = Cy - Y; int lower_y1 = Cy; //int lower_height = lower_y1 - lower_y0; //printf("lower: %d to %d\n", lower_y0, lower_y1); for(int y = lower_y0; y < lower_y1; y++) { int Y_ = Cy - y; int X_ = Y_to_X_map2[Y_]; //printf("X_: %d\n", X_); test_draw_horizontal_pixel_line( pixel_buffer, Cx - X_, draw_command->rectangle.x1 - R + X_, y, draw_command->color); } // middle. GUI_Rectangle middle = draw_command->rectangle; middle.y0 = middle.y0 + r; middle.y1 -= r; draw_rect3(pixel_buffer, middle, draw_command->color); int middle_height = middle.y1-middle.y0; int Cy1 = Cy + middle_height - 1; // lower. lower_y0 = Cy1 +1; lower_y1 = Cy1 + Y +1; //printf("lower: %d = %d = %d - %d\n", lower_height, lower_y1 - lower_y0, lower_y1, lower_y0); for(int y = lower_y0; y < lower_y1; y++) { int Y_ = y - Cy1; int X_ = Y_to_X_map2[Y_]; //printf("X_: %d\n", X_); test_draw_horizontal_pixel_line( pixel_buffer, Cx - X_, draw_command->rectangle.x1 - R + X_, y, draw_command->color); } // top. top_y0 = Cy1 + Y +1; top_y1 = Cy1 + R +1; //printf("top: %d = %d = %d - %d\n", top_height, top_y1 - top_y0, top_y1, top_y0); for(int y = top_y0; y < top_y1; y++) { int Y_ = y - Cy1; int X_ = Y_to_X_map[Y_]; //printf("X_: %d\n", X_); test_draw_horizontal_pixel_line( pixel_buffer, Cx - X_, draw_command->rectangle.x1 - R + X_, y, draw_command->color); } } void gui_draw_rounded_rect_with_border( Pixel_Buffer *pixel_buffer, GUI_Draw_Command *draw_command) { int width = draw_command->rectangle.x1 - draw_command->rectangle.x0; int height = draw_command->rectangle.y1 - draw_command->rectangle.y0; int roundedness = draw_command->roundedness; int border_thickness = draw_command->border_thickness; if(border_thickness > (height+1)/2) border_thickness = (height+1)/2; //printf("\n\n"); //printf("border_thickness: %d\n", border_thickness); // NOTE(Zelaven): Roundedness is a function of width and height, whichever // is smaller. short r = 0; { int shortest_dimension = width < height ? width : height; r = (shortest_dimension * roundedness) / 200; } int Cx = draw_command->rectangle.x0+r; int Cy = draw_command->rectangle.y0+r; int R = r; int R2, X, Y, dY, dX, D; // TODO(Zelaven): Remove this static limit. assert(R < 500); // NOTE(Zelaven): Both of these are used to cache calculations. // NOTE(Zelaven): This one is also used to prevent redrawing the same pixels. int Y_to_X_map[500]; int Y_to_X_map2[500]; #if 1 for(size_t i = 0; i < ARRAYLENGTH(Y_to_X_map); i++) { Y_to_X_map[i] = -1; Y_to_X_map2[i] = -1; } #endif { //printf("\n\n"); R2 = R+R; Y = R; X = 0; dX = -2; dY = R2+R2 - 4; D = R2 - 1; while(X <= Y) { //printf("X=%d, Y=%d\n", X, Y); //setPixel(Cx - X, Cy - Y); Y_to_X_map[Y] = X; //setPixel(Cx - X, Cy + Y); //setPixel(Cx + X, Cy - Y); //setPixel(Cx + X, Cy + Y); //setPixel(Cx - Y, Cy - X); Y_to_X_map2[X] = Y; //setPixel(Cx - Y, Cy + X); //setPixel(Cx + Y, Cy - X); //setPixel(Cx + Y, Cy + X); //if((Cx - X) == (Cx - Y) && (Cy - Y)==(Cy - X)) // printf("SAMENESS: X: %d , Y: %d\n", X, Y); D += dX; dX -= 4; ++X; // NOTE(casey): Branchless version int Mask = (D >> 31); D += dY & Mask; dY -= 4 & Mask; Y += Mask; } } //printf("R: %d , X: %d , Y: %d\n", R, X, Y); // inner circle. int R_inner = r - border_thickness; int R2_inner, X_inner, Y_inner, dY_inner, dX_inner, D_inner; assert(R_inner < 500); // NOTE(Zelaven): Both of these are used to cache calculations. // NOTE(Zelaven): This one is also used to prevent redrawing the same pixels. int Y_to_X_map_inner[500]; int Y_to_X_map2_inner[500]; #if 1 for(size_t i = 0; i < ARRAYLENGTH(Y_to_X_map_inner); i++) { Y_to_X_map_inner[i] = -1; Y_to_X_map2_inner[i] = -1; } #endif { //printf("\n\n"); R2_inner = R_inner+R_inner; Y_inner = R_inner; X_inner = 0; dX_inner = -2; dY_inner = R2_inner+R2_inner - 4; D_inner = R2_inner - 1; while(X_inner <= Y_inner) { //printf("X_inner=%d, Y_inner=%d\n", X_inner, Y_inner); Y_to_X_map_inner[Y_inner] = X_inner; Y_to_X_map2_inner[X_inner] = Y_inner; //if((Cx - X_inner) == (Cx - Y_inner) && (Cy - Y_inner)==(Cy - X_inner)) // printf("SAMENESS: X: %d , Y: %d\n", X_inner, Y_inner); D_inner += dX_inner; dX_inner -= 4; ++X_inner; // NOTE(casey): Branchless version int Mask = (D_inner >> 31); D_inner += dY_inner & Mask; dY_inner -= 4 & Mask; Y_inner += Mask; } } //printf("R_inner: %d , X_inner: %d , Y_inner: %d\n", R_inner, X_inner, Y_inner); // Drawing. int y_ = draw_command->rectangle.y0; int rect_x0 = draw_command->rectangle.x0; int rect_x1 = draw_command->rectangle.x1; int draw_guide_lines = 0; int border_y = y_ + border_thickness; #if 0 - top1 : upper octant of outer, above inner. - top2 : upper octant of outer, upper octant of inner. - lower1: lower octant of outer, above inner. Happens when border is thicker than height of outer-upper-octant. - lower2: lower octant of outer, upper octant of inner. - lower3: lower octant of outer, lower octant of inner. #endif // top1. int top1_y0 = Cy-R; int top1_ycap = Cy-Y; //printf("%d %d\n", border_y, top1_ycap); int top1_y1 = top1_ycap < border_y ? top1_ycap : border_y; for(int top1_y = top1_y0; top1_y < top1_y1; top1_y++) { int Y_ = Cy - top1_y; int X_ = Y_to_X_map[Y_]; //printf("%d = %d - %d\n", Y_, Cy, top1_y); //printf("X_: %d\n", X_); test_draw_horizontal_pixel_line( pixel_buffer, Cx - X_, rect_x1 - R + X_, top1_y, draw_command->border_color); } if(draw_guide_lines)test_draw_horizontal_pixel_line( pixel_buffer, rect_x0, rect_x1, Cy - top1_ycap-1, 0x00ffffff); // top2. int top2_y0 = Cy-R_inner; int top2_y1 = Cy - Y; for(int top2_y = top2_y0; top2_y < top2_y1; top2_y++) { int Y_ = Cy - top2_y; int X_ = Y_to_X_map[Y_]; int X_inn = Y_to_X_map_inner[Y_]; //printf("%d = %d - %d\n", Y_, Cy, top2_y); //printf("X_: %d , X_inn: %d\n", X_, X_inn); test_draw_horizontal_pixel_line(pixel_buffer, Cx - X_, Cx - X_inn, top2_y, draw_command->border_color); test_draw_horizontal_pixel_line(pixel_buffer, Cx - X_inn, rect_x1 - R + X_inn, top2_y, draw_command->color); test_draw_horizontal_pixel_line(pixel_buffer, rect_x1 - R + X_inn, rect_x1 - R + X_, top2_y, draw_command->border_color); } if(draw_guide_lines)test_draw_horizontal_pixel_line( pixel_buffer, rect_x0, rect_x1, top2_y1, 0x00ffffff); // lower1. int lower1_y0 = Cy - Y; int lower1_y1 = border_y; if(border_y > Cy) lower1_y1 = Cy; for(int lower1_y = lower1_y0; lower1_y < lower1_y1; lower1_y++) { int Y_ = Cy - lower1_y; int X_ = Y_to_X_map2[Y_]; //printf("%d = %d - %d\n", Y_, Cy, lower1_y); //printf("X_: %d\n", X_); test_draw_horizontal_pixel_line(pixel_buffer, Cx - X_, rect_x1 - R + X_, lower1_y, draw_command->border_color); } if(draw_guide_lines)test_draw_horizontal_pixel_line( pixel_buffer, draw_command->rectangle.x0, draw_command->rectangle.x1, lower1_y1, 0x00ffffff); // lower2. int lower2_y0 = Cy - Y; if(border_y > lower2_y0) lower2_y0 = border_y; if(Y_inner < 0) Y_inner = 0; // Will happen if border_thickness > R. int lower2_y1 = Cy - Y_inner; for(int lower2_y = lower2_y0; lower2_y < lower2_y1; lower2_y++) { int Y_ = Cy - lower2_y; int X_ = Y_to_X_map2[Y_]; int X_inn = Y_to_X_map_inner[Y_]; //printf("%d = %d - %d\n", Y_, Cy, lower2_y); //printf("X_: %d , X_inn: %d\n", X_, X_inn); int x0 = Cx - X_; int x1 = Cx - X_inn; int x2 = rect_x1 - R + X_inn; int x3 = rect_x1 - R + X_; test_draw_horizontal_pixel_line( pixel_buffer, x0, x1, lower2_y, draw_command->border_color); test_draw_horizontal_pixel_line( pixel_buffer, x1, x2, lower2_y, draw_command->color); test_draw_horizontal_pixel_line( pixel_buffer, x2, x3, lower2_y, draw_command->border_color); } if(draw_guide_lines)test_draw_horizontal_pixel_line( pixel_buffer, draw_command->rectangle.x0, draw_command->rectangle.x1, lower2_y1, 0x00ffffff); // lower3. int lower3_y0 = Cy - Y_inner; int lower3_y1 = Cy; for(int lower3_y = lower3_y0; lower3_y < lower3_y1; lower3_y++) { int Y_ = Cy - lower3_y; int X_ = Y_to_X_map2[Y_]; int X_inn = Y_to_X_map2_inner[Y_]; //printf("%d = %d - %d\n", Y_, Cy, lower3_y); //printf("X_: %d , X_inn: %d\n", X_, X_inn); int x0 = Cx - X_; int x1 = Cx - X_inn; int x2 = rect_x1 - R + X_inn; int x3 = rect_x1 - R + X_; test_draw_horizontal_pixel_line( pixel_buffer, x0, x1, lower3_y, draw_command->border_color); test_draw_horizontal_pixel_line( pixel_buffer, x1, x2, lower3_y, draw_command->color); test_draw_horizontal_pixel_line( pixel_buffer, x2, x3, lower3_y, draw_command->border_color); } if(draw_guide_lines)test_draw_horizontal_pixel_line( pixel_buffer, draw_command->rectangle.x0, draw_command->rectangle.x1, lower3_y1, 0x00ffffff); // Middle section. GUI_Rectangle middle = draw_command->rectangle; middle.y0 = middle.y0 + r; middle.y1 -= r; //printf("%d %d -> %d %d\n", draw_command->rectangle.y0, draw_command->rectangle.y1, middle.y0, middle.y1); int y = middle.y0; for(; y < middle.y0 + border_thickness-r; y++) { test_draw_horizontal_pixel_line( pixel_buffer, middle.x0, middle.x1, y, draw_command->border_color); } for(; y < middle.y1 - (border_thickness-r) && y < middle.y1; y++) { int x = middle.x0; for(; x < middle.x0+border_thickness && x < middle.x1; x++) {SET_PIXEL(pixel_buffer, x, y, draw_command->border_color);} for(; x < middle.x1-border_thickness; x++) {SET_PIXEL(pixel_buffer, x, y, draw_command->color);} for(; x < middle.x1; x++) {SET_PIXEL(pixel_buffer, x, y, draw_command->border_color);} } for(; y < middle.y1; y++) { test_draw_horizontal_pixel_line( pixel_buffer, middle.x0, middle.x1, y, draw_command->border_color); } int middle_height = middle.y1-middle.y0; int Cy1 = Cy + middle_height -1; border_y = draw_command->rectangle.y1 - border_thickness; //printf("middle_height: %d, Cy1: %d, border_y: %d\n", middle_height, Cy1, border_y); // top1. { int border_y = draw_command->rectangle.y1 - border_thickness; int top1_y1 = Cy1+R + 1; int top1_ycap = Cy1 + Y +1; //int top1_y0 = 1 + (border_y > top1_ycap ? border_y : top1_ycap); int top1_y0 = 0 + (border_y > top1_ycap ? border_y : top1_ycap); for(int top1_y = top1_y0; top1_y < top1_y1; top1_y++) { int Y_ = top1_y - Cy1; //printf("%d = %d + %d\n", Y_, Cy1, top1_y); int X_ = Y_to_X_map[Y_]; //printf("X_: %d\n", X_); test_draw_horizontal_pixel_line( pixel_buffer, Cx - X_, rect_x1 - R + X_, top1_y, draw_command->border_color); } // top2. int top2_y0 = Cy1 + Y +1; int top2_y1 = Cy1+R_inner +1; //printf("top2: %d to %d\n", top2_y0, top2_y1); for(int top2_y = top2_y0; top2_y < top2_y1; top2_y++) { int Y_ = top2_y - Cy1; int X_ = Y_to_X_map[Y_]; int X_inn = Y_to_X_map_inner[Y_]; //printf("%d = %d - %d\n", Y_, Cy, top2_y); //printf("X_: %d , X_inn: %d\n", X_, X_inn); test_draw_horizontal_pixel_line(pixel_buffer, Cx - X_, Cx - X_inn, top2_y, draw_command->border_color); test_draw_horizontal_pixel_line(pixel_buffer, Cx - X_inn, rect_x1 - R + X_inn, top2_y, draw_command->color); test_draw_horizontal_pixel_line(pixel_buffer, rect_x1 - R + X_inn, rect_x1 - R + X_, top2_y, draw_command->border_color); } // lower1. int lower1_y0 = border_y; int lower1_y1 = Cy1 + Y +1; if(border_y < (Cy1+1)) lower1_y0 = Cy1+1; for(int lower1_y = lower1_y0; lower1_y < lower1_y1; lower1_y++) { int Y_ = lower1_y - Cy1; int X_ = Y_to_X_map2[Y_]; //printf("%d = %d - %d\n", Y_, Cy, lower1_y); //printf("X_: %d\n", X_); test_draw_horizontal_pixel_line(pixel_buffer, Cx - X_, rect_x1 - R + X_, lower1_y, draw_command->border_color); } // lower2. if(Y_inner < 0) Y_inner = 0; // Will happen if border_thickness > R. int lower2_y0 = Cy1 + Y_inner +1; int lower2_y1 = Cy1 + Y +1; if(border_y < lower2_y1) lower2_y1 = border_y; for(int lower2_y = lower2_y0; lower2_y < lower2_y1; lower2_y++) { int Y_ = lower2_y - Cy1; int X_ = Y_to_X_map2[Y_]; int X_inn = Y_to_X_map_inner[Y_]; //printf("%d = %d - %d\n", Y_, Cy, lower2_y); //printf("X_: %d , X_inn: %d\n", X_, X_inn); int x0 = Cx - X_; int x1 = Cx - X_inn; int x2 = rect_x1 - R + X_inn; int x3 = rect_x1 - R + X_; test_draw_horizontal_pixel_line( pixel_buffer, x0, x1, lower2_y, draw_command->border_color); test_draw_horizontal_pixel_line( pixel_buffer, x1, x2, lower2_y, draw_command->color); test_draw_horizontal_pixel_line( pixel_buffer, x2, x3, lower2_y, draw_command->border_color); } // lower3. int lower3_y0 = Cy1 +1; int lower3_y1 = Cy1 + Y_inner+1; for(int lower3_y = lower3_y0; lower3_y < lower3_y1; lower3_y++) { int Y_ = lower3_y - Cy1; int X_ = Y_to_X_map2[Y_]; int X_inn = Y_to_X_map2_inner[Y_]; //printf("%d = %d - %d\n", Y_, Cy, lower3_y); //printf("X_: %d , X_inn: %d\n", X_, X_inn); int x0 = Cx - X_; int x1 = Cx - X_inn; int x2 = rect_x1 - R + X_inn; int x3 = rect_x1 - R + X_; test_draw_horizontal_pixel_line( pixel_buffer, x0, x1, lower3_y, draw_command->border_color); test_draw_horizontal_pixel_line( pixel_buffer, x1, x2, lower3_y, draw_command->color); test_draw_horizontal_pixel_line( pixel_buffer, x2, x3, lower3_y, draw_command->border_color); } } if(draw_guide_lines)test_draw_horizontal_pixel_line( pixel_buffer, rect_x0, rect_x1, Cy - top1_ycap-1, 0x00ffffff); // Debug. #if 0 GUI_Color white = 0x00ffffff; {R2 = R+R;Y = R; X = 0;dX = -2;dY = R2+R2 - 4;D = R2 - 1; while(X <= Y){ //setPixel(Cx - X, Cy - Y); //setPixel(Cx - X, Cy + Y + middle_height -1); SET_PIXEL(pixel_buffer, Cx-X, Cy-Y, white); SET_PIXEL(pixel_buffer, Cx-X, Cy+Y + middle_height -1, white); //setPixel(Cx + X, Cy - Y); //setPixel(Cx + X, Cy + Y); //setPixel(Cx - Y, Cy - X); //setPixel(Cx - Y, Cy + X + middle_height -1); SET_PIXEL(pixel_buffer, Cx-Y, Cy-X, white); SET_PIXEL(pixel_buffer, Cx-Y, Cy+X + middle_height -1, white); //setPixel(Cx + Y, Cy - X); //setPixel(Cx + Y, Cy + X); D += dX;dX -= 4;++X; int Mask = (D >> 31);D += dY & Mask;dY -= 4 & Mask;Y += Mask;}} {R2_inner = R_inner+R_inner;Y_inner = R_inner; X_inner = 0;dX_inner = -2;dY_inner = R2_inner+R2_inner - 4;D_inner = R2_inner - 1; while(X_inner <= Y_inner){ //setPixel(Cx - X_inner, Cy - Y_inner); //setPixel(Cx - X_inner, Cy + Y_inner + middle_height-1); SET_PIXEL(pixel_buffer, Cx-X_inner, Cy-Y_inner, white); SET_PIXEL(pixel_buffer, Cx-X_inner, Cy+Y_inner + middle_height-1, white); //setPixel(Cx + X_inner, Cy - Y_inner); //setPixel(Cx + X_inner, Cy + Y_inner); //setPixel(Cx - Y_inner, Cy - X_inner); //setPixel(Cx - Y_inner, Cy + X_inner + middle_height-1); SET_PIXEL(pixel_buffer, Cx-Y_inner, Cy-X_inner, white); SET_PIXEL(pixel_buffer, Cx-Y_inner, Cy+X_inner + middle_height-1, white); //setPixel(Cx + Y_inner, Cy - X_inner); //setPixel(Cx + Y_inner, Cy + X_inner); D_inner += dX_inner;dX_inner -= 4;++X_inner; int Mask = (D_inner >> 31);D_inner += dY_inner & Mask;dY_inner -= 4 & Mask;Y_inner += Mask;}} { printf("%%%%%%%%%%%%%%%%%%%%\n"); GUI_Color red = 0x00ff4444; GUI_Color cyan = 0x0000ffff; GUI_Color yellow = 0x00ffff00; int x_ = draw_command->rectangle.x0 + R; int y_ = draw_command->rectangle.y0; int Y_ = Y + 1; printf("rect corner: x: %d, y: %d\n", x_ -R, y_); printf("R: %d, R_inner: %d, Cx: %d, Cy: %d\n", R, R_inner, Cx, Cy); printf("Y: %d, Y_: %d, Y_inner: %d\n", Y, Y_, Y_inner); SET_PIXEL(pixel_buffer, Cx, Cy, white); if(0)test_draw_horizontal_pixel_line( pixel_buffer, draw_command->rectangle.x0, draw_command->rectangle.x1, Cy-Y, 0x00ffffff); int top1_height, top2_height, lower1_height, lower2_height, lower3_height; { // top1. int border_y = y_ + border_thickness; int top1_y0 = Cy-R; int top1_y = Cy-Y; //printf("%d %d\n", border_y, top1_y); int top1_y1 = top1_y < border_y ? top1_y : border_y; top1_height = top1_y1 - top1_y0; //debug. test_draw_vertical_pixel_line(pixel_buffer, cyan, x_, top1_y0, top1_y1); // top2. int top2_y0 = Cy-R_inner; int top2_y1 = Cy - Y; top2_height = top2_y1 - top2_y0; // debug. test_draw_vertical_pixel_line(pixel_buffer, red, x_+1, top2_y0, top2_y1); // lower1. int lower1_y0 = Cy - Y; int lower1_y1 = border_y; if(border_y > Cy) lower1_y1 = Cy; lower1_height = lower1_y1 - lower1_y0; //debug. test_draw_vertical_pixel_line(pixel_buffer, yellow, x_+2, lower1_y0, lower1_y1); // lower2. int lower2_y0 = Cy - Y; if(border_y > lower2_y0) lower2_y0 = border_y; if(Y_inner < 0) Y_inner = 0; // Will happen if border_thickness > R. int lower2_y1 = Cy - Y_inner; lower2_height = lower2_y1 - lower2_y0; //debug. //printf("lower2: %d %d\n", lower2_y0, lower2_y1); test_draw_vertical_pixel_line(pixel_buffer, red, x_+3, lower2_y0, lower2_y1); // lower3. int lower3_y0 = Cy - Y_inner; int lower3_y1 = Cy; lower3_height = lower3_y1 - lower3_y0; //debug. test_draw_vertical_pixel_line(pixel_buffer, cyan, x_+4, lower3_y0, lower3_y1); } if(0)test_draw_horizontal_pixel_line( pixel_buffer, draw_command->rectangle.x0, draw_command->rectangle.x1, Cy1+Y, 0x00ffffff); { int Cy1 = Cy + middle_height -1; printf("Cy1: %d\n", Cy1); SET_PIXEL(pixel_buffer, Cx, Cy1, white); // top1. int border_y = draw_command->rectangle.y1 - border_thickness; int top1_y1 = Cy1+R+1; int top1_ycap = Cy1 + Y +1; //int top1_y0 = 1 + (border_y > top1_ycap ? border_y : top1_ycap); int top1_y0 = 0 + (border_y > top1_ycap ? border_y : top1_ycap); //printf("%d %d\n", border_y, top1_y); if((top1_y1 - top1_y0) != top1_height) printf("top1 height error.\n"); test_draw_vertical_pixel_line(pixel_buffer, cyan, x_, top1_y0, top1_y1); // top2. int top2_y0 = Cy1 + Y +1; int top2_y1 = Cy1+R_inner+1; if((top2_y1 - top2_y0) != top2_height) printf("top2 height error.\n"); test_draw_vertical_pixel_line(pixel_buffer, red, x_+1, top2_y0, top2_y1); // lower1. int lower1_y0 = border_y; int lower1_y1 = Cy1 + Y +1; if(border_y < (Cy1+1)) lower1_y0 = Cy1+1; if((lower1_y1 - lower1_y0) != lower1_height) printf("lower1 height error.\n"); test_draw_vertical_pixel_line(pixel_buffer, yellow, x_+2, lower1_y0, lower1_y1); // lower2. if(Y_inner < 0) Y_inner = 0; // Will happen if border_thickness > R. int lower2_y0 = Cy1 + Y_inner +1; int lower2_y1 = Cy1 + Y +1; if(border_y < lower2_y1) lower2_y1 = border_y; //printf("lower2: %d %d\n", lower2_y0, lower2_y1); if((lower2_y1 - lower2_y0) != lower2_height) printf("lower2 height error.\n"); test_draw_vertical_pixel_line(pixel_buffer, red, x_+3, lower2_y0, lower2_y1); // lower3. int lower3_y0 = Cy1 +1; int lower3_y1 = Cy1 + Y_inner+1; if((lower3_y1 - lower3_y0) != lower3_height) printf("lower3 height error.\n"); test_draw_vertical_pixel_line(pixel_buffer, cyan, x_+4, lower3_y0, lower3_y1); } test_draw_vertical_pixel_line(pixel_buffer, cyan, draw_command->rectangle.x0+80, y_, y_+border_thickness); test_draw_vertical_pixel_line(pixel_buffer, red, draw_command->rectangle.x0+90, y_, y_+height); } #endif } void gui_draw_rect( Pixel_Buffer *pixel_buffer, GUI_Draw_Command *draw_command) { if(draw_command->roundedness == 0) { // TODO(Zelaven) Don't double-draw on the space covered by the inner // rectangle. draw_rect3( pixel_buffer, draw_command->rectangle, draw_command->border_color); GUI_Rectangle inner = draw_command->rectangle; inner.x0 += draw_command->border_thickness; inner.y0 += draw_command->border_thickness; inner.x1 -= draw_command->border_thickness; inner.y1 -= draw_command->border_thickness; if(inner.x0 < inner.x1 && inner.y0 < inner.y1) { draw_rect3(pixel_buffer, inner, draw_command->color); } } else { if(draw_command->border_thickness == 0) { gui_draw_rounded_rect(pixel_buffer, draw_command); } else { gui_draw_rounded_rect_with_border(pixel_buffer, draw_command); } } } int init_x11( X11_Window *xwindow) { xwindow->display = XOpenDisplay(NULL); if(xwindow->display == NULL) { return EXIT_FAILURE; } xwindow->root = DefaultRootWindow(xwindow->display); xwindow->screen = XDefaultScreen(xwindow->display); xwindow->visual = XDefaultVisual(xwindow->display, xwindow->screen); xwindow->colormap = XCreateColormap( xwindow->display, xwindow->root, xwindow->visual, AllocNone); xwindow->set_window_attributes.colormap = xwindow->colormap; xwindow->set_window_attributes.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | Button1MotionMask | Button3MotionMask | Button4MotionMask | Button5MotionMask | PointerMotionMask | KeymapStateMask | EnterWindowMask | LeaveWindowMask; xwindow->window = XCreateWindow( xwindow->display, xwindow->root, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, 0, XDefaultDepth(xwindow->display, xwindow->screen), InputOutput, xwindow->visual, CWEventMask | CWColormap, &xwindow->set_window_attributes); #if 0 XSelectInput( xwindow->display, xwindow->window, StructureNotifyMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask); #endif XStoreName(xwindow->display, xwindow->window, "X11 Test Program"); XMapWindow(xwindow->display, xwindow->window); XGetWindowAttributes( xwindow->display, xwindow->window, &xwindow->window_attributes); xwindow->width = (unsigned int)xwindow->window_attributes.width; xwindow->height = (unsigned int)xwindow->window_attributes.height; #if 0 printf("Initial window dimensions: %u x %u\n", xwindow->width, xwindow->height); printf("Window depth: %d\n", xwindow->window_attributes.depth); printf("ALL Event mask: %lx\n", xwindow->window_attributes.all_event_masks); print_binary_long(xwindow->window_attributes.all_event_masks); printf("\n"); printf("YOUR Event mask: %lx\n", xwindow->window_attributes.your_event_mask); print_binary_long(xwindow->window_attributes.your_event_mask); printf("\n"); #endif // TODO: This is an experiment. #if 0 { int display_width = XDisplayWidth(xwindow->display, xwindow->screen); int display_height = XDisplayHeight(xwindow->display, xwindow->screen); int display_width_mm = XDisplayWidthMM(xwindow->display, xwindow->screen); int display_height_mm = XDisplayHeightMM(xwindow->display, xwindow->screen); printf("Display Resolution: %d, %d.\n", display_width, display_height); printf("Display Size: %d, %d.\n", display_width_mm, display_height_mm); printf("Display DPmm: %d, %d.\n", display_width / display_width_mm, display_height / display_height_mm); printf("Display DPI: %d, %d.\n", display_width / ((display_width_mm*10)/254), display_height / ((display_height_mm*10)/254)); Screen *screen = XScreenOfDisplay(xwindow->display, xwindow->screen); int screen_width = XWidthOfScreen(screen); int screen_height = XHeightOfScreen(screen); int screen_width_mm = XWidthMMOfScreen(screen); int screen_height_mm = XHeightMMOfScreen(screen); printf("Screen Resolution: %d, %d.\n", screen_width, screen_height); printf("Screen Size: %d, %d.\n", screen_width_mm, screen_height_mm); printf("Screen DPmm: %d, %d.\n", screen_width / screen_width_mm, screen_height / screen_height_mm); printf("Screen DPI: %d, %d.\n", screen_width / ((screen_width_mm*10)/254), screen_height / ((screen_height_mm*10)/254)); } #endif // . #if 0 XSelectInput( xwindow->display, xwindow->window, StructureNotifyMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask); #endif // Discard everything preceding the MapNotify event that tells us that we // have been properly mapped. // This requires StructureNotifyMask to be selected. // https://tronche.com/gui/x/xlib-tutorial/2nd-program-anatomy.html { XEvent e; e.type = 0; assert(MapNotify != 0); while(e.type != MapNotify) { XNextEvent(xwindow->display, &e); } } // Re-get in case the window manager decides to instantiate the window with a // different size than the one we ask for. XGetWindowAttributes( xwindow->display, xwindow->window, &xwindow->window_attributes); xwindow->width = (unsigned int)xwindow->window_attributes.width; xwindow->height = (unsigned int)xwindow->window_attributes.height; #if 0 printf("Window dimensions: %u x %u\n", xwindow->width, xwindow->height); printf("Window depth: %d\n", xwindow->window_attributes.depth); printf("ALL Event mask: %lx\n", xwindow->window_attributes.all_event_masks); print_binary_long(xwindow->window_attributes.all_event_masks); printf("\n"); printf("YOUR Event mask: %lx\n", xwindow->window_attributes.your_event_mask); print_binary_long(xwindow->window_attributes.your_event_mask); printf("\n"); #endif uint32_t *pixels = malloc(sizeof(*pixels) * xwindow->width * xwindow->height); assert(pixels != NULL); xwindow->graphics_context = XDefaultGC(xwindow->display, xwindow->screen); XSetForeground(xwindow->display, xwindow->graphics_context, 0x123456); XImage *screen_image = XCreateImage( xwindow->display, xwindow->visual, XDefaultDepth(xwindow->display, xwindow->screen), // NOTE(Zelaven): I can't find an explanation to what this actually is // anywhere. All the documentation I can find just assumes that you // magically know. ZPixmap, 0, (char*)pixels, xwindow->width, xwindow->height, 8, // NOTE(Zelaven): No clue // NOTE(Zelaven): This is how many bytes there are in a row of pixels. xwindow->width * sizeof(uint32_t)); assert(screen_image != NULL); xwindow->pixel_buffer = (Pixel_Buffer){ .pixels = (GUI_Color*)pixels, .width = xwindow->width, .height = xwindow->height, }; xwindow->screen_image = screen_image; return EXIT_SUCCESS; } // --- // --- // --- void init_node_freelist(GUI_Node *nodes, int node_count) { // NOTE(Zelaven): We special-case the last node. for(int i = 0; i < node_count -1; i++) { nodes[i] = (GUI_Node){0}; nodes[i].rdic_node = (RDIC_Node){ .sibling = &((nodes+i+1)->rdic_node), }; } nodes[node_count-1].rdic_node = (RDIC_Node){0}; } void init_subtree_freelist(GUI_Subtree *nodes, int subtree_count) { // NOTE(Zelaven): We special-case the last node. for(int i = 0; i < subtree_count -1; i++) { nodes[i] = (GUI_Subtree){0}; nodes[i].rdic_node = (RDIC_Node){ .sibling = &((nodes+i+1)->rdic_node), }; } nodes[subtree_count-1].rdic_node = (RDIC_Node){0}; } GUI_Node g_gui_node_freelist[128]; GUI_Subtree g_gui_subtree_freelist[16]; #include "size_of_file.c" struct File_Read_Result { unsigned char *data; size_t size; } read_file__cruddy( FILE *file) { struct File_Read_Result retval = {0}; size_t status = 0; long filesize = size_of_file(file); if(filesize == -1) { status = 1;} if(status == 0) { size_t memory_size = filesize; memory_size = ((memory_size + 4096 - 1) / 4096) * 4096; void *file_memory = mmap( NULL, memory_size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0); if(file_memory == (void*)-1) { //perror("mmap"); status = 2; } else { retval.data = file_memory; } } if(status == 0) { if(fread(retval.data, filesize, 1, file) != 1) { status = 3;} } if(status == 0) { retval.size = (size_t)filesize;} else { if(retval.data != NULL) { free(retval.data); retval.data = NULL;} retval.size = status; } return retval; } void copy_from_atlas( Pixel_Buffer *pixel_buffer, int off_x, int off_y, int x, int y, int w, int h) { draw_rect( pixel_buffer->pixels, pixel_buffer->width, pixel_buffer->height, off_x, off_y, off_x+w, off_y+h, 0x00ffffff); for(int j=0; j < h; ++j) { for(int i=0; i < w; ++i) { int atlas_x = x + i; int atlas_y = y + j; int atlas_index = atlas_y * g_font_bitmap_width + atlas_x; unsigned char atlas_shade = g_font_bitmap[atlas_index]; int color = (atlas_shade << 16) | (atlas_shade << 8) | atlas_shade; //printf("%d ", color); SET_PIXEL(pixel_buffer, x+off_x+i, y+off_y+j, color); } } //printf("\n"); } void blend_glyph( Pixel_Buffer *pixel_buffer, stbtt_bakedchar *bakedchar, int x, int y) { int glyph_height = bakedchar->y1 - bakedchar->y0; int glyph_width = bakedchar->x1 - bakedchar->x0; //printf("%d, %d - %d, %d\n", x, y, glyph_width, glyph_height); if(0)draw_rect( pixel_buffer->pixels, pixel_buffer->width, pixel_buffer->height, x, y, x+glyph_width, y+glyph_height, 0x00ffffff); for(int j=0; j < glyph_height; ++j) { for(int i=0; i < glyph_width; ++i) { //unsigned char font_pixel_shade = glyph_bitmap[j*glyph_size.w+i]; int atlas_x = bakedchar->x0 + i; int atlas_y = bakedchar->y0 + j; int atlas_index = atlas_y * g_font_bitmap_width + atlas_x; GUI_Color atlas_shade = (GUI_Color)g_font_bitmap[atlas_index]; //GUI_Color color = (atlas_shade << 16) | (atlas_shade << 8) | atlas_shade; //GUI_Color color = atlas_shade; GUI_Color pixel_color = GET_PIXEL(pixel_buffer, x+i, y+j); //GUI_Color pixel_color = 0x00aa764a; int red = ((pixel_color & 0x00ff0000) >> 16) * (256-atlas_shade) / 256; int green = ((pixel_color & 0x0000ff00) >> 8) * (256-atlas_shade) / 256; int blue = ((pixel_color & 0x000000ff)) * (256-atlas_shade) / 256; GUI_Color new_color = (red << 16) | (green << 8) | blue; //GUI_Color new_color = pixel_color * (255-color) / 255; //GUI_Color new_color = (atlas_shade << 16) | (atlas_shade << 8) | atlas_shade; //printf("%x %x %x\n", color, pixel_color, new_color); SET_PIXEL(pixel_buffer, x+i, y+j, new_color); //*pixel_color = *pixel_color * (255-font_pixel_shade) / 255; //*pixel_color = font_pixel_shade; } } //printf("\n"); } int text_measure_string_width(char *string, size_t str_length) { int result = 0; for(size_t i = 0; i < str_length; i++) { int glyph_index = string[i] - 32; stbtt_bakedchar *glyph = &g_baked_font[glyph_index]; result += glyph->xadvance; } return result; } #include struct Text_Y_Bounds {int upper; int lower;} text_measure_string_y_bounds(char *string, size_t str_length) { struct Text_Y_Bounds result = {.upper = INT_MAX, .lower = INT_MIN}; for(size_t i = 0; i < str_length; i++) { int glyph_index = string[i] - 32; stbtt_bakedchar *glyph = &g_baked_font[glyph_index]; int height = glyph->y1 - glyph->y0; int offset = glyph->yoff; // This is negative. int uppermost_pixel = offset; int lowermost_pixel = height + offset; //print_bakedchar(string[i], glyph); if(uppermost_pixel < result.upper) { result.upper = uppermost_pixel; } if(lowermost_pixel > result.lower) { result.lower = lowermost_pixel; } } return result; } void blend_string( Pixel_Buffer *pixel_buffer, GUI_String text, int x, int y) { int next_x = x; for(int i = 0; i < text.length; i++) { int glyph_index = text.cstring[i] - 32; stbtt_bakedchar *g = &g_baked_font[glyph_index]; blend_glyph( pixel_buffer, g, next_x + g->xoff, y + g->yoff); next_x += g->xadvance; } } void gui_draw_text( Pixel_Buffer *pixel_buffer, GUI_Draw_Command *draw_command) { if(draw_command->text.cstring == NULL) { return; } //printf("%.*s: %d\n", draw_command->text.length, draw_command->text.cstring, draw_command->text.length); int text_width = text_measure_string_width( draw_command->text.cstring, draw_command->text.length); int width = draw_command->rectangle.x1 - draw_command->rectangle.x0; int x = draw_command->rectangle.x0 + width/2 - text_width/2; struct Text_Y_Bounds y_bounds = text_measure_string_y_bounds( draw_command->text.cstring, draw_command->text.length); int y_bounds_middle = (y_bounds.lower + y_bounds.upper)/2; int height = draw_command->rectangle.y1 - draw_command->rectangle.y0; int y = draw_command->rectangle.y0 + height/2 - y_bounds_middle; #if 0 printf("%.*s: %d, (%d,%d) %d -> %d, %d\n", draw_command->text.length, draw_command->text.cstring, text_width, y_bounds.upper, y_bounds.lower, y_bounds_middle, x, y); #endif blend_string( pixel_buffer, draw_command->text, x, y); #if 0 SET_PIXEL(pixel_buffer, x, draw_command->rectangle.y0 + height/2, 0x00ff0000); SET_PIXEL(pixel_buffer, draw_command->rectangle.x0 + width/2, draw_command->rectangle.y0 + height/2, 0x00ffff00); SET_PIXEL(pixel_buffer, x, y, 0x0000ff00); #endif } void gui_draw_image_direct( Pixel_Buffer *pixel_buffer, Pixel_Buffer *image, int off_x, int off_y) { if(image == NULL) { return; } int w = image->width; int h = image->height; for(int j=0; j < h; ++j) { for(int i=0; i < w; ++i) { int image_x = i; int image_y = j; int image_index = image_y * w + image_x; GUI_Color pixel = image->pixels[image_index]; SET_PIXEL(pixel_buffer, off_x+i, off_y+j, pixel); } } } void gui_draw_image( Pixel_Buffer *pixel_buffer, GUI_Draw_Command *draw_command) { if(draw_command->image == NULL) { return; } int off_x = draw_command->rectangle.x0; int off_y = draw_command->rectangle.y0; Pixel_Buffer *image = draw_command->image; int w = image->width; int h = image->height; for(int j=0; j < h; ++j) { for(int i=0; i < w; ++i) { int image_x = i; int image_y = j; int image_index = image_y * w + image_x; GUI_Color pixel = image->pixels[image_index]; SET_PIXEL(pixel_buffer, off_x+i, off_y+j, pixel); } } } #define MIN(a,b) ((a) < (b) ? (a) : (b)) #define MAX(a,b) ((a) > (b) ? (a) : (b)) void gui_draw_image_clipped( Pixel_Buffer *pixel_buffer, GUI_Draw_Command *draw_command, GUI_Rectangle *cliprect) { if(draw_command->image == NULL) { return; } //int off_x = cliprect->x0; //int off_y = cliprect->y0; Pixel_Buffer *image = draw_command->image; int w = image->width; int clipwidth = cliprect->x1 - cliprect->x0; w = MIN(w, clipwidth); int h = image->height; int clipheight = cliprect->y1 - cliprect->y0; h = MIN(h, clipheight); int startx = cliprect->x0 - draw_command->rectangle.x0; startx = MAX(startx, 0); int starty = cliprect->y0 - draw_command->rectangle.y0; starty = MAX(starty, 0); int off_x = cliprect->x0 - startx; int off_y = cliprect->y0 - starty; for(int j=starty; j < h; ++j) { for(int i=startx; i < w; ++i) { int image_x = i; int image_y = j; int image_index = image_y * w + image_x; GUI_Color pixel = image->pixels[image_index]; SET_PIXEL(pixel_buffer, off_x+i, off_y+j, pixel); } } } GUI_Rectangle gui_intersect_rectangles( GUI_Rectangle *r1, GUI_Rectangle *r2) { GUI_Rectangle r = { .x0 = MAX(r1->x0, r2->x0), .x1 = MIN(r1->x1, r2->x1), .y0 = MAX(r1->y0, r2->y0), .y1 = MIN(r1->y1, r2->y1), }; return r; } void gui_draw_rect_clipped( Pixel_Buffer *pixel_buffer, GUI_Draw_Command *draw_command, GUI_Rectangle *clip) { if(clip->x0 <= draw_command->rectangle.x0 && clip->y0 <= draw_command->rectangle.y0 && clip->x1 <= draw_command->rectangle.x1 && clip->y1 <= draw_command->rectangle.y1) { gui_draw_rect(pixel_buffer, draw_command); } else { if(draw_command->roundedness == 0) { GUI_Rectangle clipped_rect = gui_intersect_rectangles( clip, &draw_command->rectangle); // TODO(Zelaven) Don't double-draw on the space covered by the inner // rectangle. draw_rect3( pixel_buffer, clipped_rect, draw_command->border_color); GUI_Rectangle inner = clipped_rect; inner.x0 += draw_command->border_thickness; inner.y0 += draw_command->border_thickness; inner.x1 -= draw_command->border_thickness; inner.y1 -= draw_command->border_thickness; if(inner.x0 < inner.x1 && inner.y0 < inner.y1) { draw_rect3(pixel_buffer, inner, draw_command->color); } } else { assert(!"TODO"); if(draw_command->border_thickness == 0) { gui_draw_rounded_rect(pixel_buffer, draw_command); } else { gui_draw_rounded_rect_with_border(pixel_buffer, draw_command); } } } } void gui_layout_and_draw_subtree( GUI_Context *context, GUI_Subtree *subtree, Pixel_Buffer *pixel_buffer) { //gui_layout_nodes((GUI_Node*)subtree->rdic.root); gui_layout_nodes(subtree); gui_generate_draw_commands( subtree, context->draw_command_arena, &context->num_draw_commands); GUI_Draw_Command *draw_commands = (GUI_Draw_Command*)(context->draw_command_arena->memory); GUI_Node_Reference clipnode = subtree->clipnode; if(rdic_node_reference_valid(clipnode.rdic_ref)) { GUI_Rectangle *cliprect = &clipnode.node->rect; for(int i = 0; i < context->num_draw_commands; i++) { GUI_Draw_Command *draw_command = draw_commands+i; gui_draw_rect_clipped(pixel_buffer, draw_command, cliprect); //gui_draw_image(pixel_buffer, draw_command);//, cliprect); gui_draw_image_clipped(pixel_buffer, draw_command, cliprect); gui_draw_text (pixel_buffer, draw_command);//, cliprect); } } else { for(int i = 0; i < context->num_draw_commands; i++) { GUI_Draw_Command *draw_command = draw_commands+i; gui_draw_rect (pixel_buffer, draw_command); gui_draw_image(pixel_buffer, draw_command); gui_draw_text (pixel_buffer, draw_command); } } // TODO NOTE(Zelaven): There may arise an issue where the subtrees won't // generate draw commands even if they are overdrawn by changes in a // parent subtree. GUI_Subtree *first_child = GUI_SUBTREE_FIRST_CHILD(subtree); if(first_child != NULL) { gui_layout_and_draw_subtree(context, first_child, pixel_buffer); } GUI_Subtree *sibling = GUI_SUBTREE_SIBLING(subtree); if(sibling != NULL) { gui_layout_and_draw_subtree(context, sibling, pixel_buffer); } } void gui_layout_and_draw(GUI_Context *context) { // 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]; GUI_Subtree *subtree_root = (GUI_Subtree*)layer->subtree_rdic.root; if(subtree_root != NULL) { gui_layout_and_draw_subtree( context, subtree_root, layer->pixel_buffer); } } } 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. int main(void) { void (*gui)(GUI_Context *,GUI_Rectangle); // Font initialization. Memory_Arena font_arena = {0}; assert(linux_allocate_arena_memory( &font_arena, g_font_bitmap_width*g_font_bitmap_height) == 0); FILE *font_file = fopen("michroma/Michroma-Regular.ttf", "r"); //FILE *font_file = fopen("/usr/share/fonts/TTF/Roboto-Black.ttf", "r"); struct File_Read_Result font_data = read_file__cruddy(font_file); // TODO(Zelaven): How much data do I have to make sure that I read at minimum? assert(font_data.size > 16); { int font_bitmap_size = g_font_bitmap_width * g_font_bitmap_height; g_font_bitmap = memory_arena_allocate( &font_arena, font_bitmap_size, _Alignof(char)); int bake_font_return_value = stbtt_BakeFontBitmap( font_data.data, 0, 20.0, g_font_bitmap, g_font_bitmap_width, g_font_bitmap_height, 32, 96, g_baked_font); //printf("bake_font_return_value: %d\n", bake_font_return_value); if(bake_font_return_value < 1) { return EXIT_FAILURE; } } // GUI initializaiton. //gui = test_gui; //gui = test_gui__calculator; //gui = test_gui__scrollbars; //gui = test_gui__draw_command_using_sliders; gui = test_gui__subtree; //gui = test_gui__tmp; X11_Window xwindow = {0}; int xinit_status = init_x11(&xwindow); if(xinit_status != EXIT_SUCCESS) { return EXIT_FAILURE; } Memory_Arena draw_command_arena = {0}; assert(linux_allocate_arena_memory( &draw_command_arena, 1024*4) == 0); GUI_Context context = {0}; #if 0 init_node_freelist(g_gui_node_freelist, ARRAYLENGTH(g_gui_node_freelist)); context.rdic.node_freelist = &g_gui_node_freelist[0].rdic_node; #else init_node_freelist(g_gui_node_freelist, ARRAYLENGTH(g_gui_node_freelist)); RDIC_Freelist node_freelist = {&g_gui_node_freelist[0].rdic_node}; context.node_freelist = &node_freelist; init_subtree_freelist(g_gui_subtree_freelist, ARRAYLENGTH(g_gui_subtree_freelist)); 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*400*sizeof(GUI_Color)), .width = 300, .height = 400, }; 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 = &background_pixel_buffer, }; context.top_layer = (GUI_Layer){ .subtree_rdic.freelist = &subtree_freelist, .pixel_buffer = &top_pixel_buffer, }; #endif context.draw_command_arena = &draw_command_arena; context.num_draw_commands = 0; fill_pixel_buffer( xwindow.pixel_buffer.pixels, 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, }; bool running = true; while(running) { usleep(2*100000); context.mouse_pressed = false; while(XPending(xwindow.display)) { XEvent x_event = {0}; XNextEvent(xwindow.display, &x_event); if(XFilterEvent(&x_event, xwindow.window)) { continue; } switch(x_event.type) { case ButtonPress: { //printf("Button pressed: %d\n", x_event.xbutton.button); context.mouse_pressed = true; context.mouse_down = true; } break; case ButtonRelease: { //printf("Button released: %d\n", x_event.xbutton.button); context.mouse_down = false; } break; case MotionNotify: { //printf("Pointer motion: %d, %d\n", x_event.xmotion.x, x_event.xmotion.y); context.mouse_x = x_event.xmotion.x; context.mouse_y = x_event.xmotion.y; } break; case KeyPress: { KeySym keysym = XLookupKeysym( &x_event.xkey, 0); // What does 0 mean here? (void) keysym; //printf("Keysym pressed: %s\n", XKeysymToString(keysym)); //printf("\tkeycode: %d\n", x_event.xkey.keycode); //printf("\tkeysym code: %ld\n", keysym); switch(x_event.xkey.keycode) { case 38: break; // a case 40: break; // d case 25: break; // w case 39: break; // s //case 24: running = false; break; // q case 24: // q { // Setting the flag here is necessary. running = false; // Can I just do this after the loop iteration finishes? XDestroyWindow(xwindow.display, xwindow.window); } break; } } break; case KeyRelease: { KeySym keysym = XLookupKeysym( &x_event.xkey, 0); // What does 0 mean here? (void) keysym; //printf("Keysym released: %s\n", XKeysymToString(keysym)); switch(x_event.xkey.keycode) { case 38: break; // a case 40: break; // d case 25: break; // w case 39: break; // s } } break; case DestroyNotify: { running = false; } break; default: { //printf("Unknown event, type: %d\n", x_event.type); } break; } } static bool meh = true; if(running && meh) { gui(&context, screen_rectangle); if(0)print_node_tree( (GUI_Node*)((GUI_Subtree*)context.background_layer.subtree_rdic.root)->rdic.root, 1); if(0)print_subtree_tree((GUI_Subtree*)context.background_layer.subtree_rdic.root, 1, true); //gui_layout_nodes(&context); #if 1 gui_layout_and_draw(&context); gui_compose_layers(&context, &xwindow.pixel_buffer); //SET_PIXEL(&xwindow.pixel_buffer, 200, 388, 0x00ff0000); #else for( GUI_Subtree *current = (GUI_Subtree*)context.background_layer.subtree_rdic.root; current != NULL; current = NULL)//current->next) { gui_layout_nodes((GUI_Node*)current->rdic.root); gui_generate_draw_commands( //(GUI_Node*)current->rdic.root, current, &draw_command_arena, &context.num_draw_commands); GUI_Draw_Command *draw_command = (GUI_Draw_Command*)(draw_command_arena.memory); for(int i = 0; i < context.num_draw_commands; i++) { #if 0 if(i > 0) (draw_command+i)->roundedness = roundedness; #endif gui_draw_rect( &xwindow.pixel_buffer, draw_command+i); gui_draw_image( &xwindow.pixel_buffer, draw_command+i); gui_draw_text( &xwindow.pixel_buffer, draw_command+i); } } #elseif 0 gui_generate_draw_commands( (GUI_Node*)context.first_subtree->rdic.root, &draw_command_arena, &context.num_draw_commands); GUI_Draw_Command *draw_command = (GUI_Draw_Command*)(draw_command_arena.memory); for(int i = 0; i < context.num_draw_commands; i++) { #if 0 if(i > 0) (draw_command+i)->roundedness = roundedness; #endif gui_draw_rect( &xwindow.pixel_buffer, draw_command+i); gui_draw_text( &xwindow.pixel_buffer, draw_command+i); } #endif #if 0 GUI_Draw_Command test_draw_command = { .rectangle = {50, 150, 250, 250}, .color = 0x00aa8888, .border_color = 0x00ffaaff, .border_thickness = border_thickness, .roundedness = roundedness, }; gui_draw_rounded_rect(&xwindow.pixel_buffer, &test_draw_command); GUI_Draw_Command test_draw_command2 = { .rectangle = {50, 300, 250, 400}, .color = 0x00aa8888, .border_color = 0x00ffaaff, .border_thickness = border_thickness, .roundedness = roundedness, }; gui_draw_rounded_rect_with_border(&xwindow.pixel_buffer, &test_draw_command2); //meh = false; #endif #if 0 //print_bakedchar(32+1, &g_baked_font[1]); blend_glyph( &xwindow.pixel_buffer, &g_baked_font[1], 80, 170); blend_glyph( &xwindow.pixel_buffer, &g_baked_font[1], 80+g_baked_font[1].xadvance, 170); char *text = "This is a text! VA"; int textlen = sizeof("This is a text! VA")-1; int text_width = text_measure_string_width(text, textlen); //printf("Text width: %d\n", text_width); int next_x = 80; test_draw_horizontal_pixel_line( &xwindow.pixel_buffer, next_x, next_x + text_width, 195, 0); for(int i = 0; i < textlen; i++) { int glyph_index = text[i] - 32; stbtt_bakedchar *g = &g_baked_font[glyph_index]; blend_glyph( &xwindow.pixel_buffer, g, next_x + g->xoff, 195 + g->yoff); next_x += g->xadvance; } blend_string( &xwindow.pixel_buffer, //GUI_STRING("This is a text! VA"), GUI_STRING("a = 19 + 23; 1234567890C+-*/="), 360, 260); test_draw_horizontal_pixel_line( &xwindow.pixel_buffer, 360, 360+200, 260, 0x00ff0000); copy_from_atlas( &xwindow.pixel_buffer, 300, 100, 0, 0, 512, 100); //SET_PIXEL(&xwindow.pixel_buffer, 100, 100, 0x00ffffff); #endif draw_window(&xwindow); XFlush(xwindow.display); } } XDestroyImage(xwindow.screen_image); //XUnmapWindow(xwindow.display, xwindow.window); //XDestroyWindow(xwindow.display, xwindow.window); XFreeColormap(xwindow.display, xwindow.colormap); XCloseDisplay(xwindow.display); //getc(stdin); return 0; }