fixed some stuff + created extensive demo

This commit is contained in:
vurtun 2015-05-09 14:11:55 +02:00
parent 5f9b1aef0f
commit 24380a2dcf
4 changed files with 80 additions and 180 deletions

View File

@ -18,48 +18,12 @@ possible with fast streamlined development speed in mind.
- Configurable
- UTF-8 support
## Functionality
+ Label
+ Buttons
+ Slider
+ Progressbar
+ Checkbox
+ Radiobutton
+ Input
+ Shell
+ Spinner
+ Selector
+ Linegraph
+ Histogram
+ Table
+ Panel
+ Tab
+ Group
+ Shelf
## Limitations
- Does NOT provide os window/input management
- Does NOT provide a renderer backend
- Does NOT implement a font library
Summary: It is only responsible for the actual user interface
## IMGUIs
Immediate mode in contrast to classical retained mode GUIs store as little state as possible
by using procedural function calls as "widgets" instead of storing objects.
Each "widget" function call takes hereby all its necessary data and immediately returns
the through the user modified state back to the caller. Immediate mode graphical
user interfaces therefore combine drawing and input handling into one unit
instead of separating 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
retained GUI implementations but on the other hand grants a lot more flexibility and
support for overall layout changes. In addition without any state there is no
duplicated 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
understandability.
## Example
```c
struct gui_input input = {0};
@ -111,14 +75,22 @@ while (1) {
```
![gui screenshot](/screen.png?raw=true)
## API
The API for this gui toolkit is divided into two different layers. There
is the widget layer and the panel layer. The widget layer provides a number of
classical widgets in functional immediate mode form without any kind of internal
state. Each widget can be placed anywhere on the screen but there is no direct
way provided to group widgets together. For this to change there is the panel
layer which is build on top of the widget layer and uses most of the widget API
internally to form groups of widgets into a layout.
## IMGUIs
Immediate mode in contrast to classical retained mode GUIs store as little state as possible
by using procedural function calls as "widgets" instead of storing objects.
Each "widget" function call takes hereby all its necessary data and immediately returns
the through the user modified state back to the caller. Immediate mode graphical
user interfaces therefore combine drawing and input handling into one unit
instead of separating 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
retained GUI implementations but on the other hand grants a lot more flexibility and
support for overall layout changes. In addition without any state there is no
duplicated 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
understandability.
### Input
The `gui_input` struct holds the user input over the course of the frame and
@ -198,9 +170,9 @@ while (1) {
}
```
For the purpose of implementing overlapping panels sub buffers were implemented.
For the purpose of implementing multible panels, sub buffers were implemented.
With sub buffers you can create one global buffer which owns the allocated memory
and sub buffers which directly reference the global buffer. Biggest
and sub buffers which directly reference the global buffer. The biggest
advantage is that you do not have to allocate a buffer for each panel and boil
down the memory management to a single buffer.

View File

@ -12,9 +12,6 @@
#include "../gui.h"
/* macros */
#define MAX_BUFFER 64
#define MAX_MEMORY (8 * 1024)
#define MAX_PANELS 4
#define WIN_WIDTH 800
#define WIN_HEIGHT 600
#define DTIME 16
@ -25,6 +22,8 @@
#define LEN(a) (sizeof(a)/sizeof(a)[0])
#define UNUSED(a) ((void)(a))
#include "demo.c"
typedef struct XFont XFont;
typedef struct XSurface XSurface;
typedef struct XWindow XWindow;
@ -62,27 +61,6 @@ struct XWindow {
unsigned int height;
};
struct demo {
gui_char in_buf[MAX_BUFFER];
gui_size in_len;
gui_bool in_act;
gui_char cmd_buf[MAX_BUFFER];
gui_size cmd_len;
gui_bool cmd_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 cur;
gui_bool tab_min;
gui_float group_off;
gui_float shelf_off;
gui_bool toggle;
};
static void
die(const char *fmt, ...)
{
@ -320,7 +298,7 @@ surface_del(XSurface *surf)
}
static void
draw(XSurface *surf, struct gui_command_list *list)
execute(XSurface *surf, struct gui_command_list *list)
{
const struct gui_command *cmd;
if (!list->count) return;
@ -355,12 +333,26 @@ draw(XSurface *surf, struct gui_command_list *list)
surface_draw_text(surf, t->x, t->y, t->w, t->h, (const char*)t->string,
t->length, t->font, t->bg, t->fg);
} break;
case GUI_COMMAND_IMAGE:
case GUI_COMMAND_MAX:
default: break;
}
cmd = gui_list_next(list, cmd);
}
}
static void
draw(XSurface *surf, struct gui_panel_stack *stack)
{
struct gui_panel *iter = stack->begin;
if (!stack->count) return;
while (iter) {
struct gui_window *win = (void*)iter;
execute(surf, &win->list);
iter = iter->next;
}
}
static void
key(struct XWindow *xw, struct gui_input *in, XEvent *evt, gui_bool down)
{
@ -412,55 +404,6 @@ resize(struct XWindow *xw, XSurface *surf)
surface_resize(surf, xw->width, xw->height);
}
static void
demo_panel(struct gui_panel_layout *panel, struct demo *demo)
{
gui_int i = 0;
enum {HISTO, PLOT};
const char *shelfs[] = {"Histogram", "Lines"};
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_layout tab;
/* Tabs */
demo->tab_min = gui_panel_tab_begin(panel, &tab, "Difficulty", demo->tab_min);
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;
}
gui_panel_tab_end(panel, &tab);
/* Shelf */
gui_panel_row(panel, 200, 2);
demo->cur = gui_panel_shelf_begin(panel,&tab,shelfs,LEN(shelfs),demo->cur,demo->shelf_off);
gui_panel_row(&tab, 100, 1);
if (demo->cur == HISTO) {
gui_panel_graph(&tab, GUI_GRAPH_HISTO, values, LEN(values));
} else {
gui_panel_graph(&tab, GUI_GRAPH_LINES, values, LEN(values));
}
demo->shelf_off = gui_panel_shelf_end(panel, &tab);
/* Group */
gui_panel_group_begin(panel, &tab, "Options", demo->group_off);
gui_panel_row(&tab, 30, 1);
if (gui_panel_button_text(&tab, "button", GUI_BUTTON_DEFAULT))
fprintf(stdout, "button pressed!\n");
demo->toggle = gui_panel_button_toggle(&tab, "toggle", demo->toggle);
demo->check = gui_panel_check(&tab, "advanced", demo->check);
demo->slider = gui_panel_slider(&tab, 0, demo->slider, 10, 1.0f);
demo->prog = gui_panel_progress(&tab, demo->prog, 100, gui_true);
demo->item_cur = gui_panel_selector(&tab, items, LEN(items), demo->item_cur);
demo->spinner = gui_panel_spinner(&tab, 0, demo->spinner, 250, 10, &demo->spin_act);
if (gui_panel_shell(&tab, demo->cmd_buf, &demo->cmd_len, MAX_BUFFER, &demo->cmd_act))
demo->cmd_len = 0;
demo->in_len = gui_panel_edit(&tab, demo->in_buf, demo->in_len,
MAX_BUFFER, &demo->in_act, GUI_INPUT_DEFAULT);
demo->group_off = gui_panel_group_end(panel, &tab);
}
int
main(int argc, char *argv[])
{
@ -469,24 +412,16 @@ main(int argc, char *argv[])
long started;
XWindow xw;
gui_bool running = gui_true;
struct demo demo;
/* GUI */
struct gui_input in;
struct gui_font font;
struct gui_memory memory;
struct gui_memory_status status;
struct gui_config config;
struct gui_canvas canvas;
struct gui_command_buffer buffer;
struct gui_command_list list;
struct gui_panel_layout layout;
struct gui_panel panel;
gui_size option = 0;
gui_char buf[256];
gui_size len = 0;
gui_bool active;
struct gui_panel_stack stack;
struct show_window show;
struct control_window control;
/* Window */
UNUSED(argc); UNUSED(argv);
@ -521,18 +456,8 @@ main(int argc, char *argv[])
font.height = (gui_float)xw.font->height;
font.width = font_get_text_width;
gui_default_config(&config);
gui_panel_init(&panel, 50, 50, 400, 320,
GUI_PANEL_BORDER|GUI_PANEL_MOVEABLE|
GUI_PANEL_CLOSEABLE|GUI_PANEL_SCALEABLE|
GUI_PANEL_MINIMIZABLE, &config, &font);
/* Demo */
memset(&demo, 0, sizeof(demo));
demo.tab_min = gui_true;
demo.spinner = 100;
demo.slider = 2.0f;
demo.prog = 60;
demo.cur = 1;
gui_stack_clear(&stack);
init_demo(&show, &control, &stack, &config, &font);
while (running) {
/* Input */
@ -551,16 +476,12 @@ main(int argc, char *argv[])
gui_input_end(&in);
/* GUI */
gui_buffer_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_buffer_end(&list, &buffer, &canvas, &status);
running = run_demo(&show, &control, &stack, &config, &in, &buffer, xw.width, xw.height);
/* Draw */
XClearWindow(xw.dpy, xw.win);
surface_clear(xw.surf, 0x00646464);
draw(xw.surf, &list);
draw(xw.surf, &stack);
surface_blit(xw.win, xw.surf, xw.width, xw.height);
XFlush(xw.dpy);

55
gui.c
View File

@ -843,6 +843,7 @@ gui_buffer_push(struct gui_command_buffer* buffer,
cap = (gui_size)((gui_float)buffer->capacity * buffer->grow_factor);
buffer->memory = buffer->allocator.realloc(buffer->allocator.userdata,buffer->memory, cap);
if (!buffer->memory) return NULL;
buffer->capacity = cap;
buffer->begin = buffer->memory;
buffer->end = (void*)((gui_byte*)buffer->begin + buffer->allocated);
@ -1006,7 +1007,8 @@ gui_buffer_push_image(struct gui_command_buffer *buffer, gui_float x, gui_float
return;
}
}
cmd = gui_buffer_push(buffer, GUI_COMMAND_RECT, sizeof(*cmd));
cmd = gui_buffer_push(buffer, GUI_COMMAND_IMAGE, sizeof(*cmd));
if (!cmd) return;
cmd->x = (gui_short)x;
cmd->y = (gui_short)y;
@ -1035,6 +1037,7 @@ gui_buffer_push_text(struct gui_command_buffer *buffer, gui_float x, gui_float y
return;
}
}
cmd = gui_buffer_push(buffer, GUI_COMMAND_TEXT, sizeof(*cmd) + length + 1);
if (!cmd) return;
cmd->x = (gui_short)x;
@ -1285,7 +1288,6 @@ gui_panel_init(struct gui_panel *panel, gui_float x, gui_float y, gui_float w,
panel->y = y;
panel->w = w;
panel->h = h;
panel->flags = 0;
panel->flags = flags;
panel->config = config;
panel->font = *font;
@ -1312,8 +1314,13 @@ gui_panel_begin(struct gui_panel_layout *layout, struct gui_panel *panel,
if (!panel || !canvas || !layout)
return gui_false;
if (panel->flags & GUI_PANEL_HIDDEN)
if (panel->flags & GUI_PANEL_HIDDEN) {
zero(layout, sizeof(*layout));
layout->valid = gui_false;
layout->config = panel->config;
layout->canvas = canvas;
return gui_false;
}
config = panel->config;
layout->header_height = panel->font.height + 3 * config->item_padding.y;
@ -1323,7 +1330,6 @@ gui_panel_begin(struct gui_panel_layout *layout, struct gui_panel *panel,
mouse_y = (in) ? in->mouse_pos.y : -1;
prev_x = (in) ? in->mouse_prev.x : -1;
prev_y = (in) ? in->mouse_prev.y : -1;
clicked_x = (in) ? in->mouse_clicked_pos.x : -1;
clicked_y = (in) ? in->mouse_clicked_pos.y : -1;
@ -1345,8 +1351,8 @@ gui_panel_begin(struct gui_panel_layout *layout, struct gui_panel *panel,
gui_bool incursor;
gui_float scaler_w = MAX(0, config->scaler_size.x - config->item_padding.x);
gui_float scaler_h = MAX(0, config->scaler_size.y - config->item_padding.y);
gui_float scaler_x = (layout->x + layout->w) - (config->item_padding.x + scaler_w);
gui_float scaler_y = layout->y + layout->h - config->scaler_size.y;
gui_float scaler_x = (panel->x + panel->w) - (config->item_padding.x + scaler_w);
gui_float scaler_y = panel->y + panel->h - config->scaler_size.y;
incursor = in && INBOX(prev_x, prev_y, scaler_x, scaler_y, scaler_w, scaler_h);
if (in && in->mouse_down && incursor) {
@ -1423,7 +1429,7 @@ gui_panel_begin(struct gui_panel_layout *layout, struct gui_panel *panel,
if (INBOX(mouse_x, mouse_y, close_x, close_y, close_w, close_h)) {
if (INBOX(clicked_x, clicked_y, close_x, close_y, close_w, close_h)) {
ret = !(in->mouse_down && in->mouse_clicked);
if (ret) panel->flags |= GUI_PANEL_HIDDEN;
if (!ret) panel->flags |= GUI_PANEL_HIDDEN;
}
}
}
@ -1509,9 +1515,8 @@ gui_panel_begin_stacked(struct gui_panel_layout *layout, struct gui_panel *panel
if (in->mouse_down && in->mouse_clicked && inpanel && panel != stack->end) {
struct gui_panel *iter = panel->next;
while (iter) {
if (!iter->minimized)
if (INBOX(in->mouse_prev.x, in->mouse_prev.y, iter->x, iter->y,
iter->w, iter->h)) break;
if (INBOX(in->mouse_prev.x, in->mouse_prev.y, iter->x, iter->y, iter->w, iter->h) &&
!iter->minimized) break;
iter = iter->next;
}
if (!iter) {
@ -1574,6 +1579,8 @@ gui_panel_alloc_space(struct gui_rect *bounds, struct gui_panel_layout *layout)
ASSERT(layout);
ASSERT(layout->config);
ASSERT(bounds);
if (!layout || !layout->config || !bounds)
return;
config = layout->config;
if (layout->index >= layout->row_columns) {
@ -1644,7 +1651,6 @@ gui_panel_label(struct gui_panel_layout *layout, const char *text, enum gui_text
gui_panel_text(layout, text, len, align);
}
gui_bool
gui_panel_button_text(struct gui_panel_layout *layout, const char *str,
enum gui_button_behavior behavior)
@ -1704,7 +1710,7 @@ gui_bool gui_panel_button_color(struct gui_panel_layout *layout,
button.background = color;
button.foreground = color;
button.highlight = color;
button.highlight_content = config->colors[GUI_COLOR_BUTTON_HOVER_FONT];
button.highlight_content = color;
return gui_do_button(layout->canvas, bounds.x, bounds.y, bounds.w, bounds.h,
&button, layout->input, behavior);
}
@ -2054,18 +2060,18 @@ gui_panel_spinner(struct gui_panel_layout *layout, gui_int min, gui_int value,
ASSERT(layout->config);
ASSERT(layout->canvas);
if (!layout || !layout->config || !layout->canvas) return 0;
if (!layout->valid) return 0;
if (!layout || !layout->config || !layout->canvas) return value;
if (!layout->valid) return value;
gui_panel_alloc_space(&bounds, layout);
config = layout->config;
canvas = layout->canvas;
c = &layout->clip;
if (!INTERSECT(c->x, c->y, c->w, c->h, bounds.x, bounds.y, bounds.w, bounds.h))
return 0;
return value;
value = CLAMP(min, value, max);
len = itos(string, value);
is_active = *active;
is_active = (active) ? *active : gui_false;
old_len = len;
button.border = 1;
@ -2105,7 +2111,7 @@ gui_panel_spinner(struct gui_panel_layout *layout, gui_int min, gui_int value,
len, MAX_NUMBER_BUFFER, &is_active, &field, layout->input, &layout->font);
if (old_len != len)
strtoi(&value, string, len);
*active = is_active;
if (active) *active = is_active;
return value;
}
@ -2160,11 +2166,11 @@ gui_panel_selector(struct gui_panel_layout *layout, const char *items[],
button.content = config->colors[GUI_COLOR_TEXT];
button.highlight = config->colors[GUI_COLOR_BUTTON];
button.highlight_content = config->colors[GUI_COLOR_TEXT];
button_up_clicked = gui_button_triangle(canvas, button_x, button_y, button_w,
button_down_clicked = gui_button_triangle(canvas, button_x, button_y, button_w,
button_h, GUI_UP, GUI_BUTTON_DEFAULT, &button, layout->input);
button_y = bounds.y + button_h;
button_down_clicked = gui_button_triangle(canvas, button_x, button_y, button_w,
button_up_clicked = gui_button_triangle(canvas, button_x, button_y, button_w,
button_h, GUI_DOWN, GUI_BUTTON_DEFAULT, &button, layout->input);
item_current = (button_down_clicked && item_current < item_count-1) ?
item_current+1 : (button_up_clicked && item_current > 0) ? item_current-1 : item_current;
@ -2342,14 +2348,13 @@ gui_panel_graph(struct gui_panel_layout *layout, enum gui_graph_type type,
struct gui_rect bounds;
gui_float min_value;
gui_float max_value;
const struct gui_config *config;
struct gui_graph graph;
ASSERT(layout);
ASSERT(layout->config);
ASSERT(layout->canvas);
ASSERT(values);
ASSERT(count);
if (!layout || !values || !count)
return -1;
max_value = values[0];
min_value = values[0];
@ -2378,13 +2383,13 @@ gui_panel_graph_ex(struct gui_panel_layout *layout, enum gui_graph_type type,
struct gui_rect bounds;
gui_float min_value;
gui_float max_value;
const struct gui_config *config;
struct gui_graph graph;
ASSERT(layout);
ASSERT(layout->config);
ASSERT(layout->canvas);
ASSERT(get_value);
ASSERT(count);
if (!layout || !get_value || !count)
return -1;
max_value = get_value(userdata, 0);
min_value = max_value;

14
gui.h
View File

@ -228,7 +228,9 @@ enum gui_command_type {
GUI_COMMAND_RECT,
GUI_COMMAND_CIRCLE,
GUI_COMMAND_TRIANGLE,
GUI_COMMAND_TEXT
GUI_COMMAND_TEXT,
GUI_COMMAND_IMAGE,
GUI_COMMAND_MAX
};
struct gui_command {
@ -399,11 +401,6 @@ struct gui_panel {
struct gui_panel *prev;
};
struct gui_window {
struct gui_panel panel;
struct gui_command_list list;
};
struct gui_panel_layout {
gui_float x, y, w, h;
gui_float offset;
@ -430,6 +427,11 @@ struct gui_panel_stack {
struct gui_panel *end;
};
struct gui_window {
struct gui_panel panel;
struct gui_command_list list;
};
/* Input */
gui_size gui_utf_decode(const gui_char*, gui_long*, gui_size);
gui_size gui_utf_encode(gui_long, gui_char*, gui_size);