diff --git a/demo/common/nuklear_console_demo.c b/demo/common/nuklear_console_demo.c index 346398b..18d49bc 100644 --- a/demo/common/nuklear_console_demo.c +++ b/demo/common/nuklear_console_demo.c @@ -1,5 +1,5 @@ -#include // strcmp #include // snprintf +#include // strcmp #include "../../vendor/Nuklear/demo/common/style.c" @@ -10,56 +10,89 @@ #include "../../nuklear_console.h" // Demo -static struct nk_console* console = NULL; +static struct nk_context* ctx; +static struct nk_console* console; static struct nk_gamepads gamepads; static nk_bool shouldClose = nk_false; -// Theme -static int theme = THEME_DRACULA; - -// Progress -static nk_size progressValue = 50; - -// Combobox -static int weapon = 1; - -// Property -static int property_int_test = 20; -static float property_float_test = 0.4f; - -// Slider -static int slider_int_test = 20; -static float slider_float_test = 0.4f; - -// Radio -static int radio_option = 1; -static int radio_option2 = 0; - -// Checkbox -static nk_bool checkbox1 = nk_false; -static nk_bool checkbox2 = nk_false; -static nk_bool checkbox3 = nk_false; -static nk_bool checkbox4 = nk_false; -static nk_bool checkbox5 = nk_false; -static nk_bool checkbox6 = nk_true; - -// Messages -static int message_count = 0; - -// File -static char file_path_buffer[1024] = {0}; -static int file_path_buffer_size = 1024; - -// Textedit -static const int textedit_buffer_size = 256; -static char textedit_buffer[256] = "vurtun"; - -// Input -static int gamepad_number = 0; -static enum nk_gamepad_button gamepad_button = NK_GAMEPAD_BUTTON_A; - -// Color -static struct nk_colorf color = {0.31f, 1.0f, 0.48f, 1.0f}; +struct demo_console_state { + char title[64]; + + // Theme + int theme; + + // Progress + nk_size progressValue; + + // Combobox + int weapon; + + // Property + int property_int_test; + float property_float_test; + + // Slider + int slider_int_test; + float slider_float_test; + + // Radio + int radio_option; + int radio_option2; + + // Checkbox + nk_bool checkbox1; + nk_bool checkbox2; + nk_bool checkbox3; + nk_bool checkbox4; + nk_bool checkbox5; + nk_bool checkbox6; + + // Messages + int message_count; + + // File + char file_path_buffer[1024]; + int file_path_buffer_size; + + // Textedit + int textedit_buffer_size; + char textedit_buffer[256]; + + // Input + int gamepad_number; + enum nk_gamepad_button gamepad_button; + + // Color + struct nk_colorf color; +}; + +static struct demo_console_state demo_console_state_defaults(void) { + struct demo_console_state state = { + .theme = THEME_DRACULA, + .progressValue = 50, + .weapon = 1, + .property_int_test = 20, + .property_float_test = 0.4f, + .slider_int_test = 20, + .slider_float_test = 0.4f, + .radio_option = 1, + .radio_option2 = 0, + .checkbox1 = nk_false, + .checkbox2 = nk_false, + .checkbox3 = nk_false, + .checkbox4 = nk_false, + .checkbox5 = nk_false, + .checkbox6 = nk_true, + .message_count = 0, + .file_path_buffer_size = 1024, + .textedit_buffer_size = 256, + .color = {0.31f, 1.0f, 0.48f, 1.0f}, + .gamepad_number = 0, + .gamepad_button = NK_GAMEPAD_BUTTON_A, + }; + + return state; +} void button_clicked(struct nk_console* button, void* user_data) { NK_UNUSED(user_data); @@ -69,8 +102,8 @@ void button_clicked(struct nk_console* button, void* user_data) { } void theme_changed(struct nk_console* combobox, void* user_data) { - NK_UNUSED(user_data); - set_style(combobox->ctx, (enum theme)theme); + enum theme t = *(enum theme*)user_data; + set_style(combobox->ctx, t); } void exclude_other_checkbox(nk_console* unused, void* user_data) { @@ -86,9 +119,9 @@ void toggle_visibility(nk_console* unused, void* user_data) { } void nk_console_demo_show_message(struct nk_console* button, void* user_data) { - NK_UNUSED(user_data); + struct demo_console_state* state = (struct demo_console_state*)user_data; char message[128]; - snprintf(message, 128, "This is message #%d!", ++message_count); + snprintf(message, 128, "This is message #%d!", ++state->message_count); nk_console_show_message(button, message); } @@ -97,14 +130,14 @@ void nk_console_radio_changed(struct nk_console* radio, void* user_data) { nk_console_show_message(radio, radio->label); } -nk_console* nuklear_console_demo_init(struct nk_context* ctx, void* user_data, struct nk_image image) { - console = nk_console_init(ctx); +void nuklear_console_demo_init(struct nk_console* _console, struct demo_console_state* state, void* user_data, struct nk_image image) { + nk_console* top = nk_console_get_top(_console); nk_gamepad_init(&gamepads, ctx, user_data); - nk_console_set_gamepads(console, &gamepads); + nk_console_set_gamepads(top, &gamepads); // New Game - nk_console* newgame = nk_console_button(console, "New Game"); + nk_console* newgame = nk_console_button(_console, "New Game"); { nk_console_button_set_symbol(newgame, NK_SYMBOL_PLUS); nk_console_label(newgame, "This would start a new game!"); @@ -112,7 +145,7 @@ nk_console* nuklear_console_demo_init(struct nk_context* ctx, void* user_data, s } // Widgets - nk_console* widgets = nk_console_button(console, "Widgets"); + nk_console* widgets = nk_console_button(_console, "Widgets"); { nk_console_set_tooltip(widgets, "Displays some random options!"); @@ -138,22 +171,22 @@ nk_console* nuklear_console_demo_init(struct nk_context* ctx, void* user_data, s nk_console* checkbox_button = nk_console_button(widgets, "Checkboxes"); { - nk_console_checkbox(checkbox_button, "Checkbox", &checkbox1) + nk_console_checkbox(checkbox_button, "Checkbox", &state->checkbox1) ->tooltip = "This is a checkbox!"; - nk_console_checkbox(checkbox_button, "Right aligned", &checkbox3) + nk_console_checkbox(checkbox_button, "Right aligned", &state->checkbox3) ->alignment = NK_TEXT_RIGHT; - nk_console_checkbox(checkbox_button, "Disabled Checkbox", &checkbox2) + nk_console_checkbox(checkbox_button, "Disabled Checkbox", &state->checkbox2) ->disabled = nk_true; // Onchange callbacks can be used to implement custom logic. // These two checkboxes disable each other when checked. - nk_console* exclude_a = nk_console_checkbox(checkbox_button, "Exclusive A (disables B)", &checkbox4); - nk_console* exclude_b = nk_console_checkbox(checkbox_button, "Exclusive B (disables A)", &checkbox5); + nk_console* exclude_a = nk_console_checkbox(checkbox_button, "Exclusive A (disables B)", &state->checkbox4); + nk_console* exclude_b = nk_console_checkbox(checkbox_button, "Exclusive B (disables A)", &state->checkbox5); nk_console_add_event_handler(exclude_a, NK_CONSOLE_EVENT_CHANGED, &exclude_other_checkbox, exclude_b, NULL); nk_console_add_event_handler(exclude_b, NK_CONSOLE_EVENT_CHANGED, &exclude_other_checkbox, exclude_a, NULL); // Checkbox that will show/hide the below label. - nk_console* checkbox_show_label = nk_console_checkbox(checkbox_button, "Show Label", &checkbox6); + nk_console* checkbox_show_label = nk_console_checkbox(checkbox_button, "Show Label", &state->checkbox6); nk_console* label_to_show = nk_console_label(checkbox_button, "This label is only shown when the checkbox is checked."); nk_console_add_event_handler(checkbox_show_label, NK_CONSOLE_EVENT_CHANGED, &toggle_visibility, label_to_show, NULL); @@ -179,20 +212,20 @@ nk_console* nuklear_console_demo_init(struct nk_context* ctx, void* user_data, s nk_console* radios = nk_console_button(widgets, "Radios"); { nk_console_label(radios, "Option A:"); - nk_console_radio(radios, "Radio #1", &radio_option); - nk_console_radio(radios, "Radio #2", &radio_option); - nk_console_radio(radios, "Radio #3", &radio_option); - nk_console_radio(radios, "Radio #4", &radio_option); + nk_console_radio(radios, "Radio #1", &state->radio_option); + nk_console_radio(radios, "Radio #2", &state->radio_option); + nk_console_radio(radios, "Radio #3", &state->radio_option); + nk_console_radio(radios, "Radio #4", &state->radio_option); nk_console_label(radios, "Option B:"); - nk_console_radio(radios, "Left Aligned #1", &radio_option2)->alignment = NK_TEXT_LEFT; - nk_console_radio(radios, "Left Aligned #2", &radio_option2)->alignment = NK_TEXT_LEFT; - nk_console_radio(radios, "Center Aligned #1", &radio_option2)->alignment = NK_TEXT_CENTERED; - nk_console_radio(radios, "Center Aligned #2", &radio_option2)->alignment = NK_TEXT_CENTERED; - nk_console_radio(radios, "Right Aligned #1", &radio_option2)->alignment = NK_TEXT_RIGHT; - nk_console_radio(radios, "Right Aligned #2", &radio_option2)->alignment = NK_TEXT_RIGHT; - nk_console_radio(radios, "Disabled", &radio_option2)->disabled = nk_true; - nk_console_radio(radios, "Invisible", &radio_option2)->visible = nk_false; + nk_console_radio(radios, "Left Aligned #1", &state->radio_option2)->alignment = NK_TEXT_LEFT; + nk_console_radio(radios, "Left Aligned #2", &state->radio_option2)->alignment = NK_TEXT_LEFT; + nk_console_radio(radios, "Center Aligned #1", &state->radio_option2)->alignment = NK_TEXT_CENTERED; + nk_console_radio(radios, "Center Aligned #2", &state->radio_option2)->alignment = NK_TEXT_CENTERED; + nk_console_radio(radios, "Right Aligned #1", &state->radio_option2)->alignment = NK_TEXT_RIGHT; + nk_console_radio(radios, "Right Aligned #2", &state->radio_option2)->alignment = NK_TEXT_RIGHT; + nk_console_radio(radios, "Disabled", &state->radio_option2)->disabled = nk_true; + nk_console_radio(radios, "Invisible", &state->radio_option2)->visible = nk_false; nk_console_button_onclick(radios, "Back", &nk_console_button_back); } @@ -213,22 +246,22 @@ nk_console* nuklear_console_demo_init(struct nk_context* ctx, void* user_data, s { nk_console* row = nk_console_row_begin(spacing); nk_console_spacing(row, 1); - nk_console* b = nk_console_button(row,""); + nk_console* b = nk_console_button(row, ""); nk_console_button_set_symbol(b, NK_SYMBOL_TRIANGLE_UP); nk_console_spacing(row, 1); nk_console_row_end(row); row = nk_console_row_begin(spacing); - b = nk_console_button(row,""); + b = nk_console_button(row, ""); nk_console_button_set_symbol(b, NK_SYMBOL_TRIANGLE_LEFT); nk_console_spacing(row, 1); - b = nk_console_button(row,""); + b = nk_console_button(row, ""); nk_console_button_set_symbol(b, NK_SYMBOL_TRIANGLE_RIGHT); nk_console_row_end(row); row = nk_console_row_begin(spacing); nk_console_spacing(row, 1); - b = nk_console_button(row,""); + b = nk_console_button(row, ""); nk_console_button_set_symbol(b, NK_SYMBOL_TRIANGLE_DOWN); nk_console_spacing(row, 1); nk_console_row_end(row); @@ -239,45 +272,45 @@ nk_console* nuklear_console_demo_init(struct nk_context* ctx, void* user_data, s // Progress Bar nk_console* progressbar = nk_console_button(widgets, "Progress Bar"); { - nk_console_progress(progressbar, "Progress", &progressValue, 100); + nk_console_progress(progressbar, "Progress", &state->progressValue, 100); nk_console_button_onclick(progressbar, "Back", &nk_console_button_back); } // Input: From any gamepad (-1) - nk_console_input(widgets, "Input Button", -1, &gamepad_number, &gamepad_button); + nk_console_input(widgets, "Input Button", -1, &state->gamepad_number, &state->gamepad_button); // Combobox - nk_console_combobox(widgets, "ComboBox", "Fists;Chainsaw;Pistol;Shotgun;Chaingun", ';', &weapon) + nk_console_combobox(widgets, "ComboBox", "Fists;Chainsaw;Pistol;Shotgun;Chaingun", ';', &state->weapon) ->tooltip = "Choose a weapon! The chainsaw is the best!"; // Property nk_console* properties = nk_console_button(widgets, "Property"); { - nk_console_property_int(properties, "Property Int", 10, &property_int_test, 30, 1, 1); - nk_console_property_float(properties, "Property Float", 0.0f, &property_float_test, 2.0f, 0.1f, 1); + nk_console_property_int(properties, "Property Int", 10, &state->property_int_test, 30, 1, 1); + nk_console_property_float(properties, "Property Float", 0.0f, &state->property_float_test, 2.0f, 0.1f, 1); nk_console_button_onclick(properties, "Back", &nk_console_button_back); } // Sliders nk_console* sliders = nk_console_button(widgets, "Sliders"); { - nk_console_slider_float(sliders, "Slider Float", 0.0f, &slider_float_test, 2.0f, 0.1f)->tooltip = "Slider float is cool! It's what you want to use."; - nk_console_slider_int(sliders, "Slider Int", 0, &slider_int_test, 20, 1); + nk_console_slider_float(sliders, "Slider Float", 0.0f, &state->slider_float_test, 2.0f, 0.1f)->tooltip = "Slider float is cool! It's what you want to use."; + nk_console_slider_int(sliders, "Slider Int", 0, &state->slider_int_test, 20, 1); nk_console_button_onclick(sliders, "Back", &nk_console_button_back); } // Textedit - nk_console* textedit = nk_console_textedit(widgets, "Username", textedit_buffer, textedit_buffer_size); + nk_console* textedit = nk_console_textedit(widgets, "Username", state->textedit_buffer, state->textedit_buffer_size); nk_console_set_tooltip(textedit, "Enter your username!"); // Color - nk_console_color(widgets, "Select Color", &color, NK_RGBA); + nk_console_color(widgets, "Select Color", &state->color, NK_RGBA); // File - nk_console_file(widgets, "File", file_path_buffer, file_path_buffer_size); + nk_console_file(widgets, "File", state->file_path_buffer, state->file_path_buffer_size); // Messages - nk_console_button_onclick(widgets, "Show Message", &nk_console_demo_show_message); + nk_console_button_onclick_handler(widgets, "Show Message", &nk_console_demo_show_message, &state, NULL); // Back Button nk_console_button_set_symbol( @@ -285,71 +318,69 @@ nk_console* nuklear_console_demo_init(struct nk_context* ctx, void* user_data, s NK_SYMBOL_TRIANGLE_LEFT); } - nk_console* theme_options = nk_console_combobox(console, "Theme", "Black;White;Red;Blue;Dark;Dracula;Default", ';', &theme); - nk_console_add_event(theme_options, NK_CONSOLE_EVENT_CHANGED, &theme_changed); + nk_console* theme_options = nk_console_combobox(_console, "Theme", "Black;White;Red;Blue;Dark;Dracula;Default", ';', &state->theme); + nk_console_add_event_handler(theme_options, NK_CONSOLE_EVENT_CHANGED, &theme_changed, &state->theme, NULL); theme_options->tooltip = "Change the theme of the console!"; - set_style(ctx, (enum theme)theme); + set_style(ctx, (enum theme)state->theme); // Rows - nk_console* calc = nk_console_button(console, "Calculator"); + nk_console* calc = nk_console_button(_console, "Calculator"); { - nk_console* row = nk_console_row_begin(calc); - nk_console_button(row, "sqrt"); - nk_console_button(row, "pi"); - nk_console_row_end(row); - - row = nk_console_row_begin(calc); - nk_console_button(row, "AC"); - nk_console_button(row, "()"); - nk_console_button(row, "%"); - nk_console_button(row, "/"); - nk_console_row_end(row); - - row = nk_console_row_begin(calc); - nk_console_button(row, "7"); - nk_console_button(row, "8"); - nk_console_button(row, "9"); - nk_console_button(row, "*"); - nk_console_row_end(row); - - row = nk_console_row_begin(calc); - nk_console_button(row, "4"); - nk_console_button(row, "5"); - nk_console_button(row, "6"); - nk_console_button(row, "-"); - nk_console_row_end(row); - - row = nk_console_row_begin(calc); - nk_console_button(row, "1"); - nk_console_button(row, "2"); - nk_console_button(row, "3"); - nk_console_button(row, "+"); - nk_console_row_end(row); - - row = nk_console_row_begin(calc); - nk_console_button(row, "0"); - nk_console_button(row, "."); - nk_console_button(row, "bksp"); - nk_console_button(row, "="); - nk_console_row_end(row); - - nk_console_button_set_symbol( - nk_console_button_onclick(calc, "Back", &nk_console_button_back), - NK_SYMBOL_TRIANGLE_LEFT); - - calc->tooltip = "Demo rows and grids!"; - } + nk_console* row = nk_console_row_begin(calc); + nk_console_button(row, "sqrt"); + nk_console_button(row, "pi"); + nk_console_row_end(row); + + row = nk_console_row_begin(calc); + nk_console_button(row, "AC"); + nk_console_button(row, "()"); + nk_console_button(row, "%"); + nk_console_button(row, "/"); + nk_console_row_end(row); + + row = nk_console_row_begin(calc); + nk_console_button(row, "7"); + nk_console_button(row, "8"); + nk_console_button(row, "9"); + nk_console_button(row, "*"); + nk_console_row_end(row); + + row = nk_console_row_begin(calc); + nk_console_button(row, "4"); + nk_console_button(row, "5"); + nk_console_button(row, "6"); + nk_console_button(row, "-"); + nk_console_row_end(row); + + row = nk_console_row_begin(calc); + nk_console_button(row, "1"); + nk_console_button(row, "2"); + nk_console_button(row, "3"); + nk_console_button(row, "+"); + nk_console_row_end(row); + + row = nk_console_row_begin(calc); + nk_console_button(row, "0"); + nk_console_button(row, "."); + nk_console_button(row, "bksp"); + nk_console_button(row, "="); + nk_console_row_end(row); - nk_console_button(console, "Save Game")->disabled = nk_true; - nk_console_button_onclick(console, "Quit Game", &button_clicked); + nk_console_button_set_symbol( + nk_console_button_onclick(calc, "Back", &nk_console_button_back), + NK_SYMBOL_TRIANGLE_LEFT); + + calc->tooltip = "Demo rows and grids!"; + } - return console; + nk_console_button(_console, "Save Game")->disabled = nk_true; + nk_console_button_onclick(_console, "Quit Game", &button_clicked); } nk_bool nuklear_console_demo_render() { nk_console_render(console); - return shouldClose;; + return shouldClose; } void nuklear_console_demo_free() { diff --git a/demo/glfw/main.c b/demo/glfw/main.c index 100c42b..c1a9805 100644 --- a/demo/glfw/main.c +++ b/demo/glfw/main.c @@ -55,7 +55,6 @@ int main(void) struct nk_glfw glfw = {0}; static GLFWwindow *win; int width = 0, height = 0; - struct nk_context *ctx; float font_scale = 3; /* GLFW */ @@ -103,7 +102,13 @@ int main(void) /*nk_style_set_font(ctx, &droid->handle);*/ } - nk_console* console = nuklear_console_demo_init(ctx, NULL, nk_image_id(0)); + console = nk_console_init(ctx); + + // Initialize console state + struct demo_console_state state = demo_console_state_defaults(); + + // Intialize the console demo widgets + nuklear_console_demo_init(console, &state, NULL, nk_image_id(0)); while (!glfwWindowShouldClose(win)) { diff --git a/demo/pntr/main.c b/demo/pntr/main.c index ee2f1f1..dd065dd 100644 --- a/demo/pntr/main.c +++ b/demo/pntr/main.c @@ -16,9 +16,7 @@ typedef struct AppData { pntr_font* font; - struct nk_context* ctx; pntr_image* image; - struct nk_console* console; } AppData; bool Init(pntr_app* app) { @@ -27,18 +25,25 @@ bool Init(pntr_app* app) { // Load the default font appData->font = pntr_load_font_default(); - appData->ctx = pntr_load_nuklear(appData->font); + + ctx = pntr_load_nuklear(appData->font); appData->image = pntr_load_image("resources/image.png"); // Initialize the Gamepads - appData->console = nuklear_console_demo_init(appData->ctx, app, pntr_image_nk(appData->image)); + // Init top console + console = nk_console_init(ctx); + + // Initialize console state + struct demo_console_state state = demo_console_state_defaults(); + + // Intialize the console demo widgets + nuklear_console_demo_init(console, &state, NULL, pntr_image_nk(appData->image)); return true; } bool Update(pntr_app* app, pntr_image* screen) { AppData* appData = (AppData*)pntr_app_userdata(app); - struct nk_context* ctx = appData->ctx; // Update the pntr input state. pntr_nuklear_update(ctx, app); @@ -74,7 +79,7 @@ void Close(pntr_app* app) { // Unload the font pntr_unload_font(appData->font); pntr_unload_image(appData->image); - pntr_unload_nuklear(appData->ctx); + pntr_unload_nuklear(ctx); pntr_unload_memory(appData); } diff --git a/demo/raylib/main.c b/demo/raylib/main.c index d953e1f..eb758e2 100644 --- a/demo/raylib/main.c +++ b/demo/raylib/main.c @@ -13,7 +13,6 @@ void UpdateDrawFrame(void); -struct nk_context *ctx; nk_bool closeWindow = nk_false; int main() { @@ -28,7 +27,10 @@ int main() { ctx = InitNuklearEx(font, fontSize); Texture texture = LoadTexture("resources/image.png"); - console = nuklear_console_demo_init(ctx, NULL, TextureToNuklear(texture)); + console = nk_console_init(ctx); + + struct demo_console_state state = demo_console_state_defaults(); + nuklear_console_demo_init(console, &state, NULL, TextureToNuklear(texture)); #if defined(PLATFORM_WEB) emscripten_set_main_loop(UpdateDrawFrame, 0, 1); diff --git a/demo/sdl_renderer/Makefile b/demo/sdl_renderer/Makefile index a704f58..4481b68 100644 --- a/demo/sdl_renderer/Makefile +++ b/demo/sdl_renderer/Makefile @@ -1,5 +1,6 @@ # Install -BIN = nuklear_console_demo_sdl +BIN_MAIN = nuklear_console_demo_sdl +BIN_MULTIPLE = nuklear_console_demo_sdl_multiple_windows # Flags @@ -10,8 +11,11 @@ CXXFLAGS += -pedantic -O0 -fpermissive -std=c++17 CFLAGS += `sdl2-config --cflags` CXXFLAGS += `sdl2-config --cflags` -SRC = main.c -OBJ = $(SRC:.c=.o) +SRC_MAIN = main.c +SRC_MULTIPLE = main_multiple_windows.c + +OBJ_MAIN = $(SRC_MAIN:.c=.o) +OBJ_MULTIPLE = $(SRC_MULTIPLE:.c=.o) ifeq ($(OS),Windows_NT) #TODO @@ -20,17 +24,27 @@ ifeq ($(OS),Windows_NT) else UNAME_S := $(shell uname -s) ifeq ($(UNAME_S),Darwin) -#TODO LIBS = -lSDL2 -framework OpenGL -lm + LIBS += -lm -ldl `sdl2-config --libs` else LIBS += -lm -ldl `sdl2-config --libs` endif endif -$(BIN): clean - $(CC) $(SRC) $(CFLAGS) -o $(BIN) $(LIBS) +# Default target: build both binaries +all: $(BIN_MAIN) $(BIN_MULTIPLE) + +# Rule for nuklear_console_demo_sdl (main.c) +$(BIN_MAIN): $(SRC_MAIN) + $(CC) $(SRC_MAIN) $(CFLAGS) -o $(BIN_MAIN) $(LIBS) + +# Rule for nuklear_console_demo_sdl_multiple (main_multiple.c) +$(BIN_MULTIPLE): $(SRC_MULTIPLE) + $(CC) $(SRC_MULTIPLE) $(CFLAGS) -o $(BIN_MULTIPLE) $(LIBS) +# Clean up clean: - rm -rf $(BIN) $(OBJS) + rm -rf $(BIN_MAIN) $(BIN_MULTIPLE) $(OBJ_MAIN) $(OBJ_MULTIPLE) -test: $(BIN) - ./$(BIN) +# Test the main binary +test: $(BIN_MAIN) + ./$(BIN_MAIN) diff --git a/demo/sdl_renderer/main.c b/demo/sdl_renderer/main.c index 7053410..b924014 100644 --- a/demo/sdl_renderer/main.c +++ b/demo/sdl_renderer/main.c @@ -38,9 +38,6 @@ int main(int argc, char *argv[]) { int flags = 0; float font_scale = 3; - /* GUI */ - struct nk_context *ctx; - /* SDL setup */ SDL_SetHint(SDL_HINT_VIDEO_HIGHDPI_DISABLED, "0"); SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_GAMECONTROLLER); @@ -103,7 +100,11 @@ int main(int argc, char *argv[]) { SDL_FreeSurface(surface); } - nk_console* console = nuklear_console_demo_init(ctx, NULL, img); + console = nk_console_init(ctx); + + // Initialize console state + struct demo_console_state state = demo_console_state_defaults(); + nuklear_console_demo_init(console, &state, NULL, img); while (running) { /* Input */ diff --git a/demo/sdl_renderer/main_multiple_windows.c b/demo/sdl_renderer/main_multiple_windows.c new file mode 100644 index 0000000..8856784 --- /dev/null +++ b/demo/sdl_renderer/main_multiple_windows.c @@ -0,0 +1,59 @@ +#include "setup.h" + +int main(int argc, char* argv[]) { + NK_UNUSED(argc); + NK_UNUSED(argv); + + // Init demo context + struct demo_ctx demo = init_demo_context(1280, 720); + + // Init console + console = nk_console_init(ctx); + + const int WINDOW_COUNT = 3; + + int window_flags = NK_WINDOW_SCROLL_AUTO_HIDE | NK_WINDOW_TITLE; + + // Register console windows + struct demo_console_state states[WINDOW_COUNT]; + + for (int i = 0; i < WINDOW_COUNT; i++) { + // Initialize console window state + states[i] = demo_console_state_defaults(); + + // Set window title + snprintf(states[i].title, sizeof(states[i].title), "nuklear_console_%d", i + 1); + + // Register console window + nk_console* window = nk_console_window(console, states[i].title, nk_rect(i * demo.window_width / WINDOW_COUNT, 0, demo.window_width / WINDOW_COUNT, demo.window_height), window_flags); + + // Register demo widgets to console window + nuklear_console_demo_init(window, &states[i], NULL, demo.img); + } + + int running = 1; + + while (running) { + /* Input */ + SDL_Event evt; + nk_input_begin(ctx); + nk_gamepad_update(nk_console_get_gamepads(console)); + while (SDL_PollEvent(&evt)) { + if (evt.type == SDL_QUIT) return cleanup(&demo); + if (evt.type == SDL_KEYUP && evt.key.keysym.scancode == SDL_SCANCODE_ESCAPE) running = 0; + + nk_sdl_handle_event(&evt); + nk_gamepad_sdl_handle_event(nk_console_get_gamepads(console), &evt); + } + nk_input_end(ctx); + + /* GUI */ + if (nuklear_console_demo_render()) { + running = 0; + } + + render(demo.renderer); + } + + return 0; +} diff --git a/demo/sdl_renderer/setup.h b/demo/sdl_renderer/setup.h new file mode 100644 index 0000000..6c7e564 --- /dev/null +++ b/demo/sdl_renderer/setup.h @@ -0,0 +1,114 @@ +#include +#include + +#include + +#define NK_INCLUDE_FIXED_TYPES +#define NK_INCLUDE_STANDARD_IO +#define NK_INCLUDE_STANDARD_VARARGS +#define NK_INCLUDE_DEFAULT_ALLOCATOR +#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT +#define NK_INCLUDE_FONT_BAKING +#define NK_INCLUDE_DEFAULT_FONT +#define NK_INCLUDE_COMMAND_USERDATA +#define NK_IMPLEMENTATION +#define NK_SDL_RENDERER_IMPLEMENTATION +#include "../../vendor/Nuklear/nuklear.h" +#include "../../vendor/Nuklear/demo/sdl_renderer/nuklear_sdl_renderer.h" + +#include "../common/nuklear_console_demo.c" + +struct demo_ctx { + SDL_Renderer* renderer; + struct nk_image img; + SDL_Texture* _texture; + SDL_Window* _window; + int window_width; + int window_height; +}; + +static int configure(struct demo_ctx* demo) { + /* Platform */ + int flags = 0; + float font_scale = 3; + + /* SDL setup */ + SDL_SetHint(SDL_HINT_VIDEO_HIGHDPI_DISABLED, "0"); + SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_GAMECONTROLLER); + + demo->_window = SDL_CreateWindow("nuklear_console_demo_multiple_window", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, demo->window_width, demo->window_height, SDL_WINDOW_SHOWN); + + if (demo->_window == NULL) { + SDL_Log("Error SDL_CreateWindow %s", SDL_GetError()); + return 1; + } + + demo->renderer = SDL_CreateRenderer(demo->_window, -1, flags); + + if (demo->renderer == NULL) { + SDL_Log("Error SDL_CreateRenderer %s", SDL_GetError()); + return 1; + } + + /* GUI */ + ctx = nk_sdl_init(demo->_window, demo->renderer); + { + struct nk_font_atlas* atlas; + struct nk_font_config config = nk_font_config(0); + struct nk_font* font; + + nk_sdl_font_stash_begin(&atlas); + font = nk_font_atlas_add_default(atlas, 13 * font_scale, &config); + nk_sdl_font_stash_end(); + nk_style_set_font(ctx, &font->handle); + } + + // Attempt to load the sample image. + demo->img = nk_image_id(0); + SDL_Surface* surface = SDL_LoadBMP("image.bmp"); + if (surface != NULL) { + demo->_texture = SDL_CreateTextureFromSurface(demo->renderer, surface); + if (demo->_texture != NULL) { + demo->img = nk_image_ptr(demo->_texture); + demo->img.w = surface->w; + demo->img.h = surface->h; + demo->img.region[0] = 0; + demo->img.region[1] = 0; + demo->img.region[2] = surface->w; + demo->img.region[3] = surface->h; + } + SDL_FreeSurface(surface); + } + + return 0; +} + +static struct demo_ctx init_demo_context(int window_width, int window_height) { + struct demo_ctx demo; + demo.window_width = window_width; + demo.window_height = window_height; + configure(&demo); + return demo; +} + +static int cleanup(struct demo_ctx* demo) { + if (demo->_texture != NULL) { + SDL_DestroyTexture(demo->_texture); + } + nuklear_console_demo_free(); + nk_sdl_shutdown(); + SDL_DestroyRenderer(demo->renderer); + SDL_DestroyWindow(demo->_window); + SDL_Quit(); + + return 0; +} + +static void render(SDL_Renderer* renderer) { + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_RenderClear(renderer); + + nk_sdl_render(NK_ANTI_ALIASING_ON); + + SDL_RenderPresent(renderer); +} diff --git a/nuklear_console.h b/nuklear_console.h index 51c9b85..e6fdeb5 100644 --- a/nuklear_console.h +++ b/nuklear_console.h @@ -70,6 +70,7 @@ typedef enum { NK_CONSOLE_INPUT, NK_CONSOLE_INPUT_ACTIVE, NK_CONSOLE_RADIO, + NK_CONSOLE_WINDOW, } nk_console_widget_type; typedef struct nk_console_message { @@ -102,19 +103,7 @@ typedef struct nk_console { } nk_console; typedef struct nk_console_top_data { - nk_console* active_parent; /** The parent that is currently being displayed. */ nk_bool input_processed; /** Whether or not user input has been processed. */ - - /** - * Message queue that is to be shown. - */ - struct nk_console_message* messages; - - /** - * When set, will determine where messages should appear on the screen. - */ - struct nk_rect message_bounds; - /** * The gamepad system to use for gamepad input. * @@ -123,6 +112,9 @@ typedef struct nk_console_top_data { */ struct nk_gamepads* gamepads; + int active_window_index; /** The index of the active window. */ + int window_count; /** The number of windows in the console. */ + /** * Custom user data. This is only applied to the top-level console. * @@ -138,8 +130,12 @@ NK_API void nk_console_free(nk_console* console); NK_API void nk_console_render(nk_console* console); NK_API void nk_console_render_window(nk_console* console, const char* title, struct nk_rect bounds, nk_uint flags); +// Multiple consoles in separate windows +NK_API void nk_console_set_active_window(nk_console* console); + // Utilities NK_API nk_console* nk_console_get_top(nk_console* widget); +NK_API nk_console* nk_console_get_window(nk_console* widget); NK_API int nk_console_get_widget_index(nk_console* widget); NK_API void nk_console_check_tooltip(nk_console* console); NK_API void nk_console_check_up_down(nk_console* widget, struct nk_rect bounds); @@ -211,6 +207,7 @@ NK_API void nk_console_set_user_data(nk_console* console, void* user_data); #include "nuklear_console_spacing.h" #include "nuklear_console_textedit.h" #include "nuklear_console_textedit_text.h" +#include "nuklear_console_window.h" #undef NK_CONSOLE_HEADER_ONLY #if defined(__cplusplus) @@ -294,6 +291,7 @@ NK_API nk_bool nk_input_is_mouse_moved(const struct nk_input* input); #include "nuklear_console_spacing.h" #include "nuklear_console_textedit.h" #include "nuklear_console_textedit_text.h" +#include "nuklear_console_window.h" NK_API const char* nk_console_get_label(nk_console* widget) { if (widget == NULL) { @@ -408,6 +406,12 @@ NK_API nk_bool nk_console_is_active_widget(nk_console* widget) { return nk_false; } + nk_console* window = nk_console_get_window(widget); + + if (window->disabled) { + return nk_false; + } + nk_console* parent = widget->parent == NULL ? widget : widget->parent; return parent->activeWidget == widget; } @@ -417,9 +421,10 @@ nk_console* nk_console_active_parent(nk_console* console) { return NULL; } - nk_console* top = nk_console_get_top(console); - nk_console* active_parent = ((nk_console_top_data*)top->data)->active_parent; - return active_parent == NULL ? top : active_parent; + nk_console* window = nk_console_get_window(console); + + nk_console* active_parent = ((nk_console_window_data*)window->data)->active_parent; + return active_parent == NULL ? window : active_parent; } NK_API void nk_console_set_active_parent(nk_console* new_parent) { @@ -427,16 +432,33 @@ NK_API void nk_console_set_active_parent(nk_console* new_parent) { return; } - nk_console* top = nk_console_get_top(new_parent); - if (top == NULL) { + nk_console* window = nk_console_get_window(new_parent); + if (window == NULL) { return; } // When switching parents, bring the window scroll to the top to that the window doesn't appear empty. - nk_window_set_scroll(top->ctx, 0, 0); + nk_window_set_scroll(window->ctx, 0, 0); - nk_console_top_data* data = (nk_console_top_data*)top->data; - data->active_parent = new_parent; + nk_console_window_data* window_data = (nk_console_window_data*)window->data; + window_data->active_parent = new_parent; +} + +/** + * Get the console window for a given widget. + */ +NK_API nk_console* nk_console_get_window(nk_console* widget) { + if (widget == NULL) { + return NULL; + } + + while (widget->type != NK_CONSOLE_WINDOW) { + widget = widget->parent; + } + + NK_ASSERT(widget->type == NK_CONSOLE_WINDOW); + + return widget; } /** @@ -498,7 +520,9 @@ NK_API int nk_console_get_widget_index(nk_console* widget) { */ NK_API void nk_console_check_up_down(nk_console* widget, struct nk_rect bounds) { nk_console* top = nk_console_get_top(widget); - nk_console_top_data* data = (nk_console_top_data*)top->data; + nk_console* window = nk_console_get_window(widget); + + nk_console_top_data* top_data = (nk_console_top_data*)top->data; // Scroll to the active widget if needed. struct nk_rect content_region = nk_window_get_content_region(widget->ctx); @@ -512,9 +536,11 @@ NK_API void nk_console_check_up_down(nk_console* widget, struct nk_rect bounds) } // Only process an active input once. - if (data->input_processed == nk_false) { + if (top_data->input_processed == nk_false) { // Page Up - if (nk_console_button_pushed(top, NK_GAMEPAD_BUTTON_LB)) { + // SUGGESTION/TODO: Add NK_GAMEPAD_BUTTON_LT and NK_GAMEPAD_BUTTON_RT and bind them to Page Up and Page Down. + // Disabled in favor of console switching if there are multiple windows. + if (top_data->window_count < 2 && nk_console_button_pushed(top, NK_GAMEPAD_BUTTON_LB)) { int widgetIndex = nk_console_get_widget_index(widget); int count = 0; while (--widgetIndex >= 0) { @@ -526,10 +552,12 @@ NK_API void nk_console_check_up_down(nk_console* widget, struct nk_rect bounds) } } } - data->input_processed = nk_true; + top_data->input_processed = nk_true; } // Page Down - else if (nk_console_button_pushed(top, NK_GAMEPAD_BUTTON_RB)) { + // SUGGESTION/TODO: Add NK_GAMEPAD_BUTTON_LT and NK_GAMEPAD_BUTTON_RT and bind them to Page Up and Page Down. + // Disabled in favor of console switching if there are multiple windows. + else if (top_data->window_count < 2 && nk_console_button_pushed(top, NK_GAMEPAD_BUTTON_RB)) { int widgetIndex = nk_console_get_widget_index(widget); int count = 0; while (++widgetIndex < (int)cvector_size(widget->parent->children)) { @@ -541,7 +569,7 @@ NK_API void nk_console_check_up_down(nk_console* widget, struct nk_rect bounds) } } } - data->input_processed = nk_true; + top_data->input_processed = nk_true; } // Up else if (nk_console_button_pushed(top, NK_GAMEPAD_BUTTON_UP)) { @@ -553,7 +581,7 @@ NK_API void nk_console_check_up_down(nk_console* widget, struct nk_rect bounds) break; } } - data->input_processed = nk_true; + top_data->input_processed = nk_true; } // Down else if (nk_console_button_pushed(top, NK_GAMEPAD_BUTTON_DOWN)) { @@ -565,48 +593,26 @@ NK_API void nk_console_check_up_down(nk_console* widget, struct nk_rect bounds) break; } } - data->input_processed = nk_true; + top_data->input_processed = nk_true; } // Back else if (nk_console_button_pushed(top, NK_GAMEPAD_BUTTON_B)) { - if (nk_console_active_parent(top) == NULL) { + if (nk_console_active_parent(window) == NULL) { return; } if (widget->parent != NULL) { - if (widget->parent == top) { - nk_console_set_active_parent(top); + if (widget->parent == window) { + nk_console_set_active_parent(window); } else if (widget->parent->parent != NULL) { nk_console_set_active_parent(widget->parent->parent); } } - data->input_processed = nk_true; - } - } -} - -/** - * Retrieve the first selectable widget from the given parent. - * - * @internal - */ -static nk_console* nk_console_find_first_selectable(nk_console* parent) { - if (parent == NULL || parent->children == NULL) { - return NULL; - } - - // Iterate through the children to find the first selectable widget. - for (size_t i = 0; i < cvector_size(parent->children); i++) { - if (parent->children[i] != NULL) { - if (nk_console_selectable(parent->children[i])) { - return parent->children[i]; - } + top_data->input_processed = nk_true; } } - - return NULL; } /** @@ -681,65 +687,32 @@ NK_API void nk_console_render(nk_console* console) { return; } - // First run on this game loop + // Render window(s) if (console->parent == NULL) { - nk_console_top_data* data = (nk_console_top_data*)console->data; + nk_console_top_data* top_data = (nk_console_top_data*)console->data; // Reset the input state. - data->input_processed = nk_false; + top_data->input_processed = nk_false; - // Render the active message. - nk_console_render_message(console); - - // Render all of the parent's children. - if (data->active_parent->children != NULL) { - // Make sure there's an active widget selected. - if (data->active_parent->activeWidget == NULL) { - nk_console_set_active_widget(nk_console_find_first_selectable(data->active_parent)); - } - else { - // Make sure the widget actually exists and can be selected. - nk_bool widgetFound = nk_false; - for (size_t i = 0; i < cvector_size(data->active_parent->children); ++i) { - if (data->active_parent->children[i] == data->active_parent->activeWidget) { - // Ensure the widget is still selectable. - if (nk_console_selectable(data->active_parent->activeWidget)) { - widgetFound = nk_true; - } - break; - } - } - if (widgetFound == nk_false) { - nk_console_set_active_widget(nk_console_find_first_selectable(data->active_parent)); - } - } - - // Render all the children - for (size_t i = 0; i < cvector_size(data->active_parent->children); ++i) { - nk_console_render(data->active_parent->children[i]); - } + if (top_data->window_count == 0) { + // No windows added to console, so we capture it and add it to the console, + // moving the widgets from top console to our newly created window. + nk_console_render(nk_console_capture_window(console)); } - - // Invoke the post-render events. - size_t count = cvector_size(console->events); - if (count > 0) { - nk_bool can_erase = nk_true; - for (size_t i = 0; i < count; i++) { - if (console->events[i].type == NK_CONSOLE_EVENT_POST_RENDER_ONCE) { - // Call the callback if it's set. - if (console->events[i].callback != NULL) { - console->events[i].callback((nk_console*)console->events[i].user_data, NULL); - console->events[i].callback = NULL; - } - } - else { - can_erase = nk_false; - } - } - if (can_erase) { - cvector_clear(console->events); + else if (top_data->window_count == 1) { + nk_console_render(console->children[0]); + } + else if (top_data->window_count > 1) { + // Multiple windows added to console + for (size_t i = 0; i < cvector_size(console->children); i++) { + nk_console* window = console->children[i]; + nk_console_window_data* window_data = (nk_console_window_data*)window->data; + + // Begin a new Nuklear window + nk_console_render_window(window, window->label, window_data->bounds, window_data->flags); } } + return; } @@ -755,12 +728,18 @@ NK_API void nk_console_render(nk_console* console) { widget_bounds.y -= (float)window_scroll_y; nk_console* top = nk_console_get_top(console); - nk_console_top_data* data = (nk_console_top_data*)top->data; - if (data->input_processed == nk_false && nk_input_is_mouse_hovering_rect(&console->ctx->input, widget_bounds)) { - // Select the widget, if possible. + nk_console_top_data* top_data = (nk_console_top_data*)top->data; + if (top_data->input_processed == nk_false && nk_input_is_mouse_hovering_rect(&console->ctx->input, widget_bounds)) { if (nk_console_selectable(console)) { + // Ensure the console is active. + if (top_data->window_count > 1) { + nk_console* window = nk_console_get_window(console); + nk_console_set_active_window(window); + } + + // Select the widget. nk_console_set_active_widget(console); - data->input_processed = nk_true; + top_data->input_processed = nk_true; } } } @@ -793,12 +772,40 @@ NK_API nk_console* nk_console_init(struct nk_context* context) { nk_console_top_data* data = (nk_console_top_data*)nk_console_malloc(handle, NULL, sizeof(nk_console_top_data)); nk_zero(data, sizeof(nk_console_top_data)); - data->active_parent = console; + data->window_count = 0; + data->active_window_index = 0; console->data = data; return console; } +/** + * Set the given window as the active window. + * + * @param top The top-level console. + */ +NK_API void nk_console_set_active_window(nk_console* window) { + NK_ASSERT(window->type == NK_CONSOLE_WINDOW); + + nk_console* top = window->parent; + NK_ASSERT(top->parent == NULL); + nk_console_top_data* top_data = (nk_console_top_data*)top->data; + + + for (size_t i = 0; i < cvector_size(top->children); i++) { + nk_console* w = top->children[i]; + if (w == window) { + w->disabled = nk_false; + top_data->active_window_index = (int)i; + } + else { + w->disabled = nk_true; + } + } + + nk_window_set_focus(top->ctx, window->label); +} + /** * Renders the given console to a new window with the given properties. * @@ -844,8 +851,8 @@ NK_API void nk_console_free(nk_console* console) { // Clear any component-specific data. if (console->data != NULL) { // Free the top data. - if (console->type == NK_CONSOLE_PARENT) { - nk_console_top_data* data = (nk_console_top_data*)console->data; + if (console->type == NK_CONSOLE_WINDOW) { + nk_console_window_data* data = (nk_console_window_data*)console->data; if (data->messages != NULL) { cvector_free(data->messages); data->messages = NULL; @@ -964,8 +971,8 @@ NK_API nk_bool nk_console_button_pushed(nk_console* console, int button) { } // Gamepad - nk_console_top_data* data = (nk_console_top_data*)console->data; - if (nk_gamepad_is_button_pressed(data->gamepads, -1, (enum nk_gamepad_button)button)) { + nk_console_top_data* top_data = (nk_console_top_data*)console->data; + if (nk_gamepad_is_button_pressed(top_data->gamepads, -1, (enum nk_gamepad_button)button)) { return nk_true; } @@ -979,8 +986,26 @@ NK_API nk_bool nk_console_button_pushed(nk_console* console, int button) { case NK_GAMEPAD_BUTTON_B: return nk_input_is_key_pressed(&console->ctx->input, NK_KEY_BACKSPACE) || (nk_input_is_mouse_pressed(&console->ctx->input, NK_BUTTON_RIGHT) && nk_window_is_hovered(console->ctx)); // case NK_GAMEPAD_BUTTON_X: return nk_input_is_key_pressed(&console->ctx->input, NK_KEY_A); // case NK_GAMEPAD_BUTTON_Y: return nk_input_is_key_pressed(&console->ctx->input, NK_KEY_S); - case NK_GAMEPAD_BUTTON_LB: return nk_input_is_key_pressed(&console->ctx->input, NK_KEY_DOWN) && nk_input_is_key_down(&console->ctx->input, NK_KEY_CTRL); - case NK_GAMEPAD_BUTTON_RB: return nk_input_is_key_pressed(&console->ctx->input, NK_KEY_UP) && nk_input_is_key_down(&console->ctx->input, NK_KEY_CTRL); + case NK_GAMEPAD_BUTTON_LB: { + // PageUp/PageDown is replaced with console switching if there are multiple windows. + // Both could be supported when nuklear_gamepad supports NK_GAMEPAD_BUTTON_LT and NK_GAMEPAD_BUTTON_RT. + if (top_data->window_count > 1) { + return nk_input_is_key_pressed(&console->ctx->input, NK_KEY_TAB) && nk_input_is_key_down(&console->ctx->input, NK_KEY_SHIFT); + } + else { + return nk_input_is_key_pressed(&console->ctx->input, NK_KEY_DOWN) && nk_input_is_key_down(&console->ctx->input, NK_KEY_CTRL); + }; + } + case NK_GAMEPAD_BUTTON_RB: { + // PageUp/PageDown is replaced with console switching if there are multiple windows. + // Both could be supported when nuklear_gamepad supports NK_GAMEPAD_BUTTON_LT and NK_GAMEPAD_BUTTON_RT. + if (top_data->window_count > 1) { + return nk_input_is_key_pressed(&console->ctx->input, NK_KEY_TAB) && !nk_input_is_key_down(&console->ctx->input, NK_KEY_SHIFT); + } + else { + return nk_input_is_key_pressed(&console->ctx->input, NK_KEY_UP) && nk_input_is_key_down(&console->ctx->input, NK_KEY_CTRL); + } + } case NK_GAMEPAD_BUTTON_BACK: return nk_input_is_key_pressed(&console->ctx->input, NK_KEY_SHIFT); // case NK_GAMEPAD_BUTTON_START: return nk_input_is_key_pressed(&console->ctx->input, NK_KEY_UP); diff --git a/nuklear_console_button.h b/nuklear_console_button.h index a8876fe..d6ccbfb 100644 --- a/nuklear_console_button.h +++ b/nuklear_console_button.h @@ -204,8 +204,8 @@ NK_API void nk_console_button_back(nk_console* button, void* user_data) { return; } - nk_console* top = nk_console_get_top(button); - nk_console_top_data* data = (nk_console_top_data*)top->data; + nk_console* window = nk_console_get_window(button); + nk_console_window_data* window_data = (nk_console_window_data*)window->data; nk_console* parent = button->parent; if (parent != NULL) { @@ -215,7 +215,7 @@ NK_API void nk_console_button_back(nk_console* button, void* user_data) { nk_console_set_active_parent(parent); } else { - data->active_parent = top; + window_data->active_parent = window; } } diff --git a/nuklear_console_message.h b/nuklear_console_message.h index c9a43c0..e98579d 100644 --- a/nuklear_console_message.h +++ b/nuklear_console_message.h @@ -39,8 +39,8 @@ NK_API void nk_console_show_message(nk_console* console, const char* text) { return; } - nk_console_top_data* data = (nk_console_top_data*)(nk_console_get_top(console)->data); - if (data == NULL) { + nk_console_window_data* window_data = (nk_console_window_data*)(nk_console_get_window(console)->data); + if (window_data == NULL) { return; } @@ -57,7 +57,7 @@ NK_API void nk_console_show_message(nk_console* console, const char* text) { message.text[len] = '\0'; // Make sure it's null-terminated // Add the new message to the message queue. - cvector_push_back(data->messages, message); + cvector_push_back(window_data->messages, message); } NK_API void nk_console_message_render(nk_console* console, nk_console_message* message) { @@ -66,7 +66,7 @@ NK_API void nk_console_message_render(nk_console* console, nk_console_message* m } // Retrieve style sizes. - nk_console_top_data* data = (nk_console_top_data*)console->data; + nk_console_window_data* window_data = (nk_console_window_data*)console->data; struct nk_context* ctx = console->ctx; struct nk_vec2 padding = ctx->style.window.padding; float border = ctx->style.window.border; @@ -76,7 +76,7 @@ NK_API void nk_console_message_render(nk_console* console, nk_console_message* m struct nk_vec2 mouse_pos = ctx->input.mouse.pos; // Display the tooltip at the bottom of the window, by manipulating the mouse position - struct nk_rect bounds = data->message_bounds.w == 0 ? nk_window_get_bounds(ctx) : data->message_bounds; + struct nk_rect bounds = window_data->message_bounds.w == 0 ? nk_window_get_bounds(ctx) : window_data->message_bounds; bounds.w -= border; ctx->input.mouse.pos.x = bounds.x; ctx->input.mouse.pos.y = bounds.y + bounds.h - text_height - padding.y * 2 - border * 2.0f; @@ -104,15 +104,17 @@ NK_API void nk_console_message_render(nk_console* console, nk_console_message* m } NK_API void nk_console_render_message(nk_console* console) { - nk_console_top_data* data = (nk_console_top_data*)console->data; - if (data->messages == NULL || cvector_size(data->messages) == 0) { + NK_ASSERT(console->type == NK_CONSOLE_WINDOW); + nk_console_window_data* window_data = (nk_console_window_data*)console->data; + + if (window_data->messages == NULL || cvector_size(window_data->messages) == 0) { return; } // Loop through all messages and display the first one. nk_bool clear_all = nk_true; - nk_console_message* end = (nk_console_message*)cvector_end(data->messages); - for (nk_console_message* it = (nk_console_message*)cvector_begin(data->messages); it != end; it++) { + nk_console_message* end = (nk_console_message*)cvector_end(window_data->messages); + for (nk_console_message* it = (nk_console_message*)cvector_begin(window_data->messages); it != end; it++) { // Skip messages that have already been shown. if (it->duration <= 0.0f) { continue; @@ -124,7 +126,8 @@ NK_API void nk_console_render_message(nk_console* console) { } // If animations arn't an option, allow dismissing the message. else if (nk_console_button_pushed(console, NK_GAMEPAD_BUTTON_B)) { - data->input_processed = nk_true; + nk_console_top_data* top_data = (nk_console_top_data*)nk_console_get_top(console)->data; + top_data->input_processed = nk_true; it->duration = 0.0f; } @@ -134,7 +137,7 @@ NK_API void nk_console_render_message(nk_console* console) { } if (clear_all) { - cvector_clear(data->messages); + cvector_clear(window_data->messages); } } diff --git a/nuklear_console_window.h b/nuklear_console_window.h new file mode 100644 index 0000000..56a92c4 --- /dev/null +++ b/nuklear_console_window.h @@ -0,0 +1,249 @@ +#ifndef NK_CONSOLE_WINDOW_H__ +#define NK_CONSOLE_WINDOW_H__ + +#if defined(__cplusplus) +extern "C" { +#endif + +typedef struct nk_console_window_data { + struct nk_rect bounds; + nk_flags flags; + + nk_console* active_parent; /** The parent that is currently being displayed. */ + + /** + * Message queue that is to be shown. + */ + struct nk_console_message* messages; + + /** + * When set, will determine where messages should appear in the window. + */ + struct nk_rect message_bounds; + + /** + * Custom user data. This is only applied to the window top console. + * + * @see nk_console_user_data() + * @see nk_console_set_user_data() + */ + void* user_data; + +} nk_console_window_data; + +NK_API nk_console* nk_console_window(nk_console* parent, const char* title, const struct nk_rect bounds, nk_flags flags); +NK_API struct nk_rect nk_console_window_render(nk_console* console); + +#if defined(__cplusplus) +} +#endif + +#endif // NK_CONSOLE_WINDOW_H__ + +#if defined(NK_CONSOLE_IMPLEMENTATION) && !defined(NK_CONSOLE_HEADER_ONLY) +#ifndef NK_CONSOLE_WINDOW_IMPLEMENTATION_ONCE +#define NK_CONSOLE_WINDOW_IMPLEMENTATION_ONCE + +#if defined(__cplusplus) +extern "C" { +#endif + + +/** + * Retrieve the first selectable widget from the given parent. + * + * @internal + */ +static nk_console* nk_console_find_first_selectable(nk_console* parent) { + if (parent == NULL || parent->children == NULL) { + return NULL; + } + + // Iterate through the children to find the first selectable widget. + for (size_t i = 0; i < cvector_size(parent->children); i++) { + if (parent->children[i] != NULL) { + if (nk_console_selectable(parent->children[i])) { + return parent->children[i]; + } + } + } + + return NULL; +} + +/** + * Check that all direct children of the given top console have the same type. + * + * All children are windows or all children are widgets, mixing is not allowed. + */ +static nk_bool nk_console_has_consistent_children(nk_console* top) { + NK_ASSERT(top != NULL); + NK_ASSERT(top->parent == NULL); + + if (cvector_size(top->children) == 0) { + return nk_true; // No children, so it satisfies the condition + } + + // Determine the type of the first child to establish the rule + nk_console_widget_type first_type = top->children[0]->type; + + // Verify that all children have the same type + for (size_t i = 1; i < cvector_size(top->children); i++) { + if (top->children[i]->type != first_type) { + return nk_false; // Mismatch found, condition not satisfied + } + } + + return nk_true; // All children are consistent +} + +/** + * Capture the window from the nk context. + */ +static nk_console* nk_console_capture_window(nk_console* console) { + NK_ASSERT(console->ctx->current != NULL); /** No window is currently active. */ + NK_ASSERT(console->parent == NULL); /** Only the top console can capture windows. */ + + // Get current window + struct nk_window* nk_win = console->ctx->current; + + // Store ptr to widgets + struct nk_console** children = console->children; + + // Clear top children + console->children = NULL; + + // Add a single window to the top console + nk_console* window = nk_console_window(console, nk_win->name_string, nk_win->bounds, nk_win->flags); + + // Add widgets to window + window->children = children; + + // Set window as parent for all newly assigned children + for (size_t i = 0; i < cvector_size(window->children); ++i) { + window->children[i]->parent = window; + } + + return window; +} + + +/** + * Render the contents of a console window. + */ +NK_API struct nk_rect nk_console_window_render(nk_console* console) { + nk_console_window_data* data = (nk_console_window_data*)console->data; + + if (data == NULL) { + return nk_rect(0, 0, 0, 0); + } + + // Render the active message. + nk_console_render_message(console); + + // Render all of the parent's children. + if (data->active_parent->children != NULL) { + // Make sure there's an active widget selected. + if (data->active_parent->activeWidget == NULL) { + nk_console_set_active_widget(nk_console_find_first_selectable(data->active_parent)); + } + else { + // Make sure the widget actually exists and can be selected. + nk_bool widgetFound = nk_false; + for (size_t i = 0; i < cvector_size(data->active_parent->children); ++i) { + if (data->active_parent->children[i] == data->active_parent->activeWidget) { + // Ensure the widget is still selectable. + if (nk_console_selectable(data->active_parent->activeWidget)) { + widgetFound = nk_true; + } + break; + } + } + if (widgetFound == nk_false) { + nk_console_set_active_widget(nk_console_find_first_selectable(data->active_parent)); + } + } + + // Render all the children + for (size_t i = 0; i < cvector_size(data->active_parent->children); ++i) { + nk_console_render(data->active_parent->children[i]); + } + } + + // Invoke the post-render events. + size_t count = cvector_size(console->events); + if (count > 0) { + nk_bool can_erase = nk_true; + for (size_t i = 0; i < count; i++) { + if (console->events[i].type == NK_CONSOLE_EVENT_POST_RENDER_ONCE) { + // Call the callback if it's set. + if (console->events[i].callback != NULL) { + console->events[i].callback((nk_console*)console->events[i].user_data, NULL); + console->events[i].callback = NULL; + } + } + else { + can_erase = nk_false; + } + } + if (can_erase) { + cvector_clear(console->events); + } + } + + nk_console* top = nk_console_get_top(console); + nk_console_top_data* top_data = (nk_console_top_data*)top->data; + + if (!console->disabled && !top_data->input_processed) { + if (nk_console_button_pushed(top, NK_GAMEPAD_BUTTON_RB)) { + int index = (top_data->active_window_index + 1) % (int)cvector_size(top->children); + nk_console_set_active_window(top->children[index]); + top_data->input_processed = nk_true; + } + else if (nk_console_button_pushed(top, NK_GAMEPAD_BUTTON_LB)) { + int index = (top_data->active_window_index - 1 + (int)cvector_size(top->children)) % (int)cvector_size(top->children); + nk_console_set_active_window(top->children[index]); + top_data->input_processed = nk_true; + } + } + + return data->bounds; +} + + +/** + * Add a window to console + */ +NK_API nk_console* nk_console_window(nk_console* top, const char* title, const struct nk_rect bounds, nk_flags flags) { + NK_ASSERT(nk_console_has_consistent_children(top)); + + nk_handle handle; + + nk_console_window_data* data = (nk_console_window_data*)nk_console_malloc(handle, NULL, sizeof(nk_console_window_data)); + nk_zero(data, sizeof(nk_console_window_data)); + + data->bounds = bounds; + data->flags = flags; + + nk_console* window = nk_console_label(top, title); + window->data = (void*)data; + window->type = NK_CONSOLE_WINDOW; + window->selectable = nk_false; + window->columns = 1; + window->render = nk_console_window_render; + + data->active_parent = window; + + nk_console_top_data* top_data = (nk_console_top_data*)top->data; + top_data->window_count++; + + return window; +} + + +#if defined(__cplusplus) +} +#endif + +#endif // NK_CONSOLE_WINDOW_IMPLEMENTATION_ONCE +#endif // NK_CONSOLE_IMPLEMENTATION