Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions demo/common/nuklear_console_demo.c
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,11 @@ void nk_console_demo_show_message(struct nk_console* button, void* user_data) {
nk_console_show_message(button, message);
}

void nk_console_demo_show_marquee_message(struct nk_console* button, void* user_data) {
NK_UNUSED(user_data);
nk_console_show_message(button, "This is a very long marquee message that scrolls across the screen because it is too wide to fit!");
}

void nk_console_quit_button_focused(struct nk_console* widget, void* user_data) {
NK_UNUSED(user_data);
nk_console_show_message(widget, "Are you sure you want to quit?");
Expand Down Expand Up @@ -428,6 +433,11 @@ struct nk_console* nuklear_console_demo_init(struct nk_context* ctx, void* user_

// Messages
nk_console_button_onclick(widgets, "Show Message", &nk_console_demo_show_message);
nk_console_button_onclick(widgets, "Show Marquee Message", &nk_console_demo_show_marquee_message);

// Long tooltip
nk_console_button_onclick(widgets, "Long Tooltip Button", NULL)
->tooltip = "This is a very long tooltip that will marquee scroll across the bottom of the screen because it is too wide to fit!";

// Back Button
nk_console_button_set_symbol(
Expand Down
17 changes: 17 additions & 0 deletions demo/raylib/.cmake/Findnuklear_gamepad.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
if (NOT NUKLEAR_GAMEPAD_VERSION)
set(NUKLEAR_GAMEPAD_VERSION 1.1.0)
endif()

include(FetchContent)
FetchContent_Declare(
nuklear_gamepad
DOWNLOAD_EXTRACT_TIMESTAMP OFF
URL https://github.com/RobLoach/nuklear_gamepad/archive/refs/tags/v${NUKLEAR_GAMEPAD_VERSION}.tar.gz
)
FetchContent_GetProperties(nuklear_gamepad)

if (NOT nuklear_gamepad_POPULATED)
set(FETCHCONTENT_QUIET NO)
FetchContent_Populate(nuklear_gamepad)
add_subdirectory(${nuklear_gamepad_SOURCE_DIR} ${nuklear_gamepad_BINARY_DIR})
endif()
20 changes: 20 additions & 0 deletions demo/raylib/.cmake/Findraylib.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# RAYLIB_VERSION
if (NOT RAYLIB_VERSION)
set(RAYLIB_VERSION 6.0)
endif()

include(FetchContent)
FetchContent_Declare(
raylib
DOWNLOAD_EXTRACT_TIMESTAMP OFF
URL https://github.com/raysan5/raylib/archive/refs/tags/${RAYLIB_VERSION}.tar.gz
)
FetchContent_GetProperties(raylib)

if (NOT raylib_POPULATED)
set(FETCHCONTENT_QUIET NO)
FetchContent_Populate(raylib)
set(BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
set(BUILD_GAMES OFF CACHE BOOL "" FORCE)
add_subdirectory(${raylib_SOURCE_DIR} ${raylib_BINARY_DIR})
endif()
17 changes: 17 additions & 0 deletions demo/raylib/.cmake/Findraylib_nuklear.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
if (NOT RAYLIB_NUKLEAR_VERSION)
set(RAYLIB_NUKLEAR_VERSION 6.0.1)
endif()

include(FetchContent)
FetchContent_Declare(
raylib_nuklear
DOWNLOAD_EXTRACT_TIMESTAMP OFF
URL https://github.com/RobLoach/raylib-nuklear/archive/refs/tags/v${RAYLIB_NUKLEAR_VERSION}.tar.gz
)
FetchContent_GetProperties(raylib_nuklear)

if (NOT raylib_nuklear_POPULATED)
set(FETCHCONTENT_QUIET NO)
FetchContent_Populate(raylib_nuklear)
add_subdirectory(${raylib_nuklear_SOURCE_DIR} ${raylib_nuklear_BINARY_DIR})
endif()
52 changes: 6 additions & 46 deletions demo/raylib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,57 +5,17 @@ project(nuklear_console_demo_raylib)
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)

# Register the cmake folder for find_package()
LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/.cmake")

# raylib
find_package(raylib QUIET)
if (NOT raylib_FOUND)
include(FetchContent)
FetchContent_Declare(
raylib
GIT_REPOSITORY https://github.com/raysan5/raylib.git
GIT_TAG 6.0
)
FetchContent_GetProperties(raylib)
if (NOT raylib_POPULATED) # Have we downloaded raylib yet?
set(FETCHCONTENT_QUIET NO)
FetchContent_Populate(raylib)
set(BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
add_subdirectory(${raylib_SOURCE_DIR} ${raylib_BINARY_DIR})
endif()
endif()
find_package(raylib REQUIRED)

# raylib_nuklear
find_package(raylib_nuklear QUIET)
if (NOT raylib_nuklear_FOUND)
include(FetchContent)
FetchContent_Declare(
raylib_nuklear
GIT_REPOSITORY https://github.com/RobLoach/raylib-nuklear.git
GIT_TAG master
)
FetchContent_GetProperties(raylib_nuklear)
if (NOT raylib_nuklear_POPULATED) # Have we downloaded raylib yet?
set(FETCHCONTENT_QUIET NO)
FetchContent_Populate(raylib_nuklear)
add_subdirectory(${raylib_nuklear_SOURCE_DIR} ${raylib_nuklear_BINARY_DIR})
endif()
endif()
find_package(raylib_nuklear REQUIRED)

# nuklear_gamepad
find_package(nuklear_gamepad QUIET)
if (NOT nuklear_gamepad_FOUND)
include(FetchContent)
FetchContent_Declare(
nuklear_gamepad
GIT_REPOSITORY https://github.com/RobLoach/nuklear_gamepad.git
GIT_TAG master
)
FetchContent_GetProperties(nuklear_gamepad)
if (NOT nuklear_gamepad_POPULATED) # Have we downloaded raylib yet?
set(FETCHCONTENT_QUIET NO)
FetchContent_Populate(nuklear_gamepad)
add_subdirectory(${nuklear_gamepad_SOURCE_DIR} ${nuklear_gamepad_BINARY_DIR})
endif()
endif()
find_package(nuklear_gamepad REQUIRED)

# Resources
if (EXISTS ${PROJECT_SOURCE_DIR}/resources)
Expand Down
122 changes: 103 additions & 19 deletions nuklear_console.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ typedef enum {
typedef struct nk_console_message {
char text[256];
float duration;
float scroll_x;
} nk_console_message;

typedef struct nk_console {
Expand Down Expand Up @@ -169,6 +170,9 @@ typedef struct nk_console_top_data {
nk_uint drag_scroll_start_y; /** Window scroll Y at drag start. */
nk_uint drag_scroll_max_x; /** Maximum scroll X, updated each frame after render. */
nk_uint drag_scroll_max_y; /** Maximum scroll Y, updated each frame after render. */

float tooltip_scroll_x; /** Horizontal marquee offset for the active tooltip. */
const char* tooltip_last; /** Pointer to the last tooltip shown; used to detect tooltip changes and reset scroll. */
} nk_console_top_data;

#if defined(__cplusplus)
Expand Down Expand Up @@ -401,6 +405,78 @@ NK_API nk_bool nk_console_navigate_to_path(nk_console* console, const char* path
#endif
#include CVECTOR_H

#ifndef NK_CONSOLE_MARQUEE_SCROLL_SPEED
#define NK_CONSOLE_MARQUEE_SCROLL_SPEED 60.0f
#endif
#ifndef NK_CONSOLE_MARQUEE_SCROLL_PAUSE
#define NK_CONSOLE_MARQUEE_SCROLL_PAUSE 1.5f
#endif

/**
* Advance a marquee scroll offset and return a pointer to the visible slice of text.
* Returns `text` unchanged when the text fits or delta time is unavailable.
* The caller owns `buf` (minimum `buf_size` bytes).
*/
static const char* nk_console_marquee_slice(
struct nk_context* ctx,
const char* text, int text_len,
float full_text_width, float avail_width,
float speed, float pause,
float* scroll_x,
char* buf, int buf_size)
{
if (full_text_width <= avail_width || ctx->delta_time_seconds <= 0) {
return text;
}
float pause_pixels = pause * speed;
float total_cycle = full_text_width + pause_pixels;
*scroll_x += ctx->delta_time_seconds * speed;
if (*scroll_x > total_cycle) {
*scroll_x -= total_cycle;
}
float offset = *scroll_x - pause_pixels;
if (offset <= 0.0f) {
return text;
}
int start = 0;
for (int i = 1; i <= text_len; i++) {
float w = ctx->style.font->width(ctx->style.font->userdata, ctx->style.font->height, text, i);
if (w >= offset) { start = i - 1; break; }
if (i == text_len) { start = text_len; }
}
int copy_len = text_len - start;
if (copy_len >= buf_size) {
copy_len = buf_size - 1;
}
NK_MEMCPY(buf, text + start, (nk_size)copy_len);
buf[copy_len] = '\0';
return buf;
}

/**
* Render a single-line marquee tooltip of `tooltip_width` at the current mock mouse position.
*/
static void nk_console_tooltip_render_marquee(
struct nk_context* ctx,
const char* text, int text_len,
float full_text_width,
float tooltip_width, float text_height,
float speed, float pause,
float* scroll_x)
{
float avail_width = tooltip_width - ctx->style.window.padding.x * 2.0f;
char display_buf[256];
const char* display_text = nk_console_marquee_slice(ctx, text, text_len,
full_text_width, avail_width, speed, pause, scroll_x, display_buf, (int)sizeof(display_buf));
struct nk_vec2 zero;
nk_zero_struct(zero);
if (nk_tooltip_begin_offset(ctx, tooltip_width, NK_TOP_LEFT, zero)) {
nk_layout_row_dynamic(ctx, text_height, 1);
nk_label(ctx, display_text, NK_TEXT_LEFT);
nk_tooltip_end(ctx);
}
}

#ifdef __cplusplus
extern "C" {
#endif
Expand Down Expand Up @@ -780,37 +856,45 @@ NK_API nk_bool nk_console_selectable(nk_console* widget) {
return widget->selectable && widget->visible && !widget->disabled;
}

#ifndef NK_CONSOLE_TOOLTIP_SCROLL_SPEED
#define NK_CONSOLE_TOOLTIP_SCROLL_SPEED NK_CONSOLE_MARQUEE_SCROLL_SPEED
#endif
#ifndef NK_CONSOLE_TOOLTIP_SCROLL_PAUSE
#define NK_CONSOLE_TOOLTIP_SCROLL_PAUSE NK_CONSOLE_MARQUEE_SCROLL_PAUSE
#endif

/**
* Display a tooltip with the given text.
*
* @see nk_tooltip()
* @todo Support multiline tooltips with nk_text_calculate_text_bounds()
*/
static void nk_console_tooltip_display(struct nk_context* ctx, const char* text) {
const struct nk_style* style;
struct nk_vec2 padding;
struct nk_vec2 zero;
nk_zero_struct(zero);

style = &ctx->style;
padding = style->window.padding;

float text_height = (style->font->height + padding.y);
static void nk_console_tooltip_display(nk_console* console, const char* text) {
struct nk_context* ctx = console->ctx;
const struct nk_style* style = &ctx->style;
float text_height = style->font->height + style->window.padding.y;
float x = ctx->input.mouse.pos.x;
float y = ctx->input.mouse.pos.y;

// Display the tooltip at the bottom of the window, manipulating the mouse position
struct nk_rect windowbounds = nk_window_get_bounds(ctx);
ctx->input.mouse.pos.x = windowbounds.x;
ctx->input.mouse.pos.y = windowbounds.y + windowbounds.h - text_height - padding.y * 2.0f - ctx->style.window.border;
ctx->input.mouse.pos.y = windowbounds.y + windowbounds.h - text_height - style->window.padding.y * 2.0f - style->window.border;

if (nk_tooltip_begin_offset(ctx, windowbounds.w - ctx->style.window.border, NK_TOP_LEFT, zero)) {
nk_layout_row_dynamic(ctx, text_height, 1);
nk_text_wrap(ctx, text, nk_strlen(text));
nk_tooltip_end(ctx);
nk_console_top_data* data = (nk_console_top_data*)nk_console_get_top(console)->data;

// Reset scroll on tooltip change. Relies on pointer identity — works for string
// literals and persistent pointers; resets every frame for stack-allocated strings.
if (data->tooltip_last != text) {
data->tooltip_last = text;
data->tooltip_scroll_x = 0.0f;
}

// Restore the mouse x/y positions.
int text_len = nk_strlen(text);
float full_text_width = style->font->width(style->font->userdata, style->font->height, text, text_len);
nk_console_tooltip_render_marquee(ctx, text, text_len, full_text_width,
windowbounds.w - style->window.border, text_height,
NK_CONSOLE_TOOLTIP_SCROLL_SPEED, NK_CONSOLE_TOOLTIP_SCROLL_PAUSE,
&data->tooltip_scroll_x);

ctx->input.mouse.pos.x = x;
ctx->input.mouse.pos.y = y;
}
Expand All @@ -824,7 +908,7 @@ NK_API void nk_console_check_tooltip(nk_console* console) {
}

if (console->tooltip != NULL) {
nk_console_tooltip_display(console->ctx, console->tooltip);
nk_console_tooltip_display(console, console->tooltip);
}
}

Expand Down
2 changes: 1 addition & 1 deletion nuklear_console_file.h
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ NK_API struct nk_rect nk_console_file_render(nk_console* console) {

nk_console_layout_widget(console);

// Display the label (skipped in file_action mode it becomes the button text instead).
// Display the label (skipped in file_action mode - it becomes the button text instead).
if (!data->file_action && console->label != NULL && console->label[0] != '\0') {
if (!nk_console_is_active_widget(console)) {
nk_widget_disable_begin(console->ctx);
Expand Down
4 changes: 2 additions & 2 deletions nuklear_console_key.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ NK_API const char* nk_console_key_name(nk_rune key) {
}
}

/* Unicode character encode into a static buffer and return it. */
/* Unicode character - encode into a static buffer and return it. */
if (key == 32) return "Space";
static char nk_console_key_char_buf[NK_UTF_SIZE + 1];
int len = nk_utf_encode(key, nk_console_key_char_buf, NK_UTF_SIZE);
Expand Down Expand Up @@ -241,7 +241,7 @@ static struct nk_rect nk_console_key_active_render(nk_console* console) {
nk_console_top_data* top_data = (nk_console_top_data*)top->data;
if (top_data->input_processed == nk_false) {
/* Typed character input takes priority over special keys. Control
* characters (< 32) are excluded they overlap with nk_keys values
* characters (< 32) are excluded - they overlap with nk_keys values
* and are handled by the special-key loop below. */
if (console->ctx->input.keyboard.text_len > 0) {
nk_rune ch = 0;
Expand Down
2 changes: 1 addition & 1 deletion nuklear_console_list_view.h
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ NK_API struct nk_rect nk_console_list_view_render(nk_console* widget) {

// Handle keyboard/gamepad navigation.
if (is_active && !top_data->input_processed) {
// Hold-to-accelerate timers mirrors nk_console_check_up_down.
// Hold-to-accelerate timers - mirrors nk_console_check_up_down.
nk_bool up_held = nk_console_button_down(top, NK_GAMEPAD_BUTTON_UP);
nk_bool down_held = nk_console_button_down(top, NK_GAMEPAD_BUTTON_DOWN);
nk_bool repeat_fire = nk_false;
Expand Down
21 changes: 14 additions & 7 deletions nuklear_console_message.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,13 @@ NK_API void nk_console_show_message(nk_console* console, const char* text) {
cvector_push_back(data->messages, message);
}

#ifndef NK_CONSOLE_MESSAGE_SCROLL_SPEED
#define NK_CONSOLE_MESSAGE_SCROLL_SPEED NK_CONSOLE_MARQUEE_SCROLL_SPEED
#endif
#ifndef NK_CONSOLE_MESSAGE_SCROLL_PAUSE
#define NK_CONSOLE_MESSAGE_SCROLL_PAUSE NK_CONSOLE_MARQUEE_SCROLL_PAUSE
#endif

NK_API void nk_console_message_render(nk_console* console, nk_console_message* message) {
if (message == NULL || console->data == NULL) {
return;
Expand Down Expand Up @@ -107,13 +114,13 @@ NK_API void nk_console_message_render(nk_console* console, nk_console_message* m
}
}

// Display the tooltip where the mocked mouse is.
struct nk_vec2 zero = {0, 0};
if (nk_tooltip_begin_offset(ctx, bounds.w - ctx->style.window.border, NK_TOP_LEFT, zero)) {
nk_layout_row_dynamic(ctx, text_height, 1);
nk_label(ctx, message->text, NK_TEXT_LEFT);
nk_tooltip_end(ctx);
}
float tooltip_width = bounds.w - ctx->style.window.border;
int text_len = nk_strlen(message->text);
float full_text_width = ctx->style.font->width(ctx->style.font->userdata, ctx->style.font->height, message->text, text_len);
nk_console_tooltip_render_marquee(ctx, message->text, text_len, full_text_width,
tooltip_width, text_height,
NK_CONSOLE_MESSAGE_SCROLL_SPEED, NK_CONSOLE_MESSAGE_SCROLL_PAUSE,
&message->scroll_x);

// Restore the mouse x/y positions.
ctx->input.mouse.pos = mouse_pos;
Expand Down
Loading