Skip to content
Open
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
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ INTRO = HEADER.md
PUB = nuklear.h
OUTPUT = nuklear.h

PRIV1 = nuklear_internal.h nuklear_math.c nuklear_util.c nuklear_color.c nuklear_utf8.c nuklear_buffer.c nuklear_string.c nuklear_draw.c nuklear_vertex.c
PRIV1 = nuklear_internal.h nuklear_math.c nuklear_util.c nuklear_crc.c nuklear_color.c nuklear_utf8.c nuklear_buffer.c nuklear_string.c nuklear_draw.c nuklear_vertex.c

EXTERN = stb_rect_pack.h stb_truetype.h

Expand Down
1 change: 1 addition & 0 deletions demo/sdl_opengl2/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#define NK_INCLUDE_FONT_BAKING
#define NK_INCLUDE_DEFAULT_FONT
#define NK_IMPLEMENTATION
#define NK_DRAW_CRC
#define NK_SDL_GL2_IMPLEMENTATION
#include "../../nuklear.h"
#include "nuklear_sdl_gl2.h"
Expand Down
201 changes: 201 additions & 0 deletions nuklear.h

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
/// - [y]: Minor version with non-breaking API and library changes
/// - [z]: Patch version with no direct changes to the API
///
/// - 2025/06/05 (4.12.8) - Adds CRC check on draw command buffer
/// - 2025/04/06 (4.12.7) - Fix text input navigation and mouse scrolling
/// - 2025/03/29 (4.12.6) - Fix unitialized data in nk_input_char
/// - 2025/03/05 (4.12.5) - Fix scrolling knob also scrolling parent window, remove dead code
Expand Down
6 changes: 6 additions & 0 deletions src/nuklear.h
Original file line number Diff line number Diff line change
Expand Up @@ -4635,6 +4635,9 @@ struct nk_command_buffer {
int use_clipping;
nk_handle userdata;
nk_size begin, end, last;
#ifdef NK_DRAW_CRC
nk_hash crc;
#endif
};

/** shape outlines */
Expand Down Expand Up @@ -5740,6 +5743,9 @@ struct nk_context {
struct nk_page_element *freelist;
unsigned int count;
unsigned int seq;
#ifdef NK_DRAW_CRC
nk_hash crc;
#endif
};

/* ==============================================================
Expand Down
14 changes: 14 additions & 0 deletions src/nuklear_context.c
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,9 @@ nk_start_buffer(struct nk_context *ctx, struct nk_command_buffer *buffer)
buffer->end = buffer->begin;
buffer->last = buffer->begin;
buffer->clip = nk_null_rect;
#ifdef NK_DRAW_CRC
buffer->crc = NK_CRC_SEED;
#endif
}
NK_LIB void
nk_start(struct nk_context *ctx, struct nk_window *win)
Expand Down Expand Up @@ -225,6 +228,9 @@ nk_finish_buffer(struct nk_context *ctx, struct nk_command_buffer *buffer)
NK_ASSERT(buffer);
if (!ctx || !buffer) return;
buffer->end = ctx->memory.allocated;
#ifdef NK_DRAW_CRC
ctx->crc = buffer.crc;
#endif
}
NK_LIB void
nk_finish(struct nk_context *ctx, struct nk_window *win)
Expand All @@ -236,8 +242,16 @@ nk_finish(struct nk_context *ctx, struct nk_window *win)
NK_ASSERT(ctx);
NK_ASSERT(win);
if (!ctx || !win) return;


nk_finish_buffer(ctx, &win->buffer);

if (!win->popup.buf.active) return;
#ifdef NK_DRAW_CRC
/*combine the normal buffer with the popup buffer*/
/*i don't know why, but for all other buffers, we use "finish"*/
ctx->crc = NK_CRC_FUNC(ctx->crc, &win->popup.buf.crc, sizeof(win->popup.buf.crc));
#endif

