685 lines
24 KiB
C
685 lines
24 KiB
C
/*
|
|
* Nuklear GLFW3 Demo Application - Modular UI Components
|
|
* A simple desktop GUI application using GLFW3 and Nuklear with modular component architecture
|
|
*
|
|
* MODULAR COMPONENT STRUCTURE:
|
|
*
|
|
* This application demonstrates a clean, modular approach to UI component organization:
|
|
*
|
|
* 1. UI STATE MANAGEMENT:
|
|
* - Centralized state in ui_state_t structure
|
|
* - Clean separation between UI rendering and state management
|
|
* - Easy state reset and manipulation functions
|
|
*
|
|
* 2. COMPONENT INTERFACE:
|
|
* - Each UI component is a function with signature: void render_*_component(nk_context*, ui_state_t*)
|
|
* - Components are registered in ui_components[] array
|
|
* - Components can be enabled/disabled individually
|
|
* - Easy to add new components by implementing the interface and adding to registry
|
|
*
|
|
* 3. CLEAN SEPARATION:
|
|
* - GLFW window management is separate from UI rendering
|
|
* - Nuklear integration is isolated in init/cleanup functions
|
|
* - Application logic is separated from rendering logic
|
|
*
|
|
* 4. EXTENSIBILITY:
|
|
* - Adding new components requires: implement render function + add to registry
|
|
* - State management is centralized and easily extensible
|
|
* - Component enable/disable functionality built-in
|
|
*
|
|
* HOW TO ADD NEW COMPONENTS:
|
|
* 1. Add state variables to ui_state_t if needed
|
|
* 2. Implement render_*_component(ctx, state) function
|
|
* 3. Add component to ui_components[] array
|
|
* 4. Optionally add state management functions
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#define GLFW_INCLUDE_NONE
|
|
#include <GLFW/glfw3.h>
|
|
#include <GL/gl3w.h>
|
|
|
|
#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_IMPLEMENTATION
|
|
#define NK_GLFW_GL3_IMPLEMENTATION
|
|
#define NK_KEYSTATE_BASED_INPUT
|
|
#include "nuklear.h"
|
|
#include "nuklear_glfw_gl3.h"
|
|
|
|
// Window dimensions
|
|
static int window_width = 800;
|
|
static int window_height = 600;
|
|
static int min_window_width = 400;
|
|
static int min_window_height = 300;
|
|
static GLFWwindow* window = NULL;
|
|
|
|
// Nuklear context
|
|
static struct nk_context* ctx = NULL;
|
|
static struct nk_glfw glfw = {0};
|
|
|
|
// Nuklear rendering constants
|
|
#define MAX_VERTEX_BUFFER 512 * 1024
|
|
#define MAX_ELEMENT_BUFFER 128 * 1024
|
|
|
|
// =============================================================================
|
|
// UI COMPONENT STATE MANAGEMENT
|
|
// =============================================================================
|
|
|
|
// UI Component State Structure
|
|
typedef struct {
|
|
// Text input component state
|
|
char text_buffer[256];
|
|
|
|
// Slider component state
|
|
float slider_value;
|
|
|
|
// Checkbox component state
|
|
int checkbox_value;
|
|
|
|
// Radio button component state
|
|
int option_selected;
|
|
|
|
// Button component state
|
|
int button_click_count;
|
|
|
|
// Window information state
|
|
char status_message[512];
|
|
} ui_state_t;
|
|
|
|
// Global UI state instance
|
|
static ui_state_t ui_state = {
|
|
.text_buffer = "Hello World!",
|
|
.slider_value = 0.5f,
|
|
.checkbox_value = 1,
|
|
.option_selected = 0,
|
|
.button_click_count = 0,
|
|
.status_message = "Application started"
|
|
};
|
|
|
|
// =============================================================================
|
|
// UI COMPONENT INTERFACE DEFINITIONS
|
|
// =============================================================================
|
|
|
|
// UI Component rendering function type
|
|
typedef void (*ui_component_render_fn)(struct nk_context* ctx, ui_state_t* state);
|
|
|
|
// UI Component structure
|
|
typedef struct {
|
|
const char* name;
|
|
ui_component_render_fn render;
|
|
int enabled;
|
|
} ui_component_t;
|
|
|
|
// =============================================================================
|
|
// FORWARD DECLARATIONS
|
|
// =============================================================================
|
|
|
|
// Core application functions
|
|
void init_glfw(void);
|
|
void init_nuklear(void);
|
|
void render_ui(struct nk_context* ctx);
|
|
void cleanup(void);
|
|
|
|
// Window management callbacks
|
|
void window_size_callback(GLFWwindow* window, int width, int height);
|
|
void window_iconify_callback(GLFWwindow* window, int iconified);
|
|
void window_maximize_callback(GLFWwindow* window, int maximized);
|
|
void error_callback(int error, const char* description);
|
|
|
|
// UI utility functions
|
|
float get_ui_scale_factor(void);
|
|
struct nk_rect get_scaled_window_rect(void);
|
|
|
|
// UI Component rendering functions
|
|
void render_window_info_component(struct nk_context* ctx, ui_state_t* state);
|
|
void render_button_component(struct nk_context* ctx, ui_state_t* state);
|
|
void render_text_input_component(struct nk_context* ctx, ui_state_t* state);
|
|
void render_slider_component(struct nk_context* ctx, ui_state_t* state);
|
|
void render_checkbox_component(struct nk_context* ctx, ui_state_t* state);
|
|
void render_radio_button_component(struct nk_context* ctx, ui_state_t* state);
|
|
void render_status_component(struct nk_context* ctx, ui_state_t* state);
|
|
|
|
// UI state management functions
|
|
void ui_state_update_status(const char* message);
|
|
void ui_state_reset(void);
|
|
|
|
// UI component management functions
|
|
void ui_component_set_enabled(const char* component_name, int enabled);
|
|
int ui_component_is_enabled(const char* component_name);
|
|
|
|
// Error callback for GLFW
|
|
void error_callback(int error, const char* description) {
|
|
fprintf(stderr, "GLFW Error %d: %s\n", error, description);
|
|
}
|
|
|
|
// Window resize callback
|
|
void window_size_callback(GLFWwindow* window, int width, int height) {
|
|
// Enforce minimum window size constraints
|
|
if (width < min_window_width || height < min_window_height) {
|
|
int new_width = width < min_window_width ? min_window_width : width;
|
|
int new_height = height < min_window_height ? min_window_height : height;
|
|
glfwSetWindowSize(window, new_width, new_height);
|
|
return;
|
|
}
|
|
|
|
window_width = width;
|
|
window_height = height;
|
|
glViewport(0, 0, width, height);
|
|
|
|
printf("Window resized to: %dx%d\n", width, height);
|
|
}
|
|
|
|
// Window iconify (minimize) callback
|
|
void window_iconify_callback(GLFWwindow* window, int iconified) {
|
|
if (iconified) {
|
|
printf("Window minimized\n");
|
|
} else {
|
|
printf("Window restored from minimized state\n");
|
|
}
|
|
}
|
|
|
|
// Window maximize callback
|
|
void window_maximize_callback(GLFWwindow* window, int maximized) {
|
|
if (maximized) {
|
|
printf("Window maximized\n");
|
|
} else {
|
|
printf("Window restored from maximized state\n");
|
|
}
|
|
}
|
|
|
|
// Calculate UI scale factor based on window size
|
|
float get_ui_scale_factor(void) {
|
|
// Base scale factor on window width, with minimum scale of 0.8 and maximum of 2.0
|
|
float base_width = 800.0f;
|
|
float scale = (float)window_width / base_width;
|
|
|
|
// Clamp scale factor to reasonable bounds
|
|
if (scale < 0.8f) scale = 0.8f;
|
|
if (scale > 2.0f) scale = 2.0f;
|
|
|
|
return scale;
|
|
}
|
|
|
|
// Get scaled window rectangle for responsive UI layout
|
|
struct nk_rect get_scaled_window_rect(void) {
|
|
float scale = get_ui_scale_factor();
|
|
|
|
// Calculate responsive window position and size
|
|
float base_x = 50.0f;
|
|
float base_y = 50.0f;
|
|
float base_width = 400.0f;
|
|
float base_height = 500.0f;
|
|
|
|
// Scale the window dimensions but keep it within screen bounds
|
|
float scaled_width = base_width * scale;
|
|
float scaled_height = base_height * scale;
|
|
|
|
// Ensure the window fits within the screen
|
|
if (scaled_width > window_width - 100) {
|
|
scaled_width = window_width - 100;
|
|
}
|
|
if (scaled_height > window_height - 100) {
|
|
scaled_height = window_height - 100;
|
|
}
|
|
|
|
// Center the window if it's getting too large
|
|
float x = base_x;
|
|
float y = base_y;
|
|
if (scaled_width > base_width * 1.5f) {
|
|
x = (window_width - scaled_width) / 2.0f;
|
|
}
|
|
if (scaled_height > base_height * 1.5f) {
|
|
y = (window_height - scaled_height) / 2.0f;
|
|
}
|
|
|
|
return nk_rect(x, y, scaled_width, scaled_height);
|
|
}
|
|
|
|
// Initialize GLFW and create window
|
|
void init_glfw(void) {
|
|
// Set error callback
|
|
glfwSetErrorCallback(error_callback);
|
|
|
|
// Initialize GLFW
|
|
if (!glfwInit()) {
|
|
fprintf(stderr, "Failed to initialize GLFW\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// Set OpenGL version to 3.3 core profile
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
|
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
|
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
|
|
|
|
// Create window
|
|
window = glfwCreateWindow(window_width, window_height, "Nuklear GLFW3 Demo", NULL, NULL);
|
|
if (!window) {
|
|
fprintf(stderr, "Failed to create GLFW window\n");
|
|
glfwTerminate();
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// Make the window's context current
|
|
glfwMakeContextCurrent(window);
|
|
|
|
// Initialize OpenGL loader (gl3w)
|
|
if (gl3wInit() != 0) {
|
|
fprintf(stderr, "Failed to initialize OpenGL loader\n");
|
|
glfwDestroyWindow(window);
|
|
glfwTerminate();
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// Set window size limits
|
|
glfwSetWindowSizeLimits(window, min_window_width, min_window_height, GLFW_DONT_CARE, GLFW_DONT_CARE);
|
|
|
|
// Set callbacks
|
|
glfwSetWindowSizeCallback(window, window_size_callback);
|
|
glfwSetWindowIconifyCallback(window, window_iconify_callback);
|
|
glfwSetWindowMaximizeCallback(window, window_maximize_callback);
|
|
|
|
// Enable vsync
|
|
glfwSwapInterval(1);
|
|
|
|
// Set initial viewport
|
|
glViewport(0, 0, window_width, window_height);
|
|
|
|
printf("GLFW initialized successfully\n");
|
|
printf("OpenGL Version: %s\n", glGetString(GL_VERSION));
|
|
printf("Window size limits set to: %dx%d minimum\n", min_window_width, min_window_height);
|
|
printf("Initial window size: %dx%d\n", window_width, window_height);
|
|
}
|
|
|
|
// Initialize Nuklear with GLFW+OpenGL3 backend
|
|
void init_nuklear(void) {
|
|
// Initialize Nuklear context with GLFW+OpenGL3 backend
|
|
ctx = nk_glfw3_init(&glfw, window, NK_GLFW3_INSTALL_CALLBACKS);
|
|
if (!ctx) {
|
|
fprintf(stderr, "Failed to initialize Nuklear context\n");
|
|
cleanup();
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// Load default font
|
|
struct nk_font_atlas *atlas;
|
|
nk_glfw3_font_stash_begin(&glfw, &atlas);
|
|
nk_glfw3_font_stash_end(&glfw);
|
|
|
|
printf("Nuklear initialized successfully\n");
|
|
|
|
// Test minimum window size constraint
|
|
printf("Testing minimum window size constraint...\n");
|
|
glfwSetWindowSize(window, 200, 150); // Try to set below minimum
|
|
glfwPollEvents(); // Process the resize event
|
|
|
|
int test_width, test_height;
|
|
glfwGetWindowSize(window, &test_width, &test_height);
|
|
printf("Attempted to set window to 200x150, actual size: %dx%d\n", test_width, test_height);
|
|
|
|
// Reset to normal size
|
|
glfwSetWindowSize(window, window_width, window_height);
|
|
}
|
|
|
|
// =============================================================================
|
|
// UI COMPONENT REGISTRY
|
|
// =============================================================================
|
|
|
|
// Define all available UI components
|
|
static ui_component_t ui_components[] = {
|
|
{"Window Info", render_window_info_component, 1},
|
|
{"Buttons", render_button_component, 1},
|
|
{"Text Input", render_text_input_component, 1},
|
|
{"Slider", render_slider_component, 1},
|
|
{"Checkbox", render_checkbox_component, 1},
|
|
{"Radio Buttons", render_radio_button_component, 1},
|
|
{"Status", render_status_component, 1}
|
|
};
|
|
|
|
#define UI_COMPONENT_COUNT (sizeof(ui_components) / sizeof(ui_components[0]))
|
|
|
|
// =============================================================================
|
|
// MAIN UI RENDERING FUNCTION
|
|
// =============================================================================
|
|
|
|
// Render main UI using modular component system
|
|
void render_ui(struct nk_context* ctx) {
|
|
// Get responsive window rectangle
|
|
struct nk_rect window_rect = get_scaled_window_rect();
|
|
|
|
// Main demo window with title bar showing application name and current window size
|
|
char window_title[256];
|
|
snprintf(window_title, sizeof(window_title),
|
|
"Nuklear GLFW3 Demo - Modular UI Components [%dx%d]",
|
|
window_width, window_height);
|
|
|
|
if (nk_begin(ctx, window_title, window_rect,
|
|
NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|NK_WINDOW_SCALABLE|
|
|
NK_WINDOW_MINIMIZABLE|NK_WINDOW_TITLE)) {
|
|
|
|
// Render all enabled UI components
|
|
for (int i = 0; i < UI_COMPONENT_COUNT; i++) {
|
|
if (ui_components[i].enabled && ui_components[i].render) {
|
|
ui_components[i].render(ctx, &ui_state);
|
|
|
|
// Add separator between components (except for the last one)
|
|
if (i < UI_COMPONENT_COUNT - 1) {
|
|
nk_layout_row_dynamic(ctx, 10, 1);
|
|
nk_spacing(ctx, 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
nk_end(ctx);
|
|
}
|
|
|
|
// =============================================================================
|
|
// UI COMPONENT IMPLEMENTATIONS
|
|
// =============================================================================
|
|
|
|
// Window information component
|
|
void render_window_info_component(struct nk_context* ctx, ui_state_t* state) {
|
|
float scale = get_ui_scale_factor();
|
|
float label_height = 25.0f * scale;
|
|
|
|
nk_layout_row_dynamic(ctx, label_height, 1);
|
|
nk_label(ctx, "Window Information:", NK_TEXT_LEFT);
|
|
|
|
nk_layout_row_dynamic(ctx, label_height, 1);
|
|
nk_labelf(ctx, NK_TEXT_LEFT, "Window Size: %dx%d", window_width, window_height);
|
|
nk_labelf(ctx, NK_TEXT_LEFT, "UI Scale Factor: %.2f", scale);
|
|
}
|
|
|
|
// Button component with click tracking
|
|
void render_button_component(struct nk_context* ctx, ui_state_t* state) {
|
|
float scale = get_ui_scale_factor();
|
|
float button_height = 30.0f * scale;
|
|
float label_height = 25.0f * scale;
|
|
|
|
nk_layout_row_dynamic(ctx, label_height, 1);
|
|
nk_label(ctx, "Button Examples:", NK_TEXT_LEFT);
|
|
|
|
nk_layout_row_dynamic(ctx, button_height, 2);
|
|
if (nk_button_label(ctx, "Click Me")) {
|
|
state->button_click_count++;
|
|
snprintf(state->status_message, sizeof(state->status_message),
|
|
"Standard button clicked! Count: %d (Scale: %.2f)",
|
|
state->button_click_count, scale);
|
|
printf("%s\n", state->status_message);
|
|
}
|
|
|
|
if (nk_button_label(ctx, "Action Button")) {
|
|
state->button_click_count++;
|
|
snprintf(state->status_message, sizeof(state->status_message),
|
|
"Action button pressed! Count: %d (Scale: %.2f)",
|
|
state->button_click_count, scale);
|
|
printf("%s\n", state->status_message);
|
|
}
|
|
|
|
nk_layout_row_dynamic(ctx, button_height, 1);
|
|
if (nk_button_label(ctx, "Wide Button")) {
|
|
state->button_click_count++;
|
|
snprintf(state->status_message, sizeof(state->status_message),
|
|
"Wide button activated! Count: %d (Scale: %.2f)",
|
|
state->button_click_count, scale);
|
|
printf("%s\n", state->status_message);
|
|
}
|
|
|
|
nk_layout_row_dynamic(ctx, label_height, 1);
|
|
nk_labelf(ctx, NK_TEXT_LEFT, "Total button clicks: %d", state->button_click_count);
|
|
}
|
|
|
|
// Text input component
|
|
void render_text_input_component(struct nk_context* ctx, ui_state_t* state) {
|
|
float scale = get_ui_scale_factor();
|
|
float label_height = 25.0f * scale;
|
|
float input_height = 30.0f * scale;
|
|
|
|
nk_layout_row_dynamic(ctx, label_height, 1);
|
|
nk_label(ctx, "Text Input Example:", NK_TEXT_LEFT);
|
|
|
|
nk_layout_row_dynamic(ctx, label_height, 1);
|
|
nk_label(ctx, "Enter text below:", NK_TEXT_LEFT);
|
|
|
|
nk_layout_row_dynamic(ctx, input_height, 1);
|
|
nk_edit_string_zero_terminated(ctx, NK_EDIT_FIELD, state->text_buffer,
|
|
sizeof(state->text_buffer), nk_filter_default);
|
|
|
|
nk_layout_row_dynamic(ctx, label_height, 1);
|
|
nk_labelf(ctx, NK_TEXT_LEFT, "You typed: %s", state->text_buffer);
|
|
}
|
|
|
|
// Slider component
|
|
void render_slider_component(struct nk_context* ctx, ui_state_t* state) {
|
|
float scale = get_ui_scale_factor();
|
|
float label_height = 25.0f * scale;
|
|
float widget_height = 30.0f * scale;
|
|
|
|
nk_layout_row_dynamic(ctx, label_height, 1);
|
|
nk_label(ctx, "Slider Control:", NK_TEXT_LEFT);
|
|
|
|
nk_layout_row_dynamic(ctx, widget_height, 1);
|
|
float old_value = state->slider_value;
|
|
state->slider_value = nk_slide_float(ctx, 0.0f, state->slider_value, 1.0f, 0.01f);
|
|
|
|
// Update status when slider value changes
|
|
if (old_value != state->slider_value) {
|
|
snprintf(state->status_message, sizeof(state->status_message),
|
|
"Slider value changed to: %.2f", state->slider_value);
|
|
}
|
|
|
|
nk_layout_row_dynamic(ctx, label_height, 1);
|
|
nk_labelf(ctx, NK_TEXT_LEFT, "Slider value: %.2f", state->slider_value);
|
|
}
|
|
|
|
// Checkbox component
|
|
void render_checkbox_component(struct nk_context* ctx, ui_state_t* state) {
|
|
float scale = get_ui_scale_factor();
|
|
float label_height = 25.0f * scale;
|
|
float widget_height = 30.0f * scale;
|
|
|
|
nk_layout_row_dynamic(ctx, widget_height, 1);
|
|
int old_value = state->checkbox_value;
|
|
state->checkbox_value = nk_check_label(ctx, "Enable feature", state->checkbox_value);
|
|
|
|
// Update status when checkbox value changes
|
|
if (old_value != state->checkbox_value) {
|
|
snprintf(state->status_message, sizeof(state->status_message),
|
|
"Feature %s", state->checkbox_value ? "enabled" : "disabled");
|
|
}
|
|
|
|
nk_layout_row_dynamic(ctx, label_height, 1);
|
|
nk_labelf(ctx, NK_TEXT_LEFT, "Checkbox is: %s",
|
|
state->checkbox_value ? "Checked" : "Unchecked");
|
|
}
|
|
|
|
// Radio button component
|
|
void render_radio_button_component(struct nk_context* ctx, ui_state_t* state) {
|
|
float scale = get_ui_scale_factor();
|
|
float label_height = 25.0f * scale;
|
|
float widget_height = 30.0f * scale;
|
|
|
|
nk_layout_row_dynamic(ctx, label_height, 1);
|
|
nk_label(ctx, "Radio Button Options:", NK_TEXT_LEFT);
|
|
|
|
nk_layout_row_dynamic(ctx, widget_height, 1);
|
|
if (nk_option_label(ctx, "Option A", state->option_selected == 0)) {
|
|
if (state->option_selected != 0) {
|
|
state->option_selected = 0;
|
|
snprintf(state->status_message, sizeof(state->status_message),
|
|
"Selected Option A (Scale: %.2f)", scale);
|
|
printf("%s\n", state->status_message);
|
|
}
|
|
}
|
|
|
|
nk_layout_row_dynamic(ctx, widget_height, 1);
|
|
if (nk_option_label(ctx, "Option B", state->option_selected == 1)) {
|
|
if (state->option_selected != 1) {
|
|
state->option_selected = 1;
|
|
snprintf(state->status_message, sizeof(state->status_message),
|
|
"Selected Option B (Scale: %.2f)", scale);
|
|
printf("%s\n", state->status_message);
|
|
}
|
|
}
|
|
|
|
nk_layout_row_dynamic(ctx, widget_height, 1);
|
|
if (nk_option_label(ctx, "Option C", state->option_selected == 2)) {
|
|
if (state->option_selected != 2) {
|
|
state->option_selected = 2;
|
|
snprintf(state->status_message, sizeof(state->status_message),
|
|
"Selected Option C (Scale: %.2f)", scale);
|
|
printf("%s\n", state->status_message);
|
|
}
|
|
}
|
|
|
|
nk_layout_row_dynamic(ctx, label_height, 1);
|
|
nk_labelf(ctx, NK_TEXT_LEFT, "Selected: Option %c", 'A' + state->option_selected);
|
|
}
|
|
|
|
// Status component to show current application status
|
|
void render_status_component(struct nk_context* ctx, ui_state_t* state) {
|
|
float scale = get_ui_scale_factor();
|
|
float label_height = 25.0f * scale;
|
|
float button_height = 30.0f * scale;
|
|
|
|
nk_layout_row_dynamic(ctx, label_height, 1);
|
|
nk_label(ctx, "Status & Controls:", NK_TEXT_LEFT);
|
|
|
|
nk_layout_row_dynamic(ctx, label_height, 1);
|
|
nk_labelf(ctx, NK_TEXT_LEFT, "%s", state->status_message);
|
|
|
|
// Add a reset button to demonstrate easy component extension
|
|
nk_layout_row_dynamic(ctx, button_height, 2);
|
|
if (nk_button_label(ctx, "Reset UI State")) {
|
|
ui_state_reset();
|
|
}
|
|
|
|
// Add a component toggle demonstration
|
|
static int show_demo_toggle = 0;
|
|
if (nk_button_label(ctx, show_demo_toggle ? "Hide Demo" : "Show Demo")) {
|
|
show_demo_toggle = !show_demo_toggle;
|
|
ui_component_set_enabled("Window Info", show_demo_toggle);
|
|
snprintf(state->status_message, sizeof(state->status_message),
|
|
"Window Info component %s", show_demo_toggle ? "enabled" : "disabled");
|
|
}
|
|
}
|
|
|
|
// =============================================================================
|
|
// UI STATE MANAGEMENT FUNCTIONS
|
|
// =============================================================================
|
|
|
|
// Update status message
|
|
void ui_state_update_status(const char* message) {
|
|
if (message) {
|
|
snprintf(ui_state.status_message, sizeof(ui_state.status_message), "%s", message);
|
|
}
|
|
}
|
|
|
|
// Reset UI state to defaults
|
|
void ui_state_reset(void) {
|
|
strcpy(ui_state.text_buffer, "Hello World!");
|
|
ui_state.slider_value = 0.5f;
|
|
ui_state.checkbox_value = 1;
|
|
ui_state.option_selected = 0;
|
|
ui_state.button_click_count = 0;
|
|
strcpy(ui_state.status_message, "UI state reset to defaults");
|
|
printf("UI state has been reset to defaults\n");
|
|
}
|
|
|
|
// =============================================================================
|
|
// UTILITY FUNCTIONS FOR ADDING NEW COMPONENTS
|
|
// =============================================================================
|
|
|
|
// Function to enable/disable specific UI components
|
|
void ui_component_set_enabled(const char* component_name, int enabled) {
|
|
for (int i = 0; i < UI_COMPONENT_COUNT; i++) {
|
|
if (strcmp(ui_components[i].name, component_name) == 0) {
|
|
ui_components[i].enabled = enabled;
|
|
printf("Component '%s' %s\n", component_name, enabled ? "enabled" : "disabled");
|
|
return;
|
|
}
|
|
}
|
|
printf("Component '%s' not found\n", component_name);
|
|
}
|
|
|
|
// Function to get component status
|
|
int ui_component_is_enabled(const char* component_name) {
|
|
for (int i = 0; i < UI_COMPONENT_COUNT; i++) {
|
|
if (strcmp(ui_components[i].name, component_name) == 0) {
|
|
return ui_components[i].enabled;
|
|
}
|
|
}
|
|
return 0; // Component not found
|
|
}
|
|
|
|
// Cleanup resources
|
|
void cleanup(void) {
|
|
// Cleanup Nuklear resources
|
|
if (ctx) {
|
|
nk_glfw3_shutdown(&glfw);
|
|
ctx = NULL;
|
|
}
|
|
|
|
// Cleanup GLFW resources
|
|
if (window) {
|
|
glfwDestroyWindow(window);
|
|
}
|
|
glfwTerminate();
|
|
}
|
|
|
|
int main(int argc, char** argv) {
|
|
printf("Nuklear GLFW3 Demo Application - Modular UI Components\n");
|
|
printf("Initializing modular UI component system...\n");
|
|
printf("Available components: %zu\n", UI_COMPONENT_COUNT);
|
|
|
|
// List all available components
|
|
for (int i = 0; i < UI_COMPONENT_COUNT; i++) {
|
|
printf(" - %s: %s\n", ui_components[i].name,
|
|
ui_components[i].enabled ? "enabled" : "disabled");
|
|
}
|
|
|
|
// Initialize GLFW and create window
|
|
init_glfw();
|
|
|
|
// Initialize Nuklear
|
|
init_nuklear();
|
|
|
|
// Set initial status message
|
|
ui_state_update_status("Modular UI system initialized successfully");
|
|
|
|
// Main render loop
|
|
while (!glfwWindowShouldClose(window)) {
|
|
// Poll events and handle input
|
|
glfwPollEvents();
|
|
nk_glfw3_new_frame(&glfw);
|
|
|
|
// Render UI using modular component system
|
|
render_ui(ctx);
|
|
|
|
// Clear the screen
|
|
glClearColor(0.10f, 0.18f, 0.24f, 1.0f);
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
// Render Nuklear
|
|
nk_glfw3_render(&glfw, NK_ANTI_ALIASING_ON, MAX_VERTEX_BUFFER, MAX_ELEMENT_BUFFER);
|
|
|
|
// Swap front and back buffers
|
|
glfwSwapBuffers(window);
|
|
}
|
|
|
|
// Cleanup
|
|
cleanup();
|
|
|
|
printf("Modular UI system shut down successfully\n");
|
|
printf("Application closed successfully\n");
|
|
return 0;
|
|
} |