Nuklear/gui.h
2015-06-04 22:50:22 +02:00

4743 lines
162 KiB
C

/*
Copyright (c) 2015
vurtun <polygone@gmx.net>
MIT licence
*/
#ifndef GUI_H_
#define GUI_H_
#ifdef GUI_STATIC
#define GUI_API static
#else
#define GUI_API extern
#endif
#ifdef __cplusplus
extern "C" {
#endif
/* Constants */
#define GUI_UTF_SIZE 4
/* describes the number of bytes a glyph consists of*/
#define GUI_INPUT_MAX 16
/* defines the max number of bytes to be added as input in one frame */
#define GUI_MAX_COLOR_STACK 32
/* defines the number of temporary configuration color changes that can be stored */
#define GUI_MAX_ATTRIB_STACK 32
/* defines the number of temporary configuration attribute changes that can be stored */
/*
Since the gui uses ANSI C which does not guarantee to have fixed types, you need
to set the appropriate size of each type. However if your developer environment
supports fixed size types over the <stdint> header you can just use
#define GUI_USE_FIXED_TYPES
to automatically set the correct size for each type in the library.
*/
#ifdef GUI_USE_FIXED_TYPES
#include <stdint.h>
typedef int8_t gui_char;
typedef int32_t gui_int;
typedef int32_t gui_bool;
typedef int16_t gui_short;
typedef int64_t gui_long;
typedef float gui_float;
typedef uint16_t gui_ushort;
typedef uint32_t gui_uint;
typedef uint64_t gui_ulong;
typedef uint32_t gui_flags;
typedef uint8_t gui_byte;
typedef uint32_t gui_flag;
typedef uint64_t gui_size;
#else
typedef int gui_int;
typedef int gui_bool;
typedef char gui_char;
typedef short gui_short;
typedef long gui_long;
typedef float gui_float;
typedef unsigned short gui_ushort;
typedef unsigned int gui_uint;
typedef unsigned long gui_ulong;
typedef unsigned int gui_flags;
typedef unsigned char gui_byte;
typedef unsigned int gui_flag;
typedef unsigned long gui_size;
#endif
/* Utilities */
enum {gui_false, gui_true};
enum gui_heading {GUI_UP, GUI_RIGHT, GUI_DOWN, GUI_LEFT};
struct gui_color {gui_byte r,g,b,a;};
struct gui_vec2 {gui_float x,y;};
struct gui_rect {gui_float x,y,w,h;};
struct gui_key {gui_bool down, clicked;};
typedef union {void *ptr; gui_int id;} gui_handle;
typedef gui_char gui_glyph[GUI_UTF_SIZE];
/* Callbacks */
struct gui_font;
typedef gui_bool(*gui_filter)(gui_long unicode);
typedef gui_size(*gui_text_width_f)(gui_handle, const gui_char*, gui_size);
/*
* ==============================================================
*
* Input
*
* ===============================================================
*/
/* INPUT
----------------------------
The input API is responsible for holding input state by keeping track of
mouse, key and text input state. The core of the API is the persistent
gui_input struct which holds the input state over the runtime of the gui.
It is important to note that no direct os or window handling is done by the input
API, instead all the input state has to be provided from the user. This in one hand
expects more work from the user and complicates the usage but on the other hand
provides simple abstraction over a big number of platforms, libraries and other
already provided functionality.
USAGE
----------------------------
To instantiate the Input API the gui_input structure has to be zeroed at
the beginning of the program by either using memset or setting it to {0},
since the internal state is persistent over all frames.
To modify the internal input state you have to first set the gui_input struct
into a modifiable state with gui_input_begin. After the gui_input struct is
now ready you can add input state changes until everything is up to date.
Finally to revert back into a read state you have to call gui_input_end.
Input function API
gui_input_begin() -- begins the modification state
gui_input_motion() -- notifies of a cursor motion update
gui_input_key() -- notifies of a keyboard key update
gui_input_button() -- notifies of a action event
gui_input_char() -- adds a text glyph to gui_input
gui_input_end() -- ends the modification state
*/
#if 0
/* Example */
#define GUI_IMPLEMENTATION
#include "gui.h"
int main(void)
{
struct gui_input input;
memset(&input, 0, sizeof(input));
while (1) {
gui_input_begin(&in);
if (Key_state_changed)
gui_input_key(&input, key, is_down);
else if (button_state_changed)
gui_input_button(&in, mouse_x, mouse_y, is_down);
else if (mouse_position_changed)
gui_input_motion(&input, mouse_x, mouse_y);
else if (text_input)
gui_input_char(&input, glyph);
gui_input_end(&in);
}
return 0;
}
#endif
/* every key that is being used inside the library */
enum gui_keys {
GUI_KEY_SHIFT,
GUI_KEY_DEL,
GUI_KEY_ENTER,
GUI_KEY_BACKSPACE,
GUI_KEY_ESCAPE,
GUI_KEY_SPACE,
GUI_KEY_MAX
};
struct gui_input {
struct gui_key keys[GUI_KEY_MAX];
/* state of every used key */
gui_char text[GUI_INPUT_MAX];
/* utf8 text input frame buffer */
gui_size text_len;
/* text input frame buffer length in bytes */
struct gui_vec2 mouse_pos;
/* current mouse position */
struct gui_vec2 mouse_prev;
/* mouse position in the last frame */
struct gui_vec2 mouse_delta;
/* mouse travelling distance over the last and current frame */
gui_bool mouse_down;
/* current mouse button state */
gui_uint mouse_clicked;
/* number of mouse button state transistion between frames */
struct gui_vec2 mouse_clicked_pos;
/* mouse position of the last mouse button state change */
};
GUI_API void gui_input_begin(struct gui_input*);
/* this function sets the input state to writeable
Input:
- Input structure to set modfifiable
*/
GUI_API void gui_input_motion(struct gui_input*, gui_int x, gui_int y);
/* this function updates the current mouse position
Input:
- Input structure to update to mouse state
- local os window X position inside of the mouse
- local os window Y position inside of the mouse
*/
GUI_API void gui_input_key(struct gui_input*, enum gui_keys, gui_bool down);
/* this function updates the current state of a key
Input:
- Input structure to update to key state
- key identifies whose state has been changed
- the new state of the key
*/
GUI_API void gui_input_button(struct gui_input*, gui_int x, gui_int y, gui_bool down);
/* this function updates the current state of the button
Input:
- Input structure to update to key state
- local os window X position inside of the mouse
- local os window Y position inside of the mouse
- the new state of the button
*/
GUI_API void gui_input_char(struct gui_input*, const gui_glyph);
/* this function adds a utf8 glpyh into the internal text frame buffer
Input:
- Input structure to add the glyph to
- utf8 glyph to add
*/
GUI_API void gui_input_end(struct gui_input*);
/* this function sets the input state to readable
Input:
- Input structure to set readable
*/
/*
* ==============================================================
*
* Buffer
*
* ===============================================================
*/
/* BUFFER
----------------------------
A basic buffer API with linear allocation and resetting as only freeing policy.
The buffer main purpose is to control all memory management inside
the GUI toolkit and still leave memory control as much as possible in the hand
of the user. The memory control is herby achivable over three different ways
of memory handlong from the user.
The first way is to use a fixed size block of memory to be filled up.
Biggest advantage of using a fixed size block is a simple memory model.
Downside is that if the buffer is full no way of accessing more memory is
available, which fits target application with roughly known memory consumptions.
The second way to mnamge memory is by extending the fixed size block by querying
the buffer for information about the used size and needed size and allocate new
memory if the buffer is full. While this approach is still better than just using
a fixed size memory block the reallocation still has one invalid frame as consquence
since the used memory information is only available at the end of the frame which leads
to the last way of handling memory.
The last and most complicated way of handling memory is by allocator callbacks.
The user herby registers callbacks to be called to allocate, free and reallocate
memory if needed. While this solves most allocation problems it causes some
loss of flow control on the user side.
USAGE
----------------------------
To instantiate the Buffer you either have to call the fixed size or allocator
initialization function and provide a memory block in the first case and
an allocator in the second case.
To allocate memory from the buffer you would call gui_buffer_alloc with a request
memory block size aswell as an alignment for the block. Finally to reset the memory
at the end of the frame and when the memory buffer inside the buffer is no longer
needed you would call gui_buffer_reset. To free all memory that has been allocated
by an allocator if the buffer is no longer being used you have to call gui_buffer_clear.
Buffer function API
gui_buffer_init -- initializes a dynamic buffer
gui_buffer_init_fixed -- initializes a static buffer
gui_buffer_info -- provides memory information about a buffer
gui_buffer_alloc -- allocates a block of memory from the buffer
gui_buffer_reset -- resets the buffer back to an empty state
gui_buffer_clear -- frees all memory if the buffer is dynamic
*/
struct gui_memory_status {
void *memory;
/* pointer to the currently used memory block inside the referenced buffer */
gui_uint type;
/* type of the buffer which is either fixed size or dynamic */
gui_size size;
/* total size of the memory block */
gui_size allocated;
/* allocated amount of memory */
gui_size needed;
/* memory size that would have been allocated if enough memory was present */
gui_size calls;
/* number of allocation calls referencing this buffer */
};
struct gui_allocator {
gui_handle userdata;
/* handle to your own allocator */
void*(*alloc)(gui_handle, gui_size);
/* allocation function pointer */
void*(*realloc)(gui_handle, void*, gui_size);
/* reallocation pointer of a previously allocated memory block */
void(*free)(gui_handle, void*);
/* callback function pointer to finally free all allocated memory */
};
enum gui_buffer_type {
GUI_BUFFER_FIXED,
/* fixed size memory buffer */
GUI_BUFFER_DYNAMIC
/* dynically growing buffer */
};
struct gui_memory {void *ptr;gui_size size;};
struct gui_buffer {
struct gui_allocator pool;
/* allocator callback for dynamic buffers */
enum gui_buffer_type type;
/* memory type management type */
struct gui_memory memory;
/* memory and size of the current memory block */
gui_float grow_factor;
/* growing factor for dynamic memory management */
gui_size allocated;
/* total amount of memory allocated */
gui_size needed;
/* total amount of memory allocated if enough memory would have been present */
gui_size calls;
/* number of allcation calls */
};
GUI_API void gui_buffer_init(struct gui_buffer*, const struct gui_allocator*,
gui_size initial_size, gui_float grow_factor);
/* this function initializes a growing buffer
Input:
- allocator holding your own alloctator and memory allocation callbacks
- initial size of the buffer
- factor to grow the buffer with if the buffer is full
Output:
- dynamically growing buffer
*/
GUI_API void gui_buffer_init_fixed(struct gui_buffer*, void *memory, gui_size size);
/* this function initializes a fixed size buffer
Input:
- fixed size previously allocated memory block
- size of the memory block
Output:
- fixed size buffer
*/
GUI_API void gui_buffer_info(struct gui_memory_status*, struct gui_buffer*);
/* this function requests memory information from a buffer
Input:
- buffer to get the inforamtion from
Output:
- buffer memory information
*/
GUI_API void *gui_buffer_alloc(struct gui_buffer*, gui_size size, gui_size align);
/* this functions allocated a aligned memory block from a buffer
Input:
- buffer to allocate memory from
- size of the requested memory block
- alignment requirement for the memory block
Output:
- memory block with given size and alignment requirement
*/
GUI_API void gui_buffer_reset(struct gui_buffer*);
/* this functions resets the buffer back into a empty state
Input:
- buffer to reset
*/
GUI_API void gui_buffer_clear(struct gui_buffer*);
/* this functions frees all memory inside a dynamically growing buffer
Input:
- buffer to clear
*/
#if 0
/* Example fixed size buffer */
#define GUI_IMPLEMENTATION
#include "gui.h"
int main(void)
{
struct gui_buffer buffer;
void *memory = calloc(4*1024);
gui_buffer_init_fixed(&buffer, memory, 4*1024);
void *ptr = gui_buffer_alloc(&buffer, 256, 4);
gui_buffer_reset(&buffer);
return 0;
}
#endif
#if 0
/* Example fixed size buffer */
#define GUI_IMPLEMENTATION
#include "gui.h"
int main(void)
{
struct gui_allocator alloc;
alloc.userdata = your_allocator;
alloc.alloc = your_allocation_callback;
alloc.realloc = your_reallocation_callback;
alloc.free = your_free_callback;
struct gui_buffer buffer;
gui_buffer_init(&buffer, &alloc, 4*1024, 2.7f);
void *ptr = gui_buffer_alloc(&buffer, 256, 4);
gui_buffer_reset(&buffer);
gui_buffer_clear(&buffer);
return 0;
}
#endif
/*
* ==============================================================
*
* Commands
*
* ===============================================================
*/
/* DRAW COMMAND QUEUE
----------------------------
The command buffer API enqueues draw calls as commands and therefore abstracts
over drawing routines and enables defered drawing. The API offers a number of
drawing primitives like lines, rectangles, circles, triangles, images, text and
clipping rectangles, that have to be drawn by the user. Therefore the command
buffer is the main toolkit output besides the actual widget output.
The actual draw command execution is done by the user and is build up in a
interpreter like fashion by iterating over all commands and executing each
command differently depending on the command type.
USAGE
----------------------------
To use the command buffer you first have to initiate the buffer either as fixed
size or growing buffer. After the initilization you can add primitives by
calling the appropriate gui_command_buffer_XXX for each primitive.
To iterate over each commands inside the buffer gui_foreach_command is
provided. Finally to reuse the buffer after the frame use the
gui_command_buffer_reset function.
command buffer function API
gui_command_buffer_init -- initializes a dynamic command buffer
gui_command_buffer_init_fixed -- initializes a static command buffer
gui_command_buffer_reset -- resets the command buffer back to an empty state
gui_command_buffer_clear -- frees all memory if the command buffer is dynamic
command queue function API
gui_command_buffer_push -- pushes and enqueues a command into the buffer
gui_command_buffer_push_scissor -- pushes a clipping rectangle into the command queue
gui_command_buffer_push_line -- pushes a line into the command queue
gui_command_buffer_push_rect -- pushes a rectange into the command queue
gui_command_buffer_push_circle -- pushes a circle into the command queue
gui_command_buffer_push_triangle-- pushes a triangle command into the queue
gui_command_buffer_push_image -- pushes a image draw command into the queue
gui_command_buffer_push_text -- pushes a text draw command into the queue
command iterator function API
gui_command_buffer_begin -- returns the first command in a queue
gui_command_buffer_next -- returns the next command in a queue
gui_foreach_command -- iterates over all commands in a queue
*/
/* command type of every used drawing primitive */
enum gui_command_type {
GUI_COMMAND_NOP,
GUI_COMMAND_SCISSOR,
GUI_COMMAND_LINE,
GUI_COMMAND_RECT,
GUI_COMMAND_CIRCLE,
GUI_COMMAND_TRIANGLE,
GUI_COMMAND_TEXT,
GUI_COMMAND_IMAGE,
GUI_COMMAND_MAX
};
/* command base and header of every comand inside the buffer */
struct gui_command {
enum gui_command_type type;
/* the type of the current command */
gui_size offset;
/* offset to the next command */
};
struct gui_command_scissor {
struct gui_command header;
gui_short x, y;
gui_ushort w, h;
};
struct gui_command_line {
struct gui_command header;
gui_short begin[2];
gui_short end[2];
gui_byte color[4];
};
struct gui_command_rect {
struct gui_command header;
gui_uint r;
gui_short x, y;
gui_ushort w, h;
gui_byte color[4];
};
struct gui_command_circle {
struct gui_command header;
gui_short x, y;
gui_ushort w, h;
gui_byte color[4];
};
struct gui_command_image {
struct gui_command header;
gui_short x, y;
gui_ushort w, h;
gui_handle img;
};
struct gui_command_triangle {
struct gui_command header;
gui_short a[2];
gui_short b[2];
gui_short c[2];
gui_byte color[4];
};
struct gui_command_text {
struct gui_command header;
gui_handle font;
gui_byte bg[4];
gui_byte fg[4];
gui_short x, y;
gui_ushort w, h;
gui_size length;
gui_char string[1];
};
enum gui_command_clipping {
GUI_NOCLIP = gui_false,
GUI_CLIP = gui_true
};
struct gui_command_buffer {
struct gui_buffer base;
/* memory buffer */
struct gui_rect clip;
/* current clipping rectangle */
gui_bool use_clipping;
/* flag if the command buffer should clip commands */
};
#define gui_command_buffer_init(b, a, i, g, c)\
{gui_buffer_init(&(b)->base, a, i, g), (b)->use_clipping=c; (b)->clip=gui_null_rect;}
/* this function initializes a growing buffer
Input:
- allocator holding your own alloctator and memory allocation callbacks
- initial size of the buffer
- factor to grow the buffer with if the buffer is full
- clipping flag for draw command clipping
Output:
- dynamically growing command buffer
*/
#define gui_command_buffer_init_fixed(b, m, s,c)\
{gui_buffer_init_fixed(&(b)->base, m ,s); (b)->use_clipping=c; (b)->clip=gui_null_rect;}
/* this function initializes a fixed size buffer
Input:
- memory block to fill
- size of the memory block
- clipping flag for draw command clipping
Output:
- fixed size command buffer
*/
#define gui_command_buffer_reset(b)\
gui_buffer_reset(&(b)->base)
/* this function resets the buffer back to an empty state
Input:
- buffer to reset
*/
#define gui_command_buffer_clear(b)\
gui_buffer_clear(&(b)->base)
/* this function frees the memory of a dynamic buffer
Input:
- buffer to reset
*/
GUI_API void *gui_command_buffer_push(struct gui_command_buffer*, gui_uint type, gui_size size);
/* this function push enqueues a command into the buffer
Input:
- buffer to push the command into
- type of the command
- amount of memory that is needed for the specified command
*/
GUI_API void gui_command_buffer_push_scissor(struct gui_command_buffer*, gui_float,
gui_float, gui_float, gui_float);
/* this function push a clip rectangle command into the buffer
Input:
- buffer to push the clip rectangle command into
- x,y and width and height of the clip rectangle
*/
GUI_API void gui_command_buffer_push_line(struct gui_command_buffer*, gui_float, gui_float,
gui_float, gui_float, struct gui_color);
/* this function pushes a line draw command into the buffer
Input:
- buffer to push the clip rectangle command into
- starting position of the line
- ending position of the line
- color of the line to draw
*/
GUI_API void gui_command_buffer_push_rect(struct gui_command_buffer *buffer, gui_float x,
gui_float y, gui_float w, gui_float h,
gui_float r, struct gui_color c);
/* this function pushes a rectangle draw command into the buffer
Input:
- buffer to push the draw rectangle command into
- rectangle position
- rectangle size
- rectangle rounding
- color of the rectangle to draw
*/
GUI_API void gui_command_buffer_push_circle(struct gui_command_buffer*, gui_float, gui_float,
gui_float, gui_float, struct gui_color);
/* this function pushes a circle draw command into the buffer
Input:
- buffer to push the circle draw command into
- x position of the top left of the circle
- y position of the top left of the circle
- rectangle diameter of the circle
- color of the circle to draw
*/
GUI_API void gui_command_buffer_push_triangle(struct gui_command_buffer*, gui_float, gui_float,
gui_float, gui_float, gui_float, gui_float,
struct gui_color);
/* this function pushes a triangle draw command into the buffer
Input:
- buffer to push the draw triangle command into
- (x,y) coordinates of all three points
- rectangle diameter of the circle
- color of the triangle to draw
*/
GUI_API void gui_command_buffer_push_image(struct gui_command_buffer*, gui_float,
gui_float, gui_float, gui_float, gui_handle);
/* this function pushes a image draw command into the buffer
Input:
- buffer to push the draw image command into
- position of the image with x,y position
- size of the image to draw with width and height
- rectangle diameter of the circle
- color of the triangle to draw
*/
GUI_API void gui_command_buffer_push_text(struct gui_command_buffer*, gui_float, gui_float,
gui_float, gui_float, const gui_char*, gui_size,
const struct gui_font*, struct gui_color,
struct gui_color);
/* this function pushes a text draw command into the buffer
Input:
- buffer to push the draw text command into
- top left position of the text with x,y position
- maixmal size of the text to draw with width and height
- color of the triangle to draw
*/
#define gui_ptr_add(t, p, i) ((t*)((void*)((gui_size)(p) + (i))))
#define gui_ptr_sub(t, p, i) ((t*)((void*)((gui_size)(p) - (i))))
#define gui_command(t, c) ((const struct gui_command_##t*)c)
#define gui_command_buffer_begin(b)\
((const struct gui_command*)(b)->base.memory.ptr)
#define gui_command_buffer_end(b)\
(gui_ptr_add(const struct gui_command, (b)->base.memory.ptr, (b)->base.allocated))
#define gui_command_buffer_next(b, c)\
((gui_ptr_add(const struct gui_command,c,c->offset)<gui_command_buffer_end(b))?\
gui_ptr_add(const struct gui_command,c,c->offset):NULL)
#define gui_foreach_command(i, b)\
for((i)=gui_command_buffer_begin(b); (i)!=NULL; (i)=gui_command_buffer_next(b,i))
/*
* ==============================================================
*
* Widgets
*
* ===============================================================
*/
/* WIDGETS
----------------------------
The Widget API supports a number of basic widgets like buttons, sliders or
editboxes and stores no widget state. Instead the state of each widget has to
be managed by the user.
USAGE
----------------------------
Most widgets takes an input struct, font and widget specific data and a command
buffer to push draw command into and return the updated widget state.
Important to note is that there is no actual state that is being stored inside
the toolkit so everything has to be stored byte the user.
Widget function API
gui_text -- draws a string inside a box
gui_button_text -- button widget with text content
gui_button_image -- button widget with icon content
gui_button_triangle -- button widget with triangle content
gui_toggle -- either a checkbox or radiobutton widget
gui_slider -- floating point slider widget
gui_progress -- unsigned integer progressbar widget
gui_edit -- Editbox wiget for user input
gui_edit_filtered -- Editbox with utf8 gylph filter capabilities
gui_spinner -- unsigned integer spinner widget
gui_selector -- string selector widget
gui_scroll -- scrollbar widget imeplementation
*/
/* Example */
#if 0
gui_command_buffer buffer;
void *memory = malloc(MEMORY_SIZE)
gui_buffer_init_fixed(buffer, memory, MEMORY_SIZE);
struct gui_font font = {...};
const struct gui_slider slider = {...};
const struct gui_progress progress = {...};
gui_float value = 5.0f
gui_size prog = 20;
struct gui_input input = {0};
while (1) {
gui_input_begin(&input);
/* record input */
gui_input_end(&input);
gui_command_buffer_reset(&buffer);
value = gui_slider(&buffer, 50, 50, 100, 30, 0, value, 10, 1, &slider, &input);
prog = gui_progress(&buffer, 50, 100, 100, 30, prog, 100, gui_false, &progress, &input);
const struct gui_command *cmd;
gui_foreach_command(cmd, buffer) {
/* execute draw call command */
}
}
#endif
struct gui_font {
gui_handle userdata;
/* user provided font handle */
gui_float height;
/* max height of the font */
gui_text_width_f width;
/* font string width in pixel callback */
};
enum gui_text_align {
GUI_TEXT_LEFT,
GUI_TEXT_CENTERED,
GUI_TEXT_RIGHT
};
struct gui_text {
struct gui_vec2 padding;
/* padding between bounds and text */
struct gui_color foreground;
/*text color */
struct gui_color background;
/* text background color */
};
enum gui_button_behavior {
GUI_BUTTON_DEFAULT,
/* buton only returns on activation */
GUI_BUTTON_REPEATER,
/* button returns as long as the button is pressed */
GUI_BUTTON_MAX
};
struct gui_button {
gui_float border;
/* size of the border */
gui_float rounding;
/* buttong rectangle rounding */
struct gui_vec2 padding;
/* padding between bounds and content */
struct gui_color background;
/* button color */
struct gui_color foreground;
/* button border color */
struct gui_color content;
/* button content color */
struct gui_color highlight;
/* background color if mouse is over */
struct gui_color highlight_content;
/* content color if mouse is over */
};
enum gui_toggle_type {
GUI_TOGGLE_CHECK,
/* checkbox toggle */
GUI_TOGGLE_OPTION
/* radiobutton toggle */
};
struct gui_toggle {
gui_float rounding;
/* checkbox rectangle rounding */
struct gui_vec2 padding;
/* padding between bounds and content */
struct gui_color font;
/* text color */
struct gui_color background;
/* toggle background color*/
struct gui_color foreground;
/* toggle foreground color*/
struct gui_color cursor;
/* toggle cursor color*/
};
struct gui_progress {
gui_float rounding;
/* prgressbar rectangle rounding */
struct gui_vec2 padding;
/* padding between bounds and content */
struct gui_color background;
/* progressbar background color */
struct gui_color foreground;
/* pgressbar cursor color */
};
enum gui_slider_cursor {
GUI_SLIDER_RECT,
GUI_SLIDER_CIRCLE
};
struct gui_slider {
enum gui_slider_cursor cursor;
/* slider cursor shape */
struct gui_vec2 padding;
/* padding between bounds and content */
struct gui_color bar;
/* slider background bar color */
struct gui_color border;
/* slider cursor border color */
struct gui_color bg;
/* slider background color */
struct gui_color fg;
/* slider cursor color */
};
struct gui_scroll {
gui_float rounding;
/* scrolbar rectangle rounding */
struct gui_color background;
/* scrollbar background color */
struct gui_color foreground;
/* scrollbar cursor color */
struct gui_color border;
/* scrollbar border color */
};
enum gui_input_filter {
GUI_INPUT_DEFAULT,
GUI_INPUT_ASCII,
GUI_INPUT_FLOAT,
GUI_INPUT_DEC,
GUI_INPUT_HEX,
GUI_INPUT_OCT,
GUI_INPUT_BIN
};
struct gui_edit {
gui_float border_size;
/* editbox border line size */
gui_float rounding;
/* editbox rectangle rounding */
struct gui_vec2 padding;
/* padding between bounds and content*/
gui_bool show_cursor;
/* flag indication if the cursor should be drawn */
struct gui_color background;
/* editbox background */
struct gui_color border;
/* editbox border color */
struct gui_color cursor;
/* editbox cursor color */
struct gui_color text;
/* editbox text color */
};
struct gui_spinner {
gui_size border_button;
/* border line width */
struct gui_color button_color;
/* spinner button background color */
struct gui_color button_border;
/* spinner button border color */
struct gui_color button_triangle;
/* spinner up and down button triangle color */
struct gui_color color;
/* spinner background color */
struct gui_color border;
/* spinner border color */
struct gui_color text;
/* spinner text color */
struct gui_vec2 padding;
/* spinner padding between bounds and content*/
gui_bool show_cursor;
/* flag indicating if the cursor should be drawn */
};
struct gui_selector {
gui_size border_button;
/* border line width */
struct gui_color button_color;
/* selector button background color */
struct gui_color button_border;
/* selector button border color */
struct gui_color button_triangle;
/* selector button content color */
struct gui_color color;
/* spinner background color */
struct gui_color border;
/* spinner border color */
struct gui_color text;
/* spinner text color */
struct gui_color text_bg;
/* spinner text background color */
struct gui_vec2 padding;
/* padding between bounds and content*/
};
GUI_API void gui_text(struct gui_command_buffer*, gui_float, gui_float, gui_float, gui_float,
const char *text, gui_size len, const struct gui_text*, enum gui_text_align,
const struct gui_font*);
/* this function execute a text widget with alignment
Input:
- output command buffer for draw commands
- (x,y) coordinates for the button bounds
- (width, height) size for the button bounds
- string to draw
- length of the string
- visual widget style structure describing the text
- text alignment with either left, center and right
- font structure for text drawing
*/
GUI_API gui_bool gui_button_text(struct gui_command_buffer*, gui_float x, gui_float y,
gui_float w, gui_float h, const char*, enum gui_button_behavior,
const struct gui_button*, const struct gui_input*,
const struct gui_font*);
/* this function executes a text button widget
Input:
- output command buffer for draw commands
- (x,y) coordinates for the button bounds
- (width, height) size for the button bounds
- button text
- button behavior with either repeating or transition state event
- visual widget style structure describing the button
- input structure to update the button with
- font structure for text drawing
Output:
- returns gui_true if the button was pressed gui_false otherwise
*/
GUI_API gui_bool gui_button_image(struct gui_command_buffer*, gui_float x, gui_float y,
gui_float w, gui_float h, gui_handle img, enum gui_button_behavior,
const struct gui_button*, const struct gui_input*);
/* this function executes a image button widget
Input:
- output command buffer for draw commands
- (x,y) coordinates for the button bounds
- (width, height) size for the button bounds
- user provided image handle which is either a pointer or a id
- button behavior with either repeating or transition state event
- visual widget style structure describing the button
- input structure to update the button with
Output:
- returns gui_true if the button was pressed gui_false otherwise
*/
GUI_API gui_bool gui_button_triangle(struct gui_command_buffer*, gui_float x, gui_float y,
gui_float w, gui_float h, enum gui_heading,
enum gui_button_behavior, const struct gui_button*,
const struct gui_input*);
/* this function executes a triangle button widget
Input:
- output command buffer for draw commands
- (x,y) coordinates for the button bounds
- (width, height) size for the button bounds
- triangle direction with either left, top, right xor bottom
- button behavior with either repeating or transition state event
- visual widget style structure describing the button
- input structure to update the button with
Output:
- returns gui_true if the button was pressed gui_false otherwise
*/
GUI_API gui_bool gui_toggle(struct gui_command_buffer*, gui_float x, gui_float y, gui_float w,
gui_float h, gui_bool, const char*, enum gui_toggle_type,
const struct gui_toggle*, const struct gui_input*, const struct gui_font*);
/* this function executes a toggle (checkbox, radiobutton) widget
Input:
- output command buffer for draw commands
- (x,y) coordinates for the button bounds
- (width, height) size for the button bounds
- active or inactive flag describing the state of the toggle
- visual widget style structure describing the toggle
- input structure to update the toggle with
- font structure for text drawing
Output:
- returns the update state of the toggle
*/
GUI_API gui_float gui_slider(struct gui_command_buffer*, gui_float x, gui_float y, gui_float,
gui_float h, gui_float min, gui_float val, gui_float max, gui_float step,
const struct gui_slider*, const struct gui_input*);
/* this function executes a slider widget
Input:
- output command buffer for draw commands
- (x,y) coordinates for the button bounds
- (width, height) size for the button bounds
- minimal slider value that will not be underflown
- slider value to be updated by the user
- maximal slider value that will not be overflown
- step interval the value will be updated with
- visual widget style structure describing the slider
- input structure to update the slider with
Output:
- returns the from the user input updated value
*/
GUI_API gui_size gui_progress(struct gui_command_buffer*, gui_float x, gui_float y,
gui_float w, gui_float h, gui_size value, gui_size max,
gui_bool modifyable, const struct gui_progress*,
const struct gui_input*);
/* this function executes a progressbar widget
Input:
- output command buffer for draw commands
- (x,y) coordinates for the button bounds
- (width, height) size for the button bounds
- progressbar value to be updated by the user
- maximal progressbar value that will not be overflown
- flag if the progressbar is modifyable by the user
- visual widget style structure describing the progressbar
- input structure to update the slider with
Output:
- returns the from the user input updated value
*/
GUI_API gui_size gui_edit(struct gui_command_buffer*, gui_float x, gui_float y, gui_float w,
gui_float h, gui_char*, gui_size, gui_size max, gui_bool*,
const struct gui_edit*, enum gui_input_filter filter,
const struct gui_input*, const struct gui_font*);
/* this function executes a editbox widget
Input:
- output command buffer for draw commands
- (x,y) coordinates for the button bounds
- (width, height) size for the button bounds
- char buffer to add or remove glyphes from/to
- buffer text length in bytes
- maximal buffer size
- current state of the editbox with either active or inactive
- visual widget style structure describing the editbox
- glyph input filter type to only let specified glyph through
- input structure to update the editbox with
- font structure for text drawing
Output:
- state of the editbox with either active or inactive
- returns the size of the buffer in bytes after the modification
*/
GUI_API gui_size gui_edit_filtered(struct gui_command_buffer*, gui_float x, gui_float y,
gui_float w, gui_float h, gui_char*, gui_size,
gui_size max, gui_bool*, const struct gui_edit*,
gui_filter filter, const struct gui_input*,
const struct gui_font*);
/* this function executes a editbox widget
Input:
- output command buffer for draw commands
- (x,y) coordinates for the button bounds
- (width, height) size for the button bounds
- char buffer to add or remove glyphes from/to
- buffer text length in bytes
- maximal buffer size
- current state of the editbox with either active or inactive
- visual widget style structure describing the editbox
- glyph input filter callback to only let specified glyph through
- input structure to update the editbox with
- font structure for text drawing
Output:
- state of the editbox with either active or inactive
- returns the size of the buffer in bytes after the modification
*/
GUI_API gui_int gui_spinner(struct gui_command_buffer*, gui_float x, gui_float y, gui_float w,
gui_float h, const struct gui_spinner*, gui_int min, gui_int value,
gui_int max, gui_int step, gui_bool *active, const struct gui_input*,
const struct gui_font*);
/* this function executes a spinner widget
Input:
- output command buffer for draw commands
- (x,y) coordinates for the button bounds
- (width, height) size for the button bounds
- visual widget style structure describing the spinner
- minimal spinner value that will no be underflown
- spinner value that will be updated
- maximal spinner value that will no be overflown
- spinner input state with either active or inactive
- input structure to update the slider with
- font structure for text drawing
Output:
- returns the from the user input updated spinner value
*/
GUI_API gui_size gui_selector(struct gui_command_buffer*, gui_float x, gui_float y,
gui_float w, gui_float h, const struct gui_selector*,
const char *items[], gui_size item_count,
gui_size item_current, const struct gui_input*,
const struct gui_font*);
/* this function executes a selector widget
Input:
- output command buffer for draw commands
- (x,y) coordinates for the button bounds
- (width, height) size for the button bounds
- visual widget style structure describing the selector
- selection of string array to select from
- size of the selection array
- input structure to update the slider with
- font structure for text drawing
Output:
- returns the from the user input updated spinner value
*/
GUI_API gui_float gui_scroll(struct gui_command_buffer*, gui_float x, gui_float y,
gui_float w, gui_float h, gui_float offset, gui_float target,
gui_float step, const struct gui_scroll*, const struct gui_input*);
/* this function executes a scrollbar widget
Input:
- output command buffer for draw commands
- (x,y) coordinates for the button bounds
- (width, height) size for the button bounds
- scrollbar offset in source pixel
- destination pixel size
- step pixel size if the scrollbar up- or down button is pressed
- visual widget style structure describing the selector
- input structure to update the slider with
Output:
- returns the from the user input updated scrollbar offset in pixels
*/
/*
* ==============================================================
*
* Config
*
* ===============================================================
*/
/* CONFIG
----------------------------
The panel configuration consists of style and color information that is used for
the general style and looks of panel. In addition for temporary modification
the configuration structure consists of a stack for pushing and pop either
color or property values.
USAGE
----------------------------
To use the configuration file you either initialize every value yourself besides
the internal stack which needs to be initialized to zero or use the default
configuration by calling the function gui_config_default.
To add and remove temporary configuration states the gui_config_push_xxxx
for adding and gui_config_pop_xxxx for removing either color or property values
from the stack. To reset all previously modified values the gui_config_reset_xxx
were added.
Configuration function API
gui_config_default -- initializes a default panel configuration
gui_config_property -- returns the property value from an id
gui_config_color -- returns the color value from an id
gui_config_push_property -- push an old property onto a interal stack and sets a new value
gui_config_push_color -- push an old color onto a internal stack and sets a new value
gui_config_pop_color -- resets an old color value from the internal stack
gui_config_pop_property -- resets an old property value from the internal stack
gui_config_reset_colors -- reverts back all temporary color changes from the config
gui_config_reset_properties -- reverts back all temporary property changes from the config
gui_config_reset -- reverts back all temporary all changes from the config
*/
enum gui_config_colors {
GUI_COLOR_TEXT,
GUI_COLOR_PANEL,
GUI_COLOR_HEADER,
GUI_COLOR_BORDER,
GUI_COLOR_BUTTON,
GUI_COLOR_BUTTON_BORDER,
GUI_COLOR_BUTTON_HOVER,
GUI_COLOR_BUTTON_TOGGLE,
GUI_COLOR_BUTTON_HOVER_FONT,
GUI_COLOR_CHECK,
GUI_COLOR_CHECK_BACKGROUND,
GUI_COLOR_CHECK_ACTIVE,
GUI_COLOR_OPTION,
GUI_COLOR_OPTION_BACKGROUND,
GUI_COLOR_OPTION_ACTIVE,
GUI_COLOR_SLIDER,
GUI_COLOR_SLIDER_BAR,
GUI_COLOR_SLIDER_BORDER,
GUI_COLOR_SLIDER_CURSOR,
GUI_COLOR_PROGRESS,
GUI_COLOR_PROGRESS_CURSOR,
GUI_COLOR_INPUT,
GUI_COLOR_INPUT_CURSOR,
GUI_COLOR_INPUT_BORDER,
GUI_COLOR_INPUT_TEXT,
GUI_COLOR_SPINNER,
GUI_COLOR_SPINNER_BORDER,
GUI_COLOR_SPINNER_TRIANGLE,
GUI_COLOR_SPINNER_TEXT,
GUI_COLOR_SELECTOR,
GUI_COLOR_SELECTOR_BORDER,
GUI_COLOR_SELECTOR_TRIANGLE,
GUI_COLOR_SELECTOR_TEXT,
GUI_COLOR_HISTO,
GUI_COLOR_HISTO_BARS,
GUI_COLOR_HISTO_NEGATIVE,
GUI_COLOR_HISTO_HIGHLIGHT,
GUI_COLOR_PLOT,
GUI_COLOR_PLOT_LINES,
GUI_COLOR_PLOT_HIGHLIGHT,
GUI_COLOR_SCROLLBAR,
GUI_COLOR_SCROLLBAR_CURSOR,
GUI_COLOR_SCROLLBAR_BORDER,
GUI_COLOR_TABLE_LINES,
GUI_COLOR_SHELF,
GUI_COLOR_SHELF_TEXT,
GUI_COLOR_SHELF_ACTIVE,
GUI_COLOR_SHELF_ACTIVE_TEXT,
GUI_COLOR_SCALER,
GUI_COLOR_COUNT
};
enum gui_config_rounding {
GUI_ROUNDING_PANEL,
GUI_ROUNDING_BUTTON,
GUI_ROUNDING_CHECK,
GUI_ROUNDING_PROGRESS,
GUI_ROUNDING_INPUT,
GUI_ROUNDING_GRAPH,
GUI_ROUNDING_SCROLLBAR,
GUI_ROUNDING_MAX
};
enum gui_config_properties {
GUI_PROPERTY_ITEM_SPACING,
GUI_PROPERTY_ITEM_PADDING,
GUI_PROPERTY_PADDING,
GUI_PROPERTY_SCALER_SIZE,
GUI_PROPERTY_SCROLLBAR_WIDTH,
GUI_PROPERTY_SIZE,
GUI_PROPERTY_MAX
};
struct gui_saved_property {
enum gui_config_properties type;
/* identifier of the current modified property */
struct gui_vec2 value;
/* property value that has been saveed */
};
struct gui_saved_color {
enum gui_config_colors type;
/* identifier of the current modified color */
struct gui_color value;
/* color value that has been saveed */
};
enum gui_config_components {
GUI_DEFAULT_COLOR = 0x01,
GUI_DEFAULT_PROPERTIES = 0x02,
GUI_DEFAULT_ROUNDING = 0x04,
GUI_DEFAULT_ALL = 0xFFFF
};
struct gui_config_stack {
gui_size property;
/* current property stack pushing index */
struct gui_saved_property properties[GUI_MAX_ATTRIB_STACK];
/* saved property stack */
struct gui_saved_color colors[GUI_MAX_COLOR_STACK];
/* saved color stack */
gui_size color;
/* current color stack pushing index */
};
struct gui_config {
struct gui_font font;
/* the from the user provided font */
enum gui_slider_cursor slider_cursor;
/* slider cursor type identifier */
gui_float rounding[GUI_ROUNDING_MAX];
/* rectangle widget rounding */
struct gui_vec2 properties[GUI_PROPERTY_MAX];
/* configuration properties to modify the panel style */
struct gui_color colors[GUI_COLOR_COUNT];
/* configuration color to modify the panel color */
struct gui_config_stack stack;
/* modification stack */
};
GUI_API void gui_config_default(struct gui_config*, gui_flags, const struct gui_font*);
GUI_API struct gui_vec2 gui_config_property(const struct gui_config*,
enum gui_config_properties);
GUI_API struct gui_color gui_config_color(const struct gui_config*, enum gui_config_colors);
GUI_API void gui_config_push_property(struct gui_config*, enum gui_config_properties,
gui_float, gui_float);
GUI_API void gui_config_push_color(struct gui_config*, enum gui_config_colors,
gui_byte, gui_byte, gui_byte, gui_byte);
GUI_API void gui_config_pop_color(struct gui_config*);
GUI_API void gui_config_pop_property(struct gui_config*);
GUI_API void gui_config_reset_colors(struct gui_config*);
GUI_API void gui_config_reset_properties(struct gui_config*);
GUI_API void gui_config_reset(struct gui_config*);
/*
* ==============================================================
*
* Panel
*
* ===============================================================
*/
/* PANEL
----------------------------
USAGE
----------------------------
Panel function API
gui_panel_init -- initializes the panel with position, size and flags
gui_panel_begin -- begin sequence point in the panel layout build up process
gui_panel_begin_stacked -- extends gui_panel_begin by adding the panel into a panel stack
gui_panel_begin_tiled -- extends gui_panel_begin by adding the pnale into a tiled layout
gui_panel_row -- defines the current row layout with row height and number of columns
gui_panel_widget -- base function for all widgets to allocate space on the panel and check if valid
gui_panel_spacing -- create a column seperator and is basically an empty widget filler
gui_panel_text -- text widget for printing text with length
gui_panel_text_colored -- colored text widget for printing colored text width length
gui_panel_label -- text widget for printing zero terminated strings
gui_panel_label_colored -- text wicget for printing colored zero terminiated strings
gui_panel_check -- add a checkbox widget with either active or inactive state
gui_panel_option -- radiobutton widget with either active or inactive state
gui_panel_option_group -- radiobutton group with automates the process of having only one active
gui_panel_button_text -- button widget with text content
gui_panel_button_color -- colored button widget without content
gui_panel_button_triangle --button with triangle pointing either up-/down-/left- or right
gui_panel_button_image -- button widget width icon content
gui_panel_button_toggle -- toggle button with either active or inactive state
gui_panel_slider -- slider widget with min and max value as well as stepping range
gui_panel_progress -- either modifyable or static progressbar
gui_panel_edit -- edit textbox widget for text input
gui_panel_edit_filtered -- edit textbox widget for text input with filter input
gui_panel_spinner -- spinner widget with either keyboard or mouse modification
gui_panel_selector -- selector widget for combobox like selection of types
gui_panel_graph_begin -- immediate mode graph building begin sequence point
gui_panel_graph_push -- push a value into a graph
gui_panel_graph_end -- immediate mode graph building end sequence point
gui_panel_graph -- retained mode graph with array of values
gui_panel_graph_ex -- ratained mode graph with getter callback
gui_panel_end -- end squeunce point which finializes the panel build up
*/
enum gui_table_lines {
GUI_TABLE_HHEADER = 0x01,
GUI_TABLE_VHEADER = 0x02,
GUI_TABLE_HBODY = 0x04,
GUI_TABLE_VBODY = 0x08
};
enum gui_graph_type {
GUI_GRAPH_LINES,
GUI_GRAPH_COLUMN,
GUI_GRAPH_MAX
};
struct gui_graph {
gui_bool valid;
enum gui_graph_type type;
gui_float x, y;
gui_float w, h;
gui_float min, max;
struct gui_vec2 last;
gui_size index;
gui_size count;
};
enum gui_panel_tab {
GUI_MAXIMIZED = gui_false,
GUI_MINIMIZED = gui_true
};
enum gui_panel_flags {
GUI_PANEL_HIDDEN = 0x01,
GUI_PANEL_BORDER = 0x02,
GUI_PANEL_MINIMIZABLE = 0x04,
GUI_PANEL_CLOSEABLE = 0x08,
GUI_PANEL_MOVEABLE = 0x10,
GUI_PANEL_SCALEABLE = 0x20,
GUI_PANEL_NO_HEADER = 0x40,
GUI_PANEL_BORDER_HEADER = 0x80,
/* internal */
GUI_PANEL_ACTIVE = 0x100,
GUI_PANEL_SCROLLBAR = 0x200,
GUI_PANEL_TAB = 0x400
};
struct gui_panel {
gui_float x, y;
gui_float w, h;
gui_flags flags;
gui_float offset;
gui_bool minimized;
const struct gui_config *config;
struct gui_command_buffer *buffer;
struct gui_panel* next;
struct gui_panel* prev;
};
struct gui_panel_layout {
gui_float x, y, w, h;
gui_float offset;
gui_bool is_table;
gui_flags tbl_flags;
gui_bool valid;
gui_float at_x;
gui_float at_y;
gui_size index;
gui_float width, height;
gui_float header_height;
gui_float row_height;
gui_size row_columns;
struct gui_rect clip;
const struct gui_config *config;
const struct gui_input *input;
struct gui_command_buffer *buffer;
};
/* Panel */
struct gui_stack;
struct gui_layout;
GUI_API void gui_panel_init(struct gui_panel*, gui_float x, gui_float y, gui_float w,
gui_float h, gui_flags, struct gui_command_buffer*,const struct gui_config*);
GUI_API gui_bool gui_panel_begin(struct gui_panel_layout *layout, struct gui_panel*,
const char *title, const struct gui_input*);
GUI_API gui_bool gui_panel_begin_stacked(struct gui_panel_layout*, struct gui_panel*,
struct gui_stack*, const char*, const struct gui_input*);
GUI_API gui_bool gui_panel_begin_tiled(struct gui_panel_layout*, struct gui_panel*,
struct gui_layout*, gui_uint slot, gui_size index,
const char*, const struct gui_input*);
GUI_API gui_size gui_panel_row_columns(const struct gui_panel_layout *layout,
gui_size widget_size);
GUI_API void gui_panel_row(struct gui_panel_layout*, gui_float height, gui_size cols);
GUI_API gui_bool gui_panel_widget(struct gui_rect*, struct gui_panel_layout*);
GUI_API void gui_panel_spacing(struct gui_panel_layout*, gui_size cols);
GUI_API void gui_panel_text(struct gui_panel_layout*, const char*, gui_size,
enum gui_text_align);
GUI_API void gui_panel_text_colored(struct gui_panel_layout*, const char*, gui_size,
enum gui_text_align, struct gui_color);
GUI_API void gui_panel_label(struct gui_panel_layout*, const char*, enum gui_text_align);
GUI_API void gui_panel_label_colored(struct gui_panel_layout*, const char*,
enum gui_text_align, struct gui_color);
GUI_API gui_bool gui_panel_check(struct gui_panel_layout*, const char*, gui_bool active);
GUI_API gui_bool gui_panel_option(struct gui_panel_layout*, const char*, gui_bool active);
GUI_API gui_size gui_panel_option_group(struct gui_panel_layout*, const char**,
gui_size cnt, gui_size cur);
GUI_API gui_bool gui_panel_button_text(struct gui_panel_layout*, const char*,
enum gui_button_behavior);
GUI_API gui_bool gui_panel_button_color(struct gui_panel_layout*, struct gui_color,
enum gui_button_behavior);
GUI_API gui_bool gui_panel_button_triangle(struct gui_panel_layout*, enum gui_heading,
enum gui_button_behavior);
GUI_API gui_bool gui_panel_button_image(struct gui_panel_layout*, gui_handle img,
enum gui_button_behavior);
GUI_API gui_bool gui_panel_button_toggle(struct gui_panel_layout*, const char*,gui_bool value);
GUI_API gui_float gui_panel_slider(struct gui_panel_layout*, gui_float min, gui_float val,
gui_float max, gui_float step);
GUI_API gui_size gui_panel_progress(struct gui_panel_layout*, gui_size cur, gui_size max,
gui_bool modifyable);
GUI_API gui_size gui_panel_edit(struct gui_panel_layout*, gui_char *buffer, gui_size len,
gui_size max, gui_bool *active, enum gui_input_filter);
GUI_API gui_size gui_panel_edit_filtered(struct gui_panel_layout*, gui_char *buffer,
gui_size len, gui_size max, gui_bool *active, gui_filter);
GUI_API gui_int gui_panel_spinner(struct gui_panel_layout*, gui_int min, gui_int value,
gui_int max, gui_int step, gui_bool *active);
GUI_API gui_size gui_panel_selector(struct gui_panel_layout*, const char *items[],
gui_size item_count, gui_size item_current);
GUI_API void gui_panel_graph_begin(struct gui_panel_layout*, struct gui_graph*,
enum gui_graph_type, gui_size count, gui_float min, gui_float max);
GUI_API gui_bool gui_panel_graph_push(struct gui_panel_layout*,struct gui_graph*,gui_float);
GUI_API void gui_panel_graph_end(struct gui_panel_layout *layout, struct gui_graph*);
GUI_API gui_int gui_panel_graph(struct gui_panel_layout*, enum gui_graph_type,
const gui_float *values, gui_size count, gui_size offset);
GUI_API gui_int gui_panel_graph_ex(struct gui_panel_layout*, enum gui_graph_type,
gui_size count, gui_float(*get_value)(void*, gui_size), void *userdata);
GUI_API void gui_panel_table_begin(struct gui_panel_layout*, gui_flags flags,
gui_size row_height, gui_size cols);
GUI_API void gui_panel_table_row(struct gui_panel_layout*);
GUI_API void gui_panel_table_end(struct gui_panel_layout*);
GUI_API gui_bool gui_panel_tab_begin(struct gui_panel_layout*, struct gui_panel_layout *tab,
const char*, gui_bool);
GUI_API void gui_panel_tab_end(struct gui_panel_layout*, struct gui_panel_layout *tab);
GUI_API void gui_panel_group_begin(struct gui_panel_layout*, struct gui_panel_layout *tab,
const char*, gui_float offset);
GUI_API gui_float gui_panel_group_end(struct gui_panel_layout*, struct gui_panel_layout* tab);
GUI_API gui_size gui_panel_shelf_begin(struct gui_panel_layout*, struct gui_panel_layout*,
const char *tabs[], gui_size size, gui_size active, gui_float offset);
GUI_API gui_float gui_panel_shelf_end(struct gui_panel_layout*, struct gui_panel_layout*);
GUI_API void gui_panel_end(struct gui_panel_layout*, struct gui_panel*);
/*
* ==============================================================
*
* Stack
*
* ===============================================================
*/
struct gui_stack {
gui_size count;
struct gui_panel *begin;
struct gui_panel *end;
};
GUI_API void gui_stack_clear(struct gui_stack*);
GUI_API void gui_stack_push(struct gui_stack*, struct gui_panel*);
GUI_API void gui_stack_pop(struct gui_stack*, struct gui_panel*);
#define gui_foreach_panel(i, s) for (i = (s)->begin; i != NULL; i = (i)->next)
/*
* ==============================================================
*
* Layout
*
* ===============================================================
*/
enum gui_layout_state {
GUI_LAYOUT_INACTIVE,
GUI_LAYOUT_ACTIVE
};
enum gui_layout_slot_index {
GUI_SLOT_TOP,
GUI_SLOT_BOTTOM,
GUI_SLOT_LEFT,
GUI_SLOT_CENTER,
GUI_SLOT_RIGHT,
GUI_SLOT_MAX
};
enum gui_layout_format {
GUI_LAYOUT_HORIZONTAL,
GUI_LAYOUT_VERTICAL
};
struct gui_layout_config {
gui_float left;
gui_float right;
gui_float centerh;
gui_float centerv;
gui_float bottom;
gui_float top;
};
struct gui_layout_slot {
gui_size capacity;
struct gui_vec2 ratio;
struct gui_vec2 offset;
enum gui_layout_format format;
};
struct gui_layout {
gui_flags flags;
gui_size width, height;
enum gui_layout_state state;
struct gui_stack stack;
struct gui_layout_slot slots[GUI_SLOT_MAX];
};
GUI_API void gui_layout_init(struct gui_layout*, const struct gui_layout_config*,
gui_size width, gui_size height);
GUI_API void gui_layout_set_size(struct gui_layout*, gui_size width, gui_size height);
GUI_API void gui_layout_slot(struct gui_layout*, enum gui_layout_slot_index,
enum gui_layout_format, gui_size panel_count);
#ifdef GUI_IMPLEMENTATION
#ifndef GUI_ASSERT
#define GUI_ASSERT(expr)
#endif
#ifndef NULL
#define NULL ((void*)0)
#endif
#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif
#ifndef MAX
#define MAX(a,b) ((a) < (b) ? (b) : (a))
#endif
#ifndef CLAMP
#define CLAMP(i,v,x) (MAX(MIN(v,x), i))
#endif
#define GUI_UTF_INVALID 0xFFFD
#define GUI_MAX_NUMBER_BUFFER 64
#define GUI_SATURATE(x) (MAX(0, MIN(1.0f, x)))
#define GUI_LEN(a) (sizeof(a)/sizeof(a)[0])
#define GUI_ABS(a) (((a) < 0) ? -(a) : (a))
#define GUI_BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b))
#define GUI_INBOX(px, py, x, y, w, h) (GUI_BETWEEN(px, x, x+w) && GUI_BETWEEN(py, y, y+h))
#define GUI_INTERSECT(x0, y0, w0, h0, x1, y1, w1, h1) \
(!(((x1 > (x0 + w0)) || ((x1 + w1) < x0) || (y1 > (y0 + h0)) || (y1 + h1) < y0)))
#define gui_vec2_mov(to,from) (to).x = (from).x, (to).y = (from).y
#define gui_vec2_sub(r,a,b) do {(r).x=(a).x-(b).x; (r).y=(a).y-(b).y;} while(0)
#define gui_color_to_array(ar, c)\
(ar)[0] = (c).r, (ar)[1] = (c).g, (ar)[2] = (c).b, (ar)[3] = (c).a
#define GUI_ALIGNOF(t) ((char*)(&((struct {char c; t _h;}*)0)->_h) - (char*)0)
#define GUI_ALIGN_PTR(x, mask) (void*)((gui_size)((gui_byte*)(x) + (mask-1)) & ~(mask-1))
#define GUI_ALIGN(x, mask) ((x) + (mask-1)) & ~(mask-1)
#define GUI_OFFSETOF(st, m) ((gui_size)(&((st *)0)->m))
static const struct gui_rect gui_null_rect = {-9999.0f, 9999.0f, 9999.0f, 9999.0f};
static const gui_byte gui_utfbyte[GUI_UTF_SIZE+1] = {0x80, 0, 0xC0, 0xE0, 0xF0};
static const gui_byte gui_utfmask[GUI_UTF_SIZE+1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8};
static const long gui_utfmin[GUI_UTF_SIZE+1] = {0, 0, 0x80, 0x800, 0x100000};
static const long gui_utfmax[GUI_UTF_SIZE+1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF};
/*
* ==============================================================
*
* Utility
*
* ===============================================================
*/
GUI_API struct gui_color
gui_rgba(gui_byte r, gui_byte g, gui_byte b, gui_byte a)
{
struct gui_color ret;
ret.r = r; ret.g = g;
ret.b = b; ret.a = a;
return ret;
}
GUI_API struct gui_color
gui_rgb(gui_byte r, gui_byte g, gui_byte b)
{
struct gui_color ret;
ret.r = r; ret.g = g;
ret.b = b; ret.a = 255;
return ret;
}
static struct gui_vec2
gui_vec2(gui_float x, gui_float y)
{
struct gui_vec2 ret;
ret.x = x; ret.y = y;
return ret;
}
static void*
gui_memcopy(void *dst, const void *src, gui_size size)
{
gui_size i = 0;
char *d = dst;
const char *s = src;
for (i = 0; i < size; ++i)
d[i] = s[i];
return dst;
}
static void
gui_zero(void *dst, gui_size size)
{
gui_size i;
char *d = dst;
for (i = 0; i < size; ++i) d[i] = 0;
}
static gui_size
gui_strsiz(const char *str)
{
gui_size siz = 0;
while (str && *str++ != '\0') siz++;
return siz;
}
static gui_int
gui_strtoi(gui_int *number, const char *buffer, gui_size len)
{
gui_size i;
if (!number || !buffer)
return 0;
*number = 0;
for (i = 0; i < len; ++i)
*number = *number * 10 + (buffer[i] - '0');
return 1;
}
static gui_size
gui_itos(char *buffer, gui_int num)
{
static const char digit[] = "0123456789";
gui_int shifter;
gui_size len = 0;
char *p = buffer;
if (!buffer)
return 0;
if (num < 0) {
num = GUI_ABS(num);
*p++ = '-';
}
shifter = num;
do {
++p;
shifter = shifter/10;
} while (shifter);
*p = '\0';
len = (gui_size)(p - buffer);
do {
*--p = digit[num % 10];
num = num / 10;
} while (num);
return len;
}
static void
gui_unify(struct gui_rect *clip, const struct gui_rect *a, gui_float x0, gui_float y0,
gui_float x1, gui_float y1)
{
clip->x = MAX(a->x, x0) - 1;
clip->y = MAX(a->y, y0) - 1;
clip->w = MIN(a->x + a->w, x1) - clip->x+ 2;
clip->h = MIN(a->y + a->h, y1) - clip->y + 2;
clip->w = MAX(0, clip->w);
clip->h = MAX(0, clip->h);
}
static void
gui_triangle_from_direction(struct gui_vec2 *result, gui_float x, gui_float y,
gui_float w, gui_float h, gui_float pad_x, gui_float pad_y,
enum gui_heading direction)
{
gui_float w_half, h_half;
GUI_ASSERT(result);
w = MAX(4 * pad_x, w);
h = MAX(4 * pad_y, h);
w = w - 2 * pad_x;
h = h - 2 * pad_y;
x = x + pad_x;
y = y + pad_y;
w_half = w / 2.0f;
h_half = h / 2.0f;
if (direction == GUI_UP) {
result[0] = gui_vec2(x + w_half, y);
result[1] = gui_vec2(x, y + h);
result[2] = gui_vec2(x + w, y + h);
} else if (direction == GUI_RIGHT) {
result[0] = gui_vec2(x, y);
result[1] = gui_vec2(x, y + h);
result[2] = gui_vec2(x + w, y + h_half);
} else if (direction == GUI_DOWN) {
result[0] = gui_vec2(x, y);
result[1] = gui_vec2(x + w_half, y + h);
result[2] = gui_vec2(x + w, y);
} else {
result[0] = gui_vec2(x, y + h_half);
result[1] = gui_vec2(x + w, y + h);
result[2] = gui_vec2(x + w, y);
}
}
/*
* ==============================================================
*
* Input
*
* ===============================================================
*/
static gui_size
gui_utf_validate(long *u, gui_size i)
{
if (!u) return 0;
if (!GUI_BETWEEN(*u, gui_utfmin[i], gui_utfmax[i]) ||
GUI_BETWEEN(*u, 0xD800, 0xDFFF))
*u = GUI_UTF_INVALID;
for (i = 1; *u > gui_utfmax[i]; ++i);
return i;
}
static gui_long
gui_utf_decode_byte(gui_char c, gui_size *i)
{
if (!i) return 0;
for(*i = 0; *i < GUI_LEN(gui_utfmask); ++(*i)) {
if ((c & gui_utfmask[*i]) == gui_utfbyte[*i])
return c & ~gui_utfmask[*i];
}
return 0;
}
static gui_size
gui_utf_decode(const gui_char *c, gui_long *u, gui_size clen)
{
gui_size i, j, len, type=0;
gui_long udecoded;
*u = GUI_UTF_INVALID;
if (!c || !u) return 0;
if (!clen) return 0;
udecoded = gui_utf_decode_byte(c[0], &len);
if (!GUI_BETWEEN(len, 1, GUI_UTF_SIZE))
return 1;
for (i = 1, j = 1; i < clen && j < len; ++i, ++j) {
udecoded = (udecoded << 6) | gui_utf_decode_byte(c[i], &type);
if (type != 0)
return j;
}
if (j < len)
return 0;
*u = udecoded;
gui_utf_validate(u, len);
return len;
}
static gui_char
gui_utf_encode_byte(gui_long u, gui_size i)
{
return (gui_char)(gui_utfbyte[i] | (u & ~gui_utfmask[i]));
}
static gui_size
gui_utf_encode(gui_long u, gui_char *c, gui_size clen)
{
gui_size len, i;
len = gui_utf_validate(&u, 0);
if (clen < len || !len)
return 0;
for (i = len - 1; i != 0; --i) {
c[i] = gui_utf_encode_byte(u, 0);
u >>= 6;
}
c[0] = gui_utf_encode_byte(u, len);
return len;
}
void
gui_input_begin(struct gui_input *in)
{
gui_size i;
GUI_ASSERT(in);
if (!in) return;
in->mouse_clicked = 0;
in->text_len = 0;
gui_vec2_mov(in->mouse_prev, in->mouse_pos);
for (i = 0; i < GUI_KEY_MAX; i++)
in->keys[i].clicked = 0;
}
void
gui_input_motion(struct gui_input *in, gui_int x, gui_int y)
{
GUI_ASSERT(in);
if (!in) return;
in->mouse_pos.x = (gui_float)x;
in->mouse_pos.y = (gui_float)y;
}
void
gui_input_key(struct gui_input *in, enum gui_keys key, gui_bool down)
{
GUI_ASSERT(in);
if (!in) return;
if (in->keys[key].down == down) return;
in->keys[key].down = down;
in->keys[key].clicked++;
}
void
gui_input_button(struct gui_input *in, gui_int x, gui_int y, gui_bool down)
{
GUI_ASSERT(in);
if (!in) return;
if (in->mouse_down == down) return;
in->mouse_clicked_pos.x = (gui_float)x;
in->mouse_clicked_pos.y = (gui_float)y;
in->mouse_down = down;
in->mouse_clicked++;
}
void
gui_input_char(struct gui_input *in, const gui_glyph glyph)
{
gui_size len = 0;
gui_long unicode;
GUI_ASSERT(in);
if (!in) return;
len = gui_utf_decode(glyph, &unicode, GUI_UTF_SIZE);
if (len && ((in->text_len + len) < GUI_INPUT_MAX)) {
gui_utf_encode(unicode, &in->text[in->text_len], GUI_INPUT_MAX - in->text_len);
in->text_len += len;
}
}
void
gui_input_end(struct gui_input *in)
{
GUI_ASSERT(in);
if (!in) return;
gui_vec2_sub(in->mouse_delta, in->mouse_pos, in->mouse_prev);
}
/*
* ==============================================================
*
* Buffer
*
* ===============================================================
*/
void
gui_buffer_init(struct gui_buffer *b, const struct gui_allocator *a,
gui_size initial_size, gui_float grow_factor)
{
GUI_ASSERT(buffer);
GUI_ASSERT(memory);
GUI_ASSERT(initial_size);
if (!b || !a || !initial_size) return;
gui_zero(b, sizeof(*b));
b->type = GUI_BUFFER_DYNAMIC;
b->memory.ptr = a->alloc(a->userdata, initial_size);
b->memory.size = initial_size;
b->grow_factor = grow_factor;
b->pool = *a;
}
void
gui_buffer_init_fixed(struct gui_buffer *b, void *m, gui_size size)
{
GUI_ASSERT(b);
GUI_ASSERT(m);
GUI_ASSERT(size);
if (!b || !m || !size) return;
gui_zero(b, sizeof(*b));
b->type = GUI_BUFFER_FIXED;
b->memory.ptr = m;
b->memory.size = size;
}
void*
gui_buffer_alloc(struct gui_buffer *b, gui_size size, gui_size align)
{
gui_size cap;
gui_size alignment;
void *unaligned;
void *memory;
GUI_ASSERT(b);
if (!b || !size) return NULL;
b->needed += size;
unaligned = gui_ptr_add(void, b->memory.ptr, b->allocated);
memory = GUI_ALIGN_PTR(unaligned, align);
alignment = (gui_size)((gui_byte*)memory - (gui_byte*)unaligned);
if ((b->allocated + size + alignment) >= b->memory.size) {
void *temp;
if (b->type != GUI_BUFFER_DYNAMIC || !b->pool.realloc)
return NULL;
cap = (gui_size)((gui_float)b->memory.size * b->grow_factor);
temp = b->pool.realloc(b->pool.userdata, b->memory.ptr, cap);
if (!temp) return NULL;
b->memory.ptr = temp;
b->memory.size = cap;
unaligned = gui_ptr_add(gui_byte, b->memory.ptr, b->allocated);
memory = GUI_ALIGN_PTR(unaligned, align);
alignment = (gui_size)((gui_byte*)memory - (gui_byte*)unaligned);
}
b->allocated += size + alignment;
b->needed += alignment;
b->calls++;
return memory;
}
void
gui_buffer_reset(struct gui_buffer *b)
{
GUI_ASSERT(b);
if (!b) return;
b->allocated = 0;
b->calls = 0;
}
void
gui_buffer_clear(struct gui_buffer *b)
{
GUI_ASSERT(buffer);
if (!b || !b->memory.ptr) return;
if (b->type == GUI_BUFFER_FIXED) return;
if (!b->pool.free) return;
b->pool.free(b->pool.userdata, b->memory.ptr);
}
void
gui_buffer_info(struct gui_memory_status *s, struct gui_buffer *b)
{
GUI_ASSERT(b);
GUI_ASSERT(s);
if (!s || !b) return;
s->allocated = b->allocated;
s->size = b->memory.size;
s->needed = b->needed;
s->memory = b->memory.ptr;
s->calls = b->calls;
}
/*
* ==============================================================
*
* Command buffer
*
* ===============================================================
*/
void*
gui_command_buffer_push(struct gui_command_buffer* b,
enum gui_command_type t, gui_size size)
{
static const gui_size align = GUI_ALIGNOF(struct gui_command);
struct gui_command *cmd;
gui_size alignment;
void *unaligned;
void *memory;
GUI_ASSERT(b);
if (!b) return NULL;
cmd = gui_buffer_alloc(&b->base, size, align);
if (!cmd) return NULL;
unaligned = (gui_byte*)cmd + size;
memory = GUI_ALIGN_PTR(unaligned, align);
alignment = (gui_size)((gui_byte*)memory - (gui_byte*)unaligned);
cmd->type = t;
cmd->offset = size + alignment;
return cmd;
}
void
gui_command_buffer_push_scissor(struct gui_command_buffer *b, gui_float x, gui_float y,
gui_float w, gui_float h)
{
struct gui_command_scissor *cmd;
GUI_ASSERT(b);
if (!b) return;
b->clip.x = x;
b->clip.y = y;
b->clip.w = w;
b->clip.h = h;
cmd = gui_command_buffer_push(b, GUI_COMMAND_SCISSOR, sizeof(*cmd));
if (!cmd) return;
cmd->x = (gui_short)x;
cmd->y = (gui_short)y;
cmd->w = (gui_ushort)MAX(0, w);
cmd->h = (gui_ushort)MAX(0, h);
}
void
gui_command_buffer_push_line(struct gui_command_buffer *b, gui_float x0, gui_float y0,
gui_float x1, gui_float y1, struct gui_color c)
{
struct gui_command_line *cmd;
GUI_ASSERT(buffer);
if (!b) return;
cmd = gui_command_buffer_push(b, GUI_COMMAND_LINE, sizeof(*cmd));
if (!cmd) return;
cmd->begin[0] = (gui_short)x0;
cmd->begin[1] = (gui_short)y0;
cmd->end[0] = (gui_short)x1;
cmd->end[1] = (gui_short)y1;
gui_color_to_array(cmd->color, c);
}
void
gui_command_buffer_push_rect(struct gui_command_buffer *b, gui_float x, gui_float y,
gui_float w, gui_float h, gui_float rounding, struct gui_color c)
{
struct gui_command_rect *cmd;
GUI_ASSERT(buffer);
if (!b) return;
if (b->use_clipping) {
const struct gui_rect *clip = &b->clip;
if (!GUI_INTERSECT(x, y, w, h, clip->x, clip->y, clip->w, clip->h))
return;
}
cmd = gui_command_buffer_push(b, GUI_COMMAND_RECT, sizeof(*cmd));
if (!cmd) return;
cmd->r = (gui_uint)rounding;
cmd->x = (gui_short)x;
cmd->y = (gui_short)y;
cmd->w = (gui_ushort)MAX(0, w);
cmd->h = (gui_ushort)MAX(0, h);
gui_color_to_array(cmd->color, c);
}
void
gui_command_buffer_push_circle(struct gui_command_buffer *b, gui_float x, gui_float y,
gui_float w, gui_float h, struct gui_color c)
{
struct gui_command_circle *cmd;
GUI_ASSERT(b);
if (!b) return;
if (b->use_clipping) {
const struct gui_rect *clip = &b->clip;
if (!GUI_INTERSECT(x, y, w, h, clip->x, clip->y, clip->w, clip->h))
return;
}
cmd = gui_command_buffer_push(b, GUI_COMMAND_CIRCLE, sizeof(*cmd));
if (!cmd) return;
cmd->x = (gui_short)x;
cmd->y = (gui_short)y;
cmd->w = (gui_ushort)MAX(w, 0);
cmd->h = (gui_ushort)MAX(h, 0);
gui_color_to_array(cmd->color, c);
}
void
gui_command_buffer_push_triangle(struct gui_command_buffer *b, gui_float x0, gui_float y0,
gui_float x1, gui_float y1, gui_float x2, gui_float y2, struct gui_color c)
{
struct gui_command_triangle *cmd;
GUI_ASSERT(buffer);
if (!b) return;
if (b->use_clipping) {
const struct gui_rect *clip = &b->clip;
if (!GUI_INBOX(x0, y0, clip->x, clip->y, clip->w, clip->h) ||
!GUI_INBOX(x1, y1, clip->x, clip->y, clip->w, clip->h) ||
!GUI_INBOX(x2, y2, clip->x, clip->y, clip->w, clip->h))
return;
}
cmd = gui_command_buffer_push(b, GUI_COMMAND_TRIANGLE, sizeof(*cmd));
if (!cmd) return;
cmd->a[0] = (gui_short)x0;
cmd->a[1] = (gui_short)y0;
cmd->b[0] = (gui_short)x1;
cmd->b[1] = (gui_short)y1;
cmd->c[0] = (gui_short)x2;
cmd->c[1] = (gui_short)y2;
gui_color_to_array(cmd->color, c);
}
void
gui_command_buffer_push_image(struct gui_command_buffer *b, gui_float x, gui_float y,
gui_float w, gui_float h, gui_handle img)
{
struct gui_command_image *cmd;
GUI_ASSERT(b);
if (!b) return;
if (b->use_clipping) {
const struct gui_rect *c = &b->clip;
if (!GUI_INTERSECT(x, y, w, h, c->x, c->y, c->w, c->h))
return;
}
cmd = gui_command_buffer_push(b, GUI_COMMAND_IMAGE, sizeof(*cmd));
if (!cmd) return;
cmd->x = (gui_short)x;
cmd->y = (gui_short)y;
cmd->w = (gui_ushort)MAX(0, w);
cmd->h = (gui_ushort)MAX(0, h);
cmd->img = img;
}
void
gui_command_buffer_push_text(struct gui_command_buffer *b, gui_float x, gui_float y,
gui_float w, gui_float h, const gui_char *string, gui_size length,
const struct gui_font *font, struct gui_color bg, struct gui_color fg)
{
struct gui_command_text *cmd;
GUI_ASSERT(buffer);
GUI_ASSERT(font);
if (!b || !string || !length) return;
if (b->use_clipping) {
const struct gui_rect *c = &b->clip;
if (!GUI_INTERSECT(x, y, w, h, c->x, c->y, c->w, c->h))
return;
}
cmd = gui_command_buffer_push(b, GUI_COMMAND_TEXT, sizeof(*cmd) + length + 1);
if (!cmd) return;
cmd->x = (gui_short)x;
cmd->y = (gui_short)y;
cmd->w = (gui_ushort)w;
cmd->h = (gui_ushort)h;
gui_color_to_array(cmd->bg, bg);
gui_color_to_array(cmd->fg, fg);
cmd->font = font->userdata;
cmd->length = length;
gui_memcopy(cmd->string, string, length);
cmd->string[length] = '\0';
}
/*
* ==============================================================
*
* Widgets
*
* ===============================================================
*/
void
gui_text(struct gui_command_buffer *o, gui_float x, gui_float y, gui_float w, gui_float h,
const char *string, gui_size len, const struct gui_text *t,
enum gui_text_align a, const struct gui_font *f)
{
struct gui_rect label;
gui_size text_width;
GUI_ASSERT(o);
GUI_ASSERT(t);
if (!o || !t) return;
label.x = 0; label.w = 0;
label.y = y + t->padding.y;
label.h = MAX(0, h - 2 * t->padding.y);
text_width = f->width(f->userdata, (const gui_char*)string, len);
if (a == GUI_TEXT_LEFT) {
label.x = x + t->padding.x;
label.w = MAX(0, w - 2 * t->padding.x);
} else if (a == GUI_TEXT_CENTERED) {
label.w = 2 * t->padding.x + (gui_float)text_width;
label.x = (x + t->padding.x + ((w - 2 * t->padding.x)/2)) - (label.w/2);
label.x = MAX(x + t->padding.x, label.x);
label.w = MIN(x + w, label.x + label.w) - label.x;
} else if (a == GUI_TEXT_RIGHT) {
label.x = MAX(x, (x + w) - (2 * t->padding.x + (gui_float)text_width));
label.w = (gui_float)text_width + 2 * t->padding.x;
} else return;
gui_command_buffer_push_rect(o, x, y, w, h, 0, t->background);
gui_command_buffer_push_text(o, label.x, label.y, label.w, label.h,
(const gui_char*)string, len, f, t->background, t->foreground);
}
static gui_bool
gui_do_button(struct gui_command_buffer *o, gui_float x, gui_float y, gui_float w,
gui_float h, const struct gui_button *b, const struct gui_input *i,
enum gui_button_behavior behavior)
{
gui_bool ret = gui_false;
struct gui_color background;
GUI_ASSERT(o);
GUI_ASSERT(b);
if (!o || !b)
return gui_false;
background = b->background;
if (i && GUI_INBOX(i->mouse_pos.x, i->mouse_pos.y, x, y, w, h)) {
background = b->highlight;
if (GUI_INBOX(i->mouse_clicked_pos.x, i->mouse_clicked_pos.y, x, y, w, h)) {
ret = (behavior != GUI_BUTTON_DEFAULT) ? i->mouse_down:
(i->mouse_down && i->mouse_clicked);
}
}
gui_command_buffer_push_rect(o, x, y, w, h, b->rounding, b->foreground);
gui_command_buffer_push_rect(o, x + b->border, y + b->border,
w - 2 * b->border, h - 2 * b->border, b->rounding, background);
return ret;
}
gui_bool
gui_button_text(struct gui_command_buffer *o, gui_float x, gui_float y,
gui_float w, gui_float h, const char *string, enum gui_button_behavior behavior,
const struct gui_button *b, const struct gui_input *i,
const struct gui_font *f)
{
gui_bool ret = gui_false;
gui_float button_w, button_h;
struct gui_text t;
struct gui_color font_color;
struct gui_color bg_color;
struct gui_rect inner;
GUI_ASSERT(b);
GUI_ASSERT(o);
GUI_ASSERT(string);
if (!o || !b)
return gui_false;
font_color = b->content;
bg_color = b->background;
button_w = MAX(w, 2 * b->padding.x);
button_h = MAX(h, f->height + 2 * b->padding.y);
if (i && GUI_INBOX(i->mouse_pos.x, i->mouse_pos.y, x, y, button_w, button_h)) {
font_color = b->highlight_content;
bg_color = b->highlight;
}
ret = gui_do_button(o, x, y, button_w, button_h, b, i, behavior);
inner.x = x + b->border + b->rounding;
inner.y = y + b->border;
inner.w = button_w - (2 * b->border + 2 * b->rounding);
inner.h = button_h - (2 * b->border);
t.padding.x = b->padding.x;
t.padding.y = b->padding.y;
t.background = bg_color;
t.foreground = font_color;
gui_text(o, inner.x, inner.y, inner.w, inner.h, string, gui_strsiz(string),
&t, GUI_TEXT_CENTERED, f);
return ret;
}
gui_bool
gui_button_triangle(struct gui_command_buffer *out, gui_float x, gui_float y,
gui_float w, gui_float h, enum gui_heading heading, enum gui_button_behavior bh,
const struct gui_button *b, const struct gui_input *in)
{
gui_bool pressed;
struct gui_color col;
struct gui_vec2 points[3];
GUI_ASSERT(b);
GUI_ASSERT(out);
if (!out || !b)
return gui_false;
pressed = gui_do_button(out, x, y, w, h, b, in, bh);
gui_triangle_from_direction(points, x, y, w, h, b->padding.x, b->padding.y, heading);
col = (in && GUI_INBOX(in->mouse_pos.x, in->mouse_pos.y, x, y, w, h)) ?
b->highlight_content : b->content;
gui_command_buffer_push_triangle(out, points[0].x, points[0].y,
points[1].x, points[1].y, points[2].x, points[2].y, col);
return pressed;
}
gui_bool
gui_button_image(struct gui_command_buffer *out, gui_float x, gui_float y,
gui_float w, gui_float h, gui_handle img, enum gui_button_behavior b,
const struct gui_button *button, const struct gui_input *in)
{
gui_bool pressed;
gui_float img_x, img_y, img_w, img_h;
GUI_ASSERT(button);
GUI_ASSERT(out);
if (!out || !button)
return gui_false;
pressed = gui_do_button(out, x, y, w, h, button, in, b);
img_x = x + button->padding.x;
img_y = y + button->padding.y;
img_w = w - 2 * button->padding.x;
img_h = h - 2 * button->padding.y;
gui_command_buffer_push_image(out, img_x, img_y, img_w, img_h, img);
return pressed;
}
gui_bool
gui_toggle(struct gui_command_buffer *out, gui_float x, gui_float y, gui_float w,
gui_float h, gui_bool active, const char *string, enum gui_toggle_type type,
const struct gui_toggle *toggle, const struct gui_input *in,
const struct gui_font *font)
{
gui_bool toggle_active;
gui_float select_size;
gui_float toggle_w, toggle_h;
gui_float select_x, select_y;
gui_float cursor_x, cursor_y;
gui_float cursor_pad, cursor_size;
GUI_ASSERT(toggle);
GUI_ASSERT(out);
if (!out || !toggle)
return 0;
toggle_w = MAX(w, font->height + 2 * toggle->padding.x);
toggle_h = MAX(h, font->height + 2 * toggle->padding.y);
toggle_active = active;
select_x = x + toggle->padding.x;
select_y = y + toggle->padding.y;
select_size = font->height + 2 * toggle->padding.y;
cursor_pad = (type == GUI_TOGGLE_OPTION) ?
(gui_float)(gui_int)(select_size / 4):
(gui_float)(gui_int)(select_size / 8);
cursor_size = select_size - cursor_pad * 2;
cursor_x = select_x + cursor_pad;
cursor_y = select_y + cursor_pad;
if (in && !in->mouse_down && in->mouse_clicked)
if (GUI_INBOX(in->mouse_clicked_pos.x, in->mouse_clicked_pos.y,
cursor_x, cursor_y, cursor_size, cursor_size))
toggle_active = !toggle_active;
if (type == GUI_TOGGLE_CHECK)
gui_command_buffer_push_rect(out, select_x, select_y, select_size,
select_size, toggle->rounding, toggle->foreground);
else gui_command_buffer_push_circle(out, select_x, select_y, select_size,
select_size, toggle->foreground);
if (toggle_active) {
if (type == GUI_TOGGLE_CHECK)
gui_command_buffer_push_rect(out, cursor_x, cursor_y, cursor_size,
cursor_size, toggle->rounding, toggle->cursor);
else gui_command_buffer_push_circle(out, cursor_x, cursor_y,
cursor_size, cursor_size, toggle->cursor);
}
if (font && string) {
struct gui_text text;
gui_float inner_x, inner_y;
gui_float inner_w, inner_h;
inner_x = x + select_size + toggle->padding.x * 2;
inner_y = (y + (select_size / 2)) - (font->height / 2);
inner_w = (x + toggle_w) - (inner_x + toggle->padding.x);
inner_h = (y + toggle_h) - (inner_y + toggle->padding.y);
text.padding.x = 0;
text.padding.y = 0;
text.background = toggle->background;
text.foreground = toggle->font;
gui_text(out, inner_x, inner_y, inner_w, inner_h, string, gui_strsiz(string),
&text, GUI_TEXT_LEFT, font);
}
return toggle_active;
}
gui_float
gui_slider(struct gui_command_buffer *out, gui_float x, gui_float y, gui_float w, gui_float h,
gui_float min, gui_float val, gui_float max, gui_float step,
const struct gui_slider *s, const struct gui_input *in)
{
gui_float slider_range;
gui_float slider_min, slider_max;
gui_float slider_value, slider_steps;
gui_float slider_w, slider_h;
gui_float cursor_offset;
struct gui_rect cursor;
struct gui_rect bar;
GUI_ASSERT(s);
GUI_ASSERT(out);
if (!out || !s)
return 0;
slider_w = MAX(w, 2 * s->padding.x);
slider_h = MAX(h, 2 * s->padding.y);
slider_max = MAX(min, max);
slider_min = MIN(min, max);
slider_value = CLAMP(slider_min, val, slider_max);
slider_range = slider_max - slider_min;
slider_steps = slider_range / step;
cursor_offset = (slider_value - slider_min) / step;
cursor.w = (slider_w - 2 * s->padding.x) / (slider_steps + 1);
cursor.h = slider_h - 2 * s->padding.y;
cursor.x = x + s->padding.x + (cursor.w * cursor_offset);
cursor.y = y + s->padding.y;
bar.x = x + s->padding.x;
bar.y = (cursor.y + cursor.h/2) - cursor.h/8;
bar.w = slider_w - 2 * s->padding.x;
bar.h = cursor.h/4;
if (in && in->mouse_down &&
GUI_INBOX(in->mouse_pos.x, in->mouse_pos.y, x, y, slider_w, slider_h) &&
GUI_INBOX(in->mouse_clicked_pos.x,in->mouse_clicked_pos.y, x, y, slider_w, slider_h))
{
const float d = in->mouse_pos.x - (cursor.x + cursor.w / 2.0f);
const float pxstep = (slider_w - 2 * s->padding.x) / slider_steps;
if (GUI_ABS(d) >= pxstep) {
const gui_float steps = (gui_float)((gui_int)(GUI_ABS(d) / pxstep));
slider_value += (d > 0) ? (step * steps) : -(step * steps);
slider_value = CLAMP(slider_min, slider_value, slider_max);
cursor.x = x + s->padding.x + (cursor.w * (slider_value - slider_min));
}
}
gui_command_buffer_push_rect(out, x, y, slider_w, slider_h, 0, s->bg);
gui_command_buffer_push_rect(out, bar.x, bar.y, bar.w, bar.h,0, s->bar);
if (s->cursor == GUI_SLIDER_RECT) {
gui_command_buffer_push_rect(out,cursor.x, cursor.y, cursor.w, cursor.h,0, s->border);
gui_command_buffer_push_rect(out,cursor.x+1,cursor.y+1,cursor.w-2,cursor.h-2,0, s->fg);
} else {
gui_float c_pos = cursor.x - cursor.h/4;
gui_command_buffer_push_circle(out,c_pos,cursor.y,cursor.h,cursor.h,s->border);
gui_command_buffer_push_circle(out,c_pos + 1,cursor.y+1,cursor.h-2,cursor.h-2,s->fg);
}
return slider_value;
}
gui_size
gui_progress(struct gui_command_buffer *out, gui_float x, gui_float y,
gui_float w, gui_float h, gui_size value, gui_size max, gui_bool modifyable,
const struct gui_progress *prog, const struct gui_input *in)
{
gui_float cursor_x, cursor_y;
gui_float cursor_w, cursor_h;
gui_float prog_w, prog_h;
gui_float prog_scale;
gui_size prog_value;
GUI_ASSERT(prog);
GUI_ASSERT(out);
if (!out || !prog) return 0;
prog_w = MAX(w, 2 * prog->padding.x + 1);
prog_h = MAX(h, 2 * prog->padding.y + 1);
prog_value = MIN(value, max);
if (in && modifyable && in->mouse_down &&
GUI_INBOX(in->mouse_pos.x, in->mouse_pos.y, x, y, prog_w, prog_h)){
gui_float ratio = (gui_float)(in->mouse_pos.x - x) / (gui_float)prog_w;
prog_value = (gui_size)((gui_float)max * ratio);
}
if (!max) return prog_value;
prog_value = MIN(prog_value, max);
prog_scale = (gui_float)prog_value / (gui_float)max;
cursor_h = prog_h - 2 * prog->padding.y;
cursor_w = (prog_w - 2 * prog->padding.x) * prog_scale;
cursor_x = x + prog->padding.x;
cursor_y = y + prog->padding.y;
gui_command_buffer_push_rect(out, x, y, prog_w, prog_h,prog->rounding, prog->background);
gui_command_buffer_push_rect(out, cursor_x, cursor_y, cursor_w, cursor_h,
prog->rounding, prog->foreground);
return prog_value;
}
static gui_bool
gui_filter_input_default(gui_long unicode)
{
(void)unicode;
return gui_true;
}
static gui_bool
gui_filter_input_ascii(gui_long unicode)
{
if (unicode < 0 || unicode > 128)
return gui_false;
return gui_true;
}
static gui_bool
gui_filter_input_float(gui_long unicode)
{
if ((unicode < '0' || unicode > '9') && unicode != '.' && unicode != '-')
return gui_false;
return gui_true;
}
static gui_bool
gui_filter_input_decimal(gui_long unicode)
{
if (unicode < '0' || unicode > '9')
return gui_false;
return gui_true;
}
static gui_bool
gui_filter_input_hex(gui_long unicode)
{
if ((unicode < '0' || unicode > '9') &&
(unicode < 'a' || unicode > 'f') &&
(unicode < 'A' || unicode > 'B'))
return gui_false;
return gui_true;
}
static gui_bool
gui_filter_input_oct(gui_long unicode)
{
if (unicode < '0' || unicode > '7')
return gui_false;
return gui_true;
}
static gui_bool
gui_filter_input_binary(gui_long unicode)
{
if (unicode < '0' || unicode > '1')
return gui_false;
return gui_true;
}
static gui_size
gui_buffer_input(gui_char *buffer, gui_size length, gui_size max,
gui_filter filter, const struct gui_input *i)
{
gui_long unicode;
gui_size src_len = 0;
gui_size text_len = 0, glyph_len = 0;
GUI_ASSERT(buffer);
GUI_ASSERT(i);
glyph_len = gui_utf_decode(i->text, &unicode, i->text_len);
while (glyph_len && ((text_len+glyph_len) <= i->text_len) && (length+src_len)<max) {
if (filter(unicode)) {
gui_size n = 0;
for (n = 0; n < glyph_len; n++)
buffer[length++] = i->text[text_len + n];
text_len += glyph_len;
}
src_len = src_len + glyph_len;
glyph_len = gui_utf_decode(i->text + src_len, &unicode, i->text_len - src_len);
}
return text_len;
}
gui_size
gui_edit_filtered(struct gui_command_buffer *out, gui_float x, gui_float y, gui_float w,
gui_float h, gui_char *buffer, gui_size len, gui_size max, gui_bool *active,
const struct gui_edit *field, gui_filter filter, const struct gui_input *in,
const struct gui_font *font)
{
gui_float input_w, input_h;
gui_bool input_active;
GUI_ASSERT(out);
GUI_ASSERT(buffer);
GUI_ASSERT(field);
if (!out || !buffer || !field)
return 0;
input_w = MAX(w, 2 * field->padding.x);
input_h = MAX(h, font->height);
input_active = *active;
gui_command_buffer_push_rect(out, x, y, input_w, input_h,field->rounding, field->border);
gui_command_buffer_push_rect(out, x + field->border_size, y + field->border_size,
input_w - 2 * field->border_size, input_h - 2 * field->border_size,
field->rounding, field->background);
if (in && in->mouse_clicked && in->mouse_down)
input_active = GUI_INBOX(in->mouse_pos.x, in->mouse_pos.y, x, y, input_w, input_h);
if (input_active && in) {
const struct gui_key *bs = &in->keys[GUI_KEY_BACKSPACE];
const struct gui_key *del = &in->keys[GUI_KEY_DEL];
const struct gui_key *esc = &in->keys[GUI_KEY_ESCAPE];
const struct gui_key *enter = &in->keys[GUI_KEY_ENTER];
const struct gui_key *space = &in->keys[GUI_KEY_SPACE];
if ((del->down && del->clicked) || (bs->down && bs->clicked))
if (len > 0) len = len - 1;
if ((enter->down && enter->clicked) || (esc->down && esc->clicked))
input_active = gui_false;
if ((space->down && space->clicked) && (len < max))
buffer[len++] = ' ';
if (in->text_len && len < max)
len += gui_buffer_input(buffer, len, max, filter, in);
}
if (font && buffer) {
gui_size offset = 0;
gui_float label_x, label_y, label_h;
gui_float label_w = input_w - 2 * field->padding.x - 2 * field->border_size;
gui_size cursor_w =(gui_size)font->width(font->userdata,(const gui_char*)"X", 1);
gui_size text_len = len;
gui_size text_width = font->width(font->userdata, buffer, text_len);
while (text_len && (text_width + cursor_w) > (gui_size)label_w) {
gui_long unicode;
offset += gui_utf_decode(&buffer[offset], &unicode, text_len);
text_len = len - offset;
text_width = font->width(font->userdata, &buffer[offset], text_len);
}
label_x = x + field->padding.x + field->border_size;
label_y = y + field->padding.y + field->border_size;
label_h = input_h - (2 * field->padding.y + 2 * field->border_size);
gui_command_buffer_push_text(out , label_x, label_y, label_w, label_h,
&buffer[offset], text_len, font, field->background, field->text);
if (input_active && field->show_cursor) {
gui_command_buffer_push_rect(out, label_x + (gui_float)text_width, label_y,
(gui_float)cursor_w, label_h,0, field->cursor);
}
}
*active = input_active;
return len;
}
gui_size
gui_edit(struct gui_command_buffer *out, gui_float x, gui_float y, gui_float w,
gui_float h, gui_char *buffer, gui_size len, gui_size max, gui_bool *active,
const struct gui_edit *field, enum gui_input_filter f,
const struct gui_input *in, const struct gui_font *font)
{
static const gui_filter filter[] = {
gui_filter_input_default,
gui_filter_input_ascii,
gui_filter_input_float,
gui_filter_input_decimal,
gui_filter_input_hex,
gui_filter_input_oct,
gui_filter_input_binary,
};
return gui_edit_filtered(out, x, y, w, h, buffer, len, max, active,
field,filter[f], in, font);
}
gui_float
gui_scroll(struct gui_command_buffer *out, gui_float x, gui_float y,
gui_float w, gui_float h, gui_float offset, gui_float target,
gui_float step, const struct gui_scroll *s, const struct gui_input *i)
{
gui_bool button_up_pressed;
gui_bool button_down_pressed;
struct gui_button button;
gui_float scroll_step;
gui_float scroll_offset;
gui_float scroll_off, scroll_ratio;
gui_float scroll_y, scroll_w, scroll_h;
gui_float cursor_x, cursor_y;
gui_float cursor_w, cursor_h;
gui_bool inscroll, incursor;
GUI_ASSERT(out);
GUI_ASSERT(s);
if (!out || !s) return 0;
scroll_w = MAX(w, 0);
scroll_h = MAX(h, 2 * scroll_w);
gui_command_buffer_push_rect(out , x, y, scroll_w, scroll_h, s->rounding, s->background);
if (target <= scroll_h) return 0;
button.border = 1;
button.rounding = 0;
button.padding.x = scroll_w / 4;
button.padding.y = scroll_w / 4;
button.background = s->background;
button.foreground = s->foreground;
button.content = s->foreground;
button.highlight = s->background;
button.highlight_content = s->foreground;
button_up_pressed = gui_button_triangle(out, x, y, scroll_w, scroll_w,
GUI_UP, GUI_BUTTON_DEFAULT, &button, i);
button_down_pressed = gui_button_triangle(out, x, y + scroll_h - scroll_w,
scroll_w, scroll_w, GUI_DOWN, GUI_BUTTON_DEFAULT, &button, i);
scroll_h = scroll_h - 2 * scroll_w;
scroll_y = y + scroll_w;
scroll_step = MIN(step, scroll_h);
scroll_offset = MIN(offset, target - scroll_h);
scroll_ratio = scroll_h / target;
scroll_off = scroll_offset / target;
cursor_h = scroll_ratio * scroll_h;
cursor_y = scroll_y + (scroll_off * scroll_h);
cursor_w = scroll_w;
cursor_x = x;
if (i) {
const struct gui_vec2 mouse_pos = i->mouse_pos;
const struct gui_vec2 mouse_prev = i->mouse_prev;
inscroll = GUI_INBOX(mouse_pos.x,mouse_pos.y, x, y, scroll_w, scroll_h);
incursor = GUI_INBOX(mouse_prev.x,mouse_prev.y,cursor_x,cursor_y,cursor_w, cursor_h);
if (i->mouse_down && inscroll && incursor) {
/* update cursor over mouse dragging */
const gui_float pixel = i->mouse_delta.y;
const gui_float delta = (pixel / scroll_h) * target;
scroll_offset = CLAMP(0, scroll_offset + delta, target - scroll_h);
cursor_y += pixel;
} else if (button_up_pressed || button_down_pressed) {
/* update cursor over up/down button */
scroll_offset = (button_down_pressed) ?
MIN(scroll_offset + scroll_step, target - scroll_h):
MAX(0, scroll_offset - scroll_step);
scroll_off = scroll_offset / target;
cursor_y = scroll_y + (scroll_off * scroll_h);
}
}
gui_command_buffer_push_rect(out, cursor_x, cursor_y, cursor_w,
cursor_h, s->rounding, s->foreground);
return scroll_offset;
}
gui_int
gui_spinner(struct gui_command_buffer *out, gui_float x, gui_float y, gui_float w,
gui_float h, const struct gui_spinner *s, gui_int min, gui_int value,
gui_int max, gui_int step, gui_bool *active, const struct gui_input *in,
const struct gui_font *font)
{
char string[GUI_MAX_NUMBER_BUFFER];
gui_size len, old_len;
gui_bool is_active;
struct gui_button button;
gui_float button_x, button_y;
gui_float button_w, button_h;
gui_bool button_up_clicked, button_down_clicked;
struct gui_edit field;
gui_float field_x, field_y;
gui_float field_w, field_h;
value = CLAMP(min, value, max);
len = gui_itos(string, value);
is_active = (active) ? *active : gui_false;
old_len = len;
button_y = y;
button_h = h / 2;
button_w = h - s->padding.x;
button_x = x + w - button_w;
button.rounding = 0;
button.border = (gui_float)s->border_button;
button.padding.x = MAX(3, (button_h - font->height) / 2);
button.padding.y = MAX(3, (button_h - font->height) / 2);
button.background = s->button_color;
button.foreground = s->button_border;
button.content = s->button_triangle;
button.highlight = s->button_color;
button.highlight_content = s->button_triangle;
button_up_clicked = gui_button_triangle(out, button_x, button_y, button_w, button_h,
GUI_UP, GUI_BUTTON_DEFAULT, &button, in);
button_y = y + button_h;
button_down_clicked = gui_button_triangle(out, button_x, button_y, button_w, button_h,
GUI_DOWN, GUI_BUTTON_DEFAULT, &button, in);
if (button_up_clicked || button_down_clicked) {
value += (button_up_clicked) ? step : -step;
value = CLAMP(min, value, max);
}
field_x = x;
field_y = y;
field_h = h;
field_w = w - (button_w - button.border * 2);
field.border_size = 1;
field.rounding = 0;
field.padding.x = s->padding.x;
field.padding.y = s->padding.y;
field.show_cursor = s->show_cursor;
field.background = s->color;
field.border = s->border;
field.text = s->text;
len = gui_edit(out, field_x, field_y, field_w, field_h, (gui_char*)string,
len, GUI_MAX_NUMBER_BUFFER, &is_active, &field,GUI_INPUT_FLOAT, in, font);
if (old_len != len)
gui_strtoi(&value, string, len);
if (active) *active = is_active;
return value;
}
gui_size
gui_selector(struct gui_command_buffer *out, gui_float x, gui_float y, gui_float w,
gui_float h, const struct gui_selector *s, const char *items[],
gui_size item_count, gui_size item_current, const struct gui_input *input,
const struct gui_font *font)
{
gui_size text_len;
gui_float label_x, label_y;
gui_float label_w, label_h;
struct gui_button button;
gui_bool button_up_clicked;
gui_bool button_down_clicked;
gui_float button_x, button_y;
gui_float button_w, button_h;
GUI_ASSERT(items);
GUI_ASSERT(item_count);
GUI_ASSERT(item_current < item_count);
gui_command_buffer_push_rect(out, x, y, w, h, 0, s->border);
gui_command_buffer_push_rect(out, x+1, y+1, w-2, h-2, 0, s->color);
button_y = y;
button_h = h / 2;
button_w = h - s->padding.x;
button_x = x + w - button_w;
button.rounding = 0;
button.border = (gui_float)s->border_button;
button.padding.x = MAX(3, (button_h - font->height) / 2);
button.padding.y = MAX(3, (button_h - font->height) / 2);
button.background = s->button_color;
button.foreground = s->button_border;
button.content = s->button_triangle;
button.highlight = s->button_color;
button.highlight_content = s->button_triangle;
button_up_clicked = gui_button_triangle(out, button_x, button_y, button_w,
button_h, GUI_UP, GUI_BUTTON_DEFAULT, &button, input);
button_y = y + button_h;
button_down_clicked = gui_button_triangle(out, button_x, button_y, button_w,
button_h, GUI_DOWN, GUI_BUTTON_DEFAULT, &button, input);
item_current = (button_down_clicked && item_current > 0) ?
item_current-1 : (button_up_clicked && item_current < item_count-1) ?
item_current+1 : item_current;
label_x = x + s->padding.x;
label_y = y + s->padding.y;
label_w = w - (button_w + 2 * s->padding.x);
label_h = h - 2 * s->padding.y;
text_len = gui_strsiz(items[item_current]);
gui_command_buffer_push_text(out, label_x, label_y, label_w, label_h,
(const gui_char*)items[item_current], text_len, font,
s->text_bg, s->text);
return item_current;
}
/*
* ==============================================================
*
* Config
*
* ===============================================================
*/
static void
gui_config_default_properties(struct gui_config *config)
{
config->slider_cursor = GUI_SLIDER_CIRCLE;
config->properties[GUI_PROPERTY_SCROLLBAR_WIDTH] = gui_vec2(16, 16);
config->properties[GUI_PROPERTY_PADDING] = gui_vec2(15.0f, 10.0f);
config->properties[GUI_PROPERTY_SIZE] = gui_vec2(64.0f, 64.0f);
config->properties[GUI_PROPERTY_ITEM_SPACING] = gui_vec2(10.0f, 4.0f);
config->properties[GUI_PROPERTY_ITEM_PADDING] = gui_vec2(4.0f, 4.0f);
config->properties[GUI_PROPERTY_SCALER_SIZE] = gui_vec2(16.0f, 16.0f);
}
static void
gui_config_default_rounding(struct gui_config *config)
{
config->rounding[GUI_ROUNDING_PANEL] = 4.0f;
config->rounding[GUI_ROUNDING_BUTTON] = 2.0f;
config->rounding[GUI_ROUNDING_CHECK] = 2.0f;
config->rounding[GUI_ROUNDING_PROGRESS] = 4.0f;
config->rounding[GUI_ROUNDING_INPUT] = 2.0f;
config->rounding[GUI_ROUNDING_GRAPH] = 4.0f;
config->rounding[GUI_ROUNDING_SCROLLBAR] = 8.0f;
}
static void
gui_config_default_color(struct gui_config *config)
{
config->colors[GUI_COLOR_TEXT] = gui_rgba(100, 100, 100, 255);
config->colors[GUI_COLOR_PANEL] = gui_rgba(45, 45, 45, 255);
config->colors[GUI_COLOR_HEADER] = gui_rgba(40, 40, 40, 255);
config->colors[GUI_COLOR_BORDER] = gui_rgba(100, 100, 100, 255);
config->colors[GUI_COLOR_BUTTON] = gui_rgba(50, 50, 50, 255);
config->colors[GUI_COLOR_BUTTON_HOVER] = gui_rgba(100, 100, 100, 255);
config->colors[GUI_COLOR_BUTTON_TOGGLE] = gui_rgba(75, 75, 75, 255);
config->colors[GUI_COLOR_BUTTON_HOVER_FONT] = gui_rgba(45, 45, 45, 255);
config->colors[GUI_COLOR_BUTTON_BORDER] = gui_rgba(100, 100, 100, 255);
config->colors[GUI_COLOR_CHECK] = gui_rgba(100, 100, 100, 255);
config->colors[GUI_COLOR_CHECK_BACKGROUND] = gui_rgba(45, 45, 45, 255);
config->colors[GUI_COLOR_CHECK_ACTIVE] = gui_rgba(45, 45, 45, 255);
config->colors[GUI_COLOR_OPTION] = gui_rgba(100, 100, 100, 255);
config->colors[GUI_COLOR_OPTION_BACKGROUND] = gui_rgba(45, 45, 45, 255);
config->colors[GUI_COLOR_OPTION_ACTIVE] = gui_rgba(45, 45, 45, 255);
config->colors[GUI_COLOR_SLIDER] = gui_rgba(45, 45, 45, 255);
config->colors[GUI_COLOR_SLIDER_BAR] = gui_rgba(100, 100, 100, 255);
config->colors[GUI_COLOR_SLIDER_BORDER] = gui_rgba(45, 45, 45, 255);
config->colors[GUI_COLOR_SLIDER_CURSOR] = gui_rgba(100, 100, 100, 255);
config->colors[GUI_COLOR_PROGRESS] = gui_rgba(100, 100, 100, 255);
config->colors[GUI_COLOR_PROGRESS_CURSOR] = gui_rgba(45, 45, 45, 255);
config->colors[GUI_COLOR_INPUT] = gui_rgba(45, 45, 45, 255);
config->colors[GUI_COLOR_INPUT_CURSOR] = gui_rgba(100, 100, 100, 255);
config->colors[GUI_COLOR_INPUT_BORDER] = gui_rgba(100, 100, 100, 255);
config->colors[GUI_COLOR_INPUT_TEXT] = gui_rgba(100, 100, 100, 255);
config->colors[GUI_COLOR_SPINNER] = gui_rgba(45, 45, 45, 255);
config->colors[GUI_COLOR_SPINNER_BORDER] = gui_rgba(100, 100, 100, 255);
config->colors[GUI_COLOR_SPINNER_TRIANGLE] = gui_rgba(100, 100, 100, 255);
config->colors[GUI_COLOR_SPINNER_TEXT] = gui_rgba(100, 100, 100, 255);
config->colors[GUI_COLOR_SELECTOR] = gui_rgba(45, 45, 45, 255);
config->colors[GUI_COLOR_SELECTOR_BORDER] = gui_rgba(100, 100, 100, 255);
config->colors[GUI_COLOR_SELECTOR_TRIANGLE] = gui_rgba(100, 100, 100, 255);
config->colors[GUI_COLOR_SELECTOR_TEXT] = gui_rgba(100, 100, 100, 255);
config->colors[GUI_COLOR_HISTO] = gui_rgba(120, 120, 120, 255);
config->colors[GUI_COLOR_HISTO_BARS] = gui_rgba(45, 45, 45, 255);
config->colors[GUI_COLOR_HISTO_NEGATIVE] = gui_rgba(255, 255, 255, 255);
config->colors[GUI_COLOR_HISTO_HIGHLIGHT] = gui_rgba( 255, 0, 0, 255);
config->colors[GUI_COLOR_PLOT] = gui_rgba(100, 100, 100, 255);
config->colors[GUI_COLOR_PLOT_LINES] = gui_rgba(45, 45, 45, 255);
config->colors[GUI_COLOR_PLOT_HIGHLIGHT] = gui_rgba(255, 0, 0, 255);
config->colors[GUI_COLOR_SCROLLBAR] = gui_rgba(40, 40, 40, 255);
config->colors[GUI_COLOR_SCROLLBAR_CURSOR] = gui_rgba(70, 70, 70, 255);
config->colors[GUI_COLOR_SCROLLBAR_BORDER] = gui_rgba(45, 45, 45, 255);
config->colors[GUI_COLOR_TABLE_LINES] = gui_rgba(100, 100, 100, 255);
config->colors[GUI_COLOR_SHELF] = gui_rgba(45, 45, 45, 255);
config->colors[GUI_COLOR_SHELF_TEXT] = gui_rgba(150, 150, 150, 255);
config->colors[GUI_COLOR_SHELF_ACTIVE] = gui_rgba(30, 30, 30, 255);
config->colors[GUI_COLOR_SHELF_ACTIVE_TEXT] = gui_rgba(100, 100, 100, 255);
config->colors[GUI_COLOR_SCALER] = gui_rgba(100, 100, 100, 255);
}
void
gui_config_default(struct gui_config *config, gui_flags flags, const struct gui_font *font)
{
GUI_ASSERT(config);
if (!config) return;
gui_zero(config, sizeof(*config));
config->font = *font;
if (flags & GUI_DEFAULT_COLOR)
gui_config_default_color(config);
if (flags & GUI_DEFAULT_PROPERTIES)
gui_config_default_properties(config);
if (flags & GUI_DEFAULT_ROUNDING)
gui_config_default_rounding(config);
}
struct gui_vec2
gui_config_property(const struct gui_config *config, enum gui_config_properties index)
{
static struct gui_vec2 zero;
GUI_ASSERT(config);
if (!config) return zero;
return config->properties[index];
}
struct gui_color
gui_config_color(const struct gui_config *config, enum gui_config_colors index)
{
static struct gui_color zero;
GUI_ASSERT(config);
if (!config) return zero;
return config->colors[index];
}
void
gui_config_push_color(struct gui_config *config, enum gui_config_colors index,
gui_byte r, gui_byte g, gui_byte b, gui_byte a)
{
struct gui_saved_color *c;
GUI_ASSERT(config);
if (!config) return;
if (config->stack.color >= GUI_MAX_COLOR_STACK) return;
c = &config->stack.colors[config->stack.color++];
c->value = config->colors[index];
c->type = index;
config->colors[index].r = r;
config->colors[index].g = g;
config->colors[index].b = b;
config->colors[index].a = a;
}
void
gui_config_push_property(struct gui_config *config, enum gui_config_properties index,
gui_float x, gui_float y)
{
struct gui_saved_property *p;
GUI_ASSERT(config);
if (!config) return;
if (config->stack.property >= GUI_MAX_ATTRIB_STACK) return;
p = &config->stack.properties[config->stack.property++];
p->value = config->properties[index];
p->type = index;
config->properties[index].x = x;
config->properties[index].y = y;
}
void
gui_config_pop_color(struct gui_config *config)
{
struct gui_saved_color *c;
GUI_ASSERT(config);
if (!config) return;
if (!config->stack.color) return;
c = &config->stack.colors[--config->stack.color];
config->colors[c->type] = c->value;
}
void
gui_config_pop_property(struct gui_config *config)
{
struct gui_saved_property *p;
GUI_ASSERT(config);
if (!config) return;
if (!config->stack.property) return;
p = &config->stack.properties[--config->stack.property];
config->properties[p->type] = p->value;
}
void
gui_config_reset_colors(struct gui_config *config)
{
GUI_ASSERT(config);
if (!config) return;
while (config->stack.color)
gui_config_pop_color(config);
}
void
gui_config_reset_properties(struct gui_config *config)
{
GUI_ASSERT(config);
if (!config) return;
while (config->stack.property)
gui_config_pop_property(config);
}
void
gui_config_reset(struct gui_config *config)
{
GUI_ASSERT(config);
if (!config) return;
gui_config_reset_colors(config);
gui_config_reset_properties(config);
}
/*
* ==============================================================
*
* Panel
*
* ===============================================================
*/
void
gui_panel_init(struct gui_panel *panel, gui_float x, gui_float y, gui_float w,
gui_float h, gui_flags flags, struct gui_command_buffer *buffer,
const struct gui_config *config)
{
GUI_ASSERT(panel);
GUI_ASSERT(config);
if (!panel || !config)
return;
panel->x = x;
panel->y = y;
panel->w = w;
panel->h = h;
panel->flags = flags;
panel->config = config;
panel->buffer = buffer;
panel->offset = 0;
panel->minimized = gui_false;
}
gui_bool
gui_panel_begin(struct gui_panel_layout *l, struct gui_panel *p,
const char *text, const struct gui_input *i)
{
const struct gui_config *c;
struct gui_command_buffer *out;
const struct gui_color *header;
gui_float mouse_x, mouse_y;
gui_float prev_x, prev_y;
gui_float clicked_x, clicked_y;
gui_float header_x = 0, header_w = 0;
gui_float footer_h;
gui_bool ret = gui_true;
float scrollbar_width;
struct gui_vec2 panel_size;
struct gui_vec2 item_padding;
struct gui_vec2 item_spacing;
struct gui_vec2 panel_padding;
struct gui_vec2 scaler_size;
GUI_ASSERT(p);
GUI_ASSERT(l);
GUI_ASSERT(p->buffer);
/* check arguments */
if (!p || !p->buffer || !l)
return gui_false;
if (!(p->flags & GUI_PANEL_TAB))
gui_command_buffer_reset(p->buffer);
if (p->flags & GUI_PANEL_HIDDEN) {
gui_zero(l, sizeof(*l));
l->valid = gui_false;
l->config = p->config;
l->buffer = p->buffer;
return gui_false;
}
c = p->config;
scrollbar_width = gui_config_property(c, GUI_PROPERTY_SCROLLBAR_WIDTH).x;
panel_size = gui_config_property(c, GUI_PROPERTY_SIZE);
panel_padding = gui_config_property(c, GUI_PROPERTY_PADDING);
item_padding = gui_config_property(c, GUI_PROPERTY_ITEM_PADDING);
item_spacing = gui_config_property(c, GUI_PROPERTY_ITEM_SPACING);
scaler_size = gui_config_property(c, GUI_PROPERTY_SCALER_SIZE);
l->header_height = c->font.height + 3 * item_padding.y;
l->header_height += panel_padding.y;
mouse_x = (i) ? i->mouse_pos.x : -1;
mouse_y = (i) ? i->mouse_pos.y : -1;
prev_x = (i) ? i->mouse_prev.x : -1;
prev_y = (i) ? i->mouse_prev.y : -1;
clicked_x = (i) ? i->mouse_clicked_pos.x : -1;
clicked_y = (i) ? i->mouse_clicked_pos.y : -1;
if (p->flags & GUI_PANEL_MOVEABLE) {
gui_bool incursor;
const gui_float move_x = p->x;
const gui_float move_y = p->y;
const gui_float move_w = p->w;
const gui_float move_h = l->header_height;
incursor = i && GUI_INBOX(i->mouse_prev.x,i->mouse_prev.y,move_x,move_y,move_w,move_h);
if (i && i->mouse_down && incursor) {
p->x = MAX(0, p->x + i->mouse_delta.x);
p->y = MAX(0, p->y + i->mouse_delta.y);
}
}
if (p->flags & GUI_PANEL_SCALEABLE) {
gui_bool incursor;
gui_float scaler_w = MAX(0, scaler_size.x - item_padding.x);
gui_float scaler_h = MAX(0, scaler_size.y - item_padding.y);
gui_float scaler_x = (p->x + p->w) - (item_padding.x + scaler_w);
gui_float scaler_y = p->y + p->h - scaler_size.y;
incursor = i && GUI_INBOX(prev_x, prev_y, scaler_x, scaler_y, scaler_w, scaler_h);
if (i && i->mouse_down && incursor) {
gui_float min_x = panel_size.x;
gui_float min_y = panel_size.y;
p->w = MAX(min_x, p->w + i->mouse_delta.x);
p->h = MAX(min_y, p->h + i->mouse_delta.y);
}
}
out = p->buffer;
l->x = p->x;
l->y = p->y;
l->w = p->w;
l->h = p->h;
l->at_x = p->x;
l->at_y = p->y;
l->width = p->w;
l->height = p->h;
l->config = p->config;
l->buffer = p->buffer;
l->input = i;
l->index = 0;
l->row_columns = 0;
l->row_height = 0;
l->offset = p->offset;
if (!(p->flags & GUI_PANEL_NO_HEADER)) {
header = &c->colors[GUI_COLOR_HEADER];
header_x = p->x + panel_padding.x;
header_w = p->w - 2 * panel_padding.x;
gui_command_buffer_push_rect(out, p->x, p->y, p->w, l->header_height, 0, *header);
} else l->header_height = 1;
l->row_height = l->header_height + 2 * item_spacing.y;
footer_h = scaler_size.y + item_padding.y;
if ((p->flags & GUI_PANEL_SCALEABLE) &&
(p->flags & GUI_PANEL_SCROLLBAR) &&
!p->minimized) {
gui_float footer_x, footer_y, footer_w;
footer_x = p->x;
footer_w = p->w;
footer_y = p->y + p->h - footer_h;
gui_command_buffer_push_rect(out, footer_x, footer_y, footer_w, footer_h,
0, c->colors[GUI_COLOR_PANEL]);
}
if (!(p->flags & GUI_PANEL_TAB)) {
p->flags |= GUI_PANEL_SCROLLBAR;
if (i && i->mouse_down) {
if (!GUI_INBOX(clicked_x, clicked_y, p->x, p->y, p->w, p->h))
p->flags &= (gui_flag)~GUI_PANEL_ACTIVE;
else p->flags |= GUI_PANEL_ACTIVE;
}
}
l->clip.x = p->x;
l->clip.w = p->w;
l->clip.y = p->y + l->header_height - 1;
if (p->flags & GUI_PANEL_SCROLLBAR) {
if (p->flags & GUI_PANEL_SCALEABLE)
l->clip.h = p->h - (footer_h + l->header_height);
else l->clip.h = p->h - l->header_height;
l->clip.h -= (panel_padding.y + item_padding.y);
} else l->clip.h = gui_null_rect.h;
if ((p->flags & GUI_PANEL_CLOSEABLE) && (!(p->flags & GUI_PANEL_NO_HEADER))) {
const gui_char *X = (const gui_char*)"x";
const gui_size text_width = c->font.width(c->font.userdata, X, 1);
const gui_float close_x = header_x;
const gui_float close_y = p->y + panel_padding.y;
const gui_float close_w = (gui_float)text_width + 2 * item_padding.x;
const gui_float close_h = c->font.height + 2 * item_padding.y;
gui_command_buffer_push_text(out, close_x, close_y, close_w, close_h,
X, 1, &c->font, c->colors[GUI_COLOR_HEADER], c->colors[GUI_COLOR_TEXT]);
header_w -= close_w;
header_x += close_h - item_padding.x;
if (i && GUI_INBOX(mouse_x, mouse_y, close_x, close_y, close_w, close_h)) {
if (GUI_INBOX(clicked_x, clicked_y, close_x, close_y, close_w, close_h)) {
ret = !(i->mouse_down && i->mouse_clicked);
if (!ret) p->flags |= GUI_PANEL_HIDDEN;
}
}
}
if ((p->flags & GUI_PANEL_MINIMIZABLE) && (!(p->flags & GUI_PANEL_NO_HEADER))) {
gui_size text_width;
gui_float min_x, min_y, min_w, min_h;
const gui_char *score = (p->minimized) ?
(const gui_char*)"+":
(const gui_char*)"-";
text_width = c->font.width(c->font.userdata, score, 1);
min_x = header_x;
min_y = p->y + panel_padding.y;
min_w = (gui_float)text_width + 3 * item_padding.x;
min_h = c->font.height + 2 * item_padding.y;
gui_command_buffer_push_text(out, min_x, min_y, min_w, min_h,
score, 1, &c->font, c->colors[GUI_COLOR_HEADER],
c->colors[GUI_COLOR_TEXT]);
header_w -= min_w;
header_x += min_w - item_padding.x;
if (i && GUI_INBOX(mouse_x, mouse_y, min_x, min_y, min_w, min_h)) {
if (GUI_INBOX(clicked_x, clicked_y, min_x, min_y, min_w, min_h))
if (i->mouse_down && i->mouse_clicked)
p->minimized = !p->minimized;
}
}
l->valid = !(p->minimized || (p->flags & GUI_PANEL_HIDDEN));
if (text && !(p->flags & GUI_PANEL_NO_HEADER)) {
const gui_size text_len = gui_strsiz(text);
const gui_float label_x = header_x + item_padding.x;
const gui_float label_y = p->y + panel_padding.y;
const gui_float label_w = header_w - (3 * item_padding.x);
const gui_float label_h = c->font.height + 2 * item_padding.y;
gui_command_buffer_push_text(out, label_x, label_y, label_w, label_h,
(const gui_char*)text, text_len, &c->font, c->colors[GUI_COLOR_HEADER],
c->colors[GUI_COLOR_TEXT]);
}
if (p->flags & GUI_PANEL_SCROLLBAR) {
const struct gui_color *color = &c->colors[GUI_COLOR_PANEL];
l->width = p->w - scrollbar_width;
l->height = p->h - (l->header_height + 2 * item_spacing.y);
if (p->flags & GUI_PANEL_SCALEABLE) l->height -= footer_h;
if (l->valid)
gui_command_buffer_push_rect(out, p->x, p->y + l->header_height,
p->w, p->h - l->header_height, 0, *color);
}
if (p->flags & GUI_PANEL_BORDER) {
const struct gui_color *color = &c->colors[GUI_COLOR_BORDER];
const gui_float width = (p->flags & GUI_PANEL_SCROLLBAR) ?
l->width + scrollbar_width : l->width;
gui_command_buffer_push_line(out, p->x, p->y,
p->x + p->w, p->y, *color);
gui_command_buffer_push_line(out, p->x, p->y, p->x,
p->y + l->header_height, c->colors[GUI_COLOR_BORDER]);
gui_command_buffer_push_line(out, p->x + width, p->y, p->x + width,
p->y + l->header_height, c->colors[GUI_COLOR_BORDER]);
if (p->flags & GUI_PANEL_BORDER_HEADER)
gui_command_buffer_push_line(out, p->x, p->y + l->header_height,
p->x + p->w, p->y + l->header_height, c->colors[GUI_COLOR_BORDER]);
}
gui_command_buffer_push_scissor(out, l->clip.x, l->clip.y, l->clip.w, l->clip.h);
return ret;
}
gui_bool
gui_panel_begin_stacked(struct gui_panel_layout *l, struct gui_panel* p,
struct gui_stack *s, const char *title, const struct gui_input *i)
{
gui_bool inpanel;
GUI_ASSERT(l);
GUI_ASSERT(s);
if (!l || !s) return gui_false;
inpanel = GUI_INBOX(i->mouse_prev.x, i->mouse_prev.y, p->x, p->y, p->w, p->h);
if (i->mouse_down && i->mouse_clicked && inpanel && p != s->end) {
const struct gui_panel *iter = p->next;
while (iter) {
const struct gui_panel *cur = iter;
if (GUI_INBOX(i->mouse_prev.x,i->mouse_prev.y,cur->x,cur->y,cur->w,cur->h) &&
!cur->minimized) break;
iter = iter->next;
}
if (!iter) {
gui_stack_pop(s, p);
gui_stack_push(s, p);
}
}
return gui_panel_begin(l, p, title, (s->end == p) ? i : NULL);
}
gui_bool
gui_panel_begin_tiled(struct gui_panel_layout *tile, struct gui_panel *panel,
struct gui_layout *layout, enum gui_layout_slot_index slot, gui_size index,
const char *title, const struct gui_input *in)
{
struct gui_rect bounds;
struct gui_layout_slot *s;
GUI_ASSERT(panel);
GUI_ASSERT(tile);
GUI_ASSERT(layout);
if (!layout || !panel || !tile) return gui_false;
if (slot >= GUI_SLOT_MAX) return gui_false;
if (index >= layout->slots[slot].capacity) return gui_false;
panel->flags &= (gui_flags)~GUI_PANEL_MINIMIZABLE;
panel->flags &= (gui_flags)~GUI_PANEL_CLOSEABLE;
panel->flags &= (gui_flags)~GUI_PANEL_MOVEABLE;
panel->flags &= (gui_flags)~GUI_PANEL_SCALEABLE;
s = &layout->slots[slot];
bounds.x = s->offset.x * (gui_float)layout->width;
bounds.y = s->offset.y * (gui_float)layout->height;
bounds.w = s->ratio.x * (gui_float)layout->width;
bounds.h = s->ratio.y * (gui_float)layout->height;
if (s->format == GUI_LAYOUT_HORIZONTAL) {
panel->h = bounds.h;
panel->y = bounds.y;
panel->x = bounds.x + (gui_float)index * panel->w;
panel->w = bounds.w / (gui_float)s->capacity;
} else {
panel->x = bounds.x;
panel->w = bounds.w;
panel->h = bounds.h / (gui_float)s->capacity;
panel->y = bounds.y + (gui_float)index * panel->h;
}
gui_stack_push(&layout->stack, panel);
return gui_panel_begin(tile, panel, title, (layout->state) ? in : NULL);
}
void
gui_panel_row(struct gui_panel_layout *layout, gui_float height, gui_size cols)
{
const struct gui_config *config;
const struct gui_color *color;
struct gui_command_buffer *out;
struct gui_vec2 item_spacing;
struct gui_vec2 panel_padding;
GUI_ASSERT(layout);
GUI_ASSERT(layout->config);
GUI_ASSERT(layout->buffer);
if (!layout) return;
if (!layout->valid) return;
config = layout->config;
out = layout->buffer;
color = &config->colors[GUI_COLOR_PANEL];
item_spacing = gui_config_property(config, GUI_PROPERTY_ITEM_SPACING);
panel_padding = gui_config_property(config, GUI_PROPERTY_PADDING);
layout->index = 0;
layout->at_y += layout->row_height;
layout->row_columns = cols;
layout->row_height = height + item_spacing.y;
gui_command_buffer_push_rect(out, layout->at_x, layout->at_y,
layout->width, height + panel_padding.y, 0, *color);
}
gui_size
gui_panel_row_columns(const struct gui_panel_layout *l, gui_size widget_size)
{
struct gui_vec2 spacing;
struct gui_vec2 padding;
gui_size cols = 0, size;
GUI_ASSERT(l);
GUI_ASSERT(widget_size);
if (!l || !widget_size)
return 0;
spacing = gui_config_property(l->config, GUI_PROPERTY_ITEM_SPACING);
padding = gui_config_property(l->config, GUI_PROPERTY_PADDING);
cols = (gui_size)(l->width) / widget_size;
size = (cols * (gui_size)spacing.x) + 2 * (gui_size)padding.x + widget_size * cols;
while ((size > l->width) && --cols)
size = (cols*(gui_size)spacing.x) + 2*(gui_size)padding.x + widget_size * cols;
return cols;
}
void
gui_panel_spacing(struct gui_panel_layout *l, gui_size cols)
{
gui_size add;
GUI_ASSERT(l);
GUI_ASSERT(l->config);
GUI_ASSERT(l->buffer);
if (!l) return;
if (!l->valid) return;
add = (l->index + cols) % l->row_columns;
if (l->index + cols > l->row_columns) {
gui_size i;
const struct gui_config *c = l->config;
struct gui_vec2 item_spacing = gui_config_property(c, GUI_PROPERTY_ITEM_SPACING);
const gui_float row_height = l->row_height - item_spacing.y;
gui_size rows = (l->index + cols) / l->row_columns;
for (i = 0; i < rows; ++i)
gui_panel_row(l, row_height, l->row_columns);
}
l->index += add;
}
static void
gui_panel_alloc_space(struct gui_rect *bounds, struct gui_panel_layout *layout)
{
const struct gui_config *config;
gui_float panel_padding, panel_spacing, panel_space;
gui_float item_offset, item_width, item_spacing;
struct gui_vec2 spacing;
struct gui_vec2 padding;
GUI_ASSERT(layout);
GUI_ASSERT(layout->config);
GUI_ASSERT(bounds);
if (!layout || !layout->config || !bounds)
return;
config = layout->config;
spacing = gui_config_property(config, GUI_PROPERTY_ITEM_SPACING);
padding = gui_config_property(config, GUI_PROPERTY_PADDING);
if (layout->index >= layout->row_columns) {
const gui_float row_height = layout->row_height - spacing.y;
gui_panel_row(layout, row_height, layout->row_columns);
}
panel_padding = 2 * padding.x;
panel_spacing = (gui_float)(layout->row_columns - 1) * spacing.x;
panel_space = layout->width - panel_padding - panel_spacing;
item_width = panel_space / (gui_float)layout->row_columns;
item_offset = (gui_float)layout->index * item_width;
item_spacing = (gui_float)layout->index * spacing.x;
bounds->x = layout->at_x + item_offset + item_spacing + padding.x;
bounds->y = layout->at_y - layout->offset;
bounds->w = item_width;
bounds->h = layout->row_height - spacing.y;
layout->index++;
}
gui_bool
gui_panel_widget(struct gui_rect *bounds, struct gui_panel_layout *layout)
{
struct gui_rect *c = NULL;
GUI_ASSERT(layout);
GUI_ASSERT(layout->config);
GUI_ASSERT(layout->buffer);
if (!layout || !layout->config || !layout->buffer) return gui_false;
if (!layout->valid) return gui_false;
gui_panel_alloc_space(bounds, layout);
c = &layout->clip;
if (!GUI_INTERSECT(c->x, c->y, c->w, c->h, bounds->x, bounds->y, bounds->w, bounds->h))
return gui_false;
return gui_true;
}
void
gui_panel_text_colored(struct gui_panel_layout *layout, const char *str, gui_size len,
enum gui_text_align alignment, struct gui_color color)
{
struct gui_rect bounds;
struct gui_text text;
const struct gui_config *config;
struct gui_vec2 item_padding;
GUI_ASSERT(layout);
GUI_ASSERT(layout->config);
GUI_ASSERT(layout->buffer);
GUI_ASSERT(str && len);
if (!layout || !layout->config || !layout->buffer) return;
if (!layout->valid) return;
gui_panel_alloc_space(&bounds, layout);
config = layout->config;
item_padding = gui_config_property(config, GUI_PROPERTY_ITEM_PADDING);
text.padding.x = item_padding.x;
text.padding.y = item_padding.y;
text.foreground = color;
text.background = config->colors[GUI_COLOR_PANEL];
gui_text(layout->buffer,bounds.x,bounds.y,bounds.w,bounds.h, str, len,
&text, alignment, &config->font);
}
void
gui_panel_text(struct gui_panel_layout *l, const char *str, gui_size len,
enum gui_text_align alignment)
{
gui_panel_text_colored(l, str, len, alignment,l->config->colors[GUI_COLOR_TEXT]);
}
void
gui_panel_label_colored(struct gui_panel_layout *layout, const char *text,
enum gui_text_align align, struct gui_color color)
{
gui_size len = gui_strsiz(text);
gui_panel_text_colored(layout, text, len, align, color);
}
void
gui_panel_label(struct gui_panel_layout *layout, const char *text,
enum gui_text_align align)
{
gui_size len = gui_strsiz(text);
gui_panel_text(layout, text, len, align);
}
static gui_bool
gui_panel_button(struct gui_button *button, struct gui_rect *bounds,
struct gui_panel_layout *layout)
{
const struct gui_config *config;
struct gui_vec2 item_padding;
if (!gui_panel_widget(bounds, layout)) return gui_false;
config = layout->config;
item_padding = gui_config_property(config, GUI_PROPERTY_ITEM_PADDING);
button->border = 1;
button->rounding = config->rounding[GUI_ROUNDING_BUTTON];
button->padding.x = item_padding.x;
button->padding.y = item_padding.y;
return gui_true;
}
gui_bool
gui_panel_button_text(struct gui_panel_layout *layout, const char *str,
enum gui_button_behavior behavior)
{
struct gui_rect bounds;
struct gui_button button;
const struct gui_config *config;
if (!gui_panel_button(&button, &bounds, layout))
return gui_false;
config = layout->config;
button.background = config->colors[GUI_COLOR_BUTTON];
button.foreground = config->colors[GUI_COLOR_BUTTON_BORDER];
button.content = config->colors[GUI_COLOR_TEXT];
button.highlight = config->colors[GUI_COLOR_BUTTON_HOVER];
button.highlight_content = config->colors[GUI_COLOR_BUTTON_HOVER_FONT];
button.rounding = config->rounding[GUI_ROUNDING_BUTTON];
return gui_button_text(layout->buffer, bounds.x, bounds.y, bounds.w, bounds.h,
str, behavior, &button, layout->input, &config->font);
}
gui_bool
gui_panel_button_color(struct gui_panel_layout *layout,
struct gui_color color, enum gui_button_behavior behavior)
{
struct gui_rect bounds;
struct gui_button button;
if (!gui_panel_button(&button, &bounds, layout))
return gui_false;
button.background = color;
button.foreground = color;
button.highlight = color;
button.highlight_content = color;
button.rounding = layout->config->rounding[GUI_ROUNDING_BUTTON];
return gui_do_button(layout->buffer, bounds.x, bounds.y, bounds.w, bounds.h,
&button, layout->input, behavior);
}
gui_bool
gui_panel_button_triangle(struct gui_panel_layout *layout, enum gui_heading heading,
enum gui_button_behavior behavior)
{
struct gui_rect bounds;
struct gui_button button;
const struct gui_config *config;
if (!gui_panel_button(&button, &bounds, layout))
return gui_false;
config = layout->config;
button.rounding = config->rounding[GUI_ROUNDING_BUTTON];
button.background = config->colors[GUI_COLOR_BUTTON];
button.foreground = config->colors[GUI_COLOR_BUTTON_BORDER];
button.content = config->colors[GUI_COLOR_TEXT];
button.highlight = config->colors[GUI_COLOR_BUTTON_HOVER];
button.highlight_content = config->colors[GUI_COLOR_BUTTON_HOVER_FONT];
return gui_button_triangle(layout->buffer, bounds.x, bounds.y, bounds.w,
bounds.h, heading, behavior, &button, layout->input);
}
gui_bool
gui_panel_button_image(struct gui_panel_layout *layout, gui_handle image,
enum gui_button_behavior behavior)
{
struct gui_rect bounds;
struct gui_button button;
const struct gui_config *config;
if (!gui_panel_button(&button, &bounds, layout))
return gui_false;
config = layout->config;
button.rounding = config->rounding[GUI_ROUNDING_BUTTON];
button.background = config->colors[GUI_COLOR_BUTTON];
button.foreground = config->colors[GUI_COLOR_BUTTON_BORDER];
button.content = config->colors[GUI_COLOR_TEXT];
button.highlight = config->colors[GUI_COLOR_BUTTON_HOVER];
button.highlight_content = config->colors[GUI_COLOR_BUTTON_HOVER_FONT];
return gui_button_image(layout->buffer, bounds.x, bounds.y, bounds.w,
bounds.h, image, behavior, &button, layout->input);
}
gui_bool
gui_panel_button_toggle(struct gui_panel_layout *layout, const char *str, gui_bool value)
{
struct gui_rect bounds;
struct gui_button button;
const struct gui_config *config;
GUI_ASSERT(str);
if (!gui_panel_button(&button, &bounds, layout))
return value;
config = layout->config;
button.rounding = config->rounding[GUI_ROUNDING_BUTTON];
if (!value) {
button.background = config->colors[GUI_COLOR_BUTTON];
button.foreground = config->colors[GUI_COLOR_BUTTON_BORDER];
button.content = config->colors[GUI_COLOR_TEXT];
button.highlight = config->colors[GUI_COLOR_BUTTON_HOVER];
button.highlight_content = config->colors[GUI_COLOR_BUTTON];
} else {
button.background = config->colors[GUI_COLOR_BUTTON_TOGGLE];
button.foreground = config->colors[GUI_COLOR_BUTTON_BORDER];
button.content = config->colors[GUI_COLOR_TEXT];
button.highlight = config->colors[GUI_COLOR_BUTTON_HOVER];
button.highlight_content = config->colors[GUI_COLOR_BUTTON];
}
if (gui_button_text(layout->buffer, bounds.x, bounds.y, bounds.w, bounds.h,
str, GUI_BUTTON_DEFAULT, &button, layout->input, &config->font)) value = !value;
return value;
}
static gui_bool
gui_panel_toggle_base(struct gui_toggle *toggle, struct gui_rect *bounds,
struct gui_panel_layout *layout)
{
const struct gui_config *config;
struct gui_vec2 item_padding;
if (!gui_panel_widget(bounds, layout))
return gui_false;
config = layout->config;
item_padding = gui_config_property(config, GUI_PROPERTY_ITEM_PADDING);
toggle->rounding = 0;
toggle->padding.x = item_padding.x;
toggle->padding.y = item_padding.y;
toggle->font = config->colors[GUI_COLOR_TEXT];
return gui_true;
}
gui_bool
gui_panel_check(struct gui_panel_layout *layout, const char *text, gui_bool is_active)
{
struct gui_rect bounds;
struct gui_toggle toggle;
const struct gui_config *config;
if (!text || !gui_panel_toggle_base(&toggle, &bounds, layout))
return is_active;
config = layout->config;
toggle.rounding = config->rounding[GUI_ROUNDING_CHECK];
toggle.cursor = config->colors[GUI_COLOR_CHECK_ACTIVE];
toggle.background = config->colors[GUI_COLOR_CHECK_BACKGROUND];
toggle.foreground = config->colors[GUI_COLOR_CHECK];
return gui_toggle(layout->buffer, bounds.x, bounds.y, bounds.w, bounds.h,
is_active, text, GUI_TOGGLE_CHECK, &toggle, layout->input, &config->font);
}
gui_bool
gui_panel_option(struct gui_panel_layout *layout, const char *text, gui_bool is_active)
{
struct gui_rect bounds;
struct gui_toggle toggle;
const struct gui_config *config;
if (!text || !gui_panel_toggle_base(&toggle, &bounds, layout))
return is_active;
config = layout->config;
toggle.cursor = config->colors[GUI_COLOR_OPTION_ACTIVE];
toggle.background = config->colors[GUI_COLOR_OPTION_BACKGROUND];
toggle.foreground = config->colors[GUI_COLOR_OPTION];
return gui_toggle(layout->buffer, bounds.x, bounds.y, bounds.w, bounds.h,
is_active, text, GUI_TOGGLE_OPTION, &toggle, layout->input, &config->font);
}
gui_size
gui_panel_option_group(struct gui_panel_layout *layout, const char **options,
gui_size count, gui_size current)
{
gui_size i;
GUI_ASSERT(layout && options && count);
if (!layout || !options || !count) return current;
for (i = 0; i < count; ++i) {
if (gui_panel_option(layout, options[i], i == current))
current = i;
}
return current;
}
gui_float
gui_panel_slider(struct gui_panel_layout *layout, gui_float min_value, gui_float value,
gui_float max_value, gui_float value_step)
{
struct gui_rect bounds;
struct gui_slider slider;
const struct gui_config *config;
struct gui_vec2 item_padding;
if (!gui_panel_widget(&bounds, layout))
return value;
config = layout->config;
item_padding = gui_config_property(config, GUI_PROPERTY_ITEM_PADDING);
slider.cursor = config->slider_cursor;
slider.padding.x = item_padding.x;
slider.padding.y = item_padding.y;
slider.bg = config->colors[GUI_COLOR_SLIDER];
slider.fg = config->colors[GUI_COLOR_SLIDER_CURSOR];
slider.bar = config->colors[GUI_COLOR_SLIDER_BAR];
slider.border = config->colors[GUI_COLOR_SLIDER_BORDER];
return gui_slider(layout->buffer, bounds.x, bounds.y, bounds.w, bounds.h,
min_value, value, max_value, value_step, &slider, layout->input);
}
gui_size
gui_panel_progress(struct gui_panel_layout *layout, gui_size cur_value, gui_size max_value,
gui_bool is_modifyable)
{
struct gui_rect bounds;
struct gui_progress prog;
const struct gui_config *config;
struct gui_vec2 item_padding;
if (!gui_panel_widget(&bounds, layout))
return cur_value;
config = layout->config;
item_padding = gui_config_property(config, GUI_PROPERTY_ITEM_PADDING);
prog.rounding = config->rounding[GUI_ROUNDING_PROGRESS];
prog.padding.x = item_padding.x;
prog.padding.y = item_padding.y;
prog.background = config->colors[GUI_COLOR_PROGRESS];
prog.foreground = config->colors[GUI_COLOR_PROGRESS_CURSOR];
return gui_progress(layout->buffer, bounds.x, bounds.y, bounds.w, bounds.h,
cur_value, max_value, is_modifyable, &prog, layout->input);
}
static gui_bool
gui_panel_edit_base(struct gui_rect *bounds, struct gui_edit *field,
struct gui_panel_layout *layout)
{
const struct gui_config *config;
struct gui_vec2 item_padding;
if (!gui_panel_widget(bounds, layout))
return gui_false;
config = layout->config;
item_padding = gui_config_property(config, GUI_PROPERTY_ITEM_PADDING);
field->border_size = 1;
field->rounding = config->rounding[GUI_ROUNDING_INPUT];
field->padding.x = item_padding.x;
field->padding.y = item_padding.y;
field->show_cursor = gui_true;
field->background = config->colors[GUI_COLOR_INPUT];
field->border = config->colors[GUI_COLOR_INPUT_BORDER];
field->cursor = config->colors[GUI_COLOR_INPUT_CURSOR];
field->text = config->colors[GUI_COLOR_INPUT_TEXT];
return gui_true;
}
gui_size
gui_panel_edit(struct gui_panel_layout *layout, gui_char *buffer, gui_size len,
gui_size max, gui_bool *active, enum gui_input_filter filter)
{
struct gui_rect bounds;
struct gui_edit field;
const struct gui_config *config = layout->config;
if (!gui_panel_edit_base(&bounds, &field, layout)) return len;
return gui_edit(layout->buffer, bounds.x, bounds.y, bounds.w, bounds.h,
buffer, len, max, active, &field, filter, layout->input, &config->font);
}
gui_size
gui_panel_edit_filtered(struct gui_panel_layout *layout, gui_char *buffer, gui_size len,
gui_size max, gui_bool *active, gui_filter filter)
{
struct gui_rect bounds;
struct gui_edit field;
const struct gui_config *config = layout->config;
if (!gui_panel_edit_base(&bounds, &field, layout)) return len;
return gui_edit_filtered(layout->buffer, bounds.x, bounds.y, bounds.w, bounds.h,
buffer, len, max, active, &field, filter, layout->input, &config->font);
}
gui_int
gui_panel_spinner(struct gui_panel_layout *layout, gui_int min, gui_int value,
gui_int max, gui_int step, gui_bool *active)
{
struct gui_rect bounds;
struct gui_spinner spinner;
const struct gui_config *config;
struct gui_command_buffer *out;
struct gui_vec2 item_padding;
if (!gui_panel_widget(&bounds, layout))
return value;
config = layout->config;
out = layout->buffer;
item_padding = gui_config_property(config, GUI_PROPERTY_ITEM_PADDING);
spinner.border_button = 1;
spinner.button_color = config->colors[GUI_COLOR_BUTTON];
spinner.button_border = config->colors[GUI_COLOR_BUTTON_BORDER];
spinner.button_triangle = config->colors[GUI_COLOR_SPINNER_TRIANGLE];
spinner.padding.x = item_padding.x;
spinner.padding.y = item_padding.y;
spinner.color = config->colors[GUI_COLOR_SPINNER];
spinner.border = config->colors[GUI_COLOR_SPINNER_BORDER];
spinner.text = config->colors[GUI_COLOR_SPINNER_TEXT];
spinner.show_cursor = gui_false;
return gui_spinner(out, bounds.x, bounds.y, bounds.w, bounds.h, &spinner,
min, value, max, step, active, layout->input, &layout->config->font);
}
gui_size
gui_panel_selector(struct gui_panel_layout *layout, const char *items[],
gui_size item_count, gui_size item_current)
{
struct gui_rect bounds;
struct gui_selector selector;
const struct gui_config *config;
struct gui_command_buffer *out;
GUI_ASSERT(items);
GUI_ASSERT(item_count);
GUI_ASSERT(item_current < item_count);
if (!gui_panel_widget(&bounds, layout))
return item_current;
out = layout->buffer;
config = layout->config;
selector.border_button = 1;
selector.button_color = config->colors[GUI_COLOR_BUTTON];
selector.button_border = config->colors[GUI_COLOR_BUTTON_BORDER];
selector.button_triangle = config->colors[GUI_COLOR_SELECTOR_TRIANGLE];
selector.color = config->colors[GUI_COLOR_SELECTOR];
selector.border = config->colors[GUI_COLOR_SELECTOR_BORDER];
selector.text = config->colors[GUI_COLOR_TEXT];
selector.text_bg = config->colors[GUI_COLOR_PANEL];
selector.padding = gui_config_property(config, GUI_PROPERTY_ITEM_PADDING);
return gui_selector(out, bounds.x, bounds.y, bounds.w, bounds.h, &selector,
items, item_count, item_current, layout->input, &layout->config->font);
}
void
gui_panel_graph_begin(struct gui_panel_layout *layout, struct gui_graph *graph,
enum gui_graph_type type, gui_size count, gui_float min_value, gui_float max_value)
{
struct gui_rect bounds = {0, 0, 0, 0};
const struct gui_config *config;
struct gui_command_buffer *out;
struct gui_color color;
struct gui_vec2 item_padding;
if (!gui_panel_widget(&bounds, layout)) {
gui_zero(graph, sizeof(*graph));
return;
}
config = layout->config;
out = layout->buffer;
item_padding = gui_config_property(config, GUI_PROPERTY_ITEM_PADDING);
color = (type == GUI_GRAPH_LINES) ?
config->colors[GUI_COLOR_PLOT]: config->colors[GUI_COLOR_HISTO];
gui_command_buffer_push_rect(out, bounds.x, bounds.y, bounds.w, bounds.h,
config->rounding[GUI_ROUNDING_GRAPH], color);
graph->valid = gui_true;
graph->type = type;
graph->index = 0;
graph->count = count;
graph->min = min_value;
graph->max = max_value;
graph->x = bounds.x + item_padding.x;
graph->y = bounds.y + item_padding.y;
graph->w = bounds.w - 2 * item_padding.x;
graph->h = bounds.h - 2 * item_padding.y;
graph->w = MAX(graph->w, 2 * item_padding.x);
graph->h = MAX(graph->h, 2 * item_padding.y);
graph->last.x = 0; graph->last.y = 0;
}
static gui_bool
gui_panel_graph_push_line(struct gui_panel_layout *l,
struct gui_graph *g, gui_float value)
{
struct gui_command_buffer *out = l->buffer;
const struct gui_config *config = l->config;
const struct gui_input *i = l->input;
struct gui_color color = config->colors[GUI_COLOR_PLOT_LINES];
gui_bool selected = gui_false;
gui_float step, range, ratio;
struct gui_vec2 cur;
GUI_ASSERT(g);
GUI_ASSERT(l);
GUI_ASSERT(out);
if (!g || !l || !g->valid || g->index >= g->count)
return gui_false;
step = g->w / (gui_float)g->count;
range = g->max - g->min;
ratio = (value - g->min) / range;
if (g->index == 0) {
g->last.x = g->x;
g->last.y = (g->y + g->h) - ratio * (gui_float)g->h;
if (i && GUI_INBOX(i->mouse_pos.x, i->mouse_pos.y, g->last.x-3, g->last.y-3, 6, 6)){
selected = (i->mouse_down && i->mouse_clicked) ? gui_true: gui_false;
color = config->colors[GUI_COLOR_PLOT_HIGHLIGHT];
}
gui_command_buffer_push_rect(out, g->last.x - 3, g->last.y - 3, 6, 6, 0, color);
g->index++;
return selected;
}
cur.x = g->x + (gui_float)(step * (gui_float)g->index);
cur.y = (g->y + g->h) - (ratio * (gui_float)g->h);
gui_command_buffer_push_line(out, g->last.x, g->last.y, cur.x, cur.y,
config->colors[GUI_COLOR_PLOT_LINES]);
if (i && GUI_INBOX(i->mouse_pos.x, i->mouse_pos.y, cur.x-3, cur.y-3, 6, 6)) {
selected = (i->mouse_down && i->mouse_clicked) ? gui_true: gui_false;
color = config->colors[GUI_COLOR_PLOT_HIGHLIGHT];
} else color = config->colors[GUI_COLOR_PLOT_LINES];
gui_command_buffer_push_rect(out, cur.x - 3, cur.y - 3, 6, 6, 0, color);
g->last.x = cur.x;
g->last.y = cur.y;
g->index++;
return selected;
}
static gui_bool
gui_panel_graph_push_column(struct gui_panel_layout *layout,
struct gui_graph *graph, gui_float value)
{
struct gui_command_buffer *out = layout->buffer;
const struct gui_config *config = layout->config;
const struct gui_input *in = layout->input;
struct gui_vec2 item_padding;
struct gui_color color;
gui_float ratio;
gui_bool selected = gui_false;
gui_float item_x, item_y;
gui_float item_w = 0.0f, item_h = 0.0f;
item_padding = gui_config_property(config, GUI_PROPERTY_ITEM_PADDING);
if (!graph->valid || graph->index >= graph->count)
return gui_false;
if (graph->count) {
gui_float padding = (gui_float)(graph->count-1) * item_padding.x;
item_w = (graph->w - padding) / (gui_float)(graph->count);
}
ratio = GUI_ABS(value) / graph->max;
color = (value < 0) ? config->colors[GUI_COLOR_HISTO_NEGATIVE]:
config->colors[GUI_COLOR_HISTO_BARS];
item_h = graph->h * ratio;
item_y = (graph->y + graph->h) - item_h;
item_x = graph->x + ((gui_float)graph->index * item_w);
item_x = item_x + ((gui_float)graph->index * item_padding.x);
if (in && GUI_INBOX(in->mouse_pos.x, in->mouse_pos.y, item_x, item_y, item_w, item_h)) {
selected = (in->mouse_down && in->mouse_clicked) ? (gui_int)graph->index: selected;
color = config->colors[GUI_COLOR_HISTO_HIGHLIGHT];
}
gui_command_buffer_push_rect(out, item_x, item_y, item_w, item_h, 0, color);
graph->index++;
return selected;
}
gui_bool
gui_panel_graph_push(struct gui_panel_layout *layout, struct gui_graph *graph,
gui_float value)
{
GUI_ASSERT(layout);
GUI_ASSERT(graph);
if (!layout || !graph || !layout->valid) return gui_false;
switch (graph->type) {
case GUI_GRAPH_LINES:
return gui_panel_graph_push_line(layout, graph, value);
case GUI_GRAPH_COLUMN:
return gui_panel_graph_push_column(layout, graph, value);
case GUI_GRAPH_MAX:
default: return gui_false;
}
}
void
gui_panel_graph_end(struct gui_panel_layout *layout, struct gui_graph *graph)
{
GUI_ASSERT(layout);
GUI_ASSERT(graph);
if (!layout || !graph) return;
graph->type = GUI_GRAPH_MAX;
graph->index = 0;
graph->count = 0;
graph->min = 0;
graph->max = 0;
graph->x = 0;
graph->y = 0;
graph->w = 0;
graph->h = 0;
}
gui_int
gui_panel_graph(struct gui_panel_layout *layout, enum gui_graph_type type,
const gui_float *values, gui_size count, gui_size offset)
{
gui_size i;
gui_int index = -1;
gui_float min_value;
gui_float max_value;
struct gui_graph graph;
GUI_ASSERT(layout);
GUI_ASSERT(values);
GUI_ASSERT(count);
if (!layout || !layout->valid || !values || !count)
return -1;
max_value = values[0];
min_value = values[0];
for (i = offset; i < count; ++i) {
if (values[i] > max_value)
max_value = values[i];
if (values[i] < min_value)
min_value = values[i];
}
gui_panel_graph_begin(layout, &graph, type, count, min_value, max_value);
for (i = offset; i < count; ++i) {
if (gui_panel_graph_push(layout, &graph, values[i]))
index = (gui_int)i;
}
gui_panel_graph_end(layout, &graph);
return index;
}
gui_int
gui_panel_graph_ex(struct gui_panel_layout *layout, enum gui_graph_type type,
gui_size count, gui_float(*get_value)(void*, gui_size), void *userdata)
{
gui_size i;
gui_int index = -1;
gui_float min_value;
gui_float max_value;
struct gui_graph graph;
GUI_ASSERT(layout);
GUI_ASSERT(get_value);
GUI_ASSERT(count);
if (!layout || !layout->valid || !get_value || !count)
return -1;
max_value = get_value(userdata, 0);
min_value = max_value;
for (i = 1; i < count; ++i) {
gui_float value = get_value(userdata, i);
if (value > max_value)
max_value = value;
if (value < min_value)
min_value = value;
}
gui_panel_graph_begin(layout, &graph, type, count, min_value, max_value);
for (i = 0; i < count; ++i) {
gui_float value = get_value(userdata, i);
if (gui_panel_graph_push(layout, &graph, value))
index = (gui_int)i;
}
gui_panel_graph_end(layout, &graph);
return index;
}
static void
gui_panel_table_hline(struct gui_panel_layout *l, gui_size row_height)
{
struct gui_command_buffer *out = l->buffer;
const struct gui_config *c = l->config;
const struct gui_vec2 item_padding = gui_config_property(c, GUI_PROPERTY_ITEM_PADDING);
const struct gui_vec2 item_spacing = gui_config_property(c, GUI_PROPERTY_ITEM_SPACING);
gui_float y = l->at_y + (gui_float)row_height - l->offset;
gui_float x = l->at_x + item_spacing.x + item_padding.x;
gui_float w = l->width - (2 * item_spacing.x + 2 * item_padding.x);
gui_command_buffer_push_line(out, x, y, x + w, y, c->colors[GUI_COLOR_TABLE_LINES]);
}
static void
gui_panel_table_vline(struct gui_panel_layout *layout, gui_size cols)
{
gui_size i;
struct gui_command_buffer *out = layout->buffer;
const struct gui_config *config = layout->config;
for (i = 0; i < cols - 1; ++i) {
gui_float y, h;
struct gui_rect bounds;
gui_panel_alloc_space(&bounds, layout);
y = layout->at_y + layout->row_height;
h = layout->row_height;
gui_command_buffer_push_line(out, bounds.x + bounds.w, y,
bounds.x + bounds.w, h, config->colors[GUI_COLOR_TABLE_LINES]);
}
layout->index -= i;
}
void
gui_panel_table_begin(struct gui_panel_layout *layout, gui_flags flags,
gui_size row_height, gui_size cols)
{
GUI_ASSERT(layout);
if (!layout || !layout->valid) return;
layout->is_table = gui_true;
layout->tbl_flags = flags;
gui_panel_row(layout, (gui_float)row_height, cols);
if (layout->tbl_flags & GUI_TABLE_HHEADER)
gui_panel_table_hline(layout, row_height);
if (layout->tbl_flags & GUI_TABLE_VHEADER)
gui_panel_table_vline(layout, cols);
}
void
gui_panel_table_row(struct gui_panel_layout *layout)
{
const struct gui_config *config;
struct gui_vec2 item_spacing;
GUI_ASSERT(layout);
if (!layout) return;
config = layout->config;
item_spacing = gui_config_property(config, GUI_PROPERTY_ITEM_SPACING);
gui_panel_row(layout, layout->row_height - item_spacing.y, layout->row_columns);
if (layout->tbl_flags & GUI_TABLE_HBODY)
gui_panel_table_hline(layout, (gui_size)(layout->row_height - item_spacing.y));
if (layout->tbl_flags & GUI_TABLE_VBODY)
gui_panel_table_vline(layout, layout->row_columns);
}
void
gui_panel_table_end(struct gui_panel_layout *layout)
{
layout->is_table = gui_false;
}
gui_bool
gui_panel_tab_begin(struct gui_panel_layout *parent, struct gui_panel_layout *tab,
const char *title, gui_bool minimized)
{
struct gui_rect bounds;
struct gui_command_buffer *out;
struct gui_panel panel;
struct gui_rect clip;
gui_flags flags;
GUI_ASSERT(parent);
GUI_ASSERT(tab);
if (!parent || !tab) return gui_true;
out = parent->buffer;
gui_zero(tab, sizeof(*tab));
tab->valid = !minimized;
if (!parent->valid) {
tab->valid = gui_false;
tab->config = parent->config;
tab->buffer = parent->buffer;
tab->input = parent->input;
return minimized;
}
parent->index = 0;
gui_panel_row(parent, 0, 1);
gui_panel_alloc_space(&bounds, parent);
flags = GUI_PANEL_BORDER|GUI_PANEL_MINIMIZABLE|GUI_PANEL_TAB|GUI_PANEL_BORDER_HEADER;
gui_panel_init(&panel,bounds.x,bounds.y,bounds.w,gui_null_rect.h,flags,out,parent->config);
panel.minimized = minimized;
gui_panel_begin(tab, &panel, title, parent->input);
gui_unify(&clip, &parent->clip, tab->clip.x, tab->clip.y, tab->clip.x + tab->clip.w,
tab->clip.y + tab->clip.h);
gui_command_buffer_push_scissor(out, clip.x, clip.y, clip.w, clip.h);
return panel.minimized;
}
void
gui_panel_tab_end(struct gui_panel_layout *p, struct gui_panel_layout *t)
{
struct gui_panel panel;
struct gui_command_buffer *out;
struct gui_vec2 item_spacing;
GUI_ASSERT(t);
GUI_ASSERT(p);
if (!p || !t || !p->valid)
return;
panel.x = t->at_x;
panel.y = t->y;
panel.flags = GUI_PANEL_BORDER|GUI_PANEL_MINIMIZABLE|GUI_PANEL_TAB;
gui_panel_end(t, &panel);
item_spacing = gui_config_property(p->config, GUI_PROPERTY_ITEM_SPACING);
p->at_y += t->height + item_spacing.y;
out = p->buffer;
gui_command_buffer_push_scissor(out, p->clip.x, p->clip.y, p->clip.w, p->clip.h);
}
void
gui_panel_group_begin(struct gui_panel_layout *p, struct gui_panel_layout *g,
const char *title, gui_float offset)
{
gui_flags flags;
struct gui_rect bounds;
struct gui_command_buffer *out;
struct gui_rect clip;
struct gui_panel panel;
const struct gui_rect *c;
GUI_ASSERT(p);
GUI_ASSERT(g);
if (!p || !g) return;
if (!p->valid)
goto failed;
gui_panel_alloc_space(&bounds, p);
gui_zero(g, sizeof(*g));
c = &p->clip;
if (!GUI_INTERSECT(c->x, c->y, c->w, c->h, bounds.x, bounds.y, bounds.w, bounds.h))
goto failed;
out = p->buffer;
flags = GUI_PANEL_BORDER|GUI_PANEL_SCROLLBAR|GUI_PANEL_TAB;
gui_panel_init(&panel, bounds.x,bounds.y,bounds.w,bounds.h,flags, out, p->config);
gui_panel_begin(g, &panel, title, p->input);
g->offset = offset;
gui_unify(&clip, &p->clip, g->clip.x, g->clip.y, g->clip.x + g->clip.w,
g->clip.y + g->clip.h);
gui_command_buffer_push_scissor(out, clip.x, clip.y, clip.w, clip.h);
return;
failed:
g->valid = gui_false;
g->config = p->config;
g->buffer = p->buffer;
g->input = p->input;
}
gui_float
gui_panel_group_end(struct gui_panel_layout *p, struct gui_panel_layout *g)
{
struct gui_command_buffer *out;
struct gui_rect clip;
struct gui_panel pan;
GUI_ASSERT(p);
GUI_ASSERT(g);
if (!p || !g) return 0;
if (!p->valid) return 0;
out = p->buffer;
pan.x = g->at_x;
pan.y = g->y;
pan.flags = GUI_PANEL_BORDER|GUI_PANEL_MINIMIZABLE|GUI_PANEL_TAB|GUI_PANEL_SCROLLBAR;
gui_unify(&clip, &p->clip, g->clip.x, g->clip.y, g->x + g->w, g->y + g->h);
gui_command_buffer_push_scissor(out, clip.x, clip.y, clip.w, clip.h);
gui_panel_end(g, &pan);
gui_command_buffer_push_scissor(out, p->clip.x, p->clip.y, p->clip.w, p->clip.h);
return pan.offset;
}
gui_size
gui_panel_shelf_begin(struct gui_panel_layout *parent, struct gui_panel_layout *shelf,
const char *tabs[], gui_size size, gui_size active, gui_float offset)
{
const struct gui_config *config;
struct gui_command_buffer *out;
const struct gui_font *font;
struct gui_vec2 item_spacing;
struct gui_vec2 item_padding;
struct gui_vec2 panel_padding;
struct gui_rect bounds;
struct gui_rect clip;
struct gui_rect *c;
struct gui_panel panel;
struct gui_button button;
gui_float header_x, header_y;
gui_float header_w, header_h;
gui_float item_width;
gui_flags flags;
gui_size i;
GUI_ASSERT(parent);
GUI_ASSERT(tabs);
GUI_ASSERT(shelf);
GUI_ASSERT(active < size);
if (!parent || !shelf || !tabs || active >= size)
return active;
if (!parent->valid)
goto failed;
config = parent->config;
out = parent->buffer;
font = &config->font;
item_padding = gui_config_property(config, GUI_PROPERTY_ITEM_PADDING);
item_spacing = gui_config_property(config, GUI_PROPERTY_ITEM_SPACING);
panel_padding = gui_config_property(config, GUI_PROPERTY_PADDING);
gui_panel_alloc_space(&bounds, parent);
gui_zero(shelf, sizeof(*shelf));
c = &parent->clip;
if (!GUI_INTERSECT(c->x, c->y, c->w, c->h, bounds.x, bounds.y, bounds.w, bounds.h))
goto failed;
header_x = bounds.x;
header_y = bounds.y;
header_w = bounds.w;
header_h = panel_padding.y + 3 * item_padding.y + config->font.height;
item_width = (header_w - (gui_float)size) / (gui_float)size;
button.border = 1;
button.rounding = 0;
button.padding.x = item_padding.x;
button.padding.y = item_padding.y;
button.foreground = config->colors[GUI_COLOR_BORDER];
for (i = 0; i < size; i++) {
gui_float button_x, button_y;
gui_float button_w, button_h;
gui_size text_width = font->width(font->userdata,
(const gui_char*)tabs[i], gui_strsiz(tabs[i]));
text_width = text_width + (gui_size)(2 * item_spacing.x);
button_y = header_y;
button_h = header_h;
button_x = header_x;
button_w = MIN(item_width, (gui_float)text_width);
header_x += MIN(item_width, (gui_float)text_width);
if ((button_x + button_w) >= (bounds.x + bounds.w)) break;
if (active != i) {
button_y += item_padding.y;
button_h -= item_padding.y;
button.background = config->colors[GUI_COLOR_SHELF];
button.content = config->colors[GUI_COLOR_SHELF_TEXT];
button.highlight = config->colors[GUI_COLOR_SHELF];
button.highlight_content = config->colors[GUI_COLOR_SHELF_TEXT];
} else {
button.background = config->colors[GUI_COLOR_SHELF_ACTIVE];
button.content = config->colors[GUI_COLOR_SHELF_ACTIVE_TEXT];
button.highlight = config->colors[GUI_COLOR_SHELF_ACTIVE];
button.highlight_content = config->colors[GUI_COLOR_SHELF_ACTIVE_TEXT];
}
if (gui_button_text(out, button_x, button_y, button_w, button_h,
tabs[i], GUI_BUTTON_DEFAULT, &button, parent->input, &config->font))
active = i;
}
bounds.y += header_h;
bounds.h -= header_h;
flags = GUI_PANEL_BORDER|GUI_PANEL_SCROLLBAR|GUI_PANEL_TAB|GUI_PANEL_NO_HEADER;
gui_panel_init(&panel, bounds.x, bounds.y, bounds.w, bounds.h, flags, out, config);
gui_panel_begin(shelf, &panel, NULL, parent->input);
shelf->offset = offset;
gui_unify(&clip, &parent->clip, shelf->clip.x, shelf->clip.y,
shelf->clip.x + shelf->clip.w, shelf->clip.y + shelf->clip.h);
gui_command_buffer_push_scissor(out, clip.x, clip.y, clip.w, clip.h);
return active;
failed:
shelf->valid = gui_false;
shelf->config = parent->config;
shelf->buffer = parent->buffer;
shelf->input = parent->input;
return active;
}
gui_float
gui_panel_shelf_end(struct gui_panel_layout *p, struct gui_panel_layout *s)
{
struct gui_command_buffer *out;
struct gui_rect clip;
struct gui_panel pan;
GUI_ASSERT(p);
GUI_ASSERT(s);
if (!p || !s) return 0;
if (!p->valid) return 0;
out = p->buffer;
pan.x = s->at_x; pan.y = s->y;
pan.flags = GUI_PANEL_BORDER|GUI_PANEL_MINIMIZABLE|GUI_PANEL_TAB|GUI_PANEL_SCROLLBAR;
gui_unify(&clip, &p->clip, s->clip.x, s->clip.y, s->x + s->w, s->y + s->h);
gui_command_buffer_push_scissor(out, clip.x, clip.y, clip.w, clip.h);
gui_panel_end(s, &pan);
gui_command_buffer_push_scissor(out, p->clip.x, p->clip.y, p->clip.w, p->clip.h);
return pan.offset;
}
void
gui_panel_end(struct gui_panel_layout *layout, struct gui_panel *panel)
{
const struct gui_config *config;
struct gui_command_buffer *out;
gui_float scrollbar_width;
struct gui_vec2 item_padding;
struct gui_vec2 item_spacing;
struct gui_vec2 panel_padding;
struct gui_vec2 scaler_size;
GUI_ASSERT(layout);
GUI_ASSERT(panel);
if (!panel || !layout) return;
layout->at_y += layout->row_height;
config = layout->config;
out = layout->buffer;
item_padding = gui_config_property(config, GUI_PROPERTY_ITEM_PADDING);
item_spacing = gui_config_property(config, GUI_PROPERTY_ITEM_SPACING);
panel_padding = gui_config_property(config, GUI_PROPERTY_PADDING);
scrollbar_width = gui_config_property(config, GUI_PROPERTY_SCROLLBAR_WIDTH).x;
scaler_size = gui_config_property(config, GUI_PROPERTY_SCALER_SIZE);
if (!(panel->flags & GUI_PANEL_TAB))
gui_command_buffer_push_scissor(out, layout->x,layout->y,layout->w+1,layout->h+1);
if (panel->flags & GUI_PANEL_SCROLLBAR && layout->valid) {
struct gui_scroll scroll;
gui_float panel_y;
gui_float scroll_x, scroll_y;
gui_float scroll_w, scroll_h;
gui_float scroll_target, scroll_offset, scroll_step;
scroll_x = layout->at_x + layout->width;
scroll_y = (panel->flags & GUI_PANEL_BORDER) ? layout->y + 1 : layout->y;
scroll_y += layout->header_height;
scroll_w = scrollbar_width;
scroll_h = layout->height;
scroll_offset = layout->offset;
scroll_step = layout->height * 0.25f;
scroll.rounding = config->rounding[GUI_ROUNDING_SCROLLBAR];
scroll.background = config->colors[GUI_COLOR_SCROLLBAR];
scroll.foreground = config->colors[GUI_COLOR_SCROLLBAR_CURSOR];
scroll.border = config->colors[GUI_COLOR_SCROLLBAR_BORDER];
if (panel->flags & GUI_PANEL_BORDER) scroll_h -= 1;
scroll_target = (layout->at_y-layout->y)-(layout->header_height+2*item_spacing.y);
panel->offset = gui_scroll(out, scroll_x, scroll_y, scroll_w, scroll_h,
scroll_offset, scroll_target, scroll_step,
&scroll, layout->input);
panel_y = layout->y + layout->height + layout->header_height - panel_padding.y;
gui_command_buffer_push_rect(out, layout->x,panel_y,layout->width,panel_padding.y,
0, config->colors[GUI_COLOR_PANEL]);
} else layout->height = layout->at_y - layout->y;
if ((panel->flags & GUI_PANEL_SCALEABLE) && layout->valid) {
struct gui_color col = config->colors[GUI_COLOR_SCALER];
gui_float scaler_w = MAX(0, scaler_size.x - item_padding.x);
gui_float scaler_h = MAX(0, scaler_size.y - item_padding.y);
gui_float scaler_x = (layout->x + layout->w) - (item_padding.x + scaler_w);
gui_float scaler_y = layout->y + layout->h - scaler_size.y;
gui_command_buffer_push_triangle(out, scaler_x + scaler_w, scaler_y,
scaler_x + scaler_w, scaler_y + scaler_h, scaler_x, scaler_y + scaler_h, col);
}
if (panel->flags & GUI_PANEL_BORDER) {
const gui_float width = (panel->flags & GUI_PANEL_SCROLLBAR) ?
layout->width + scrollbar_width : layout->width;
const gui_float padding_y = (!layout->valid) ?
panel->y + layout->header_height:
(panel->flags & GUI_PANEL_SCROLLBAR) ?
panel->y + layout->h :
panel->y + layout->height + item_padding.y;
gui_command_buffer_push_line(out, panel->x, padding_y, panel->x + width,
padding_y, config->colors[GUI_COLOR_BORDER]);
gui_command_buffer_push_line(out, panel->x, panel->y, panel->x,
padding_y, config->colors[GUI_COLOR_BORDER]);
gui_command_buffer_push_line(out, panel->x + width, panel->y, panel->x + width,
padding_y, config->colors[GUI_COLOR_BORDER]);
}
gui_command_buffer_push_scissor(out, 0, 0, gui_null_rect.w, gui_null_rect.h);
}
/*
* ==============================================================
*
* Stack
*
* ===============================================================
*/
void
gui_stack_clear(struct gui_stack *stack)
{
stack->begin = NULL;
stack->end = NULL;
stack->count = 0;
}
void
gui_stack_push(struct gui_stack *stack, struct gui_panel *panel)
{
struct gui_panel *iter;
GUI_ASSERT(stack);
GUI_ASSERT(panel);
if (!stack || !panel) return;
gui_foreach_panel(iter, stack)
if (iter == panel) return;
if (!stack->begin) {
panel->next = NULL;
panel->prev = NULL;
stack->begin = panel;
stack->end = panel;
stack->count = 1;
return;
}
stack->end->next = panel;
panel->prev = stack->end;
panel->next = NULL;
stack->end = panel;
stack->count++;
}
void
gui_stack_pop(struct gui_stack *stack, struct gui_panel*panel)
{
GUI_ASSERT(stack);
GUI_ASSERT(panel);
if (!stack || !panel) return;
if (panel->prev)
panel->prev->next = panel->next;
if (panel->next)
panel->next->prev = panel->prev;
if (stack->begin == panel)
stack->begin = panel->next;
if (stack->end == panel)
stack->end = panel->prev;
panel->next = NULL;
panel->prev = NULL;
}
/*
* ==============================================================
*
* Layout
*
* ===============================================================
*/
void
gui_layout_init(struct gui_layout *layout, const struct gui_layout_config *config,
gui_size width, gui_size height)
{
gui_float left, right;
gui_float centerh, centerv;
gui_float bottom, top;
GUI_ASSERT(layout);
GUI_ASSERT(config);
if (!layout || !config) return;
gui_zero(layout, sizeof(*layout));
layout->state = GUI_LAYOUT_ACTIVE;
layout->width = width;
layout->height = height;
left = GUI_SATURATE(config->left);
right = GUI_SATURATE(config->right);
centerh = GUI_SATURATE(config->centerh);
centerv = GUI_SATURATE(config->centerv);
bottom = GUI_SATURATE(config->bottom);
top = GUI_SATURATE(config->top);
layout->slots[GUI_SLOT_TOP].ratio = gui_vec2(1.0f, top);
layout->slots[GUI_SLOT_LEFT].ratio = gui_vec2(left, centerv);
layout->slots[GUI_SLOT_BOTTOM].ratio = gui_vec2(1.0f, bottom);
layout->slots[GUI_SLOT_CENTER].ratio = gui_vec2(centerh, centerv);
layout->slots[GUI_SLOT_RIGHT].ratio = gui_vec2(right, centerv);
layout->slots[GUI_SLOT_TOP].offset = gui_vec2(0.0f, 0.0f);
layout->slots[GUI_SLOT_LEFT].offset = gui_vec2(0.0f, top);
layout->slots[GUI_SLOT_BOTTOM].offset = gui_vec2(0.0f, top + centerv);
layout->slots[GUI_SLOT_CENTER].offset = gui_vec2(left, top);
layout->slots[GUI_SLOT_RIGHT].offset = gui_vec2(left + centerh, top);
}
void
gui_layout_set_size(struct gui_layout *layout, gui_size width, gui_size height)
{
GUI_ASSERT(layout);
if (!layout) return;
layout->width = width;
layout->height = height;
}
void
gui_layout_slot(struct gui_layout *layout, enum gui_layout_slot_index slot,
enum gui_layout_format format, gui_size count)
{
GUI_ASSERT(layout);
GUI_ASSERT(count);
GUI_ASSERT(slot >= GUI_SLOT_TOP && slot < GUI_SLOT_MAX);
if (!layout || !count) return;
layout->slots[slot].capacity = count;
layout->slots[slot].format = format;
}
#endif
#ifdef __cplusplus
}
#endif
#endif /* GUI_H_ */