選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

3841 行
110 KiB

#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include <assert.h>
#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);
}
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;
} 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
// ---
// 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(root_subtree != NULL)
{
GUI_Node *top_layer_root =
(GUI_Node*)root_subtree->rdic.root;
if(top_layer_root != NULL)
if(point_in_rect(mouse_x, mouse_y, top_layer_root->rect))
{
hit_layer = &context->top_layer;
hit_root_subtree = root_subtree;
}
}
}
(void)hit_layer;
assert(hit_root_subtree != NULL);
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(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);
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),
&current_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 = &current_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;
rdic_cull_subtrees(&subtree->rdic, 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;
}
#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_Subtree_Reference root_subtree = context->current_layer->root_subtree;
//root_subtree = gui_get_subtree(context, root_subtree);
root_subtree.rdic_ref = rdic_context_start_frame(
&context->current_layer->subtree_rdic, root_subtree.rdic_ref);
if(root_subtree.node == NULL)
{
return (GUI_Node_Reference){0};
}
context->current_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;
}
gui_apply_input(context, context->mouse_x, context->mouse_y);
root->debug_string = GUI_STRING("root");
root->layout_direction = layout_direction;
root->semantic_size[GUI_AXIS2_X] = (GUI_Size)
{.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;
}
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;
rdic_push_parent(&current_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;
rdic_pop_parent(&current_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_Node *root)
{
//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);
}
// ---
#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) {
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,
};
(*draw_command).rectangle.x0 += x_offset;
(*draw_command).rectangle.x1 += x_offset;
(*draw_command).rectangle.y0 += y_offset;
(*draw_command).rectangle.y1 += y_offset;
*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)
{
// 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;
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_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_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;
}
// ---
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_Node_Reference scrollbar_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}};
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);
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.
#if 0
float contents_height_ratio = 0.0f;
if(scrollbox_layout.node->computed_size[GUI_AXIS2_Y] > 0) {
float layout_height =
(float)scrollbox_layout.node->computed_size[GUI_AXIS2_Y];
float contents_height = 600.0f; // TODO(Zelaven): take from scrollregion_layout. (dependency on CHILDRENSUM).
}
#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 = {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};
// 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.
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, 200, 100},
{GUI_SIZERULE_PIXELS, 40, 100}};
if(gui_dumb_button(
context, &dirty_button, GUI_STRING("Dirty middle"),
&button_style, button_size))
{
should_dirty = true;
}
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");
}
} 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;
} 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
((GUI_Node*)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 <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#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 <stdlib.h>
#include <stdarg.h>
#include <stdnoreturn.h>
#include <stdio.h>
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_pixel_buffer(
unsigned int width,
unsigned int height,
Display *game_display,
Window game_window,
XImage *game_image,
GC x_graphics_context)
{
/*
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(
game_display,
game_window,
x_graphics_context,
game_image,
0, 0,
0, 0,
width, 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 *game_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(game_image != NULL);
fill_pixel_buffer(
pixels,
xwindow->width,
xwindow->height,
0,
0);
draw_rect(
pixels,
xwindow->width,
xwindow->height,
20, 40, 25, 50,
0xFFFF00);
GUI_Draw_Command my_draw_command = {
.rectangle = {60, 60, 100, 90},
.color = 0xFF00FF,
};
xwindow->pixel_buffer = (Pixel_Buffer){
.pixels = (GUI_Color*)pixels,
.width = xwindow->width,
.height = xwindow->height,
};
draw_rect2(
&xwindow->pixel_buffer,
&my_draw_command);
xwindow->screen_image = game_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 <limits.h>
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(
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_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): Handle all layers.
for(
GUI_Subtree *current = (GUI_Subtree*)context->background_layer.subtree_rdic.root;
current != NULL;
current = NULL)//current->next)
{
gui_layout_and_draw_subtree(
context,
current,
context->background_layer.pixel_buffer);
}
}
#include <unistd.h> // 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);
g_font_bitmap = memory_arena_allocate(&font_arena, sizeof(char), _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;
context.background_layer = (GUI_Layer){
.subtree_rdic.freelist = &subtree_freelist,
.pixel_buffer = &xwindow.pixel_buffer,
};
context.top_layer = (GUI_Layer){
.subtree_rdic.freelist = &subtree_freelist,
};
#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);
GUI_Rectangle screen_rectangle = {
.x0 = 0, .y0 = 0,
.x1 = xwindow.width, .y1 = xwindow.height,
};
//gui(&context, screen_rectangle);
//gui_layout_nodes(&context);
#if 0
gui_generate_draw_commands(
context.root,
&draw_command_arena,
&context.num_draw_commands);
GUI_Draw_Command *draw_commands = (GUI_Draw_Command*)(draw_command_arena.memory);
for(int i = 0; i < context.num_draw_commands; i++)
{
draw_rect2(
&pixel_buffer,
&draw_commands[i]);
}
for(int i = 4; i < 100; i += 5)
{
pixel_buffer.pixels[i*xwindow.width + 10] = 0x0000FFFF;
}
draw_pixel_buffer(
xwindow.width,
xwindow.height,
xwindow.display,
xwindow.window,
xwindow.screen_image,
xwindow.graphics_context);
#endif
//getc(stdin);
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);
#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_pixel_buffer(
xwindow.width,
xwindow.height,
xwindow.display,
xwindow.window,
xwindow.screen_image,
xwindow.graphics_context);
#if 1
// NOTE(Zelaven): What does this _really_ do and why would you want it?
//XClearWindow(xwindow.display, xwindow.window);
/*
GUI_Draw_Command *draw_command =
(GUI_Draw_Command*)(draw_command_arena.memory);
for(int i = 0; i < context.num_draw_commands; i++)
{
//xstuff_filled_rect(&xwindow, draw_command+i);
xstuff_draw_rect(
&xwindow,
draw_command+i);
}
*/
//xstuff_blit_window(
// &xwindow);
XFlush(xwindow.display);
#endif
}
}
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;
}