split panel into persitent and transient state

This commit is contained in:
vurtun 2015-04-25 15:30:51 +02:00
parent 1d417a5336
commit 7109605c20
4 changed files with 616 additions and 508 deletions

View File

@ -17,28 +17,28 @@ streamlined user development speed in mind.
- Suited for embedding into graphical applications
- No global hidden state
- No direct dependencies (not even libc!)
- No memory allocation needed
- Full memory management control
- Renderer and platform independent
- Configurable
- UTF-8 supported
## Functionality
+ Label
+ Buttons(Text, Triangle, Color, Toggle, Icon)
+ Buttons
+ Slider
+ Progressbar
+ Checkbox
+ Radiobutton
+ Input field
+ Input
+ Spinner
+ Selector
+ Linegraph
+ Histogram
+ Panel
+ Layouts(Tabs, Groups, Shelf)
+ Layouts
## Limitations
- Does NOT provide window management
- Does NOT provide os window management
- Does NOT provide input handling
- Does NOT provide a renderer backend
- Does NOT implement a font library
@ -55,8 +55,8 @@ instead of seperating them like retain mode GUIs.
Since there is no to minimal internal state in immediate mode user interfaces,
updates have to occur every frame which on one hand is more drawing expensive than classic
ratained GUI implementations but on the other hand grants a lot more flexibility and
support for overall changes. In addition without any state there is no need to
transfer state between your program, the gui state and the user which greatly
support for overall layout changes. In addition without any state there is no need to
transfer state between your program, the gui and the user which greatly
simplifies code. Further traits of immediate mode graphic user interfaces are a
code driven style, centralized flow control, easy extensibility and
understandablity.
@ -79,6 +79,13 @@ font data and the width and height of the canvas drawing area.
Therefore the canvas is the heart of the toolkit and is probably the biggest
chunk of work to be done by the user.
### Font
Since there is no direct font implementation in the toolkit but font handling is
still an aspect of a gui implemenatation the gui struct was introduced. It only
contains the bare minimum of what is needed for font handling with a handle to
your font structure, the font height and a callback to calculate the width of a
given string.
### Configuration
The gui toolkit provides a number of different attributes that can be
configured, like spacing, padding, size and color.
@ -89,6 +96,46 @@ filled by the user or can be setup with some default values by the function
`gui_default_config`. Modification on the fly to the `gui_config` struct is in
true immedate mode fashion possible and supported.
### Input
The input structure holds the user input over the course of the frame and
manages the complete modification of widget and panel state. Like the panel and
buffering the input is an immediate mode API and consist of an begin sequence
point with `gui_input_begin` and a end sequence point with `gui_input_end`.
All modifications to the input struct can only occur between both of these
sequence points while all outside modifcation provoke undefined behavior.
### Buffering
For the purpose of defered drawing or the implementation of overlapping panels
the command buffering API was added. The command buffer hereby holds a queue of
drawing commands for a number of primitives like line, rectangle, circle,
triangle and text. The memory for the command buffer can be provided by the user
in three different ways. First a fixed memory block can be given over to the
command buffer which fills the memory up until no memory is left. The seond way
is still using a fixed size block but rellocating a new block of memory at the
end of the frame, if not enough memory was provided. The final way of memory
management is by providing allocator callbacks with alloc, realloc and free.
In true immediate mode fashion the buffering API is by base around a sequence
point API with an begin sequence point `gui_output_begin` and a end sequence
point with `gui_output_end` and modification of state between both points. Just
like the input API the buffer modification before the begining or after the end
sequence point is undefined behavior.
```c
struct gui_memory memory = {...};
struct gui_memory_status status = {0};
struct gui_command_list out = {0};
struct gui_command_buffer buffer = {0};
struct gui_canvas canvas = {0};
gui_output_init_fixed(buffer, &memory);
while (1) {
gui_output_begin(&canvas, &buffer, window_width, window_height);
/* add commands by using the canvas */
gui_output_end(&list, buffer, &status);
}
```
### Widgets
The minimal widget API provides a basic number of widgets and is designed for
uses cases where only a small number of basic widgets are needed without any kind of
@ -97,7 +144,8 @@ draw to, positional and widgets specific data as well as user input
and returns the from the user input modified state of the widget.
```c
struct gui_input in = {0};
struct gui_input input = {0};
struct gui_font font = {...};
struct gui_canvas canvas = {...};
struct gui_button style = {...};
@ -105,7 +153,7 @@ while (1) {
gui_input_begin(&input);
/* record input */
gui_input_end(&input);
if(gui_button_text(&canvas, 0, 0, 100, 30, &style, "ok", GUI_BUTTON_DEFAULT, &input))
if(gui_button_text(&canvas, 0, 0, 100, 30, "ok", GUI_BUTTON_DEFAULT, &style, &input, &font))
fprintf(stdout, "button pressed!\n");
}
```
@ -117,31 +165,38 @@ widgets but in true immediate mode fashion does not save any widget state from
widgets that have been added to the panel. In addition the panel enables a
number of nice features for a group of widgets like panel movement, scaling,
closing and minimizing. An additional use for panels is to further group widgets
in panels to tabs, groups and shelfs. The state of internal panels (tabs,
groups. shelf) is only needed over the course of the build up unlike normal
panels, which further emphasizes the minimal state mindset.
in panels to tabs, groups and shelfs.
The panel is divided into a persistent state struct with `struct gui_panel` with a number
of attributes which have a persistent life time outside the frame and the panel layout
`struct gui_panel_layout` with a transient frame life time. While the layout
state is constantly modifed over the course of the frame the panel struct is
only modifed at the immendiate mode sequence points `gui_panel_begin` and
`gui_panel_end`. Therefore all changes to the panel struct inside of both
sequence points has no effect on the current frame and is only visible in the
next frame.
```c
struct gui_panel panel;
struct gui_config config;
struct gui_input in = {0};
struct gui_panel panel = {0};
struct gui_font font = {...}
struct gui_input input = {0};
struct gui_canvas canvas = {...};
gui_default_config(&config);
gui_panel_init(&panel, 50, 50, 300, 200, 0, &config, &font);
while (1) {
gui_input_begin(&input);
/* record input */
gui_input_end(&input);
gui_panel_begin(&panel, "Demo", panel.x, panel.y, panel.width, panel.height,
GUI_PANEL_CLOSEABLE|GUI_PANEL_MINIMIZABLE|GUI_PANEL_BORDER|
GUI_PANEL_MOVEABLE|GUI_PANEL_SCALEABLE, &config, &canvas, &in);
gui_panel_layout(&panel, 30, 1);
if (gui_panel_button_text(&panel, "button", GUI_BUTTON_DEFAULT))
struct gui_panel_layout layout;
gui_panel_begin(&layout, &panel, "Demo", &canvas, &in);
gui_panel_layout(&layout, 30, 1);
if (gui_panel_button_text(&layout, "button", GUI_BUTTON_DEFAULT))
fprintf(stdout, "button pressed!\n");
value = gui_panel_slider(&panel, 0, value, 10, 1);
progress = gui_panel_progress(&panel, progress, 100, gui_true);
gui_panel_end(&panel);
value = gui_panel_slider(&layout, 0, value, 10, 1);
progress = gui_panel_progress(&layout, progress, 100, gui_true);
gui_panel_end(&layout, &panel);
}
```

