#include "nuklear.h"
#include "nuklear_internal.h"
/* ===============================================================
*
* WINDOW
*
* ===============================================================*/
NK_LIB void*
nk_create_window(struct nk_context *ctx)
{
struct nk_page_element *elem;
elem = nk_create_page_element(ctx);
if (!elem) return 0;
elem->data.win.seq = ctx->seq;
return &elem->data.win;
}
NK_LIB void
nk_free_window(struct nk_context *ctx, struct nk_window *win)
{
/* unlink windows from list */
struct nk_table *it = win->tables;
if (win->popup.win) {
nk_free_window(ctx, win->popup.win);
win->popup.win = 0;
}
win->next = 0;
win->prev = 0;
while (it) {
/*free window state tables */
struct nk_table *n = it->next;
nk_remove_table(win, it);
nk_free_table(ctx, it);
if (it == win->tables)
win->tables = n;
it = n;
}
/* link windows into freelist */
{union nk_page_data *pd = NK_CONTAINER_OF(win, union nk_page_data, win);
struct nk_page_element *pe = NK_CONTAINER_OF(pd, struct nk_page_element, data);
nk_free_page_element(ctx, pe);}
}
NK_LIB struct nk_window*
nk_find_window(struct nk_context *ctx, nk_hash hash, const char *name)
{
struct nk_window *iter;
iter = ctx->begin;
while (iter) {
NK_ASSERT(iter != iter->next);
if (iter->name == hash) {
int max_len = nk_strlen(iter->name_string);
if (!nk_stricmpn(iter->name_string, name, max_len))
return iter;
}
iter = iter->next;
}
return 0;
}
NK_LIB void
nk_insert_window(struct nk_context *ctx, struct nk_window *win,
enum nk_window_insert_location loc)
{
const struct nk_window *iter;
NK_ASSERT(ctx);
NK_ASSERT(win);
if (!win || !ctx) return;
iter = ctx->begin;
while (iter) {
NK_ASSERT(iter != iter->next);
NK_ASSERT(iter != win);
if (iter == win) return;
iter = iter->next;
}
if (!ctx->begin) {
win->next = 0;
win->prev = 0;
ctx->begin = win;
ctx->end = win;
ctx->count = 1;
return;
}
if (loc == NK_INSERT_BACK) {
struct nk_window *end;
end = ctx->end;
end->flags |= NK_WINDOW_ROM;
end->next = win;
win->prev = ctx->end;
win->next = 0;
ctx->end = win;
ctx->active = ctx->end;
ctx->end->flags &= ~(nk_flags)NK_WINDOW_ROM;
} else {
/*ctx->end->flags |= NK_WINDOW_ROM;*/
ctx->begin->prev = win;
win->next = ctx->begin;
win->prev = 0;
ctx->begin = win;
ctx->begin->flags &= ~(nk_flags)NK_WINDOW_ROM;
}
ctx->count++;
}
NK_LIB void
nk_remove_window(struct nk_context *ctx, struct nk_window *win)
{
if (win == ctx->begin || win == ctx->end) {
if (win == ctx->begin) {
ctx->begin = win->next;
if (win->next)
win->next->prev = 0;
}
if (win == ctx->end) {
ctx->end = win->prev;
if (win->prev)
win->prev->next = 0;
}
} else {
if (win->next)
win->next->prev = win->prev;
if (win->prev)
win->prev->next = win->next;
}
if (win == ctx->active || !ctx->active) {
ctx->active = ctx->end;
if (ctx->end)
ctx->end->flags &= ~(nk_flags)NK_WINDOW_ROM;
}
win->next = 0;
win->prev = 0;
ctx->count--;
}
NK_API nk_bool
nk_begin(struct nk_context *ctx, const char *title,
struct nk_rect bounds, nk_flags flags)
{
return nk_begin_titled(ctx, title, title, bounds, flags);
}
NK_API nk_bool
nk_begin_titled(struct nk_context *ctx, const char *name, const char *title,
struct nk_rect bounds, nk_flags flags)
{
struct nk_window *win;
struct nk_style *style;
nk_hash name_hash;
int name_len;
int ret = 0;
NK_ASSERT(ctx);
NK_ASSERT(name);
NK_ASSERT(title);
NK_ASSERT(ctx->style.font && ctx->style.font->width && "if this triggers you forgot to add a font");
NK_ASSERT(!ctx->current && "if this triggers you missed a `nk_end` call");
if (!ctx || ctx->current || !title || !name)
return 0;
/* find or create window */
style = &ctx->style;
name_len = (int)nk_strlen(name);
name_hash = nk_murmur_hash(name, (int)name_len, NK_WINDOW_TITLE);
win = nk_find_window(ctx, name_hash, name);
if (!win) {
/* create new window */
nk_size name_length = (nk_size)name_len;
win = (struct nk_window*)nk_create_window(ctx);
NK_ASSERT(win);
if (!win) return 0;
if (flags & NK_WINDOW_BACKGROUND)
nk_insert_window(ctx, win, NK_INSERT_FRONT);
else nk_insert_window(ctx, win, NK_INSERT_BACK);
nk_command_buffer_init(&win->buffer, &ctx->memory, NK_CLIPPING_ON);
win->flags = flags;
win->bounds = bounds;
win->name = name_hash;
name_length = NK_MIN(name_length, NK_WINDOW_MAX_NAME-1);
NK_MEMCPY(win->name_string, name, name_length);
win->name_string[name_length] = 0;
win->popup.win = 0;
if (!ctx->active)
ctx->active = win;
} else {
/* update window */
win->flags &= ~(nk_flags)(NK_WINDOW_PRIVATE-1);
win->flags |= flags;
if (!(win->flags & (NK_WINDOW_MOVABLE | NK_WINDOW_SCALABLE)))
win->bounds = bounds;
/* If this assert triggers you either:
*
* I.) Have more than one window with the same name or
* II.) You forgot to actually draw the window.
* More specific you did not call `nk_clear` (nk_clear will be
* automatically called for you if you are using one of the
* provided demo backends). */
NK_ASSERT(win->seq != ctx->seq);
win->seq = ctx->seq;
if (!ctx->active && !(win->flags & NK_WINDOW_HIDDEN)) {
ctx->active = win;
ctx->end = win;
}
}
if (win->flags & NK_WINDOW_HIDDEN) {
ctx->current = win;
win->layout = 0;
return 0;
} else nk_start(ctx, win);
/* window overlapping */
if (!(win->flags & NK_WINDOW_HIDDEN) && !(win->flags & NK_WINDOW_NO_INPUT))
{
int inpanel, ishovered;
struct nk_window *iter = win;
float h = ctx->style.font->height + 2.0f * style->window.header.padding.y +
(2.0f * style->window.header.label_padding.y);
struct nk_rect win_bounds = (!(win->flags & NK_WINDOW_MINIMIZED))?
win->bounds: nk_rect(win->bounds.x, win->bounds.y, win->bounds.w, h);
/* activate window if hovered and no other window is overlapping this window */
inpanel = nk_input_has_mouse_click_down_in_rect(&ctx->input, NK_BUTTON_LEFT, win_bounds, nk_true);
inpanel = inpanel && ctx->input.mouse.buttons[NK_BUTTON_LEFT].clicked;
ishovered = nk_input_is_mouse_hovering_rect(&ctx->input, win_bounds);
if ((win != ctx->active) && ishovered && !ctx->input.mouse.buttons[NK_BUTTON_LEFT].down) {
iter = win->next;
while (iter) {
struct nk_rect iter_bounds = (!(iter->flags & NK_WINDOW_MINIMIZED))?
iter->bounds: nk_rect(iter->bounds.x, iter->bounds.y, iter->bounds.w, h);
if (NK_INTERSECT(win_bounds.x, win_bounds.y, win_bounds.w, win_bounds.h,
iter_bounds.x, iter_bounds.y, iter_bounds.w, iter_bounds.h) &&
(!(iter->flags & NK_WINDOW_HIDDEN)))
break;
if (iter->popup.win && iter->popup.active && !(iter->flags & NK_WINDOW_HIDDEN) &&
NK_INTERSECT(win->bounds.x, win_bounds.y, win_bounds.w, win_bounds.h,
iter->popup.win->bounds.x, iter->popup.win->bounds.y,
iter->popup.win->bounds.w, iter->popup.win->bounds.h))
break;
iter = iter->next;
}
}
/* activate window if clicked */
if (iter && inpanel && (win != ctx->end)) {
iter = win->next;
while (iter) {
/* try to find a panel with higher priority in the same position */
struct nk_rect iter_bounds = (!(iter->flags & NK_WINDOW_MINIMIZED))?
iter->bounds: nk_rect(iter->bounds.x, iter->bounds.y, iter->bounds.w, h);
if (NK_INBOX(ctx->input.mouse.pos.x, ctx->input.mouse.pos.y,
iter_bounds.x, iter_bounds.y, iter_bounds.w, iter_bounds.h) &&
!(iter->flags & NK_WINDOW_HIDDEN))
break;
if (iter->popup.win && iter->popup.active && !(iter->flags & NK_WINDOW_HIDDEN) &&
NK_INTERSECT(win_bounds.x, win_bounds.y, win_bounds.w, win_bounds.h,
iter->popup.win->bounds.x, iter->popup.win->bounds.y,
iter->popup.win->bounds.w, iter->popup.win->bounds.h))
break;
iter = iter->next;
}
}
if (iter && !(win->flags & NK_WINDOW_ROM) && (win->flags & NK_WINDOW_BACKGROUND)) {
win->flags |= (nk_flags)NK_WINDOW_ROM;
iter->flags &= ~(nk_flags)NK_WINDOW_ROM;
ctx->active = iter;
if (!(iter->flags & NK_WINDOW_BACKGROUND)) {
/* current window is active in that position so transfer to top
* at the highest priority in stack */
nk_remove_window(ctx, iter);
nk_insert_window(ctx, iter, NK_INSERT_BACK);
}
} else {
if (!iter && ctx->end != win) {
if (!(win->flags & NK_WINDOW_BACKGROUND)) {
/* current window is active in that position so transfer to top
* at the highest priority in stack */
nk_remove_window(ctx, win);
nk_insert_window(ctx, win, NK_INSERT_BACK);
}
win->flags &= ~(nk_flags)NK_WINDOW_ROM;
ctx->active = win;
}
if (ctx->end != win && !(win->flags & NK_WINDOW_BACKGROUND))
win->flags |= NK_WINDOW_ROM;
}
}
win->layout = (struct nk_panel*)nk_create_panel(ctx);
ctx->current = win;
ret = nk_panel_begin(ctx, title, NK_PANEL_WINDOW);
win->layout->offset_x = &win->scrollbar.x;
win->layout->offset_y = &win->scrollbar.y;
return ret;
}
NK_API void
nk_end(struct nk_context *ctx)
{
struct nk_panel *layout;
NK_ASSERT(ctx);
NK_ASSERT(ctx->current && "if this triggers you forgot to call `nk_begin`");
if (!ctx || !ctx->current)
return;
layout = ctx->current->layout;
if (!layout || (layout->type == NK_PANEL_WINDOW && (ctx->current->flags & NK_WINDOW_HIDDEN))) {
ctx->current = 0;
return;
}
nk_panel_end(ctx);
nk_free_panel(ctx, ctx->current->layout);
ctx->current = 0;
}
NK_API struct nk_rect
nk_window_get_bounds(const struct nk_context *ctx)
{
NK_ASSERT(ctx);
NK_ASSERT(ctx->current);
if (!ctx || !ctx->current) return nk_rect(0,0,0,0);
return ctx->current->bounds;
}
NK_API struct nk_vec2
nk_window_get_position(const struct nk_context *ctx)
{
NK_ASSERT(ctx);
NK_ASSERT(ctx->current);
if (!ctx || !ctx->current) return nk_vec2(0,0);
return nk_vec2(ctx->current->bounds.x, ctx->current->bounds.y);
}
NK_API struct nk_vec2
nk_window_get_size(const struct nk_context *ctx)
{
NK_ASSERT(ctx);
NK_ASSERT(ctx->current);
if (!ctx || !ctx->current) return nk_vec2(0,0);
return nk_vec2(ctx->current->bounds.w, ctx->current->bounds.h);
}
NK_API float
nk_window_get_width(const struct nk_context *ctx)
{
NK_ASSERT(ctx);
NK_ASSERT(ctx->current);
if (!ctx || !ctx->current) return 0;
return ctx->current->bounds.w;
}
NK_API float
nk_window_get_height(const struct nk_context *ctx)
{
NK_ASSERT(ctx);
NK_ASSERT(ctx->current);
if (!ctx || !ctx->current) return 0;
return ctx->current->bounds.h;
}
NK_API struct nk_rect
nk_window_get_content_region(struct nk_context *ctx)
{
NK_ASSERT(ctx);
NK_ASSERT(ctx->current);
if (!ctx || !ctx->current) return nk_rect(0,0,0,0);
return ctx->current->layout->clip;
}
NK_API struct nk_vec2
nk_window_get_content_region_min(struct nk_context *ctx)
{
NK_ASSERT(ctx);
NK_ASSERT(ctx->current);
NK_ASSERT(ctx->current->layout);
if (!ctx || !ctx->current) return nk_vec2(0,0);
return nk_vec2(ctx->current->layout->clip.x, ctx->current->layout->clip.y);
}
NK_API struct nk_vec2
nk_window_get_content_region_max(struct nk_context *ctx)
{
NK_ASSERT(ctx);
NK_ASSERT(ctx->current);
NK_ASSERT(ctx->current->layout);
if (!ctx || !ctx->current) return nk_vec2(0,0);
return nk_vec2(ctx->current->layout->clip.x + ctx->current->layout->clip.w,
ctx->current->layout->clip.y + ctx->current->layout->clip.h);
}
NK_API struct nk_vec2
nk_window_get_content_region_size(struct nk_context *ctx)
{
NK_ASSERT(ctx);
NK_ASSERT(ctx->current);
NK_ASSERT(ctx->current->layout);
if (!ctx || !ctx->current) return nk_vec2(0,0);
return nk_vec2(ctx->current->layout->clip.w, ctx->current->layout->clip.h);
}
NK_API struct nk_command_buffer*
nk_window_get_canvas(struct nk_context *ctx)
{
NK_ASSERT(ctx);
NK_ASSERT(ctx->current);
NK_ASSERT(ctx->current->layout);
if (!ctx || !ctx->current) return 0;
return &ctx->current->buffer;
}
NK_API struct nk_panel*
nk_window_get_panel(struct nk_context *ctx)
{
NK_ASSERT(ctx);
NK_ASSERT(ctx->current);
if (!ctx || !ctx->current) return 0;
return ctx->current->layout;
}
NK_API void
nk_window_get_scroll(struct nk_context *ctx, nk_uint *offset_x, nk_uint *offset_y)
{
struct nk_window *win;
NK_ASSERT(ctx);
NK_ASSERT(ctx->current);
if (!ctx || !ctx->current)
return ;
win = ctx->current;
if (offset_x)
*offset_x = win->scrollbar.x;
if (offset_y)
*offset_y = win->scrollbar.y;
}
NK_API nk_bool
nk_window_has_focus(const struct nk_context *ctx)
{
NK_ASSERT(ctx);
NK_ASSERT(ctx->current);
NK_ASSERT(ctx->current->layout);
if (!ctx || !ctx->current) return 0;
return ctx->current == ctx->active;
}
NK_API nk_bool
nk_window_is_hovered(struct nk_context *ctx)
{
NK_ASSERT(ctx);
NK_ASSERT(ctx->current);
if (!ctx || !ctx->current || (ctx->current->flags & NK_WINDOW_HIDDEN))
return 0;
else {
struct nk_rect actual_bounds = ctx->current->bounds;
if (ctx->begin->flags & NK_WINDOW_MINIMIZED) {
actual_bounds.h = ctx->current->layout->header_height;
}
return nk_input_is_mouse_hovering_rect(&ctx->input, actual_bounds);
}
}
NK_API nk_bool
nk_window_is_any_hovered(struct nk_context *ctx)
{
struct nk_window *iter;
NK_ASSERT(ctx);
if (!ctx) return 0;
iter = ctx->begin;
while (iter) {
/* check if window is being hovered */
if(!(iter->flags & NK_WINDOW_HIDDEN)) {
/* check if window popup is being hovered */
if (iter->popup.active && iter->popup.win && nk_input_is_mouse_hovering_rect(&ctx->input, iter->popup.win->bounds))
return 1;
if (iter->flags & NK_WINDOW_MINIMIZED) {
struct nk_rect header = iter->bounds;
header.h = ctx->style.font->height + 2 * ctx->style.window.header.padding.y;
if (nk_input_is_mouse_hovering_rect(&ctx->input, header))
return 1;
} else if (nk_input_is_mouse_hovering_rect(&ctx->input, iter->bounds)) {
return 1;
}
}
iter = iter->next;
}
return 0;
}
NK_API nk_bool
nk_item_is_any_active(struct nk_context *ctx)
{
int any_hovered = nk_window_is_any_hovered(ctx);
int any_active = (ctx->last_widget_state & NK_WIDGET_STATE_MODIFIED);
return any_hovered || any_active;
}
NK_API nk_bool
nk_window_is_collapsed(struct nk_context *ctx, const char *name)
{
int title_len;
nk_hash title_hash;
struct nk_window *win;
NK_ASSERT(ctx);
if (!ctx) return 0;
title_len = (int)nk_strlen(name);
title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE);
win = nk_find_window(ctx, title_hash, name);
if (!win) return 0;
return win->flags & NK_WINDOW_MINIMIZED;
}
NK_API nk_bool
nk_window_is_closed(struct nk_context *ctx, const char *name)
{
int title_len;
nk_hash title_hash;
struct nk_window *win;
NK_ASSERT(ctx);
if (!ctx) return 1;
title_len = (int)nk_strlen(name);
title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE);
win = nk_find_window(ctx, title_hash, name);
if (!win) return 1;
return (win->flags & NK_WINDOW_CLOSED);
}
NK_API nk_bool
nk_window_is_hidden(struct nk_context *ctx, const char *name)
{
int title_len;
nk_hash title_hash;
struct nk_window *win;
NK_ASSERT(ctx);
if (!ctx) return 1;
title_len = (int)nk_strlen(name);
title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE);
win = nk_find_window(ctx, title_hash, name);
if (!win) return 1;
return (win->flags & NK_WINDOW_HIDDEN);
}
NK_API nk_bool
nk_window_is_active(struct nk_context *ctx, const char *name)
{
int title_len;
nk_hash title_hash;
struct nk_window *win;
NK_ASSERT(ctx);
if (!ctx) return 0;
title_len = (int)nk_strlen(name);
title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE);
win = nk_find_window(ctx, title_hash, name);
if (!win) return 0;
return win == ctx->active;
}
NK_API struct nk_window*
nk_window_find(struct nk_context *ctx, const char *name)
{
int title_len;
nk_hash title_hash;
title_len = (int)nk_strlen(name);
title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE);
return nk_find_window(ctx, title_hash, name);
}
NK_API void
nk_window_close(struct nk_context *ctx, const char *name)
{
struct nk_window *win;
NK_ASSERT(ctx);
if (!ctx) return;
win = nk_window_find(ctx, name);
if (!win) return;
NK_ASSERT(ctx->current != win && "You cannot close a currently active window");
if (ctx->current == win) return;
win->flags |= NK_WINDOW_HIDDEN;
win->flags |= NK_WINDOW_CLOSED;
}
NK_API void
nk_window_set_bounds(struct nk_context *ctx,
const char *name, struct nk_rect bounds)
{
struct nk_window *win;
NK_ASSERT(ctx);
if (!ctx) return;
win = nk_window_find(ctx, name);
if (!win) return;
NK_ASSERT(ctx->current != win && "You cannot update a currently in procecss window");
win->bounds = bounds;
}
NK_API void
nk_window_set_position(struct nk_context *ctx,
const char *name, struct nk_vec2 pos)
{
struct nk_window *win = nk_window_find(ctx, name);
if (!win) return;
win->bounds.x = pos.x;
win->bounds.y = pos.y;
}
NK_API void
nk_window_set_size(struct nk_context *ctx,
const char *name, struct nk_vec2 size)
{
struct nk_window *win = nk_window_find(ctx, name);
if (!win) return;
win->bounds.w = size.x;
win->bounds.h = size.y;
}
NK_API void
nk_window_set_scroll(struct nk_context *ctx, nk_uint offset_x, nk_uint offset_y)
{
struct nk_window *win;
NK_ASSERT(ctx);
NK_ASSERT(ctx->current);
if (!ctx || !ctx->current)
return;
win = ctx->current;
win->scrollbar.x = offset_x;
win->scrollbar.y = offset_y;
}
NK_API void
nk_window_collapse(struct nk_context *ctx, const char *name,
enum nk_collapse_states c)
{
int title_len;
nk_hash title_hash;
struct nk_window *win;
NK_ASSERT(ctx);
if (!ctx) return;
title_len = (int)nk_strlen(name);
title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE);
win = nk_find_window(ctx, title_hash, name);
if (!win) return;
if (c == NK_MINIMIZED)
win->flags |= NK_WINDOW_MINIMIZED;
else win->flags &= ~(nk_flags)NK_WINDOW_MINIMIZED;
}
NK_API void
nk_window_collapse_if(struct nk_context *ctx, const char *name,
enum nk_collapse_states c, int cond)
{
NK_ASSERT(ctx);
if (!ctx || !cond) return;
nk_window_collapse(ctx, name, c);
}
NK_API void
nk_window_show(struct nk_context *ctx, const char *name, enum nk_show_states s)
{
int title_len;
nk_hash title_hash;
struct nk_window *win;
NK_ASSERT(ctx);
if (!ctx) return;
title_len = (int)nk_strlen(name);
title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE);
win = nk_find_window(ctx, title_hash, name);
if (!win) return;
if (s == NK_HIDDEN) {
win->flags |= NK_WINDOW_HIDDEN;
} else win->flags &= ~(nk_flags)NK_WINDOW_HIDDEN;
}
NK_API void
nk_window_show_if(struct nk_context *ctx, const char *name,
enum nk_show_states s, int cond)
{
NK_ASSERT(ctx);
if (!ctx || !cond) return;
nk_window_show(ctx, name, s);
}
NK_API void
nk_window_set_focus(struct nk_context *ctx, const char *name)
{
int title_len;
nk_hash title_hash;
struct nk_window *win;
NK_ASSERT(ctx);
if (!ctx) return;
title_len = (int)nk_strlen(name);
title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE);
win = nk_find_window(ctx, title_hash, name);
if (win && ctx->end != win) {
nk_remove_window(ctx, win);
nk_insert_window(ctx, win, NK_INSERT_BACK);
}
ctx->active = win;
}
NK_API void
nk_rule_horizontal(struct nk_context *ctx, struct nk_color color, nk_bool rounding)
{
struct nk_rect space;
enum nk_widget_layout_states state = nk_widget(&space, ctx);
struct nk_command_buffer *canvas = nk_window_get_canvas(ctx);
if (!state) return;
nk_fill_rect(canvas, space, rounding && space.h > 1.5f ? space.h / 2.0f : 0, color);
}