2023-07-30 19:18:50 +00:00
# ifndef UI_ICONS_SMALL
//#define UI_ICONS_SMALL 1
# endif
2023-09-17 09:50:34 +00:00
# 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"
2023-07-30 19:18:50 +00:00
# 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
2023-09-17 09:50:34 +00:00
# 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)
2023-07-30 19:18:50 +00:00
# elif UI_FONT_SMALL
2023-09-17 09:50:34 +00:00
# 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)
2023-07-30 19:18:50 +00:00
# else
2023-09-21 10:45:42 +00:00
# define UI_FONT_REGULAR_SIZE UI_FONT_ENUM(14.5,16)
# define UI_FONT_HEADING_SIZE UI_FONT_ENUM(16,17.5)
2023-09-17 09:50:34 +00:00
# define UI_FONT_TERMINAL_SIZE UI_FONT_ENUM(14,14)
2023-07-30 19:18:50 +00:00
# endif
# if UI_ICONS_SMALL
2023-09-17 09:50:34 +00:00
# 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)
2023-07-30 19:18:50 +00:00
# else
2023-09-17 09:50:34 +00:00
# define UI_ICON_FONTSIZE UI_FONT_ENUM(20,20)
# define UI_ICON_SPACING_X UI_FONT_ENUM(0,0)
2023-10-01 06:49:08 +00:00
# define UI_ICON_SPACING_Y UI_FONT_ENUM(6.5f,5.0f)
2023-07-30 19:18:50 +00:00
# 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_MIN_MD
# define UI_ICON_MED ICON_MAX_16_MD
# define UI_ICON_MAX ICON_MAX_MD
# 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)...
2023-10-14 19:47:24 +00:00
int datalen = 0 ;
for ( char * data = vfs_load ( UI_FONT_REGULAR , & datalen ) ; data ; data = 0 ) {
2023-07-30 19:18:50 +00:00
float font_size = UI_FONT_REGULAR_SIZE ;
struct nk_font_config cfg = nk_font_config ( font_size ) ;
cfg . oversample_v = 2 ;
cfg . pixel_snap = 0 ;
// 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;
2023-10-14 19:47:24 +00:00
struct nk_font * regular = nk_font_atlas_add_from_memory ( atlas , data , datalen , font_size , & cfg ) ; font = regular ? regular : font ;
2023-07-30 19:18:50 +00:00
}
// ...with icons embedded on it.
2023-10-21 09:18:13 +00:00
static struct icon_font {
const char * file ; nk_rune range [ 3 ] ;
} icons [ ] = {
{ " MaterialIconsSharp-Regular.otf " , { UI_ICON_MIN , UI_ICON_MED /*MAX*/ , 0 } } , // "MaterialIconsOutlined-Regular.otf" "MaterialIcons-Regular.ttf"
{ " materialdesignicons-webfont.ttf " , { 0xF68C /*ICON_MIN_MDI*/ , 0xF1C80 /*ICON_MAX_MDI*/ , 0 } } ,
} ;
for ( int f = 0 ; f < countof ( icons ) ; + + f )
for ( char * data = vfs_load ( icons [ f ] . file , & datalen ) ; data ; data = 0 ) {
2023-07-30 19:18:50 +00:00
struct nk_font_config cfg = nk_font_config ( UI_ICON_FONTSIZE ) ;
2023-10-21 09:18:13 +00:00
cfg . range = icons [ f ] . range ; // nk_font_default_glyph_ranges();
2023-07-30 19:18:50 +00:00
cfg . merge_mode = 1 ;
cfg . spacing . x + = UI_ICON_SPACING_X ;
cfg . spacing . y + = UI_ICON_SPACING_Y ;
// cfg.font->ascent += ICON_ASCENT;
// cfg.font->height += ICON_HEIGHT;
cfg . oversample_h = 1 ;
cfg . oversample_v = 1 ;
cfg . pixel_snap = 1 ;
2023-10-14 19:47:24 +00:00
struct nk_font * icons = nk_font_atlas_add_from_memory ( atlas , data , datalen , UI_ICON_FONTSIZE , & cfg ) ;
2023-07-30 19:18:50 +00:00
}
// Monospaced font. Used in terminals or consoles.
2023-10-14 19:47:24 +00:00
for ( char * data = vfs_load ( UI_FONT_TERMINAL , & datalen ) ; data ; data = 0 ) {
2023-09-17 09:50:34 +00:00
const float font_size = UI_FONT_REGULAR_SIZE ;
2023-07-30 19:18:50 +00:00
static const nk_rune icon_range [ ] = { 32 , 127 , 0 } ;
2023-09-17 09:50:34 +00:00
struct nk_font_config cfg = nk_font_config ( font_size ) ;
2023-07-30 19:18:50 +00:00
cfg . range = icon_range ;
cfg . oversample_h = 1 ;
cfg . oversample_v = 1 ;
cfg . pixel_snap = 1 ;
2023-09-17 09:50:34 +00:00
// struct nk_font *proggy = nk_font_atlas_add_default(atlas, font_size, &cfg);
2023-10-14 19:47:24 +00:00
struct nk_font * bold = nk_font_atlas_add_from_memory ( atlas , data , datalen , font_size , & cfg ) ;
2023-07-30 19:18:50 +00:00
}
// Extra optional fonts from here...
2023-10-14 19:47:24 +00:00
for ( char * data = vfs_load ( UI_FONT_HEADING , & datalen ) ; data ; data = 0 ) {
struct nk_font * bold = nk_font_atlas_add_from_memory ( atlas , data , datalen , UI_FONT_HEADING_SIZE , 0 ) ; // font = bold ? bold : font;
2023-07-30 19:18:50 +00:00
}
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);
}
2023-09-25 04:29:29 +00:00
static void nk_config_custom_theme ( ) {
2023-07-30 19:18:50 +00:00
# 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 ) ;
2023-09-27 06:49:59 +00:00
table [ NK_COLOR_WINDOW ] = nk_rgba ( 42 , 42 , 42 , 245 ) ;
table [ NK_COLOR_HEADER ] = nk_rgba ( 51 , 51 , 56 , 245 ) ;
2023-07-30 19:18:50 +00:00
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;
2023-09-25 10:24:49 +00:00
// @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
2023-07-30 19:18:50 +00:00
nk_style_default ( ui_ctx ) ;
nk_style_from_table ( ui_ctx , table ) ;
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 ;
2023-10-07 17:34:09 +00:00
s - > combo . button_padding . x = - 18 ;
2023-07-30 19:18:50 +00:00
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_items ; // 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 ;
const struct nk_user_font * f = style - > font ;
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 = f - > width ( f - > userdata , f - > height , 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 ( ) ;
2023-09-25 04:29:29 +00:00
nk_config_custom_theme ( ) ;
2023-07-30 19:18:50 +00:00
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 ;
}
2023-09-17 20:56:51 +00:00
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 } ) ;
2023-09-18 07:43:28 +00:00
return 0 ;
2023-09-17 20:56:51 +00:00
}
vec2 ui_get_dims ( ) {
return ( vec2 ) { nk_window_get_width ( ui_ctx ) , nk_window_get_height ( ui_ctx ) } ;
}
2023-07-30 19:18:50 +00:00
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
2023-09-27 06:49:59 +00:00
int ui_set_enable_ ( int enabled ) {
2023-07-30 19:18:50 +00:00
static struct nk_style off , on ;
do_once {
off = on = ui_ctx - > style ;
float alpha = 0.5f ;
off . text . color . a * = alpha ;
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 ;
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 ;
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 ;
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 ;
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 ;
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 ;
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 ;
}
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 ;
2023-09-27 06:49:59 +00:00
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 ) ;
2023-07-30 19:18:50 +00:00
}
2023-09-25 04:29:29 +00:00
int ui_enabled ( ) {
return ui_is_enabled ;
2023-07-30 19:18:50 +00:00
}
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 ;
2023-10-01 06:49:08 +00:00
ui_enable ( ) ;
2023-07-30 19:18:50 +00:00
}
}
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
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);
2023-09-25 04:29:29 +00:00
2023-09-25 10:24:49 +00:00
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
2023-07-30 19:18:50 +00:00
nk_glfw3_render ( & nk_glfw , NK_ANTI_ALIASING_ON , MAX_VERTEX_MEMORY , MAX_ELEMENT_MEMORY ) ;
2023-09-25 04:29:29 +00:00
glColorMask ( GL_TRUE , GL_TRUE , GL_TRUE , GL_TRUE ) ; // @transparent
2023-07-30 19:18:50 +00:00
# if is(ems)
glFinish ( ) ;
# endif
2023-09-21 10:45:42 +00:00
2023-07-30 19:18:50 +00:00
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 ;
2023-09-21 10:45:42 +00:00
int closed = 0 ;
2023-07-30 19:18:50 +00:00
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
2023-09-21 10:45:42 +00:00
closed = 1 ;
2023-07-30 19:18:50 +00:00
}
2023-09-21 10:45:42 +00:00
// @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
2023-07-30 19:18:50 +00:00
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)
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 ;
ui_collapse_state = nk_tree_base_ ( ui_ctx , NK_TREE_NODE , 0 , label , NK_MINIMIZED , 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 ( ) {
2023-10-13 10:59:44 +00:00
struct nk_rect bounds = nk_widget_bounds ( ui_ctx ) ; // = nk_window_get_bounds(ui_ctx);
2023-07-30 19:18:50 +00:00
bounds . y - = 25 ;
return ui_popups ( ) ? 0 : nk_contextual_begin ( ui_ctx , 0 , nk_vec2 ( 150 , 300 ) , bounds ) ;
}
2023-10-13 10:59:44 +00:00
int ui_contextual_end ( int close ) {
if ( close ) nk_contextual_close ( ui_ctx ) ;
2023-07-30 19:18:50 +00:00
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 ;
}
2023-10-13 10:59:44 +00:00
ui_contextual_end ( 0 ) ;
2023-07-30 19:18:50 +00:00
}
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 ) ;
2023-10-01 06:49:08 +00:00
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 ) ;
2023-07-30 19:18:50 +00:00
}
int skip_color_tab = label & & label [ 0 ] = = ' ! ' ;
if ( skip_color_tab ) label + + ;
2023-10-01 06:49:08 +00:00
int spacing = 8 ; // between left colorbar and content
2023-07-30 19:18:50 +00:00
struct nk_window * win = ui_ctx - > current ;
struct nk_panel * layout = win - > layout ;
2023-10-01 06:49:08 +00:00
layout - > at_x + = spacing ;
layout - > bounds . w - = spacing ;
2023-07-30 19:18:50 +00:00
if ( ! skip_color_tab ) {
2023-10-01 06:49:08 +00:00
float w = is_hovering ? 4 : 2 ; // spacing*3/4 : spacing/2-1;
bounds . w = w ;
2023-07-30 19:18:50 +00:00
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 ) ) ;
}
2023-10-01 06:49:08 +00:00
if ( ! label | | ! label [ 0 ] ) {
nk_label ( ui_ctx , " " , alignment ) ;
layout - > at_x - = spacing ;
layout - > bounds . w + = spacing ;
return 0 ;
}
2023-07-30 19:18:50 +00:00
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 ;
2023-08-04 19:39:36 +00:00
struct nk_font * font = bold & & nk_glfw . atlas . fonts - > next ? nk_glfw . atlas . fonts - > next - > next /*3rd font*/ : NULL ; // list
2023-07-30 19:18:50 +00:00
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 ) ) {
2023-10-23 13:25:03 +00:00
nk_tooltip ( ui_ctx , split + 1 ) ; // @fixme: not working under ui_disable() state
2023-07-30 19:18:50 +00:00
}
2023-10-01 06:49:08 +00:00
layout - > at_x - = spacing ;
layout - > bounds . w + = spacing ;
2023-07-30 19:18:50 +00:00
// 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 * text ) {
int align = text [ 0 ] = = ' > ' ? ( text + + , NK_TEXT_RIGHT ) : text [ 0 ] = = ' = ' ? ( text + + , NK_TEXT_CENTERED ) : text [ 0 ] = = ' < ' ? ( text + + , NK_TEXT_LEFT ) : NK_TEXT_LEFT ;
nk_layout_row_dynamic ( ui_ctx , 0 , 1 ) ;
return ui_label_ ( text , align ) ;
}
int ui_label2 ( const char * label , const char * text_ ) {
2023-10-01 06:49:08 +00:00
nk_layout_row_dynamic ( ui_ctx , 0 , 2 ) ;
2023-07-30 19:18:50 +00:00
2023-10-13 10:59:44 +00:00
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 ;
2023-07-30 19:18:50 +00:00
ui_label_ ( label , align1 ) ;
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 ;
}
2023-09-27 06:49:59 +00:00
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?)
2023-10-01 06:49:08 +00:00
nk_layout_row_dynamic ( ui_ctx , 0 , 2 ) ;
2023-09-27 06:49:59 +00:00
ui_label_ ( label , NK_TEXT_LEFT ) ;
nk_text_wrap ( ui_ctx , str , strlen ( str ) ) ;
return 0 ;
}
2023-07-30 19:18:50 +00:00
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 ) {
2023-10-20 17:55:43 +00:00
app_beep ( ) ;
2023-07-30 19:18:50 +00:00
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 * text ) {
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
2023-09-25 04:29:29 +00:00
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 ) ) ;
2023-07-30 19:18:50 +00:00
2023-09-25 04:29:29 +00:00
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 ) ) ;
2023-07-30 19:18:50 +00:00
# endif
}
struct nk_rect bounds = nk_widget_bounds ( ui_ctx ) ;
const char * split = strchr ( text , ' @ ' ) , * tooltip = split + 1 ;
2023-09-21 10:45:42 +00:00
int ret = nk_button_text ( ui_ctx , text , split ? ( int ) ( split - text ) : strlen ( text ) ) ;
2023-07-30 19:18:50 +00:00
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 , . . . ) {
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 ;
va_list list ;
va_start ( list , buttons ) ;
for ( int i = 0 ; i < buttons ; + + i ) {
if ( ui_button_ ( va_arg ( list , const char * ) ) ) 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 ) {
2023-10-01 06:49:08 +00:00
nk_layout_row_dynamic ( ui_ctx , 0 , 2 ) ;
2023-07-30 19:18:50 +00:00
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 ;
}
int ui_color4f ( const char * label , float * color4 ) {
float c [ 4 ] = { color4 [ 0 ] * 255 , color4 [ 1 ] * 255 , color4 [ 2 ] * 255 , color4 [ 3 ] * 255 } ;
int ret = ui_color4 ( label , c ) ;
for ( int i = 0 ; i < 4 ; + + i ) color4 [ i ] = c [ i ] / 255.0f ;
return ret ;
}
static enum color_mode { COL_RGB , COL_HSV } ui_color_mode = COL_RGB ;
int ui_color4 ( const char * label , float * color4 ) {
2023-10-01 06:49:08 +00:00
nk_layout_row_dynamic ( ui_ctx , 0 , 2 ) ;
2023-07-30 19:18:50 +00:00
ui_label_ ( label , NK_TEXT_LEFT ) ;
struct nk_colorf after = { color4 [ 0 ] * ui_alpha / 255 , color4 [ 1 ] * ui_alpha / 255 , color4 [ 2 ] * ui_alpha / 255 , color4 [ 3 ] / 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_propertyf ( ui_ctx , " #R: " , 0 , after . r , 1.0f , 0.01f , 0.005f ) ;
after . g = nk_propertyf ( ui_ctx , " #G: " , 0 , after . g , 1.0f , 0.01f , 0.005f ) ;
after . b = nk_propertyf ( ui_ctx , " #B: " , 0 , after . b , 1.0f , 0.01f , 0.005f ) ;
after . a = nk_propertyf ( ui_ctx , " #A: " , 0 , after . a , 1.0f , 0.01f , 0.005f ) ;
} else {
float hsva [ 4 ] ;
nk_colorf_hsva_fv ( hsva , after ) ;
hsva [ 0 ] = nk_propertyf ( ui_ctx , " #H: " , 0 , hsva [ 0 ] , 1.0f , 0.01f , 0.05f ) ;
hsva [ 1 ] = nk_propertyf ( ui_ctx , " #S: " , 0 , hsva [ 1 ] , 1.0f , 0.01f , 0.05f ) ;
hsva [ 2 ] = nk_propertyf ( ui_ctx , " #V: " , 0 , hsva [ 2 ] , 1.0f , 0.01f , 0.05f ) ;
hsva [ 3 ] = nk_propertyf ( ui_ctx , " #A: " , 0 , hsva [ 3 ] , 1.0f , 0.01f , 0.05f ) ;
after = nk_hsva_colorfv ( hsva ) ;
}
color4 [ 0 ] = after . r * 255 ;
color4 [ 1 ] = after . g * 255 ;
color4 [ 2 ] = after . b * 255 ;
color4 [ 3 ] = after . a * 255 ;
nk_combo_end ( ui_ctx ) ;
}
return ! ! memcmp ( & before . r , & after . r , sizeof ( struct nk_colorf ) ) ;
}
int ui_color3f ( const char * label , float * color3 ) {
float c [ 3 ] = { color3 [ 0 ] * 255 , color3 [ 1 ] * 255 , color3 [ 2 ] * 255 } ;
int ret = ui_color3 ( label , c ) ;
for ( int i = 0 ; i < 3 ; + + i ) color3 [ i ] = c [ i ] / 255.0f ;
return ret ;
}
int ui_color3 ( const char * label , float * color3 ) {
2023-10-01 06:49:08 +00:00
nk_layout_row_dynamic ( ui_ctx , 0 , 2 ) ;
2023-07-30 19:18:50 +00:00
ui_label_ ( label , NK_TEXT_LEFT ) ;
struct nk_colorf after = { color3 [ 0 ] * ui_alpha / 255 , color3 [ 1 ] * ui_alpha / 255 , color3 [ 2 ] * ui_alpha / 255 , 1 } , 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_propertyf ( ui_ctx , " #R: " , 0 , after . r , 1.0f , 0.01f , 0.005f ) ;
after . g = nk_propertyf ( ui_ctx , " #G: " , 0 , after . g , 1.0f , 0.01f , 0.005f ) ;
after . b = nk_propertyf ( ui_ctx , " #B: " , 0 , after . b , 1.0f , 0.01f , 0.005f ) ;
} else {
float hsva [ 4 ] ;
nk_colorf_hsva_fv ( hsva , after ) ;
hsva [ 0 ] = nk_propertyf ( ui_ctx , " #H: " , 0 , hsva [ 0 ] , 1.0f , 0.01f , 0.05f ) ;
hsva [ 1 ] = nk_propertyf ( ui_ctx , " #S: " , 0 , hsva [ 1 ] , 1.0f , 0.01f , 0.05f ) ;
hsva [ 2 ] = nk_propertyf ( ui_ctx , " #V: " , 0 , hsva [ 2 ] , 1.0f , 0.01f , 0.05f ) ;
after = nk_hsva_colorfv ( hsva ) ;
}
color3 [ 0 ] = after . r * 255 ;
color3 [ 1 ] = after . g * 255 ;
color3 [ 2 ] = after . b * 255 ;
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 ) {
2023-10-01 06:49:08 +00:00
nk_layout_row_dynamic ( ui_ctx , 0 , 2 ) ;
2023-07-30 19:18:50 +00:00
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 ) {
// return ui_slider2(label, slider, va("%.2f ", *slider));
2023-10-01 06:49:08 +00:00
nk_layout_row_dynamic ( ui_ctx , 0 , 2 ) ;
2023-07-30 19:18:50 +00:00
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 ) {
2023-10-01 06:49:08 +00:00
nk_layout_row_dynamic ( ui_ctx , 0 , 2 ) ;
2023-07-30 19:18:50 +00:00
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 ) {
2023-10-01 06:49:08 +00:00
nk_layout_row_dynamic ( ui_ctx , 0 , 2 ) ;
2023-07-30 19:18:50 +00:00
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 ;
}
int ui_int ( const char * label , int * v ) {
2023-10-01 06:49:08 +00:00
nk_layout_row_dynamic ( ui_ctx , 0 , 2 ) ;
2023-07-30 19:18:50 +00:00
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 ) {
2023-10-01 06:49:08 +00:00
nk_layout_row_dynamic ( ui_ctx , 0 , 2 ) ;
2023-07-30 19:18:50 +00:00
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_short ( const char * label , short * v ) {
int i = * v , ret = ui_int ( label , & i ) ;
return * v = ( short ) i , ret ;
}
int ui_float ( const char * label , float * v ) {
2023-10-01 06:49:08 +00:00
nk_layout_row_dynamic ( ui_ctx , 0 , 2 ) ;
2023-07-30 19:18:50 +00:00
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 ) {
2023-10-01 06:49:08 +00:00
nk_layout_row_dynamic ( ui_ctx , 0 , 2 ) ;
2023-07-30 19:18:50 +00:00
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 ( minf > maxf ) return ui_clampf ( label , v , maxf , minf ) ;
2023-10-01 06:49:08 +00:00
nk_layout_row_dynamic ( ui_ctx , 0 , 2 ) ;
2023-07-30 19:18:50 +00:00
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 ] ;
}
2023-10-07 17:34:09 +00:00
static bool ui_float_sign = 0 ;
2023-07-30 19:18:50 +00:00
int ui_float2 ( const char * label , float * v ) {
2023-10-01 06:49:08 +00:00
nk_layout_row_dynamic ( ui_ctx , 0 , 2 ) ;
2023-07-30 19:18:50 +00:00
ui_label_ ( label , NK_TEXT_LEFT ) ;
2023-10-07 17:34:09 +00:00
char * buffer = ui_float_sign ?
- - ui_float_sign , va ( " %+.3f %+.3f " , v [ 0 ] , v [ 1 ] ) :
va ( " %.3f, %.3f " , v [ 0 ] , v [ 1 ] ) ;
2023-07-30 19:18:50 +00:00
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 ) {
2023-10-01 06:49:08 +00:00
nk_layout_row_dynamic ( ui_ctx , 0 , 2 ) ;
2023-07-30 19:18:50 +00:00
ui_label_ ( label , NK_TEXT_LEFT ) ;
2023-10-07 17:34:09 +00:00
char * buffer = ui_float_sign ?
- - ui_float_sign , va ( " %+.2f %+.2f %+.2f " , v [ 0 ] , v [ 1 ] , v [ 2 ] ) :
va ( " %.2f, %.2f, %.2f " , v [ 0 ] , v [ 1 ] , v [ 2 ] ) ;
2023-07-30 19:18:50 +00:00
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 ) {
2023-10-01 06:49:08 +00:00
nk_layout_row_dynamic ( ui_ctx , 0 , 2 ) ;
2023-07-30 19:18:50 +00:00
ui_label_ ( label , NK_TEXT_LEFT ) ;
2023-10-07 17:34:09 +00:00
char * buffer = ui_float_sign ?
- - ui_float_sign , 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 ] ) ;
2023-07-30 19:18:50 +00:00
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 ] ;
}
2023-10-07 17:34:09 +00:00
2023-07-30 19:18:50 +00:00
return 0 ;
}
2023-10-01 06:49:08 +00:00
int ui_mat33 ( const char * label , float M [ 9 ] ) {
2023-10-07 17:34:09 +00:00
ui_float_sign = 3 ;
2023-10-01 06:49:08 +00:00
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 ] ) {
2023-10-07 17:34:09 +00:00
ui_float_sign = 3 ;
2023-10-01 06:49:08 +00:00
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 ] ) {
2023-10-07 17:34:09 +00:00
ui_float_sign = 4 ;
2023-10-01 06:49:08 +00:00
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 ;
}
2023-07-30 19:18:50 +00:00
int ui_buffer ( const char * label , char * buffer , int buflen ) {
2023-10-01 06:49:08 +00:00
nk_layout_row_dynamic ( ui_ctx , 0 , 2 ) ;
2023-07-30 19:18:50 +00:00
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 ) {
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 ( ) {
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 ) {
2023-10-01 06:49:08 +00:00
nk_layout_row_dynamic ( ui_ctx , sh < 30 | | id = = texture_checker ( ) . id ? 0 : sh , 1 + ( label & & label [ 0 ] ) ) ;
2023-07-30 19:18:50 +00:00
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 * map_name , colormap_t * cm ) {
int ret = 0 ;
if ( ! cm - > texture ) {
const char * title = va ( " %s (no image) " , map_name ) ;
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) " , map_name , cm - > texture - > filename ) ) ; // @fixme: labelf would crash?
const char * fmt [ ] = { " " , " R " , " RG " , " RGB " , " RGBA " } ;
const char * title = va ( " %s %dx%d %s " , map_name , w , h , fmt [ cm - > texture - > n ] ) ;
if ( ui_image ( title , cm - > texture - > id , 128 , 128 ) ) {
ret = 2 ;
}
}
if ( ui_color4f ( va ( " %s Color " , map_name ) , ( float * ) & cm - > color ) ) {
ret = 1 ;
}
return ret ;
}
int ui_radio ( const char * label , const char * * items , int num_items , int * selector ) {
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 * section ) {
//ui_separator();
return ui_label ( va ( " *%s " , section ) ) ;
}
int ui_dialog ( const char * title , const char * text , int choices , bool * show ) { // @fixme: return
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 , title , NK_WINDOW_BORDER | NK_WINDOW_CLOSABLE , s ) ) {
nk_layout_row_dynamic ( ui_ctx , 20 , 1 ) ;
for ( char label [ 1024 ] ; * text & & sscanf ( text , " %[^ \r \n ] " , label ) ; ) {
nk_label ( ui_ctx , label , NK_TEXT_LEFT ) ;
text + = strlen ( label ) ; 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 ;
}
2023-09-27 06:49:59 +00:00
# define ui_bitmask_template(X) \
int ui_bitmask # # X ( const char * label , uint # # X # # _t * enabled ) { \
2023-07-30 19:18:50 +00:00
/* @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 ; \
}
2023-09-27 06:49:59 +00:00
ui_bitmask_template ( 8 ) ;
ui_bitmask_template ( 16 ) ;
//ui_bitmask_template(32);
2023-07-30 19:18:50 +00:00
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 , 400 , 300 } ; // @fixme: how to retrieve inlined region below? (inlined)
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 ; }
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 rgb [ 3 ] = { 0.84 , 0.67 , 0.17 } ;
static float rgba [ 4 ] = { 0.67 , 0.90 , 0.12 , 1 } ;
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 ;
2023-09-27 06:49:59 +00:00
if ( disable_all ) ui_disable ( ) ;
2023-07-30 19:18:50 +00:00
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__ ) ) { }
2023-09-27 06:49:59 +00:00
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")) {}
2023-07-30 19:18:50 +00:00
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_color3f ( " my color3 " , rgb ) ) puts ( " color3 changed " ) ;
if ( ui_color4f ( " my color4@this is a tooltip " , rgba ) ) puts ( " color4 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 " ) ) { }
2023-09-27 06:49:59 +00:00
if ( ui_bitmask8 ( " my bitmask " , & bitmask ) ) printf ( " bitmask changed %x \n " , bitmask ) ;
2023-07-30 19:18:50 +00:00
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 ) ) { }
2023-09-27 06:49:59 +00:00
if ( disable_all ) ui_enable ( ) ; // restore enabled state
2023-07-30 19:18:50 +00:00
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 ;
}