View File

@ -29,23 +29,6 @@ typedef struct XFont XFont;
typedef struct XSurface XSurface;
typedef struct XWindow XWindow;
struct demo {
gui_char in_buf[MAX_BUFFER];
gui_size in_len;
gui_bool in_act;
gui_bool check;
gui_int option;
gui_float slider;
gui_size prog;
gui_int spinner;
gui_bool spin_act;
gui_size item_cur;
gui_size current;
gui_bool tab_minimized;
gui_float group_offset;
gui_float shelf_offset;
};
struct XFont {
int ascent;
int descent;
@ -77,6 +60,23 @@ struct XWindow {
unsigned int height;
};
struct demo {
gui_char in_buf[MAX_BUFFER];
gui_size in_len;
gui_bool in_act;
gui_bool check;
gui_int option;
gui_float slider;
gui_size prog;
gui_int spinner;
gui_bool spin_act;
gui_size item_cur;
gui_size current;
gui_bool tab_minimized;
gui_float group_offset;
gui_float shelf_offset;
};
static void
die(const char *fmt, ...)
{
@ -405,7 +405,7 @@ resize(struct XWindow *xw, XSurface *surf)
}
static void
demo_panel(struct gui_panel *panel, struct demo *demo)
demo_panel(struct gui_panel_layout *panel, struct demo *demo)
{
gui_int i = 0;
enum {HISTO, PLOT};
@ -413,12 +413,11 @@ demo_panel(struct gui_panel *panel, struct demo *demo)
const gui_float values[] = {8.0f, 15.0f, 20.0f, 12.0f, 30.0f};
const char *items[] = {"Fist", "Pistol", "Shotgun", "Railgun", "BFG"};
const char *options[] = {"easy", "normal", "hard", "hell", "doom", "godlike"};
struct gui_panel tab;
struct gui_panel_layout tab;
/* Tabs */
gui_panel_layout(panel, 100, 1);
demo->tab_minimized = gui_panel_tab_begin(panel, &tab, "Difficulty", demo->tab_minimized);
gui_panel_layout(&tab, 30, 3);
gui_panel_row(&tab, 30, 3);
for (i = 0; i < (gui_int)LEN(options); i++) {
if (gui_panel_option(&tab, options[i], demo->option == i))
demo->option = i;
@ -426,9 +425,9 @@ demo_panel(struct gui_panel *panel, struct demo *demo)
gui_panel_tab_end(panel, &tab);
/* Shelf */
gui_panel_layout(panel, 200, 2);
gui_panel_row(panel, 200, 2);
demo->current = gui_panel_shelf_begin(panel, &tab, shelfs, LEN(shelfs), demo->current, demo->shelf_offset);
gui_panel_layout(&tab, 100, 1);
gui_panel_row(&tab, 100, 1);
if (demo->current == HISTO) {
gui_panel_histo(&tab, values, LEN(values));
} else {
@ -438,7 +437,7 @@ demo_panel(struct gui_panel *panel, struct demo *demo)
/* Group */
gui_panel_group_begin(panel, &tab, "Options", demo->group_offset);
gui_panel_layout(&tab, 30, 1);
gui_panel_row(&tab, 30, 1);
if (gui_panel_button_text(&tab, "button", GUI_BUTTON_DEFAULT))
fprintf(stdout, "button pressed!\n");
demo->check = gui_panel_check(&tab, "advanced", demo->check);
@ -455,25 +454,24 @@ main(int argc, char *argv[])
{
long dt;
long started;
XWindow xw;
XSurface *surf;
XFont *xfont;
gui_bool running = gui_true;
struct demo demo;
/* GUI */
struct gui_input in;
struct gui_font font_data;
struct gui_font font;
struct gui_memory memory;
struct gui_context *ctx;
struct gui_config config;
struct gui_canvas canvas;
struct gui_command_buffer buffer;
struct gui_command_list list;
struct gui_panel panel;
struct gui_canvas canvas;
struct gui_panel_layout layout;
/* Window */
XWindow xw;
XSurface *surf;
XFont *font;
UNUSED(argc); UNUSED(argv);
memset(&xw, 0, sizeof xw);
xw.dpy = XOpenDisplay(NULL);
@ -494,19 +492,22 @@ main(int argc, char *argv[])
xw.width = (unsigned int)xw.attr.width;
xw.height = (unsigned int)xw.attr.height;
surf = surface_create(xw.dpy, xw.screen, xw.win, xw.width, xw.height);
font = font_create(xw.dpy, "fixed");
xfont = font_create(xw.dpy, "fixed");
/* GUI */
memset(&in, 0, sizeof in);
memset(&panel, 0, sizeof panel);
gui_default_config(&config);
memory.memory = calloc(MAX_MEMORY, 1);
memory.size = MAX_MEMORY;
font_data.userdata = font;
font_data.height = (gui_float)font->height;
font_data.width = font_get_text_width;
panel.x = 50; panel.y = 50;
panel.w = 420; panel.h = 300;
gui_output_init_fixed(&buffer, &memory);
font.userdata = xfont;
font.height = (gui_float)xfont->height;
font.width = font_get_text_width;
gui_default_config(&config);
gui_panel_init(&panel, 50, 50, 420, 300,
GUI_PANEL_BORDER|GUI_PANEL_MOVEABLE|
GUI_PANEL_CLOSEABLE|GUI_PANEL_SCALEABLE|
GUI_PANEL_MINIMIZABLE, &config, &font);
/* Demo */
memset(&demo, 0, sizeof(demo));
@ -532,13 +533,11 @@ main(int argc, char *argv[])
gui_input_end(&in);
/* GUI */
gui_output_begin_fixed(&buffer, &canvas, &memory, xw.width, xw.height);
running = gui_panel_begin(&panel, "Demo", panel.x, panel.y, panel.w, panel.h,
GUI_PANEL_CLOSEABLE|GUI_PANEL_MINIMIZABLE|GUI_PANEL_BORDER|
GUI_PANEL_MOVEABLE|GUI_PANEL_SCALEABLE, &config, &canvas, &font_data, &in);
demo_panel(&panel, &demo);
gui_panel_end(&panel);
gui_output_end(&buffer, &list, &canvas, NULL);
gui_output_begin(&canvas, &buffer, xw.width, xw.height);
running = gui_panel_begin(&layout, &panel , "Demo", &canvas, &in);
demo_panel(&layout, &demo);
gui_panel_end(&layout, &panel);
gui_output_end(&list, &buffer, &canvas, NULL);
/* Draw */
XClearWindow(xw.dpy, xw.win);
@ -553,7 +552,8 @@ main(int argc, char *argv[])
sleep_for(DTIME - dt);
}
font_del(xw.dpy, font);
free(memory.memory);
font_del(xw.dpy, xfont);
surface_del(surf);
XUnmapWindow(xw.dpy, xw.win);
XFreeColormap(xw.dpy, xw.cmap);

834
gui.c

File diff suppressed because it is too large Load Diff

97
gui.h
View File

@ -45,7 +45,6 @@ typedef unsigned long gui_size;
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_colorf {gui_float 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;};
@ -93,7 +92,6 @@ enum gui_text_align {
struct gui_text {
struct gui_vec2 padding;
enum gui_text_align align;
struct gui_color foreground;
struct gui_color background;
};
@ -347,21 +345,30 @@ enum gui_panel_flags {
};
struct gui_panel {
gui_flags flags;
gui_float x, y;
gui_float w, h;
gui_flags flags;
gui_float offset;
gui_bool minimized;
struct gui_font font;
const struct gui_config *config;
};
struct gui_panel_layout {
gui_float x, y, w, h;
gui_float offset;
gui_bool valid;
gui_float at_x;
gui_float at_y;
gui_float width, height;
gui_size index;
gui_float width, height;
gui_float header_height;
gui_float row_height;
gui_size row_columns;
gui_float offset;
gui_bool minimized;
struct gui_rect clip;
struct gui_font font;
const struct gui_input *in;
const struct gui_config *config;
const struct gui_input *input;
const struct gui_canvas *canvas;
};
@ -375,11 +382,11 @@ void gui_input_end(struct gui_input*);
/* Output */
void gui_output_begin(struct gui_command_buffer*, struct gui_canvas *out,
const struct gui_allocator*, gui_size initial_size,
gui_float grow_factor, gui_size width, gui_size height);
void gui_output_begin_fixed(struct gui_command_buffer*, struct gui_canvas *out,
const struct gui_memory*, gui_size width, gui_size height);
void gui_output_init(struct gui_command_buffer*, const struct gui_allocator*,
gui_size initial_size, gui_float grow_factor);
void gui_output_init_fixed(struct gui_command_buffer*, const struct gui_memory*);
void gui_output_begin(struct gui_canvas *canvas, struct gui_command_buffer *buffer,
gui_size width, gui_size height);
void *gui_output_push(struct gui_command_buffer*,
enum gui_command_type, gui_size size);
void gui_output_push_scissor(struct gui_command_buffer*, gui_float, gui_float,
@ -396,13 +403,14 @@ void gui_output_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);
void gui_output_clear(struct gui_command_buffer*);
void gui_output_end(struct gui_command_buffer*, struct gui_command_list*,
struct gui_canvas *canvas, struct gui_memory_status*);
void gui_output_end(struct gui_command_list*, struct gui_command_buffer*,
struct gui_canvas*, struct gui_memory_status*);
/* Widgets */
void gui_text(const struct gui_canvas*, gui_float x, gui_float y, gui_float w, gui_float h,
const char *text, gui_size len, const struct gui_text*, const struct gui_font*);
const char *text, gui_size len, const struct gui_text*, enum gui_text_align,
const struct gui_font*);
gui_bool gui_button_text(const struct gui_canvas*, 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*);
@ -431,41 +439,46 @@ gui_float gui_scroll(const struct gui_canvas*, 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*);
/* Panel */
gui_bool gui_panel_begin(struct gui_panel*, const char*, gui_float x, gui_float y,
gui_float w, gui_float h, gui_flags, const struct gui_config*,
const struct gui_canvas*, const struct gui_font*, const struct gui_input*);
void gui_default_config(struct gui_config*);
gui_bool gui_panel_is_hidden(const struct gui_panel*);
void gui_panel_layout(struct gui_panel*, gui_float height, gui_size cols);
void gui_panel_seperator(struct gui_panel*, gui_size cols);
void gui_panel_text(struct gui_panel*, const char *str, gui_size len, enum gui_text_align);
gui_bool gui_panel_check(struct gui_panel*, const char*, gui_bool active);
gui_bool gui_panel_option(struct gui_panel*, const char*, gui_bool active);
gui_bool gui_panel_button_text(struct gui_panel*, const char*, enum gui_button_behavior);
gui_bool gui_panel_button_color(struct gui_panel*, const struct gui_color, enum gui_button_behavior);
gui_bool gui_panel_button_triangle(struct gui_panel*, enum gui_heading, enum gui_button_behavior);
gui_bool gui_panel_button_toggle(struct gui_panel*, const char*, gui_bool value);
gui_float gui_panel_slider(struct gui_panel*, gui_float min, gui_float val,
void gui_panel_init(struct gui_panel*, gui_float x, gui_float y, gui_float w, gui_float h, gui_flags,
const struct gui_config *config, const struct gui_font*);
gui_bool gui_panel_begin(struct gui_panel_layout *layout, struct gui_panel*,
const char *title, const struct gui_canvas*, const struct gui_input*);
void gui_panel_row(struct gui_panel_layout*, gui_float height, gui_size cols);
void gui_panel_seperator(struct gui_panel_layout*, gui_size cols);
void gui_panel_text(struct gui_panel_layout*, const char *str, gui_size len, enum gui_text_align);
gui_bool gui_panel_check(struct gui_panel_layout*, const char*, gui_bool active);
gui_bool gui_panel_option(struct gui_panel_layout*, const char*, gui_bool active);
gui_bool gui_panel_button_text(struct gui_panel_layout*, const char*, enum gui_button_behavior);
gui_bool gui_panel_button_color(struct gui_panel_layout*, const struct gui_color,
enum gui_button_behavior);
gui_bool gui_panel_button_triangle(struct gui_panel_layout*, enum gui_heading,
enum gui_button_behavior);
gui_bool gui_panel_button_toggle(struct gui_panel_layout*, const char*, gui_bool value);
gui_float gui_panel_slider(struct gui_panel_layout*, gui_float min, gui_float val,
gui_float max, gui_float step);
gui_size gui_panel_progress(struct gui_panel*, gui_size cur, gui_size max,
gui_size gui_panel_progress(struct gui_panel_layout*, gui_size cur, gui_size max,
gui_bool modifyable);
gui_size gui_panel_input(struct gui_panel*, gui_char *buffer, gui_size len,
gui_size gui_panel_input(struct gui_panel_layout*, gui_char *buffer, gui_size len,
gui_size max, gui_bool *active, enum gui_input_filter);
gui_int gui_panel_spinner(struct gui_panel*, gui_int min, gui_int value,
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_size gui_panel_selector(struct gui_panel*, const char *items[],
gui_size gui_panel_selector(struct gui_panel_layout*, const char *items[],
gui_size item_count, gui_size item_current);
gui_int gui_panel_plot(struct gui_panel*, const gui_float *values, gui_size value_count);
gui_int gui_panel_histo(struct gui_panel*, const gui_float *values, gui_size value_count);
gui_bool gui_panel_tab_begin(struct gui_panel*, struct gui_panel*, const char*, gui_bool minimized);
void gui_panel_tab_end(struct gui_panel*, struct gui_panel *tab);
void gui_panel_group_begin(struct gui_panel *panel, struct gui_panel*, const char*,gui_float offset);
gui_float gui_panel_group_end(struct gui_panel*, struct gui_panel* tab);
gui_size gui_panel_shelf_begin(struct gui_panel*, struct gui_panel *shelf,
gui_int gui_panel_plot(struct gui_panel_layout*, const gui_float *values, gui_size value_count);
gui_int gui_panel_histo(struct gui_panel_layout*, const gui_float *values, gui_size value_count);
gui_bool gui_panel_tab_begin(struct gui_panel_layout*, struct gui_panel_layout*,
const char*, gui_bool minimized);
void gui_panel_tab_end(struct gui_panel_layout*, struct gui_panel_layout *tab);
void gui_panel_group_begin(struct gui_panel_layout *panel, struct gui_panel_layout*,
const char*,gui_float offset);
gui_float gui_panel_group_end(struct gui_panel_layout*, struct gui_panel_layout* tab);
gui_size gui_panel_shelf_begin(struct gui_panel_layout*, struct gui_panel_layout *shelf,
const char *tabs[], gui_size size, gui_size active, gui_float offset);
gui_float gui_panel_shelf_end(struct gui_panel*, struct gui_panel *shelf);
void gui_panel_end(struct gui_panel*);
gui_float gui_panel_shelf_end(struct gui_panel_layout*, struct gui_panel_layout *shelf);
void gui_panel_end(struct gui_panel_layout*, struct gui_panel*);
#ifdef __cplusplus
}