2890 lines
118 KiB
C
2890 lines
118 KiB
C
// ----------------------------------------------------------------------------------------
|
|
// ui extensions first
|
|
|
|
static float
|
|
nk_text_width(struct nk_context *ctx, const char *str, unsigned len) {
|
|
const struct nk_style *style = &ctx->style;
|
|
const struct nk_user_font *f = style->font;
|
|
float pixels_width = f->width(f->userdata, f->height, str, len ? (int)len : (int)strlen(str));
|
|
return pixels_width + 10; // 10 -> internal widget padding
|
|
}
|
|
|
|
static nk_bool
|
|
nk_hovered_text(struct nk_context *ctx, const char *str, int len,
|
|
nk_flags align, nk_bool value)
|
|
{
|
|
struct nk_window *win;
|
|
struct nk_panel *layout;
|
|
const struct nk_input *in;
|
|
const struct nk_style *style;
|
|
|
|
enum nk_widget_layout_states state;
|
|
struct nk_rect bounds;
|
|
|
|
NK_ASSERT(ctx);
|
|
NK_ASSERT(ctx->current);
|
|
NK_ASSERT(ctx->current->layout);
|
|
if (!ctx || !ctx->current || !ctx->current->layout)
|
|
return 0;
|
|
|
|
win = ctx->current;
|
|
layout = win->layout;
|
|
style = &ctx->style;
|
|
|
|
state = nk_widget(&bounds, ctx);
|
|
if (!state) return 0;
|
|
in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
|
|
|
|
#if 1 //< @r-lyeh: sim button logic
|
|
struct nk_rect touch;
|
|
touch.x = bounds.x - style->selectable.touch_padding.x;
|
|
touch.y = bounds.y - style->selectable.touch_padding.y;
|
|
touch.w = bounds.w + style->selectable.touch_padding.x * 2;
|
|
touch.h = bounds.h + style->selectable.touch_padding.y * 2;
|
|
int clicked = !!nk_button_behavior(&ctx->last_widget_state, touch, in, NK_BUTTON_DEFAULT);
|
|
in = 0; //< @r-lyeh: do not pass any input
|
|
#endif
|
|
|
|
nk_do_selectable(&ctx->last_widget_state, &win->buffer, bounds,
|
|
str, len, align, &value, &style->selectable, in, style->font);
|
|
|
|
return clicked; //< @r-lyeh: return sim button logic instead of prev function call
|
|
}
|
|
|
|
#define ui_push_hspace(px) \
|
|
(int xx = px; xx; xx = 0) \
|
|
for(struct nk_context *ctx = (struct nk_context*)ui_handle(); ctx; ctx = 0 ) \
|
|
for(struct nk_panel *layout = ui_ctx->current->layout; layout; ) \
|
|
for( xx = (layout->at_x += px, layout->bounds.w -= px, 0); layout; layout->at_x -= px, layout->bounds.w += px, layout = 0 )
|
|
|
|
// helper macros to instance an overlayed toolbar within the regions of an existing widget
|
|
#define UI_TOOLBAR_OVERLAY_DECLARE(...) \
|
|
__VA_ARGS__; \
|
|
struct nk_rect toolbar_bounds; nk_layout_peek(&toolbar_bounds, ui_ctx); \
|
|
struct nk_vec2 item_padding = ui_ctx->style.text.padding; \
|
|
struct nk_text text; \
|
|
text.padding.x = item_padding.x; \
|
|
text.padding.y = item_padding.y; \
|
|
text.background = ui_ctx->style.window.background;
|
|
#define UI_TOOLBAR_OVERLAY(CHOICE,TEXT,COLOR,ALIGNMENT) \
|
|
do { \
|
|
text.text = COLOR; \
|
|
nk_widget_text(&ui_ctx->current->buffer, toolbar_bounds, TEXT, strlen(TEXT), &text, ALIGNMENT, ui_ctx->style.font); \
|
|
int clicked_x = input_down(MOUSE_L) && nk_input_is_mouse_hovering_rect(&ui_ctx->input, toolbar_bounds); \
|
|
if( clicked_x ) clicked_x = (int)((ui_ctx->input.mouse.pos.x - toolbar_bounds.x) - (ALIGNMENT == NK_TEXT_RIGHT ? bounds.w : 0) ); \
|
|
CHOICE = 1 + (ALIGNMENT == NK_TEXT_RIGHT ? -1 : +1) * clicked_x / (UI_ICON_FONTSIZE + UI_ICON_SPACING_X); /* divided by px per ICON_MD_ glyph approximately */ \
|
|
int glyphs = strlen(TEXT) / 4 /*3:MD,4:MDI*/; CHOICE *= !!clicked_x * (CHOICE <= glyphs); } while(0)
|
|
|
|
// menu macros that work not only standalone but also contained within a panel or window
|
|
static int ui_using_v2_menubar = 0;
|
|
#define UI_MENU(N, ...) do { \
|
|
enum { MENUROW_HEIGHT = 25 }; \
|
|
int embedded = !!ui_ctx->current; \
|
|
struct nk_rect total_space = {0,0,window_width(),window_height()}; \
|
|
if( embedded ) total_space = nk_window_get_bounds(ui_ctx), total_space.w -= 10; \
|
|
int created = !embedded && nk_begin(ui_ctx, "MENU_" STRINGIZE(__COUNTER__), nk_rect(0, 0, window_width(), UI_MENUROW_HEIGHT), NK_WINDOW_NO_SCROLLBAR); \
|
|
if ( embedded || created ) { \
|
|
ui_using_v2_menubar = 1; \
|
|
int align = NK_TEXT_LEFT, Nth = (N), ITEM_WIDTH = 30, span = 0; \
|
|
nk_menubar_begin(ui_ctx); \
|
|
nk_layout_row_begin(ui_ctx, NK_STATIC, MENUROW_HEIGHT, Nth); \
|
|
__VA_ARGS__; \
|
|
nk_menubar_end(ui_ctx); \
|
|
if( created ) nk_end(ui_ctx); \
|
|
} } while(0)
|
|
#define UI_MENU_POPUP(title, px, ...) { \
|
|
int hspace = maxi(ITEM_WIDTH, nk_text_width(ui_ctx,(title),0)); \
|
|
nk_layout_row_push(ui_ctx, hspace); span += hspace; \
|
|
if (nk_menu_begin_label(ui_ctx, (title), align, nk_vec2(px.x>1?px.x:px.x*total_space.w,px.y>1?px.y:px.y*total_space.h))) { \
|
|
__VA_ARGS__; \
|
|
nk_menu_end(ui_ctx); \
|
|
}}
|
|
#define UI_MENU_ITEM(title, ...) { \
|
|
int hspace = maxi(ITEM_WIDTH, nk_text_width(ui_ctx,(title),0)); \
|
|
nk_layout_row_push(ui_ctx, hspace); span += hspace; \
|
|
if (nk_menu_begin_label(ui_ctx, (title), align, nk_vec2(1,1))) { \
|
|
__VA_ARGS__; \
|
|
nk_menu_close(ui_ctx); \
|
|
nk_menu_end(ui_ctx); \
|
|
}}
|
|
#define UI_MENU_ALIGN_RIGHT(px, ...) { \
|
|
int hspace = total_space.w - span - (px) - 1.5 * ITEM_WIDTH; \
|
|
nk_layout_row_push(ui_ctx, hspace); span += hspace; \
|
|
if (nk_menu_begin_label(ui_ctx, (title), align = NK_TEXT_RIGHT, nk_vec2(1,1))) { \
|
|
__VA_ARGS__; \
|
|
nk_menu_close(ui_ctx); \
|
|
nk_menu_end(ui_ctx); \
|
|
}}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// ui
|
|
|
|
#ifndef UI_ICONS_SMALL
|
|
//#define UI_ICONS_SMALL 1
|
|
#endif
|
|
|
|
#define UI_FONT_ENUM(carlito,b612) b612 // carlito
|
|
|
|
#define UI_FONT_REGULAR UI_FONT_ENUM("Carlito", "B612") "-Regular.ttf"
|
|
#define UI_FONT_HEADING UI_FONT_ENUM("Carlito", "B612") "-BoldItalic.ttf"
|
|
#define UI_FONT_TERMINAL UI_FONT_ENUM("Inconsolata", "B612Mono") "-Regular.ttf"
|
|
|
|
#if UI_LESSER_SPACING
|
|
enum { UI_SEPARATOR_HEIGHT = 5, UI_MENUBAR_ICON_HEIGHT = 20, UI_ROW_HEIGHT = 22, UI_MENUROW_HEIGHT = 32 };
|
|
#else
|
|
enum { UI_SEPARATOR_HEIGHT = 10, UI_MENUBAR_ICON_HEIGHT = 25, UI_ROW_HEIGHT = 32, UI_MENUROW_HEIGHT = 32 };
|
|
#endif
|
|
|
|
#if UI_FONT_LARGE
|
|
#define UI_FONT_REGULAR_SIZE UI_FONT_ENUM(18,17)
|
|
#define UI_FONT_HEADING_SIZE UI_FONT_ENUM(20,19)
|
|
#define UI_FONT_TERMINAL_SIZE UI_FONT_ENUM(14,14)
|
|
#elif UI_FONT_SMALL
|
|
#define UI_FONT_REGULAR_SIZE UI_FONT_ENUM(13,14)
|
|
#define UI_FONT_HEADING_SIZE UI_FONT_ENUM(14.5,15)
|
|
#define UI_FONT_TERMINAL_SIZE UI_FONT_ENUM(14,14)
|
|
#else
|
|
#define UI_FONT_REGULAR_SIZE UI_FONT_ENUM(14.5,16)
|
|
#define UI_FONT_HEADING_SIZE UI_FONT_ENUM(16,17.5)
|
|
#define UI_FONT_TERMINAL_SIZE UI_FONT_ENUM(14,14)
|
|
#endif
|
|
|
|
#define UI_FONT_REGULAR_SAMPLING UI_FONT_ENUM(vec3(1,1,1),vec3(1,1,1))
|
|
#define UI_FONT_HEADING_SAMPLING UI_FONT_ENUM(vec3(1,1,1),vec3(1,1,1))
|
|
#define UI_FONT_TERMINAL_SAMPLING UI_FONT_ENUM(vec3(1,1,1),vec3(1,1,1))
|
|
|
|
#if UI_ICONS_SMALL
|
|
#define UI_ICON_FONTSIZE UI_FONT_ENUM(16.5f,16.5f)
|
|
#define UI_ICON_SPACING_X UI_FONT_ENUM(-2,-2)
|
|
#define UI_ICON_SPACING_Y UI_FONT_ENUM(4.5f,3.5f)
|
|
#else
|
|
#define UI_ICON_FONTSIZE UI_FONT_ENUM(20,20)
|
|
#define UI_ICON_SPACING_X UI_FONT_ENUM(0,0)
|
|
#define UI_ICON_SPACING_Y UI_FONT_ENUM(6.5f,5.0f)
|
|
#endif
|
|
|
|
#define MAX_VERTEX_MEMORY 512 * 1024
|
|
#define MAX_ELEMENT_MEMORY 128 * 1024
|
|
|
|
static struct nk_context *ui_ctx;
|
|
static struct nk_glfw nk_glfw = {0};
|
|
|
|
void* ui_handle() {
|
|
return ui_ctx;
|
|
}
|
|
|
|
static void nk_config_custom_fonts() {
|
|
#define UI_ICON_MIN ICON_MD_MIN
|
|
#define UI_ICON_MED ICON_MD_MAX_16
|
|
#define UI_ICON_MAX ICON_MD_MAX
|
|
|
|
#define ICON_BARS ICON_MD_MENU
|
|
#define ICON_FILE ICON_MD_INSERT_DRIVE_FILE
|
|
#define ICON_TRASH ICON_MD_DELETE
|
|
|
|
struct nk_font *font = NULL;
|
|
struct nk_font_atlas *atlas = NULL;
|
|
nk_glfw3_font_stash_begin(&nk_glfw, &atlas); // nk_sdl_font_stash_begin(&atlas);
|
|
|
|
// Default font(#1)...
|
|
int datalen = 0;
|
|
for( char *data = vfs_load(UI_FONT_REGULAR, &datalen); data; data = 0 ) {
|
|
float font_size = UI_FONT_REGULAR_SIZE;
|
|
struct nk_font_config cfg = nk_font_config(font_size);
|
|
cfg.oversample_h = UI_FONT_REGULAR_SAMPLING.x;
|
|
cfg.oversample_v = UI_FONT_REGULAR_SAMPLING.y;
|
|
cfg.pixel_snap = UI_FONT_REGULAR_SAMPLING.z;
|
|
#if UI_LESSER_SPACING
|
|
cfg.spacing.x -= 1.0;
|
|
#endif
|
|
// win32: struct nk_font *arial = nk_font_atlas_add_from_file(atlas, va("%s/fonts/arial.ttf",getenv("windir")), font_size, &cfg); font = arial ? arial : font;
|
|
// struct nk_font *droid = nk_font_atlas_add_from_file(atlas, "nuklear/extra_font/DroidSans.ttf", font_size, &cfg); font = droid ? droid : font;
|
|
struct nk_font *regular = nk_font_atlas_add_from_memory(atlas, data, datalen, font_size, &cfg); font = regular ? regular : font;
|
|
}
|
|
|
|
// ...with icons embedded on it.
|
|
static struct icon_font {
|
|
const char *file; int yspacing; vec3 sampling; nk_rune range[3];
|
|
} icons[] = {
|
|
{"MaterialIconsSharp-Regular.otf", UI_ICON_SPACING_Y, {1,1,1}, {UI_ICON_MIN, UI_ICON_MED /*MAX*/, 0}}, // "MaterialIconsOutlined-Regular.otf" "MaterialIcons-Regular.ttf"
|
|
{"materialdesignicons-webfont.ttf", 2, {1,1,1}, {0xF68C /*ICON_MDI_MIN*/, 0xF1CC7/*ICON_MDI_MAX*/, 0}},
|
|
};
|
|
for( int f = 0; f < countof(icons); ++f )
|
|
for( char *data = vfs_load(icons[f].file, &datalen); data; data = 0 ) {
|
|
struct nk_font_config cfg = nk_font_config(UI_ICON_FONTSIZE);
|
|
cfg.range = icons[f].range; // nk_font_default_glyph_ranges();
|
|
cfg.merge_mode = 1;
|
|
|
|
cfg.spacing.x += UI_ICON_SPACING_X;
|
|
cfg.spacing.y += icons[f].yspacing;
|
|
// cfg.font->ascent += ICON_ASCENT;
|
|
// cfg.font->height += ICON_HEIGHT;
|
|
|
|
cfg.oversample_h = icons[f].sampling.x;
|
|
cfg.oversample_v = icons[f].sampling.y;
|
|
cfg.pixel_snap = icons[f].sampling.z;
|
|
|
|
#if UI_LESSER_SPACING
|
|
cfg.spacing.x -= 1.0;
|
|
#endif
|
|
|
|
struct nk_font *icons = nk_font_atlas_add_from_memory(atlas, data, datalen, UI_ICON_FONTSIZE, &cfg);
|
|
}
|
|
|
|
// Monospaced font. Used in terminals or consoles.
|
|
|
|
for( char *data = vfs_load(UI_FONT_TERMINAL, &datalen); data; data = 0 ) {
|
|
const float font_size = UI_FONT_TERMINAL_SIZE;
|
|
static const nk_rune icon_range[] = {32, 127, 0};
|
|
|
|
struct nk_font_config cfg = nk_font_config(font_size);
|
|
cfg.range = icon_range;
|
|
|
|
cfg.oversample_h = UI_FONT_TERMINAL_SAMPLING.x;
|
|
cfg.oversample_v = UI_FONT_TERMINAL_SAMPLING.y;
|
|
cfg.pixel_snap = UI_FONT_TERMINAL_SAMPLING.z;
|
|
|
|
#if UI_LESSER_SPACING
|
|
cfg.spacing.x -= 1.0;
|
|
#endif
|
|
|
|
// struct nk_font *proggy = nk_font_atlas_add_default(atlas, font_size, &cfg);
|
|
struct nk_font *bold = nk_font_atlas_add_from_memory(atlas, data, datalen, font_size, &cfg);
|
|
}
|
|
|
|
// Extra optional fonts from here...
|
|
|
|
for( char *data = vfs_load(UI_FONT_HEADING, &datalen); data; data = 0 ) {
|
|
struct nk_font_config cfg = nk_font_config(UI_FONT_HEADING_SIZE);
|
|
cfg.oversample_h = UI_FONT_HEADING_SAMPLING.x;
|
|
cfg.oversample_v = UI_FONT_HEADING_SAMPLING.y;
|
|
cfg.pixel_snap = UI_FONT_HEADING_SAMPLING.z;
|
|
|
|
#if UI_LESSER_SPACING
|
|
cfg.spacing.x -= 1.0;
|
|
#endif
|
|
|
|
struct nk_font *bold = nk_font_atlas_add_from_memory(atlas, data, datalen, UI_FONT_HEADING_SIZE, &cfg);
|
|
// font = bold ? bold : font;
|
|
}
|
|
|
|
nk_glfw3_font_stash_end(&nk_glfw); // nk_sdl_font_stash_end();
|
|
// ASSERT(font);
|
|
if(font) nk_style_set_font(ui_ctx, &font->handle);
|
|
|
|
// Load Cursor: if you uncomment cursor loading please hide the cursor
|
|
// nk_style_load_all_cursors(ctx, atlas->cursors); glfwSetInputMode(win, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
|
|
}
|
|
|
|
static void nk_config_custom_theme() {
|
|
#ifdef UI_HUE
|
|
float default_hue = UI_HUE;
|
|
#else
|
|
// 0.09 orange, 0.14 yellow, 0.40 green, 0.45 turquoise, 0.50 cyan, 0.52 default, 0.55 blue, 0.80 purple, 0.92 cherry, 0.96 red
|
|
float hues[] = { 0.40,0.45,0.50,0.52,0.55,0.80,0.92 };
|
|
float default_hue = hues[ (int)((((date() / 10000) % 100) / 24.f) * countof(hues)) ]; // YYYYMMDDhhmmss -> hh as 0..1
|
|
default_hue = 0.52;
|
|
#endif
|
|
float hue = clampf( optionf("--ui-hue", default_hue /*= 0.52*/), 0, 1 );
|
|
struct nk_color main_hue = nk_hsv_f(hue+0.025, 0.80, 0.400); // washed
|
|
struct nk_color hover_hue = nk_hsv_f(hue+0.025, 1.00, 0.600); // vivid
|
|
struct nk_color active_hue = nk_hsv_f(hue-0.010, 1.00, 0.600); // bright; same /S/V than vivid, but H/ slighty biased towards a different luma on spectrum
|
|
struct nk_color main = nk_hsv_f( 0.600, 0.00, 0.125); // washed b/w
|
|
struct nk_color hover = nk_hsv_f( 0.900, 0.00, 0.000); // vivid b/w
|
|
struct nk_color active = nk_hsv_f( 0.600, 0.00, 0.150); // bright b/w
|
|
struct nk_color table[NK_COLOR_COUNT] = {0};
|
|
table[NK_COLOR_TEXT] = nk_rgba(210, 210, 210, 255);
|
|
table[NK_COLOR_WINDOW] = nk_rgba(42, 42, 42, 245);
|
|
table[NK_COLOR_HEADER] = nk_rgba(51, 51, 56, 245);
|
|
table[NK_COLOR_BORDER] = nk_rgba(46, 46, 46, 255);
|
|
table[NK_COLOR_BUTTON] = main;
|
|
table[NK_COLOR_BUTTON_HOVER] = hover;
|
|
table[NK_COLOR_BUTTON_ACTIVE] = active;
|
|
// ok
|
|
table[NK_COLOR_TOGGLE] = nk_rgba(45*1.2, 53*1.2, 56*1.2, 255); // table[NK_COLOR_WINDOW]; // nk_rgba(45/1.2, 53/1.2, 56/1.2, 255);
|
|
table[NK_COLOR_TOGGLE_HOVER] = active;
|
|
table[NK_COLOR_TOGGLE_CURSOR] = main; // vivid_blue;
|
|
table[NK_COLOR_SCROLLBAR] = nk_rgba(50, 58, 61, 255);
|
|
table[NK_COLOR_SCROLLBAR_CURSOR] = main_hue;
|
|
table[NK_COLOR_SCROLLBAR_CURSOR_HOVER] = hover_hue;
|
|
table[NK_COLOR_SCROLLBAR_CURSOR_ACTIVE] = active_hue;
|
|
table[NK_COLOR_SLIDER] = nk_rgba(50, 58, 61, 255);
|
|
table[NK_COLOR_SLIDER_CURSOR] = main_hue;
|
|
table[NK_COLOR_SLIDER_CURSOR_HOVER] = hover_hue;
|
|
table[NK_COLOR_SLIDER_CURSOR_ACTIVE] = active_hue;
|
|
table[NK_COLOR_EDIT] = nk_rgba(50, 58, 61, 225);
|
|
table[NK_COLOR_EDIT_CURSOR] = nk_rgba(210, 210, 210, 255);
|
|
|
|
// table[NK_COLOR_COMBO] = nk_rgba(50, 58, 61, 255);
|
|
|
|
// table[NK_COLOR_PROPERTY] = nk_rgba(50, 58, 61, 255);
|
|
table[NK_COLOR_CHART] = nk_rgba(50, 58, 61, 255);
|
|
table[NK_COLOR_CHART_COLOR] = main_hue;
|
|
table[NK_COLOR_CHART_COLOR_HIGHLIGHT] = hover_hue; // nk_rgba(255, 0, 0, 255);
|
|
// table[NK_COLOR_TAB_HEADER] = main;
|
|
// table[NK_COLOR_SELECT] = nk_rgba(57, 67, 61, 255);
|
|
// table[NK_COLOR_SELECT_ACTIVE] = main;
|
|
|
|
// table[NK_COLOR_SELECT] = nk_rgba(255,255,255,255);
|
|
table[NK_COLOR_SELECT_ACTIVE] = main_hue;
|
|
|
|
// @transparent
|
|
#if !is(ems)
|
|
if( glfwGetWindowAttrib(window_handle(), GLFW_TRANSPARENT_FRAMEBUFFER) == GLFW_TRUE ) {
|
|
table[NK_COLOR_WINDOW].a =
|
|
table[NK_COLOR_HEADER].a = 255;
|
|
}
|
|
#endif
|
|
// @transparent
|
|
|
|
nk_style_default(ui_ctx);
|
|
nk_style_from_table(ui_ctx, table);
|
|
|
|
|
|
if(1)
|
|
{
|
|
struct nk_style_selectable *select;
|
|
select = &ui_ctx->style.selectable;
|
|
// nk_zero_struct(*select);
|
|
// select->hover.data.color = hover_hue;
|
|
// select->normal_active = nk_style_item_color(table[NK_COLOR_SELECT_ACTIVE]);
|
|
select->text_hover = nk_rgba(0,192,255,255);
|
|
select->text_hover_active = select->text_hover;
|
|
select->text_normal_active = select->text_hover; // nk_style_item_color(table[NK_COLOR_SELECT_ACTIVE]).data.color;
|
|
select->rounding = 2.0f;
|
|
}
|
|
|
|
|
|
struct nk_style *s = &ui_ctx->style;
|
|
s->window.spacing = nk_vec2(4,0);
|
|
s->window.combo_border = 0.f;
|
|
s->window.scrollbar_size = nk_vec2(5,5);
|
|
s->property.rounding = 0;
|
|
s->combo.border = 0;
|
|
s->combo.button_padding.x = -18;
|
|
s->button.border = 1;
|
|
s->edit.border = 0;
|
|
|
|
if( UI_ROW_HEIGHT < 32 ) { // UI_LESSER_SPACING
|
|
s->window.header.label_padding.y /= 2; // 2
|
|
s->window.header.padding.y /= 2; // /= 4 -> 1
|
|
}
|
|
}
|
|
|
|
static float ui_alpha = 1;
|
|
static array(float) ui_alphas;
|
|
static void ui_alpha_push(float alpha) {
|
|
array_push(ui_alphas, ui_alpha);
|
|
ui_alpha = alpha;
|
|
|
|
struct nk_color c;
|
|
struct nk_style *s = &ui_ctx->style;
|
|
c = s->window.background; c.a = alpha * 255; nk_style_push_color(ui_ctx, &s->window.background, c);
|
|
c = s->text.color; c.a = alpha * 255; nk_style_push_color(ui_ctx, &s->text.color, c);
|
|
c = s->window.fixed_background.data.color; c.a = alpha * 255; nk_style_push_style_item(ui_ctx, &s->window.fixed_background, nk_style_item_color(c));
|
|
}
|
|
static void ui_alpha_pop() {
|
|
if( array_count(ui_alphas) ) {
|
|
nk_style_pop_style_item(ui_ctx);
|
|
nk_style_pop_color(ui_ctx);
|
|
nk_style_pop_color(ui_ctx);
|
|
|
|
ui_alpha = *array_back(ui_alphas);
|
|
array_pop(ui_alphas);
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// ui menu
|
|
|
|
typedef struct ui_item_t {
|
|
char *buf;
|
|
int bufcap;
|
|
int type; // 0xED17 'edit' for a writeable inputbox buffer, else read-only label
|
|
} ui_item_t;
|
|
|
|
static array(ui_item_t) ui_items; // queued menu names. to be evaluated during next frame
|
|
static vec2 ui_results = {0}; // clicked menu items from last frame
|
|
|
|
int ui_item() {
|
|
return ui_items ? (ui_results.x == array_count(ui_items) ? ui_results.y : 0) : 0;
|
|
}
|
|
|
|
int ui_menu(const char *items) { // semicolon- or comma-separated items
|
|
array_push(ui_items, ((ui_item_t){STRDUP(items),0,0}));
|
|
return ui_item();
|
|
}
|
|
int ui_menu_editbox(char *buf, int bufcap) {
|
|
array_push(ui_items, ((ui_item_t){buf,bufcap,0xED17}));
|
|
return ui_item();
|
|
}
|
|
|
|
int ui_has_menubar() {
|
|
return ui_using_v2_menubar || !!ui_items; // ? UI_MENUROW_HEIGHT + 8 : 0; // array_count(ui_items) > 0;
|
|
}
|
|
|
|
static
|
|
void ui_separator_line() {
|
|
struct nk_rect space; nk_layout_peek(&space, ui_ctx); // bounds.w *= 0.95f;
|
|
struct nk_command_buffer *canvas = nk_window_get_canvas(ui_ctx);
|
|
nk_stroke_line(canvas, space.x+0,space.y+0,space.x+space.w,space.y+0, 3.0, nk_rgb(128,128,128));
|
|
}
|
|
|
|
NK_API nk_bool
|
|
nk_menu_begin_text_styled(struct nk_context *ctx, const char *title, int len,
|
|
nk_flags align, struct nk_vec2 size, struct nk_style_button *style_button) //< @r-lyeh: added style_button param
|
|
{
|
|
struct nk_window *win;
|
|
const struct nk_input *in;
|
|
struct nk_rect header;
|
|
int is_clicked = nk_false;
|
|
nk_flags state;
|
|
|
|
NK_ASSERT(ctx);
|
|
NK_ASSERT(ctx->current);
|
|
NK_ASSERT(ctx->current->layout);
|
|
if (!ctx || !ctx->current || !ctx->current->layout)
|
|
return 0;
|
|
|
|
win = ctx->current;
|
|
state = nk_widget(&header, ctx);
|
|
if (!state) return 0;
|
|
in = (state == NK_WIDGET_ROM || win->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
|
|
if (nk_do_button_text(&ctx->last_widget_state, &win->buffer, header,
|
|
title, len, align, NK_BUTTON_DEFAULT, style_button, in, ctx->style.font))
|
|
is_clicked = nk_true;
|
|
return nk_menu_begin(ctx, win, title, is_clicked, header, size);
|
|
}
|
|
|
|
static
|
|
vec2 ui_toolbar_(array(ui_item_t) ui_items, vec2 ui_results) {
|
|
// adjust size for all the upcoming UI elements
|
|
// old method: nk_layout_row_dynamic(ui_ctx, UI_MENUBAR_ICON_HEIGHT/*h*/, array_count(ui_items));
|
|
{
|
|
const struct nk_style *style = &ui_ctx->style;
|
|
|
|
nk_layout_row_template_begin(ui_ctx, UI_MENUBAR_ICON_HEIGHT/*h*/);
|
|
for(int i = 0; i < array_count(ui_items); ++i) {
|
|
char first_token[512];
|
|
sscanf(ui_items[i].buf, "%[^,;|]", first_token); // @fixme: vsnscanf
|
|
|
|
char *tooltip = strchr(first_token, '@');
|
|
int len = tooltip ? (int)(tooltip - first_token /*- 1*/) : strlen(first_token);
|
|
|
|
float pixels_width = nk_text_width(ui_ctx, first_token, len);
|
|
pixels_width += style->window.header.label_padding.x * 2 + style->window.header.padding.x * 2;
|
|
if( pixels_width < 5 ) pixels_width = 5;
|
|
nk_layout_row_template_push_static(ui_ctx, pixels_width);
|
|
}
|
|
nk_layout_row_template_end(ui_ctx);
|
|
}
|
|
|
|
// display the UI elements
|
|
bool has_popups = ui_popups();
|
|
for( int i = 0, end = array_count(ui_items); i < end; ++i ) {
|
|
array(char*) ids = strsplit(ui_items[i].buf, ",;|");
|
|
|
|
// transparent style
|
|
static struct nk_style_button transparent_style;
|
|
do_once transparent_style = ui_ctx->style.button;
|
|
do_once transparent_style.normal.data.color = nk_rgba(0,0,0,0);
|
|
do_once transparent_style.border_color = nk_rgba(0,0,0,0);
|
|
do_once transparent_style.active = transparent_style.normal;
|
|
do_once transparent_style.hover = transparent_style.normal;
|
|
do_once transparent_style.hover.data.color = nk_rgba(0,0,0,127);
|
|
transparent_style.text_alignment = NK_TEXT_ALIGN_CENTERED|NK_TEXT_ALIGN_MIDDLE; // array_count(ids) > 1 ? NK_TEXT_ALIGN_LEFT : NK_TEXT_ALIGN_CENTERED;
|
|
|
|
char *tooltip = strchr(ids[0], '@');
|
|
int len = tooltip ? (int)(tooltip - ids[0]) : strlen(ids[0]);
|
|
|
|
// single button
|
|
if( array_count(ids) == 1 ) {
|
|
// tooltip
|
|
if( tooltip && !has_popups ) {
|
|
struct nk_rect bounds = nk_widget_bounds(ui_ctx);
|
|
if (nk_input_is_mouse_hovering_rect(&ui_ctx->input, bounds) && nk_window_has_focus(ui_ctx)) {
|
|
nk_tooltip(ui_ctx, tooltip+1);
|
|
}
|
|
}
|
|
// input...
|
|
if( ui_items[i].type == 0xED17 ) {
|
|
int active = nk_edit_string_zero_terminated(ui_ctx, NK_EDIT_AUTO_SELECT|NK_EDIT_CLIPBOARD|NK_EDIT_FIELD/*NK_EDIT_BOX*/|NK_EDIT_SIG_ENTER, ui_items[i].buf, ui_items[i].bufcap, nk_filter_default);
|
|
if( !!(active & NK_EDIT_COMMITED) ) ui_results = vec2(i+1, 0+1), nk_edit_unfocus(ui_ctx);
|
|
}
|
|
else
|
|
// ... else text
|
|
if( nk_button_text_styled(ui_ctx, &transparent_style, ids[0], len) ) {
|
|
ui_results = vec2(i+1, 0+1);
|
|
}
|
|
}
|
|
else {
|
|
struct nk_vec2 dims = {120, array_count(ids) * UI_MENUROW_HEIGHT};
|
|
const struct nk_style *style = &ui_ctx->style;
|
|
const struct nk_user_font *f = style->font;
|
|
static array(float) lens = 0; array_resize(lens, array_count(ids));
|
|
lens[0] = len;
|
|
for( int j = 1; j < array_count(ids); ++j ) {
|
|
lens[j] = strlen(ids[j]);
|
|
float width_px = f->width(f->userdata, f->height, ids[j], lens[j]);
|
|
dims.x = maxf(dims.x, width_px);
|
|
}
|
|
dims.x += 2 * style->window.header.label_padding.x;
|
|
|
|
// dropdown menu
|
|
if( nk_menu_begin_text_styled(ui_ctx, ids[0], lens[0], NK_TEXT_ALIGN_CENTERED|NK_TEXT_ALIGN_MIDDLE, dims, &transparent_style) ) {
|
|
nk_layout_row_dynamic(ui_ctx, 0, 1);
|
|
|
|
for( int j = 1; j < array_count(ids); ++j ) {
|
|
char *item = ids[j];
|
|
if( *item == '-' ) {
|
|
while(*item == '-') ++item, --lens[j];
|
|
//nk_menu_item_label(ui_ctx, "---", NK_TEXT_LEFT);
|
|
ui_separator_line();
|
|
}
|
|
|
|
if( nk_menu_item_text(ui_ctx, item, lens[j], NK_TEXT_LEFT) ) {
|
|
ui_results = vec2(i+1, j+1-1);
|
|
}
|
|
}
|
|
|
|
nk_menu_end(ui_ctx);
|
|
}
|
|
}
|
|
}
|
|
|
|
return ui_results;
|
|
}
|
|
|
|
int ui_toolbar(const char *icons) { // usage: int clicked_icon = ui_toolbar( ICON_1 ";" ICON_2 ";" ICON_3 ";" ICON_4 );
|
|
vec2 results = {0};
|
|
array(char*) items = strsplit(icons, ",;|");
|
|
static array(ui_item_t) temp = 0;
|
|
array_resize(temp, array_count(items));
|
|
for( int i = 0; i < array_count(items); ++i ) temp[i].buf = items[i], temp[i].bufcap = 0, temp[i].type = 0;
|
|
return ui_toolbar_(temp, results).x;
|
|
}
|
|
|
|
|
|
// UI Windows handlers. These are not OS Windows but UI Windows instead. For OS Windows check window_*() API.
|
|
|
|
#ifndef WINDOWS_INI
|
|
#define WINDOWS_INI editor_path("windows.ini")
|
|
#endif
|
|
|
|
static map(char*,unsigned) ui_windows = 0;
|
|
|
|
static void ui_init() {
|
|
do_once {
|
|
nk_config_custom_fonts();
|
|
nk_config_custom_theme();
|
|
|
|
map_init(ui_windows, less_str, hash_str);
|
|
}
|
|
}
|
|
|
|
static int ui_window_register(const char *panel_or_window_title) {
|
|
unsigned *state = map_find_or_add_allocated_key(ui_windows, STRDUP(panel_or_window_title), 0);
|
|
|
|
// check for visibility flag on first call
|
|
int visible = 0;
|
|
if( *state == 0 ) {
|
|
static ini_t i = 0;
|
|
do_once i = ini(WINDOWS_INI); // @leak
|
|
char **found = i ? map_find(i, va("%s.visible", panel_or_window_title)) : NULL;
|
|
if( found ) visible = (*found)[0] == '1';
|
|
}
|
|
|
|
*state |= 2;
|
|
return visible;
|
|
}
|
|
int ui_visible(const char *panel_or_window_title) {
|
|
return *map_find_or_add_allocated_key(ui_windows, STRDUP(panel_or_window_title), 0) & 1;
|
|
}
|
|
int ui_show(const char *panel_or_window_title, int enabled) {
|
|
unsigned *found = map_find_or_add_allocated_key(ui_windows, STRDUP(panel_or_window_title), 0);
|
|
if( enabled ) {
|
|
*found |= 1;
|
|
nk_window_collapse(ui_ctx, panel_or_window_title, NK_MAXIMIZED); // in case windows was previously collapsed
|
|
} else {
|
|
*found &= ~1;
|
|
}
|
|
return !!enabled;
|
|
}
|
|
int ui_dims(const char *panel_or_window_title, float width, float height) {
|
|
nk_window_set_size(ui_ctx, panel_or_window_title, (struct nk_vec2){width, height});
|
|
return 0;
|
|
}
|
|
vec2 ui_get_dims() {
|
|
return (vec2){nk_window_get_width(ui_ctx), nk_window_get_height(ui_ctx)};
|
|
}
|
|
static char *ui_build_window_list() {
|
|
char *build_windows_menu = 0;
|
|
strcatf(&build_windows_menu, "%s;", ICON_MD_VIEW_QUILT); // "Windows");
|
|
for each_map_ptr_sorted(ui_windows, char*, k, unsigned, v) {
|
|
strcatf(&build_windows_menu, "%s %s;", ui_visible(*k) ? ICON_MD_CHECK_BOX : ICON_MD_CHECK_BOX_OUTLINE_BLANK, *k); // ICON_MD_VISIBILITY : ICON_MD_VISIBILITY_OFF, *k); // ICON_MD_TOGGLE_ON : ICON_MD_TOGGLE_OFF, *k);
|
|
}
|
|
strcatf(&build_windows_menu, "-%s;%s", ICON_MD_RECYCLING " Reset layout", ICON_MD_SAVE_AS " Save layout");
|
|
return build_windows_menu; // @leak if discarded
|
|
}
|
|
static int ui_layout_all_reset(const char *mask);
|
|
static int ui_layout_all_save_disk(const char *mask);
|
|
static int ui_layout_all_load_disk(const char *mask);
|
|
|
|
|
|
static
|
|
void ui_menu_render() {
|
|
// clean up from past frame
|
|
ui_results = vec2(0,0);
|
|
if( !ui_items ) return;
|
|
if( !array_count(ui_items) ) return;
|
|
|
|
// artificially inject Windows menu on the first icon
|
|
bool show_window_menu = !!array_count(ui_items);
|
|
if( show_window_menu ) {
|
|
array_push_front(ui_items, ((ui_item_t){ui_build_window_list(), 0, 0}));
|
|
}
|
|
|
|
// process menus
|
|
if( nk_begin(ui_ctx, "Menu", nk_rect(0, 0, window_width(), UI_MENUROW_HEIGHT), NK_WINDOW_NO_SCROLLBAR/*|NK_WINDOW_BACKGROUND*/)) {
|
|
if( ui_ctx->current ) {
|
|
nk_menubar_begin(ui_ctx);
|
|
|
|
ui_results = ui_toolbar_(ui_items, ui_results);
|
|
|
|
//nk_layout_row_end(ui_ctx);
|
|
nk_menubar_end(ui_ctx);
|
|
}
|
|
}
|
|
nk_end(ui_ctx);
|
|
|
|
if( show_window_menu ) {
|
|
// if clicked on first menu (Windows)
|
|
if( ui_results.x == 1 ) {
|
|
array(char*) split = strsplit(ui_items[0].buf,";"); // *array_back(ui_items), ";");
|
|
const char *title = split[(int)ui_results.y]; title += title[0] == '-'; title += 3 * (title[0] == '\xee'); title += title[0] == ' '; /*skip separator+emoji+space*/
|
|
// toggle window unless clicked on lasts items {"reset layout", "save layout"}
|
|
bool clicked_reset_layout = ui_results.y == array_count(split) - 2;
|
|
bool clicked_save_layout = ui_results.y == array_count(split) - 1;
|
|
/**/ if( clicked_reset_layout ) ui_layout_all_reset("*");
|
|
else if( clicked_save_layout ) file_delete(WINDOWS_INI), ui_layout_all_save_disk("*");
|
|
else ui_show(title, ui_visible(title) ^ true);
|
|
// reset value so developers don't catch this click
|
|
ui_results = vec2(0,0);
|
|
}
|
|
// restore state prior to previously injected Windows menu
|
|
else
|
|
ui_results.x = ui_results.x > 0 ? ui_results.x - 1 : 0;
|
|
}
|
|
|
|
// clean up for next frame
|
|
for( int i = 0; i < array_count(ui_items); ++i ) {
|
|
if(ui_items[i].type != 0xED17)
|
|
FREE(ui_items[i].buf);
|
|
}
|
|
array_resize(ui_items, 0);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
static int ui_dirty = 1;
|
|
static int ui_has_active_popups = 0;
|
|
static float ui_hue = 0; // hue
|
|
static int ui_is_hover = 0;
|
|
static int ui_is_active = 0;
|
|
static uint64_t ui_active_mask = 0;
|
|
|
|
int ui_popups() {
|
|
return ui_has_active_popups;
|
|
}
|
|
int ui_hover() {
|
|
return ui_is_hover;
|
|
}
|
|
int ui_active() {
|
|
return ui_is_active; //window_has_cursor() && nk_window_is_any_hovered(ui_ctx) && nk_item_is_any_active(ui_ctx);
|
|
}
|
|
|
|
static
|
|
int ui_set_enable_(int enabled) {
|
|
static struct nk_style off, on;
|
|
do_once {
|
|
off = on = ui_ctx->style;
|
|
float alpha = 0.5f;
|
|
|
|
off.text.color.a *= alpha;
|
|
|
|
#if 0
|
|
off.button.normal.data.color.a *= alpha;
|
|
off.button.hover.data.color.a *= alpha;
|
|
off.button.active.data.color.a *= alpha;
|
|
off.button.border_color.a *= alpha;
|
|
off.button.text_background.a *= alpha;
|
|
off.button.text_normal.a *= alpha;
|
|
off.button.text_hover.a *= alpha;
|
|
off.button.text_active.a *= alpha;
|
|
|
|
off.contextual_button.normal.data.color.a *= alpha;
|
|
off.contextual_button.hover.data.color.a *= alpha;
|
|
off.contextual_button.active.data.color.a *= alpha;
|
|
off.contextual_button.border_color.a *= alpha;
|
|
off.contextual_button.text_background.a *= alpha;
|
|
off.contextual_button.text_normal.a *= alpha;
|
|
off.contextual_button.text_hover.a *= alpha;
|
|
off.contextual_button.text_active.a *= alpha;
|
|
#endif
|
|
off.menu_button.normal.data.color.a *= alpha;
|
|
off.menu_button.hover.data.color.a *= alpha;
|
|
off.menu_button.active.data.color.a *= alpha;
|
|
off.menu_button.border_color.a *= alpha;
|
|
off.menu_button.text_background.a *= alpha;
|
|
off.menu_button.text_normal.a *= alpha;
|
|
off.menu_button.text_hover.a *= alpha;
|
|
off.menu_button.text_active.a *= alpha;
|
|
#if 0
|
|
off.option.normal.data.color.a *= alpha;
|
|
off.option.hover.data.color.a *= alpha;
|
|
off.option.active.data.color.a *= alpha;
|
|
off.option.border_color.a *= alpha;
|
|
off.option.cursor_normal.data.color.a *= alpha;
|
|
off.option.cursor_hover.data.color.a *= alpha;
|
|
off.option.text_normal.a *= alpha;
|
|
off.option.text_hover.a *= alpha;
|
|
off.option.text_active.a *= alpha;
|
|
off.option.text_background.a *= alpha;
|
|
|
|
off.checkbox.normal.data.color.a *= alpha;
|
|
off.checkbox.hover.data.color.a *= alpha;
|
|
off.checkbox.active.data.color.a *= alpha;
|
|
off.checkbox.border_color.a *= alpha;
|
|
off.checkbox.cursor_normal.data.color.a *= alpha;
|
|
off.checkbox.cursor_hover.data.color.a *= alpha;
|
|
off.checkbox.text_normal.a *= alpha;
|
|
off.checkbox.text_hover.a *= alpha;
|
|
off.checkbox.text_active.a *= alpha;
|
|
off.checkbox.text_background.a *= alpha;
|
|
|
|
off.selectable.normal.data.color.a *= alpha;
|
|
off.selectable.hover.data.color.a *= alpha;
|
|
off.selectable.pressed.data.color.a *= alpha;
|
|
off.selectable.normal_active.data.color.a *= alpha;
|
|
off.selectable.hover_active.data.color.a *= alpha;
|
|
off.selectable.pressed_active.data.color.a *= alpha;
|
|
off.selectable.text_normal.a *= alpha;
|
|
off.selectable.text_hover.a *= alpha;
|
|
off.selectable.text_pressed.a *= alpha;
|
|
off.selectable.text_normal_active.a *= alpha;
|
|
off.selectable.text_hover_active.a *= alpha;
|
|
off.selectable.text_pressed_active.a *= alpha;
|
|
off.selectable.text_background.a *= alpha;
|
|
|
|
off.slider.normal.data.color.a *= alpha;
|
|
off.slider.hover.data.color.a *= alpha;
|
|
off.slider.active.data.color.a *= alpha;
|
|
off.slider.border_color.a *= alpha;
|
|
off.slider.bar_normal.a *= alpha;
|
|
off.slider.bar_hover.a *= alpha;
|
|
off.slider.bar_active.a *= alpha;
|
|
off.slider.bar_filled.a *= alpha;
|
|
off.slider.cursor_normal.data.color.a *= alpha;
|
|
off.slider.cursor_hover.data.color.a *= alpha;
|
|
off.slider.cursor_active.data.color.a *= alpha;
|
|
off.slider.dec_button.normal.data.color.a *= alpha;
|
|
off.slider.dec_button.hover.data.color.a *= alpha;
|
|
off.slider.dec_button.active.data.color.a *= alpha;
|
|
off.slider.dec_button.border_color.a *= alpha;
|
|
off.slider.dec_button.text_background.a *= alpha;
|
|
off.slider.dec_button.text_normal.a *= alpha;
|
|
off.slider.dec_button.text_hover.a *= alpha;
|
|
off.slider.dec_button.text_active.a *= alpha;
|
|
off.slider.inc_button.normal.data.color.a *= alpha;
|
|
off.slider.inc_button.hover.data.color.a *= alpha;
|
|
off.slider.inc_button.active.data.color.a *= alpha;
|
|
off.slider.inc_button.border_color.a *= alpha;
|
|
off.slider.inc_button.text_background.a *= alpha;
|
|
off.slider.inc_button.text_normal.a *= alpha;
|
|
off.slider.inc_button.text_hover.a *= alpha;
|
|
off.slider.inc_button.text_active.a *= alpha;
|
|
|
|
off.progress.normal.data.color.a *= alpha;
|
|
off.progress.hover.data.color.a *= alpha;
|
|
off.progress.active.data.color.a *= alpha;
|
|
off.progress.border_color.a *= alpha;
|
|
off.progress.cursor_normal.data.color.a *= alpha;
|
|
off.progress.cursor_hover.data.color.a *= alpha;
|
|
off.progress.cursor_active.data.color.a *= alpha;
|
|
off.progress.cursor_border_color.a *= alpha;
|
|
#endif
|
|
off.property.normal.data.color.a *= alpha;
|
|
off.property.hover.data.color.a *= alpha;
|
|
off.property.active.data.color.a *= alpha;
|
|
off.property.border_color.a *= alpha;
|
|
off.property.label_normal.a *= alpha;
|
|
off.property.label_hover.a *= alpha;
|
|
off.property.label_active.a *= alpha;
|
|
off.property.edit.normal.data.color.a *= alpha;
|
|
off.property.edit.hover.data.color.a *= alpha;
|
|
off.property.edit.active.data.color.a *= alpha;
|
|
off.property.edit.border_color.a *= alpha;
|
|
off.property.edit.cursor_normal.a *= alpha;
|
|
off.property.edit.cursor_hover.a *= alpha;
|
|
off.property.edit.cursor_text_normal.a *= alpha;
|
|
off.property.edit.cursor_text_hover.a *= alpha;
|
|
off.property.edit.text_normal.a *= alpha;
|
|
off.property.edit.text_hover.a *= alpha;
|
|
off.property.edit.text_active.a *= alpha;
|
|
off.property.edit.selected_normal.a *= alpha;
|
|
off.property.edit.selected_hover.a *= alpha;
|
|
off.property.edit.selected_text_normal.a *= alpha;
|
|
off.property.edit.selected_text_hover.a *= alpha;
|
|
off.property.dec_button.normal.data.color.a *= alpha;
|
|
off.property.dec_button.hover.data.color.a *= alpha;
|
|
off.property.dec_button.active.data.color.a *= alpha;
|
|
off.property.dec_button.border_color.a *= alpha;
|
|
off.property.dec_button.text_background.a *= alpha;
|
|
off.property.dec_button.text_normal.a *= alpha;
|
|
off.property.dec_button.text_hover.a *= alpha;
|
|
off.property.dec_button.text_active.a *= alpha;
|
|
off.property.inc_button.normal.data.color.a *= alpha;
|
|
off.property.inc_button.hover.data.color.a *= alpha;
|
|
off.property.inc_button.active.data.color.a *= alpha;
|
|
off.property.inc_button.border_color.a *= alpha;
|
|
off.property.inc_button.text_background.a *= alpha;
|
|
off.property.inc_button.text_normal.a *= alpha;
|
|
off.property.inc_button.text_hover.a *= alpha;
|
|
off.property.inc_button.text_active.a *= alpha;
|
|
|
|
off.edit.normal.data.color.a *= alpha;
|
|
off.edit.hover.data.color.a *= alpha;
|
|
off.edit.active.data.color.a *= alpha;
|
|
off.edit.border_color.a *= alpha;
|
|
off.edit.cursor_normal.a *= alpha;
|
|
off.edit.cursor_hover.a *= alpha;
|
|
off.edit.cursor_text_normal.a *= alpha;
|
|
off.edit.cursor_text_hover.a *= alpha;
|
|
off.edit.text_normal.a *= alpha;
|
|
off.edit.text_hover.a *= alpha;
|
|
off.edit.text_active.a *= alpha;
|
|
off.edit.selected_normal.a *= alpha;
|
|
off.edit.selected_hover.a *= alpha;
|
|
off.edit.selected_text_normal.a *= alpha;
|
|
off.edit.selected_text_hover.a *= alpha;
|
|
#if 0
|
|
off.chart.background.data.color.a *= alpha;
|
|
off.chart.border_color.a *= alpha;
|
|
off.chart.selected_color.a *= alpha;
|
|
off.chart.color.a *= alpha;
|
|
|
|
off.scrollh.normal.data.color.a *= alpha;
|
|
off.scrollh.hover.data.color.a *= alpha;
|
|
off.scrollh.active.data.color.a *= alpha;
|
|
off.scrollh.border_color.a *= alpha;
|
|
off.scrollh.cursor_normal.data.color.a *= alpha;
|
|
off.scrollh.cursor_hover.data.color.a *= alpha;
|
|
off.scrollh.cursor_active.data.color.a *= alpha;
|
|
off.scrollh.cursor_border_color.a *= alpha;
|
|
|
|
off.scrollv.normal.data.color.a *= alpha;
|
|
off.scrollv.hover.data.color.a *= alpha;
|
|
off.scrollv.active.data.color.a *= alpha;
|
|
off.scrollv.border_color.a *= alpha;
|
|
off.scrollv.cursor_normal.data.color.a *= alpha;
|
|
off.scrollv.cursor_hover.data.color.a *= alpha;
|
|
off.scrollv.cursor_active.data.color.a *= alpha;
|
|
off.scrollv.cursor_border_color.a *= alpha;
|
|
|
|
off.tab.background.data.color.a *= alpha;
|
|
off.tab.border_color.a *= alpha;
|
|
off.tab.text.a *= alpha;
|
|
#endif
|
|
off.combo.normal.data.color.a *= alpha;
|
|
off.combo.hover.data.color.a *= alpha;
|
|
off.combo.active.data.color.a *= alpha;
|
|
off.combo.border_color.a *= alpha;
|
|
off.combo.label_normal.a *= alpha;
|
|
off.combo.label_hover.a *= alpha;
|
|
off.combo.label_active.a *= alpha;
|
|
off.combo.symbol_normal.a *= alpha;
|
|
off.combo.symbol_hover.a *= alpha;
|
|
off.combo.symbol_active.a *= alpha;
|
|
off.combo.button.normal.data.color.a *= alpha;
|
|
off.combo.button.hover.data.color.a *= alpha;
|
|
off.combo.button.active.data.color.a *= alpha;
|
|
off.combo.button.border_color.a *= alpha;
|
|
off.combo.button.text_background.a *= alpha;
|
|
off.combo.button.text_normal.a *= alpha;
|
|
off.combo.button.text_hover.a *= alpha;
|
|
off.combo.button.text_active.a *= alpha;
|
|
#if 0
|
|
off.window.fixed_background.data.color.a *= alpha;
|
|
off.window.background.a *= alpha;
|
|
off.window.border_color.a *= alpha;
|
|
off.window.popup_border_color.a *= alpha;
|
|
off.window.combo_border_color.a *= alpha;
|
|
off.window.contextual_border_color.a *= alpha;
|
|
off.window.menu_border_color.a *= alpha;
|
|
off.window.group_border_color.a *= alpha;
|
|
off.window.tooltip_border_color.a *= alpha;
|
|
off.window.scaler.data.color.a *= alpha;
|
|
off.window.header.normal.data.color.a *= alpha;
|
|
off.window.header.hover.data.color.a *= alpha;
|
|
off.window.header.active.data.color.a *= alpha;
|
|
#endif
|
|
}
|
|
static struct nk_input input;
|
|
if (!enabled) {
|
|
ui_alpha_push(0.5);
|
|
ui_ctx->style = off; // .button = off.button;
|
|
input = ui_ctx->input;
|
|
memset(&ui_ctx->input, 0, sizeof(ui_ctx->input));
|
|
} else {
|
|
ui_alpha_pop();
|
|
ui_ctx->style = on; // .button = on.button;
|
|
ui_ctx->input = input;
|
|
}
|
|
return enabled;
|
|
}
|
|
|
|
static int ui_is_enabled = 1;
|
|
int ui_enable() {
|
|
return ui_is_enabled == 1 ? 0 : ui_set_enable_(ui_is_enabled = 1);
|
|
}
|
|
int ui_disable() {
|
|
return ui_is_enabled == 0 ? 0 : ui_set_enable_(ui_is_enabled = 0);
|
|
}
|
|
int ui_enabled() {
|
|
return ui_is_enabled;
|
|
}
|
|
|
|
static
|
|
void ui_destroy(void) {
|
|
if(ui_ctx) {
|
|
nk_glfw3_shutdown(&nk_glfw); // nk_sdl_shutdown();
|
|
ui_ctx = 0;
|
|
}
|
|
}
|
|
static
|
|
void ui_create() {
|
|
do_once atexit(ui_destroy);
|
|
|
|
if( ui_dirty ) {
|
|
nk_glfw3_new_frame(&nk_glfw); //g->nk_glfw);
|
|
ui_dirty = 0;
|
|
|
|
ui_enable();
|
|
}
|
|
}
|
|
|
|
enum {
|
|
UI_NOTIFICATION_1 = 32, // sets panel as 1-story notification. used by ui_notify()
|
|
UI_NOTIFICATION_2 = 64, // sets panel as 2-story notification. used by ui_notify()
|
|
};
|
|
|
|
struct ui_notify {
|
|
char *title;
|
|
char *body; // char *icon;
|
|
float timeout;
|
|
float alpha;
|
|
int used;
|
|
};
|
|
|
|
static array(struct ui_notify) ui_notifications; // format=("%d*%s\n%s", timeout, title, body)
|
|
|
|
static
|
|
void ui_notify_render() {
|
|
// draw queued notifications
|
|
if( array_count(ui_notifications) ) {
|
|
struct ui_notify *n = array_back(ui_notifications);
|
|
|
|
static double timeout = 0;
|
|
timeout += 1/60.f; // window_delta(); // @fixme: use editor_time() instead
|
|
|
|
ui_alpha_push( timeout >= n->timeout ? 1 - clampf(timeout - n->timeout,0,1) : 1 );
|
|
|
|
if( timeout < (n->timeout + 1) ) { // N secs display + 1s fadeout
|
|
if(n->used++ < 3) nk_window_set_focus(ui_ctx, "!notify");
|
|
|
|
if( ui_panel( "!notify", n->title && n->body ? UI_NOTIFICATION_2 : UI_NOTIFICATION_1 ) ) {
|
|
if(n->title) ui_label(n->title);
|
|
if(n->body) ui_label(n->body);
|
|
|
|
ui_panel_end();
|
|
}
|
|
}
|
|
|
|
if( timeout >= (n->timeout + 2) ) { // 1s fadeout + 1s idle
|
|
timeout = 0;
|
|
|
|
if(n->title) FREE(n->title);
|
|
if(n->body) FREE(n->body);
|
|
array_pop(ui_notifications);
|
|
}
|
|
|
|
ui_alpha_pop();
|
|
}
|
|
}
|
|
|
|
static
|
|
void ui_hue_cycle( unsigned num_cycles ) {
|
|
// cycle color (phi ratio)
|
|
for( unsigned i = 0; i < num_cycles; ++i ) {
|
|
//ui_hue = (ui_hue+0.61803f)*1.61803f; while(ui_hue > 1) ui_hue -= 1;
|
|
ui_hue *= 1.61803f / 1.85f; while(ui_hue > 1) ui_hue -= 1;
|
|
}
|
|
}
|
|
|
|
static bool win_debug_visible = true;
|
|
|
|
static
|
|
void ui_render() {
|
|
|
|
// draw queued menus
|
|
ui_notify_render();
|
|
ui_menu_render();
|
|
|
|
/* IMPORTANT: `nk_sdl_render` modifies some global OpenGL state
|
|
* with blending, scissor, face culling, depth test and viewport and
|
|
* defaults everything back into a default state.
|
|
* Make sure to either a.) save and restore or b.) reset your own state after
|
|
* rendering the UI. */
|
|
//nk_sdl_render(NK_ANTI_ALIASING_ON, MAX_VERTEX_MEMORY, MAX_ELEMENT_MEMORY);
|
|
|
|
if (win_debug_visible) {
|
|
GLfloat bkColor[4]; glGetFloatv(GL_COLOR_CLEAR_VALUE, bkColor); // @transparent
|
|
glClearColor(0,0,0,1); // @transparent
|
|
glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,!bkColor[3] ? GL_TRUE : GL_FALSE); // @transparent
|
|
nk_glfw3_render(&nk_glfw, NK_ANTI_ALIASING_ON, MAX_VERTEX_MEMORY, MAX_ELEMENT_MEMORY);
|
|
glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE); // @transparent
|
|
} else {
|
|
nk_clear(&nk_glfw.ctx);
|
|
}
|
|
|
|
#if is(ems)
|
|
glFinish();
|
|
#endif
|
|
|
|
ui_dirty = 1;
|
|
ui_hue = 0;
|
|
|
|
ui_is_hover = nk_window_is_any_hovered(ui_ctx) && window_has_cursor();
|
|
|
|
if(input_down(MOUSE_L))
|
|
ui_is_active = (ui_is_hover && nk_item_is_any_active(ui_ctx));
|
|
if(input_up(MOUSE_L))
|
|
ui_is_active = 0;
|
|
}
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// save/restore all window layouts on every framebuffer resize
|
|
|
|
#define UI_SNAP_PX 1 /*treshold of pixels when snapping panels/windows to the application borders [1..N]*/
|
|
#define UI_ANIM_ALPHA 0.9 /*animation alpha used when restoring panels/windows state from application resizing events: [0..1]*/
|
|
//#define UI_MENUBAR_Y 32 // menubar and row
|
|
|
|
typedef struct ui_layout {
|
|
const char *title;
|
|
|
|
bool is_panel;
|
|
|
|
vec2 desktop;
|
|
vec2 p0,p1;
|
|
float l0,l1;
|
|
|
|
float alpha;
|
|
float anim_timer;
|
|
|
|
} ui_layout;
|
|
|
|
static array(ui_layout) ui_layouts[2] = {0};
|
|
|
|
static
|
|
int ui_layout_find(const char *title, bool is_panel) {
|
|
int i = 0;
|
|
for each_array_ptr(ui_layouts[is_panel], ui_layout, s) {
|
|
if( !strcmp(title, s->title) ) return i;
|
|
++i;
|
|
}
|
|
ui_layout s = {0};
|
|
s.is_panel = is_panel;
|
|
s.title = STRDUP(title);
|
|
array_push(ui_layouts[is_panel], s);
|
|
return array_count(ui_layouts[is_panel]) - 1;
|
|
}
|
|
|
|
static
|
|
void ui_layout_save_mem(int idx, vec2 desktop, float workarea_h, struct nk_rect *xywh_, bool is_panel) {
|
|
struct nk_rect xywh = *xywh_; //< workaround for a (tcc-0.9.27+lubuntu16) bug, where xywh_ is never populated (ie, empty always) when passed by-copy
|
|
|
|
ui_layout *s = &ui_layouts[is_panel][idx];
|
|
|
|
struct nk_window *win = nk_window_find(ui_ctx, s->title);
|
|
// if(win && win->flags & NK_WINDOW_FULLSCREEN) return; // skip if maximized
|
|
|
|
s->desktop = desktop;
|
|
|
|
float excess = 0;
|
|
if( win && (win->flags & NK_WINDOW_MINIMIZED)) {
|
|
excess = xywh.h - UI_MENUROW_HEIGHT;
|
|
xywh.h = UI_MENUROW_HEIGHT;
|
|
}
|
|
|
|
// sanity checks
|
|
if(xywh.x<0) xywh.x = 0;
|
|
if(xywh.w>desktop.w-UI_SNAP_PX) xywh.w = desktop.w-UI_SNAP_PX-1;
|
|
|
|
if(xywh.y<workarea_h) xywh.y = workarea_h;
|
|
if(xywh.h>desktop.h-workarea_h-UI_SNAP_PX) xywh.h = desktop.h-workarea_h-UI_SNAP_PX-1;
|
|
|
|
if((xywh.x+xywh.w)>desktop.w) xywh.x-= xywh.x+xywh.w-desktop.w;
|
|
if((xywh.y+xywh.h)>desktop.h) xywh.y-= xywh.y+xywh.h-desktop.h;
|
|
|
|
if( win && (win->flags & NK_WINDOW_MINIMIZED)) {
|
|
xywh.h += excess;
|
|
}
|
|
|
|
// build reconstruction vectors from bottom-right corner
|
|
s->p0 = vec2(xywh.x/s->desktop.x,xywh.y/s->desktop.y);
|
|
s->p1 = vec2(xywh.w/s->desktop.x,xywh.h/s->desktop.y);
|
|
s->p0 = sub2(s->p0, vec2(1,1)); s->l0 = len2(s->p0);
|
|
s->p1 = sub2(s->p1, vec2(1,1)); s->l1 = len2(s->p1);
|
|
}
|
|
|
|
static
|
|
struct nk_rect ui_layout_load_mem(int idx, vec2 desktop, bool is_panel) {
|
|
ui_layout *s = &ui_layouts[is_panel][idx];
|
|
|
|
// extract reconstruction coords from bottom-right corner
|
|
vec2 p0 = mul2(add2(vec2(1,1), scale2(norm2(s->p0), s->l0)), desktop);
|
|
vec2 p1 = mul2(add2(vec2(1,1), scale2(norm2(s->p1), s->l1)), desktop);
|
|
|
|
return nk_rect( p0.x, p0.y, p1.x, p1.y );
|
|
}
|
|
|
|
static
|
|
int ui_layout_all_reset(const char *mask) {
|
|
ui_layout z = {0};
|
|
|
|
vec2 desktop = vec2(window_width(), window_height());
|
|
float workarea_h = ui_has_menubar()*UI_MENUROW_HEIGHT; // @fixme workarea -> reserved_area
|
|
|
|
for( int is_panel = 0; is_panel < 2; ++is_panel ) {
|
|
for( int j = 0; j < array_count(ui_layouts[is_panel]); ++j ) {
|
|
if( ui_layouts[is_panel][j].title ) {
|
|
|
|
if( nk_window_is_hidden(ui_ctx, ui_layouts[is_panel][j].title) ) continue;
|
|
|
|
struct nk_rect xywh = { 0, workarea_h + j * UI_MENUROW_HEIGHT, desktop.w / 3.333, UI_MENUROW_HEIGHT };
|
|
if( is_panel ) {
|
|
xywh.x = 0;
|
|
xywh.y = workarea_h + j * UI_MENUROW_HEIGHT;
|
|
xywh.w = desktop.w / 4;
|
|
xywh.h = desktop.h / 3;
|
|
} else {
|
|
xywh.x = desktop.w / 3.00 + j * UI_MENUROW_HEIGHT;
|
|
xywh.y = workarea_h + j * UI_MENUROW_HEIGHT;
|
|
xywh.w = desktop.w / 4;
|
|
xywh.h = desktop.h / 3;
|
|
}
|
|
nk_window_set_focus(ui_ctx, ui_layouts[is_panel][j].title);
|
|
nk_window_collapse(ui_ctx, ui_layouts[is_panel][j].title, is_panel ? 0 : 1);
|
|
struct nk_window* win = is_panel ? 0 : nk_window_find(ui_ctx, ui_layouts[is_panel][j].title );
|
|
if(win) win->flags &= ~NK_WINDOW_FULLSCREEN;
|
|
if(win) win->flags &= ~NK_WINDOW_MINIMIZED;
|
|
ui_layout_save_mem(j, desktop, workarea_h, &xywh, is_panel);
|
|
ui_layouts[is_panel][j].anim_timer = 1.0;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static
|
|
int ui_layout_all_save_disk(const char *mask) {
|
|
float w = window_width(), h = window_height();
|
|
for each_map_ptr_sorted(ui_windows, char*, k, unsigned, v) {
|
|
struct nk_window *win = nk_window_find(ui_ctx, *k);
|
|
if( win && strmatchi(*k, mask) ) {
|
|
ini_write(WINDOWS_INI, *k, "x", va("%f", win->bounds.x / w ));
|
|
ini_write(WINDOWS_INI, *k, "y", va("%f", win->bounds.y / h ));
|
|
ini_write(WINDOWS_INI, *k, "w", va("%f", win->bounds.w / w ));
|
|
ini_write(WINDOWS_INI, *k, "h", va("%f", win->bounds.h / h ));
|
|
ini_write(WINDOWS_INI, *k, "visible", ui_visible(*k) ? "1":"0");
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static
|
|
const char *ui_layout_load_disk(const char *title, const char *mask, ini_t i, struct nk_rect *r) {
|
|
if(!i) return 0;
|
|
|
|
const char *dot = strrchr(title, '.');
|
|
if( dot ) title = va("%.*s", (int)(dot - title), title);
|
|
if( !strmatchi(title, mask) ) return 0;
|
|
|
|
char **x = map_find(i, va("%s.x", title));
|
|
char **y = map_find(i, va("%s.y", title));
|
|
char **w = map_find(i, va("%s.w", title));
|
|
char **h = map_find(i, va("%s.h", title));
|
|
if( x && y && w && h ) {
|
|
float ww = window_width(), wh = window_height();
|
|
r->x = atof(*x) * ww;
|
|
r->y = atof(*y) * wh;
|
|
r->w = atof(*w) * ww;
|
|
r->h = atof(*h) * wh;
|
|
|
|
char **on = map_find(i, va("%s.visible", title));
|
|
|
|
return title;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static
|
|
int ui_layout_all_load_disk(const char *mask) {
|
|
ini_t i = ini(WINDOWS_INI); // @leak
|
|
if( !i ) return 0;
|
|
for each_map(i, char*, k, char*, v) {
|
|
struct nk_rect out = {0};
|
|
const char *title = ui_layout_load_disk(k, mask, i, &out);
|
|
if( title ) {
|
|
struct nk_window *win = nk_window_find(ui_ctx, title);
|
|
if( win ) {
|
|
win->bounds.x = out.x;
|
|
win->bounds.y = out.y;
|
|
win->bounds.w = out.w;
|
|
win->bounds.h = out.h;
|
|
}
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// shared code for both panels and windows. really messy.
|
|
|
|
static
|
|
int ui_begin_panel_or_window_(const char *title, int flags, bool is_window) {
|
|
|
|
struct nk_window *win = nk_window_find(ui_ctx, title);
|
|
|
|
int is_panel = !is_window;
|
|
bool starts_minimized = is_panel ? !(flags & PANEL_OPEN) : 0;
|
|
bool is_closable = is_window;
|
|
bool is_scalable = true;
|
|
bool is_movable = true;
|
|
bool is_auto_minimizes = starts_minimized; // false;
|
|
bool is_pinned = win && (win->flags & NK_WINDOW_PINNED);
|
|
|
|
if( is_pinned ) {
|
|
// is_closable = false;
|
|
is_auto_minimizes = false;
|
|
is_scalable = false;
|
|
// is_movable = false;
|
|
}
|
|
|
|
ui_create();
|
|
|
|
uint64_t hash = 14695981039346656037ULL, mult = 0x100000001b3ULL;
|
|
for(int i = 0; title[i]; ++i) hash = (hash ^ title[i]) * mult;
|
|
ui_hue = (hash & 0x3F) / (float)0x3F; ui_hue += !ui_hue;
|
|
|
|
int idx = ui_layout_find(title, is_panel);
|
|
ui_layout *s = &ui_layouts[is_panel][idx];
|
|
|
|
vec2 desktop = vec2(window_width(), window_height());
|
|
float workarea_h = ui_has_menubar()*UI_MENUROW_HEIGHT;
|
|
|
|
int row = idx + !!ui_has_menubar(); // add 1 to skip menu
|
|
vec2 offset = vec2(0, UI_ROW_HEIGHT*row);
|
|
float w = desktop.w / 3.33, h = (flags & UI_NOTIFICATION_2 ? UI_MENUROW_HEIGHT*2 : (flags & UI_NOTIFICATION_1 ? UI_MENUROW_HEIGHT : desktop.h - offset.y * 2 - 1)); // h = desktop.h * 0.66; //
|
|
struct nk_rect start_coords = {offset.x, offset.y, offset.x+w, offset.y+h};
|
|
|
|
if(is_window) {
|
|
w = desktop.w / 1.5;
|
|
h = desktop.h / 1.5;
|
|
start_coords.x = (desktop.w-w)/2;
|
|
start_coords.y = (desktop.h-h)/2 + workarea_h/2;
|
|
start_coords.w = w;
|
|
start_coords.h = h;
|
|
}
|
|
|
|
static vec2 edge = {0}; static int edge_type = 0; // [off],L,R,U,D
|
|
do_once edge = vec2(desktop.w * 0.33, desktop.h * 0.66);
|
|
|
|
// do not snap windows and/or save windows when using may be interacting with UI
|
|
int is_active = 0;
|
|
int mouse_pressed = !!input(MOUSE_L) && ui_ctx->active == win;
|
|
if( win ) {
|
|
// update global window activity bitmask
|
|
is_active = ui_ctx->active == win;
|
|
ui_active_mask = is_active ? ui_active_mask | (1ull << idx) : ui_active_mask & ~(1ull << idx);
|
|
}
|
|
|
|
// struct nk_style *s = &ui_ctx->style;
|
|
// nk_style_push_color(ui_ctx, &s->window.header.normal.data.color, nk_hsv_f(ui_hue,0.6,0.8));
|
|
|
|
// adjust inactive edges automatically
|
|
if( win ) {
|
|
bool group1_any = !is_active; // && !input(MOUSE_L);
|
|
bool group2_not_resizing = is_active && !win->is_window_resizing;
|
|
bool group2_interacting = is_active && input(MOUSE_L);
|
|
|
|
#if 0
|
|
if( group1_any ) {
|
|
// cancel self-adjust if this window is not overlapping the active one that is being resized at the moment
|
|
struct nk_window *parent = ui_ctx->active;
|
|
|
|
struct nk_rect a = win->bounds, b = parent->bounds;
|
|
bool overlap = a.x <= (b.x+b.w) && b.x <= (a.x+a.w) && a.y <= (b.y+b.h) && b.y <= (a.y+a.h);
|
|
|
|
group1_any = overlap;
|
|
}
|
|
#else
|
|
if( group1_any )
|
|
group1_any = !(win->flags & NK_WINDOW_PINNED);
|
|
#endif
|
|
|
|
if( group1_any ) {
|
|
float mouse_x = clampf(input(MOUSE_X), 0, desktop.w);
|
|
float mouse_y = clampf(input(MOUSE_Y), 0, desktop.h);
|
|
float distance_x = absf(mouse_x - win->bounds.x) / desktop.w;
|
|
float distance_y = absf(mouse_y - win->bounds.y) / desktop.h;
|
|
float alpha_x = sqrt(sqrt(distance_x)); // amplify signals a little bit: 0.1->0.56,0.5->0.84,0.98->0.99,etc
|
|
float alpha_y = sqrt(sqrt(distance_y));
|
|
|
|
/**/ if( (edge_type & 1) && win->bounds.x <= UI_SNAP_PX ) {
|
|
win->bounds.w = win->bounds.w * alpha_y + edge.w * (1-alpha_y);
|
|
}
|
|
else if( (edge_type & 2) && (win->bounds.x + win->bounds.w) >= (desktop.w-UI_SNAP_PX) ) {
|
|
win->bounds.w = win->bounds.w * alpha_y + edge.w * (1-alpha_y);
|
|
win->bounds.x = desktop.w - win->bounds.w;
|
|
}
|
|
if( (edge_type & 8) && (win->bounds.y + (win->flags & NK_WINDOW_MINIMIZED ? UI_ROW_HEIGHT : win->bounds.h)) >= (desktop.h-UI_SNAP_PX) ) {
|
|
win->bounds.h = win->bounds.h * alpha_x + edge.h * (1-alpha_x);
|
|
win->bounds.y = desktop.h - (win->flags & NK_WINDOW_MINIMIZED ? UI_ROW_HEIGHT : win->bounds.h);
|
|
}
|
|
}
|
|
|
|
// skip any saving if window is animating (moving) and/or maximized
|
|
bool anim_in_progress = s->anim_timer > 1e-3;
|
|
s->anim_timer *= anim_in_progress * UI_ANIM_ALPHA;
|
|
|
|
if( group1_any || !group2_interacting || anim_in_progress ) {
|
|
struct nk_rect target = ui_layout_load_mem(idx, desktop, is_panel);
|
|
float alpha = len2sq(sub2(s->desktop, desktop)) ? 0 : UI_ANIM_ALPHA; // smooth unless we're restoring a desktop change
|
|
#if 1
|
|
if( is_window && win->flags & NK_WINDOW_FULLSCREEN ) {
|
|
target.x = 1;
|
|
target.w = desktop.w - 1;
|
|
target.y = workarea_h + 1;
|
|
target.h = desktop.h - workarea_h - 2;
|
|
}
|
|
if( is_window && win->is_window_restoring > 1e-2) {
|
|
win->is_window_restoring = win->is_window_restoring * alpha + 0 * (1 - alpha);
|
|
target.w = desktop.w / 2;
|
|
target.h = (desktop.h - workarea_h) / 2;
|
|
target.x = (desktop.w - target.w) / 2;
|
|
target.y = ((desktop.h - workarea_h) - target.h) / 2;
|
|
}
|
|
#endif
|
|
win->bounds = nk_rect(
|
|
win->bounds.x * alpha + target.x * (1 - alpha),
|
|
win->bounds.y * alpha + target.y * (1 - alpha),
|
|
win->bounds.w * alpha + target.w * (1 - alpha),
|
|
win->bounds.h * alpha + target.h * (1 - alpha)
|
|
);
|
|
}
|
|
if(!anim_in_progress)
|
|
ui_layout_save_mem(idx, desktop, workarea_h, &win->bounds, is_panel);
|
|
} else { // if(!win)
|
|
ui_layout_save_mem(idx, desktop, workarea_h, &start_coords, is_panel);
|
|
}
|
|
|
|
|
|
int window_flags = NK_WINDOW_PINNABLE | NK_WINDOW_MINIMIZABLE | NK_WINDOW_NO_SCROLLBAR_X | (is_window ? NK_WINDOW_MAXIMIZABLE : 0);
|
|
if( starts_minimized ) window_flags |= (win ? 0 : NK_WINDOW_MINIMIZED);
|
|
if( is_auto_minimizes ) window_flags |= is_active ? 0 : !!starts_minimized * NK_WINDOW_MINIMIZED;
|
|
if( is_movable ) window_flags |= NK_WINDOW_MOVABLE;
|
|
if( is_closable ) window_flags |= NK_WINDOW_CLOSABLE;
|
|
if( is_scalable ) {
|
|
window_flags |= NK_WINDOW_SCALABLE;
|
|
if(win) window_flags |= input(MOUSE_X) < (win->bounds.x + win->bounds.w/2) ? NK_WINDOW_SCALE_LEFT : 0;
|
|
if(win) window_flags |= input(MOUSE_Y) < (win->bounds.y + win->bounds.h/2) ? NK_WINDOW_SCALE_TOP : 0;
|
|
}
|
|
|
|
// if( is_pinned )
|
|
window_flags |= NK_WINDOW_BORDER;
|
|
|
|
if( is_panel && win && !is_active ) {
|
|
if( !is_pinned && is_auto_minimizes ) {
|
|
window_flags |= NK_WINDOW_MINIMIZED;
|
|
}
|
|
}
|
|
|
|
// if( is_modal ) window_flags &= ~(NK_WINDOW_MINIMIZED | NK_WINDOW_MINIMIZABLE);
|
|
if( is_panel && win ) {
|
|
// if( win->bounds.x > 0 && (win->bounds.x+win->bounds.w) < desktop.w-1 ) window_flags &= ~NK_WINDOW_MINIMIZED;
|
|
}
|
|
|
|
if(!win) { // if newly created window (!win)
|
|
// first time, try to restore from WINDOWS_INI file
|
|
static ini_t i; do_once i = ini(WINDOWS_INI); // @leak
|
|
ui_layout_load_disk(title, "*", i, &start_coords);
|
|
ui_layout_save_mem(idx, desktop, workarea_h, &start_coords, is_panel);
|
|
}
|
|
|
|
bool is_notify = flags & (UI_NOTIFICATION_1 | UI_NOTIFICATION_2);
|
|
if( is_notify ) {
|
|
window_flags = NK_WINDOW_MOVABLE | NK_WINDOW_NOT_INTERACTIVE | NK_WINDOW_NO_SCROLLBAR;
|
|
start_coords = nk_rect(desktop.w / 2 - w / 2, -h, w, h);
|
|
}
|
|
|
|
if( nk_begin(ui_ctx, title, start_coords, window_flags) ) {
|
|
|
|
// set width for all inactive panels
|
|
struct nk_rect bounds = nk_window_get_bounds(ui_ctx);
|
|
if( mouse_pressed && win && win->is_window_resizing ) {
|
|
edge = vec2(bounds.w, bounds.h);
|
|
|
|
// push direction
|
|
int top = !!(win->is_window_resizing & NK_WINDOW_SCALE_TOP);
|
|
int left = !!(win->is_window_resizing & NK_WINDOW_SCALE_LEFT), right = !left;
|
|
|
|
edge_type = 0;
|
|
/**/ if( right && (win->bounds.x <= UI_SNAP_PX) ) edge_type |= 1;
|
|
else if( left && (win->bounds.x + win->bounds.w) >= (desktop.w-UI_SNAP_PX) ) edge_type |= 2;
|
|
/**/ if( top && (win->bounds.y + win->bounds.h) >= (desktop.h-UI_SNAP_PX) ) edge_type |= 8;
|
|
|
|
// @fixme
|
|
// - if window is in a corner (sharing 2 edges), do not allow for multi edges. either vert or horiz depending on the clicked scaler
|
|
// - or maybe, only propagate edge changes to the other windows that overlapping our window.
|
|
}
|
|
|
|
return 1;
|
|
} else {
|
|
|
|
if(is_panel) {
|
|
ui_panel_end();
|
|
} else ui_window_end();
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static const char *ui_last_title = 0;
|
|
static int *ui_last_enabled = 0;
|
|
static int ui_has_window = 0;
|
|
static int ui_window_has_menubar = 0;
|
|
int ui_window(const char *title, int *enabled) {
|
|
if( window_width() <= 0 ) return 0;
|
|
if( window_height() <= 0 ) return 0;
|
|
if( !ui_ctx || !ui_ctx->style.font ) return 0;
|
|
|
|
bool forced_creation = enabled && *enabled; // ( enabled ? *enabled : !ui_has_menubar() );
|
|
forced_creation |= ui_window_register(title);
|
|
if(!ui_visible(title)) {
|
|
if( !forced_creation ) return 0;
|
|
ui_show(title, forced_creation);
|
|
}
|
|
|
|
ui_last_enabled = enabled;
|
|
ui_last_title = title;
|
|
ui_has_window = 1;
|
|
return ui_begin_panel_or_window_(title, /*flags*/0, true);
|
|
}
|
|
int ui_window_end() {
|
|
if(ui_window_has_menubar) nk_menubar_end(ui_ctx), ui_window_has_menubar = 0;
|
|
nk_end(ui_ctx), ui_has_window = 0;
|
|
|
|
int closed = 0;
|
|
if( nk_window_is_hidden(ui_ctx, ui_last_title) ) {
|
|
nk_window_close(ui_ctx, ui_last_title);
|
|
ui_show(ui_last_title, false);
|
|
if( ui_last_enabled ) *ui_last_enabled = 0; // clear developers' flag
|
|
closed = 1;
|
|
}
|
|
|
|
// @transparent
|
|
#if !is(ems)
|
|
static bool has_transparent_attrib = 0; do_once has_transparent_attrib = glfwGetWindowAttrib(window_handle(), GLFW_TRANSPARENT_FRAMEBUFFER) == GLFW_TRUE;
|
|
if( closed && has_transparent_attrib && !ui_has_menubar() ) {
|
|
bool any_open = 0;
|
|
for each_map_ptr(ui_windows, char*, k, unsigned, v) any_open |= *v & 1;
|
|
if( !any_open ) glfwSetWindowShouldClose(window_handle(), GLFW_TRUE);
|
|
}
|
|
#endif
|
|
// @transparent
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ui_panel(const char *title, int flags) {
|
|
if( window_width() <= 0 ) return 0;
|
|
if( window_height() <= 0 ) return 0;
|
|
if( !ui_ctx || !ui_ctx->style.font ) return 0;
|
|
|
|
if( ui_has_window ) {
|
|
// transparent style
|
|
static struct nk_style_button transparent_style;
|
|
do_once transparent_style = ui_ctx->style.button;
|
|
do_once transparent_style.normal.data.color = nk_rgba(0,0,0,0);
|
|
do_once transparent_style.border_color = nk_rgba(0,0,0,0);
|
|
do_once transparent_style.active = transparent_style.normal;
|
|
do_once transparent_style.hover = transparent_style.normal;
|
|
do_once transparent_style.hover.data.color = nk_rgba(0,0,0,127);
|
|
transparent_style.text_alignment = NK_TEXT_ALIGN_CENTERED|NK_TEXT_ALIGN_MIDDLE;
|
|
|
|
if(!ui_window_has_menubar) nk_menubar_begin(ui_ctx);
|
|
if(!ui_window_has_menubar) nk_layout_row_begin(ui_ctx, NK_STATIC, UI_MENUBAR_ICON_HEIGHT, 4);
|
|
if(!ui_window_has_menubar) nk_layout_row_push(ui_ctx, 70);
|
|
ui_window_has_menubar = 1;
|
|
|
|
return nk_menu_begin_text_styled(ui_ctx, title, strlen(title), NK_TEXT_ALIGN_CENTERED|NK_TEXT_ALIGN_MIDDLE, nk_vec2(220, 200), &transparent_style);
|
|
}
|
|
|
|
return ui_begin_panel_or_window_(title, flags, false);
|
|
}
|
|
int ui_panel_end() {
|
|
if( ui_has_window ) {
|
|
nk_menu_end(ui_ctx);
|
|
return 0;
|
|
}
|
|
nk_end(ui_ctx);
|
|
// nk_style_pop_color(ui_ctx);
|
|
return 0;
|
|
}
|
|
|
|
static unsigned ui_collapse_state = 0;
|
|
int ui_collapse(const char *label, const char *id) { // mask: 0(closed),1(open),2(created)
|
|
int start_open = label[0] == '!'; label += start_open;
|
|
|
|
uint64_t hash = 14695981039346656037ULL, mult = 0x100000001b3ULL;
|
|
for(int i = 0; id[i]; ++i) hash = (hash ^ id[i]) * mult;
|
|
ui_hue = (hash & 0x3F) / (float)0x3F; ui_hue += !ui_hue;
|
|
|
|
int forced = ui_filter && ui_filter[0];
|
|
enum nk_collapse_states forced_open = NK_MAXIMIZED;
|
|
|
|
ui_collapse_state = nk_tree_base_(ui_ctx, NK_TREE_NODE, 0, label, start_open ? NK_MAXIMIZED : NK_MINIMIZED, forced ? &forced_open : NULL, id, strlen(id), 0);
|
|
|
|
return ui_collapse_state & 1; // |1 open, |2 clicked, |4 toggled
|
|
}
|
|
int ui_collapse_clicked() {
|
|
return ui_collapse_state >> 1; // |1 clicked, |2 toggled
|
|
}
|
|
int ui_collapse_end() {
|
|
return nk_tree_pop(ui_ctx), 1;
|
|
}
|
|
|
|
|
|
int ui_contextual() {
|
|
#if 0
|
|
struct nk_rect bounds = nk_widget_bounds(ui_ctx); // = nk_window_get_bounds(ui_ctx);
|
|
bounds.y -= 25;
|
|
return ui_popups() ? 0 : nk_contextual_begin(ui_ctx, 0, nk_vec2(150, 300), bounds);
|
|
#else
|
|
return ui_popups() ? 0 : nk_contextual_begin(ui_ctx, 0, nk_vec2(300, 220), nk_window_get_bounds(ui_ctx));
|
|
#endif
|
|
}
|
|
int ui_contextual_end(int close) {
|
|
if(close) nk_contextual_close(ui_ctx);
|
|
nk_contextual_end(ui_ctx);
|
|
return 1;
|
|
}
|
|
int ui_submenu(const char *options) {
|
|
int choice = 0;
|
|
if( ui_contextual() ) {
|
|
array(char*) tokens = strsplit(options, ";");
|
|
for( int i = 0; i < array_count(tokens) ; ++i ) {
|
|
if( ui_button_transparent(tokens[i]) ) choice = i + 1;
|
|
}
|
|
ui_contextual_end(0);
|
|
}
|
|
return choice;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// code for all the widgets
|
|
|
|
static
|
|
int nk_button_transparent(struct nk_context *ctx, const char *text) {
|
|
static struct nk_style_button transparent_style;
|
|
do_once transparent_style = ctx->style.button;
|
|
do_once transparent_style.text_alignment = NK_TEXT_ALIGN_CENTERED|NK_TEXT_ALIGN_MIDDLE;
|
|
do_once transparent_style.normal.data.color = nk_rgba(0,0,0,0);
|
|
do_once transparent_style.border_color = nk_rgba(0,0,0,0);
|
|
do_once transparent_style.active = transparent_style.normal;
|
|
do_once transparent_style.hover = transparent_style.normal;
|
|
do_once transparent_style.hover.data.color = nk_rgba(0,0,0,127);
|
|
transparent_style.text_background.a = 255 * ui_alpha;
|
|
transparent_style.text_normal.a = 255 * ui_alpha;
|
|
transparent_style.text_hover.a = 255 * ui_alpha;
|
|
transparent_style.text_active.a = 255 * ui_alpha;
|
|
return nk_button_label_styled(ctx, &transparent_style, text);
|
|
}
|
|
|
|
// internal vars for our editor. @todo: maybe expose these to the end-user as well?
|
|
bool ui_label_icon_highlight;
|
|
vec2 ui_label_icon_clicked_L; // left
|
|
vec2 ui_label_icon_clicked_R; // right
|
|
|
|
static
|
|
int ui_label_(const char *label, int alignment) {
|
|
// beware: assuming label can start with any ICON_MD_ glyph, which I consider them to be a 3-bytes utf8 sequence.
|
|
// done for optimization reasons because this codepath is called a lot!
|
|
const char *icon = label ? label : ""; while( icon[0] == '!' || icon[0] == '*' ) ++icon;
|
|
int has_icon = (unsigned)icon[0] > 127, icon_len = 3, icon_width_px = 1*24;
|
|
|
|
struct nk_rect bounds = nk_widget_bounds(ui_ctx);
|
|
const struct nk_input *input = &ui_ctx->input;
|
|
int is_hovering = nk_input_is_mouse_hovering_rect(input, bounds) && !ui_has_active_popups;
|
|
if( is_hovering ) {
|
|
struct nk_rect winbounds = nk_window_get_bounds(ui_ctx);
|
|
is_hovering &= nk_input_is_mouse_hovering_rect(input, winbounds);
|
|
|
|
struct nk_window *win = ui_ctx->current;
|
|
bool has_contextual = !win->name; // contextual windows are annonymous
|
|
|
|
is_hovering &= has_contextual || nk_window_has_focus(ui_ctx);
|
|
}
|
|
|
|
int skip_color_tab = label && label[0] == '!';
|
|
if( skip_color_tab) label++;
|
|
|
|
int spacing = 8; // between left colorbar and content
|
|
struct nk_window *win = ui_ctx->current;
|
|
struct nk_panel *layout = win->layout;
|
|
layout->at_x += spacing;
|
|
layout->bounds.w -= spacing;
|
|
if( !skip_color_tab ) {
|
|
float w = is_hovering ? 4 : 2; // spacing*3/4 : spacing/2-1;
|
|
bounds.w = w;
|
|
bounds.h -= 1;
|
|
struct nk_command_buffer *canvas = nk_window_get_canvas(ui_ctx);
|
|
nk_fill_rect(canvas, bounds, 0, nk_hsva_f(ui_hue, 0.75f, 0.8f, ui_alpha) );
|
|
}
|
|
|
|
if(!label || !label[0]) {
|
|
nk_label(ui_ctx, "", alignment);
|
|
layout->at_x -= spacing;
|
|
layout->bounds.w += spacing;
|
|
return 0;
|
|
}
|
|
|
|
const char *split = strchr(label, '@');
|
|
char buffer[128]; if( split ) label = (snprintf(buffer, 128, "%.*s", (int)(split-label), label), buffer);
|
|
|
|
struct nk_style *style = &ui_ctx->style;
|
|
bool bold = label[0] == '*'; label += bold;
|
|
struct nk_font *font = bold && nk_glfw.atlas.fonts->next ? nk_glfw.atlas.fonts->next->next /*3rd font*/ : NULL; // list
|
|
|
|
if( !has_icon ) {
|
|
// set bold style and color if needed
|
|
if( font && nk_style_push_font(ui_ctx, &font->handle) ) {} else font = 0;
|
|
if( font ) nk_style_push_color(ui_ctx, &style->text.color, nk_rgba(255, 255, 255, 255 * ui_alpha));
|
|
nk_label(ui_ctx, label, alignment);
|
|
} else {
|
|
char *icon_glyph = va("%.*s", icon_len, icon);
|
|
|
|
// @todo: implement nk_push_layout()
|
|
// nk_rect bounds = {..}; nk_panel_alloc_space(bounds, ctx);
|
|
struct nk_window *win = ui_ctx->current;
|
|
struct nk_panel *layout = win->layout, copy = *layout;
|
|
struct nk_rect before; nk_layout_peek(&before, ui_ctx);
|
|
nk_label_colored(ui_ctx, icon_glyph, alignment, nk_rgba(255, 255, 255, (64 + 192 * ui_label_icon_highlight) * ui_alpha) );
|
|
struct nk_rect after; nk_layout_peek(&after, ui_ctx);
|
|
*layout = copy;
|
|
layout->at_x += icon_width_px; layout->bounds.w -= icon_width_px; // nk_layout_space_push(ui_ctx, nk_rect(0,0,icon_width_px,0));
|
|
|
|
// set bold style and color if needed
|
|
if( font && nk_style_push_font(ui_ctx, &font->handle) ) {} else font = 0;
|
|
if( font ) nk_style_push_color(ui_ctx, &style->text.color, nk_rgba(255, 255, 255, 255 * ui_alpha));
|
|
nk_label(ui_ctx, icon+icon_len, alignment);
|
|
|
|
layout->at_x -= icon_width_px; layout->bounds.w += icon_width_px;
|
|
}
|
|
|
|
if( font ) nk_style_pop_color(ui_ctx);
|
|
if( font ) nk_style_pop_font(ui_ctx);
|
|
|
|
if (split && is_hovering && !ui_has_active_popups && nk_window_has_focus(ui_ctx)) {
|
|
nk_tooltip(ui_ctx, split + 1); // @fixme: not working under ui_disable() state
|
|
}
|
|
|
|
layout->at_x -= spacing;
|
|
layout->bounds.w += spacing;
|
|
|
|
// old way
|
|
// ui_labeicon_l_icked_L.x = is_hovering ? nk_input_has_mouse_click_down_in_rect(input, NK_BUTTON_LEFT, layout->bounds, nk_true) : 0;
|
|
// new way
|
|
// this is an ugly hack to detect which icon (within a label) we're clicking on.
|
|
// @todo: figure out a better way to detect this... would it be better to have a ui_label_toolbar(lbl,bar) helper function instead?
|
|
ui_label_icon_clicked_L.x = is_hovering ? ( (int)((input->mouse.pos.x - bounds.x) - (alignment == NK_TEXT_RIGHT ? bounds.w : 0) ) * nk_input_is_mouse_released(input, NK_BUTTON_LEFT)) : 0;
|
|
|
|
return ui_label_icon_clicked_L.x;
|
|
}
|
|
|
|
int ui_label(const char *label) {
|
|
if( label && ui_filter && ui_filter[0] ) if( !strstri(label, ui_filter) ) return 0;
|
|
|
|
int align = label[0] == '>' ? (label++, NK_TEXT_RIGHT) : label[0] == '=' ? (label++, NK_TEXT_CENTERED) : label[0] == '<' ? (label++, NK_TEXT_LEFT) : NK_TEXT_LEFT;
|
|
nk_layout_row_dynamic(ui_ctx, 0, 1);
|
|
return ui_label_(label, align);
|
|
}
|
|
|
|
static int nk_label_(struct nk_context *ui_ctx, const char *text_, int align2 ) {
|
|
const struct nk_input *input = &ui_ctx->input;
|
|
struct nk_rect bounds = nk_widget_bounds(ui_ctx);
|
|
int is_hovering = nk_input_is_mouse_hovering_rect(input, bounds) && !ui_has_active_popups;
|
|
if( is_hovering ) {
|
|
struct nk_rect winbounds = nk_window_get_bounds(ui_ctx);
|
|
is_hovering &= nk_input_is_mouse_hovering_rect(input, winbounds);
|
|
is_hovering &= nk_window_has_focus(ui_ctx);
|
|
}
|
|
|
|
nk_label(ui_ctx, text_, align2);
|
|
|
|
// this is an ugly hack to detect which icon (within a label) we're clicking on.
|
|
// @todo: figure out a better way to detect this... would it be better to have a ui_label_toolbar(lbl,bar) helper function instead?
|
|
ui_label_icon_clicked_R.x = is_hovering ? ( (int)((input->mouse.pos.x - bounds.x) - (align2 == NK_TEXT_RIGHT ? bounds.w : 0) ) * nk_input_is_mouse_released(input, NK_BUTTON_LEFT)) : 0;
|
|
|
|
return ui_label_icon_clicked_R.x;
|
|
}
|
|
|
|
|
|
int ui_label2(const char *label, const char *text_) {
|
|
if( label && ui_filter && ui_filter[0] ) if( !strstri(label, ui_filter) ) return 0;
|
|
|
|
nk_layout_row_dynamic(ui_ctx, 0, 2);
|
|
|
|
int align1 = NK_TEXT_LEFT;
|
|
int align2 = NK_TEXT_LEFT;
|
|
if( label ) align1 = label[0] == '>' ? (label++, NK_TEXT_RIGHT) : label[0] == '=' ? (label++, NK_TEXT_CENTERED) : label[0] == '<' ? (label++, NK_TEXT_LEFT) : NK_TEXT_LEFT;
|
|
if( text_ ) align2 = text_[0] == '>' ? (text_++, NK_TEXT_RIGHT) : text_[0] == '=' ? (text_++, NK_TEXT_CENTERED) : text_[0] == '<' ? (text_++, NK_TEXT_LEFT) : NK_TEXT_LEFT;
|
|
ui_label_(label, align1);
|
|
|
|
return nk_label_(ui_ctx, text_, align2);
|
|
}
|
|
int ui_label2_bool(const char *text, bool value) {
|
|
bool b = !!value;
|
|
return ui_bool(text, &b), 0;
|
|
}
|
|
int ui_label2_float(const char *text, float value) {
|
|
float f = (float)value;
|
|
return ui_float(text, &f), 0;
|
|
}
|
|
int ui_label2_wrap(const char *label, const char *str) { // @fixme: does not work (remove dynamic layout?)
|
|
nk_layout_row_dynamic(ui_ctx, 0, 2);
|
|
ui_label_(label, NK_TEXT_LEFT);
|
|
nk_text_wrap(ui_ctx, str, strlen(str));
|
|
return 0;
|
|
}
|
|
int ui_label2_toolbar(const char *label, const char *icons) {
|
|
int mouse_click = ui_label2(label, va(">%s", icons));
|
|
int choice = !mouse_click ? 0 : 1 + -mouse_click / (UI_ICON_FONTSIZE + UI_ICON_SPACING_X); // divided by px per ICON_MD_ glyph approximately
|
|
int glyphs = strlen(icons) / 3;
|
|
return choice > glyphs ? 0 : choice;
|
|
}
|
|
|
|
int ui_notify(const char *title, const char *body) {
|
|
app_beep();
|
|
|
|
struct ui_notify n = {0};
|
|
n.title = title && title[0] ? stringf("*%s", title) : 0;
|
|
n.body = body && body[0] ? STRDUP(body) : 0;
|
|
n.timeout = 2; // 4s = 2s timeout (+ 1s fade + 1s idle)
|
|
n.alpha = 1;
|
|
array_push_front(ui_notifications, n);
|
|
return 1;
|
|
}
|
|
|
|
int ui_button_transparent(const char *text) {
|
|
nk_layout_row_dynamic(ui_ctx, 0, 1);
|
|
int align = text[0] == '>' ? (text++, NK_TEXT_RIGHT) : text[0] == '=' ? (text++, NK_TEXT_CENTERED) : text[0] == '<' ? (text++, NK_TEXT_LEFT) : NK_TEXT_CENTERED;
|
|
return !!nk_contextual_item_label(ui_ctx, text, align);
|
|
}
|
|
|
|
#ifndef UI_BUTTON_MONOCHROME
|
|
#define UI_BUTTON_MONOCHROME 0
|
|
#endif
|
|
|
|
static
|
|
int ui_button_(const char *label) {
|
|
if( label && ui_filter && ui_filter[0] ) if( !strstri(label, ui_filter) ) return 0;
|
|
|
|
if( 1 ) {
|
|
#if UI_BUTTON_MONOCHROME
|
|
nk_style_push_color(ui_ctx, &ui_ctx->style.button.text_normal, nk_rgba(0,0,0,ui_alpha));
|
|
nk_style_push_color(ui_ctx, &ui_ctx->style.button.text_hover, nk_rgba(0,0,0,ui_alpha));
|
|
nk_style_push_color(ui_ctx, &ui_ctx->style.button.text_active, nk_rgba(0,0,0,ui_alpha));
|
|
|
|
nk_style_push_color(ui_ctx, &ui_ctx->style.button.normal.data.color, nk_hsva_f(ui_hue,0.0,0.8*ui_alpha));
|
|
nk_style_push_color(ui_ctx, &ui_ctx->style.button.hover.data.color, nk_hsva_f(ui_hue,0.0,1.0*ui_alpha));
|
|
nk_style_push_color(ui_ctx, &ui_ctx->style.button.active.data.color, nk_hsva_f(ui_hue,0.0,0.4*ui_alpha));
|
|
#elif 0 // old
|
|
nk_style_push_color(ui_ctx, &ui_ctx->style.button.text_normal, nk_rgba(0,0,0,ui_alpha));
|
|
nk_style_push_color(ui_ctx, &ui_ctx->style.button.text_hover, nk_rgba(0,0,0,ui_alpha));
|
|
nk_style_push_color(ui_ctx, &ui_ctx->style.button.text_active, nk_rgba(0,0,0,ui_alpha));
|
|
|
|
nk_style_push_color(ui_ctx, &ui_ctx->style.button.normal.data.color, nk_hsva_f(ui_hue,0.75,0.8*ui_alpha));
|
|
nk_style_push_color(ui_ctx, &ui_ctx->style.button.hover.data.color, nk_hsva_f(ui_hue,1.00,1.0*ui_alpha));
|
|
nk_style_push_color(ui_ctx, &ui_ctx->style.button.active.data.color, nk_hsva_f(ui_hue,0.60,0.4*ui_alpha));
|
|
#else // new
|
|
nk_style_push_color(ui_ctx, &ui_ctx->style.button.text_normal, nk_rgba_f(0.00,0.00,0.00,ui_alpha));
|
|
nk_style_push_color(ui_ctx, &ui_ctx->style.button.text_hover, nk_rgba_f(0.11,0.11,0.11,ui_alpha));
|
|
nk_style_push_color(ui_ctx, &ui_ctx->style.button.text_active, nk_rgba_f(0.00,0.00,0.00,ui_alpha));
|
|
|
|
nk_style_push_color(ui_ctx, &ui_ctx->style.button.normal.data.color, nk_hsva_f(ui_hue,0.80,0.6,0.90*ui_alpha));
|
|
nk_style_push_color(ui_ctx, &ui_ctx->style.button.hover.data.color, nk_hsva_f(ui_hue,0.85,0.9,0.90*ui_alpha));
|
|
nk_style_push_color(ui_ctx, &ui_ctx->style.button.active.data.color, nk_hsva_f(ui_hue,0.80,0.6,0.90*ui_alpha));
|
|
#endif
|
|
}
|
|
|
|
struct nk_rect bounds = nk_widget_bounds(ui_ctx);
|
|
|
|
const char *split = strchr(label, '@'), *tooltip = split + 1;
|
|
int ret = nk_button_text(ui_ctx, label, split ? (int)(split - label) : strlen(label) );
|
|
|
|
const struct nk_input *in = &ui_ctx->input;
|
|
if (split && nk_input_is_mouse_hovering_rect(in, bounds) && !ui_has_active_popups && nk_window_has_focus(ui_ctx)) {
|
|
nk_tooltip(ui_ctx, tooltip);
|
|
}
|
|
|
|
if( 1 ) {
|
|
nk_style_pop_color(ui_ctx);
|
|
nk_style_pop_color(ui_ctx);
|
|
nk_style_pop_color(ui_ctx);
|
|
|
|
nk_style_pop_color(ui_ctx);
|
|
nk_style_pop_color(ui_ctx);
|
|
nk_style_pop_color(ui_ctx);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int ui_buttons(int buttons, ...) {
|
|
static array(char*) args = 0;
|
|
array_resize(args, 0);
|
|
|
|
int num_skips = 0;
|
|
va_list list;
|
|
va_start(list, buttons);
|
|
for( int i = 0; i < buttons; ++i ) {
|
|
const char *label = va_arg(list, const char*);
|
|
int skip = ui_filter && ui_filter[0] && !strstri(label, ui_filter);
|
|
array_push(args, skip ? NULL : (char*)label);
|
|
num_skips += skip;
|
|
}
|
|
va_end(list);
|
|
|
|
if( num_skips == array_count(args) ) return 0;
|
|
buttons = array_count(args) - num_skips;
|
|
|
|
nk_layout_row_dynamic(ui_ctx, 0, buttons);
|
|
|
|
float ui_hue_old = ui_hue;
|
|
|
|
int indent = 8;
|
|
struct nk_window *win = ui_ctx->current;
|
|
struct nk_panel *layout = win->layout;
|
|
struct nk_panel copy = *layout;
|
|
ui_label_("", NK_TEXT_LEFT);
|
|
*layout = copy;
|
|
layout->at_x += indent;
|
|
layout->bounds.w -= indent;
|
|
|
|
int rc = 0;
|
|
for( int i = 0, end = array_count(args); i < end; ++i ) {
|
|
if( args[i] && ui_button_( args[i] ) ) rc = i+1;
|
|
ui_hue_cycle( 3 );
|
|
}
|
|
va_end(list);
|
|
|
|
layout->at_x -= indent;
|
|
layout->bounds.w += indent;
|
|
|
|
ui_hue = ui_hue_old;
|
|
return rc;
|
|
}
|
|
|
|
int ui_button(const char *s) {
|
|
return ui_buttons(1, s);
|
|
}
|
|
|
|
int ui_toggle(const char *label, bool *value) {
|
|
if( label && ui_filter && ui_filter[0] ) if( !strstri(label, ui_filter) ) return 0;
|
|
|
|
nk_layout_row_dynamic(ui_ctx, 0, 2);
|
|
ui_label_(label, NK_TEXT_LEFT);
|
|
|
|
// nk_label(ui_ctx, label, alignment);
|
|
int rc = nk_button_transparent(ui_ctx, *value ? ICON_MD_TOGGLE_ON : ICON_MD_TOGGLE_OFF);
|
|
return rc ? (*value ^= 1), rc : rc;
|
|
}
|
|
|
|
static enum color_mode {COL_RGB, COL_HSV} ui_color_mode = COL_RGB;
|
|
|
|
int ui_color4f(const char *label, float *color) {
|
|
if( label && ui_filter && ui_filter[0] ) if( !strstri(label, ui_filter) ) return 0;
|
|
|
|
nk_layout_row_dynamic(ui_ctx, 0, 2);
|
|
ui_label_(label, NK_TEXT_LEFT);
|
|
|
|
struct nk_colorf after = { color[0]*ui_alpha, color[1]*ui_alpha, color[2]*ui_alpha, color[3]*ui_alpha }, before = after;
|
|
struct nk_colorf clamped = { clampf(after.r,0,1), clampf(after.g,0,1), clampf(after.b,0,1), clampf(after.a,0,1) };
|
|
if (nk_combo_begin_color(ui_ctx, nk_rgb_cf(clamped), nk_vec2(200,400))) {
|
|
nk_layout_row_dynamic(ui_ctx, 120, 1);
|
|
after = nk_color_picker(ui_ctx, after, NK_RGB);
|
|
|
|
nk_layout_row_dynamic(ui_ctx, 0, 2);
|
|
ui_color_mode = nk_option_label(ui_ctx, "RGB", ui_color_mode == COL_RGB) ? COL_RGB : ui_color_mode;
|
|
ui_color_mode = nk_option_label(ui_ctx, "HSV", ui_color_mode == COL_HSV) ? COL_HSV : ui_color_mode;
|
|
|
|
nk_layout_row_dynamic(ui_ctx, 0, 1);
|
|
if (ui_color_mode == COL_RGB) {
|
|
after.r = nk_propertyf(ui_ctx, "#R:", -FLT_MAX, after.r, FLT_MAX, 0.01f,0.005f);
|
|
after.g = nk_propertyf(ui_ctx, "#G:", -FLT_MAX, after.g, FLT_MAX, 0.01f,0.005f);
|
|
after.b = nk_propertyf(ui_ctx, "#B:", -FLT_MAX, after.b, FLT_MAX, 0.01f,0.005f);
|
|
} else {
|
|
float hsva[4];
|
|
nk_colorf_hsva_fv(hsva, after);
|
|
hsva[0] = nk_propertyf(ui_ctx, "#H:", -FLT_MAX, hsva[0], FLT_MAX, 0.01f,0.005f);
|
|
hsva[1] = nk_propertyf(ui_ctx, "#S:", -FLT_MAX, hsva[1], FLT_MAX, 0.01f,0.005f);
|
|
hsva[2] = nk_propertyf(ui_ctx, "#V:", -FLT_MAX, hsva[2], FLT_MAX, 0.01f,0.005f);
|
|
after = nk_hsva_colorfv(hsva);
|
|
}
|
|
nk_label(ui_ctx, va("#%02X%02X%02X", (unsigned)clampf(after.r*255,0,255), (unsigned)clampf(after.g*255,0,255), (unsigned)clampf(after.b*255,0,255)), NK_TEXT_CENTERED);
|
|
|
|
color[0] = after.r;
|
|
color[1] = after.g;
|
|
color[2] = after.b;
|
|
color[3] = after.a;
|
|
|
|
nk_combo_end(ui_ctx);
|
|
}
|
|
return !!memcmp(&before.r, &after.r, sizeof(struct nk_colorf));
|
|
}
|
|
int ui_color4(const char *label, unsigned *color) {
|
|
if( label && ui_filter && ui_filter[0] ) if( !strstri(label, ui_filter) ) return 0;
|
|
|
|
unsigned a = *color >> 24;
|
|
unsigned b =(*color >> 16)&255;
|
|
unsigned g =(*color >> 8)&255;
|
|
unsigned r = *color & 255;
|
|
|
|
nk_layout_row_dynamic(ui_ctx, 0, 2);
|
|
ui_label_(label, NK_TEXT_LEFT);
|
|
|
|
struct nk_colorf after = { r*ui_alpha/255, g*ui_alpha/255, b*ui_alpha/255, a*ui_alpha/255 }, before = after;
|
|
if (nk_combo_begin_color(ui_ctx, nk_rgb_cf(after), nk_vec2(200,400))) {
|
|
nk_layout_row_dynamic(ui_ctx, 120, 1);
|
|
after = nk_color_picker(ui_ctx, after, NK_RGBA);
|
|
|
|
nk_layout_row_dynamic(ui_ctx, 0, 2);
|
|
ui_color_mode = nk_option_label(ui_ctx, "RGB", ui_color_mode == COL_RGB) ? COL_RGB : ui_color_mode;
|
|
ui_color_mode = nk_option_label(ui_ctx, "HSV", ui_color_mode == COL_HSV) ? COL_HSV : ui_color_mode;
|
|
|
|
nk_layout_row_dynamic(ui_ctx, 0, 1);
|
|
if (ui_color_mode == COL_RGB) {
|
|
after.r = nk_propertyi(ui_ctx, "#R:", 0, after.r * 255, 255, 1,1) / 255.f;
|
|
after.g = nk_propertyi(ui_ctx, "#G:", 0, after.g * 255, 255, 1,1) / 255.f;
|
|
after.b = nk_propertyi(ui_ctx, "#B:", 0, after.b * 255, 255, 1,1) / 255.f;
|
|
after.a = nk_propertyi(ui_ctx, "#A:", 0, after.a * 255, 255, 1,1) / 255.f;
|
|
} else {
|
|
float hsva[4];
|
|
nk_colorf_hsva_fv(hsva, after);
|
|
hsva[0] = nk_propertyi(ui_ctx, "#H:", 0, hsva[0] * 255, 255, 1,1) / 255.f;
|
|
hsva[1] = nk_propertyi(ui_ctx, "#S:", 0, hsva[1] * 255, 255, 1,1) / 255.f;
|
|
hsva[2] = nk_propertyi(ui_ctx, "#V:", 0, hsva[2] * 255, 255, 1,1) / 255.f;
|
|
hsva[3] = nk_propertyi(ui_ctx, "#A:", 0, hsva[3] * 255, 255, 1,1) / 255.f;
|
|
after = nk_hsva_colorfv(hsva);
|
|
}
|
|
r = after.r * 255;
|
|
g = after.g * 255;
|
|
b = after.b * 255;
|
|
a = after.a * 255;
|
|
*color = rgba(r,g,b,a);
|
|
|
|
nk_label(ui_ctx, va("#%02X%02X%02X%02X", r, g, b, a), NK_TEXT_CENTERED);
|
|
|
|
nk_combo_end(ui_ctx);
|
|
}
|
|
return !!memcmp(&before.r, &after.r, sizeof(struct nk_colorf));
|
|
}
|
|
|
|
int ui_color3f(const char *label, float *color) {
|
|
if( label && ui_filter && ui_filter[0] ) if( !strstri(label, ui_filter) ) return 0;
|
|
|
|
nk_layout_row_dynamic(ui_ctx, 0, 2);
|
|
ui_label_(label, NK_TEXT_LEFT);
|
|
|
|
struct nk_colorf after = { color[0]*ui_alpha, color[1]*ui_alpha, color[2]*ui_alpha, color[3]*ui_alpha }, before = after;
|
|
struct nk_colorf clamped = { clampf(after.r,0,1), clampf(after.g,0,1), clampf(after.b,0,1), ui_alpha };
|
|
if (nk_combo_begin_color(ui_ctx, nk_rgb_cf(clamped), nk_vec2(200,400))) {
|
|
nk_layout_row_dynamic(ui_ctx, 120, 1);
|
|
after = nk_color_picker(ui_ctx, after, NK_RGB);
|
|
|
|
nk_layout_row_dynamic(ui_ctx, 0, 2);
|
|
ui_color_mode = nk_option_label(ui_ctx, "RGB", ui_color_mode == COL_RGB) ? COL_RGB : ui_color_mode;
|
|
ui_color_mode = nk_option_label(ui_ctx, "HSV", ui_color_mode == COL_HSV) ? COL_HSV : ui_color_mode;
|
|
|
|
nk_layout_row_dynamic(ui_ctx, 0, 1);
|
|
if (ui_color_mode == COL_RGB) {
|
|
after.r = nk_propertyf(ui_ctx, "#R:", -FLT_MAX, after.r, FLT_MAX, 0.01f,0.005f);
|
|
after.g = nk_propertyf(ui_ctx, "#G:", -FLT_MAX, after.g, FLT_MAX, 0.01f,0.005f);
|
|
after.b = nk_propertyf(ui_ctx, "#B:", -FLT_MAX, after.b, FLT_MAX, 0.01f,0.005f);
|
|
} else {
|
|
float hsva[4];
|
|
nk_colorf_hsva_fv(hsva, after);
|
|
hsva[0] = nk_propertyf(ui_ctx, "#H:", -FLT_MAX, hsva[0], FLT_MAX, 0.01f,0.005f);
|
|
hsva[1] = nk_propertyf(ui_ctx, "#S:", -FLT_MAX, hsva[1], FLT_MAX, 0.01f,0.005f);
|
|
hsva[2] = nk_propertyf(ui_ctx, "#V:", -FLT_MAX, hsva[2], FLT_MAX, 0.01f,0.005f);
|
|
after = nk_hsva_colorfv(hsva);
|
|
}
|
|
nk_label(ui_ctx, va("#%02X%02X%02X", (unsigned)clampf(after.r*255,0,255), (unsigned)clampf(after.g*255,0,255), (unsigned)clampf(after.b*255,0,255)), NK_TEXT_CENTERED);
|
|
|
|
color[0] = after.r;
|
|
color[1] = after.g;
|
|
color[2] = after.b;
|
|
|
|
nk_combo_end(ui_ctx);
|
|
}
|
|
return !!memcmp(&before.r, &after.r, sizeof(struct nk_colorf));
|
|
}
|
|
int ui_color3(const char *label, unsigned *color) {
|
|
if( label && ui_filter && ui_filter[0] ) if( !strstri(label, ui_filter) ) return 0;
|
|
|
|
unsigned a = *color >> 24;
|
|
unsigned b =(*color >> 16)&255;
|
|
unsigned g =(*color >> 8)&255;
|
|
unsigned r = *color & 255;
|
|
|
|
nk_layout_row_dynamic(ui_ctx, 0, 2);
|
|
ui_label_(label, NK_TEXT_LEFT);
|
|
|
|
struct nk_colorf after = { r*ui_alpha/255, g*ui_alpha/255, b*ui_alpha/255, ui_alpha }, before = after;
|
|
if (nk_combo_begin_color(ui_ctx, nk_rgb_cf(after), nk_vec2(200,400))) {
|
|
nk_layout_row_dynamic(ui_ctx, 120, 1);
|
|
after = nk_color_picker(ui_ctx, after, NK_RGB);
|
|
|
|
nk_layout_row_dynamic(ui_ctx, 0, 2);
|
|
ui_color_mode = nk_option_label(ui_ctx, "RGB", ui_color_mode == COL_RGB) ? COL_RGB : ui_color_mode;
|
|
ui_color_mode = nk_option_label(ui_ctx, "HSV", ui_color_mode == COL_HSV) ? COL_HSV : ui_color_mode;
|
|
|
|
nk_layout_row_dynamic(ui_ctx, 0, 1);
|
|
if (ui_color_mode == COL_RGB) {
|
|
after.r = nk_propertyi(ui_ctx, "#R:", 0, after.r * 255, 255, 1,1) / 255.f;
|
|
after.g = nk_propertyi(ui_ctx, "#G:", 0, after.g * 255, 255, 1,1) / 255.f;
|
|
after.b = nk_propertyi(ui_ctx, "#B:", 0, after.b * 255, 255, 1,1) / 255.f;
|
|
} else {
|
|
float hsva[4];
|
|
nk_colorf_hsva_fv(hsva, after);
|
|
hsva[0] = nk_propertyi(ui_ctx, "#H:", 0, hsva[0] * 255, 255, 1,1) / 255.f;
|
|
hsva[1] = nk_propertyi(ui_ctx, "#S:", 0, hsva[1] * 255, 255, 1,1) / 255.f;
|
|
hsva[2] = nk_propertyi(ui_ctx, "#V:", 0, hsva[2] * 255, 255, 1,1) / 255.f;
|
|
after = nk_hsva_colorfv(hsva);
|
|
}
|
|
r = after.r * 255;
|
|
g = after.g * 255;
|
|
b = after.b * 255;
|
|
*color = rgba(r,g,b,a);
|
|
|
|
nk_label(ui_ctx, va("#%02X%02X%02X", r, g, b), NK_TEXT_CENTERED);
|
|
|
|
nk_combo_end(ui_ctx);
|
|
}
|
|
return !!memcmp(&before.r, &after.r, sizeof(struct nk_colorf));
|
|
}
|
|
|
|
int ui_list(const char *label, const char **items, int num_items, int *selector) {
|
|
if( label && ui_filter && ui_filter[0] ) if( !strstri(label, ui_filter) ) return 0;
|
|
|
|
nk_layout_row_dynamic(ui_ctx, 0, 2);
|
|
ui_label_(label, NK_TEXT_LEFT);
|
|
|
|
int val = nk_combo(ui_ctx, items, num_items, *selector, UI_ROW_HEIGHT, nk_vec2(200,200));
|
|
int chg = val != *selector;
|
|
*selector = val;
|
|
return chg;
|
|
}
|
|
|
|
int ui_slider(const char *label, float *slider) {
|
|
if( label && ui_filter && ui_filter[0] ) if( !strstri(label, ui_filter) ) return 0;
|
|
|
|
// return ui_slider2(label, slider, va("%.2f ", *slider));
|
|
nk_layout_row_dynamic(ui_ctx, 0, 2);
|
|
ui_label_(label, NK_TEXT_LEFT);
|
|
|
|
nk_size val = *slider * 1000;
|
|
int chg = nk_progress(ui_ctx, &val, 1000, NK_MODIFIABLE);
|
|
*slider = val / 1000.f;
|
|
return chg;
|
|
}
|
|
int ui_slider2(const char *label, float *slider, const char *caption) {
|
|
if( label && ui_filter && ui_filter[0] ) if( !strstri(label, ui_filter) ) return 0;
|
|
|
|
nk_layout_row_dynamic(ui_ctx, 0, 2);
|
|
ui_label_(label, NK_TEXT_LEFT);
|
|
|
|
struct nk_window *win = ui_ctx->current;
|
|
const struct nk_style *style = &ui_ctx->style;
|
|
struct nk_rect bounds; nk_layout_peek(&bounds, ui_ctx); bounds.w -= 10; // bounds.w *= 0.95f;
|
|
struct nk_vec2 item_padding = style->text.padding;
|
|
struct nk_text text;
|
|
text.padding.x = item_padding.x;
|
|
text.padding.y = item_padding.y;
|
|
text.background = style->window.background;
|
|
text.text = nk_rgba_f(1,1,1,ui_alpha);
|
|
|
|
nk_size val = *slider * 1000;
|
|
int chg = nk_progress(ui_ctx, &val, 1000, NK_MODIFIABLE);
|
|
*slider = val / 1000.f;
|
|
|
|
chg |= input(MOUSE_L) && nk_input_is_mouse_hovering_rect(&ui_ctx->input, bounds); // , true);
|
|
|
|
nk_widget_text(&win->buffer, bounds, caption, strlen(caption), &text, NK_TEXT_RIGHT, style->font);
|
|
return chg;
|
|
}
|
|
|
|
int ui_bool(const char *label, bool *enabled ) {
|
|
if( label && ui_filter && ui_filter[0] ) if( !strstri(label, ui_filter) ) return 0;
|
|
|
|
nk_layout_row_dynamic(ui_ctx, 0, 2);
|
|
ui_label_(label, NK_TEXT_LEFT);
|
|
|
|
int val = *enabled;
|
|
#if 0
|
|
int chg = !!nk_checkbox_label(ui_ctx, "", &val);
|
|
#else
|
|
int chg = !!nk_button_transparent(ui_ctx, val ? ICON_MD_CHECK_BOX : ICON_MD_CHECK_BOX_OUTLINE_BLANK );
|
|
#endif
|
|
*enabled ^= chg;
|
|
return chg;
|
|
}
|
|
|
|
static int ui_num_signs = 0;
|
|
|
|
int ui_int(const char *label, int *v) {
|
|
if( label && ui_filter && ui_filter[0] ) if( !strstri(label, ui_filter) ) return 0;
|
|
|
|
nk_layout_row_dynamic(ui_ctx, 0, 2);
|
|
ui_label_(label, NK_TEXT_LEFT);
|
|
|
|
int prev = *v;
|
|
*v = nk_propertyi(ui_ctx, "#", INT_MIN, *v, INT_MAX, 1,1);
|
|
return prev != *v;
|
|
}
|
|
|
|
int ui_unsigned(const char *label, unsigned *v) {
|
|
if( label && ui_filter && ui_filter[0] ) if( !strstri(label, ui_filter) ) return 0;
|
|
|
|
nk_layout_row_dynamic(ui_ctx, 0, 2);
|
|
ui_label_(label, NK_TEXT_LEFT);
|
|
|
|
unsigned prev = *v;
|
|
*v = (unsigned)nk_propertyd(ui_ctx, "#", 0, *v, UINT_MAX, 1,1);
|
|
return prev != *v;
|
|
}
|
|
int ui_unsigned2(const char *label, unsigned *v) {
|
|
if( label && ui_filter && ui_filter[0] ) if( !strstri(label, ui_filter) ) return 0;
|
|
|
|
nk_layout_row_dynamic(ui_ctx, 0, 2);
|
|
ui_label_(label, NK_TEXT_LEFT);
|
|
|
|
char *buffer = ui_num_signs ?
|
|
--ui_num_signs, va("%+2u %+2u", v[0], v[1]) :
|
|
va("%2u, %2u", v[0], v[1]);
|
|
|
|
if (nk_combo_begin_label(ui_ctx, buffer, nk_vec2(200,200))) {
|
|
nk_layout_row_dynamic(ui_ctx, 0, 1);
|
|
unsigned prev0 = v[0]; nk_property_int(ui_ctx, "#X:", 0, &v[0], INT_MAX, 1,0.5f);
|
|
unsigned prev1 = v[1]; nk_property_int(ui_ctx, "#Y:", 0, &v[1], INT_MAX, 1,0.5f);
|
|
nk_combo_end(ui_ctx);
|
|
return prev0 != v[0] || prev1 != v[1];
|
|
}
|
|
return 0;
|
|
}
|
|
int ui_unsigned3(const char *label, unsigned *v) {
|
|
if( label && ui_filter && ui_filter[0] ) if( !strstri(label, ui_filter) ) return 0;
|
|
|
|
nk_layout_row_dynamic(ui_ctx, 0, 2);
|
|
ui_label_(label, NK_TEXT_LEFT);
|
|
|
|
char *buffer = ui_num_signs ?
|
|
--ui_num_signs, va("%+2u %+2u %+2u", v[0], v[1], v[2]) :
|
|
va("%2u, %2u, %2u", v[0], v[1], v[2]);
|
|
|
|
if (nk_combo_begin_label(ui_ctx, buffer, nk_vec2(200,200))) {
|
|
nk_layout_row_dynamic(ui_ctx, 0, 1);
|
|
unsigned prev0 = v[0]; nk_property_int(ui_ctx, "#X:", 0, &v[0], INT_MAX, 1,0.5f);
|
|
unsigned prev1 = v[1]; nk_property_int(ui_ctx, "#Y:", 0, &v[1], INT_MAX, 1,0.5f);
|
|
unsigned prev2 = v[2]; nk_property_int(ui_ctx, "#Z:", 0, &v[2], INT_MAX, 1,0.5f);
|
|
nk_combo_end(ui_ctx);
|
|
return prev0 != v[0] || prev1 != v[1] || prev2 != v[2];
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int ui_short(const char *label, short *v) {
|
|
if( label && ui_filter && ui_filter[0] ) if( !strstri(label, ui_filter) ) return 0;
|
|
|
|
int i = *v, ret = ui_int( label, &i );
|
|
return *v = (short)i, ret;
|
|
}
|
|
|
|
int ui_float(const char *label, float *v) {
|
|
if( label && ui_filter && ui_filter[0] ) if( !strstri(label, ui_filter) ) return 0;
|
|
|
|
nk_layout_row_dynamic(ui_ctx, 0, 2);
|
|
ui_label_(label, NK_TEXT_LEFT);
|
|
|
|
float prev = v[0]; v[0] = nk_propertyf(ui_ctx, "#", -FLT_MAX, v[0], FLT_MAX, 0.01f,0.005f);
|
|
return prev != v[0];
|
|
}
|
|
|
|
int ui_double(const char *label, double *v) {
|
|
if( label && ui_filter && ui_filter[0] ) if( !strstri(label, ui_filter) ) return 0;
|
|
|
|
nk_layout_row_dynamic(ui_ctx, 0, 2);
|
|
ui_label_(label, NK_TEXT_LEFT);
|
|
|
|
double prev = v[0]; v[0] = nk_propertyd(ui_ctx, "#", -DBL_MAX, v[0], DBL_MAX, 0.01f,0.005f);
|
|
return prev != v[0];
|
|
}
|
|
|
|
int ui_clampf(const char *label, float *v, float minf, float maxf) {
|
|
if( label && ui_filter && ui_filter[0] ) if( !strstri(label, ui_filter) ) return 0;
|
|
|
|
if( minf > maxf ) return ui_clampf(label, v, maxf, minf);
|
|
|
|
nk_layout_row_dynamic(ui_ctx, 0, 2);
|
|
ui_label_(label, NK_TEXT_LEFT);
|
|
|
|
float prev = v[0]; v[0] = nk_propertyf(ui_ctx, "#", minf, v[0], maxf, 0.1f,0.05f);
|
|
return prev != v[0];
|
|
}
|
|
|
|
int ui_float2(const char *label, float *v) {
|
|
if( label && ui_filter && ui_filter[0] ) if( !strstri(label, ui_filter) ) return 0;
|
|
|
|
nk_layout_row_dynamic(ui_ctx, 0, 2);
|
|
ui_label_(label, NK_TEXT_LEFT);
|
|
|
|
char *buffer = ui_num_signs ?
|
|
--ui_num_signs, va("%+.3f %+.3f", v[0], v[1]) :
|
|
va("%.3f, %.3f", v[0], v[1]);
|
|
|
|
if (nk_combo_begin_label(ui_ctx, buffer, nk_vec2(200,200))) {
|
|
nk_layout_row_dynamic(ui_ctx, 0, 1);
|
|
float prev0 = v[0]; nk_property_float(ui_ctx, "#X:", -FLT_MAX, &v[0], FLT_MAX, 1,0.5f);
|
|
float prev1 = v[1]; nk_property_float(ui_ctx, "#Y:", -FLT_MAX, &v[1], FLT_MAX, 1,0.5f);
|
|
nk_combo_end(ui_ctx);
|
|
return prev0 != v[0] || prev1 != v[1];
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int ui_float3(const char *label, float *v) {
|
|
if( label && ui_filter && ui_filter[0] ) if( !strstri(label, ui_filter) ) return 0;
|
|
|
|
nk_layout_row_dynamic(ui_ctx, 0, 2);
|
|
ui_label_(label, NK_TEXT_LEFT);
|
|
|
|
char *buffer = ui_num_signs ?
|
|
--ui_num_signs, va("%+.2f %+.2f %+.2f", v[0], v[1], v[2]) :
|
|
va("%.2f, %.2f, %.2f", v[0], v[1], v[2]);
|
|
|
|
if (nk_combo_begin_label(ui_ctx, buffer, nk_vec2(200,200))) {
|
|
nk_layout_row_dynamic(ui_ctx, 0, 1);
|
|
float prev0 = v[0]; nk_property_float(ui_ctx, "#X:", -FLT_MAX, &v[0], FLT_MAX, 1,0.5f);
|
|
float prev1 = v[1]; nk_property_float(ui_ctx, "#Y:", -FLT_MAX, &v[1], FLT_MAX, 1,0.5f);
|
|
float prev2 = v[2]; nk_property_float(ui_ctx, "#Z:", -FLT_MAX, &v[2], FLT_MAX, 1,0.5f);
|
|
nk_combo_end(ui_ctx);
|
|
return prev0 != v[0] || prev1 != v[1] || prev2 != v[2];
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int ui_float4(const char *label, float *v) {
|
|
if( label && ui_filter && ui_filter[0] ) if( !strstri(label, ui_filter) ) return 0;
|
|
|
|
nk_layout_row_dynamic(ui_ctx, 0, 2);
|
|
ui_label_(label, NK_TEXT_LEFT);
|
|
|
|
char *buffer = ui_num_signs ?
|
|
--ui_num_signs, va("%+.2f %+.2f %+.2f %+.2f", v[0], v[1], v[2], v[3]) :
|
|
va("%.2f,%.2f,%.2f,%.2f", v[0], v[1], v[2], v[3]);
|
|
|
|
if (nk_combo_begin_label(ui_ctx, buffer, nk_vec2(200,200))) {
|
|
nk_layout_row_dynamic(ui_ctx, 0, 1);
|
|
float prev0 = v[0]; nk_property_float(ui_ctx, "#X:", -FLT_MAX, &v[0], FLT_MAX, 1,0.5f);
|
|
float prev1 = v[1]; nk_property_float(ui_ctx, "#Y:", -FLT_MAX, &v[1], FLT_MAX, 1,0.5f);
|
|
float prev2 = v[2]; nk_property_float(ui_ctx, "#Z:", -FLT_MAX, &v[2], FLT_MAX, 1,0.5f);
|
|
float prev3 = v[3]; nk_property_float(ui_ctx, "#W:", -FLT_MAX, &v[3], FLT_MAX, 1,0.5f);
|
|
nk_combo_end(ui_ctx);
|
|
return prev0 != v[0] || prev1 != v[1] || prev2 != v[2] || prev3 != v[3];
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ui_mat33(const char *label, float M[9]) {
|
|
if( label && ui_filter && ui_filter[0] ) if( !strstri(label, ui_filter) ) return 0;
|
|
|
|
ui_num_signs = 3;
|
|
int changed = 0;
|
|
changed |= ui_label(label);
|
|
changed |= ui_float3(NULL, M);
|
|
changed |= ui_float3(NULL, M+3);
|
|
changed |= ui_float3(NULL, M+6);
|
|
return changed;
|
|
}
|
|
int ui_mat34(const char *label, float M[12]) {
|
|
if( label && ui_filter && ui_filter[0] ) if( !strstri(label, ui_filter) ) return 0;
|
|
|
|
ui_num_signs = 3;
|
|
int changed = 0;
|
|
changed |= ui_label(label);
|
|
changed |= ui_float4(NULL, M);
|
|
changed |= ui_float4(NULL, M+4);
|
|
changed |= ui_float4(NULL, M+8);
|
|
return changed;
|
|
}
|
|
int ui_mat44(const char *label, float M[16]) {
|
|
if( label && ui_filter && ui_filter[0] ) if( !strstri(label, ui_filter) ) return 0;
|
|
|
|
ui_num_signs = 4;
|
|
int changed = 0;
|
|
changed |= ui_label(label);
|
|
changed |= ui_float4(NULL, M);
|
|
changed |= ui_float4(NULL, M+4);
|
|
changed |= ui_float4(NULL, M+8);
|
|
changed |= ui_float4(NULL, M+12);
|
|
return changed;
|
|
}
|
|
|
|
int ui_buffer(const char *label, char *buffer, int buflen) {
|
|
if( label && ui_filter && ui_filter[0] ) if( !strstri(label, ui_filter) ) return 0;
|
|
|
|
nk_layout_row_dynamic(ui_ctx, 0, 1 + (label && label[0]));
|
|
if(label && label[0]) ui_label_(label, NK_TEXT_LEFT);
|
|
|
|
int active = nk_edit_string_zero_terminated(ui_ctx, NK_EDIT_AUTO_SELECT|NK_EDIT_CLIPBOARD|NK_EDIT_FIELD/*NK_EDIT_BOX*/|NK_EDIT_SIG_ENTER, buffer, buflen, nk_filter_default);
|
|
return !!(active & NK_EDIT_COMMITED) ? nk_edit_unfocus(ui_ctx), 1 : 0;
|
|
}
|
|
|
|
int ui_string(const char *label, char **str) {
|
|
if( label && ui_filter && ui_filter[0] ) if( !strstri(label, ui_filter) ) return 0;
|
|
|
|
char *bak = va("%s%c", *str ? *str : "", '\0');
|
|
int rc = ui_buffer(label, bak, strlen(bak)+2);
|
|
if( *str ) 0[*str] = '\0';
|
|
strcatf(str, "%s", bak);
|
|
return rc;
|
|
}
|
|
|
|
int ui_separator() {
|
|
if( /*label &&*/ ui_filter && ui_filter[0] ) return 0; // if( !strstri(label, ui_filter) ) return 0;
|
|
|
|
nk_layout_row_dynamic(ui_ctx, UI_SEPARATOR_HEIGHT, 1);
|
|
|
|
ui_hue_cycle( 1 );
|
|
|
|
struct nk_command_buffer *canvas;
|
|
struct nk_input *input = &ui_ctx->input;
|
|
canvas = nk_window_get_canvas(ui_ctx);
|
|
struct nk_rect space;
|
|
enum nk_widget_layout_states state;
|
|
state = nk_widget(&space, ui_ctx);
|
|
if (state) nk_fill_rect(canvas, space, 0, ui_ctx->style.window.header.normal.data.color );
|
|
|
|
return 1;
|
|
}
|
|
|
|
int ui_subimage(const char *label, handle id, unsigned iw, unsigned ih, unsigned sx, unsigned sy, unsigned sw, unsigned sh) {
|
|
if( label && ui_filter && ui_filter[0] ) if( !strstri(label, ui_filter) ) return 0;
|
|
|
|
nk_layout_row_dynamic(ui_ctx, sh < 30 || id == texture_checker().id ? 0 : sh, 1 + (label && label[0]));
|
|
if( label && label[0] ) ui_label_(label, NK_TEXT_LEFT);
|
|
|
|
struct nk_rect bounds; nk_layout_peek(&bounds, ui_ctx); bounds.w -= 10; // bounds.w *= 0.95f;
|
|
|
|
int ret = nk_button_image_styled(ui_ctx, &ui_ctx->style.button, nk_subimage_id((int)id, iw, ih, nk_rect(sx,sy,sw,sh)));
|
|
if( !ret ) {
|
|
ret |= input(MOUSE_L) && nk_input_is_mouse_hovering_rect(&ui_ctx->input, bounds); // , true);
|
|
}
|
|
if( ret ) {
|
|
int px = 100 * (ui_ctx->input.mouse.pos.x - bounds.x ) / (float)bounds.w;
|
|
int py = 100 * (ui_ctx->input.mouse.pos.y - bounds.y ) / (float)bounds.h;
|
|
return px * 100 + py; // printf("%d %d xy:%d\n", px, py, (px * 100) + py);
|
|
}
|
|
return ret; // !!ret;
|
|
}
|
|
|
|
int ui_image(const char *label, handle id, unsigned w, unsigned h) {
|
|
return ui_subimage(label, id, w,h, 0,0,w,h);
|
|
}
|
|
|
|
int ui_texture(const char *label, texture_t t) {
|
|
return ui_subimage(label, t.id, t.w,t.h, 0,0,t.w,t.h);
|
|
}
|
|
|
|
int ui_subtexture(const char *label, texture_t t, unsigned x, unsigned y, unsigned w, unsigned h) {
|
|
return ui_subimage(label, t.id, t.w,t.h, x,y,w,h);
|
|
}
|
|
|
|
int ui_colormap( const char *label, colormap_t *cm ) {
|
|
if( label && ui_filter && ui_filter[0] ) if( !strstri(label, ui_filter) ) return 0;
|
|
|
|
int ret = 0;
|
|
if( !cm->texture ) {
|
|
const char *title = va("%s (no image)", label);
|
|
if( ui_image( title, texture_checker().id, 0,0 ) ) {
|
|
ret = 2;
|
|
}
|
|
} else {
|
|
unsigned w = cm->texture->w, h = cm->texture->h;
|
|
ui_label(va("%s (%s)", label, cm->texture->filename) ); // @fixme: labelf would crash?
|
|
|
|
const char *fmt[] = { "", "R", "RG", "RGB", "RGBA" };
|
|
const char *title = va("%s %dx%d %s", label, w, h, fmt[cm->texture->n]);
|
|
if( ui_image( title, cm->texture->id, 128, 128 ) ) {
|
|
ret = 2;
|
|
}
|
|
}
|
|
|
|
if( ui_color4f( va("%s Color", label), (float *) &cm->color ) ) {
|
|
ret = 1;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ui_radio(const char *label, const char **items, int num_items, int *selector) {
|
|
if( label && ui_filter && ui_filter[0] ) if( !strstri(label, ui_filter) ) return 0;
|
|
|
|
int ret = 0;
|
|
if( label && label[0] ) ui_label(label);
|
|
for( int i = 0; i < num_items; i++ ) {
|
|
bool enabled = *selector == i;
|
|
if( ui_bool( va("%s%s", label && label[0] ? " ":"", items[i]), &enabled ) ) {
|
|
*selector = i;
|
|
ret |= 1;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ui_section(const char *label) {
|
|
if( label && ui_filter && ui_filter[0] ) if( !strstri(label, ui_filter) ) return 0;
|
|
|
|
//ui_separator();
|
|
return ui_label(va("*%s", label));
|
|
}
|
|
|
|
int ui_dialog(const char *label, const char *text, int choices, bool *show) { // @fixme: return
|
|
if( label && ui_filter && ui_filter[0] ) if( !strstri(label, ui_filter) ) return 0;
|
|
|
|
ui_has_active_popups = 0;
|
|
if(*show) {
|
|
static struct nk_rect s = {0, 0, 300, 190};
|
|
if (nk_popup_begin(ui_ctx, NK_POPUP_STATIC, label, NK_WINDOW_BORDER|NK_WINDOW_CLOSABLE, s)) {
|
|
nk_layout_row_dynamic(ui_ctx, 20, 1);
|
|
for( char t[1024]; *text && sscanf(text, "%[^\r\n]", t); ) {
|
|
nk_label(ui_ctx, t, NK_TEXT_LEFT);
|
|
text += strlen(t); while(*text && *text < 32) text++;
|
|
}
|
|
|
|
if( choices ) {
|
|
if( ui_buttons(choices > 1 ? 2 : 1, "OK", "Cancel") ) {
|
|
*show = 0;
|
|
nk_popup_close(ui_ctx);
|
|
}
|
|
}
|
|
|
|
nk_popup_end(ui_ctx);
|
|
} else {
|
|
*show = nk_false;
|
|
}
|
|
ui_has_active_popups = *show;
|
|
}
|
|
return *show;
|
|
}
|
|
|
|
#define ui_bitmask_template(X) \
|
|
int ui_bitmask##X(const char *label, uint##X##_t *enabled) { \
|
|
if( label && ui_filter && ui_filter[0] ) if( !strstri(label, ui_filter) ) return 0; \
|
|
\
|
|
/* @fixme: better way to retrieve widget width? nk_layout_row_dynamic() seems excessive */ \
|
|
nk_layout_row_dynamic(ui_ctx, 1, 1); \
|
|
struct nk_rect bounds = nk_widget_bounds(ui_ctx); \
|
|
\
|
|
/* actual widget: label + X checkboxes */ \
|
|
nk_layout_row_begin(ui_ctx, NK_STATIC, 0, 1+X); \
|
|
\
|
|
int offset = bounds.w > (15*X) ? bounds.w - (15*X) : 0; /* bits widget below needs at least 118px wide */ \
|
|
nk_layout_row_push(ui_ctx, offset); \
|
|
ui_label_(label, NK_TEXT_LEFT); \
|
|
\
|
|
uint8_t copy = *enabled; \
|
|
for( int i = 0; i < X; ++i ) { \
|
|
int b = (X-1-i); \
|
|
nk_layout_row_push(ui_ctx, 10); \
|
|
/* bit */ \
|
|
int val = (*enabled >> b) & 1; \
|
|
int chg = nk_checkbox_label(ui_ctx, "", &val); \
|
|
*enabled = (*enabled & ~(1 << b)) | ((!!val) << b); \
|
|
/* tooltip */ \
|
|
struct nk_rect bb = { offset + 10 + i * 14, bounds.y, 14, 30 }; /* 10:padding,14:width,30:height */ \
|
|
if (nk_input_is_mouse_hovering_rect(&ui_ctx->input, bb) && !ui_has_active_popups && nk_window_has_focus(ui_ctx)) { \
|
|
nk_tooltipf(ui_ctx, "Bit %d", b); \
|
|
} \
|
|
} \
|
|
\
|
|
nk_layout_row_end(ui_ctx); \
|
|
return copy ^ *enabled; \
|
|
}
|
|
|
|
ui_bitmask_template(8);
|
|
ui_bitmask_template(16);
|
|
//ui_bitmask_template(32);
|
|
|
|
int ui_console() { // @fixme: buggy
|
|
static char *cmd = 0;
|
|
static int enter = 0;
|
|
|
|
struct nk_font *font = nk_glfw.atlas.fonts->next /*2nd font*/;
|
|
if( font && nk_style_push_font(ui_ctx, &font->handle) ) {} else font = 0;
|
|
|
|
struct nk_rect bounds = {0,0,400,300}; // @fixme: how to retrieve inlined region below? (inlined)
|
|
if( 1 ) /*if( windowed || (!windowed && *inlined) )*/ bounds = nk_window_get_content_region(ui_ctx);
|
|
else { struct nk_rect b; nk_layout_peek(&b, ui_ctx); bounds.w = b.w; }
|
|
|
|
nk_layout_row_static(ui_ctx, bounds.h - UI_ROW_HEIGHT, bounds.w, 1);
|
|
|
|
static array(char*) lines = 0;
|
|
for( int i = 0; i < array_count(lines); ++i )
|
|
if(lines[i]) nk_label_wrap(ui_ctx, lines[i]); // "This is a very long line to hopefully get this text to be wrapped into multiple lines to show line wrapping");
|
|
|
|
if( enter ) {
|
|
array_push(lines, 0);
|
|
|
|
for( FILE *fp = popen(cmd, "r"); fp; pclose(fp), fp = 0) {
|
|
int ch;
|
|
do {
|
|
ch = fgetc(fp);
|
|
if( strchr("\r\n\t\b", ch) ) {
|
|
array_push(lines,0);
|
|
continue;
|
|
}
|
|
if( ch >= ' ' ) strcatf(array_back(lines), "%c", ch);
|
|
} while(ch > 0);
|
|
}
|
|
|
|
cmd[0] = 0;
|
|
}
|
|
|
|
enter = ui_string("", &cmd);
|
|
|
|
if( font ) nk_style_pop_font(ui_ctx);
|
|
|
|
return enter;
|
|
}
|
|
|
|
int ui_browse(const char **output, bool *inlined) {
|
|
static struct browser_media media = {0};
|
|
static struct browser browsers[2] = {0}; // 2 instances max: 0=inlined, 1=windowed
|
|
static char *results[2] = {0}; // 2 instances max: 0=inlined, 1=windowed
|
|
do_once {
|
|
const int W = 96, H = 96; // 2048x481 px, 21x5 cells
|
|
texture_t i = texture("icons/suru.png", TEXTURE_RGBA|TEXTURE_MIPMAPS);
|
|
browser_config_dir(icon_load_rect(i.id, i.w, i.h, W, H, 16, 3), BROWSER_FOLDER); // default group
|
|
browser_config_dir(icon_load_rect(i.id, i.w, i.h, W, H, 2, 4), BROWSER_HOME);
|
|
browser_config_dir(icon_load_rect(i.id, i.w, i.h, W, H, 17, 3), BROWSER_COMPUTER);
|
|
browser_config_dir(icon_load_rect(i.id, i.w, i.h, W, H, 1, 4), BROWSER_PROJECT);
|
|
browser_config_dir(icon_load_rect(i.id, i.w, i.h, W, H, 0, 4), BROWSER_DESKTOP);
|
|
|
|
browser_config_type(icon_load_rect(i.id, i.w, i.h, W, H, 8, 0), "");
|
|
browser_config_type(icon_load_rect(i.id, i.w, i.h, W, H, 10, 2), ".txt.md.doc.license" ".");
|
|
browser_config_type(icon_load_rect(i.id, i.w, i.h, W, H, 8, 1), ".ini.cfg" ".");
|
|
browser_config_type(icon_load_rect(i.id, i.w, i.h, W, H, 8, 3), ".xlsx" ".");
|
|
browser_config_type(icon_load_rect(i.id, i.w, i.h, W, H, 9, 0), ".c" ".");
|
|
browser_config_type(icon_load_rect(i.id, i.w, i.h, W, H, 4, 1), ".h.hpp.hh.hxx" ".");
|
|
browser_config_type(icon_load_rect(i.id, i.w, i.h, W, H, 4, 2), ".fs.vs.gs.fx.glsl.shader" ".");
|
|
browser_config_type(icon_load_rect(i.id, i.w, i.h, W, H, 12, 0), ".cpp.cc.cxx" ".");
|
|
browser_config_type(icon_load_rect(i.id, i.w, i.h, W, H, 15, 0), ".json" ".");
|
|
browser_config_type(icon_load_rect(i.id, i.w, i.h, W, H, 15, 2), ".bat.sh" ".");
|
|
browser_config_type(icon_load_rect(i.id, i.w, i.h, W, H, 6, 1), ".htm.html" ".");
|
|
browser_config_type(icon_load_rect(i.id, i.w, i.h, W, H, 20, 1), ".xml" ".");
|
|
browser_config_type(icon_load_rect(i.id, i.w, i.h, W, H, 12, 1), ".js" ".");
|
|
browser_config_type(icon_load_rect(i.id, i.w, i.h, W, H, 0, 3), ".ts" ".");
|
|
browser_config_type(icon_load_rect(i.id, i.w, i.h, W, H, 6, 2), ".py" ".");
|
|
browser_config_type(icon_load_rect(i.id, i.w, i.h, W, H, 16, 1), ".lua" ".");
|
|
browser_config_type(icon_load_rect(i.id, i.w, i.h, W, H, 10, 0), ".css.doc" ".");
|
|
browser_config_type(icon_load_rect(i.id, i.w, i.h, W, H, 6, 0), ".wav.flac.ogg.mp1.mp3.mod.xm.s3m.it.sfxr.mid.fur" ".");
|
|
browser_config_type(icon_load_rect(i.id, i.w, i.h, W, H, 1, 3), ".ttf.ttc.otf" ".");
|
|
browser_config_type(icon_load_rect(i.id, i.w, i.h, W, H, 7, 1), ".jpg.jpeg.png.bmp.psd.pic.pnm.ico.ktx.pvr.dds.astc.basis.hdr.tga.gif" ".");
|
|
browser_config_type(icon_load_rect(i.id, i.w, i.h, W, H, 4, 3), ".mp4.mpg.ogv.mkv.wmv.avi" ".");
|
|
browser_config_type(icon_load_rect(i.id, i.w, i.h, W, H, 2, 1), ".iqm.iqe.gltf.gltf2.glb.fbx.obj.dae.blend.md3.md5.ms3d.smd.x.3ds.bvh.dxf.lwo" ".");
|
|
browser_config_type(icon_load_rect(i.id, i.w, i.h, W, H, 0, 1), ".exe" ".");
|
|
browser_config_type(icon_load_rect(i.id, i.w, i.h, W, H, 7, 0), ".bin.dSYM.pdb.o.lib.dll" ".");
|
|
browser_config_type(icon_load_rect(i.id, i.w, i.h, W, H, 15, 3), ".zip.rar.7z.pak" ".");
|
|
|
|
for( int j = 0; j < countof(browsers); ++j ) browser_init(&browsers[j]);
|
|
browsers[0].listing = 1; // inlined version got listing by default, as there is not much space for layouting
|
|
}
|
|
// at_exit: browser_free(&browser);
|
|
|
|
int clicked = 0;
|
|
bool windowed = !inlined;
|
|
if( windowed || (!windowed && *inlined) ) {
|
|
|
|
struct browser *browser = browsers + windowed; // select instance
|
|
char **result = results + windowed; // select instance
|
|
|
|
struct nk_rect bounds = {0}; // // {0,0,400,300};
|
|
// #define P(b) printf(FILELINE " (%3d,%3d) %3d,%3d\n", (int)b.x,(int)b.y, (int)b.w,(int)b.h)
|
|
// if(ui_ctx->current) bounds = nk_layout_space_bounds(ui_ctx), P(bounds);
|
|
// if(ui_ctx->current) bounds = nk_layout_widget_bounds(ui_ctx), P(bounds);
|
|
// if(ui_ctx->current) bounds = nk_widget_bounds(ui_ctx), P(bounds);
|
|
// if(ui_ctx->current) bounds = nk_window_get_bounds(ui_ctx), P(bounds);
|
|
// if(ui_ctx->current) bounds = nk_window_get_content_region(ui_ctx), P(bounds);
|
|
// if(ui_ctx->current) nk_layout_peek(&bounds, ui_ctx), P(bounds);
|
|
// if(ui_ctx->current) nk_layout_widget_space(&bounds, ui_ctx, ui_ctx->current, nk_false), P(bounds); // note: cant be used within a panel
|
|
// #undef P
|
|
|
|
// panel
|
|
// v4k_ui.c:2497 ( 6, 34) 310, 24
|
|
// v4k_ui.c:2498 ( 16, 62) 286, 24
|
|
// v4k_ui.c:2499 ( 16, 86) 296, 24
|
|
// v4k_ui.c:2500 ( 0, 0) 327,613
|
|
// v4k_ui.c:2501 ( 6, 34) 310,572 << ok
|
|
// v4k_ui.c:2502 ( 16, 86) 296, 24
|
|
// v4k_ui.c:2503 (316, 62) 297, 24
|
|
|
|
// window
|
|
// v4k_ui.c:2497 (188,152) 711, 4
|
|
// v4k_ui.c:2498 (188,152) 711, 4
|
|
// v4k_ui.c:2499 (-2147483648,156) -2147483648, 4
|
|
// v4k_ui.c:2500 (182,118) 728,409
|
|
// v4k_ui.c:2501 (188,152) 711,368 << ok
|
|
// v4k_ui.c:2502 (-2147483648,156) -2147483648, 4
|
|
// v4k_ui.c:2503 (-2147483648,152) -2147483648, 4
|
|
|
|
// popup
|
|
// v4k_ui.c:2497 ( 9, 30) 350, 24
|
|
// v4k_ui.c:2498 ( 19, 58) 326, 24
|
|
// v4k_ui.c:2499 ( 19, 82) 336, 24
|
|
// v4k_ui.c:2500 ( 4, 29) 360,460
|
|
// v4k_ui.c:2501 ( 9, 30) 350,458 << ok
|
|
// v4k_ui.c:2502 ( 19, 82) 336, 24
|
|
// v4k_ui.c:2503 (359, 58) 336, 24
|
|
|
|
bounds = nk_window_get_content_region(ui_ctx);
|
|
if( !windowed && *inlined ) bounds.h *= 0.80;
|
|
|
|
clicked = browser_run(ui_ctx, browser, windowed, bounds);
|
|
if( clicked ) {
|
|
strcatf(result, "%d", 0);
|
|
(*result)[0] = '\0';
|
|
strcatf(result, "%s", browser->file);
|
|
if( inlined ) *inlined = 0;
|
|
|
|
const char *target = ifdef(win32, "/", "\\"), *replace = ifdef(win32, "\\", "/");
|
|
strswap(*result, target, replace);
|
|
|
|
if( output ) *output = *result;
|
|
}
|
|
}
|
|
|
|
return clicked;
|
|
}
|
|
|
|
/*
|
|
// demo:
|
|
static const char *file;
|
|
if( ui_panel("inlined", 0)) {
|
|
static bool show_browser = 0;
|
|
if( ui_button("my button") ) { show_browser = true; }
|
|
if( ui_browse(&file, &show_browser) ) puts(file);
|
|
ui_panel_end();
|
|
}
|
|
if( ui_window("windowed", 0) ) {
|
|
if( ui_browse(&file, NULL) ) puts(file);
|
|
ui_window_end();
|
|
}
|
|
*/
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
int ui_demo(int do_windows) {
|
|
static int integer = 42;
|
|
static bool toggle = true;
|
|
static bool boolean = true;
|
|
static float floating = 3.14159;
|
|
static float float2[2] = {1,2};
|
|
static float float3[3] = {1,2,3};
|
|
static float float4[4] = {1,2,3,4};
|
|
static float rgbf[3] = {0.84,0.67,0.17};
|
|
static float rgbaf[4] = {0.67,0.90,0.12,1};
|
|
static unsigned rgb = CYAN;
|
|
static unsigned rgba = PINK;
|
|
static float slider = 0.5f;
|
|
static float slider2 = 0.5f;
|
|
static char string[64] = "hello world 123";
|
|
static int item = 0; const char *list[] = {"one","two","three"};
|
|
static bool show_dialog = false;
|
|
static bool show_browser = false;
|
|
static const char* browsed_file = "";
|
|
static uint8_t bitmask = 0x55;
|
|
static int hits = 0;
|
|
static int window1 = 0, window2 = 0, window3 = 0;
|
|
static int disable_all = 0;
|
|
|
|
if( ui_panel("UI", 0) ) {
|
|
int choice = ui_toolbar("Browser;Toast@Notify;Toggle on/off");
|
|
if(choice == 1) show_browser = true;
|
|
if(choice == 2) ui_notify(va("My random toast (%d)", rand()), va("This is notification #%d", ++hits));
|
|
if(choice == 3) disable_all ^= 1;
|
|
|
|
if( disable_all ) ui_disable();
|
|
|
|
if( ui_browse(&browsed_file, &show_browser) ) puts(browsed_file);
|
|
|
|
if( ui_section("Labels")) {}
|
|
if( ui_label("my label")) {}
|
|
if( ui_label("my label with tooltip@built on " __DATE__ " " __TIME__)) {}
|
|
if( ui_label2_toolbar("my toolbar", ICON_MD_STAR ICON_MD_STAR_OUTLINE ICON_MD_BOOKMARK ICON_MD_BOOKMARK_BORDER) ) {}
|
|
//if( ui_label2_wrap("my long label", "and some long long long long text wrapped")) {}
|
|
|
|
if( ui_section("Types")) {}
|
|
if( ui_bool("my bool", &boolean) ) puts("bool changed");
|
|
if( ui_int("my int", &integer) ) puts("int changed");
|
|
if( ui_float("my float", &floating) ) puts("float changed");
|
|
if( ui_buffer("my string", string, 64) ) puts("string changed");
|
|
|
|
if( ui_section("Vectors") ) {}
|
|
if( ui_float2("my float2", float2) ) puts("float2 changed");
|
|
if( ui_float3("my float3", float3) ) puts("float3 changed");
|
|
if( ui_float4("my float4", float4) ) puts("float4 changed");
|
|
|
|
if( ui_section("Lists")) {}
|
|
if( ui_list("my list", list, 3, &item ) ) puts("list changed");
|
|
|
|
if( ui_section("Colors")) {}
|
|
if( ui_color3("my color3", &rgb) ) puts("color3 changed");
|
|
if( ui_color4("my color4@this is a tooltip", &rgba) ) puts("color4 changed");
|
|
if( ui_color3f("my color3f", rgbf) ) puts("color3f changed");
|
|
if( ui_color4f("my color4f@this is a tooltip", rgbaf) ) puts("color4f changed");
|
|
|
|
if( ui_section("Sliders")) {}
|
|
if( ui_slider("my slider", &slider)) puts("slider changed");
|
|
if( ui_slider2("my slider 2", &slider2, va("%.2f", slider2))) puts("slider2 changed");
|
|
|
|
if( do_windows ) {
|
|
if( ui_section("Windows")) {}
|
|
int show = ui_buttons(3, "Container", "SubPanel", "SubRender");
|
|
if( show == 1 ) window1 = 1;
|
|
if( show == 2 ) window2 = 1;
|
|
if( show == 3 ) window3 = 1;
|
|
}
|
|
|
|
if( ui_section("Others")) {}
|
|
if( ui_bitmask8("my bitmask", &bitmask) ) printf("bitmask changed %x\n", bitmask);
|
|
if( ui_toggle("my toggle", &toggle) ) printf("toggle %s\n", toggle ? "on":"off");
|
|
if( ui_image("my image", texture_checker().id, 0, 0) ) { puts("image clicked"); }
|
|
|
|
if( ui_separator() ) {}
|
|
if( ui_button("my button") ) { puts("button clicked"); show_dialog = true; }
|
|
if( ui_buttons(2, "yes", "no") ) { puts("button clicked"); }
|
|
if( ui_buttons(3, "yes", "no", "maybe") ) { puts("button clicked"); }
|
|
if( ui_dialog("my dialog", __FILE__ "\n" __DATE__ "\n" "Public Domain.", 2/*two buttons*/, &show_dialog) ) {}
|
|
|
|
if( disable_all ) ui_enable(); // restore enabled state
|
|
|
|
ui_panel_end();
|
|
}
|
|
|
|
if( !do_windows ) return 0;
|
|
|
|
// window api showcasing
|
|
if( ui_window("Container demo", &window1) ) {
|
|
ui_label("label #1");
|
|
if( ui_bool("my bool", &boolean) ) puts("bool changed");
|
|
if( ui_int("my int", &integer) ) puts("int changed");
|
|
if( ui_float("my float", &floating) ) puts("float changed");
|
|
if( ui_buffer("my string", string, 64) ) puts("string changed");
|
|
ui_window_end();
|
|
}
|
|
|
|
if( ui_window("SubPanel demo", &window2) ) {
|
|
if( ui_panel("panel #2", 0) ) {
|
|
ui_label("label #2");
|
|
if( ui_bool("my bool", &boolean) ) puts("bool changed");
|
|
if( ui_int("my int", &integer) ) puts("int changed");
|
|
if( ui_float("my float", &floating) ) puts("float changed");
|
|
if( ui_buffer("my string", string, 64) ) puts("string changed");
|
|
ui_panel_end();
|
|
}
|
|
ui_window_end();
|
|
}
|
|
|
|
if( ui_window("SubRender demo", &window3) ) {
|
|
if( ui_panel("panel #3A", 0) ) {
|
|
if( ui_bool("my bool", &boolean) ) puts("bool changed");
|
|
if( ui_int("my int", &integer) ) puts("int changed");
|
|
if( ui_float("my float", &floating) ) puts("float changed");
|
|
if( ui_buffer("my string", string, 64) ) puts("string changed");
|
|
if( ui_separator() ) {}
|
|
if( ui_slider("my slider", &slider)) puts("slider changed");
|
|
if( ui_slider2("my slider 2", &slider2, va("%.2f", slider2))) puts("slider2 changed");
|
|
ui_panel_end();
|
|
}
|
|
if( ui_panel("panel #3B", 0) ) {
|
|
if( ui_bool("my bool", &boolean) ) puts("bool changed");
|
|
if( ui_int("my int", &integer) ) puts("int changed");
|
|
if( ui_float("my float", &floating) ) puts("float changed");
|
|
if( ui_buffer("my string", string, 64) ) puts("string changed");
|
|
if( ui_separator() ) {}
|
|
if( ui_slider("my slider", &slider)) puts("slider changed");
|
|
if( ui_slider2("my slider 2", &slider2, va("%.2f", slider2))) puts("slider2 changed");
|
|
ui_panel_end();
|
|
}
|
|
|
|
const char *title = "SubRender demo";
|
|
struct nk_window *win = nk_window_find(ui_ctx, title);
|
|
if( win ) {
|
|
enum { menubar_height = 65 }; // title bar (~32) + menu bounds (~25)
|
|
struct nk_rect bounds = win->bounds; bounds.y += menubar_height; bounds.h -= menubar_height;
|
|
#if 1
|
|
ddraw_flush();
|
|
|
|
// @fixme: this is breaking rendering when post-fxs are in use. edit: cannot reproduce
|
|
static texture_t tx = {0};
|
|
if( texture_rec_begin(&tx, bounds.w, bounds.h )) {
|
|
glClearColor(0.15,0.15,0.15,1);
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
ddraw_grid(10);
|
|
ddraw_flush();
|
|
texture_rec_end(&tx);
|
|
}
|
|
struct nk_image image = nk_image_id((int)tx.id);
|
|
nk_draw_image_flipped(nk_window_get_canvas(ui_ctx), bounds, &image, nk_white);
|
|
#else
|
|
static video_t *v = NULL;
|
|
do_once v = video( "bjork-all-is-full-of-love.mp4", VIDEO_RGB );
|
|
|
|
texture_t *textures = video_decode( v );
|
|
|
|
struct nk_image image = nk_image_id((int)textures[0].id);
|
|
nk_draw_image(nk_window_get_canvas(ui_ctx), bounds, &image, nk_white);
|
|
#endif
|
|
}
|
|
|
|
ui_window_end();
|
|
}
|
|
return 0;
|
|
}
|