updated Readme

This commit is contained in:
vurtun 2015-04-17 22:28:00 +02:00
parent 322bb21b9c
commit f5b4bd73f3
4 changed files with 185 additions and 63 deletions

152
Readme.md
View File

@ -18,11 +18,11 @@ WORK IN PROGRESS: I do not garantee that everything works right now
- Does NOT provide platform independent window management
- Does NOT provide platform independent input handling
- Does NOT provide a renderer backend
- Does NOT implement a font library
- Does NOT implement a font library
Summary: It is only responsible for the actual user interface
## Layer
The gui toolkit consists of three level of abstraction. First the basic widget layer
The gui toolkit consists of three levels of abstraction. First the basic widget layer
for a as pure functional as possible set of widgets functions without
any kind of internal state, with the tradeoff off of a lot of boilerplate code.
Second the panel layer for a static grouping of widgets into a panel with a reduced need for
@ -34,6 +34,117 @@ over the panel management and therefore needs the most amount of internal state.
Each higher level of abstraction uses the lower level(s) internally to build
on but offers a little bit different API.
#### Widgets
The widget layer provides the most basic way of creating graphical user interface
elements. It consist only of functions and only operates on the given data. Each
widgets takes in the current input state, font and the basic element configuration
and returns an updated draw buffer and the state of the element.
With each widget the buffer gets filled with a number of primitives that need be
drawn to screen. The main reason for the command buffer to queue up the draw
calls instead of just using a callback to directly draw the primitive lies in
the context which needs control over the drawing order. For a more limited scope
of functionality it would have been better to just use draw callbacks and to not
rely on memory allocation at all. The API will change if I find a way to
combine the buffer needed by the context with the drawing callbacks.
```c
struct gui_input input = {0};
struct gui_command_buffer buffer;
struct gui_command_list list;
struct gui_memory_status status;
const struct gui_font font = {...};
const struct gui_buffer button = {...};
struct gui_memory memory = {...};
while (1) {
gui_input_begin(&input);
/* record input */
gui_input_end(&input);
gui_output_begin(&buffer, &memory);
if (gui_widget_button(&buffer, &button, "button", &font, &input))
fprintf(stdout, "button pressed!\n");
gui_output_end(&buffer, &status, &list);
/* execute command list */
}
```
#### Panels
Panels provide an easy way to group together a number of widgets and reduce
some of the boilerplate code of the widget layer. Most of the boilerplate code
gets reduced by introducing a configration structure to provide a common look.
Instead of having a fixed layout and owning and holding widgets like in classic
graphical user interfaces, panels use an immediate mode approach of just setting
the layout of one row of the panel at a time and filling each row with widgets.
Therefore the only state that is being modfied over the course of setting up the
panel is an index descriping the current position of the next widget and the
current height and number of columns of the current row. In addition panels
provide a number of grouping functionality for widgets with groups, tabs and
shelfs and provide a minimizing and closing functionality.
```c
struct gui_config config;
struct gui_command_buffer buffer;
struct gui_command_list list;
struct gui_memory_status status;
const struct gui_font font = {...};
struct gui_memory memory = {...};
struct gui_panel panel = {0};
gui_default_config(&config);
gui_panel_init(&panel, &config, &font);
while (1) {
gui_input_begin(&input);
/* record input */
gui_input_end(&input);
gui_output_begin(&buffer, &memory);
gui_panel_begin(&panel, &buffer, &input, "Demo", 50, 50, 400, 300, 0);
gui_panel_layout(&panel, 30, 1);
if (gui_panel_button_text(&panel, "button", GUI_BUTTON_DEFAULT))
fprintf(stdout, "button pressed!\n");
gui_panel_end(&panel);
gui_output_end(&buffer, &status, &list);
/* execute command list */
}
```
#### Context
The context extends the panel functionality with moving, scaling and overlapping
panels which are quite a bit more complicated than just minimzing and closing of
panels. For panel overlapping to work as intented the context needs complete control
over all created panels to control the drawing order. OVerall the expense to
provide overlapping panels is quite hight since draw calls, the context and
all panels need to be managed and allocated.
```c
struct gui_config config;
struct gui_output output;
struct gui_memory_status status;
const struct gui_font font = {...};
struct gui_memory memory = {...};
struct gui_panel *panel;
struct gui_context *ctx;
gui_default_config(&config);
ctx = gui_new(&memory, &input);
panel = gui_new_panel(ctx, 50, 50, 400, 300, &config, &font);
while (1) {
gui_input_begin(&input);
/* record input */
gui_input_end(&input);
gui_begin(ctx, 800, 600);
gui_begin_panel(ctx, panel, "demo", 0);
gui_panel_layout(&panel, 30, 1);
if (gui_panel_button_text(&panel, "button", GUI_BUTTON_DEFAULT))
fprintf(stdout, "button pressed!\n");
gui_end_panel(ctx, &panel, NULL);
gui_end(ctx, &output, NULL);
/* execute output lists */
}
```
## Configuration
The gui toolkit provides a number of different attributes that can be
configured, like spacing, padding, size and color.
@ -59,7 +170,7 @@ iterations with C++11 and C++14.
While this hopefully settles my view on C vs C++ there is still ANSI C vs C99.
While for personal projects I only use C99 with all its niceties, libraries are
a little bit different. Libraries are designed to reach the highest number of
users possible which brings me to ANSI C which is the most portable version.
users possible which brings me to ANSI C as the most portable version.
In addition not all C compiler like the MSVC
compiler fully support C99, which finalized my decision to use ANSI C.
@ -70,6 +181,41 @@ I defined my own types which need to be set to the correct size for each
plaform. But if your development environment provides the header file you can define
`GUI_USE_FIXED_SIZE_TYPES` to directly use the correct types.
#### Why is the font/input/window management not provided
As for window and input management it is a ton of work to abstract over
all possible platforms and there are already libraries like SDL or SFML or even
the platform itself which provide you with the functionality.
So instead of reinventing the wheel and trying to do everything the project tries
to be as indepenedent and out of the users way as possible.
This means in practice a litte bit more work on the users behalf but grants a
lot more freedom especially because the toolkit is designed to be embeddable.
The font management on the other hand is a more tricky subject. In the beginning
the toolkit had some basic font handling but it got later removed. This is mainly
a question of if font handling should be part of a gui toolkit or not. As for a
framework the question would definitely be yes but for a toolkit library the
question is not as easy. In the end the project does not have font handling
since there are already a number of font handling libraries in existence or even the
platform (Xlib, Win32) itself already provides a solution.
#### Why do you use fixed size memory management
This is one of the more controversial decision in the toolkit and it comes down
to some preference that I personally build up. There are two general
ways to allocate memory, the standard way of callbacks and preallocation.
Personally I am not a big fan of callbacks even though they have their use cases
for abstraction purposes but are greatly overused in my experience. The biggest
misuse of callbacks are asynchrounous callbacks with resulting state changes which are
hell to grasp and truly comprehend in a big system.
Memory callbacks are an edge case for me and definitly shine in cases where a lot
of unpredictable allocation with varying life cycles take place. This toolkit on
the other hand has a relative stable memory allocation behavior. In the worse
case on the highst abstraction layer only the context, panels and the command
buffer need memory. In addition the general memory consumption is not that high
and could even be described as insignificant for the modern memory size. For a
system with a low amount of memory it is even better since there is only a small
limited amount of memory which is easier to optimize for as a fixed amount of memory than
a number of unrelated allocation calls.
## References
- [Tutorial from Jari Komppa about imgui libraries](http://www.johno.se/book/imgui.html)
- [Johannes 'johno' Norneby's article](http://iki.fi/sol/imgui/)

View File

@ -65,9 +65,6 @@ 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;
@ -428,7 +425,7 @@ gui_draw(XSurface *surf, const struct gui_output *out)
static gui_bool
demo_panel(struct gui_context *ctx, struct gui_panel *panel, struct demo *demo)
{
enum {PLOT, HISTO};
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"};
@ -452,7 +449,7 @@ demo_panel(struct gui_context *ctx, struct gui_panel *panel, struct demo *demo)
gui_panel_layout(panel, 200, 2);
demo->current = gui_panel_shelf_begin(panel, &demo->shelf, shelfs, LEN(shelfs), demo->current);
gui_panel_layout(&demo->shelf, 100, 1);
if (demo->current == PLOT) {
if (demo->current == HISTO) {
gui_panel_histo(&demo->shelf, values, LEN(values));
} else {
gui_panel_plot(&demo->shelf, values, LEN(values));
@ -483,8 +480,8 @@ message_panel(struct gui_context *ctx, struct gui_panel *panel)
gui_int ret = -1;
gui_begin_panel(ctx, panel, "Error", GUI_PANEL_MOVEABLE|GUI_PANEL_BORDER);
gui_panel_layout(panel, 30, 2);
if (gui_panel_button_text(panel, "ok", GUI_BUTTON_DEFAULT)) ret = 1;
if (gui_panel_button_text(panel, "cancel", GUI_BUTTON_DEFAULT)) ret = 0;
gui_panel_button_text(panel, "ok", GUI_BUTTON_DEFAULT);
gui_panel_button_text(panel, "cancel", GUI_BUTTON_DEFAULT);
gui_end_panel(ctx, panel, NULL);
}

72
gui.c
View File

@ -479,14 +479,14 @@ gui_buffer_push_triangle(struct gui_command_buffer *buffer, struct gui_vec2 *pos
}
static void
gui_buffer_push_bitmap(struct gui_command_buffer *buffer, gui_float x, gui_float y,
gui_float w, gui_float h, gui_texture texture)
gui_buffer_push_bitmap(struct gui_command_buffer *buffer, const struct gui_rect *src,
const struct gui_rect *dst, gui_texture texture)
{
struct gui_command_bitmap *bitmap;
bitmap = gui_buffer_push(buffer, GUI_COMMAND_BITMAP, sizeof(*bitmap));
if (!bitmap) return;
bitmap->x = x; bitmap->y = y;
bitmap->w = w; bitmap->h = h;
bitmap->src = *src;
bitmap->dst = *dst;
bitmap->texture = texture;
}
@ -566,29 +566,22 @@ gui_widget_text(struct gui_command_buffer *buffer, const struct gui_text *text,
(const gui_char*)text->string, text->length, text->background, text->foreground);
}
/*
void
gui_widget_image(struct gui_draw_buffer *buffer, const struct gui_image *image)
gui_widget_image(struct gui_command_buffer *buffer, const struct gui_image *image)
{
gui_float image_x;
gui_float image_y;
gui_float image_w;
gui_float image_h;
struct gui_rect dst;
assert(buffer);
assert(image);
if (!buffer || !image) return;
image_x = image->x + image->pad_x;
image_y = image->y + image->pad_y;
image_w = MAX(0, image->w - 2 * image->pad_x);
image_h = MAX(0, image->h - 2 * image->pad_y);
dst.x = image->dst.x + image->pad_x;
dst.y = image->dst.y + image->pad_y;
dst.w = MAX(0, image->dst.w - 2 * image->pad_x);
dst.h = MAX(0, image->dst.h - 2 * image->pad_y);
gui_draw_rectf(buffer, image->x, image->y, image->w, image->h, image->background);
gui_draw_image(buffer, image_x, image_y, image_w, image_h,
image->texture, image->uv[0], image->uv[1], image->color);
gui_buffer_push_rect(buffer, image->dst.x, image->dst.y, image->dst.w, image->dst.h, image->background);
gui_buffer_push_bitmap(buffer, &image->src, &dst, image->texture);
}
*/
static gui_bool
gui_widget_button(struct gui_command_buffer *buffer, const struct gui_button *button,
@ -623,8 +616,7 @@ gui_widget_button(struct gui_command_buffer *buffer, const struct gui_button *bu
gui_bool
gui_widget_button_text(struct gui_command_buffer *buffer, const struct gui_button *button,
const char *string, gui_size length, const struct gui_font *font,
const struct gui_input *in)
const char *string, const struct gui_font *font, const struct gui_input *in)
{
gui_bool ret = gui_false;
gui_float button_w, button_h;
@ -656,7 +648,7 @@ gui_widget_button_text(struct gui_command_buffer *buffer, const struct gui_butto
text.pad_x = button->pad_x;
text.pad_y = button->pad_y;
text.string = string;
text.length = length;
text.length = strsiz(string);
text.align = GUI_TEXT_CENTERED;
text.font = font->user;
text.background = bg_color;
@ -682,11 +674,9 @@ gui_widget_button_triangle(struct gui_command_buffer *buffer, struct gui_button*
return pressed;
}
/*
gui_bool
gui_widget_button_icon(struct gui_command_buffer *buffer, struct gui_button* button,
gui_texture tex, struct gui_texCoord from, struct gui_texCoord to,
const struct gui_input *in)
gui_texture texture, const struct gui_rect *src, const struct gui_input *in)
{
gui_bool pressed;
struct gui_image image;
@ -698,23 +688,20 @@ gui_widget_button_icon(struct gui_command_buffer *buffer, struct gui_button* but
return gui_false;
pressed = gui_widget_button(buffer, button, in);
image.x = button->x + button->pad_x;
image.y = button->y + button->pad_y;
image.w = button->w - 2 * button->pad_x;
image.h = button->h - 2 * button->pad_y;
image.dst.x = button->x + button->pad_x;
image.dst.y = button->y + button->pad_y;
image.dst.w = button->w - 2 * button->pad_x;
image.dst.h = button->h - 2 * button->pad_y;
image.pad_x = button->pad_x;
image.pad_y = button->pad_y;
image.texture = tex;
image.uv[0] = from;
image.uv[1] = to;
image.texture = texture;
image.src = *src;
col = (in && INBOX(in->mouse_pos.x,in->mouse_pos.y,button->x,button->y,button->w,button->h)) ?
button->highlight: button->background;
image.background = col;
image.color = color;
gui_widget_image(buffer, &image);
return pressed;
}
*/
gui_bool
gui_widget_toggle(struct gui_command_buffer *buffer, const struct gui_toggle *toggle,
@ -1540,7 +1527,6 @@ gui_panel_button_text(struct gui_panel *panel, const char *str,
struct gui_rect bounds;
struct gui_button button;
const struct gui_config *config;
gui_size len;
assert(panel);
assert(panel->config);
@ -1550,7 +1536,6 @@ gui_panel_button_text(struct gui_panel *panel, const char *str,
if (panel->minimized || (panel->flags & GUI_PANEL_HIDDEN)) return 0;
gui_panel_alloc_space(&bounds, panel);
config = panel->config;
len = strsiz(str);
button.x = bounds.x;
button.y = bounds.y;
@ -1565,7 +1550,7 @@ gui_panel_button_text(struct gui_panel *panel, const char *str,
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_widget_button_text(panel->out, &button, str, len, &panel->font, panel->in);
return gui_widget_button_text(panel->out, &button, str, &panel->font, panel->in);
}
gui_bool gui_panel_button_color(struct gui_panel *panel, const struct gui_color color,
@ -1632,11 +1617,9 @@ gui_panel_button_triangle(struct gui_panel *panel, enum gui_heading heading,
return gui_widget_button_triangle(panel->out, &button, heading, panel->in);
}
/*
gui_bool
gui_panel_button_icon(struct gui_panel *panel, gui_texture tex,
struct gui_texCoord from, struct gui_texCoord to,
enum gui_button_behavior behavior)
const struct gui_rect *src, enum gui_button_behavior behavior)
{
struct gui_rect bounds;
struct gui_button button;
@ -1663,9 +1646,8 @@ gui_panel_button_icon(struct gui_panel *panel, gui_texture tex,
button.content = config->colors[GUI_COLOR_TEXT];
button.highlight = config->colors[GUI_COLOR_BUTTON_HOVER];
button.highlight_content = config->colors[GUI_COLOR_BUTTON_HOVER];
return gui_widget_button_icon(panel->out, &button, tex, from, to, panel->in);
return gui_widget_button_icon(panel->out, &button, tex, src, panel->in);
}
*/
gui_bool
gui_panel_button_toggle(struct gui_panel *panel, const char *str, gui_bool value)
@ -1673,7 +1655,6 @@ gui_panel_button_toggle(struct gui_panel *panel, const char *str, gui_bool value
struct gui_rect bounds;
struct gui_button button;
const struct gui_config *config;
gui_size len;
assert(panel);
assert(panel->config);
@ -1684,7 +1665,6 @@ gui_panel_button_toggle(struct gui_panel *panel, const char *str, gui_bool value
if (panel->minimized || (panel->flags & GUI_PANEL_HIDDEN)) return 0;
gui_panel_alloc_space(&bounds, panel);
config = panel->config;
len = strsiz(str);
button.x = bounds.x;
button.y = bounds.y;
@ -1708,7 +1688,7 @@ gui_panel_button_toggle(struct gui_panel *panel, const char *str, gui_bool value
button.highlight = config->colors[GUI_COLOR_BUTTON_HOVER];
button.highlight_content = config->colors[GUI_COLOR_BUTTON];
}
if (gui_widget_button_text(panel->out, &button, str, len, &panel->font, panel->in))
if (gui_widget_button_text(panel->out, &button, str, &panel->font, panel->in))
value = !value;
return value;
}
@ -2202,7 +2182,7 @@ gui_panel_shelf_begin(struct gui_panel *panel, gui_shelf *shelf,
button.y += config->item_padding.y;
button.h -= config->item_padding.y;
}
if (gui_widget_button_text(panel->out, &button, tabs[i], strsiz(tabs[i]),
if (gui_widget_button_text(panel->out, &button, tabs[i],
&panel->font, panel->in)) active = i;
}

13
gui.h
View File

@ -145,8 +145,8 @@ struct gui_command_circle {
struct gui_command_bitmap {
struct gui_command header;
gui_texture texture;
gui_float x, y;
gui_float w, h;
struct gui_rect src;
struct gui_rect dst;
};
struct gui_command_triangle {
@ -206,8 +206,8 @@ struct gui_text {
};
struct gui_image {
gui_float x, y;
gui_float w, h;
struct gui_rect src;
struct gui_rect dst;
gui_float pad_x, pad_y;
struct gui_color color;
gui_texture texture;
@ -425,12 +425,11 @@ void gui_widget_text(struct gui_command_buffer*, const struct gui_text*,
const struct gui_font*);
void gui_widget_image(struct gui_command_buffer*, const struct gui_image*);
gui_bool gui_widget_button_text(struct gui_command_buffer*, const struct gui_button*,
const char *text, gui_size len, const struct gui_font*,
const struct gui_input*);
const char *text, const struct gui_font*, const struct gui_input*);
gui_bool gui_widget_button_triangle(struct gui_command_buffer*, struct gui_button*,
enum gui_heading, const struct gui_input*);
gui_bool gui_widget_button_icon(struct gui_command_buffer*, struct gui_button*,
gui_texture, struct gui_rect *source, const struct gui_input*);
gui_texture, const struct gui_rect *source, const struct gui_input*);
gui_bool gui_widget_toggle(struct gui_command_buffer*, const struct gui_toggle*,
gui_bool active, const struct gui_font*, const struct gui_input*);
gui_float gui_widget_slider(struct gui_command_buffer*, const struct gui_slider*,