major rewrite

This commit is contained in:
vurtun 2015-04-19 22:12:37 +02:00
parent 6297aeac68
commit 075925a66c
4 changed files with 944 additions and 1853 deletions

127
Readme.md
View File

@ -5,143 +5,60 @@ WORK IN PROGRESS: I do not garantee that everything works right now
## Features
- Immediate mode graphical user interface toolkit
- Written in C89 (ANSI C)
- Small (~2.5kLOC)
- Small codebase with roughly 2kLOC
- Focus on portability and minimal internal state
- Suited for embedding into graphical applications
- No global hidden state
- No direct dependencies (not even libc!)
- No memory allocation
- Renderer and platform independent
- Configurable
- UTF-8 supported
## Limitations
- Does NOT provide platform independent window management
- Does NOT provide platform independent input handling
- Does NOT provide window management
- Does NOT provide input handling
- Does NOT provide a renderer backend
- Does NOT implement a font library
Summary: It is only responsible for the actual user interface
## 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
a lot of the boilerplate code but takes away some freedom of widget placing and
introduces the state of the panel.
Finally there is the context layer which represent the complete window and
enables moveable, scaleable and overlapping panels, but needs complete control
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 = {...};
struct gui_input in = {0};
struct gui_canvas canvas = {...};
struct gui_button style = {...};
gui_default_config(&config);
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))
if (gui_button_text(&canvas, 50, 50, 100, 30, &style, "button", GUI_BUTTON_DEFAULT, &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_input in = {0};
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};
struct gui_canvas canvas = {...};
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_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))
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 */
}
```
@ -198,22 +115,6 @@ 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.
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

@ -1,3 +1,4 @@
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
@ -11,10 +12,8 @@
#include "../gui.h"
/* macros */
#define MAX_BUFFER 64
#define MAX_MEMORY (16 * 1024)
#define MAX_DEPTH 8
#define MAX_PANEL 4
#define MAX_BUFFER 64
#define MAX_DEPTH 4
#define WIN_WIDTH 800
#define WIN_HEIGHT 600
#define DTIME 16
@ -29,38 +28,6 @@ typedef struct XFont XFont;
typedef struct XSurface XSurface;
typedef struct XWindow XWindow;
struct XFont {
int ascent;
int descent;
int height;
XFontSet set;
XFontStruct *xfont;
};
struct XSurface {
GC gc;
Display *dpy;
int screen;
Window root;
Drawable drawable;
unsigned int w, h;
};
struct XWindow {
Display *dpy;
Window root;
Visual *vis;
Colormap cmap;
XWindowAttributes attr;
XSetWindowAttributes swa;
Window win;
GC gc;
int screen;
unsigned int width;
unsigned int height;
XSurface surface;
};
struct demo {
gui_char in_buf[MAX_BUFFER];
gui_size in_len;
@ -78,6 +45,38 @@ struct demo {
gui_size current;
};
struct XFont {
int ascent;
int descent;
int height;
XFontSet set;
XFontStruct *xfont;
};
struct XSurface {
GC gc;
Display *dpy;
int screen;
Window root;
Drawable drawable;
unsigned int w, h;
gui_size clip_depth;
struct gui_rect clips[MAX_DEPTH];
};
struct XWindow {
Display *dpy;
Window root;
Visual *vis;
Colormap cmap;
XWindowAttributes attr;
XSetWindowAttributes swa;
Window win;
int screen;
unsigned int width;
unsigned int height;
};
static void
die(const char *fmt, ...)
{
@ -115,122 +114,6 @@ sleep_for(long t)
while(-1 == nanosleep(&req, &req));
}
static XSurface*
surface_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h)
{
XSurface *surface = xcalloc(1, sizeof(XSurface));
surface->w = w;
surface->h = h;
surface->dpy = dpy;
surface->screen = screen;
surface->root = root;
surface->gc = XCreateGC(dpy, root, 0, NULL);
XSetLineAttributes(dpy, surface->gc, 1, LineSolid, CapButt, JoinMiter);
surface->drawable = XCreatePixmap(dpy, root, w, h, (unsigned int)DefaultDepth(dpy, screen));
return surface;
}
static void
surface_resize(XSurface *surf, unsigned int w, unsigned int h) {
if(!surf) return;
surf->w = w; surf->h = h;
if(surf->drawable) XFreePixmap(surf->dpy, surf->drawable);
surf->drawable = XCreatePixmap(surf->dpy, surf->root, w, h,
(unsigned int)DefaultDepth(surf->dpy, surf->screen));
}
static void
surface_scissor(XSurface *surf, int x, int y, unsigned int w, unsigned int h)
{
XRectangle clip_rect;
clip_rect.x = (short)x;
clip_rect.y = (short)y;
clip_rect.width = (unsigned short)w;
clip_rect.height = (unsigned short)h;
clip_rect.width = (unsigned short)MIN(surf->w, clip_rect.width);
clip_rect.height = (unsigned short)MIN(surf->h, clip_rect.height);
XSetClipRectangles(surf->dpy, surf->gc, 0, 0, &clip_rect, 1, Unsorted);
}
static void
surface_draw_line(XSurface *surf, int x0, int y0, int x1, int y1, unsigned long c)
{
XSetForeground(surf->dpy, surf->gc, c);
XDrawLine(surf->dpy, surf->drawable, surf->gc, x0, y0, x1, y1);
}
static void
surface_draw_rect(XSurface *surf, int x, int y, unsigned int w, unsigned int h, unsigned long c)
{
XSetForeground(surf->dpy, surf->gc, c);
XFillRectangle(surf->dpy, surf->drawable, surf->gc, x, y, w, h);
}
static void
surface_draw_triangle(XSurface *surf, int x0, int y0, int x1, int y1, int x2, int y2,
unsigned long c)
{
XPoint pnts[3];
pnts[0].x = (short)x0;
pnts[0].y = (short)y0;
pnts[1].x = (short)x1;
pnts[1].y = (short)y1;
pnts[2].x = (short)x2;
pnts[2].y = (short)y2;
XSetForeground(surf->dpy, surf->gc, c);
XFillPolygon(surf->dpy, surf->drawable, surf->gc, pnts, 3, Convex, CoordModeOrigin);
}
static void
surface_draw_circle(XSurface *surf, int x, int y, unsigned int radius, unsigned long c)
{
unsigned int d = radius * 2;
XSetForeground(surf->dpy, surf->gc, c);
x -= (int)radius;
y -= (int)radius;
XFillArc(surf->dpy, surf->drawable, surf->gc, x, y, d, d, 0, 360 * 64);
}
static void
surface_draw_text(XSurface *surf, XFont *font, int x, int y, unsigned int w, unsigned int h,
const char *text, unsigned int len, unsigned long bg, unsigned long fg)
{
int i, tx, ty, th, olen;
XSetForeground(surf->dpy, surf->gc, bg);
XFillRectangle(surf->dpy, surf->drawable, surf->gc, x, y, w, h);
if(!text || !font || !len) return;
th = font->ascent + font->descent;
ty = y + ((int)h / 2) - (th / 2) + font->ascent;
tx = x;/* + ((int)h / 2);*/
XSetForeground(surf->dpy, surf->gc, fg);
if(font->set)
XmbDrawString(surf->dpy, surf->drawable, font->set, surf->gc, tx, ty, text, (int)len);
else
XDrawString(surf->dpy, surf->drawable, surf->gc, tx, ty, text, (int)len);
}
static void
surface_clear(XSurface *surf, unsigned long color)
{
XSetForeground(surf->dpy, surf->gc, color);
XFillRectangle(surf->dpy, surf->drawable, surf->gc, 0, 0, surf->w, surf->h);
}
static void
surface_blit(Drawable target, XSurface *surf, unsigned int width, unsigned int height)
{
XCopyArea(surf->dpy, surf->drawable, target, surf->gc, 0, 0, width, height, 0, 0);
}
static void
surface_del(XSurface *surf)
{
XFreePixmap(surf->dpy, surf->drawable);
XFreeGC(surf->dpy, surf->gc);
free(surf);
}
static XFont*
font_create(Display *dpy, const char *name)
{
@ -295,6 +178,153 @@ font_del(Display *dpy, XFont *font)
free(font);
}
static unsigned long
color_from_byte(struct gui_color col)
{
unsigned long res = 0;
res |= (unsigned long)col.r << 16;
res |= (unsigned long)col.g << 8;
res |= (unsigned long)col.b << 0;
return (res);
}
static XSurface*
surface_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h)
{
XSurface *surface = xcalloc(1, sizeof(XSurface));
surface->w = w;
surface->h = h;
surface->dpy = dpy;
surface->screen = screen;
surface->root = root;
surface->gc = XCreateGC(dpy, root, 0, NULL);
XSetLineAttributes(dpy, surface->gc, 1, LineSolid, CapButt, JoinMiter);
surface->drawable = XCreatePixmap(dpy, root, w, h, (unsigned int)DefaultDepth(dpy, screen));
return surface;
}
static void
surface_resize(XSurface *surf, unsigned int w, unsigned int h) {
if(!surf) return;
if (surf->w == w && surf->h == h) return;
surf->w = w; surf->h = h;
if(surf->drawable) XFreePixmap(surf->dpy, surf->drawable);
surf->drawable = XCreatePixmap(surf->dpy, surf->root, w, h,
(unsigned int)DefaultDepth(surf->dpy, surf->screen));
}
static void
unify(struct gui_rect *clip, const struct gui_rect *a, const struct gui_rect *b)
{
clip->x = MAX(a->x, b->x);
clip->y = MAX(a->y, b->y);
clip->w = MIN(a->x + a->w, b->x + b->w) - clip->x;
clip->h = MIN(a->y + a->h, b->y + b->h) - clip->y;
}
static void
surface_scissor(void *handle, gui_float x, gui_float y, gui_float w, gui_float h)
{
XSurface *surf = handle;
XRectangle clip_rect;
clip_rect.x = (short)x;
clip_rect.y = (short)y;
clip_rect.width = (unsigned short)w;
clip_rect.height = (unsigned short)h;
clip_rect.width = (unsigned short)MIN(surf->w, clip_rect.width);
clip_rect.height = (unsigned short)MIN(surf->h, clip_rect.height);
XSetClipRectangles(surf->dpy, surf->gc, 0, 0, &clip_rect, 1, Unsorted);
}
static void
surface_draw_line(void *handle, gui_float x0, gui_float y0, gui_float x1, gui_float y1, struct gui_color col)
{
XSurface *surf = handle;
unsigned long c = color_from_byte(col);
XSetForeground(surf->dpy, surf->gc, c);
XDrawLine(surf->dpy, surf->drawable, surf->gc, (int)x0, (int)y0, (int)x1, (int)y1);
}
static void
surface_draw_rect(void *handle, gui_float x, gui_float y, gui_float w, gui_float h, struct gui_color col)
{
XSurface *surf = handle;
unsigned long c = color_from_byte(col);
XSetForeground(surf->dpy, surf->gc, c);
XFillRectangle(surf->dpy, surf->drawable, surf->gc, (int)x, (int)y, (unsigned)w, (unsigned)h);
}
static void
surface_draw_triangle(void *handle, const struct gui_vec2 *src, struct gui_color col)
{
XPoint pnts[3];
XSurface *surf = handle;
unsigned long c = color_from_byte(col);
pnts[0].x = (short)src[0].x;
pnts[0].y = (short)src[0].y;
pnts[1].x = (short)src[1].x;
pnts[1].y = (short)src[1].y;
pnts[2].x = (short)src[2].x;
pnts[2].y = (short)src[2].y;
XSetForeground(surf->dpy, surf->gc, c);
XFillPolygon(surf->dpy, surf->drawable, surf->gc, pnts, 3, Convex, CoordModeOrigin);
}
static void
surface_draw_circle(void *handle, gui_float x, gui_float y, gui_float w, gui_float h, struct gui_color col)
{
XSurface *surf = handle;
unsigned long c = color_from_byte(col);
XSetForeground(surf->dpy, surf->gc, c);
XFillArc(surf->dpy, surf->drawable, surf->gc, (int)x, (int)y,
(unsigned)w, (unsigned)h, 0, 360 * 64);
}
static void
surface_draw_text(void *handle, gui_float x, gui_float y, gui_float w, gui_float h, const gui_char *text,
gui_size len, const struct gui_font *f, struct gui_color cbg, struct gui_color cfg)
{
int i, tx, ty, th, olen;
XSurface *surf = handle;
XFont *font = f->userdata;
unsigned long bg = color_from_byte(cbg);
unsigned long fg = color_from_byte(cfg);
XSetForeground(surf->dpy, surf->gc, bg);
XFillRectangle(surf->dpy, surf->drawable, surf->gc, (int)x, (int)y, (unsigned)w, (unsigned)h);
if(!text || !font || !len) return;
tx = (int)x;
th = font->ascent + font->descent;
ty = (int)y + ((int)h / 2) - (th / 2) + font->ascent;
XSetForeground(surf->dpy, surf->gc, fg);
if(font->set)
XmbDrawString(surf->dpy, surf->drawable, font->set, surf->gc, tx, ty, (const char*)text, (int)len);
else
XDrawString(surf->dpy, surf->drawable, surf->gc, tx, ty, (const char*)text, (int)len);
}
static void
surface_clear(XSurface *surf, unsigned long color)
{
XSetForeground(surf->dpy, surf->gc, color);
XFillRectangle(surf->dpy, surf->drawable, surf->gc, 0, 0, surf->w, surf->h);
}
static void
surface_blit(Drawable target, XSurface *surf, unsigned int width, unsigned int height)
{
XCopyArea(surf->dpy, surf->drawable, target, surf->gc, 0, 0, width, height, 0, 0);
}
static void
surface_del(XSurface *surf)
{
XFreePixmap(surf->dpy, surf->drawable);
XFreeGC(surf->dpy, surf->gc);
free(surf);
}
static void
key(struct XWindow *xw, struct gui_input *in, XEvent *evt, gui_bool down)
{
@ -329,7 +359,7 @@ motion(struct gui_input *in, XEvent *evt)
}
static void
button(struct gui_input *in, XEvent *evt, gui_bool down)
btn(struct gui_input *in, XEvent *evt, gui_bool down)
{
const gui_int x = evt->xbutton.x;
const gui_int y = evt->xbutton.y;
@ -346,103 +376,24 @@ resize(struct XWindow *xw, XSurface *surf)
surface_resize(surf, xw->width, xw->height);
}
static unsigned long
color_from_byte(struct gui_color col)
{
unsigned long res = 0;
res |= (unsigned long)col.r << 16;
res |= (unsigned long)col.g << 8;
res |= (unsigned long)col.b << 0;
return (res);
}
static void
execute(XSurface *surf, const struct gui_command_list *list)
{
const struct gui_command *iter = NULL;
if (!list->count) return;
iter = list->begin;
while (iter != list->end) {
switch (iter->type) {
case GUI_COMMAND_NOP: break;
case GUI_COMMAND_CLIP: {
const struct gui_command_clip *clip = (const struct gui_command_clip*)iter;
surface_scissor(surf, (int)clip->x, (int)clip->y,
(unsigned int)clip->w, (unsigned int)clip->h);
} break;
case GUI_COMMAND_LINE: {
const struct gui_command_line *line = (const struct gui_command_line*)iter;
const unsigned long color = color_from_byte(line->color);
surface_draw_line(surf, (int)line->from.x, (int)line->from.y,
(int)line->to.x, (int)line->to.y, color);
} break;
case GUI_COMMAND_RECT: {
const struct gui_command_rect *rect = (const struct gui_command_rect*)iter;
const unsigned long color = color_from_byte(rect->color);
surface_draw_rect(surf, (int)rect->x, (int)rect->y,
(unsigned int)rect->w, (unsigned int)rect->h, color);
} break;
case GUI_COMMAND_CIRCLE: {
const struct gui_command_circle *circle = (const struct gui_command_circle*)iter;
const unsigned long color = color_from_byte(circle->color);
surface_draw_circle(surf, (int)circle->x, (int)circle->y,
(unsigned int)circle->radius, color);
} break;
case GUI_COMMAND_BITMAP: {
const struct gui_command_bitmap *bitmap = (const struct gui_command_bitmap*)iter;
} break;
case GUI_COMMAND_TRIANGLE: {
const struct gui_command_triangle *triangle = (const struct gui_command_triangle*)iter;
const unsigned long color = color_from_byte(triangle->color);
surface_draw_triangle(surf, (int)triangle->pnt[0].x, (int)triangle->pnt[0].y,
(int)triangle->pnt[1].x, (int)triangle->pnt[1].y, (int)triangle->pnt[2].x,
(int)triangle->pnt[2].y, color);
} break;
case GUI_COMMAND_TEXT: {
const struct gui_command_text *text = (const struct gui_command_text*)iter;
const unsigned long bg = color_from_byte(text->background);
const unsigned long fg = color_from_byte(text->foreground);
surface_draw_text(surf, text->font, (int)text->x, (int)text->y,
(unsigned int)text->w, (unsigned int)text->h,
(const char*)text->string, (unsigned int)text->length, bg, fg);
} break;
case GUI_COMMAND_MAX: break;
default: break;
}
iter = iter->next;
}
}
static void
gui_draw(XSurface *surf, const struct gui_output *out)
{
gui_size i = 0;
if (!out->list_size) return;
for (i = 0; i < out->list_size; i++)
execute(surf, out->list[i]);
}
static gui_bool
demo_panel(struct gui_context *ctx, struct gui_panel *panel, struct demo *demo)
demo_panel(struct gui_panel *panel, struct demo *demo)
{
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"};
gui_bool running;
running = gui_begin_panel(ctx, panel, "Demo",
GUI_PANEL_CLOSEABLE|GUI_PANEL_MINIMIZABLE|GUI_PANEL_SCALEABLE|
GUI_PANEL_MOVEABLE|GUI_PANEL_BORDER);
/* Tabs */
gui_panel_layout(panel, 100, 1);
gui_panel_tab_begin(panel, &demo->tab, "Difficulty");
gui_panel_layout(&demo->tab, 30, 1);
gui_panel_layout(&demo->tab, 30, 3);
if (gui_panel_option(&demo->tab, "easy", demo->option == 0)) demo->option = 0;
if (gui_panel_option(&demo->tab, "hard", demo->option == 1)) demo->option = 1;
if (gui_panel_option(&demo->tab, "normal", demo->option == 2)) demo->option = 2;
if (gui_panel_option(&demo->tab, "godlike", demo->option == 3)) demo->option = 3;
if (gui_panel_option(&demo->tab, "normal", demo->option == 1)) demo->option = 1;
if (gui_panel_option(&demo->tab, "hard", demo->option == 2)) demo->option = 2;
if (gui_panel_option(&demo->tab, "hell", demo->option == 3)) demo->option = 3;
if (gui_panel_option(&demo->tab, "doom", demo->option == 4)) demo->option = 4;
if (gui_panel_option(&demo->tab, "godlike", demo->option == 5)) demo->option = 5;
gui_panel_tab_end(panel, &demo->tab);
/* Shelf */
@ -466,23 +417,7 @@ demo_panel(struct gui_context *ctx, struct gui_panel *panel, struct demo *demo)
demo->prog = gui_panel_progress(&demo->group, demo->prog, 100, gui_true);
demo->item_cur = gui_panel_selector(&demo->group, items, LEN(items), demo->item_cur);
demo->spin_act = gui_panel_spinner(&demo->group, 0, &demo->spinner, 250, 10, demo->spin_act);
demo->in_act = gui_panel_input(&demo->group, demo->in_buf, &demo->in_len,
MAX_BUFFER, GUI_INPUT_DEFAULT, demo->in_act);
gui_panel_group_end(panel, &demo->group);
gui_end_panel(ctx, panel, NULL);
return running;
}
static void
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);
gui_panel_button_text(panel, "ok", GUI_BUTTON_DEFAULT);
gui_panel_button_text(panel, "cancel", GUI_BUTTON_DEFAULT);
gui_end_panel(ctx, panel, NULL);
}
int
@ -491,23 +426,19 @@ main(int argc, char *argv[])
long dt;
long started;
gui_bool running = gui_true;
XWindow xw;
/* GUI */
struct demo demo;
gui_bool checked = gui_false;
gui_float value = 5.0f;
gui_size done = 20;
struct gui_input in;
struct gui_config config;
struct gui_memory memory;
struct gui_font font;
struct gui_panel *panel;
struct gui_panel *message;
struct gui_context *ctx;
struct gui_output output;
struct gui_canvas canvas;
struct gui_panel panel;
struct demo demo;
/* Window */
XWindow xw;
XSurface *surf;
XFont *xfont;
UNUSED(argc); UNUSED(argv);
memset(&xw, 0, sizeof xw);
xw.dpy = XOpenDisplay(NULL);
@ -527,22 +458,26 @@ main(int argc, char *argv[])
XGetWindowAttributes(xw.dpy, xw.win, &xw.attr);
xw.width = (unsigned int)xw.attr.width;
xw.height = (unsigned int)xw.attr.height;
xfont = font_create(xw.dpy, "fixed");
surf = surface_create(xw.dpy, xw.screen, xw.win, xw.width, xw.height);
/* GUI */
memory.size = MAX_MEMORY;
memory.memory = calloc(1, memory.size);
memory.max_depth = MAX_DEPTH;
memory.max_panels = MAX_PANEL;
ctx = gui_new(&memory, &in);
font.user = xfont;
font.height = (gui_float)xfont->height;
font.width = font_get_text_width;
canvas.userdata = surface_create(xw.dpy, xw.screen, xw.win, xw.width, xw.height);
canvas.width = xw.width;
canvas.height = xw.height;
canvas.scissor = surface_scissor;
canvas.draw_line = surface_draw_line;
canvas.draw_rect = surface_draw_rect;
canvas.draw_circle = surface_draw_circle;
canvas.draw_triangle = surface_draw_triangle;
canvas.draw_text = surface_draw_text;
canvas.font.userdata = font_create(xw.dpy, "fixed");
canvas.font.height = (gui_float)((XFont*)canvas.font.userdata)->height;
canvas.font.width = font_get_text_width;
gui_default_config(&config);
panel = gui_new_panel(ctx, 50, 50, 400, 300, &config, &font);
message = gui_new_panel(ctx, 150, 150, 200, 100, &config, &font);
memset(&in, 0, sizeof in);
memset(&panel, 0, sizeof panel);
panel.x = 50; panel.y = 50;
/*panel.w = 420; panel.h = 300;*/
panel.w = 420; panel.h = 200;
memset(&demo, 0, sizeof(demo));
demo.tab.minimized = gui_true;
@ -559,25 +494,24 @@ main(int argc, char *argv[])
while (XCheckWindowEvent(xw.dpy, xw.win, xw.swa.event_mask, &evt)) {
if (evt.type == KeyPress) key(&xw, &in, &evt, gui_true);
else if (evt.type == KeyRelease) key(&xw, &in, &evt, gui_false);
else if (evt.type == ButtonPress) button(&in, &evt, gui_true);
else if (evt.type == ButtonRelease) button(&in, &evt, gui_false);
else if (evt.type == ButtonPress) btn(&in, &evt, gui_true);
else if (evt.type == ButtonRelease) btn(&in, &evt, gui_false);
else if (evt.type == MotionNotify) motion(&in, &evt);
else if (evt.type == Expose || evt.type == ConfigureNotify)
resize(&xw, surf);
resize(&xw, canvas.userdata);
}
gui_input_end(&in);
/* GUI */
gui_begin(ctx, (gui_float)xw.width, (gui_float)xw.height);
running = demo_panel(ctx, panel, &demo);
message_panel(ctx, message);
gui_end(ctx, &output, NULL);
/* Draw */
XClearWindow(xw.dpy, xw.win);
surface_clear(surf, 0x00646464);
gui_draw(surf, &output);
surface_blit(xw.win, surf, xw.width, xw.height);
surface_clear(canvas.userdata, 0x002D2D2D);
canvas.width = xw.width; canvas.height = 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, &in);
demo_panel(&panel, &demo);
gui_panel_end(&panel);
surface_blit(xw.win, canvas.userdata, xw.width, xw.height);
XFlush(xw.dpy);
/* Timing */
@ -586,9 +520,10 @@ main(int argc, char *argv[])
sleep_for(DTIME - dt);
}
free(memory.memory);
font_del(xw.dpy, xfont);
surface_del(surf);
font_del(xw.dpy, canvas.font.userdata);
surface_del(canvas.userdata);
XUnmapWindow(xw.dpy, xw.win);
XFreeColormap(xw.dpy, xw.cmap);
XDestroyWindow(xw.dpy, xw.win);
XCloseDisplay(xw.dpy);
return 0;

1854
gui.c

File diff suppressed because it is too large Load Diff

293
gui.h
View File

@ -42,13 +42,6 @@ typedef unsigned int gui_flag;
typedef unsigned long gui_size;
#endif
typedef struct gui_panel gui_tab;
typedef struct gui_panel gui_group;
typedef struct gui_panel gui_shelf;
typedef gui_char gui_glyph[GUI_UTF_SIZE];
typedef union {void* handle; gui_uint id;} gui_texture;
typedef gui_size(*gui_text_width_f)(void*,const gui_char*, 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;};
@ -56,9 +49,24 @@ 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;};
struct gui_font;
typedef struct gui_panel gui_tab;
typedef struct gui_panel gui_group;
typedef struct gui_panel gui_shelf;
typedef gui_char gui_glyph[GUI_UTF_SIZE];
typedef union {void* handle; gui_uint id;} gui_texture;
typedef gui_size(*gui_text_width_f)(void*,const gui_char*, gui_size);
typedef void(*gui_scissor)(void*, gui_float, gui_float, gui_float, gui_float);
typedef void(*gui_draw_line)(void*, gui_float, gui_float, gui_float, gui_float, struct gui_color);
typedef void(*gui_draw_rect)(void*, gui_float, gui_float, gui_float, gui_float, struct gui_color);
typedef void(*gui_draw_circle)(void*, gui_float, gui_float, gui_float, gui_float, struct gui_color);
typedef void(*gui_draw_triangle)(void*, const struct gui_vec2*, struct gui_color);
typedef void(*gui_draw_text)(void*, gui_float, gui_float, gui_float, gui_float,
const gui_char*, gui_size, const struct gui_font*,
struct gui_color, struct gui_color);
struct gui_font {
void *user;
void *userdata;
gui_float height;
gui_text_width_f width;
};
@ -86,105 +94,17 @@ struct gui_input {
struct gui_vec2 mouse_clicked_pos;
};
struct gui_memory {
void *memory;
gui_size size;
gui_size max_panels;
gui_size max_depth;
};
struct gui_memory_status {
gui_size allocated;
gui_size needed;
};
enum gui_command_type {
GUI_COMMAND_NOP = 0,
GUI_COMMAND_CLIP,
GUI_COMMAND_LINE,
GUI_COMMAND_RECT,
GUI_COMMAND_CIRCLE,
GUI_COMMAND_BITMAP,
GUI_COMMAND_TRIANGLE,
GUI_COMMAND_TEXT,
GUI_COMMAND_MAX
};
struct gui_command {
enum gui_command_type type;
struct gui_command *next;
};
struct gui_command_clip {
struct gui_command header;
gui_float x, y;
gui_float w, h;
};
struct gui_command_line {
struct gui_command header;
struct gui_color color;
struct gui_vec2 from;
struct gui_vec2 to;
};
struct gui_command_rect {
struct gui_command header;
struct gui_color color;
gui_float x, y;
gui_float w, h;
};
struct gui_command_circle {
struct gui_command header;
struct gui_color color;
gui_float radius;
gui_float x, y;
};
struct gui_command_bitmap {
struct gui_command header;
gui_texture texture;
struct gui_rect src;
struct gui_rect dst;
};
struct gui_command_triangle {
struct gui_command header;
struct gui_vec2 pnt[3];
struct gui_color color;
};
struct gui_command_text {
struct gui_command header;
struct gui_color background;
struct gui_color foreground;
void *font;
gui_float x, y;
gui_float w, h;
gui_size length;
gui_byte string[1];
};
struct gui_command_list{
struct gui_command *begin;
struct gui_command *end;
gui_size count;
gui_size capacity;
gui_size needed;
gui_size size;
};
struct gui_command_buffer {
struct gui_command_list cmds;
struct gui_rect *clips;
gui_size clip_capacity;
gui_size clip_size;
};
struct gui_output {
struct gui_command_list **list;
gui_size list_size;
struct gui_canvas {
void *userdata;
gui_size width;
gui_size height;
struct gui_font font;
gui_scissor scissor;
gui_draw_line draw_line;
gui_draw_rect draw_rect;
gui_draw_circle draw_circle;
gui_draw_triangle draw_triangle;
gui_draw_text draw_text;
};
enum gui_text_align {
@ -194,23 +114,14 @@ enum gui_text_align {
};
struct gui_text {
void *font;
gui_float x, y;
gui_float w, h;
gui_float pad_x, pad_y;
const char *string;
gui_size length;
struct gui_vec2 padding;
enum gui_text_align align;
struct gui_color foreground;
struct gui_color background;
};
struct gui_image {
struct gui_rect src;
struct gui_rect dst;
gui_float pad_x, pad_y;
struct gui_color color;
gui_texture texture;
struct gui_vec2 padding;
struct gui_color background;
};
@ -220,11 +131,8 @@ enum gui_button_behavior {
};
struct gui_button {
gui_float x, y;
gui_float w, h;
gui_float pad_x, pad_y;
gui_float border;
enum gui_button_behavior behavior;
struct gui_vec2 padding;
struct gui_color background;
struct gui_color foreground;
struct gui_color content;
@ -238,45 +146,19 @@ enum gui_toggle_type {
};
struct gui_toggle {
gui_float x, y;
gui_float w, h;
gui_float pad_x, pad_y;
const gui_char *text;
gui_size length;
enum gui_toggle_type type;
struct gui_vec2 padding;
struct gui_color font;
struct gui_color background;
struct gui_color foreground;
};
struct gui_slider {
gui_float x, y;
gui_float w, h;
gui_float pad_x, pad_y;
gui_float min, max;
gui_float value;
gui_float step;
struct gui_color background;
struct gui_color foreground;
};
struct gui_progress {
gui_float x, y;
gui_float w, h;
gui_float pad_x, pad_y;
gui_size current;
gui_size max;
gui_bool modifyable;
struct gui_vec2 padding;
struct gui_color background;
struct gui_color foreground;
};
struct gui_scroll {
gui_float x, y;
gui_float w, h;
gui_float offset;
gui_float target;
gui_float step;
struct gui_color background;
struct gui_color foreground;
struct gui_color border;
@ -292,37 +174,23 @@ enum gui_input_filter {
};
struct gui_input_field {
gui_float x, y;
gui_float w, h;
gui_float pad_x, pad_y;
gui_size max;
gui_bool active;
struct gui_vec2 padding;
gui_bool show_cursor;
enum gui_input_filter filter;
gui_bool password;
struct gui_color background;
struct gui_color foreground;
struct gui_color font;
};
struct gui_plot {
gui_float x, y;
gui_float w, h;
gui_float pad_x;
gui_float pad_y;
gui_size value_count;
const gui_float *values;
struct gui_vec2 padding;
struct gui_color background;
struct gui_color foreground;
struct gui_color highlight;
};
struct gui_histo {
gui_float x, y;
gui_float w, h;
gui_float pad_x, pad_y;
gui_size value_count;
const gui_float *values;
struct gui_vec2 padding;
struct gui_color background;
struct gui_color foreground;
struct gui_color negative;
@ -393,6 +261,7 @@ enum gui_panel_flags {
struct gui_panel {
gui_flags flags;
gui_float x, y;
gui_float w, h;
gui_float at_y;
gui_float width, height;
gui_size index;
@ -401,10 +270,10 @@ struct gui_panel {
gui_size row_columns;
gui_float offset;
gui_bool minimized;
struct gui_command_buffer *out;
struct gui_font font;
struct gui_rect clip;
const struct gui_input *in;
const struct gui_config *config;
const struct gui_canvas *canvas;
};
/* Input */
@ -415,46 +284,43 @@ void gui_input_button(struct gui_input*, gui_int x, gui_int y, gui_bool down);
void gui_input_char(struct gui_input*, const gui_glyph);
void gui_input_end(struct gui_input*);
/* Output */
void gui_output_begin(struct gui_command_buffer*, const struct gui_memory*);
void gui_output_end(struct gui_command_buffer*, struct gui_command_list*,
struct gui_memory_status*);
/* Widgets */
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, 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, 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*,
const struct gui_input*);
gui_size gui_widget_progress(struct gui_command_buffer*, const struct gui_progress*,
const struct gui_input*);
gui_bool gui_widget_input(struct gui_command_buffer*, gui_char*, gui_size*,
const struct gui_input_field*, const struct gui_font*,
const struct gui_input*);
gui_float gui_widget_scroll(struct gui_command_buffer*, const struct gui_scroll*,
const struct gui_input*);
gui_int gui_widget_histo(struct gui_command_buffer*, const struct gui_histo*,
const struct gui_input*);
gui_int gui_widget_plot(struct gui_command_buffer*, const struct gui_plot*,
const struct gui_input*);
void gui_text(const struct gui_canvas*, gui_float x, gui_float y, gui_float w, gui_float h,
const struct gui_text*, const char *text, gui_size len);
gui_bool gui_button_text(const struct gui_canvas*, gui_float x, gui_float y,
gui_float w, gui_float h, const struct gui_button*, const char*,
enum gui_button_behavior, const struct gui_input*);
gui_bool gui_button_triangle(const struct gui_canvas*, gui_float x, gui_float y,
gui_float w, gui_float h, const struct gui_button*, enum gui_heading,
enum gui_button_behavior, const struct gui_input*);
gui_bool gui_toggle(const struct gui_canvas*, gui_float x, gui_float y, gui_float w,
gui_float h, gui_bool, const char*, const struct gui_toggle*,
enum gui_toggle_type, const struct gui_input*);
gui_float gui_slider(const struct gui_canvas*, gui_float x, gui_float y, gui_float w,
gui_float h, gui_float min, gui_float val, gui_float max, gui_float step,
const struct gui_slider*, const struct gui_input*);
gui_size gui_progress(const struct gui_canvas*, gui_float x, gui_float y, gui_float w,
gui_float h, gui_size value, gui_size max, gui_bool modifyable,
const struct gui_slider*, const struct gui_input*);
gui_size gui_input(const struct gui_canvas*, gui_float x, gui_float y, gui_float w,
gui_float h, gui_char*, gui_size, gui_size max, gui_bool*,
const struct gui_input_field*, const struct gui_input*);
gui_int gui_histo(const struct gui_canvas*, gui_float x, gui_float y, gui_float w,
gui_float h, const gui_float*, gui_size,
const struct gui_histo*, const struct gui_input*);
gui_int gui_plot(const struct gui_canvas*, gui_float x, gui_float y, gui_float w,
gui_float h, const gui_float*, gui_size,
const struct gui_plot*, const struct gui_input*);
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 */
void gui_default_config(struct gui_config*);
void gui_panel_show(struct gui_panel*);
void gui_panel_hide(struct gui_panel*);
void gui_panel_init(struct gui_panel*, const struct gui_config*, const struct gui_font*);
gui_bool gui_panel_is_hidden(const struct gui_panel*);
gui_bool gui_panel_begin(struct gui_panel*, struct gui_command_buffer*,
const struct gui_input*, const char*,
gui_float x, gui_float y, gui_float w, gui_float h, gui_flags);
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_input*);
void gui_panel_end(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);
@ -465,14 +331,12 @@ gui_bool gui_panel_button_text(struct gui_panel*, const char*, enum gui_button_b
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_bool gui_panel_button_icon(struct gui_panel*, gui_texture, const struct gui_rect*,
enum gui_button_behavior);
gui_float gui_panel_slider(struct gui_panel*, 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_bool modifyable);
gui_bool gui_panel_input(struct gui_panel*, gui_char *buffer, gui_size *len,
gui_size max, enum gui_input_filter, gui_bool active);
gui_size gui_panel_input(struct gui_panel*, gui_char *buffer, gui_size len,
gui_size max, gui_bool *active, enum gui_input_filter);
gui_bool gui_panel_spinner(struct gui_panel*, 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[],
@ -487,19 +351,6 @@ gui_size gui_panel_shelf_begin(gui_shelf*, gui_shelf *shelf, const char *tabs[],
gui_size size, gui_size active);
void gui_panel_shelf_end(struct gui_panel*, gui_shelf *shelf);
/* Context */
struct gui_context;
struct gui_context *gui_new(const struct gui_memory*, const struct gui_input*);
void gui_begin(struct gui_context*, gui_float width, gui_float height);
void gui_end(struct gui_context*, struct gui_output*, struct gui_memory_status*);
struct gui_panel *gui_new_panel(struct gui_context*, gui_float x, gui_float y, gui_float w,
gui_float h, const struct gui_config* , const struct gui_font*);
void gui_del_panel(struct gui_context*, struct gui_panel*);
struct gui_vec2 gui_get_panel_position(const struct gui_context*, const struct gui_panel*);
struct gui_vec2 gui_get_panel_size(const struct gui_context*, const struct gui_panel*);
gui_bool gui_begin_panel(struct gui_context*, struct gui_panel*, const char *title, gui_flags flags);
void gui_end_panel(struct gui_context*, struct gui_panel*, struct gui_memory_status*);
#ifdef __cplusplus
}
#endif