2015-03-11 16:00:59 +03:00
|
|
|
# GUI
|
2015-04-26 12:33:31 +03:00
|
|
|
This is a bloat free stateless immediate mode graphical user interface toolkit
|
|
|
|
written in ANSI C. It was designed as a embeddable user interface for graphical
|
2015-04-20 22:24:39 +03:00
|
|
|
application and does not have any direct dependencies. The main premise of this
|
2015-04-26 12:33:31 +03:00
|
|
|
toolkit is to be as stateless and simple but at the same time as powerful as
|
|
|
|
possible with fast streamlined development speed in mind.
|
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-04-26 11:06:24 +03:00
|
|
|
- Small codebase (~2kLOC)
|
2015-04-07 04:24:31 +03:00
|
|
|
- Focus on portability and minimal internal state
|
2015-04-03 18:40:47 +03:00
|
|
|
- Suited for embedding into graphical applications
|
2015-03-31 19:29:45 +03:00
|
|
|
- No global hidden state
|
2015-04-03 18:40:47 +03:00
|
|
|
- No direct dependencies (not even libc!)
|
2015-04-25 16:30:51 +03:00
|
|
|
- Full memory management control
|
2015-03-25 16:32:43 +03:00
|
|
|
- Renderer and platform independent
|
2015-03-14 19:05:30 +03:00
|
|
|
- Configurable
|
2015-03-25 16:32:43 +03:00
|
|
|
- UTF-8 supported
|
2015-03-24 15:08:42 +03:00
|
|
|
|
2015-04-20 22:14:58 +03:00
|
|
|
## Functionality
|
|
|
|
+ Label
|
2015-04-25 16:30:51 +03:00
|
|
|
+ Buttons
|
2015-04-20 22:14:58 +03:00
|
|
|
+ Slider
|
|
|
|
+ Progressbar
|
|
|
|
+ Checkbox
|
|
|
|
+ Radiobutton
|
2015-04-25 16:30:51 +03:00
|
|
|
+ Input
|
2015-04-20 22:14:58 +03:00
|
|
|
+ Spinner
|
|
|
|
+ Selector
|
|
|
|
+ Linegraph
|
|
|
|
+ Histogram
|
2015-04-20 23:29:31 +03:00
|
|
|
+ Panel
|
2015-04-26 11:06:24 +03:00
|
|
|
+ Tab
|
|
|
|
+ Group
|
|
|
|
+ Shelf
|
2015-04-20 22:14:58 +03:00
|
|
|
|
2015-04-16 21:05:43 +03:00
|
|
|
## Limitations
|
2015-04-25 16:30:51 +03:00
|
|
|
- Does NOT provide os window management
|
2015-04-19 23:12:37 +03:00
|
|
|
- Does NOT provide input handling
|
2015-04-16 21:05:43 +03:00
|
|
|
- Does NOT provide a renderer 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-04-20 22:24:39 +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 neccessary data and immediatly 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 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
|
2015-04-26 12:33:31 +03:00
|
|
|
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
|
2015-04-20 22:24:39 +03:00
|
|
|
simplifies code. Further traits of immediate mode graphic user interfaces are a
|
|
|
|
code driven style, centralized flow control, easy extensibility and
|
|
|
|
understandablity.
|
|
|
|
|
2015-04-25 17:44:43 +03:00
|
|
|
## Example
|
|
|
|
```c
|
2015-04-27 17:51:25 +03:00
|
|
|
struct gui_input input = {0};
|
2015-04-25 17:44:43 +03:00
|
|
|
struct gui_config config;
|
|
|
|
struct gui_font font = {...};
|
|
|
|
struct gui_memory memory = {...};
|
|
|
|
struct gui_command_buffer buffer;
|
|
|
|
struct gui_panel panel;
|
|
|
|
|
|
|
|
gui_default_config(&config);
|
2015-04-25 17:55:27 +03:00
|
|
|
gui_panel_init(&panel, 50, 50, 220, 170,
|
2015-04-25 17:44:43 +03:00
|
|
|
GUI_PANEL_BORDER|GUI_PANEL_MOVEABLE|
|
|
|
|
GUI_PANEL_CLOSEABLE|GUI_PANEL_SCALEABLE|
|
|
|
|
GUI_PANEL_MINIMIZABLE, &config, &font);
|
2015-04-27 17:01:40 +03:00
|
|
|
gui_buffer_init_fixed(buffer, &memory, GUI_CLIP);
|
2015-04-25 17:44:43 +03:00
|
|
|
|
|
|
|
while (1) {
|
|
|
|
gui_input_begin(&input);
|
|
|
|
/* record input */
|
|
|
|
gui_input_end(&input);
|
|
|
|
|
|
|
|
struct gui_canvas canvas;
|
2015-04-27 17:50:49 +03:00
|
|
|
struct gui_command_list list;
|
2015-04-25 17:44:43 +03:00
|
|
|
struct gui_panel_layout layout;
|
2015-04-27 17:01:40 +03:00
|
|
|
struct gui_memory_status status;
|
|
|
|
|
2015-04-26 14:30:22 +03:00
|
|
|
gui_buffer_begin(&canvas, &buffer, window_width, window_height);
|
2015-04-25 17:44:43 +03:00
|
|
|
gui_panel_begin(&layout, &panel, "Demo", &canvas, &input);
|
|
|
|
gui_panel_row(&layout, 30, 1);
|
|
|
|
if (gui_panel_button_text(&layout, "button", GUI_BUTTON_DEFAULT)) {
|
|
|
|
/* event handling */
|
|
|
|
}
|
|
|
|
gui_panel_row(&layout, 30, 2);
|
2015-04-25 17:49:40 +03:00
|
|
|
if (gui_panel_option(&layout, "easy", option == 0)) option = 0;
|
|
|
|
if (gui_panel_option(&layout, "hard", option == 1)) option = 1;
|
2015-04-26 23:38:55 +03:00
|
|
|
gui_panel_text(&layout, "input:", 6, GUI_TEXT_LEFT);
|
2015-04-27 17:51:55 +03:00
|
|
|
len = gui_panel_input(&layout, buffer, len, 256, &active, GUI_INPUT_DEFAULT);
|
2015-04-25 17:44:43 +03:00
|
|
|
gui_panel_end(&layout, &panel);
|
2015-04-26 14:30:22 +03:00
|
|
|
gui_buffer_end(&list, buffer, &status);
|
2015-04-25 17:44:43 +03:00
|
|
|
|
|
|
|
struct gui_command *cmd = list.begin;
|
|
|
|
while (cmd != list.end) {
|
|
|
|
/* execute command */
|
|
|
|
cmd = cmd->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
![gui screenshot](/screen.png?raw=true)
|
|
|
|
|
2015-04-20 22:14:58 +03:00
|
|
|
## 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 directy
|
|
|
|
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.
|
|
|
|
|
2015-04-25 17:44:43 +03:00
|
|
|
### 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.
|
|
|
|
|
|
|
|
```c
|
|
|
|
struct gui_input input = {0};
|
|
|
|
while (1) {
|
|
|
|
gui_input_begin(&input);
|
|
|
|
/* record input */
|
|
|
|
gui_input_end(&input);
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2015-04-24 16:17:10 +03:00
|
|
|
### Canvas
|
|
|
|
The Canvas is the abstract drawing interface between the GUI toolkit
|
|
|
|
and the user and contains drawing callbacks for the primitives
|
|
|
|
scissor, line, rectangle, circle, triangle, bitmap and text which need to be
|
2015-04-26 11:06:24 +03:00
|
|
|
provided by the user. Therefore the canvas is probably the biggest chunk of work
|
|
|
|
to be done by the user.
|
2015-04-24 16:17:10 +03:00
|
|
|
|
2015-04-25 16:30:51 +03:00
|
|
|
### 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.
|
|
|
|
|
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
|
|
|
|
`gui_default_config`. Modification on the fly to the `gui_config` struct is in
|
|
|
|
true immedate mode fashion possible and supported.
|
|
|
|
|
2015-04-25 16:30:51 +03:00
|
|
|
### 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
|
2015-04-26 00:39:46 +03:00
|
|
|
drawing commands for a number of primitives eg.: line, rectangle, circle,
|
|
|
|
triangle and text. memory The command buffer memory is provided by the user
|
|
|
|
in three possible ways. First by providing a fixed size memory block which
|
|
|
|
will be filled up until no memory is left.
|
|
|
|
The second way is extending the fixed size memory block by reallocating at the
|
|
|
|
end of the frame if the providided memory size was not sufficient.
|
2015-04-26 12:33:31 +03:00
|
|
|
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 based around sequence
|
2015-04-26 14:30:22 +03:00
|
|
|
points with an begin sequence point `gui_buffer_begin` and a end sequence
|
|
|
|
point `gui_buffer_end` and modification of state between both points. Just
|
2015-04-26 11:06:24 +03:00
|
|
|
like the input API the buffer modification before the beginning or after the end
|
2015-04-25 16:30:51 +03:00
|
|
|
sequence point is undefined behavior.
|
|
|
|
|
|
|
|
```c
|
|
|
|
struct gui_memory memory = {...};
|
2015-04-28 12:56:12 +03:00
|
|
|
struct gui_memory_status status;
|
|
|
|
struct gui_command_list list;
|
|
|
|
struct gui_command_buffer buffer;
|
2015-04-26 14:30:22 +03:00
|
|
|
gui_buffer_init_fixed(buffer, &memory);
|
2015-04-25 16:30:51 +03:00
|
|
|
|
|
|
|
while (1) {
|
2015-04-26 11:06:24 +03:00
|
|
|
struct gui_canvas canvas;
|
2015-04-26 14:30:22 +03:00
|
|
|
gui_buffer_begin(&canvas, &buffer, window_width, window_height);
|
2015-04-25 16:30:51 +03:00
|
|
|
/* add commands by using the canvas */
|
2015-04-26 14:30:22 +03:00
|
|
|
gui_buffer_end(&list, buffer, &status);
|
2015-04-25 16:30:51 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
```
|
|
|
|
|
2015-04-20 22:14:58 +03:00
|
|
|
### Widgets
|
2015-04-26 12:33:31 +03:00
|
|
|
The minimal widget API provides a number of basic widgets and is designed for
|
|
|
|
uses cases where no complex wideget layouts or grouping is needed.
|
|
|
|
In order for the GUI to work each widget needs a canvas to
|
2015-04-20 22:14:58 +03:00
|
|
|
draw to, positional and widgets specific data as well as user input
|
|
|
|
and returns the from the user input modified state of the widget.
|
|
|
|
|
2015-04-17 23:28:00 +03:00
|
|
|
```c
|
2015-04-25 16:30:51 +03:00
|
|
|
struct gui_input input = {0};
|
|
|
|
struct gui_font font = {...};
|
2015-04-19 23:12:37 +03:00
|
|
|
struct gui_canvas canvas = {...};
|
|
|
|
struct gui_button style = {...};
|
2015-04-17 23:28:00 +03:00
|
|
|
|
|
|
|
while (1) {
|
2015-04-25 16:30:51 +03:00
|
|
|
if(gui_button_text(&canvas, 0, 0, 100, 30, "ok", GUI_BUTTON_DEFAULT, &style, &input, &font))
|
2015-04-17 23:28:00 +03:00
|
|
|
fprintf(stdout, "button pressed!\n");
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2015-04-20 22:14:58 +03:00
|
|
|
### Panels
|
|
|
|
To further extend the basic widget layer and remove some of the boilerplate
|
|
|
|
code the panel was introduced. The panel groups together a number of
|
2015-04-26 00:39:46 +03:00
|
|
|
widgets but in true immediate mode fashion does not save any state from
|
2015-04-20 22:14:58 +03:00
|
|
|
widgets that have been added to the panel. In addition the panel enables a
|
2015-04-26 00:39:46 +03:00
|
|
|
number of nice features on a group of widgets like movement, scaling,
|
|
|
|
closing and minimizing. An additional use for panel is to further extend the
|
|
|
|
grouping of widgets into tabs, groups and shelfs.
|
2015-04-26 12:33:31 +03:00
|
|
|
The panel is divided into a `struct gui_panel` with persistent life time and
|
|
|
|
the `struct gui_panel_layout` structure with a temporary life time.
|
2015-04-26 00:39:46 +03:00
|
|
|
While the layout state is constantly modifed over the course of
|
2015-04-26 12:33:31 +03:00
|
|
|
the frame, the panel struct is only modified at the immediate mode sequence points
|
2015-04-26 00:39:46 +03:00
|
|
|
`gui_panel_begin` and `gui_panel_end`. Therefore all changes to the panel struct inside of both
|
|
|
|
sequence points have no effect in the current frame and are only visible in the
|
2015-04-25 16:30:51 +03:00
|
|
|
next frame.
|
2015-04-20 22:14:58 +03:00
|
|
|
|
2015-04-17 23:28:00 +03:00
|
|
|
```c
|
2015-04-25 16:30:51 +03:00
|
|
|
struct gui_panel panel;
|
2015-04-17 23:28:00 +03:00
|
|
|
struct gui_config config;
|
2015-04-25 16:30:51 +03:00
|
|
|
struct gui_font font = {...}
|
|
|
|
struct gui_input input = {0};
|
2015-04-19 23:12:37 +03:00
|
|
|
struct gui_canvas canvas = {...};
|
2015-04-17 23:28:00 +03:00
|
|
|
gui_default_config(&config);
|
2015-04-25 16:30:51 +03:00
|
|
|
gui_panel_init(&panel, 50, 50, 300, 200, 0, &config, &font);
|
2015-04-17 23:28:00 +03:00
|
|
|
|
|
|
|
while (1) {
|
2015-04-25 16:30:51 +03:00
|
|
|
struct gui_panel_layout layout;
|
2015-04-28 12:56:12 +03:00
|
|
|
gui_panel_begin(&layout, &panel, "Demo", &canvas, &input);
|
2015-04-25 17:44:43 +03:00
|
|
|
gui_panel_row(&layout, 30, 1);
|
2015-04-25 16:30:51 +03:00
|
|
|
if (gui_panel_button_text(&layout, "button", GUI_BUTTON_DEFAULT))
|
2015-04-17 23:28:00 +03:00
|
|
|
fprintf(stdout, "button pressed!\n");
|
2015-04-25 16:30:51 +03:00
|
|
|
value = gui_panel_slider(&layout, 0, value, 10, 1);
|
|
|
|
progress = gui_panel_progress(&layout, progress, 100, gui_true);
|
|
|
|
gui_panel_end(&layout, &panel);
|
2015-04-17 23:28:00 +03:00
|
|
|
}
|
|
|
|
```
|
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-20 22:14:58 +03:00
|
|
|
The demo and example code can be found in the demo folder. For now there is
|
|
|
|
only example code for Linux with X11 and Xlib but a Win32, OpenGL and Directx
|
|
|
|
demo is in the working.
|
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-04-16 17:20:00 +03:00
|
|
|
personally love C and have nothing against people using C++ exspecially the new
|
|
|
|
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-04-16 17:20:00 +03:00
|
|
|
plaform. 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
|
|
|
|
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.
|
|
|
|
|
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-04-20 22:36: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)
|