2015-03-11 16:00:59 +03:00
|
|
|
# GUI
|
2015-07-25 23:16:35 +03:00
|
|
|
[![Coverity Status](https://scan.coverity.com/projects/5863/badge.svg)](https://scan.coverity.com/projects/5863)
|
2015-07-25 22:32:37 +03:00
|
|
|
|
2015-05-21 14:06:29 +03:00
|
|
|
This is a bloat free minimal state immediate mode graphical user interface toolkit
|
2015-08-27 20:25:13 +03:00
|
|
|
written in ANSI C. It was designed as a simple embeddable user interface for
|
|
|
|
application and does not have any direct dependencies. The toolkit was mainly
|
|
|
|
developed to have a simple GUI for the X11 window system but can be used
|
|
|
|
with other platforms like win32 or libraries like nanovg. The library uses
|
2015-08-29 12:04:09 +03:00
|
|
|
no heap allocation outside of draw commands and as a whole has a low default
|
|
|
|
heap memory footprint.
|
2015-03-11 16:00:59 +03:00
|
|
|
|
2015-03-14 19:05:30 +03:00
|
|
|
## Features
|
2015-04-16 17:20:00 +03:00
|
|
|
- Immediate mode graphical user interface toolkit
|
2015-03-14 19:05:30 +03:00
|
|
|
- Written in C89 (ANSI C)
|
2015-08-10 21:34:47 +03:00
|
|
|
- Small codebase (~6kLOC)
|
2015-05-17 15:39:02 +03:00
|
|
|
- Focus on portability, efficiency, simplicity and minimal internal state
|
|
|
|
- No global or hidden state
|
2015-04-03 18:40:47 +03:00
|
|
|
- No direct dependencies (not even libc!)
|
2015-05-11 13:45:22 +03:00
|
|
|
- Configurable style and colors
|
2015-04-30 17:12:21 +03:00
|
|
|
- UTF-8 support
|
2015-03-24 15:08:42 +03:00
|
|
|
|
2015-04-16 21:05:43 +03:00
|
|
|
## Limitations
|
2015-04-28 23:08:07 +03:00
|
|
|
- Does NOT provide os window/input management
|
2015-08-27 20:25:13 +03:00
|
|
|
- Does NOT implement a render backend
|
2015-04-24 16:17:10 +03:00
|
|
|
- Does NOT implement a font library
|
2015-04-16 21:05:43 +03:00
|
|
|
Summary: It is only responsible for the actual user interface
|
2015-04-16 17:20:00 +03:00
|
|
|
|
2015-05-09 16:22:23 +03:00
|
|
|
## Gallery
|
2015-07-17 00:15:53 +03:00
|
|
|
![gui demo](/screen/demo.png?raw=true)
|
2015-08-02 14:49:51 +03:00
|
|
|
![gui explorer](/screen/explorer.png?raw=true)
|
2015-08-29 22:08:44 +03:00
|
|
|
![gui nodedit](/screen/nodedit.png?raw=true)
|
2015-05-09 16:22:23 +03:00
|
|
|
|
2015-04-25 17:44:43 +03:00
|
|
|
## Example
|
|
|
|
```c
|
2015-05-30 22:20:04 +03:00
|
|
|
/* allocate memory to hold the draw commands */
|
2015-08-06 17:36:28 +03:00
|
|
|
struct gui_command_queue queue;
|
2015-07-02 12:26:52 +03:00
|
|
|
void *memory = malloc(MEMORY_SIZE);
|
2015-08-06 17:36:28 +03:00
|
|
|
gui_command_queue_init_fixed(&buffer, memory, MEMORY_SIZE);
|
2015-05-11 14:25:05 +03:00
|
|
|
|
2015-08-10 21:34:47 +03:00
|
|
|
/* setup configuration */
|
2015-07-02 12:32:08 +03:00
|
|
|
struct gui_font font;
|
2015-08-29 13:56:40 +03:00
|
|
|
struct gui_style style;
|
2015-07-02 12:32:08 +03:00
|
|
|
font.userdata.ptr = your_font_data;
|
|
|
|
font.height = your_font_data.height;
|
2015-08-06 17:36:28 +03:00
|
|
|
font.width = your_font_string_width_callback_function;
|
2015-08-29 13:56:40 +03:00
|
|
|
gui_style_default(&style, GUI_DEFAULT_ALL, &font);
|
2015-05-29 15:47:42 +03:00
|
|
|
|
|
|
|
/* initialize panel */
|
2015-08-29 13:56:40 +03:00
|
|
|
struct gui_window panel;
|
|
|
|
gui_window_init(&panel, 50, 50, 220, 180,
|
2015-08-06 17:36:28 +03:00
|
|
|
GUI_PANEL_BORDER|GUI_PANEL_MOVEABLE|GUI_PANEL_SCALEABLE,
|
2015-08-29 13:56:40 +03:00
|
|
|
&queue, &style, &input);
|
2015-04-25 17:44:43 +03:00
|
|
|
|
2015-06-28 11:09:53 +03:00
|
|
|
/* setup widget data */
|
2015-08-05 13:48:01 +03:00
|
|
|
enum {EASY, HARD};
|
2015-08-10 21:34:47 +03:00
|
|
|
gui_size option = EASY;
|
|
|
|
gui_size item = 0;
|
2015-08-29 14:41:44 +03:00
|
|
|
gui_state active = 0;
|
2015-06-28 11:09:53 +03:00
|
|
|
|
2015-05-29 15:47:42 +03:00
|
|
|
struct gui_input input = {0};
|
2015-04-25 17:44:43 +03:00
|
|
|
while (1) {
|
|
|
|
gui_input_begin(&input);
|
|
|
|
/* record input */
|
|
|
|
gui_input_end(&input);
|
|
|
|
|
2015-05-14 16:14:15 +03:00
|
|
|
/* GUI */
|
2015-08-29 13:56:40 +03:00
|
|
|
struct gui_context context;
|
|
|
|
gui_begin(&context, &panel);
|
2015-08-06 17:36:28 +03:00
|
|
|
{
|
|
|
|
const char *items[] = {"Fist", "Pistol", "Railgun", "BFG"};
|
2015-08-29 14:41:44 +03:00
|
|
|
gui_header(&context, "Show", GUI_CLOSEABLE, 0, GUI_HEADER_LEFT);
|
2015-08-29 13:56:40 +03:00
|
|
|
gui_layout_row_static(&context, 30, 80, 1);
|
|
|
|
if (gui_button_text(&context, "button", GUI_BUTTON_DEFAULT)) {
|
2015-08-06 17:36:28 +03:00
|
|
|
/* event handling */
|
|
|
|
}
|
2015-08-29 13:56:40 +03:00
|
|
|
gui_layout_row_dynamic(&context, 30, 2);
|
|
|
|
if (gui_option(&context, "easy", option == EASY)) option = EASY;
|
|
|
|
if (gui_option(&context, "hard", option == HARD)) option = HARD;
|
|
|
|
gui_label(&context, "Weapon:", GUI_TEXT_LEFT);
|
2015-08-29 14:41:44 +03:00
|
|
|
gui_combo(&context, items, LEN(items), &selected, 20, &combo->active);
|
2015-04-25 17:44:43 +03:00
|
|
|
}
|
2015-08-29 13:56:40 +03:00
|
|
|
gui_end(&context, &panel);
|
2015-04-25 17:44:43 +03:00
|
|
|
|
2015-05-14 16:14:15 +03:00
|
|
|
/* draw */
|
2015-05-16 13:27:51 +03:00
|
|
|
const struct gui_command *cmd;
|
2015-08-06 17:36:28 +03:00
|
|
|
gui_foreach_command(cmd, &queue) {
|
2015-05-29 15:47:42 +03:00
|
|
|
/* execute draw call command */
|
2015-04-25 17:44:43 +03:00
|
|
|
}
|
2015-08-06 17:36:28 +03:00
|
|
|
gui_command_queue_clear(&queue);
|
2015-04-25 17:44:43 +03:00
|
|
|
}
|
|
|
|
```
|
2015-05-09 21:26:23 +03:00
|
|
|
![gui screenshot](/screen/screen.png?raw=true)
|
2015-04-25 17:44:43 +03:00
|
|
|
|
2015-05-09 15:11:55 +03:00
|
|
|
## 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.
|
2015-04-20 22:14:58 +03:00
|
|
|
|
2015-04-25 17:44:43 +03:00
|
|
|
### Input
|
2015-05-03 12:45:29 +03:00
|
|
|
The `gui_input` struct holds the user input over the course of the frame and
|
2015-05-12 15:14:36 +03:00
|
|
|
manages the complete modification of widget and panel state. To fill the
|
|
|
|
structure with data over the frame there are a number of functions provided for
|
|
|
|
key, motion, button and text input. The input is hereby completly independent of
|
|
|
|
the underlying platform or way of input so even touch or other ways of input are
|
|
|
|
possible.
|
|
|
|
Like the panel and the buffer, input is based on an immediate mode API and
|
|
|
|
consist of an begin sequence with `gui_input_begin` and a end sequence point
|
|
|
|
with `gui_input_end`. All modifications can only occur between both of these
|
|
|
|
sequence points while all outside modification provoke undefined behavior.
|
2015-04-25 17:44:43 +03:00
|
|
|
|
|
|
|
```c
|
|
|
|
struct gui_input input = {0};
|
|
|
|
while (1) {
|
|
|
|
gui_input_begin(&input);
|
2015-08-06 17:36:28 +03:00
|
|
|
if (/*mouse moved*/)
|
|
|
|
gui_input_motion(&input, mouse.x, mouse.y);
|
|
|
|
if (/*key pressed*/)
|
|
|
|
gui_input_key(&input, key, gui_true);
|
|
|
|
if (/*key released*/)
|
|
|
|
gui_input_key(&input, key, gui_false);
|
|
|
|
if (/*mouse button pressed*/)
|
|
|
|
gui_input_button(&input, mouse.x, mouse.y, gui_true);
|
|
|
|
if (/*mouse button released */)
|
|
|
|
gui_input_button(&input, mouse.x, mouse.y, gui_false);
|
2015-04-25 17:44:43 +03:00
|
|
|
gui_input_end(&input);
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2015-04-24 16:17:10 +03:00
|
|
|
### Configuration
|
|
|
|
The gui toolkit provides a number of different attributes that can be
|
|
|
|
configured, like spacing, padding, size and color.
|
|
|
|
While the widget API even expects you to provide the configuration
|
|
|
|
for each and every widget the panel layer provides you with a set of
|
|
|
|
attributes in the `gui_config` structure. The structure either needs to be
|
|
|
|
filled by the user or can be setup with some default values by the function
|
2015-05-20 18:27:21 +03:00
|
|
|
`gui_config_default`. Modification on the fly to the `gui_config` struct is in
|
2015-05-03 13:22:43 +03:00
|
|
|
true immediate mode fashion possible and supported.
|
2015-04-24 16:17:10 +03:00
|
|
|
|
2015-05-12 15:14:36 +03:00
|
|
|
```c
|
2015-08-29 13:56:40 +03:00
|
|
|
struct gui_style {
|
2015-08-06 17:36:28 +03:00
|
|
|
gui_float rounding[GUI_ROUNDING_MAX];
|
2015-05-20 18:33:41 +03:00
|
|
|
struct gui_vec2 properties[GUI_PROPERTY_MAX];
|
2015-05-12 15:14:36 +03:00
|
|
|
struct gui_color colors[GUI_COLOR_COUNT];
|
|
|
|
};
|
|
|
|
```
|
2015-08-29 13:56:40 +03:00
|
|
|
In addition to modifing the `gui_style` struct directly the styleration API
|
2015-05-20 19:11:46 +03:00
|
|
|
enables you to temporarily change a property or color and revert back directly
|
2015-05-29 15:47:42 +03:00
|
|
|
after the change is no longer needed. The number of temporary changes are
|
|
|
|
limited but can be changed with constants `GUI_MAX_COLOR_STACK` and
|
|
|
|
`GUI_MAX_ATTRIB_STACK`.
|
|
|
|
|
2015-05-20 18:33:41 +03:00
|
|
|
|
|
|
|
```c
|
2015-08-29 13:56:40 +03:00
|
|
|
gui_style_push_color(style, GUI_COLORS_PANEL, 255, 0, 0, 255);
|
|
|
|
gui_style_push_attribute(style, GUI_ATTRIBUTE_PADDING, 10.0f, 5.0f);
|
|
|
|
/* use the styleuration data */
|
|
|
|
gui_style_pop_attribute(style);
|
|
|
|
gui_style_pop_color(style);
|
2015-05-20 18:33:41 +03:00
|
|
|
```
|
2015-05-12 15:14:36 +03:00
|
|
|
|
2015-05-29 15:47:42 +03:00
|
|
|
Since there is no direct font implementation in the toolkit but font handling is
|
|
|
|
still an aspect of a gui implementation, the `gui_font` struct was introduced. It only
|
|
|
|
contains the bare minimum of what is needed for font handling.
|
|
|
|
For widgets the `gui_font` data has to be persistent while the
|
|
|
|
panel hold the font internally. Important to node is that the font does not hold
|
|
|
|
your font data but merely references it so you have to make sure that the font
|
|
|
|
always points to a valid object.
|
2015-05-03 12:45:29 +03:00
|
|
|
|
2015-05-12 15:14:36 +03:00
|
|
|
```c
|
2015-05-29 15:47:42 +03:00
|
|
|
struct gui_font {
|
2015-07-02 12:32:08 +03:00
|
|
|
gui_handle userdata;
|
2015-05-29 15:47:42 +03:00
|
|
|
gui_float height;
|
|
|
|
gui_text_width_f width;
|
2015-05-12 15:14:36 +03:00
|
|
|
};
|
2015-05-29 15:47:42 +03:00
|
|
|
|
2015-08-06 17:36:28 +03:00
|
|
|
font.userdata.ptr = your_font_data;
|
|
|
|
font.height = your_font_data.height;
|
|
|
|
font.width = your_font_string_width_callback_function;
|
2015-05-07 16:52:35 +03:00
|
|
|
```
|
|
|
|
|
2015-08-07 17:53:52 +03:00
|
|
|
### Memory
|
|
|
|
Almost all memory as well as object management for the toolkit
|
|
|
|
is left to the user for maximum control. In fact a big subset of the toolkit can
|
|
|
|
be used without any heap allocation at all. The only place where heap allocation
|
|
|
|
is needed at all is for buffering draw calls. While the standart way of
|
|
|
|
memory allocation in that case for libraries is to just provide allocator callbacks
|
|
|
|
which is implemented aswell with the `gui_allocator`
|
|
|
|
structure, there are two addition ways to provided memory. The
|
|
|
|
first one is to just providing a static fixed size memory block to fill up which
|
|
|
|
is handy for UIs with roughly known memory requirements. The other way of memory
|
|
|
|
managment is to extend the fixed size block with the abiltiy to resize your block
|
|
|
|
at the end of the frame if there is not enough memory.
|
|
|
|
For the purpose of resizable fixed size memory blocks and for general
|
|
|
|
information about memory consumption the `gui_memory_info` structure was
|
|
|
|
added. It contains information about the allocated amount of data in the current
|
|
|
|
frame as well as the needed amount if not enough memory was provided.
|
|
|
|
|
|
|
|
```c
|
2015-08-10 21:34:47 +03:00
|
|
|
/* fixed size queue */
|
2015-08-07 17:53:52 +03:00
|
|
|
void *memory = malloc(size);
|
2015-08-10 21:34:47 +03:00
|
|
|
gui_command_queue queue;
|
|
|
|
gui_command_queue_init_fixed(&queue, memory, MEMORY_SIZE, GUI_CLIP);
|
2015-08-07 17:53:52 +03:00
|
|
|
```
|
|
|
|
|
|
|
|
```c
|
2015-08-10 21:34:47 +03:00
|
|
|
/* dynamically growing queue */
|
2015-08-07 17:53:52 +03:00
|
|
|
struct gui_allocator alloc;
|
|
|
|
alloc.userdata = your_allocator;
|
|
|
|
alloc.alloc = your_allocation_callback;
|
|
|
|
alloc.relloac = your_reallocation_callback;
|
|
|
|
alloc.free = your_free_callback;
|
|
|
|
|
|
|
|
struct gui_command_queue queue;
|
|
|
|
const gui_size initial_size = 4*1024;
|
|
|
|
const gui_float grow_factor = 2.0f;
|
|
|
|
gui_command_queue_init(&queue, &alloc, initial_size, grow_factor);
|
|
|
|
```
|
|
|
|
|
2015-04-12 16:40:42 +03:00
|
|
|
## FAQ
|
2015-04-20 23:29:31 +03:00
|
|
|
#### Where is the demo/example code?
|
2015-04-30 17:12:21 +03:00
|
|
|
The demo and example code can be found in the demo folder.
|
2015-08-29 12:04:09 +03:00
|
|
|
There is demo code for Linux(X11) and nanovg.
|
2015-04-12 16:40:42 +03:00
|
|
|
|
2015-04-16 21:05:43 +03:00
|
|
|
#### Why did you use ANSI C and not C99 or C++?
|
2015-04-12 16:40:42 +03:00
|
|
|
Personally I stay out of all "discussions" about C vs C++ since they are totally
|
|
|
|
worthless and never brought anything good with it. The simple answer is I
|
2015-05-03 13:22:43 +03:00
|
|
|
personally love C and have nothing against people using C++ especially the new
|
2015-04-16 17:20:00 +03:00
|
|
|
iterations with C++11 and C++14.
|
2015-04-12 16:40:42 +03:00
|
|
|
While this hopefully settles my view on C vs C++ there is still ANSI C vs C99.
|
2015-04-16 21:05:43 +03:00
|
|
|
While for personal projects I only use C99 with all its niceties, libraries are
|
2015-04-16 17:20:00 +03:00
|
|
|
a little bit different. Libraries are designed to reach the highest number of
|
2015-04-17 23:28:00 +03:00
|
|
|
users possible which brings me to ANSI C as the most portable version.
|
2015-04-16 21:05:43 +03:00
|
|
|
In addition not all C compiler like the MSVC
|
2015-04-16 17:20:00 +03:00
|
|
|
compiler fully support C99, which finalized my decision to use ANSI C.
|
2015-04-12 16:40:42 +03:00
|
|
|
|
2015-04-20 23:29:31 +03:00
|
|
|
#### Why do you typedef your own types instead of using the standard types?
|
2015-04-16 21:05:43 +03:00
|
|
|
This Project uses ANSI C which does not have the header file `<stdint.h>`
|
|
|
|
and therefore does not provide the fixed sized types that I need. Therefore
|
2015-04-12 16:40:42 +03:00
|
|
|
I defined my own types which need to be set to the correct size for each
|
2015-05-03 13:22:43 +03:00
|
|
|
platform. But if your development environment provides the header file you can define
|
2015-04-12 16:40:42 +03:00
|
|
|
`GUI_USE_FIXED_SIZE_TYPES` to directly use the correct types.
|
|
|
|
|
2015-04-20 23:29:31 +03:00
|
|
|
#### Why is font/input/window management not provided?
|
2015-04-17 23:28:00 +03:00
|
|
|
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
|
2015-05-03 13:22:43 +03:00
|
|
|
to be as independent and out of the users way as possible.
|
|
|
|
This means in practice a little bit more work on the users behalf but grants a
|
2015-04-17 23:28:00 +03:00
|
|
|
lot more freedom especially because the toolkit is designed to be embeddable.
|
|
|
|
|
2015-04-24 16:17:10 +03:00
|
|
|
The font management on the other hand is litte bit more tricky. In the beginning
|
2015-04-20 12:13:27 +03:00
|
|
|
the toolkit had some basic font handling but I removed it later. This is mainly
|
2015-04-17 23:28:00 +03:00
|
|
|
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.
|
|
|
|
|
2015-03-14 19:05:30 +03:00
|
|
|
## References
|
2015-04-12 16:40:42 +03:00
|
|
|
- [Tutorial from Jari Komppa about imgui libraries](http://www.johno.se/book/imgui.html)
|
2015-03-25 16:32:43 +03:00
|
|
|
- [Johannes 'johno' Norneby's article](http://iki.fi/sol/imgui/)
|
2015-04-12 16:40:42 +03:00
|
|
|
- [Casey Muratori's original introduction to imgui's](http:://mollyrocket.com/861?node=861)
|
2015-05-18 14:37:16 +03:00
|
|
|
- [Casey Muratori's imgui panel design (1/2)](http://mollyrocket.com/casey/stream_0019.html)
|
|
|
|
- [Casey Muratori's imgui panel design (2/2)](http://mollyrocket.com/casey/stream_0020.html)
|
2015-04-24 16:17:10 +03:00
|
|
|
- [Casey Muratori: Designing and Evaluation Reusable Components](http://mollyrocket.com/casey/stream_0028.html)
|
2015-04-12 16:40:42 +03:00
|
|
|
- [ImGui: The inspiration for this project](https://github.com/ocornut/imgui)
|
2015-04-20 22:36:16 +03:00
|
|
|
- [Nvidia's imgui toolkit](https://code.google.com/p/nvidia-widgets/)
|
2015-03-14 19:05:30 +03:00
|
|
|
|
2015-03-03 19:24:02 +03:00
|
|
|
# License
|
|
|
|
(The MIT License)
|