You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3297 lines
94 KiB
3297 lines
94 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 {
|
|
#define RDIC_GUI_NODE_MEMBER_NAME rdic_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.
|
|
bool dirty;
|
|
|
|
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 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 {
|
|
RDIC_Context rdic;
|
|
GUI_Subtree *prev;
|
|
GUI_Subtree *next;
|
|
GUI_Subtree *structural_parent;
|
|
|
|
RDIC_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;
|
|
|
|
GUI_Draw_Command *first_draw_command;
|
|
int num_draw_commands;
|
|
Pixel_Buffer *pixel_buffer;
|
|
};
|
|
|
|
|
|
typedef struct {
|
|
//RDIC_Context rdic;
|
|
|
|
GUI_Subtree *first_subtree;
|
|
GUI_Subtree *last_subtree;
|
|
GUI_Subtree *current_subtree;
|
|
|
|
RDIC_Node_Reference focused_node;
|
|
RDIC_Node_Reference hovered_node;
|
|
RDIC_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_Node *node_freelist;
|
|
|
|
Memory_Arena *draw_command_arena;
|
|
int num_draw_commands;
|
|
} GUI_Context;
|
|
|
|
|
|
|
|
|
|
|
|
// ---
|
|
|
|
#if 0
|
|
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\n", text.length, text.cstring);
|
|
|
|
GUI_Node *child = node->rdic_node.first_child;
|
|
while(child != NULL)
|
|
{
|
|
print_node_tree(child, level+1);
|
|
child = child->rdic_node.sibling;
|
|
}
|
|
}
|
|
#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)
|
|
{
|
|
//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 *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->rdic_node;
|
|
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);
|
|
}
|
|
|
|
|
|
RDIC_Node_Reference gui_get_node(
|
|
GUI_Context *context,
|
|
RDIC_Node_Reference last_reference,
|
|
GUI_Layout_Direction direction,
|
|
GUI_Style *style,
|
|
GUI_Size size[static 2])
|
|
{
|
|
int get_node_flags = 0;
|
|
RDIC_Node_Reference new_reference =
|
|
rdic_get_node(&context->current_subtree->rdic, last_reference, &get_node_flags);
|
|
GUI_Node *node = (GUI_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;
|
|
}
|
|
|
|
#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.
|
|
RDIC_Node_Reference gui_context_start_frame(
|
|
GUI_Context *context,
|
|
RDIC_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);
|
|
|
|
static GUI_Subtree background_subtree = {0};
|
|
background_subtree.rdic.node_freelist = context->node_freelist;
|
|
context->first_subtree = &background_subtree;
|
|
context->last_subtree = &background_subtree;
|
|
context->current_subtree = &background_subtree;
|
|
|
|
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->first_subtree->rdic);
|
|
context->node_freelist = context->first_subtree->rdic.node_freelist;
|
|
}
|
|
|
|
RDIC_Node_Reference gui_push_subtree(
|
|
GUI_Context *context,
|
|
GUI_Subtree *subtree,
|
|
RDIC_Node_Reference last_root_reference,
|
|
GUI_Layout_Direction layout_direction,
|
|
GUI_Style *style,
|
|
GUI_Size size[static 2],
|
|
RDIC_Node_Reference relative_to)
|
|
{
|
|
context->node_freelist = context->current_subtree->rdic.node_freelist;
|
|
subtree->rdic.node_freelist = context->node_freelist;
|
|
|
|
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->relative_to = relative_to;
|
|
|
|
RDIC_Node_Reference new_root_reference = rdic_context_start_frame(
|
|
&subtree->rdic,
|
|
last_root_reference);
|
|
GUI_Node *root = (GUI_Node*)new_root_reference.node;
|
|
root->dirty = false;
|
|
if(!gui_node_references_equal(last_root_reference, new_root_reference)) {
|
|
root->dirty = true;
|
|
}
|
|
if(layout_direction != root->layout_direction) {
|
|
root->dirty = true;
|
|
}
|
|
|
|
root->debug_string = GUI_STRING("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;
|
|
}
|
|
|
|
|
|
void gui_push_parent(
|
|
GUI_Context *context,
|
|
RDIC_Node_Reference parent)
|
|
{
|
|
rdic_push_parent(&context->current_subtree->rdic, parent);
|
|
}
|
|
|
|
void gui_pop_parent(
|
|
GUI_Context *context)
|
|
{
|
|
rdic_pop_parent(&context->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_generate_draw_commands_inner(
|
|
x_offset, y_offset,
|
|
sibling,
|
|
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);
|
|
gui_generate_draw_commands_inner(
|
|
x_offset, y_offset,
|
|
root, root->dirty,
|
|
draw_command_arena, out_num_draw_commands);
|
|
}
|
|
|
|
|
|
|
|
// ---
|
|
|
|
|
|
|
|
|
|
RDIC_Node_Reference gui_layout(
|
|
GUI_Context *context,
|
|
RDIC_Node_Reference last_reference,
|
|
GUI_Layout_Direction direction,
|
|
GUI_Style *style,
|
|
GUI_Size size[static 2])
|
|
{
|
|
RDIC_Node_Reference new_reference = gui_get_node(
|
|
context, last_reference, direction, style, size);
|
|
GUI_Node *node = (GUI_Node*)new_reference.node;
|
|
node->text_string = (GUI_String){0};
|
|
node->debug_string = GUI_STRING("gui_layout");
|
|
return new_reference;
|
|
}
|
|
|
|
|
|
// ---
|
|
|
|
|
|
RDIC_Node_Reference gui_dumb_block(
|
|
GUI_Context *context,
|
|
RDIC_Node_Reference last_reference,
|
|
GUI_Style *style,
|
|
GUI_Size size[static 2])
|
|
{
|
|
RDIC_Node_Reference new_reference = gui_get_node(
|
|
context, last_reference, GUI_LAYOUT_NONE, style, size);
|
|
GUI_Node *node = (GUI_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,
|
|
RDIC_Node_Reference *last_reference,
|
|
GUI_String text,
|
|
GUI_Style *style,
|
|
GUI_Size size[static 2])
|
|
{
|
|
RDIC_Node_Reference new_reference = gui_get_node(
|
|
context, *last_reference, GUI_LAYOUT_NONE, style, size);
|
|
GUI_Node *node = (GUI_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,
|
|
RDIC_Node_Reference *last_reference,
|
|
RDIC_Node_Reference *inner_box_reference,
|
|
float *value,
|
|
GUI_Style *background_style,
|
|
GUI_Style *bar_style,
|
|
GUI_Size size[static 2])
|
|
{
|
|
RDIC_Node_Reference new_reference = gui_get_node(
|
|
context, *last_reference, GUI_LAYOUT_NONE, background_style, size);
|
|
GUI_Node *node = (GUI_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);
|
|
RDIC_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 = (GUI_Node*)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 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,
|
|
&root_style);
|
|
|
|
static RDIC_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 RDIC_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 RDIC_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 RDIC_Node_Reference dumb_block2 = {0};
|
|
dumb_block2 = gui_dumb_block(
|
|
context,
|
|
dumb_block2,
|
|
&inner_block_2_style,
|
|
dumb_button_size);
|
|
static RDIC_Node_Reference dumb_block3 = {0};
|
|
dumb_block3 = gui_dumb_block(
|
|
context,
|
|
dumb_block3,
|
|
&inner_block_3_style,
|
|
dumb_button_size);
|
|
static RDIC_Node_Reference dumb_block4 = {0};
|
|
dumb_block4 = gui_dumb_block(
|
|
context,
|
|
dumb_block4,
|
|
&inner_block_4_style,
|
|
dumb_button_size);
|
|
static RDIC_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 RDIC_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 RDIC_Node_Reference slider = {0};
|
|
static RDIC_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 RDIC_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 RDIC_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 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,
|
|
300, 400,
|
|
GUI_LAYOUT_VERTICAL,
|
|
&root_style);
|
|
|
|
#if 1
|
|
static RDIC_Node_Reference rows[4] = {0};
|
|
static RDIC_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 RDIC_Node_Reference row1 = {0};
|
|
row1 = gui_layout2(
|
|
context, row1, GUI_LAYOUT_HORIZONTAL, &row_style, row_size);
|
|
gui_push_parent(context, row1);
|
|
static RDIC_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 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,
|
|
//300, 400,
|
|
GUI_LAYOUT_VERTICAL,
|
|
&root_style);
|
|
|
|
|
|
static RDIC_Node_Reference top = {0};
|
|
static RDIC_Node_Reference middle = {0};
|
|
static RDIC_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 RDIC_Node_Reference left = {0};
|
|
static RDIC_Node_Reference center = {0};
|
|
static RDIC_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 RDIC_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 RDIC_Node_Reference scrollregion_layout = {0};
|
|
static RDIC_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 RDIC_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 RDIC_Node_Reference scrollbar_upper_spacer = {0};
|
|
static RDIC_Node_Reference scrollbar_knob = {0};
|
|
static RDIC_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 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_VERTICAL,
|
|
&root_style);
|
|
|
|
static GUI_Size layout_size[2] = {
|
|
{GUI_SIZERULE_PERCENTOFPARENT, 100, 100},
|
|
{GUI_SIZERULE_PERCENTOFPARENT, 50, 100}};
|
|
static RDIC_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 RDIC_Node_Reference slider = {0};
|
|
static RDIC_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 RDIC_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 RDIC_Node_Reference spacer1 = {0};
|
|
spacer1 = gui_dumb_block(
|
|
context, spacer1, &button_spacer_style, button_spacer_size);
|
|
|
|
static RDIC_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 RDIC_Node_Reference spacer2 = {0};
|
|
spacer2 = gui_dumb_block(
|
|
context, spacer2, &button_spacer_style, button_spacer_size);
|
|
|
|
static RDIC_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 RDIC_Node_Reference slider_spacer = {0};
|
|
slider_spacer = gui_dumb_block(
|
|
context, slider_spacer, &slider_spacer_style, slider_spacer_size);
|
|
|
|
static RDIC_Node_Reference slider2 = {0};
|
|
static RDIC_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);
|
|
}
|
|
|
|
|
|
void test_gui__subtree(
|
|
GUI_Context *context,
|
|
GUI_Rectangle 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 test_size[2] = {
|
|
{GUI_SIZERULE_PIXELS, 200, 100},
|
|
{GUI_SIZERULE_PIXELS, 600, 100}};
|
|
|
|
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_VERTICAL,
|
|
&root_style);
|
|
|
|
|
|
static RDIC_Node_Reference top = {0};
|
|
static RDIC_Node_Reference middle = {0};
|
|
static RDIC_Node_Reference bottom = {0};
|
|
|
|
static RDIC_Node_Reference slider = {0};
|
|
static float slider_value = 0.25f;
|
|
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 RDIC_Node_Reference slider = {0};
|
|
static RDIC_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);
|
|
} gui_pop_parent(context); // middle.
|
|
|
|
middle = gui_dumb_block(context, middle, &outer_style, middle_size);
|
|
|
|
static GUI_Subtree middle_subtree = {0};
|
|
static RDIC_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*10] = {
|
|
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 = 10,
|
|
};
|
|
static RDIC_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);
|
|
#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.offset_relative_node_percentage_y = slider_value*100;
|
|
middle_subtree.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, &outer_style, top_bottom_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};
|
|
}
|
|
|
|
GUI_Node g_gui_node_freelist[128];
|
|
|
|
|
|
|
|
#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;
|
|
}
|
|
|
|
// TODO(Zelaven): Clipping.
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#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;
|
|
|
|
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));
|
|
context.node_freelist = &g_gui_node_freelist[0].rdic_node;
|
|
#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
|
|
|
|
//print_node_tree(context.rdic.root, 1);
|
|
//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);
|
|
//gui_layout_nodes(&context);
|
|
#if 1
|
|
for(
|
|
GUI_Subtree *current = context.first_subtree;
|
|
current != NULL;
|
|
current = 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);
|
|
}
|
|
}
|
|
#else
|
|
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;
|
|
}
|
|
|
|
|
|
|
|
|