nuklear
nuklear copied to clipboard
Differentiating between events Nuklear want to consume, and events to sent to app
Currently my code to handle events looks like this:
nk_input_begin(ctx);
if (SDL_PollEvent(&window_event)) {
if (window_event.type == SDL_QUIT) break;
nk_sdl_handle_event(&window_event);
handle_event(&pplane_state, &window_event);
}
nk_input_end(ctx);
The problem is that the events go to both Nuklear and to my app. What I want to do is first pass it to Nuklear, then check if it is something Nuklear uses, then if it is not, pass it to my own handle_event
function. How do I do that?
In "dear imgui" it seems it sets the wantCaptureFlags
. Is there anything equivalent in Nuklear?
Hey and thanks for your issue.
if I understand you correctly you want a flag for nk_sdl_handle_event to indicate if nuklear used
the given event or not? If so I uploaded a updated version for X11
and SDL2
and if you misunderstood you please elaborate in more depth what you mean with wantCaptureFlags
.
The change you pushed doesn't really address my problem. You're returning 1 if the type of event is something Nuklear might consume. However, even for any given type(say SDL_MOUSEBUTTONDOWN), Nuklear might not consume that event because the mouse click happened outside a Nuklear widget.
(EDIT: Assuming you're taking about this commit: https://github.com/vurtun/nuklear/commit/c4315eaf5b9f37225d074ba72ff5cb09be7cf51c)
Actually, it turns out just checking nk_window_is_any_hovered(ctx)
is enough for me. Sorry for the trouble.
Actually, it turns out just checking nk_window_is_any_hovered(ctx) is enough for me. Sorry for the bother.
Ah ok you meant that. Instead of nk_window_is_any_hovered
I would recommend using nk_item_is_any_active
which also takes widget state (scollbar scrolling, property dragging) into account. You can also only use it at the end of a frame so like imgui it only has the state of the last frame.
Thank you, this was very helpful. But is there a way to exclude "simply hovered" from being counted as "active"?
I have some hot keys (events) which I handle in my underlying event routine to toggle things in the Nuklear GUI. These are things like hiding the GUI, bringing up certain windows, or turning on different widgets. The problem is that the any mouse hover will block these. This means if the user just left the mouse cursor idle over any GUI widgets, the hot keys won't work. (E.g. Move the touchpad to click a button. When you are done, the cursor is just parked there because there was no reason to move it elsewhere.)
I think I need something like nk_item_is_any_interacting, or a mask flag parameter for nk_item_is_any_active that can filter out mouse hover. (I don't usually think of mouse hover events doing any primary action that can't be ignored. Touch screens and mobile really necessitated that mouse hover not do anything primary that cannot be lived without.)
Thanks
P.S. I tried the naive thing of copying/modifying the nk_item_is_any_active() function as so
NK_API int
nk_item_is_any_interacting(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;
return any_active;
}
But this didn't do the right thing. The combo box going outside the window problem in #212 came back, and edit_string fields no longer blocked other input.
Here is another attempt. Seems to work better. What do you think?
NK_API int
nk_item_is_any_interacting(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_MINIMIZED) {
struct nk_rect header = iter->bounds;
header.h = ctx->style.font->height + 2 * ctx->style.window.header.padding.y;
if (nk_input_mouse_clicked(&ctx->input, NK_BUTTON_LEFT, header)
|| nk_input_mouse_clicked(&ctx->input, NK_BUTTON_MIDDLE, header)
|| nk_input_mouse_clicked(&ctx->input, NK_BUTTON_RIGHT, header)
)
{
return 1;
}
} else if ( (ctx->last_widget_state & NK_WIDGET_STATE_ACTIVE)
|| nk_input_mouse_clicked(&ctx->input, NK_BUTTON_LEFT, iter->bounds)
|| nk_input_mouse_clicked(&ctx->input, NK_BUTTON_MIDDLE, iter->bounds)
|| nk_input_mouse_clicked(&ctx->input, NK_BUTTON_RIGHT, iter->bounds)
)
{
return 1;
}
/* check if window popup is being hovered */
// (Willing to accept hover blocking for popups for now)
if (iter->popup.active && iter->popup.win && nk_input_is_mouse_hovering_rect(&ctx->input, iter->popup.win->bounds))
return 1;
/* check if window edit is being edited */
if (iter->edit.active & NK_EDIT_ACTIVE)
return 1;
iter = iter->next;
}
return 0;
}
I made a mistake and discovered that while handling combo boxes, popups, and edit fields, it no longer worked with simple clicks on a window. However, I think I found a bug in Nuklear itself.
The call in the following function to nk_input_has_mouse_click_down_in_rect passed nk_false as the 4th parameter, which always results in my click detection failing when using nk_input_any_mouse_click_in_rect(). I think it should be nk_true like so:
NK_API int
nk_input_is_mouse_click_in_rect(const struct nk_input *i, enum nk_buttons id,
struct nk_rect b)
{
const struct nk_mouse_button *btn;
if (!i) return nk_false;
btn = &i->mouse.buttons[id];
return (nk_input_has_mouse_click_down_in_rect(i, id, b, nk_true) &&
btn->clicked) ? nk_true : nk_false;
}
This seems to work and my function seems to be working (at least in the cases I've tested so far). Here is the newer, slightly simplified code.
NK_API int
nk_item_is_any_interacting(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_MINIMIZED) {
struct nk_rect header = iter->bounds;
header.h = ctx->style.font->height + 2 * ctx->style.window.header.padding.y;
if (nk_input_any_mouse_click_in_rect(&ctx->input, header)) {
return 1;
}
}
else if ( (ctx->last_widget_state & NK_WIDGET_STATE_ACTIVE)
|| nk_input_any_mouse_click_in_rect(&ctx->input, iter->bounds)
)
{
return 1;
}
/* check if window popup is being hovered */
// (Willing to accept hover blocking for popups for now)
if (iter->popup.active && iter->popup.win && nk_input_is_mouse_hovering_rect(&ctx->input, iter->popup.win->bounds))
return 1;
/* check if window edit is being edited */
if (iter->edit.active & NK_EDIT_ACTIVE)
return 1;
iter = iter->next;
}
return 0;
}
I found this trying to figure out how I figure out if Nuklear uses an input event. Is this or a smiliar feature now included in the current build?
i don't think nk_item_is_any_active
actually works for my usecase for example: if I rescale a window and drag the mouse over the left edge of the windows such that the cursor is no longer hovering over the window, nk_item_is_any_active
returns false, even though i am currently scaling the window.
Using ewmailing's code as a starting point I came up with this soulution, using SDL2:
NK_API int nk_consume_keyboard(struct nk_context *ctx)
{
struct nk_window *iter;
NK_ASSERT(ctx);
if (!ctx) return 0;
iter = ctx->begin;
while (iter){
if (iter->edit.active & NK_EDIT_ACTIVE)
return 1;
iter = iter->next;
}
return 0;
}
NK_API int nk_consume_mouse(struct nk_context *ctx)
{
static unsigned sdl_previous_button_state = 0;
static int nk_consume_mouse_at_button_press = 0;
unsigned sdl_current_button_state = SDL_GetMouseState(nullptr, nullptr);
if (sdl_previous_button_state == 0 && sdl_current_button_state != 0){
nk_consume_mouse_at_button_press = nk_item_is_any_active(ctx);
}
sdl_previous_button_state = sdl_current_button_state;
if (sdl_current_button_state != 0)
return nk_consume_mouse_at_button_press;
else
return nk_item_is_any_active(ctx);
}
With these functions the input queue handling looks like this:
SDL_Event evt;
nk_input_begin(ctx);
while (SDL_PollEvent(&evt)) {
if (evt.type == SDL_QUIT) goto cleanup;
nk_sdl_handle_event(&evt);
if (nk_consume_keyboard(ctx) == 0){
//handle global hotkeys
if (evt.type == SDL_KEYDOWN && evt.key.keysym.sym == SDLK_t)
printf("pressed T\n");
}
if (nk_consume_mouse(ctx) == 0){
//handle non-nuklear mouse events (world camera)
if (evt.type == SDL_MOUSEMOTION && (evt.motion.state & SDL_BUTTON_LMASK))
printf("camera moving\n");
}
}
nk_input_end(ctx);
This solves the problem where the mouse click happens inside a nuklear window but leaves it after (dragging a scrollbar or resizing a window) while the mouse button is still pressed. I'm really surprised that this use case is not covered by nuklear. Did I miss something?
I'm really surprised that this use case is not covered by nuklear. Did I miss something?
Generally Nuklear itself (i.e. the backend-agnostic single-header library) doesn't handle this use case - it should be probably handled by backends e.g. like in your solution. Feel free to make a pull request and I'll be happy to review & merge it (but it might take some weeks/months - see https://github.com/vurtun/nuklear/pull/913#issuecomment-544426947 ).