buf = &win->popup.buf;
memory = ctx->memory.memory.ptr;
Expand Down
124 changes: 124 additions & 0 deletions src/nuklear_crc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/**
* \file nuklear_crc.c
* \brief Implements CRC checking of the draw command buffer.
*
* \details Implements a rolling CRC that gets updated with every draw_push
* function call. That is, for every `nk_xxx` api function call between
* `nk_begin()` and `nk_end()` there is one to many draw commands pushed into
* the draw buffer. For each one of those pushes, the command type and data is
* hashed, then stored into that buffer's CRC member. Then on the next `nk_xxx`
* function call, another command push is executed and the previous crc is
* updated with the new hash of the new command. This will result in a unique CRC
* value in the buffers crc member at the end of the draw execution.
*
* The purpose of this is to allow super cheap and fast detection of unique'ness
* between draw loops, such that the user can determine if they need to redraw
* the screen or not. The original method was to have the user store a copy of
* the entire draw buffer, then loop through the buffer and compare that with
* the most recent draw buffer. Once done, you can determine if something
* changed on the screen and needs to be `nk_convert`'d. Doing it that way would
* require 1 loop to fill the command buffer, 1 loop to check the command buffer
* against the previously stored buffer, then 1 more loop to copy the new buffer
* into the old for the next draw loop iteration. That is 3 loops of
* `command_buffer'length()` which could end up being quite large; making this
* an expensive operation. Which almost defeats the purpose of detecting
* unique'ness in the first place.
*
* Yet implementing this CRC, we crunch all of that down into just the initial
* draw loop. Then you have a unique code stored in a 32-bit integer that
* represents the buffer. So if you need to know if the current draw command
* buffer is different from the previous one; you simply need to retain a single
* interger value and then check whether the old interger is equal to the new
* one. We reduced 3x O(n) loop complexity and 2x O(n) memory/space complexity
* down to 1x O(n) loop complexity and O(1) memory complexity. The CRC
* calculation (depending on default or custom implementation) simply adds 1
* table lookup and 1 XOR operation to a single integer. Practically a free
* upgrade by comparison.
*
* If you want to use the CRC, then you must either `#define NK_DRAW_CRC`
* or `#define NK_DRAW_CRC_CUSTOM`
*
* `#define NK_DRAW_CRC` will implement the default CRC algorithm which is the
* same murmur hash function used in the nuklear window titles.
*
* `#define NK_DRAW_CRC_CUSTOM` will allow the user to define their own CRC
* implementation. This is available because most embedded systems already have
* a CRC calculation of some kind, usually defined by their system constraints,
* so it would be redundant to create yet another CRC method. Or, perhaps the
* murmur hash is more expensive than what your application requires, so you can
* implement a super simple/cheap version of your own for your needs.
*
* To use the `NK_DRAW_CRC_CUSTOM` the user will need to `#define
* NK_CRC_SEED` to their desired seed and also `#define NK_CRC_FUNC(k,l,s)` to
* the user defined implementation of the crc.
*
* The user defined implementation of the CRC **SHALL** have the declaration of
* ```c
* nk_hash <xxCustomNamexx>(const void * key, int len, nk_hash seed)
* ```
*
* other than that, the user is free to use any CRC algorithm, table,
* implementation they choose.
*
* \internal
* Information for nuklear developers. Each context includes a command buffer
* for drawing, usually called `ctx->current->buffer`. This is the buffer with
* the draw commands and we grab the CRC value stored with this buffer
* `ctx->current->buffer.crc`. The buffer, in regards to a context, only exists
* to draw a panel and will _dissappear_ once `nk_end()` is called. This is
* because nk_end commits the draw buffer to the context's overal draw array and
* closes out the `current` buffer to wait for another `nk_begin()` to start a
* new panel using the `nk_buffer_start()` command.
*
* What this means for the CRC is that, when a user decides they want to check
* on the CRC, they will do so **after** the `nk_end()` call (because you need
* to make sure all of the information is there). But attempting to grab the CRC
* at `ctx->current->buffer.crc` will result in a Seg Fault because that memory
* was deallocated with the `nk_end()` call. But not only that, we determined
* that the `current->buffer` is meant to be used for all sequences of
* `nk_begin()` to `nk_end()`. Meaning that we will get new CRCs for each panel;
* but only be able to look at the final CRC (unless we grab them as we go, but
* thats silly when we just want to know whether to draw or not). That means we
* better hope the user only ever interacts with which ever panel was the final
* one drawn, otherwise we wont see a crc change.
*
* The solution was to store the CRC at the Context level. So now `struct
* nk_context` contains a `crc` member that gets rolled when the current buffer
* finishes (i.e. `nk_end()` was called). This means that, whenever
* `nk_finish` is called, we calc the context CRC based off the current buffer
* crc. This retains the CRC value through successive calls to `nk_begin()` and
* `nk_end()` and rolls the number appropriately.
*
* also, the crc/hash calculation has to be done, not within
* `nk_command_buffer_push` but within each individual command type function
* - `nk_push_scissor()
* - `nk_stroke_line()`
* - `nk_push_image()`
* - ...
* because the `nk_command_buffer_push` actually doesn't _push_ anything. its
* really just and allocator that return the pointer, then allows the calling
* function to fill that memory space with the parameters. So we need the
* individual calling functions to calculate the crc less we lose unique data
* for the specific call (looking at you highlight on hover feature).
* \addtogroup crc
* \brief Command buffer CRC Implementation
* @{
*/

