From 87b0f100201634368e48c0e052aaa2624ca7d9ba Mon Sep 17 00:00:00 2001 From: Fredrik Kallevik Date: Sat, 16 Nov 2024 16:56:34 +0100 Subject: [PATCH 1/5] Add support for multiple consoles/windows. Suggesting an initial solution for issue #5 --- demo/common/nuklear_console_demo.c | 230 ++++++++++++++++----------- demo/sdl_renderer/main.c | 44 +++++- nuklear_console.h | 244 ++++++++++++++++++++++++++++- 3 files changed, 416 insertions(+), 102 deletions(-) diff --git a/demo/common/nuklear_console_demo.c b/demo/common/nuklear_console_demo.c index 346398b..39fea57 100644 --- a/demo/common/nuklear_console_demo.c +++ b/demo/common/nuklear_console_demo.c @@ -10,56 +10,85 @@ #include "../../nuklear_console.h" // Demo -static struct nk_console* console = NULL; 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 { + // 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 +98,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 +115,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,9 +126,7 @@ 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_context* ctx, struct nk_console* console, struct demo_console_state* state, void* user_data, struct nk_image image) { nk_gamepad_init(&gamepads, ctx, user_data); nk_console_set_gamepads(console, &gamepads); @@ -138,22 +165,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 +206,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); } @@ -239,45 +266,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,10 +312,10 @@ 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"); @@ -342,17 +369,38 @@ nk_console* nuklear_console_demo_init(struct nk_context* ctx, void* user_data, s nk_console_button(console, "Save Game")->disabled = nk_true; nk_console_button_onclick(console, "Quit Game", &button_clicked); - - return console; } -nk_bool nuklear_console_demo_render() { +nk_bool nuklear_console_demo_render(nk_console* console) { nk_console_render(console); - return shouldClose;; + return shouldClose; } -void nuklear_console_demo_free() { +nk_bool nuklear_console_demo_render_window(nk_console* console, int num, int width, int height, int flags) { + char title[18]; + snprintf(title, sizeof(title), "nuklear_console_%d", num); + + int margin = 10; + + nk_console_render_window( + console, + title, + nk_rect(margin + width * (num - 1), margin, width - margin, height - margin * 2), + flags); + + return shouldClose; +} + +#if CONSOLE_COUNT < 2 +void nuklear_console_demo_free(nk_console* console) { nk_gamepad_free(nk_console_get_gamepads(console)); nk_console_free(console); } +#else +void nuklear_console_demo_free(nk_consoles* consoles) { + nk_gamepad_free(&gamepads); + nk_consoles_free(consoles); +} +#endif + diff --git a/demo/sdl_renderer/main.c b/demo/sdl_renderer/main.c index 7053410..b22c275 100644 --- a/demo/sdl_renderer/main.c +++ b/demo/sdl_renderer/main.c @@ -23,8 +23,10 @@ #include "../../vendor/Nuklear/nuklear.h" #include "../../vendor/Nuklear/demo/sdl_renderer/nuklear_sdl_renderer.h" -#define WINDOW_WIDTH 800 -#define WINDOW_HEIGHT 600 +#define WINDOW_WIDTH 1280 +#define WINDOW_HEIGHT 720 +#define CONSOLE_COUNT 3 +NK_STATIC_ASSERT(CONSOLE_COUNT > 0); #include "../common/nuklear_console_demo.c" @@ -103,9 +105,26 @@ int main(int argc, char *argv[]) { SDL_FreeSurface(surface); } - nk_console* console = nuklear_console_demo_init(ctx, NULL, img); +#if CONSOLE_COUNT < 2 + nk_console* console = nk_console_init(ctx); + struct demo_console_state state = demo_console_state_defaults(); + nuklear_console_demo_init(ctx, console, &state, NULL, img); +#else + nk_consoles* consoles = nk_console_init_multiple(ctx, CONSOLE_COUNT, 0); + + struct demo_console_state states[CONSOLE_COUNT]; + + for (int i = 0; i < CONSOLE_COUNT; i++) { + states[i] = demo_console_state_defaults(); + nuklear_console_demo_init(ctx, consoles->consoles[i], &states[i], NULL, img); + } +#endif while (running) { +#if CONSOLE_COUNT > 1 + nk_console* console = nk_console_get_active_console(consoles); +#endif + /* Input */ SDL_Event evt; nk_input_begin(ctx); @@ -119,16 +138,25 @@ int main(int argc, char *argv[]) { } nk_input_end(ctx); + int flags = NK_WINDOW_SCROLL_AUTO_HIDE | NK_WINDOW_TITLE; /* GUI */ + /* Render it, and see if we're to stop running. */ +#if CONSOLE_COUNT < 2 if (nk_begin(ctx, "nuklear_console", nk_rect(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT), flags)) { - /* Render it, and see if we're to stop running. */ - if (nuklear_console_demo_render()) { + if (nuklear_console_demo_render(console)) { running = 0; } } nk_end(ctx); +#else + for (int i = 0; i < CONSOLE_COUNT; i++) { + if (nuklear_console_demo_render_window(consoles->consoles[i], i + 1, WINDOW_WIDTH / CONSOLE_COUNT, WINDOW_HEIGHT, flags)) { + running = 0; + } + } +#endif SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); SDL_RenderClear(renderer); @@ -142,7 +170,11 @@ int main(int argc, char *argv[]) { if (texture != NULL) { SDL_DestroyTexture(texture); } - nuklear_console_demo_free(); +#if CONSOLE_COUNT < 2 + nuklear_console_demo_free(console); +#else + nuklear_console_demo_free(consoles); +#endif nk_sdl_shutdown(); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(win); diff --git a/nuklear_console.h b/nuklear_console.h index 51c9b85..e0dc027 100644 --- a/nuklear_console.h +++ b/nuklear_console.h @@ -77,6 +77,14 @@ typedef struct nk_console_message { float duration; } nk_console_message; +typedef struct nk_consoles { + int active_console_index; /** The index of the active console. */ + struct nk_console** consoles; /** The consoles that are being managed. */ + struct nk_console* dispatcher; /** The console that is currently dispatching a console switch. */ + struct nk_console* target; /** The console that is the target of the console switch. */ + int count; /** The number of consoles that are being managed. */ +} nk_consoles; + typedef struct nk_console { nk_console_widget_type type; const char* label; @@ -123,6 +131,11 @@ typedef struct nk_console_top_data { */ struct nk_gamepads* gamepads; + /** + * The multiple consoles that are being managed, if any. + */ + struct nk_consoles* consoles; + /** * Custom user data. This is only applied to the top-level console. * @@ -138,7 +151,15 @@ 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 struct nk_consoles* nk_console_init_multiple(struct nk_context* context, int count, int active_console_index); +NK_API void nk_consoles_free(nk_consoles* consoles); +NK_API void nk_console_set_active_console(nk_console* console); +NK_API nk_console* nk_console_get_active_console(nk_consoles* consoles); +NK_API void nk_console_handle_console_switch(nk_console* widget); + // Utilities +NK_API nk_bool nk_console_has_multiple_consoles(nk_console* widget); NK_API nk_console* nk_console_get_top(nk_console* widget); NK_API int nk_console_get_widget_index(nk_console* widget); NK_API void nk_console_check_tooltip(nk_console* console); @@ -408,7 +429,14 @@ NK_API nk_bool nk_console_is_active_widget(nk_console* widget) { return nk_false; } + nk_console* top = nk_console_get_top(widget); + + if (top->disabled) { + return nk_false; + } + nk_console* parent = widget->parent == NULL ? widget : widget->parent; + return parent->activeWidget == widget; } @@ -498,6 +526,11 @@ 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); + + if (top->disabled) { + return; + } + nk_console_top_data* data = (nk_console_top_data*)top->data; // Scroll to the active widget if needed. @@ -514,7 +547,9 @@ 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) { // 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 consoles. + if (!nk_console_has_multiple_consoles(top) && nk_console_button_pushed(top, NK_GAMEPAD_BUTTON_LB)) { int widgetIndex = nk_console_get_widget_index(widget); int count = 0; while (--widgetIndex >= 0) { @@ -529,7 +564,9 @@ NK_API void nk_console_check_up_down(nk_console* widget, struct nk_rect bounds) 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 consoles. + else if (!nk_console_has_multiple_consoles(top) && 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)) { @@ -688,6 +725,9 @@ NK_API void nk_console_render(nk_console* console) { // Reset the input state. data->input_processed = nk_false; + // Handle console switching. + nk_console_handle_console_switch(console); + // Render the active message. nk_console_render_message(console); @@ -757,8 +797,13 @@ NK_API void nk_console_render(nk_console* console) { 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. if (nk_console_selectable(console)) { + // Ensure the console is active. + if (nk_console_has_multiple_consoles(top)) { + nk_console_set_active_console(top); + } + + // Select the widget. nk_console_set_active_widget(console); data->input_processed = nk_true; } @@ -799,6 +844,158 @@ NK_API nk_console* nk_console_init(struct nk_context* context) { return console; } +/** + * Initialize a new nk_consoles structure with the given number of consoles. + * + * @param context The associated Nuklear context. + * @param count The number of consoles to initialize. + * @param active_console_index The index of the console to make active. + * + * @return The initialized nk_consoles structure. + */ +NK_API nk_consoles* nk_console_init_multiple(struct nk_context* context, int count, int active_console_index) { + if (!context || count <= 0 || active_console_index < 0 || active_console_index >= count) { + fprintf(stderr, "Error: Invalid parameters passed to nk_console_init_multiple.\n"); + return NULL; + } + + nk_handle handle; + + // Allocate memory for the nk_consoles structure + nk_consoles* consoles = (nk_consoles*)nk_console_malloc(handle, NULL, sizeof(nk_consoles)); + if (!consoles) { + fprintf(stderr, "Error: Failed to allocate memory for nk_consoles.\n"); + return NULL; + } + nk_zero(consoles, sizeof(nk_consoles)); + + consoles->count = count; + consoles->active_console_index = active_console_index; + + // Allocate memory for the array of console pointers + consoles->consoles = (nk_console**)nk_console_malloc(handle, NULL, count * sizeof(nk_console*)); + if (!consoles->consoles) { + fprintf(stderr, "Error: Failed to allocate memory for console pointers.\n"); + nk_consoles_free(consoles); + return NULL; + } + + // Initialize each console + for (int i = 0; i < count; ++i) { + nk_console* console = nk_console_init(context); + if (!console) { + fprintf(stderr, "Error: Failed to initialize console at index %d.\n", i); + nk_consoles_free(consoles); + return NULL; + } + consoles->consoles[i] = console; + + // Link the consoles structure back to the console + nk_console_top_data* data = console->data; + if (data) data->consoles = consoles; + + // Disable all consoles except the active one + console->disabled = (i != active_console_index); + } + + return consoles; +} + +/** + * Check if the console is associated with multiple consoles. + * + * @param widget The widget to check. + * + * @return nk_true if the widget has multiple consoles, nk_false otherwise. + */ +NK_API nk_bool nk_console_has_multiple_consoles(nk_console* top) { + NK_ASSERT(top->parent == NULL); + nk_console_top_data* data = (nk_console_top_data*)top->data; + return data->consoles != NULL && data->consoles->count > 1; +} + +/** + * Set the given console as the active console. + * + * @param top The top-level console. + */ +NK_API void nk_console_set_active_console(nk_console* top) { + NK_ASSERT(top->parent == NULL); + + nk_console_top_data* data = (nk_console_top_data*)top->data; + + for (int i = 0; i < data->consoles->count; i++) { + if (data->consoles->consoles[i] == top) { + data->consoles->consoles[i]->disabled = nk_false; + data->consoles->active_console_index = i; + } + else { + data->consoles->consoles[i]->disabled = nk_true; + } + } +} + +/** + * Get the active console from the given consoles structure. + * + * @param consoles The consoles structure. + * + * @return The active console. + */ +NK_API nk_console* nk_console_get_active_console(nk_consoles* consoles) { + NK_ASSERT(consoles != NULL); + return consoles->consoles[consoles->active_console_index]; +} + +/** + * Handle console switching. + * + * A console switch is dispatched (queued) when the user presses the left or right bumper on the gamepad. + * The switch is then performed on the next frame. The occurence of the next frame is inferred + * from the dispatcher console's next call. + * + * @param top The top-level console. + */ +NK_API void nk_console_handle_console_switch(nk_console* top) { + NK_ASSERT(top->parent == NULL); + + nk_console_top_data* data = (nk_console_top_data*)top->data; + + // If there aren't multiple consoles, there's nothing to do. + if (!nk_console_has_multiple_consoles(top)) { + return; + } + + // Perform the queued console switch, if this console queued it. + if (data->consoles->dispatcher == top) { + // Switch to the next active console + nk_console_set_active_console(data->consoles->target); + + // Clear the queue after performing the switch + data->consoles->dispatcher = NULL; + data->consoles->target = NULL; + return; + } + + // A console switch is already underway, so no need to check for console switch input. + if (data->consoles->dispatcher != NULL) { + return; + } + + // Queue the next console to be activated + if (nk_console_button_pushed(top, NK_GAMEPAD_BUTTON_RB)) { + int index = (data->consoles->active_console_index + 1) % data->consoles->count; + data->consoles->dispatcher = top; + data->consoles->target = data->consoles->consoles[index]; + } + // Queue the previous console to be activated + else if (nk_console_button_pushed(top, NK_GAMEPAD_BUTTON_LB)) { + int index = (data->consoles->active_console_index - 1 + data->consoles->count) % data->consoles->count; + data->consoles->dispatcher = top; + data->consoles->target = data->consoles->consoles[index]; + } +} + /** * Renders the given console to a new window with the given properties. * @@ -819,6 +1016,25 @@ NK_API void nk_console_render_window(nk_console* console, const char* title, str nk_end(console->ctx); } +/** + * Free all consoles associated with the given nk_consoles structure, + * as well as the structure itself. + * + * @param consoles The consoles structure to free. + */ +NK_API void nk_consoles_free(nk_consoles* consoles) { + if (consoles == NULL) { + return; + } + + for (int i = 0; i < consoles->count; i++) { + nk_console_free(consoles->consoles[i]); + } + + nk_console_mfree(nk_handle_id(0), consoles->consoles); + nk_console_mfree(nk_handle_id(0), consoles); +} + /** * Free the given nk_console's data. */ @@ -979,8 +1195,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 consoles. + // Both could be supported when nuklear_gamepad supports NK_GAMEPAD_BUTTON_LT and NK_GAMEPAD_BUTTON_RT. + if (nk_console_has_multiple_consoles(console)) { + 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 consoles. + // Both could be supported when nuklear_gamepad supports NK_GAMEPAD_BUTTON_LT and NK_GAMEPAD_BUTTON_RT. + if (nk_console_has_multiple_consoles(console)) { + 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); From 731681cd44f75c91eca2c878ba924787daefe63b Mon Sep 17 00:00:00 2001 From: Fredrik Kallevik Date: Mon, 18 Nov 2024 22:38:55 +0100 Subject: [PATCH 2/5] Refactor multiple window solution to use nk_console_window. --- demo/common/nuklear_console_demo.c | 129 +++---- demo/sdl_renderer/Makefile | 32 +- demo/sdl_renderer/main.c | 37 +- demo/sdl_renderer/main_multiple_windows.c | 53 +++ demo/sdl_renderer/setup.h | 116 ++++++ nuklear_console.h | 408 ++++++---------------- nuklear_console_button.h | 6 +- nuklear_console_message.h | 25 +- nuklear_console_window.h | 248 +++++++++++++ 9 files changed, 618 insertions(+), 436 deletions(-) create mode 100644 demo/sdl_renderer/main_multiple_windows.c create mode 100644 demo/sdl_renderer/setup.h create mode 100644 nuklear_console_window.h diff --git a/demo/common/nuklear_console_demo.c b/demo/common/nuklear_console_demo.c index 39fea57..fd65878 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" @@ -14,6 +14,8 @@ static struct nk_gamepads gamepads; static nk_bool shouldClose = nk_false; struct demo_console_state { + char title[64]; + // Theme int theme; @@ -127,8 +129,10 @@ void nk_console_radio_changed(struct nk_console* radio, void* user_data) { } void nuklear_console_demo_init(struct nk_context* ctx, 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"); @@ -240,22 +244,22 @@ void nuklear_console_demo_init(struct nk_context* ctx, struct nk_console* consol { 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); @@ -320,51 +324,51 @@ void nuklear_console_demo_init(struct nk_context* ctx, struct nk_console* consol // Rows 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_set_symbol( + nk_console_button_onclick(calc, "Back", &nk_console_button_back), + NK_SYMBOL_TRIANGLE_LEFT); + + calc->tooltip = "Demo rows and grids!"; } nk_console_button(console, "Save Game")->disabled = nk_true; @@ -377,30 +381,7 @@ nk_bool nuklear_console_demo_render(nk_console* console) { return shouldClose; } -nk_bool nuklear_console_demo_render_window(nk_console* console, int num, int width, int height, int flags) { - char title[18]; - snprintf(title, sizeof(title), "nuklear_console_%d", num); - - int margin = 10; - - nk_console_render_window( - console, - title, - nk_rect(margin + width * (num - 1), margin, width - margin, height - margin * 2), - flags); - - return shouldClose; -} - -#if CONSOLE_COUNT < 2 void nuklear_console_demo_free(nk_console* console) { nk_gamepad_free(nk_console_get_gamepads(console)); nk_console_free(console); } -#else -void nuklear_console_demo_free(nk_consoles* consoles) { - nk_gamepad_free(&gamepads); - nk_consoles_free(consoles); -} -#endif - 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 b22c275..cd32dba 100644 --- a/demo/sdl_renderer/main.c +++ b/demo/sdl_renderer/main.c @@ -23,10 +23,8 @@ #include "../../vendor/Nuklear/nuklear.h" #include "../../vendor/Nuklear/demo/sdl_renderer/nuklear_sdl_renderer.h" -#define WINDOW_WIDTH 1280 -#define WINDOW_HEIGHT 720 -#define CONSOLE_COUNT 3 -NK_STATIC_ASSERT(CONSOLE_COUNT > 0); +#define WINDOW_WIDTH 800 +#define WINDOW_HEIGHT 600 #include "../common/nuklear_console_demo.c" @@ -105,26 +103,12 @@ int main(int argc, char *argv[]) { SDL_FreeSurface(surface); } -#if CONSOLE_COUNT < 2 nk_console* console = nk_console_init(ctx); + struct demo_console_state state = demo_console_state_defaults(); nuklear_console_demo_init(ctx, console, &state, NULL, img); -#else - nk_consoles* consoles = nk_console_init_multiple(ctx, CONSOLE_COUNT, 0); - - struct demo_console_state states[CONSOLE_COUNT]; - - for (int i = 0; i < CONSOLE_COUNT; i++) { - states[i] = demo_console_state_defaults(); - nuklear_console_demo_init(ctx, consoles->consoles[i], &states[i], NULL, img); - } -#endif while (running) { -#if CONSOLE_COUNT > 1 - nk_console* console = nk_console_get_active_console(consoles); -#endif - /* Input */ SDL_Event evt; nk_input_begin(ctx); @@ -138,25 +122,16 @@ int main(int argc, char *argv[]) { } nk_input_end(ctx); - int flags = NK_WINDOW_SCROLL_AUTO_HIDE | NK_WINDOW_TITLE; /* GUI */ - /* Render it, and see if we're to stop running. */ -#if CONSOLE_COUNT < 2 if (nk_begin(ctx, "nuklear_console", nk_rect(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT), flags)) { + /* Render it, and see if we're to stop running. */ if (nuklear_console_demo_render(console)) { running = 0; } } nk_end(ctx); -#else - for (int i = 0; i < CONSOLE_COUNT; i++) { - if (nuklear_console_demo_render_window(consoles->consoles[i], i + 1, WINDOW_WIDTH / CONSOLE_COUNT, WINDOW_HEIGHT, flags)) { - running = 0; - } - } -#endif SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); SDL_RenderClear(renderer); @@ -170,11 +145,7 @@ int main(int argc, char *argv[]) { if (texture != NULL) { SDL_DestroyTexture(texture); } -#if CONSOLE_COUNT < 2 nuklear_console_demo_free(console); -#else - nuklear_console_demo_free(consoles); -#endif nk_sdl_shutdown(); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(win); diff --git a/demo/sdl_renderer/main_multiple_windows.c b/demo/sdl_renderer/main_multiple_windows.c new file mode 100644 index 0000000..84fc3a2 --- /dev/null +++ b/demo/sdl_renderer/main_multiple_windows.c @@ -0,0 +1,53 @@ +#include "setup.h" + +int main(int argc, char* argv[]) { + NK_UNUSED(argc); + NK_UNUSED(argv); + + struct demo_ctx demo = init_demo_context(1280, 720); + + struct nk_context* ctx = demo.ctx; + + // Init console + demo.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++) { + states[i] = demo_console_state_defaults(); + snprintf(states[i].title, sizeof(states[i].title), "nuklear_console_%d", i + 1); + nk_console* window = nk_console_window(demo.console, states[i].title, nk_rect(i * demo.window_width / WINDOW_COUNT, 0, demo.window_width / WINDOW_COUNT, demo.window_height), window_flags); + + nuklear_console_demo_init(ctx, 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(demo.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(demo.console), &evt); + } + nk_input_end(ctx); + + /* GUI */ + if (nuklear_console_demo_render(demo.console)) { + 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..fde7c83 --- /dev/null +++ b/demo/sdl_renderer/setup.h @@ -0,0 +1,116 @@ +#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_context* ctx; + struct nk_console* console; + 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 */ + demo->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(demo->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(demo->console); + 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 e0dc027..a1c7066 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 { @@ -77,14 +78,6 @@ typedef struct nk_console_message { float duration; } nk_console_message; -typedef struct nk_consoles { - int active_console_index; /** The index of the active console. */ - struct nk_console** consoles; /** The consoles that are being managed. */ - struct nk_console* dispatcher; /** The console that is currently dispatching a console switch. */ - struct nk_console* target; /** The console that is the target of the console switch. */ - int count; /** The number of consoles that are being managed. */ -} nk_consoles; - typedef struct nk_console { nk_console_widget_type type; const char* label; @@ -110,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. * @@ -131,10 +112,8 @@ typedef struct nk_console_top_data { */ struct nk_gamepads* gamepads; - /** - * The multiple consoles that are being managed, if any. - */ - struct nk_consoles* consoles; + 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. @@ -152,15 +131,11 @@ 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 struct nk_consoles* nk_console_init_multiple(struct nk_context* context, int count, int active_console_index); -NK_API void nk_consoles_free(nk_consoles* consoles); -NK_API void nk_console_set_active_console(nk_console* console); -NK_API nk_console* nk_console_get_active_console(nk_consoles* consoles); -NK_API void nk_console_handle_console_switch(nk_console* widget); +NK_API void nk_console_set_active_window(nk_console* console); // Utilities -NK_API nk_bool nk_console_has_multiple_consoles(nk_console* widget); 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); @@ -232,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) @@ -315,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) { @@ -429,14 +406,13 @@ NK_API nk_bool nk_console_is_active_widget(nk_console* widget) { return nk_false; } - nk_console* top = nk_console_get_top(widget); + nk_console* window = nk_console_get_window(widget); - if (top->disabled) { + if (window->disabled) { return nk_false; } nk_console* parent = widget->parent == NULL ? widget : widget->parent; - return parent->activeWidget == widget; } @@ -445,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) { @@ -455,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; } /** @@ -526,12 +520,10 @@ 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* window = nk_console_get_window(widget); - if (top->disabled) { - return; - } - - nk_console_top_data* data = (nk_console_top_data*)top->data; + nk_console_top_data* top_data = (nk_console_top_data*)top->data; + nk_console_window_data* window_data = (nk_console_window_data*)window->data; // Scroll to the active widget if needed. struct nk_rect content_region = nk_window_get_content_region(widget->ctx); @@ -545,11 +537,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 // 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 consoles. - if (!nk_console_has_multiple_consoles(top) && nk_console_button_pushed(top, NK_GAMEPAD_BUTTON_LB)) { + // 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) { @@ -561,12 +553,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 // 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 consoles. - else if (!nk_console_has_multiple_consoles(top) && nk_console_button_pushed(top, NK_GAMEPAD_BUTTON_RB)) { + // 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)) { @@ -578,7 +570,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)) { @@ -590,7 +582,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)) { @@ -602,50 +594,28 @@ 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; + top_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]; - } - } - } - - return NULL; -} - /** * A function to check whether or not the mouse moved. */ @@ -718,71 +688,37 @@ 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; - - // Handle console switching. - nk_console_handle_console_switch(console); - - // 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)); - } - } + top_data->input_processed = nk_false; - // 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 (int 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; } + // Render the widget and get its bounds. struct nk_rect widget_bounds = console->render != NULL ? console->render(console) : nk_rect(0, 0, 0, 0); @@ -795,17 +731,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)) { + 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 (nk_console_has_multiple_consoles(top)) { - nk_console_set_active_console(top); + 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; } } } @@ -838,80 +775,12 @@ 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->active_window_index = 0; console->data = data; - return console; -} - -/** - * Initialize a new nk_consoles structure with the given number of consoles. - * - * @param context The associated Nuklear context. - * @param count The number of consoles to initialize. - * @param active_console_index The index of the console to make active. - * - * @return The initialized nk_consoles structure. - */ -NK_API nk_consoles* nk_console_init_multiple(struct nk_context* context, int count, int active_console_index) { - if (!context || count <= 0 || active_console_index < 0 || active_console_index >= count) { - fprintf(stderr, "Error: Invalid parameters passed to nk_console_init_multiple.\n"); - return NULL; - } - - nk_handle handle; - - // Allocate memory for the nk_consoles structure - nk_consoles* consoles = (nk_consoles*)nk_console_malloc(handle, NULL, sizeof(nk_consoles)); - if (!consoles) { - fprintf(stderr, "Error: Failed to allocate memory for nk_consoles.\n"); - return NULL; - } - nk_zero(consoles, sizeof(nk_consoles)); - - consoles->count = count; - consoles->active_console_index = active_console_index; - - // Allocate memory for the array of console pointers - consoles->consoles = (nk_console**)nk_console_malloc(handle, NULL, count * sizeof(nk_console*)); - if (!consoles->consoles) { - fprintf(stderr, "Error: Failed to allocate memory for console pointers.\n"); - nk_consoles_free(consoles); - return NULL; - } - - // Initialize each console - for (int i = 0; i < count; ++i) { - nk_console* console = nk_console_init(context); - if (!console) { - fprintf(stderr, "Error: Failed to initialize console at index %d.\n", i); - nk_consoles_free(consoles); - return NULL; - } - consoles->consoles[i] = console; - - // Link the consoles structure back to the console - nk_console_top_data* data = console->data; - if (data) data->consoles = consoles; + data->window_count = 0; - // Disable all consoles except the active one - console->disabled = (i != active_console_index); - } - - return consoles; -} - -/** - * Check if the console is associated with multiple consoles. - * - * @param widget The widget to check. - * - * @return nk_true if the widget has multiple consoles, nk_false otherwise. - */ -NK_API nk_bool nk_console_has_multiple_consoles(nk_console* top) { - NK_ASSERT(top->parent == NULL); - nk_console_top_data* data = (nk_console_top_data*)top->data; - return data->consoles != NULL && data->consoles->count > 1; + return console; } /** @@ -919,81 +788,27 @@ NK_API nk_bool nk_console_has_multiple_consoles(nk_console* top) { * * @param top The top-level console. */ -NK_API void nk_console_set_active_console(nk_console* top) { +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; - nk_console_top_data* data = (nk_console_top_data*)top->data; - for (int i = 0; i < data->consoles->count; i++) { - if (data->consoles->consoles[i] == top) { - data->consoles->consoles[i]->disabled = nk_false; - data->consoles->active_console_index = i; + for (int i = 0; i < cvector_size(top->children); i++) { + nk_console* w = top->children[i]; + nk_console_window_data* window_data = (nk_console_window_data*)w->data; + if (w == window) { + w->disabled = nk_false; + top_data->active_window_index = i; } else { - data->consoles->consoles[i]->disabled = nk_true; + w->disabled = nk_true; } } -} -/** - * Get the active console from the given consoles structure. - * - * @param consoles The consoles structure. - * - * @return The active console. - */ -NK_API nk_console* nk_console_get_active_console(nk_consoles* consoles) { - NK_ASSERT(consoles != NULL); - return consoles->consoles[consoles->active_console_index]; -} - -/** - * Handle console switching. - * - * A console switch is dispatched (queued) when the user presses the left or right bumper on the gamepad. - * The switch is then performed on the next frame. The occurence of the next frame is inferred - * from the dispatcher console's next call. - * - * @param top The top-level console. - */ -NK_API void nk_console_handle_console_switch(nk_console* top) { - NK_ASSERT(top->parent == NULL); - - nk_console_top_data* data = (nk_console_top_data*)top->data; - - // If there aren't multiple consoles, there's nothing to do. - if (!nk_console_has_multiple_consoles(top)) { - return; - } - - // Perform the queued console switch, if this console queued it. - if (data->consoles->dispatcher == top) { - // Switch to the next active console - nk_console_set_active_console(data->consoles->target); - - // Clear the queue after performing the switch - data->consoles->dispatcher = NULL; - data->consoles->target = NULL; - return; - } - - // A console switch is already underway, so no need to check for console switch input. - if (data->consoles->dispatcher != NULL) { - return; - } - - // Queue the next console to be activated - if (nk_console_button_pushed(top, NK_GAMEPAD_BUTTON_RB)) { - int index = (data->consoles->active_console_index + 1) % data->consoles->count; - data->consoles->dispatcher = top; - data->consoles->target = data->consoles->consoles[index]; - } - // Queue the previous console to be activated - else if (nk_console_button_pushed(top, NK_GAMEPAD_BUTTON_LB)) { - int index = (data->consoles->active_console_index - 1 + data->consoles->count) % data->consoles->count; - data->consoles->dispatcher = top; - data->consoles->target = data->consoles->consoles[index]; - } + nk_window_set_focus(top->ctx, window->label); } /** @@ -1016,25 +831,6 @@ NK_API void nk_console_render_window(nk_console* console, const char* title, str nk_end(console->ctx); } -/** - * Free all consoles associated with the given nk_consoles structure, - * as well as the structure itself. - * - * @param consoles The consoles structure to free. - */ -NK_API void nk_consoles_free(nk_consoles* consoles) { - if (consoles == NULL) { - return; - } - - for (int i = 0; i < consoles->count; i++) { - nk_console_free(consoles->consoles[i]); - } - - nk_console_mfree(nk_handle_id(0), consoles->consoles); - nk_console_mfree(nk_handle_id(0), consoles); -} - /** * Free the given nk_console's data. */ @@ -1060,8 +856,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; @@ -1180,8 +976,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; } @@ -1196,9 +992,9 @@ NK_API nk_bool nk_console_button_pushed(nk_console* console, int button) { // 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: { - // PageUp/PageDown is replaced with console switching if there are multiple consoles. + // 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 (nk_console_has_multiple_consoles(console)) { + 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 { @@ -1206,9 +1002,9 @@ NK_API nk_bool nk_console_button_pushed(nk_console* console, int button) { }; } case NK_GAMEPAD_BUTTON_RB: { - // PageUp/PageDown is replaced with console switching if there are multiple consoles. + // 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 (nk_console_has_multiple_consoles(console)) { + 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 { 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..6d936a2 --- /dev/null +++ b/nuklear_console_window.h @@ -0,0 +1,248 @@ +#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 children of the given 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 struct nk_console* nk_console_capture_window(nk_console* console) { + NK_ASSERT(console->ctx->current != NULL); /** No window is currently active. */ + + // 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 window 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) % 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 + cvector_size(top->children)) % 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 From fae9979e443885067b9514e32164974ed479353e Mon Sep 17 00:00:00 2001 From: Fredrik Kallevik Date: Thu, 21 Nov 2024 20:58:28 +0100 Subject: [PATCH 3/5] Cleanup compiler warnings. --- nuklear_console.h | 8 +++----- nuklear_console_window.h | 4 ++-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/nuklear_console.h b/nuklear_console.h index a1c7066..7fc20e4 100644 --- a/nuklear_console.h +++ b/nuklear_console.h @@ -523,7 +523,6 @@ NK_API void nk_console_check_up_down(nk_console* widget, struct nk_rect bounds) nk_console* window = nk_console_get_window(widget); nk_console_top_data* top_data = (nk_console_top_data*)top->data; - nk_console_window_data* window_data = (nk_console_window_data*)window->data; // Scroll to the active widget if needed. struct nk_rect content_region = nk_window_get_content_region(widget->ctx); @@ -705,7 +704,7 @@ NK_API void nk_console_render(nk_console* console) { } else if (top_data->window_count > 1) { // Multiple windows added to console - for (int i = 0; i < cvector_size(console->children); i++) { + 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; @@ -796,12 +795,11 @@ NK_API void nk_console_set_active_window(nk_console* window) { nk_console_top_data* top_data = (nk_console_top_data*)top->data; - for (int i = 0; i < cvector_size(top->children); i++) { + for (size_t i = 0; i < cvector_size(top->children); i++) { nk_console* w = top->children[i]; - nk_console_window_data* window_data = (nk_console_window_data*)w->data; if (w == window) { w->disabled = nk_false; - top_data->active_window_index = i; + top_data->active_window_index = (int)i; } else { w->disabled = nk_true; diff --git a/nuklear_console_window.h b/nuklear_console_window.h index 6d936a2..6cdd4f4 100644 --- a/nuklear_console_window.h +++ b/nuklear_console_window.h @@ -195,12 +195,12 @@ NK_API struct nk_rect nk_console_window_render(nk_console* console) { 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) % cvector_size(top->children); + 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 + cvector_size(top->children)) % cvector_size(top->children); + 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; } From 86b8cd369e97377f18a1bec6eb84f6567c7cb724 Mon Sep 17 00:00:00 2001 From: Fredrik Kallevik Date: Fri, 22 Nov 2024 18:02:21 +0100 Subject: [PATCH 4/5] Some more cleanup. --- nuklear_console.h | 7 ++----- nuklear_console_window.h | 7 ++++--- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/nuklear_console.h b/nuklear_console.h index 7fc20e4..e6fdeb5 100644 --- a/nuklear_console.h +++ b/nuklear_console.h @@ -713,11 +713,9 @@ NK_API void nk_console_render(nk_console* console) { } } - return; } - // Render the widget and get its bounds. struct nk_rect widget_bounds = console->render != NULL ? console->render(console) : nk_rect(0, 0, 0, 0); @@ -774,16 +772,15 @@ 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->window_count = 0; data->active_window_index = 0; console->data = data; - data->window_count = 0; - return console; } /** - * Set the given console as the active console. + * Set the given window as the active window. * * @param top The top-level console. */ diff --git a/nuklear_console_window.h b/nuklear_console_window.h index 6cdd4f4..56a92c4 100644 --- a/nuklear_console_window.h +++ b/nuklear_console_window.h @@ -72,7 +72,7 @@ static nk_console* nk_console_find_first_selectable(nk_console* parent) { } /** - * Check that all children of the given console have the same type. + * 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. */ @@ -100,8 +100,9 @@ static nk_bool nk_console_has_consistent_children(nk_console* top) { /** * Capture the window from the nk context. */ -static struct nk_console* nk_console_capture_window(nk_console* console) { +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; @@ -118,7 +119,7 @@ static struct nk_console* nk_console_capture_window(nk_console* console) { // Add widgets to window window->children = children; - // Set window as parent for all window 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; } From 218f87dfe24baf7cb14666107e7ffc5ad32943d9 Mon Sep 17 00:00:00 2001 From: Fredrik Kallevik Date: Fri, 22 Nov 2024 20:31:45 +0100 Subject: [PATCH 5/5] Update demos to use the new demo_console_state struct. --- demo/common/nuklear_console_demo.c | 22 ++++++++++++---------- demo/glfw/main.c | 9 +++++++-- demo/pntr/main.c | 17 +++++++++++------ demo/raylib/main.c | 6 ++++-- demo/sdl_renderer/main.c | 12 +++++------- demo/sdl_renderer/main_multiple_windows.c | 22 ++++++++++++++-------- demo/sdl_renderer/setup.h | 8 +++----- 7 files changed, 56 insertions(+), 40 deletions(-) diff --git a/demo/common/nuklear_console_demo.c b/demo/common/nuklear_console_demo.c index fd65878..18d49bc 100644 --- a/demo/common/nuklear_console_demo.c +++ b/demo/common/nuklear_console_demo.c @@ -10,6 +10,8 @@ #include "../../nuklear_console.h" // Demo +static struct nk_context* ctx; +static struct nk_console* console; static struct nk_gamepads gamepads; static nk_bool shouldClose = nk_false; @@ -128,14 +130,14 @@ void nk_console_radio_changed(struct nk_console* radio, void* user_data) { nk_console_show_message(radio, radio->label); } -void nuklear_console_demo_init(struct nk_context* ctx, struct nk_console* console, struct demo_console_state* state, void* user_data, struct nk_image image) { - nk_console* top = nk_console_get_top(console); +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(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!"); @@ -143,7 +145,7 @@ void nuklear_console_demo_init(struct nk_context* ctx, struct nk_console* consol } // 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!"); @@ -316,13 +318,13 @@ void nuklear_console_demo_init(struct nk_context* ctx, struct nk_console* consol NK_SYMBOL_TRIANGLE_LEFT); } - nk_console* theme_options = nk_console_combobox(console, "Theme", "Black;White;Red;Blue;Dark;Dracula;Default", ';', &state->theme); + 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)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"); @@ -371,17 +373,17 @@ void nuklear_console_demo_init(struct nk_context* ctx, struct nk_console* consol calc->tooltip = "Demo rows and grids!"; } - nk_console_button(console, "Save Game")->disabled = nk_true; - nk_console_button_onclick(console, "Quit Game", &button_clicked); + 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* console) { +nk_bool nuklear_console_demo_render() { nk_console_render(console); return shouldClose; } -void nuklear_console_demo_free(nk_console* console) { +void nuklear_console_demo_free() { nk_gamepad_free(nk_console_get_gamepads(console)); nk_console_free(console); } 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/main.c b/demo/sdl_renderer/main.c index cd32dba..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,10 +100,11 @@ int main(int argc, char *argv[]) { SDL_FreeSurface(surface); } - nk_console* console = nk_console_init(ctx); + console = nk_console_init(ctx); + // Initialize console state struct demo_console_state state = demo_console_state_defaults(); - nuklear_console_demo_init(ctx, console, &state, NULL, img); + nuklear_console_demo_init(console, &state, NULL, img); while (running) { /* Input */ @@ -127,7 +125,7 @@ int main(int argc, char *argv[]) { /* GUI */ if (nk_begin(ctx, "nuklear_console", nk_rect(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT), flags)) { /* Render it, and see if we're to stop running. */ - if (nuklear_console_demo_render(console)) { + if (nuklear_console_demo_render()) { running = 0; } } @@ -145,7 +143,7 @@ int main(int argc, char *argv[]) { if (texture != NULL) { SDL_DestroyTexture(texture); } - nuklear_console_demo_free(console); + nuklear_console_demo_free(); nk_sdl_shutdown(); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(win); diff --git a/demo/sdl_renderer/main_multiple_windows.c b/demo/sdl_renderer/main_multiple_windows.c index 84fc3a2..8856784 100644 --- a/demo/sdl_renderer/main_multiple_windows.c +++ b/demo/sdl_renderer/main_multiple_windows.c @@ -4,12 +4,11 @@ int main(int argc, char* argv[]) { NK_UNUSED(argc); NK_UNUSED(argv); + // Init demo context struct demo_ctx demo = init_demo_context(1280, 720); - struct nk_context* ctx = demo.ctx; - // Init console - demo.console = nk_console_init(ctx); + console = nk_console_init(ctx); const int WINDOW_COUNT = 3; @@ -17,12 +16,19 @@ int main(int argc, char* argv[]) { // 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); - nk_console* window = nk_console_window(demo.console, states[i].title, nk_rect(i * demo.window_width / WINDOW_COUNT, 0, demo.window_width / WINDOW_COUNT, demo.window_height), window_flags); - nuklear_console_demo_init(ctx, window, &states[i], NULL, demo.img); + // 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; @@ -31,18 +37,18 @@ int main(int argc, char* argv[]) { /* Input */ SDL_Event evt; nk_input_begin(ctx); - nk_gamepad_update(nk_console_get_gamepads(demo.console)); + 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(demo.console), &evt); + nk_gamepad_sdl_handle_event(nk_console_get_gamepads(console), &evt); } nk_input_end(ctx); /* GUI */ - if (nuklear_console_demo_render(demo.console)) { + if (nuklear_console_demo_render()) { running = 0; } diff --git a/demo/sdl_renderer/setup.h b/demo/sdl_renderer/setup.h index fde7c83..6c7e564 100644 --- a/demo/sdl_renderer/setup.h +++ b/demo/sdl_renderer/setup.h @@ -20,8 +20,6 @@ struct demo_ctx { SDL_Renderer* renderer; - struct nk_context* ctx; - struct nk_console* console; struct nk_image img; SDL_Texture* _texture; SDL_Window* _window; @@ -53,7 +51,7 @@ static int configure(struct demo_ctx* demo) { } /* GUI */ - demo->ctx = nk_sdl_init(demo->_window, demo->renderer); + ctx = nk_sdl_init(demo->_window, demo->renderer); { struct nk_font_atlas* atlas; struct nk_font_config config = nk_font_config(0); @@ -62,7 +60,7 @@ static int configure(struct demo_ctx* demo) { 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(demo->ctx, &font->handle); + nk_style_set_font(ctx, &font->handle); } // Attempt to load the sample image. @@ -97,7 +95,7 @@ static int cleanup(struct demo_ctx* demo) { if (demo->_texture != NULL) { SDL_DestroyTexture(demo->_texture); } - nuklear_console_demo_free(demo->console); + nuklear_console_demo_free(); nk_sdl_shutdown(); SDL_DestroyRenderer(demo->renderer); SDL_DestroyWindow(demo->_window);