Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Seeking something like "IsAnyOtherItemHovered()" to fix interaction with multiple overlapping elements #3909

Closed
pixtur opened this issue Mar 13, 2021 · 4 comments
Labels

Comments

@pixtur
Copy link

pixtur commented Mar 13, 2021

Version/Branch of Dear ImGui:
Version: 1.77
Branch: master

Back-end/Renderer/Compiler/OS
Back-ends: imgui.net
Operating System: win10

My Issue/Question:
I know that overlapping interactive elements are tricky to implement with a single pass immediate UI.
And I learned, that it is possible to partly resolve the inversed draw/interaction sort order by channel splitting.

I normally try to work around or ignore these problems, but for my timeline editing this get kind of annoying.

I am wondering, if there is the possibility to prevent multiple click events by something like this...

            ImGui.PushId(item.id);
            var somethingElseWasHovered = ImGui.IsAnyItemHovered();
            var wasClicked = ImGui.InvisibleButton("item", bodySize);
            if(!somethingElseWasHovered  && wasClicked) {
                  // do stuff
            }
            ImGui.PopId();

For some reason however, somethingElseWasHovered becomes true even before the invisible button is being drawn. Obviously I don't understand what's going on (I guess some information from the previous frame must be used for this).

  • Is there a method to check, if another (overlapping) item is hovered besides the current item?
  • Are there any tutorials on how how IsAnyItemHovered() works? (search the documentation in imgui.h)

Screenshots/Video
https://user-images.githubusercontent.com/1732545/111050991-336d8e00-8450-11eb-8616-c325f622ccdc.mp4

@ocornut
Copy link
Owner

ocornut commented Mar 15, 2021

Obviously I don't understand what's going on (I guess some information from the previous frame must be used for this).

Yes it return true if any item has been hovered in the previous frame.

Are there any tutorials on how how IsAnyItemHovered() works? (search the documentation in imgui.h)

It's a 1 line function you can check the code.
The || g.Hovered != 0 part of the test always bothered me since that part is testing for element hovered this frame but only covers what has been submitted so far.

It's hard to understand what you are getting at with your question, I reckon you should be looking at SetItemAllowOverlap() which is a mechanism to allow overlapping hit-testing.

This is going to be confusing as relatively to half-implemented/designed API, but I'm still going to write it down:

ImGui::Begin("Overlap");

ImGui::SetCursorPos(ImVec2(50, 50));
bool button_1_pressed = ImGui::Button("11111", ImVec2(50, 50));
bool button_1_hovered = ImGui::IsItemHovered();
//ImGui::SetItemAllowOverlap();

ImGui::SetCursorPos(ImVec2(70, 70));
bool button_2_pressed = ImGui::Button("22222", ImVec2(50, 50));
bool button_2_hovered = ImGui::IsItemHovered();

ImGui::Text("Button 1: %s%s", button_1_hovered ? "hovered " : "", button_1_pressed ? "PRESSED " : "");
ImGui::Text("Button 2: %s%s", button_2_hovered ? "hovered " : "", button_2_pressed ? "PRESSED " : "");

ImGui::End();

image

When hovering in the intersection of both button:

(A) Without SetItemAllowOverlap() after Button 1

  • when clicking: button 1 is pressed,
  • only button 1 appears hovered
  • both button return true to IsItemHovered()

(B) With SetItemAllowOverlap() after Button 1

  • when clicking: button 2 is pressed
  • both buttons appears hovered
  • both button return true to IsItemHovered()

(C) With (internal) ImGuiButtonFlags_AllowItemOverlap flag on Button 1 + SetItemAllowOverlap() after Button 1

  • when clicking: button 2 is pressed
  • only button 2 appears appears hovered
  • both button return true to IsItemHovered()

Added records of 1.88 behavior on 2023/06:
(D) With (internal) ImGuiButtonFlags_AllowItemOverlap flag on Button 1 + WIITHOUT SetItemAllowOverlap() call.

  • when clicking: button 1 is pressed
  • only button 1 appears hovered
  • both button return true to IsItemHovered()

Notice issues:

  • Only difference between B and C is how Button 1 visibly react to hover. Both cases are useful depending on context, but C is likely to be more commonly useful.
  • Inconsistency between the return value of IsItemHovered() and what the button display, I think this should be fixed.
  • ImGuiButtonFlags_AllowItemOverlap is currently explicit, I've been considering switching to a hover model where it is implicit default, but it's going to be a lots of work. With this model we would only need to call SetItemAllowOverlap().
  • Either settings could be wired to shared item flags.

Added records of 1.88 behavior on 2023/06:

  • The only difference between (D) and not doing anything is: affected button will check g.HoveredIdPreviousFrame and defer initial hover reaction by 1 frame, but because SetItemAllowOverlap() is not called, other well-behaved items won't claim g.HoveredId.
  • As of today 1.89.7 WIP (18965) two places were using ImGuiButtonFlags_AllowItemOverlap WITHOUT SetItemAllowOverlap(): SplitterBehavior() and TableUpdateBorders(). Effectively this behavior was analogous to not doing anything, EXCEPT in the case of SplitterBehavior() is allows the caller to follow the call with SetItemAllowOverlap().