#ifdef NK_DRAW_CRC_CUSTOM
#ifndef NK_CRC_SEED
#error "Must #define NK_CRC_SEED when using NK_DRAW_CRC_CUSTOM"
#endif
#ifndef NK_CRC_FUNC(k,l,s)
#error "Must #define NK_CRC_FUNC(key,length,seed) when using NK_DRAW_CRC_CUSTOM"
#endif
#define NK_DRAW_CRC
#endif

#ifdef NK_DRAW_CRC
#define NK_CRC_SEED 0xffffffff /**< seed value of the crc*/
#define NK_CRC_FUNC(k,l,s) nk_murmur_hash(k,(l), s)
#warn "YO FUCK THIS CRC SHIT"

/** @} *//*end documentation grouping*/
#endif /* NK_DRAW_CRC
55 changes: 55 additions & 0 deletions src/nuklear_draw.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ nk_push_scissor(struct nk_command_buffer *b, struct nk_rect r)
cmd->y = (short)r.y;
cmd->w = (unsigned short)NK_MAX(0, r.w);
cmd->h = (unsigned short)NK_MAX(0, r.h);
#ifdef NK_DRAW_CRC
b->crc = NK_CRC_FUNC((const void*)cmd,sizeof nk_command_scissor*, b->crc);
#endif
}
NK_API void
nk_stroke_line(struct nk_command_buffer *b, float x0, float y0,
Expand All @@ -101,6 +104,9 @@ nk_stroke_line(struct nk_command_buffer *b, float x0, float y0,
cmd->end.x = (short)x1;
cmd->end.y = (short)y1;
cmd->color = c;
#ifdef NK_DRAW_CRC
b->crc = NK_CRC_FUNC((const void*)cmd,sizeof(nk_command_line*), b->crc);
#endif
}
NK_API void
nk_stroke_curve(struct nk_command_buffer *b, float ax, float ay,
Expand All @@ -124,6 +130,9 @@ nk_stroke_curve(struct nk_command_buffer *b, float ax, float ay,
cmd->end.x = (short)bx;
cmd->end.y = (short)by;
cmd->color = col;
#ifdef NK_DRAW_CRC
b->crc = NK_CRC_FUNC((const void*)cmd,sizeof(nk_command_curve*), b->crc);
#endif
}
NK_API void
nk_stroke_rect(struct nk_command_buffer *b, struct nk_rect rect,
Expand All @@ -147,6 +156,9 @@ nk_stroke_rect(struct nk_command_buffer *b, struct nk_rect rect,
cmd->w = (unsigned short)NK_MAX(0, rect.w);
cmd->h = (unsigned short)NK_MAX(0, rect.h);
cmd->color = c;
#ifdef NK_DRAW_CRC
b->crc = NK_CRC_FUNC((const void*)cmd,sizeof(nk_command_rect*), b->crc);
#endif
}
NK_API void
nk_fill_rect(struct nk_command_buffer *b, struct nk_rect rect,
Expand All @@ -170,6 +182,9 @@ nk_fill_rect(struct nk_command_buffer *b, struct nk_rect rect,
cmd->w = (unsigned short)NK_MAX(0, rect.w);
cmd->h = (unsigned short)NK_MAX(0, rect.h);
cmd->color = c;
#ifdef NK_DRAW_CRC
b->crc = NK_CRC_FUNC((const void*)cmd,sizeof(nk_command_rect_filled*), b->crc);
#endif
}
NK_API void
nk_fill_rect_multi_color(struct nk_command_buffer *b, struct nk_rect rect,
Expand All @@ -196,6 +211,9 @@ nk_fill_rect_multi_color(struct nk_command_buffer *b, struct nk_rect rect,
cmd->top = top;
cmd->right = right;
cmd->bottom = bottom;
#ifdef NK_DRAW_CRC
b->crc = NK_CRC_FUNC((const void*)cmd,sizeof(nk_command_rect_multi_color*), b->crc);
#endif
}
NK_API void
nk_stroke_circle(struct nk_command_buffer *b, struct nk_rect r,
Expand All @@ -218,6 +236,9 @@ nk_stroke_circle(struct nk_command_buffer *b, struct nk_rect r,
cmd->w = (unsigned short)NK_MAX(r.w, 0);
cmd->h = (unsigned short)NK_MAX(r.h, 0);
cmd->color = c;
#ifdef NK_DRAW_CRC
b->crc = NK_CRC_FUNC((const void*)cmd,sizeof(nk_command_circle*), b->crc);
#endif
}
NK_API void
nk_fill_circle(struct nk_command_buffer *b, struct nk_rect r, struct nk_color c)
Expand All @@ -239,6 +260,9 @@ nk_fill_circle(struct nk_command_buffer *b, struct nk_rect r, struct nk_color c)
cmd->w = (unsigned short)NK_MAX(r.w, 0);
cmd->h = (unsigned short)NK_MAX(r.h, 0);
cmd->color = c;
#ifdef NK_DRAW_CRC
b->crc = NK_CRC_FUNC((const void*)cmd,sizeof(nk_command_circle_filled*), b->crc);
#endif
}
NK_API void
nk_stroke_arc(struct nk_command_buffer *b, float cx, float cy, float radius,
Expand All @@ -256,6 +280,9 @@ nk_stroke_arc(struct nk_command_buffer *b, float cx, float cy, float radius,
cmd->a[0] = a_min;
cmd->a[1] = a_max;
cmd->color = c;
#ifdef NK_DRAW_CRC
b->crc = NK_CRC_FUNC((const void*)cmd,sizeof(nk_command_arc*), b->crc);
#endif
}
NK_API void
nk_fill_arc(struct nk_command_buffer *b, float cx, float cy, float radius,
Expand All @@ -273,6 +300,9 @@ nk_fill_arc(struct nk_command_buffer *b, float cx, float cy, float radius,
cmd->a[0] = a_min;
cmd->a[1] = a_max;
cmd->color = c;
#ifdef NK_DRAW_CRC
b->crc = NK_CRC_FUNC((const void*)cmd,sizeof(nk_command_arc_filled*), b->crc);
#endif
}
NK_API void
nk_stroke_triangle(struct nk_command_buffer *b, float x0, float y0, float x1,
Expand Down Expand Up @@ -300,6 +330,9 @@ nk_stroke_triangle(struct nk_command_buffer *b, float x0, float y0, float x1,
cmd->c.x = (short)x2;
cmd->c.y = (short)y2;
cmd->color = c;
#ifdef NK_DRAW_CRC
b->crc = NK_CRC_FUNC((const void*)cmd,sizeof(nk_command_triangle*), b->crc);
#endif
}
NK_API void
nk_fill_triangle(struct nk_command_buffer *b, float x0, float y0, float x1,
Expand Down Expand Up @@ -327,6 +360,9 @@ nk_fill_triangle(struct nk_command_buffer *b, float x0, float y0, float x1,
cmd->c.x = (short)x2;
cmd->c.y = (short)y2;
cmd->color = c;
#ifdef NK_DRAW_CRC
b->crc = NK_CRC_FUNC((const void*)cmd,sizeof(nk_command_triangle_filled*), b->crc);
#endif
}
NK_API void
nk_stroke_polygon(struct nk_command_buffer *b, const float *points, int point_count,
Expand All @@ -348,6 +384,9 @@ nk_stroke_polygon(struct nk_command_buffer *b, const float *points, int point_co
cmd->points[i].x = (short)points[i*2];
cmd->points[i].y = (short)points[i*2+1];
}
#ifdef NK_DRAW_CRC
b->crc = NK_CRC_FUNC((const void*)cmd,sizeof(nk_command_polygon*), b->crc);
#endif
}
NK_API void
nk_fill_polygon(struct nk_command_buffer *b, const float *points, int point_count,
Expand All @@ -369,6 +408,9 @@ nk_fill_polygon(struct nk_command_buffer *b, const float *points, int point_coun
cmd->points[i].x = (short)points[i*2+0];
cmd->points[i].y = (short)points[i*2+1];
}
#ifdef NK_DRAW_CRC
b->crc = NK_CRC_FUNC((const void*)cmd,sizeof(nk_command_polygon_filled*), b->crc);
#endif
}
NK_API void
nk_stroke_polyline(struct nk_command_buffer *b, const float *points, int point_count,
Expand All @@ -390,6 +432,9 @@ nk_stroke_polyline(struct nk_command_buffer *b, const float *points, int point_c
cmd->points[i].x = (short)points[i*2];
cmd->points[i].y = (short)points[i*2+1];
}
#ifdef NK_DRAW_CRC
b->crc = NK_CRC_FUNC((const void*)cmd,sizeof(nk_command_polyline*), b->crc);
#endif
}
NK_API void
nk_draw_image(struct nk_command_buffer *b, struct nk_rect r,
Expand All @@ -413,6 +458,9 @@ nk_draw_image(struct nk_command_buffer *b, struct nk_rect r,
cmd->h = (unsigned short)NK_MAX(0, r.h);
cmd->img = *img;
cmd->col = col;
#ifdef NK_DRAW_CRC
b->crc = NK_CRC_FUNC((const void*)cmd,sizeof(nk_command_image*), b->crc);
#endif
}
NK_API void
nk_draw_nine_slice(struct nk_command_buffer *b, struct nk_rect r,
Expand Down Expand Up @@ -513,6 +561,9 @@ nk_push_custom(struct nk_command_buffer *b, struct nk_rect r,
cmd->h = (unsigned short)NK_MAX(0, r.h);
cmd->callback_data = usr;
cmd->callback = cb;
#ifdef NK_DRAW_CRC
b->crc = NK_CRC_FUNC((const void*)cmd,sizeof(nk_command_custom*), b->crc); //here be dragons
#endif
}
NK_API void
nk_draw_text(struct nk_command_buffer *b, struct nk_rect r,
Expand Down Expand Up @@ -554,4 +605,8 @@ nk_draw_text(struct nk_command_buffer *b, struct nk_rect r,
cmd->height = font->height;
NK_MEMCPY(cmd->string, string, (nk_size)length);
cmd->string[length] = '\0';
#ifdef NK_DRAW_CRC
b->crc = NK_CRC_FUNC((const void*)cmd,sizeof(nk_command_text *), b->crc); // command
b->crc = NK_CRC_FUNC((const void*)cmd->string,length, b->crc); //text
#endif
}
2 changes: 1 addition & 1 deletion src/paq.bat
Original file line number Diff line number Diff line change
@@ -1 +1 @@
build.py --macro NK --intro HEADER.md --pub nuklear.h --priv1 nuklear_internal.h,nuklear_math.c,nuklear_util.c,nuklear_color.c,nuklear_utf8.c,nuklear_buffer.c,nuklear_string.c,nuklear_draw.c,nuklear_vertex.c --extern stb_rect_pack.h,stb_truetype.h --priv2 nuklear_font.c,nuklear_input.c,nuklear_style.c,nuklear_context.c,nuklear_pool.c,nuklear_page_element.c,nuklear_table.c,nuklear_panel.c,nuklear_window.c,nuklear_popup.c,nuklear_contextual.c,nuklear_menu.c,nuklear_layout.c,nuklear_tree.c,nuklear_group.c,nuklear_list_view.c,nuklear_widget.c,nuklear_text.c,nuklear_image.c,nuklear_9slice.c,nuklear_button.c,nuklear_toggle.c,nuklear_selectable.c,nuklear_slider.c,nuklear_knob.c,nuklear_progress.c,nuklear_scrollbar.c,nuklear_text_editor.c,nuklear_edit.c,nuklear_property.c,nuklear_chart.c,nuklear_color_picker.c,nuklear_combo.c,nuklear_tooltip.c --outro LICENSE,CHANGELOG,CREDITS > ..\nuklear.h
build.py --macro NK --intro HEADER.md --pub nuklear.h --priv1 nuklear_internal.h,nuklear_crc.c,nuklear_math.c,nuklear_util.c,nuklear_color.c,nuklear_utf8.c,nuklear_buffer.c,nuklear_string.c,nuklear_draw.c,nuklear_vertex.c --extern stb_rect_pack.h,stb_truetype.h --priv2 nuklear_font.c,nuklear_input.c,nuklear_style.c,nuklear_context.c,nuklear_pool.c,nuklear_page_element.c,nuklear_table.c,nuklear_panel.c,nuklear_window.c,nuklear_popup.c,nuklear_contextual.c,nuklear_menu.c,nuklear_layout.c,nuklear_tree.c,nuklear_group.c,nuklear_list_view.c,nuklear_widget.c,nuklear_text.c,nuklear_image.c,nuklear_9slice.c,nuklear_button.c,nuklear_toggle.c,nuklear_selectable.c,nuklear_slider.c,nuklear_knob.c,nuklear_progress.c,nuklear_scrollbar.c,nuklear_text_editor.c,nuklear_edit.c,nuklear_property.c,nuklear_chart.c,nuklear_color_picker.c,nuklear_combo.c,nuklear_tooltip.c --outro LICENSE,CHANGELOG,CREDITS > ..\nuklear.h
2 changes: 1 addition & 1 deletion src/paq.sh
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
#!/bin/sh
python3 build.py --macro NK --intro HEADER.md --pub nuklear.h --priv1 nuklear_internal.h,nuklear_math.c,nuklear_util.c,nuklear_color.c,nuklear_utf8.c,nuklear_buffer.c,nuklear_string.c,nuklear_draw.c,nuklear_vertex.c --extern stb_rect_pack.h,stb_truetype.h --priv2 nuklear_font.c,nuklear_input.c,nuklear_style.c,nuklear_context.c,nuklear_pool.c,nuklear_page_element.c,nuklear_table.c,nuklear_panel.c,nuklear_window.c,nuklear_popup.c,nuklear_contextual.c,nuklear_menu.c,nuklear_layout.c,nuklear_tree.c,nuklear_group.c,nuklear_list_view.c,nuklear_widget.c,nuklear_text.c,nuklear_image.c,nuklear_9slice.c,nuklear_button.c,nuklear_toggle.c,nuklear_selectable.c,nuklear_slider.c,nuklear_knob.c,nuklear_progress.c,nuklear_scrollbar.c,nuklear_text_editor.c,nuklear_edit.c,nuklear_property.c,nuklear_chart.c,nuklear_color_picker.c,nuklear_combo.c,nuklear_tooltip.c --outro LICENSE,CHANGELOG,CREDITS > ../nuklear.h
python3 build.py --macro NK --intro HEADER.md --pub nuklear.h --priv1 nuklear_internal.h,nuklear_math.c,nuklear_util.c,nuklear_crc.c,nuklear_color.c,nuklear_utf8.c,nuklear_buffer.c,nuklear_string.c,nuklear_draw.c,nuklear_vertex.c --extern stb_rect_pack.h,stb_truetype.h --priv2 nuklear_font.c,nuklear_input.c,nuklear_style.c,nuklear_context.c,nuklear_pool.c,nuklear_page_element.c,nuklear_table.c,nuklear_panel.c,nuklear_window.c,nuklear_popup.c,nuklear_contextual.c,nuklear_menu.c,nuklear_layout.c,nuklear_tree.c,nuklear_group.c,nuklear_list_view.c,nuklear_widget.c,nuklear_text.c,nuklear_image.c,nuklear_9slice.c,nuklear_button.c,nuklear_toggle.c,nuklear_selectable.c,nuklear_slider.c,nuklear_knob.c,nuklear_progress.c,nuklear_scrollbar.c,nuklear_text_editor.c,nuklear_edit.c,nuklear_property.c,nuklear_chart.c,nuklear_color_picker.c,nuklear_combo.c,nuklear_tooltip.c --outro LICENSE,CHANGELOG,CREDITS > ../nuklear.h
Loading