Added script to UI bytecode compile + file browser

This is quite a big commit which adds an experimental text script to
UI bytecode compiler. The compiler is still under heavy development
and can and will be under heavy changes, so use with caution.

In addition I added the old file browser for unix like platforms
back into the demo. At the moment it only supports windows but
the only part of the file browser that is platform dependend
is the directory content loader which should be easy to implement
in other platforms as well.
This commit is contained in:
vurtun 2016-03-07 18:17:15 +01:00
parent 5ac6e31128
commit b09849f41a
16 changed files with 1450 additions and 51 deletions

View File

@ -1,6 +1,9 @@
#include <limits.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <assert.h>
#include <stdarg.h>
#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
@ -32,13 +35,26 @@ struct icons {
struct zr_image prev;
struct zr_image next;
struct zr_image tools;
struct zr_image directory;
struct zr_image dir;
struct zr_image copy;
struct zr_image convert;
struct zr_image del;
struct zr_image edit;
struct zr_image images[9];
struct zr_image menu[6];
struct zr_image desktop;
struct zr_image home;
struct zr_image computer;
struct zr_image directory;
struct zr_image default_file;
struct zr_image text_file;
struct zr_image music_file;
struct zr_image font_file;
struct zr_image img_file;
struct zr_image movie_file;
};
enum theme {THEME_BLACK, THEME_WHITE, THEME_RED, THEME_BLUE, THEME_DARK};
@ -49,6 +65,7 @@ struct demo {
enum theme theme;
struct zr_memory_status status;
int show_filex;
int show_simple;
int show_replay;
int show_demo;
@ -186,8 +203,8 @@ set_style(struct zr_context *ctx, enum theme theme)
ctx->style.colors[ZR_COLOR_BUTTON_HOVER] = zr_rgba(142, 187, 229, 255);
ctx->style.colors[ZR_COLOR_BUTTON_ACTIVE] = zr_rgba(147, 192, 234, 255);
ctx->style.colors[ZR_COLOR_TOGGLE] = zr_rgba(177, 210, 210, 255);
ctx->style.colors[ZR_COLOR_TOGGLE_HOVER] = zr_rgba(245, 245, 245, 255);
ctx->style.colors[ZR_COLOR_TOGGLE_CURSOR] = zr_rgba(142, 187, 229, 255);
ctx->style.colors[ZR_COLOR_TOGGLE_HOVER] = zr_rgba(182, 215, 215, 255);
ctx->style.colors[ZR_COLOR_TOGGLE_CURSOR] = zr_rgba(137, 182, 224, 255);
ctx->style.colors[ZR_COLOR_SELECTABLE] = zr_rgba(147, 192, 234, 255);
ctx->style.colors[ZR_COLOR_SELECTABLE_HOVER] = zr_rgba(150, 150, 150, 255);
ctx->style.colors[ZR_COLOR_SELECTABLE_TEXT] = zr_rgba(70, 70, 70, 255);
@ -229,8 +246,8 @@ set_style(struct zr_context *ctx, enum theme theme)
ctx->style.colors[ZR_COLOR_HEADER] = zr_rgba(51, 51, 56, 220);
ctx->style.colors[ZR_COLOR_BORDER] = zr_rgba(46, 46, 46, 255);
ctx->style.colors[ZR_COLOR_BUTTON] = zr_rgba(48, 83, 111, 255);
ctx->style.colors[ZR_COLOR_BUTTON_HOVER] = zr_rgba(53, 88, 116, 255);
ctx->style.colors[ZR_COLOR_BUTTON_ACTIVE] = zr_rgba(58, 93, 121, 255);
ctx->style.colors[ZR_COLOR_BUTTON_HOVER] = zr_rgba(58, 93, 121, 255);
ctx->style.colors[ZR_COLOR_BUTTON_ACTIVE] = zr_rgba(63, 98, 126, 255);
ctx->style.colors[ZR_COLOR_TOGGLE] = zr_rgba(50, 58, 61, 255);
ctx->style.colors[ZR_COLOR_TOGGLE_HOVER] = zr_rgba(55, 63, 66, 255);
ctx->style.colors[ZR_COLOR_TOGGLE_CURSOR] = zr_rgba(48, 83, 111, 255);
@ -286,6 +303,7 @@ control_window(struct zr_context *ctx, struct demo *gui)
gui->show_node = !zr_window_is_closed(ctx, "Node Editor");
gui->show_demo = !zr_window_is_closed(ctx, "Demo");
#ifndef DEMO_DO_NOT_DRAW_IMAGES
gui->show_filex = !zr_window_is_closed(ctx, "File Browser");
gui->show_grid = !zr_window_is_closed(ctx, "Grid Demo");
gui->show_basic = !zr_window_is_closed(ctx, "Basic Demo");
gui->show_button = !zr_window_is_closed(ctx, "Button Demo");
@ -306,6 +324,8 @@ control_window(struct zr_context *ctx, struct demo *gui)
zr_window_close(ctx, "Basic Demo");
if (zr_checkbox(ctx, "Button", &gui->show_button) && !gui->show_button)
zr_window_close(ctx, "Button Demo");
if (zr_checkbox(ctx, "Filex", &gui->show_filex) && !gui->show_filex)
zr_window_close(ctx, "File Browser");
#endif
zr_layout_pop(ctx);
}
@ -704,7 +724,7 @@ demo_window(struct demo *gui, struct zr_context *ctx)
{
/* Combobox Widgets
* In this library comboboxes are not limited to being a popup
* list of selected text. Instead it is a abstract concept of
* list of selectable text. Instead it is a abstract concept of
* having something that is *selected* or displayed, a popup window
* which opens if something needs to be modified and the content
* of the popup which causes the *selected* or displayed value to
@ -729,16 +749,14 @@ demo_window(struct demo *gui, struct zr_context *ctx)
* which only show the currently activated time/data and hide the
* selection logic inside the combobox popup.
*/
enum color_mode {COL_RGB, COL_HSV};
static float chart_selection = 8.0f;
static const char *weapons[] = {"Fist","Pistol","Shotgun","Plasma","BFG"};
static size_t current_weapon = 0;
static int current_weapon = 0;
static int check_values[5];
static float position[3];
static int col_mode = COL_RGB;
static struct zr_color combo_color = {130, 50, 50, 255};
static struct zr_color combo_color2 = {130, 180, 50, 255};
static size_t prog_a = 20, prog_b = 40, prog_c = 10, prog_d = 90;
static const char *weapons[] = {"Fist","Pistol","Shotgun","Plasma","BFG"};
char buffer[64];
size_t sum = 0;
@ -746,15 +764,7 @@ demo_window(struct demo *gui, struct zr_context *ctx)
/* default combobox */
zr_layout_row_static(ctx, 25, 200, 1);
if (zr_combo_begin_text(ctx, &combo, weapons[current_weapon], 200)) {
size_t i = 0;
zr_layout_row_dynamic(ctx, 25, 1);
for (i = 0; i < LEN(weapons); ++i) {
if (zr_combo_item(ctx, weapons[i], ZR_TEXT_LEFT))
current_weapon = i;
}
zr_combo_end(ctx);
}
current_weapon = zr_combo(ctx, weapons, LEN(weapons), current_weapon, 25);
/* slider color combobox */
if (zr_combo_begin_color(ctx, &combo, combo_color, 200)) {
@ -773,6 +783,8 @@ demo_window(struct demo *gui, struct zr_context *ctx)
/* complex color combobox */
if (zr_combo_begin_color(ctx, &combo, combo_color2, 400)) {
enum color_mode {COL_RGB, COL_HSV};
static int col_mode = COL_RGB;
#ifndef DEMO_DO_NOT_USE_COLOR_PICKER
zr_layout_row_dynamic(ctx, 120, 1);
combo_color2 = zr_color_picker(ctx, combo_color2, ZR_RGBA);
@ -838,7 +850,7 @@ demo_window(struct demo *gui, struct zr_context *ctx)
sprintf(buffer, "%.1f", chart_selection);
if (zr_combo_begin_text(ctx, &combo, buffer, 250)) {
size_t i = 0;
static const float values[]={30.0f,15.0f,25.0f,10.0f,20.0f,40.0f};
static const float values[]={26.0f,13.0f,30.0f,15.0f,25.0f,10.0f,20.0f,40.0f, 12.0f, 8.0f, 22.0f, 28.0f, 5.0f};
zr_layout_row_dynamic(ctx, 150, 1);
zr_chart_begin(ctx, ZR_CHART_COLUMN, LEN(values), 0, 50);
for (i = 0; i < LEN(values); ++i) {
@ -1406,7 +1418,6 @@ demo_window(struct demo *gui, struct zr_context *ctx)
/* tiles */
zr_layout_row(ctx, ZR_STATIC, 200, 5, row_layout);
zr_push_property(ctx, ZR_PROPERTY_ITEM_SPACING, zr_vec2(0, 4));
/* left space */
if (zr_group_begin(ctx, &sub, "left", ZR_WINDOW_NO_SCROLLBAR|ZR_WINDOW_BORDER|ZR_WINDOW_NO_SCROLLBAR)) {
@ -1466,7 +1477,6 @@ demo_window(struct demo *gui, struct zr_context *ctx)
zr_group_end(ctx);
}
zr_pop_property(ctx);
zr_layout_pop(ctx);
}
@ -1882,7 +1892,7 @@ basic_demo(struct zr_context *ctx, struct icons *img)
*------------------------------------------------*/
ui_header(ctx, "Popup & Scrollbar & Images");
ui_widget(ctx, 35, 22);
if (zr_button_text_image(ctx, img->directory,
if (zr_button_text_image(ctx, img->dir,
"Images", ZR_TEXT_CENTERED, ZR_BUTTON_DEFAULT))
image_active = !image_active;
@ -1972,6 +1982,435 @@ basic_demo(struct zr_context *ctx, struct icons *img)
ctx->style.font.height = 14;
zr_end(ctx);
}
/* ===============================================================
*
* FILE BROWSER
*
* ===============================================================*/
/* sorry only works for posix systems right now because of the directory function */
enum file_groups {
FILE_GROUP_DEFAULT,
FILE_GROUP_TEXT,
FILE_GROUP_MUSIC,
FILE_GROUP_FONT,
FILE_GROUP_IMAGE,
FILE_GROUP_MOVIE,
FILE_GROUP_MAX
};
enum file_types {
FILE_DEFAULT,
FILE_TEXT,
FILE_C_SOURCE,
FILE_CPP_SOURCE,
FILE_HEADER,
FILE_CPP_HEADER,
FILE_MP3,
FILE_WAV,
FILE_OGG,
FILE_TTF,
FILE_BMP,
FILE_PNG,
FILE_JPEG,
FILE_PCX,
FILE_TGA,
FILE_GIF,
FILE_MAX
};
struct file_group {
enum file_groups group;
const char *name;
struct zr_image *icon;
};
struct file {
enum file_types type;
const char *suffix;
enum file_groups group;
};
struct media {
int font;
int icon_sheet;
struct icons *icons;
struct file_group group[FILE_GROUP_MAX];
struct file files[FILE_MAX];
};
#define MAX_PATH_LEN 512
struct file_browser {
/* path */
char file[MAX_PATH_LEN];
char home[MAX_PATH_LEN];
char desktop[MAX_PATH_LEN];
char directory[MAX_PATH_LEN];
/* directory content */
char **files;
char **directories;
size_t file_count;
size_t dir_count;
struct media media;
};
#ifndef DEMO_DO_NOT_DRAW_IMAGES
#ifdef __unix__
#include <dirent.h>
#include <unistd.h>
#ifndef _WIN32
# include <pwd.h>
#endif
static char*
str_duplicate(const char *src)
{
char *ret;
size_t len = strlen(src);
if (!len) return 0;
ret = malloc(len+1);
if (!ret) return 0;
memcpy(ret, src, len);
ret[len] = '\0';
return ret;
}
static void
dir_free_list(char **list, size_t size)
{
size_t i;
for (i = 0; i < size; ++i)
free(list[i]);
free(list);
}
static char**
dir_list(const char *dir, int return_subdirs, size_t *count)
{
size_t n = 0;
char buffer[MAX_PATH_LEN];
char **results = NULL;
const DIR *none = NULL;
size_t capacity = 32;
size_t size;
DIR *z;
assert(dir);
assert(count);
strncpy(buffer, dir, MAX_PATH_LEN);
n = strlen(buffer);
if (n > 0 && (buffer[n-1] != '/'))
buffer[n++] = '/';
size = 0;
z = opendir(dir);
if (z != none) {
int nonempty = 1;
struct dirent *data = readdir(z);
nonempty = (data != NULL);
if (!nonempty) return NULL;
do {
DIR *y;
char *p;
int is_subdir;
if (data->d_name[0] == '.')
continue;
strncpy(buffer + n, data->d_name, MAX_PATH_LEN-n);
y = opendir(buffer);
is_subdir = (y != NULL);
if (y != NULL) closedir(y);
if ((return_subdirs && is_subdir) || (!is_subdir && !return_subdirs)){
if (!size) {
results = calloc(sizeof(char*), capacity);
} else if (size >= capacity) {
capacity = capacity * 2;
results = realloc(results, capacity * sizeof(char*));
}
p = str_duplicate(data->d_name);
results[size++] = p;
}
} while ((data = readdir(z)) != NULL);
}
if (z) closedir(z);
*count = size;
return results;
}
static struct file_group
FILE_GROUP(enum file_groups group, const char *name, struct zr_image *icon)
{
struct file_group fg;
fg.group = group;
fg.name = name;
fg.icon = icon;
return fg;
}
static struct file
FILE_DEF(enum file_types type, const char *suffix, enum file_groups group)
{
struct file fd;
fd.type = type;
fd.suffix = suffix;
fd.group = group;
return fd;
}
static struct zr_image*
media_icon_for_file(struct media *media, const char *file)
{
int i = 0;
const char *s = file;
char suffix[4];
int found = 0;
memset(suffix, 0, sizeof(suffix));
/* extract suffix .xxx from file */
while (*s++ != '\0') {
if (found && i < 3)
suffix[i++] = *s;
if (*s == '.') {
if (found){
found = 0;
break;
}
found = 1;
}
}
/* check for all file definition of all groups for fitting suffix*/
for (i = 0; i < FILE_MAX && found; ++i) {
struct file *d = &media->files[i];
{
const char *f = d->suffix;
s = suffix;
while (f && *f && *s && *s == *f) {
s++; f++;
}
/* found correct file definition so */
if (f && *s == '\0' && *f == '\0')
return media->group[d->group].icon;
}
}
return &media->icons->default_file;
}
static void
media_init(struct media *media, struct icons *icons)
{
/* file groups */
media->icons = icons;
media->group[FILE_GROUP_DEFAULT] = FILE_GROUP(FILE_GROUP_DEFAULT,"default",&icons->default_file);
media->group[FILE_GROUP_TEXT] = FILE_GROUP(FILE_GROUP_TEXT, "textual", &icons->text_file);
media->group[FILE_GROUP_MUSIC] = FILE_GROUP(FILE_GROUP_MUSIC, "music", &icons->music_file);
media->group[FILE_GROUP_FONT] = FILE_GROUP(FILE_GROUP_FONT, "font", &icons->font_file);
media->group[FILE_GROUP_IMAGE] = FILE_GROUP(FILE_GROUP_IMAGE, "image", &icons->img_file);
media->group[FILE_GROUP_MOVIE] = FILE_GROUP(FILE_GROUP_MOVIE, "movie", &icons->movie_file);
/* files */
media->files[FILE_DEFAULT] = FILE_DEF(FILE_DEFAULT, NULL, FILE_GROUP_DEFAULT);
media->files[FILE_TEXT] = FILE_DEF(FILE_TEXT, "txt", FILE_GROUP_TEXT);
media->files[FILE_C_SOURCE] = FILE_DEF(FILE_C_SOURCE, "c", FILE_GROUP_TEXT);
media->files[FILE_CPP_SOURCE] = FILE_DEF(FILE_CPP_SOURCE, "cpp", FILE_GROUP_TEXT);
media->files[FILE_HEADER] = FILE_DEF(FILE_HEADER, "h", FILE_GROUP_TEXT);
media->files[FILE_CPP_HEADER] = FILE_DEF(FILE_HEADER, "hpp", FILE_GROUP_TEXT);
media->files[FILE_MP3] = FILE_DEF(FILE_MP3, "mp3", FILE_GROUP_MUSIC);
media->files[FILE_WAV] = FILE_DEF(FILE_WAV, "wav", FILE_GROUP_MUSIC);
media->files[FILE_OGG] = FILE_DEF(FILE_OGG, "ogg", FILE_GROUP_MUSIC);
media->files[FILE_TTF] = FILE_DEF(FILE_TTF, "ttf", FILE_GROUP_FONT);
media->files[FILE_BMP] = FILE_DEF(FILE_BMP, "bmp", FILE_GROUP_IMAGE);
media->files[FILE_PNG] = FILE_DEF(FILE_PNG, "png", FILE_GROUP_IMAGE);
media->files[FILE_JPEG] = FILE_DEF(FILE_JPEG, "jpg", FILE_GROUP_IMAGE);
media->files[FILE_PCX] = FILE_DEF(FILE_PCX, "pcx", FILE_GROUP_IMAGE);
media->files[FILE_TGA] = FILE_DEF(FILE_TGA, "tga", FILE_GROUP_IMAGE);
media->files[FILE_GIF] = FILE_DEF(FILE_GIF, "gif", FILE_GROUP_IMAGE);
}
static void
file_browser_reload_directory_content(struct file_browser *browser, const char *path)
{
strncpy(browser->directory, path, MAX_PATH_LEN);
dir_free_list(browser->files, browser->file_count);
dir_free_list(browser->directories, browser->dir_count);
browser->files = dir_list(path, 0, &browser->file_count);
browser->directories = dir_list(path, 1, &browser->dir_count);
}
static void
file_browser_init(struct file_browser *browser, struct icons *icons)
{
memset(browser, 0, sizeof(*browser));
media_init(&browser->media, icons);
{
/* load files and sub-directory list */
const char *home = getenv("HOME");
#ifdef _WIN32
if (!home) home = getenv("USERPROFILE");
#else
if (!home) home = getpwuid(getuid())->pw_dir;
{
size_t l;
strncpy(browser->home, home, MAX_PATH_LEN);
l = strlen(browser->home);
strcpy(browser->home + l, "/");
strcpy(browser->directory, browser->home);
}
#endif
{
size_t l;
strcpy(browser->desktop, browser->home);
l = strlen(browser->desktop);
strcpy(browser->desktop + l, "desktop/");
}
browser->files = dir_list(browser->directory, 0, &browser->file_count);
browser->directories = dir_list(browser->directory, 1, &browser->dir_count);
}
}
static void
file_browser_free(struct file_browser *browser)
{
if (browser->files)
dir_free_list(browser->files, browser->file_count);
if (browser->directories)
dir_free_list(browser->directories, browser->dir_count);
browser->files = NULL;
browser->directories = NULL;
memset(browser, 0, sizeof(*browser));
}
static int
file_browser_run(struct file_browser *browser, struct zr_context *ctx)
{
int ret = 0;
struct zr_panel layout;
struct media *media = &browser->media;
struct icons *icons = media->icons;
struct zr_rect total_space;
if (zr_begin(ctx, &layout, "File Browser", zr_rect(50, 50, 800, 600),
ZR_WINDOW_BORDER|ZR_WINDOW_NO_SCROLLBAR|ZR_WINDOW_CLOSABLE|ZR_WINDOW_MOVABLE))
{
struct zr_panel sub;
static float ratio[] = {0.25f, ZR_UNDEFINED};
/* output path directory selector in the menubar */
zr_menubar_begin(ctx);
{
char *d = browser->directory;
char *begin = d + 1;
zr_layout_row_dynamic(ctx, 25, 6);
zr_push_property(ctx, ZR_PROPERTY_ITEM_SPACING, zr_vec2(0, 0));
while (*d++) {
if (*d == '/') {
*d = '\0';
if (zr_button_text(ctx, begin, ZR_BUTTON_DEFAULT)) {
*d++ = '/'; *d = '\0';
file_browser_reload_directory_content(browser, browser->directory);
break;
}
*d = '/';
begin = d + 1;
}
}
zr_pop_property(ctx);
}
zr_menubar_end(ctx);
/* window layout */
total_space = zr_window_get_content_region(ctx);
zr_layout_row(ctx, ZR_DYNAMIC, total_space.h, 2, ratio);
zr_group_begin(ctx, &sub, "Special", ZR_WINDOW_NO_SCROLLBAR);
{
struct zr_image home = icons->home;
struct zr_image desktop = icons->desktop;
struct zr_image computer = icons->computer;
zr_layout_row_dynamic(ctx, 40, 1);
zr_push_property(ctx, ZR_PROPERTY_ITEM_SPACING, zr_vec2(0, 0));
if (zr_button_text_image(ctx, home, "home", ZR_TEXT_CENTERED, ZR_BUTTON_DEFAULT))
file_browser_reload_directory_content(browser, browser->home);
if (zr_button_text_image(ctx,desktop,"desktop",ZR_TEXT_CENTERED, ZR_BUTTON_DEFAULT))
file_browser_reload_directory_content(browser, browser->desktop);
if (zr_button_text_image(ctx,computer,"computer",ZR_TEXT_CENTERED,ZR_BUTTON_DEFAULT))
file_browser_reload_directory_content(browser, "/");
zr_pop_property(ctx);
zr_group_end(ctx);
}
/* output directory content window */
zr_group_begin(ctx, &sub, "Content", 0);
{
int index = -1;
size_t i = 0, j = 0, k = 0;
size_t rows = 0, cols = 0;
size_t count = browser->dir_count + browser->file_count;
cols = 4;
rows = count / cols;
for (i = 0; i <= rows; i += 1) {
{size_t n = j + cols;
zr_layout_row_dynamic(ctx, 135, (int)cols);
for (; j < count && j < n; ++j) {
/* draw one row of icons */
if (j < browser->dir_count) {
/* draw and execute directory buttons */
if (zr_button_image(ctx,icons->directory,ZR_BUTTON_DEFAULT))
index = (int)j;
} else {
/* draw and execute files buttons */
struct zr_image *icon;
size_t fileIndex = ((size_t)j - browser->dir_count);
icon = media_icon_for_file(media,browser->files[fileIndex]);
if (zr_button_image(ctx, *icon, ZR_BUTTON_DEFAULT)) {
strncpy(browser->file, browser->directory, MAX_PATH_LEN);
n = strlen(browser->file);
strncpy(browser->file + n, browser->files[fileIndex], MAX_PATH_LEN - n);
ret = 1;
}
}
}}
{size_t n = k + cols;
zr_layout_row_dynamic(ctx, 20, (int)cols);
for (; k < count && k < n; k++) {
/* draw one row of labels */
if (k < browser->dir_count) {
zr_label(ctx, browser->directories[k], ZR_TEXT_CENTERED);
} else {
size_t t = k-browser->dir_count;
zr_label(ctx,browser->files[t],ZR_TEXT_CENTERED);
}
}}
}
if (index != -1) {
size_t n = strlen(browser->directory);
strncpy(browser->directory + n, browser->directories[index], MAX_PATH_LEN - n);
n = strlen(browser->directory);
if (n < MAX_PATH_LEN - 1) {
browser->directory[n] = '/';
browser->directory[n+1] = '\0';
}
file_browser_reload_directory_content(browser, browser->directory);
}
zr_group_end(ctx);
}
}
zr_end(ctx);
return ret;
}
#endif
#endif
/* ===============================================================
*
@ -2078,8 +2517,8 @@ node_editor_add(struct node_editor *editor, const char *name, struct zr_rect bou
node->input_count = in_count;
node->output_count = out_count;
node->color = col;
strcpy(node->name, name);
node->bounds = bounds;
strcpy(node->name, name);
node_editor_push(editor, node);
}
@ -2106,6 +2545,18 @@ node_editor_demo(struct zr_context *ctx, struct node_editor *nodedit)
struct node *updated = 0;
struct zr_panel layout;
/* This is a simple node editor just to show a simple implementation and that
* it is possible to achieve with this library. While all nodes inside this
* example use a simple color modifier as content you could change them
* to have your custom content depending on the node time.
* Biggest difference to most usual implementation is that this example does
* not has connectors on the right position of the proprety that it links.
* This is mainly done out of lazyness and could be implemented as well but
* requires calculating the position of all rows and add connectors.
* In addition adding and removing nodes is quite limited at the
* moment since it is based on a simple array. If this is to be converted
* into something more serious it is probably best to extend it.*/
if (zr_begin(ctx, &layout, "Node Editor", zr_rect(50, 50, 650, 650),
ZR_WINDOW_BORDER|ZR_WINDOW_NO_SCROLLBAR|ZR_WINDOW_CLOSABLE|ZR_WINDOW_MOVABLE))
{
@ -2307,6 +2758,7 @@ node_editor_init(struct node_editor *editor)
static void
record_window(struct zr_context *ctx, struct zr_buffer *buffer)
{
/* **EXPERIMENTAL** */
/* Recording a UI begins by calling `zr_recording_begin` and ends with
* `zr_recording_end`. All supported API call between these two calls
* are saved inside the buffer and can later be replayed with `zr_exec`.
@ -2341,6 +2793,73 @@ record_window(struct zr_context *ctx, struct zr_buffer *buffer)
zr_recording_end(ctx);
}
/* ===============================================================
*
* COMPILED WINDOW
*
* ===============================================================*/
static void
compile_log(void *userdata, zr_size line, const char *fmt, ...)
{
int l = (int)line;
char buffer[1024];
va_list args;
va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args);
buffer[1023] = 0;
fprintf(stderr, "[COMPILER] error (%d): %s\n", l, buffer);
va_end(args);
}
static void
compile_window(struct zr_context *ctx, struct zr_buffer *buffer)
{
/* **EXPERIMENTAL** */
const char script[] =
"begin('Recorded', 420, 350, 200, 350, 317)"
"layout_row_static(30, 150, 1)"
"button_text(0, 'Test', 0)"
"slider(1, 0, 5, 10, 1)"
"progress(2, 20, 100, 1)"
"property(3, 'Compression:', 0, 20, 100, 10, 1, 0);"
"select(4, 'select me', 18, 0)"
"check(5, 'check me', 1)"
"layout_row_static(30, 50, 3)"
"option(6, 'RGB', 1)"
"option(8, 'HSV', 0)"
"option(9, 'HEX', 0)"
"layout_push(10, 1, 'Tab', 0)"
"layout_row_static(30, 150, 1)"
"button_text(11, 'Test', 0)"
"layout_pop()"
"end()";
enum zr_compiler_status ret;
UNUSED(ctx);
ret = zr_compile(buffer, script, sizeof(script), compile_log, 0);
if (ret == ZR_COMPILER_OK) return;
else if (ret == ZR_COMPILER_INVALID_VALUE)
fprintf(stdout, "[Zahnrad]: compiling error: invalid valid\n");
else if (ret == ZR_COMPILER_INVALID_ARG)
fprintf(stdout, "[Zahnrad]: compiling error: invalid operation argument\n");
else if (ret == ZR_COMPILER_OP_NOT_FOUND)
fprintf(stdout, "[Zahnrad]: compiling error: invalid operation\n");
else if (ret == ZR_COMPILER_MISSING_COMMA)
fprintf(stdout, "[Zahnrad]: compiling error: missing comma between arguments\n");
else if (ret == ZR_COMPILER_WRONG_ARG_TYPE)
fprintf(stdout, "[Zahnrad]: compiling error: argument has wrong type\n");
else if (ret == ZR_COMPILER_NO_MEMORY)
fprintf(stdout, "[Zahnrad]: out of memory\n");
else if (ret == ZR_COMPILER_INVALID_SCRIPT)
fprintf(stdout, "[Zahnrad]: script is not correct\n");
}
/* ===============================================================
*
* REPLAY WINDOW
*
* ===============================================================*/
static void
replay_window(struct zr_context *ctx, struct zr_buffer *record)
{
@ -2385,8 +2904,8 @@ replay_window(struct zr_context *ctx, struct zr_buffer *record)
ctx->next_id = 0;
{const union zr_event *evt;
/* To execute a previously recorded UI you need to provide two buffer. The
* first one is to store events into and the other is for runtime memory.
/* To execute a previously recorded or compile UI you need to provide two buffer.
* The first one is to store events into and the other is for runtime memory.
* For event memory I would recommend using a fixed size `zr_event` array
* since the number of events generated is small most of the time. Runtime
* memory is only used for `zr_panel`s so only the max number of panels
@ -2399,7 +2918,7 @@ replay_window(struct zr_context *ctx, struct zr_buffer *record)
* bring a huge performance boost. (NOTICE: some functions like zr_begin generate
* heartbeat events which do not have any impact and are only used while using
* the API in immediate mode and can easily be ignored). You can also use
* an event mask to only specify which events should be generated and ignore
* an event mask to specify which events should be generated and ignore
* all other. This example here uses a default event mask by passing `0` to
* zr_exec which allows all events, but you could also create your own:
*
@ -2527,6 +3046,7 @@ run_demo(struct demo *gui)
static char record_memory[4*1024];
static struct zr_buffer record;
static struct node_editor nodedit;
static struct file_browser filex;
struct zr_context *ctx = &gui->ctx;
if (!init) {
@ -2535,16 +3055,18 @@ run_demo(struct demo *gui)
gui->show_replay = 0;
gui->show_simple = 0;
#ifndef DEMO_DO_NOT_DRAW_IMAGES
gui->show_grid = 0;
gui->show_basic = 0;
gui->show_button = 0;
#endif
memset(&nodedit, 0, sizeof(nodedit));
zr_buffer_init_fixed(&record, record_memory, sizeof(record_memory));
record_window(ctx, &record);
compile_window(ctx, &record);
/*record_window(ctx, &record);*/
node_editor_init(&nodedit);
#ifndef DEMO_DO_NOT_DRAW_IMAGES
file_browser_init(&filex, &gui->icons);
#endif
init = 1;
}
@ -2559,12 +3081,15 @@ run_demo(struct demo *gui)
node_editor_demo(ctx, &nodedit);
#ifndef DEMO_DO_NOT_DRAW_IMAGES
if (gui->show_filex)
file_browser_run(&filex, ctx);
if (gui->show_grid)
grid_demo(ctx);
if (gui->show_button)
button_demo(ctx, &gui->icons);
if (gui->show_basic)
basic_demo(ctx, &gui->icons);
if (!ret) file_browser_free(&filex);
#endif
zr_buffer_info(&gui->status, &gui->ctx.memory);
return ret;

View File

@ -580,7 +580,7 @@ main(int argc, char *argv[])
gui.icons.next = icon_load("../../icon/next.png");
gui.icons.prev = icon_load("../../icon/prev.png");
gui.icons.tools = icon_load("../../icon/tools.png");
gui.icons.directory = icon_load("../../icon/directory.png");
gui.icons.dir = icon_load("../../icon/directory.png");
gui.icons.copy = icon_load("../../icon/copy.png");
gui.icons.convert = icon_load("../../icon/export.png");
gui.icons.del = icon_load("../../icon/delete.png");
@ -591,6 +591,18 @@ main(int argc, char *argv[])
gui.icons.menu[3] = icon_load("../../icon/wifi.png");
gui.icons.menu[4] = icon_load("../../icon/settings.png");
gui.icons.menu[5] = icon_load("../../icon/volume.png");
gui.icons.home = icon_load("../../icon/home.png");
gui.icons.directory = icon_load("../../icon/directory.png");
gui.icons.computer = icon_load("../../icon/computer.png");
gui.icons.desktop = icon_load("../../icon/desktop.png");
gui.icons.default_file = icon_load("../../icon/default.png");
gui.icons.text_file = icon_load("../../icon/text.png");
gui.icons.music_file = icon_load("../../icon/music.png");
gui.icons.font_file = icon_load("../../icon/font.png");
gui.icons.img_file = icon_load("../../icon/img.png");
gui.icons.movie_file = icon_load("../../icon/movie.png");
for (i = 0; i < 9; ++i) {
char buffer[256];
sprintf(buffer, "../../images/image%d.png", (i+1));
@ -627,8 +639,20 @@ main(int argc, char *argv[])
glDeleteTextures(1,(const GLuint*)&gui.icons.next.handle.id);
glDeleteTextures(1,(const GLuint*)&gui.icons.prev.handle.id);
glDeleteTextures(1,(const GLuint*)&gui.icons.tools.handle.id);
glDeleteTextures(1,(const GLuint*)&gui.icons.directory.handle.id);
glDeleteTextures(1,(const GLuint*)&gui.icons.dir.handle.id);
glDeleteTextures(1,(const GLuint*)&gui.icons.del.handle.id);
glDeleteTextures(1,(const GLuint*)&gui.icons.home.handle.id);
glDeleteTextures(1,(const GLuint*)&gui.icons.directory.handle.id);
glDeleteTextures(1,(const GLuint*)&gui.icons.computer.handle.id);
glDeleteTextures(1,(const GLuint*)&gui.icons.desktop.handle.id);
glDeleteTextures(1,(const GLuint*)&gui.icons.default_file.handle.id);
glDeleteTextures(1,(const GLuint*)&gui.icons.text_file.handle.id);
glDeleteTextures(1,(const GLuint*)&gui.icons.music_file.handle.id);
glDeleteTextures(1,(const GLuint*)&gui.icons.font_file.handle.id);
glDeleteTextures(1,(const GLuint*)&gui.icons.img_file.handle.id);
glDeleteTextures(1,(const GLuint*)&gui.icons.movie_file.handle.id);
for (i = 0; i < 9; ++i)
glDeleteTextures(1, (const GLuint*)&gui.icons.images[i].handle.id);
for (i = 0; i < 6; ++i)

BIN
demo/icon/computer.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 620 B

BIN
demo/icon/default.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 B

BIN
demo/icon/desktop.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 583 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 533 B

BIN
demo/icon/font.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 561 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 819 B

BIN
demo/icon/img.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 648 B

BIN
demo/icon/movie.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 626 B

BIN
demo/icon/music.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 610 B

BIN
demo/icon/text.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 601 B

View File

@ -943,7 +943,7 @@ int main(int argc, char **argv)
gui.icons.next = icon_load("../../icon/next.png");
gui.icons.prev = icon_load("../../icon/prev.png");
gui.icons.tools = icon_load("../../icon/tools.png");
gui.icons.directory = icon_load("../../icon/directory.png");
gui.icons.dir = icon_load("../../icon/directory.png");
gui.icons.copy = icon_load("../../icon/copy.png");
gui.icons.convert = icon_load("../../icon/export.png");
gui.icons.del = icon_load("../../icon/delete.png");
@ -954,6 +954,18 @@ int main(int argc, char **argv)
gui.icons.menu[3] = icon_load("../../icon/wifi.png");
gui.icons.menu[4] = icon_load("../../icon/settings.png");
gui.icons.menu[5] = icon_load("../../icon/volume.png");
gui.icons.home = icon_load("../../icon/home.png");
gui.icons.directory = icon_load("../../icon/directory.png");
gui.icons.computer = icon_load("../../icon/computer.png");
gui.icons.desktop = icon_load("../../icon/desktop.png");
gui.icons.default_file = icon_load("../../icon/default.png");
gui.icons.text_file = icon_load("../../icon/text.png");
gui.icons.music_file = icon_load("../../icon/music.png");
gui.icons.font_file = icon_load("../../icon/font.png");
gui.icons.img_file = icon_load("../../icon/img.png");
gui.icons.movie_file = icon_load("../../icon/movie.png");
for (i = 0; i < 9; ++i) {
char buffer[256];
sprintf(buffer, "../../images/image%d.png", (i+1));
@ -1012,8 +1024,20 @@ cleanup:
glDeleteTextures(1,(const GLuint*)&gui.icons.next.handle.id);
glDeleteTextures(1,(const GLuint*)&gui.icons.prev.handle.id);
glDeleteTextures(1,(const GLuint*)&gui.icons.tools.handle.id);
glDeleteTextures(1,(const GLuint*)&gui.icons.directory.handle.id);
glDeleteTextures(1,(const GLuint*)&gui.icons.dir.handle.id);
glDeleteTextures(1,(const GLuint*)&gui.icons.del.handle.id);
glDeleteTextures(1,(const GLuint*)&gui.icons.home.handle.id);
glDeleteTextures(1,(const GLuint*)&gui.icons.directory.handle.id);
glDeleteTextures(1,(const GLuint*)&gui.icons.computer.handle.id);
glDeleteTextures(1,(const GLuint*)&gui.icons.desktop.handle.id);
glDeleteTextures(1,(const GLuint*)&gui.icons.default_file.handle.id);
glDeleteTextures(1,(const GLuint*)&gui.icons.text_file.handle.id);
glDeleteTextures(1,(const GLuint*)&gui.icons.music_file.handle.id);
glDeleteTextures(1,(const GLuint*)&gui.icons.font_file.handle.id);
glDeleteTextures(1,(const GLuint*)&gui.icons.img_file.handle.id);
glDeleteTextures(1,(const GLuint*)&gui.icons.movie_file.handle.id);
for (i = 0; i < 9; ++i)
glDeleteTextures(1, (const GLuint*)&gui.icons.images[i].handle.id);
for (i = 0; i < 6; ++i)

View File

@ -555,7 +555,7 @@ main(int argc, char *argv[])
gui.icons.next = icon_load("../../icon/next.png");
gui.icons.prev = icon_load("../../icon/prev.png");
gui.icons.tools = icon_load("../../icon/tools.png");
gui.icons.directory = icon_load("../../icon/directory.png");
gui.icons.dir = icon_load("../../icon/directory.png");
gui.icons.copy = icon_load("../../icon/copy.png");
gui.icons.convert = icon_load("../../icon/export.png");
gui.icons.del = icon_load("../../icon/delete.png");
@ -566,6 +566,18 @@ main(int argc, char *argv[])
gui.icons.menu[3] = icon_load("../../icon/wifi.png");
gui.icons.menu[4] = icon_load("../../icon/settings.png");
gui.icons.menu[5] = icon_load("../../icon/volume.png");
gui.icons.home = icon_load("../../icon/home.png");
gui.icons.directory = icon_load("../../icon/directory.png");
gui.icons.computer = icon_load("../../icon/computer.png");
gui.icons.desktop = icon_load("../../icon/desktop.png");
gui.icons.default_file = icon_load("../../icon/default.png");
gui.icons.text_file = icon_load("../../icon/text.png");
gui.icons.music_file = icon_load("../../icon/music.png");
gui.icons.font_file = icon_load("../../icon/font.png");
gui.icons.img_file = icon_load("../../icon/img.png");
gui.icons.movie_file = icon_load("../../icon/movie.png");
for (i = 0; i < 9; ++i) {
char buffer[256];
sprintf(buffer, "../../images/image%d.png", (i+1));
@ -620,8 +632,20 @@ cleanup:
glDeleteTextures(1,(const GLuint*)&gui.icons.next.handle.id);
glDeleteTextures(1,(const GLuint*)&gui.icons.prev.handle.id);
glDeleteTextures(1,(const GLuint*)&gui.icons.tools.handle.id);
glDeleteTextures(1,(const GLuint*)&gui.icons.directory.handle.id);
glDeleteTextures(1,(const GLuint*)&gui.icons.dir.handle.id);
glDeleteTextures(1,(const GLuint*)&gui.icons.del.handle.id);
glDeleteTextures(1,(const GLuint*)&gui.icons.home.handle.id);
glDeleteTextures(1,(const GLuint*)&gui.icons.directory.handle.id);
glDeleteTextures(1,(const GLuint*)&gui.icons.computer.handle.id);
glDeleteTextures(1,(const GLuint*)&gui.icons.desktop.handle.id);
glDeleteTextures(1,(const GLuint*)&gui.icons.default_file.handle.id);
glDeleteTextures(1,(const GLuint*)&gui.icons.text_file.handle.id);
glDeleteTextures(1,(const GLuint*)&gui.icons.music_file.handle.id);
glDeleteTextures(1,(const GLuint*)&gui.icons.font_file.handle.id);
glDeleteTextures(1,(const GLuint*)&gui.icons.img_file.handle.id);
glDeleteTextures(1,(const GLuint*)&gui.icons.movie_file.handle.id);
for (i = 0; i < 9; ++i)
glDeleteTextures(1, (const GLuint*)&gui.icons.images[i].handle.id);
for (i = 0; i < 6; ++i)

789
zahnrad.c
View File

@ -223,10 +223,9 @@ enum zr_param_types {
ZR_TYPE_UINT = 'u',
ZR_TYPE_FLOAT = 'f',
ZR_TYPE_HASH = 'h',
ZR_TYPE_OFFSET = 's',
ZR_TYPE_FLAGS = 'g',
ZR_TYPE_IMAGE = 'm',
ZR_TYPE_CHEAT = 'c',
ZR_TYPE_IMAGE = 'm',
ZR_TYPE_COLOR = 'l',
ZR_TYPE_PTR = 'p'
};
@ -254,7 +253,7 @@ enum zr_param_types {
ZR_OP(layout_space_begin, ZR_STACK_NOP, ZR_FMT("oifi"))\
ZR_OP(layout_space_push, ZR_STACK_NOP, ZR_FMT("of"))\
ZR_OP(layout_space_end, ZR_STACK_NOP, ZR_FMT("o"))\
ZR_OP(layout_push, ZR_STACK_INC|ZR_STACK_GEN, ZR_FMT("ohici"))\
ZR_OP(layout_push, ZR_STACK_INC|ZR_STACK_GEN|ZR_STACK_TXT, ZR_FMT("ohici"))\
ZR_OP(layout_pop, ZR_STACK_DEC|ZR_STACK_TXT, ZR_FMT("o"))\
ZR_OP(text, ZR_STACK_NOP|ZR_STACK_TXT, ZR_FMT("ocugl"))\
ZR_OP(text_wrap, ZR_STACK_NOP|ZR_STACK_TXT, ZR_FMT("ocul"))\
@ -265,9 +264,9 @@ enum zr_param_types {
ZR_OP(button_image, ZR_STACK_GEN, ZR_FMT("ohmffffffi"))\
ZR_OP(button_text_symbol, ZR_STACK_GEN|ZR_STACK_TXT, ZR_FMT("ohici"))\
ZR_OP(button_text_image, ZR_STACK_GEN|ZR_STACK_TXT, ZR_FMT("ohmffffffcgi"))\
ZR_OP(check, ZR_STACK_GEN, ZR_FMT("ohci"))\
ZR_OP(option, ZR_STACK_GEN, ZR_FMT("ohci"))\
ZR_OP(select, ZR_STACK_GEN, ZR_FMT("ohcfi"))\
ZR_OP(check, ZR_STACK_GEN|ZR_STACK_TXT, ZR_FMT("ohci"))\
ZR_OP(option, ZR_STACK_GEN|ZR_STACK_TXT, ZR_FMT("ohci"))\
ZR_OP(select, ZR_STACK_GEN|ZR_STACK_TXT, ZR_FMT("ohcgi"))\
ZR_OP(slider, ZR_STACK_GEN, ZR_FMT("ohffff"))\
ZR_OP(progress, ZR_STACK_GEN, ZR_FMT("ohiii"))\
ZR_OP(property, ZR_STACK_GEN|ZR_STACK_TXT, ZR_FMT("ohcfffffi"))\
@ -323,6 +322,7 @@ struct zr_event_queue {
#define zr_ptr_add(t, p, i) ((t*)((void*)((zr_byte*)(p) + (i))))
#define zr_ptr_add_const(t, p, i) ((const t*)((const void*)((const zr_byte*)(p) + (i))))
#define zr_zero_struct(s) zr_zero(&s, sizeof(s))
/* ==============================================================
* ALIGNMENT
@ -673,8 +673,6 @@ zr_memset(void *ptr, int c0, zr_size size)
#undef wmask
}
#define zr_zero_struct(s) zr_zero(&s, sizeof(s))
static void
zr_zero(void *ptr, zr_size size)
{
@ -691,6 +689,16 @@ zr_strsiz(const char *str)
return siz;
}
static int
zr_stricmpn(const char *a, const char *b, int len)
{
int i = 0;
for (i = 0; i < len && a[i] && b[i]; ++i)
if (a[i] != b[i]) return 1;
if (i != len) return 1;
return 0;
}
static int
zr_strtof(float *number, const char *buffer)
{
@ -12426,6 +12434,148 @@ void zr_combo_end(struct zr_context *ctx)
void zr_combo_close(struct zr_context *ctx)
{zr_contextual_close(ctx);}
int
zr_combo(struct zr_context *ctx, const char **items, int count,
int selected, int item_height)
{
int i = 0;
int max_height;
struct zr_panel combo;
struct zr_vec2 item_padding;
struct zr_vec2 window_padding;
ZR_ASSERT(ctx);
ZR_ASSERT(items);
if (!ctx || !items ||!count)
return selected;
item_padding = zr_get_property(ctx, ZR_PROPERTY_ITEM_PADDING);
window_padding = zr_get_property(ctx, ZR_PROPERTY_PADDING);
max_height = (count+1) * item_height + (int)item_padding.y * 3 + (int)window_padding.y * 2;
if (zr_combo_begin_text(ctx, &combo, items[selected], max_height)) {
zr_layout_row_dynamic(ctx, (float)item_height, 1);
for (i = 0; i < count; ++i) {
if (zr_combo_item(ctx, items[i], ZR_TEXT_LEFT))
selected = i;
}
zr_combo_end(ctx);
}
return selected;
}
int
zr_combo_string(struct zr_context *ctx, const char *items_seperated_by_zeros,
int selected, int count, int item_height)
{
int i;
int max_height;
struct zr_panel combo;
struct zr_vec2 item_padding;
struct zr_vec2 window_padding;
const char *current_item;
zr_size length;
ZR_ASSERT(ctx);
ZR_ASSERT(items_seperated_by_zeros);
if (!ctx || !items_seperated_by_zeros)
return selected;
/* calculate popup window */
item_padding = zr_get_property(ctx, ZR_PROPERTY_ITEM_PADDING);
window_padding = zr_get_property(ctx, ZR_PROPERTY_PADDING);
max_height = (count+1) * item_height + (int)item_padding.y * 3 + (int)window_padding.y * 2;
/* find selected item */
current_item = items_seperated_by_zeros;
length = zr_strsiz(current_item);
for (i = 0; i < selected; ++i) {
current_item = current_item + length + 1;
length = zr_strsiz(current_item);
}
current_item = items_seperated_by_zeros;
if (zr_combo_begin_text(ctx, &combo, current_item, max_height)) {
zr_layout_row_dynamic(ctx, (float)item_height, 1);
for (i = 0; i < count; ++i) {
if (zr_combo_item(ctx, current_item, ZR_TEXT_LEFT))
selected = i;
length = zr_strsiz(current_item);
current_item = current_item + length + 1;
}
zr_combo_end(ctx);
}
return selected;
}
int
zr_combo_callback(struct zr_context *ctx, void(item_getter)(void*, int, const char**),
void *userdata, int selected, int count, int item_height)
{
int i;
int max_height;
struct zr_panel combo;
struct zr_vec2 item_padding;
struct zr_vec2 window_padding;
const char *item;
ZR_ASSERT(ctx);
ZR_ASSERT(item_getter);
if (!ctx || !item_getter)
return selected;
item_padding = zr_get_property(ctx, ZR_PROPERTY_ITEM_PADDING);
window_padding = zr_get_property(ctx, ZR_PROPERTY_PADDING);
max_height = (count+1) * item_height + (int)item_padding.y * 3 + (int)window_padding.y * 2;
item_getter(userdata, selected, &item);
if (zr_combo_begin_text(ctx, &combo, item, max_height)) {
zr_layout_row_dynamic(ctx, (float)item_height, 1);
for (i = 0; i < count; ++i) {
item_getter(userdata, i, &item);
if (zr_combo_item(ctx, item, ZR_TEXT_LEFT))
selected = i;
}
zr_combo_end(ctx);
}
return selected;
}
void
zr_combobox(struct zr_context *ctx, const char **items, int count, int *selected,
int item_height)
{
ZR_ASSERT(ctx);
ZR_ASSERT(items);
ZR_ASSERT(selected);
if (!ctx || !items || !selected) return;
*selected = zr_combo(ctx, items, count, *selected, item_height);
}
void
zr_combobox_string(struct zr_context *ctx, const char *items_seperated_by_zeros,
int *selected, int count, int item_height)
{
ZR_ASSERT(ctx);
ZR_ASSERT(items_seperated_by_zeros);
ZR_ASSERT(selected);
if (!ctx || !items_seperated_by_zeros || !selected) return;
*selected = zr_combo_string(ctx, items_seperated_by_zeros, *selected, count, item_height);
}
void
zr_combobox_callback(struct zr_context *ctx,
void(item_getter)(void* data, int id, const char **out_text),
void *userdata, int *selected, int count, int item_height)
{
ZR_ASSERT(ctx);
ZR_ASSERT(item_getter);
ZR_ASSERT(selected);
if (!ctx || !item_getter || !selected) return;
*selected = zr_combo_callback(ctx, item_getter, userdata, *selected, count, item_height);
}
/*
* -------------------------------------------------------------
*
@ -12604,16 +12754,17 @@ zr_menu_end(struct zr_context *ctx)
* =============================================================== */
static int zr_op_handle(struct zr_context*, union zr_param*, struct zr_event_queue*);
static const struct zr_instruction {
const char *keyword;
const char *name;
zr_flags flags;
int(*func)(struct zr_context*, union zr_param *in, struct zr_event_queue*);
const char *fmt;
unsigned short argc;
} zr_op_table[ZR_OP_MAX] = {
#define ZR_OPCODE(a, b, c) {"zr_op_"#a, b, zr_op_##a, c},
#define ZR_OPCODE(a, b, c) {#a, "zr_op_"#a, b, zr_op_##a, c},
ZR_OPCODES(ZR_OPCODE)
#undef ZR_OPCODE
{0,0,0,"o",1}
{0,0,0,0,"o",1}
};
void
@ -13833,7 +13984,7 @@ zr_color_pick(struct zr_context *ctx, struct zr_color *color,
/* ==============================================================
*
* RETAIN
* RECORDING
*
* =============================================================== */
void
@ -13912,6 +14063,622 @@ zr_store_op(struct zr_buffer *buffer, union zr_param *p, int count)
}
}
/* ==============================================================
*
* COMPILER
*
* =============================================================== */
#define ZR_LEXER_DEFAULT_PUNCTION_MAP(PUNCTUATION)\
PUNCTUATION(">>=", ZR_PUNCT_RSHIFT_ASSIGN)\
PUNCTUATION("<<=", ZR_PUNCT_LSHIFT_ASSIGN)\
PUNCTUATION("...", ZR_PUNCT_PARAMS)\
PUNCTUATION("&&", ZR_PUNCT_LOGIC_AND)\
PUNCTUATION("||", ZR_PUNCT_LOGIC_OR)\
PUNCTUATION(">=", ZR_PUNCT_LOGIC_GEQ)\
PUNCTUATION("<=", ZR_PUNCT_LOGIC_LEQ)\
PUNCTUATION("==", ZR_PUNCT_LOGIC_EQ)\
PUNCTUATION("!=", ZR_PUNCT_LOGIC_UNEQ)\
PUNCTUATION("*=", ZR_PUNCT_MUL_ASSIGN)\
PUNCTUATION("/=", ZR_PUNCT_DIV_ASSIGN)\
PUNCTUATION("%=", ZR_PUNCT_MOD_ASSIGN)\
PUNCTUATION("+=", ZR_PUNCT_ADD_ASSIGN)\
PUNCTUATION("-=", ZR_PUNCT_SUB_ASSIGN)\
PUNCTUATION("++", ZR_PUNCT_INC)\
PUNCTUATION("--", ZR_PUNCT_DEC)\
PUNCTUATION("&=", ZR_PUNCT_BIN_AND_ASSIGN)\
PUNCTUATION("|=", ZR_PUNCT_BIN_OR_ASSIGN)\
PUNCTUATION("^=", ZR_PUNCT_BIN_XOR_ASSIGN)\
PUNCTUATION(">>", ZR_PUNCT_RSHIFT)\
PUNCTUATION("<<", ZR_PUNCT_LSHIFT)\
PUNCTUATION("->", ZR_PUNCT_POINTER)\
PUNCTUATION("::", ZR_PUNCT_CPP1)\
PUNCTUATION(".*", ZR_PUNCT_CPP2)\
PUNCTUATION("*", ZR_PUNCT_MUL)\
PUNCTUATION("/", ZR_PUNCT_DIV)\
PUNCTUATION("%", ZR_PUNCT_MOD)\
PUNCTUATION("+", ZR_PUNCT_ADD)\
PUNCTUATION("-", ZR_PUNCT_SUB)\
PUNCTUATION("=", ZR_PUNCT_ASSIGN)\
PUNCTUATION("&", ZR_PUNCT_BIN_AND)\
PUNCTUATION("|", ZR_PUNCT_BIN_OR)\
PUNCTUATION("^", ZR_PUNCT_BIN_XOR)\
PUNCTUATION("~", ZR_PUNCT_BIN_NOT)\
PUNCTUATION("!", ZR_PUNCT_LOGIC_NOT)\
PUNCTUATION(">", ZR_PUNCT_LOGIC_GREATER)\
PUNCTUATION("<", ZR_PUNCT_LOGIC_LESS)\
PUNCTUATION(".", ZR_PUNCT_REF)\
PUNCTUATION(",", ZR_PUNCT_COMMA)\
PUNCTUATION(";", ZR_PUNCT_SEMICOLON)\
PUNCTUATION(":", ZR_PUNCT_COLON)\
PUNCTUATION("?", ZR_PUNCT_QUESTIONMARK)\
PUNCTUATION("(", ZR_PUNCT_PARENTHESE_OPEN)\
PUNCTUATION(")", ZR_PUNCT_PARENTHESE_CLOSE)\
PUNCTUATION("{", ZR_PUNCT_BRACE_OPEN)\
PUNCTUATION("}", ZR_PUNCT_BRACE_CLOSE)\
PUNCTUATION("[", ZR_PUNCT_BRACKET_OPEN)\
PUNCTUATION("]", ZR_PUNCT_BRACKET_CLOSE)\
PUNCTUATION("\\", ZR_PUNCT_BACKSLASH)\
PUNCTUATION("#", ZR_PUNCT_PRECOMPILER)\
PUNCTUATION("$", ZR_PUNCT_DOLLAR)
enum zr_lexer_default_punctuation_ids {
#define PUNCTUATION(chars, id) id,
ZR_LEXER_DEFAULT_PUNCTION_MAP(PUNCTUATION)
#undef PUNCTUATION
ZR_PUNCT_MAX
};
static const struct zr_punctuation {
const char *string; int id;
} zr_lexer_default_punctuations[] = {
#define PUNCTUATION(chars, id) {chars, id},
ZR_LEXER_DEFAULT_PUNCTION_MAP(PUNCTUATION)
#undef PUNCTUATION
{0, 0}
};
enum zr_token_type {
ZR_TOKEN_STRING,
ZR_TOKEN_LITERAL,
ZR_TOKEN_NUMBER,
ZR_TOKEN_NAME,
ZR_TOKEN_PUNCT
};
struct zr_token {
enum zr_token_type type;
zr_size line;
int line_crossed;
struct {unsigned long i; double f;} value;
const char *str;
zr_size len;
int punct;
};
struct zr_lexer {
const char *last;
const char *current;
const char *end;
zr_size length;
zr_size line;
zr_size last_line;
zr_compile_log_f log;
void *userdata;
int error;
};
static int
zr_skip_white_space(struct zr_lexer *lexer, int current_line)
{
while (1) {
/* skip white spaces */
while (*lexer->current <= ' ' && lexer->current < lexer->end) {
if (!*lexer->current || lexer->current == lexer->end)
return 0;
if (*lexer->current == '\n') {
lexer->line++;
if (current_line) {
lexer->current++;
return 1;
}
}
lexer->current++;
}
/* skip comments */
if (*lexer->current == '/' && lexer->current < lexer->end) {
if (lexer->current+1 >= lexer->end)
return 0;
if (*(lexer->current + 1) == '/') {
/* C++ style comments */
lexer->current++;
do {
lexer->current++;
if ((lexer->current >= lexer->end) || !*lexer->current)
return 0;
} while (*lexer->current != '\n');
lexer->line++;
lexer->current++;
if (current_line)
return 1;
if (lexer->current >= lexer->end || !*lexer->current)
return 0;
continue;
} else if ((*lexer->current + 1) == '*') {
/* C style comments */
lexer->current++;
while (1) {
lexer->current++;
if (lexer->current >= lexer->end || !*lexer->current)
return 0;
if (*lexer->current == '\n') {
lexer->line++;
} else if (*lexer->current == '/' && lexer->current+1 < lexer->end) {
if (*(lexer->current-1) == '*') break;
if (*(lexer->current+1) == '*' && lexer->log) {
lexer->log(lexer->userdata, lexer->line, "nested comment");
}
}
}
lexer->current++;
if (lexer->current >= lexer->end || !*lexer->current)
return 0;
lexer->current++;
if (lexer->current >= lexer->end || !*lexer->current)
return 0;
continue;
}
}
break;
}
return 1;
}
static int
zr_read_string(struct zr_lexer *lexer, struct zr_token *token, int quote)
{
zr_size tmpline;
const char *tmp;
if (quote == '\"')
token->type = ZR_TOKEN_STRING;
else token->type = ZR_TOKEN_LITERAL;
lexer->current++;
if (lexer->current >= lexer->end)
return 0;
token->len = 0;
token->str = lexer->current;
while (lexer->current < lexer->end) {
if (*lexer->current == quote) {
lexer->current++;
if (lexer->current >= lexer->end)
return 0;
tmp = lexer->current;
tmpline = lexer->line;
if (!zr_skip_white_space(lexer, 0)) {
lexer->current = tmp;
lexer->line = tmpline;
break;
}
if (*lexer->current == '\0') {
if (lexer->log)
lexer->log(lexer->userdata, lexer->line,
"expecting string after '\' terminated line");
lexer->error = 1;
return 0;
}
break;
} else {
if (*lexer->current == '\0') {
if (lexer->log)
lexer->log(lexer->userdata, lexer->line, "missing trailing quote");
lexer->error = 1;
return 0;
}
if (*lexer->current == '\n') {
if (lexer->log)
lexer->log(lexer->userdata, lexer->line, "newline inside string");
lexer->error = 1;
return 0;
}
lexer->current++;
}
}
if (token->str)
token->len = (zr_size)(lexer->current - token->str) - 1;
return 1;
}
static int
zr_read_name(struct zr_lexer *lexer, struct zr_token *token)
{
char c;
token->type = ZR_TOKEN_NAME;
token->str = lexer->current;
token->len = 0;
do {
token->len++;
lexer->current++;
if (lexer->current >= lexer->end)
break;
c = *lexer->current;
} while ((c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z') ||
(c >= '0' && c <= '9') ||
c == '_');
return 1;
}
static int
zr_read_number(struct zr_lexer *lexer, struct zr_token *token)
{
int dot;
char c, c2;
token->type = ZR_TOKEN_NUMBER;
token->value.f = 0;
token->value.i = 0;
token->str = 0;
token->len = 0;
c = *lexer->current;
if ((lexer->current + 1) < lexer->end)
c2 = *(lexer->current + 1);
else c2 = 0;
/* decimal or floating point number */
dot = 0;
token->str = lexer->current;
while (1) {
if (c >= '0' && c <= '9') {
} else if (c == '.') dot++;
else break;
token->len++;
if (lexer->current+1 >= lexer->end) break;
c = *(++lexer->current);
}
if (c == 'e' && dot == 0)
dot++; /* scientific notation */
if (dot) {
if (c == 'e') {
if (lexer->current+1 >= lexer->end)
return 0;
token->len++;
c = *(++lexer->current);
if (c == '-' || c == '+') {
token->len++;
if (lexer->current+1 >= lexer->end)
return 0;
c = *(++lexer->current);
}
while (c >= '0' && c <= '9') {
if (lexer->current+1 >= lexer->end) break;
c = *(++lexer->current);
token->len++;
}
}
}
return 1;
}
static int
zr_read_punctuation(struct zr_lexer *lexer, struct zr_token *token)
{
int l, i;
const char *p;
const struct zr_punctuation *punc;
token->len = 0;
token->str = lexer->current;
for (i = 0; zr_lexer_default_punctuations[i].string; ++i) {
punc = &zr_lexer_default_punctuations[i];
p = punc->string;
for (l = 0; p[l] && lexer->current < lexer->end && lexer->current[l]; ++l) {
if (lexer->current[l] != p[l])
break;
}
if (!p[l]) {
token->len += (zr_size)l;
lexer->current += l;
token->type = ZR_TOKEN_PUNCT;
token->punct = punc->id;
return 1;
}
}
return 0;
}
static int
zr_parse(struct zr_lexer *lexer, struct zr_token *token)
{
int c;
if (!lexer->current) return 0;
if (lexer->current >= lexer->end) return 0;
if (lexer->error == 1) return 0;
zr_zero_struct(*token);
lexer->last = lexer->current;
lexer->last_line = lexer->line;
lexer->error = 0;
if (!zr_skip_white_space(lexer, 0))
return 0;
token->line = lexer->line;
token->line_crossed = (lexer->line - lexer->last_line) ? 1 : 0;
c = *lexer->current;
if ((c >= '0' && c <= '9') ||
(c == '.' && (*(lexer->current + 1)) >= '0' &&
(c == '.' && (*(lexer->current + 1)) <= '9'))) {
if (!zr_read_number(lexer, token)) return 0;
} else if (c == '\"' || c == '\'') {
if (!zr_read_string(lexer, token, c)) return 0;
} else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_') {
if (!zr_read_name(lexer, token)) return 0;
} else if (!zr_read_punctuation(lexer, token)) {
if (lexer->log)
lexer->log(lexer->userdata, lexer->line, "unkown punctuation");
lexer->error = 1;
return 0;
}
return 1;
}
static int
zr_check_type(struct zr_lexer *lexer, enum zr_token_type type,
int subtype, struct zr_token *token)
{
struct zr_token tok;
if (!zr_parse(lexer, &tok))
return 0;
if (tok.type == type && (tok.punct == subtype)) {
*token = tok;
return 1;
}
/* unread token */
lexer->current = lexer->last;
lexer->line = lexer->last_line;
return 0;
}
static double
zr_token_parse_number(const char *p, zr_size length)
{
int i, div, pow;
double m;
zr_size len = 0;
double f = 0;
while (len < length && p[len] != '.' && p[len] != 'e') {
f = f * 10.0 + (double)(p[len] - '0');
len++;
}
if (len < length && p[len] == '.') {
len++;
for (m = 0.1; len < length; len++) {
f = f + (double)(p[len] - '0') * m;
m *= 0.1;
}
}
if (len < length && p[len] == 'e' ) {
len++;
if (p[len] == '-') {
div = 1;
len++;
} else if (p[len] == '+') {
div = 0;
len++;
} else div = 0;
pow = 0;
for (pow = 0; len < length; len++)
pow = pow * 10 + (int)(p[len] - '0');
for (m = 1.0, i = 0; i < pow; ++i)
m *= 100.0;
if (div) f /= m;
else f *= m;
}
return f;
}
enum zr_compiler_status
zr_compile(struct zr_buffer *program, const char *script,
zr_size len, zr_compile_log_f log, void *userdata)
{
unsigned short opcode;
zr_size i = 0;
struct zr_lexer lexer;
ZR_ASSERT(program);
ZR_ASSERT(script);
if (!program || !script)
return ZR_COMPILER_INVALID_VALUE;
/* setup lexer */
zr_zero_struct(lexer);
lexer.error = 0;
lexer.current = script;
lexer.end = script + len;
lexer.length = len;
lexer.line = 0;
lexer.log = log;
lexer.userdata = userdata;
while (!lexer.error && *lexer.current && lexer.current < lexer.end) {
struct zr_token tok;
struct zr_token name;
char buffer[1024];
/* parse function name */
if (!zr_parse(&lexer, &name) || name.type != ZR_TOKEN_NAME) {
if (!lexer.current || !(lexer.current < lexer.end))
break;
ZR_ASSERT(!ZR_COMPILER_INVALID_SCRIPT);
return ZR_COMPILER_INVALID_SCRIPT;
}
if (!zr_check_type(&lexer, ZR_TOKEN_PUNCT, ZR_PUNCT_PARENTHESE_OPEN, &tok)) {
ZR_ASSERT(!ZR_COMPILER_INVALID_SCRIPT);
return ZR_COMPILER_INVALID_SCRIPT;
}
/* try to find instruction */
for (opcode = 0; opcode < ZR_OP_MAX; ++opcode) {
if (!zr_stricmpn(zr_op_table[opcode].keyword, name.str, (int)name.len))
break;
}
if (opcode == ZR_OP_MAX) {
ZR_ASSERT(!ZR_COMPILER_OP_NOT_FOUND);
return ZR_COMPILER_OP_NOT_FOUND;
}
/* parse widget arguments */
{union zr_param p[32];
const struct zr_instruction *op = &zr_op_table[opcode];
ZR_ASSERT(op->argc < ZR_LEN(p));
if (op->argc >= ZR_LEN(p)) {
ZR_ASSERT(!ZR_COMPILER_WRONG_ARG_COUNT);
return ZR_COMPILER_WRONG_ARG_COUNT;
}
for (i = 0; i < op->argc; ++i)
{
double value;
int has_comma = 1;
if (op->fmt[i] != ZR_TYPE_PTR && op->fmt[i] != ZR_TYPE_IMAGE && op->fmt[i] != ZR_TYPE_OP) {
if (!zr_parse(&lexer, &tok)) {
if (log) log(userdata, lexer.line, "missing argument in %.*s at index %d", name.len, name.str, i+1);
ZR_ASSERT(!ZR_COMPILER_INVALID_SCRIPT);
return ZR_COMPILER_INVALID_SCRIPT;
}
}
switch (op->fmt[i]) {
case ZR_TYPE_OP: {
p[i].h.op = opcode;
p[i].h.next = zr_op_table[opcode].argc;
has_comma = 0;
} break;
case ZR_TYPE_INT: {
if (tok.type != ZR_TOKEN_NUMBER) {
if (log) log(userdata, lexer.line, "argument in %.*s at index %d is not a number", name.len, name.str, i+1);
ZR_ASSERT(!ZR_COMPILER_WRONG_ARG_TYPE);
return ZR_COMPILER_WRONG_ARG_TYPE;
}
value = zr_token_parse_number(tok.str, tok.len);
p[i].i = (int)value;
} break;
case ZR_TYPE_UINT: {
if (tok.type != ZR_TOKEN_NUMBER) {
if (log) log(userdata, lexer.line, "argument in %.*s at index %d is not a number", name.len, name.str, i+1);
ZR_ASSERT(!ZR_COMPILER_WRONG_ARG_TYPE);
return ZR_COMPILER_WRONG_ARG_TYPE;
}
value = zr_token_parse_number(tok.str, tok.len);
if (value < 0) value = 0;
p[i].ui = (unsigned int)value;
} break;
case ZR_TYPE_FLOAT: {
if (tok.type != ZR_TOKEN_NUMBER) {
if (log) log(userdata, lexer.line, "argument in %.*s at index %d is not a number", name.len, name.str, i+1);
ZR_ASSERT(!ZR_COMPILER_WRONG_ARG_TYPE);
return ZR_COMPILER_WRONG_ARG_TYPE;
}
value = zr_token_parse_number(tok.str, tok.len);
p[i].f = (float)value;
} break;
case ZR_TYPE_HASH: {
if (tok.type == ZR_TOKEN_NUMBER) {
value = zr_token_parse_number(tok.str, tok.len);
if (value < 0) value = 0;
p[i].hash = (zr_hash)value;
} else if (tok.type == ZR_TOKEN_STRING || tok.type == ZR_TOKEN_LITERAL) {
p[i].hash = zr_murmur_hash(tok.str, (int)tok.len, 0);
} else {
if (log) log(userdata, lexer.line, "argument in %.*s at index %d is not a number or string", name.len, name.str, i+1);
ZR_ASSERT(!ZR_COMPILER_WRONG_ARG_TYPE);
return ZR_COMPILER_WRONG_ARG_TYPE;
}
} break;
case ZR_TYPE_FLAGS: {
if (tok.type != ZR_TOKEN_NUMBER) {
if (log) log(userdata, lexer.line, "argument in %.*s at index %d is not a number", name.len, name.str, i+1);
ZR_ASSERT(!ZR_COMPILER_WRONG_ARG_TYPE);
return ZR_COMPILER_WRONG_ARG_TYPE;
}
value = zr_token_parse_number(tok.str, tok.len);
if (value < 0) value = 0;
p[i].flags = (zr_flags)value;
} break;
case ZR_TYPE_CHEAT: {
zr_size length = ZR_MIN(tok.len, ZR_LEN(buffer)-1);
if (tok.type != ZR_TOKEN_STRING && tok.type != ZR_TOKEN_LITERAL) {
if (log) log(userdata, lexer.line, "argument in %.*s at index %d is not a string", name.len, name.str, i+1);
ZR_ASSERT(!ZR_COMPILER_WRONG_ARG_TYPE);
return ZR_COMPILER_WRONG_ARG_TYPE;
}
zr_memcopy(buffer, tok.str, length);
buffer[length] = '\0';
p[i].cheat = buffer;
} break;
case ZR_TYPE_COLOR: {
char hex_color[16];
zr_size length = ZR_MIN(tok.len, ZR_LEN(hex_color)-1);
if (tok.type != ZR_TOKEN_STRING && tok.type != ZR_TOKEN_LITERAL) {
if (log) log(userdata, lexer.line, "argument in %.*s at index %d is not a hex color string", name.len, name.str, i+1);
ZR_ASSERT(!ZR_COMPILER_WRONG_ARG_TYPE);
return ZR_COMPILER_WRONG_ARG_TYPE;
}
zr_memcopy(hex_color, tok.str, length);
hex_color[length] = '\0';
p[i].color = zr_rgba_hex(hex_color);
} break;
case ZR_TYPE_IMAGE: p[i].img.ptr = 0; has_comma = 0; break;
case ZR_TYPE_PTR: p[i].ptr = 0; has_comma = 0; break;
default:
ZR_ASSERT(!ZR_COMPILER_INVALID_ARG);
return ZR_COMPILER_INVALID_ARG;
}
if (has_comma && (int)i < op->argc-1 && !zr_check_type(&lexer, ZR_TOKEN_PUNCT, ZR_PUNCT_COMMA, &tok)) {
if (log) log(userdata, lexer.line, "missing argument or ',' in %.*s after argument %d", name.len, name.str, i+1);
ZR_ASSERT(!ZR_COMPILER_MISSING_COMMA);
return ZR_COMPILER_MISSING_COMMA;
}
}
/* parse ')' at end of line */
if (!zr_check_type(&lexer, ZR_TOKEN_PUNCT, ZR_PUNCT_PARENTHESE_CLOSE, &tok)) {
if (log) log(userdata, lexer.line, "expected ')' at the end of widget: %.*s", name.len, name.str);
ZR_ASSERT(!ZR_COMPILER_INVALID_ARG);
return ZR_COMPILER_INVALID_ARG;
}
zr_check_type(&lexer, ZR_TOKEN_PUNCT, ZR_PUNCT_SEMICOLON, &tok);
zr_store_op(program, p, op->argc);}
}
{union zr_param p;
p.h.op = ZR_OP_list_end;
p.h.next = zr_op_table[p.h.op].argc;
zr_store_op(program, &p, 1);}
return ZR_COMPILER_OK;
}
/* ==============================================================
*
* INTERPRETER
*
* =============================================================== */
static int
zr_op_handle(struct zr_context *ctx, union zr_param *p,
struct zr_event_queue *queue)

View File

@ -1710,11 +1710,29 @@ void zr_set_user_data(struct zr_context*, zr_handle handle);
#endif
/*--------------------------------------------------------------
* RECORDING
* INTERPRETER
* -------------------------------------------------------------*/
/* generate UI bytecode from source code */
void zr_recording_begin(struct zr_context*, struct zr_buffer*);
void zr_recording_end(struct zr_context*);
/* generate UI bytecode from script */
enum zr_compiler_status {
ZR_COMPILER_OK,
ZR_COMPILER_INVALID_VALUE,
ZR_COMPILER_INVALID_ARG,
ZR_COMPILER_OP_NOT_FOUND,
ZR_COMPILER_MISSING_COMMA,
ZR_COMPILER_WRONG_ARG_TYPE,
ZR_COMPILER_NO_MEMORY,
ZR_COMPILER_WRONG_ARG_COUNT,
ZR_COMPILER_INVALID_SCRIPT
};
typedef void(*zr_compile_log_f)(void *userdata, zr_size line, const char *error, ...);
enum zr_compiler_status zr_compile(struct zr_buffer *program, const char *script,
zr_size len, zr_compile_log_f, void *log_usr);
/* run recorded or compiled UI bytecode */
int zr_exec(struct zr_context*, struct zr_buffer *event_buffer,
int *event_count, const struct zr_event_mask*,
struct zr_buffer *program, struct zr_buffer *runtime);
@ -1864,7 +1882,7 @@ void zr_layout_pop(struct zr_context*);
/*--------------------------------------------------------------
* WIDGETS
* -------------------------------------------------------------*/
/* base widget calls for custom widgets (used by all widgets internally) */
/* base widget calls for custom widgets */
enum zr_widget_state zr_widget(struct zr_rect*, const struct zr_context*);
enum zr_widget_state zr_widget_fitting(struct zr_rect*, struct zr_context*);
@ -1898,7 +1916,7 @@ int zr_option(struct zr_context*, const char*, int active);
int zr_selectable(struct zr_context*, const char*, zr_flags alignment, int *value);
int zr_select(struct zr_context*, const char*, zr_flags alignment, int value);
/* buttons (push/repeater) */
/* buttons */
int zr_button_text(struct zr_context *ctx, const char *title, enum zr_button_behavior);
int zr_button_color(struct zr_context*, struct zr_color, enum zr_button_behavior);
int zr_button_symbol(struct zr_context*, enum zr_symbol_type, enum zr_button_behavior);
@ -1922,7 +1940,7 @@ struct zr_color zr_color_picker(struct zr_context*, struct zr_color,
void zr_color_pick(struct zr_context*, struct zr_color*,
enum zr_color_picker_format);
/* extended value modifier by dragging, increment/decrement and text input */
/* extended value (dragging, increment/decrement and text input) */
void zr_property_float(struct zr_context *layout, const char *name,
float min, float *val, float max, float step,
float inc_per_pixel);
@ -1974,6 +1992,23 @@ int zr_combo_item_symbol(struct zr_context*, enum zr_symbol_type,
void zr_combo_close(struct zr_context*);
void zr_combo_end(struct zr_context*);
/* combobox */
int zr_combo(struct zr_context*, const char **items, int count, int selected,
int item_height);
int zr_combo_string(struct zr_context*, const char *items_seperated_by_zeros,
int selected, int count, int item_height);
int zr_combo_callback(struct zr_context*,
void(item_getter)(void* data, int id, const char **out_text),
void *userdata, int selected, int count, int item_height);
void zr_combobox(struct zr_context*, const char **items, int count, int *selected,
int item_height);
void zr_combobox_string(struct zr_context*, const char *items_seperated_by_zeros,
int *selected, int count, int item_height);
void zr_combobox_callback(struct zr_context*,
void(item_getter)(void* data, int id, const char **out_text),
void *userdata, int *selected, int count, int item_height);
/* contextual menu */
int zr_contextual_begin(struct zr_context*, struct zr_panel*, zr_flags,
struct zr_vec2, struct zr_rect trigger_bounds);