(This is a a bit of a mess. There are they two aspects: the ButtonBehavior() logic has the ImGuiButtonFlags_AllowItemOverlap flag + there's a `SetItemAllowOverlap() call which can be called afterwards.... hence I know this post is probably only opening the veil to a new world of confusion especially if you are stuck without access to internals..)

@pixtur
Copy link
Author

pixtur commented Mar 15, 2021

Thank you for this excellent explanation. I guess I have to think about this.

Some thoughts, though:

  • Just to verify that I understood this correctly: ImGui will make sure, that only one of overlapping elements is activated. And I don't need the above hack for detecting if something else was hovered to prevent simultaneous clicks on multiple elements.
  • I find your explanation above much clearer than then documentation of SetItemAllowOverlap()...

allow last item to be overlapped by a subsequent item. sometimes useful with invisible buttons, selectables, etc. to catch unused area.

  • I personally would prefer a version D: Only "Button 2" returns true to when calling with some fancy optional parameter IsItemHovered(wasNotOverlappedInPreviousFrame:true); so I could only react on clicks if this returns true.😄 Especially with node graphs and timeline elements, activating the overlapped element is probably worse than a slight delay.

But I'm sure, that you already thought about this problem. If there would be an easy solution, you would have done it already...

@ocornut
Copy link
Owner

ocornut commented Jun 12, 2023

Just to verify that I understood this correctly: ImGui will make sure, that only one of overlapping elements is activated. And I don't need the above hack for detecting if something else was hovered to prevent simultaneous clicks on multiple elements

Correct.

I find your explanation above much clearer than then documentation of SetItemAllowOverlap()...

This was never advertised much because of the subtleties between case B and C and the potential need for both.

However, plot twist discovered via #6512, case B simply doesn't work since 1.89 because of the introduction of the input ownership system.

Update of preceding report following changes in 1.89+. When hovering in the intersection of both button, case A and C are identical, but case B changes:

(B after 1.89) With SetItemAllowOverlap() after Button 1 but not using ImGuiButtonFlags_AllowItemOverlap in Button 1:

  • when clicking: button 1 is pressed --> This differ from pre 1.89
  • both buttons appears hovered
  • both button return true to IsItemHovered()

This is because Button 1 takes ownership for ImGuiKey_MouseLeft on mouse down, preventing Mouse 2 to access it.
The API SetItemAllowOverlap() cannot be modified to solve this.

I am going to work on a solution for this.

ocornut added a commit that referenced this issue Jun 26, 2023
…AllowtemOverlap in TableUpdateBorders(). (#6512, #3909)

This was copied from SplitterBehavior(). The only hypothetical value in SplitterBehavior() would be ability to manually call SetItemAllowOverlap() after the call.
Btw generally AllowOverlap is undesirable for columns as e.g. a spanning selectable would cover entire width and prevent columns from being used.

# Conflicts:
#	imgui_tables.cpp
ocornut added a commit that referenced this issue Jun 26, 2023
…GuiTreeNodeFlags_AllowOverlap and holding item held, overlapping widgets won't appear as hovered. (#6512, #3909)

Essentially we are going to remove calls to SetItemAllowOverlap() and standardize the fact that only 'HoveredId == id' test from it is performed.

# Conflicts:
#	imgui_widgets.cpp
ocornut added a commit that referenced this issue Jun 27, 2023
ocornut added a commit that referenced this issue Jun 28, 2023
…'ImGuiTreeNodeFlags_AllowOverlap', 'ImGuiSelectableFlags_AllowItemOverlap' to 'ImGuiSelectableFlags_AllowOverlap'. Kept redirecting enums (will obsolete). (#6512, #3909, #517)

+ Internals: Renamed 'ImGuiButtonFlags_AllowItemOverlap' to 'ImGuiButtonFlags_AllowOverlap' without redirection.
ocornut added a commit that referenced this issue Jun 28, 2023
…tation of SetNextItemAllowOverlap() + potentially remove extra_flags from ItemAdd(). (#6512, #3909)
ocornut added a commit that referenced this issue Jun 28, 2023
…temAllowOverlap()'. (#6512, #3909, #517)

# Conflicts:
#	imgui.cpp
#	imgui_widgets.cpp
ocornut added a commit that referenced this issue Jun 28, 2023
…g an item using AllowOverlap mode. Added ImGuiHoveredFlags_AllowWhenOverlappedByItem, ImGuiHoveredFlags_AllowWhenOverlappedByWindow., (#6512, #3909, #517)
ocornut added a commit that referenced this issue Jun 28, 2023
…so it can be called from ButtonBehavior() not following an ItemAdd().

This also allow moving AllowOverlap logic from ButtonBehavior() to ItemHoverable(), allowing other widgets to honor it. (#6512, #3909, #517)
ocornut added a commit that referenced this issue Jun 28, 2023
…r() to ItemHoverable() now that it is possible. (#6512, #3909, #517)

This allows DragXXX, SliderXXX, PlotXXX etc to honor SetNextItemAllowOverlap().
@ocornut
Copy link
Owner

ocornut commented Jun 28, 2023

I have pushed a bunch of changes related to AllowOverlap mode.

See this comment for details: #6512 (comment)

TL;DR;

  • SetItemAllowOverlap() couldn't work since 1.89 and was obsoleted.
  • Introduced SetNextItemAllowOverlap(). It doesn't need an internal button flags and should work across most/all widgets.
  • Made IsItemHovered() aware of this (previously it wasn't) with opt-out flag ImGuiHoveredFlags_AllowWhenOverlappedByItem.
  • Renamed various xxxAllowItemOverlap flags to xxxAllowOverlap.
  • Other things, see link above.

@ocornut ocornut closed this as completed Jun 28, 2023
ocornut added a commit that referenced this issue Jul 5, 2023
…#3909, #517)

+ Merge some shallow changes from range-select branch.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants