Skip to content
This repository has been archived by the owner on Dec 2, 2019. It is now read-only.

Differentiating between events Nuklear want to consume, and events to sent to app #189

Open
samrat opened this issue Aug 1, 2016 · 11 comments

Comments

@samrat
Copy link

samrat commented Aug 1, 2016

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?

@vurtun
Copy link
Owner

vurtun commented Aug 1, 2016

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.

@samrat
Copy link
Author

samrat commented Aug 1, 2016

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: c4315ea)

@samrat
Copy link
Author

samrat commented Aug 1, 2016

Actually, it turns out just checking nk_window_is_any_hovered(ctx) is enough for me. Sorry for the trouble.

@samrat samrat closed this as completed Aug 1, 2016
@vurtun
Copy link
Owner

vurtun commented Aug 1, 2016

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.

@ewmailing
Copy link

ewmailing commented Aug 15, 2016

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.

@ewmailing
Copy link

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;
}

@vurtun vurtun reopened this Aug 16, 2016
@ewmailing
Copy link

ewmailing commented Aug 16, 2016

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;
}

@pragmascript
Copy link

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?

@vurtun vurtun closed this as completed Jan 31, 2018
@vurtun vurtun reopened this Jan 31, 2018
@pragmascript
Copy link

pragmascript commented Jan 31, 2018

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.

@mihaly-sisak
Copy link

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?

@dumblob
Copy link
Contributor

dumblob commented Oct 29, 2019

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 #913 (comment) ).

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

6 participants