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

Pinned Tooltip Windows #1345

Open
mikesart opened this issue Sep 29, 2017 · 13 comments
Open

Pinned Tooltip Windows #1345

mikesart opened this issue Sep 29, 2017 · 13 comments

Comments

@mikesart
Copy link

I would like windows with the following properties:

  • Render on top of a window, but don't take focus and don't activate.
  • When I mouse click on them and drag, they move.
  • All other events (mouse hovering, right clicking, etc go to window underneath).

I'm planning on using them for "pinned tooltip" behavior like in the below picture. Hover over some event, hit a hotkey, that tooltip is now a "pinned tooltip" and you can view / compare to other events in the main tooltip. I also think these will be useful for displaying floating information about other things.

I've got this working using the code below (which pretty much abuses tooltips). I tried using popups and a few other techniques and couldn't get anything working.

The only issue I've got is the z-ordering, but I think I can get everything else working like I want without imgui changes.

My question would be: this ok? Is there a better way to accomplish this behavior?

Thanks!

image

void imgui_set_tooltip( const char *name, const ImVec2 pos, const char *str, rect_t *rc )
{
    ImGuiIO& io = ImGui::GetIO();
    const ImVec2 mousepos_orig = io.MousePos;

    ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip |
            ImGuiWindowFlags_NoTitleBar |
            ImGuiWindowFlags_NoMove |
            ImGuiWindowFlags_NoResize |
            ImGuiWindowFlags_NoSavedSettings |
            ImGuiWindowFlags_AlwaysAutoResize;

    io.MousePos = pos;

    ImGui::Begin( name, NULL, flags );
    ImGui::Text( "%s", str );

    if ( rc )
    {
        ImVec2 wpos = ImGui::GetWindowPos();
        ImVec2 size = ImGui::GetWindowSize();

        // Return window dimensions so higher level can handle click / moving window
        *rc = { wpos.x, wpos.y, size.x, size.y };
    }

    ImGui::End();

    io.MousePos = mousepos_orig;
}
@mikesart
Copy link
Author

I have this code checked into my project.

imgui_set_tooltip() in gpuvis_utils.cpp
TraceWin::graph_render() has window moving code: gpuvis_graph.cpp

Going to generalize it to handle more windows, but otherwise it's working great. Only issue I'm seeing is window z-order. I'll take a look at that at some point, but it's not that big deal right now.

@ocornut ocornut added the popups label Sep 30, 2017
@ocornut
Copy link
Owner

ocornut commented Sep 30, 2017

Mike,

It looks like the property you need the most is to keep that window over the parent window at all times (even if the parent window is interacted with).

But do you actually need inputs to go through? "All other events (mouse hovering, right clicking, etc go to window underneath).". I am surprised you want/need that, I'd find it odd to have a mouse click go through.

Some thoughts..

A) It may be possible to introduce a specific type of window for this, e.g. using Child+Tooltip flag might be appropriate.

B) A more generic solution, if we can ignore the inputs constraint, may boils down to the requirement that we want some form of finer control over the z-order. I've been reluctant to expose this just using absolute Z values because I think it'd be putting a lot of burden on the application. But perhaps expressed as a series of constraint (e.g. Window B always above Window A, or Window B stays between Z0 and Z1) it would be more flexible and appropriate. I haven't thought about it further than that for now.

I'll try to mess around to see if A) is doable without too much trouble.

(Linking to #983 #1328 for later ref)

@ocornut
Copy link
Owner

ocornut commented Sep 30, 2017

It seems to work the hacky proof-of-concept patch below.

Use in the context of the parent window.

ImGui::Begin("Child Tooltip?", NULL, ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_AlwaysUseWindowPadding);
ImGui::Text("I am a child tooltip");
ImGui::EndChild();

If you want to give it a test.
I'm pretty sure there will be other issues, now and later, and depending on them I'm not yet sure this is something I would want to maintain. But I suggest you try this locally and if it seems to work without too much complication, I don't mind committing the changes in imgui.cpp (I'll add extra comments + fixes to allow the "Child Tooltip" to have its own childs, and won't expose that particular Child+Tooltip combination publicly for now).

Let me know!

patch.zip

diff --git a/imgui.cpp b/imgui.cpp
index 849a05f..72be861 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -2264,9 +2264,11 @@ void ImGui::NewFrame()
         IM_ASSERT(g.MovedWindow->MoveId == g.MovedWindowMoveId);
         if (g.IO.MouseDown[0])
         {
-            g.MovedWindow->RootWindow->PosFloat += g.IO.MouseDelta;
+            // FIXME: Won't work if the tooltip itself has child.
+            ImGuiWindow* actual_moved_window = ((g.MovedWindow->Flags & ImGuiWindowFlags_ChildWindow) && (g.MovedWindow->Flags & ImGuiWindowFlags_Tooltip)) ? g.MovedWindow : g.MovedWindow->RootWindow;
+            actual_moved_window->PosFloat += g.IO.MouseDelta;
             if (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f)
-                MarkIniSettingsDirty(g.MovedWindow->RootWindow);
+                MarkIniSettingsDirty(actual_moved_window);
             FocusWindow(g.MovedWindow);
         }
         else
@@ -4115,7 +4117,7 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us
         window->DrawList->Clear();
         window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID);
         ImRect fullscreen_rect(GetVisibleRect());
-        if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_ComboBox|ImGuiWindowFlags_Popup)))
+        if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_ComboBox|ImGuiWindowFlags_Popup|ImGuiWindowFlags_Tooltip)))
             PushClipRect(parent_window->ClipRect.Min, parent_window->ClipRect.Max, true);
         else
             PushClipRect(fullscreen_rect.Min, fullscreen_rect.Max, true);
@@ -4208,7 +4210,7 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us
             window->OrderWithinParent = parent_window->DC.ChildWindows.Size;
             parent_window->DC.ChildWindows.push_back(window);
         }
-        if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup))
+        if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_Tooltip)))
         {
             window->Pos = window->PosFloat = parent_window->DC.CursorPos;
             window->Size = window->SizeFull = size_on_first_use; // NB: argument name 'size_on_first_use' misleading here, it's really just 'size' as provided by user passed via BeginChild()->Begin().
@@ -4240,7 +4242,7 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us
         }
 
         // Position tooltip (always follows mouse)
-        if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api)
+        if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow))
         {
             ImVec2 ref_pos = g.IO.MousePos;
             ImRect rect_to_avoid(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24, ref_pos.y + 24); // FIXME: Completely hard-coded. Perhaps center on cursor hit-point instead?

@mikesart
Copy link
Author

I tried your patch and it worked well but there are issues. One is the layout - appears to still be making room for the window. So if I draw graph, draw the "child tooltip", then draw the event list the tooltip shows up on top of the graph but there is space between graph and event list equal to height of tooltip. Also for my graph I'm checking if the mouse is over the window, has focus, and mouse click for panning, and it looks like I'd still need code to disable that cause right now clicking + moving tooltip window also pans the graph.

Also played around with this feature a bit and I think you're right, I probably don't need inputs to go through to the below window. It's quite easy for me to get either behavior with the current tooltip windows though.

B) A more generic solution, if we can ignore the inputs constraint, may boils down to the requirement that we want some form of finer control over the z-order. I've been reluctant to expose this just using absolute Z values because I think it'd be putting a lot of burden on the application. But perhaps expressed as a series of constraint (e.g. Window B always above Window A, or Window B stays between Z0 and Z1) it would be more flexible and appropriate. I haven't thought about it further than that for now.

I think this is the real issue I need solved. I want the "real" tooltip to show up on top of the pinned tooltips, and I'd like the tooltip that's being moved to be on top, and both of these are at the whim of that qsort function.

Maybe you could come up with what you think a good imgui quality API for this might look like and I could write some code for it and we iterate a bit? If you think that's useful, otherwise I'll hack something small in on my side. :)

Thanks a ton Omar!

@mikesart
Copy link
Author

mikesart commented Oct 2, 2017

I got everything working with your patch and pushed it out. I don't have to worry about z-order right now because I only have one tip window and your child+tip windows show up under that. Seems like everything is working great. Thank you very much!

screenshot_2017-10-02_13-45-18

@ocornut
Copy link
Owner

ocornut commented Oct 11, 2017

FYI I realize this is essentially the behavior Win32 refer to as "Tool window".
Should eventually integrate that as a main line feature.

@mikesart
Copy link
Author

Yeah, this is really useful. Couple issues I think would need solving before it's a main line feature are z-order and widgets don't work. I wound up writing my own close button. Working great in gpuvis to display text right now though.

@ocornut
Copy link
Owner

ocornut commented Oct 27, 2017

FYI posting this as it is related (but different from the "pinned" tooltips), if people want multiple tooltips that you want to manually position it is possible to do:

ImGui::Begin("Test Multi Tooltip");
ImGui::Button("Hello");
if (ImGui::IsItemHovered())
{
    ImVec2 m = ImGui::GetIO().MousePos;
    ImGui::SetNextWindowPos(ImVec2(m.x - 10, m.y));
    ImGui::Begin("1", NULL, ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar);
    ImGui::Text("FIRST TOOLTIP");
    ImGui::End();

    ImGui::SetNextWindowPos(ImVec2(m.x + 100, m.y));
    ImGui::Begin("2", NULL, ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar);
    ImGui::Text("SECOND TOOLTIP");
    ImGui::End();
}
ImGui::End(); 

The ImGuiWindowFlags_Tooltip flag is in theory marked as private but it'll probably keep working for a long while.

ocornut added a commit that referenced this issue Jan 14, 2018
…otWindow. Followup to 3849bb4. Comments + adding a local to ease patch #1345.
ocornut added a commit that referenced this issue Jan 19, 2018
… This should not affect the patch used for #1345 as the RootWindow for Child+Tooltip window points to itself now.
ocornut added a commit that referenced this issue Jan 19, 2018
… feature needs substancially more work but this is enough for simplest cases. (#1345)
@ocornut
Copy link
Owner

ocornut commented Jan 19, 2018

I have applied the patch discussed here as an undocumented feature. I think the full feature is desirable down the line but the patch has severe limitation (namely that you won't have any z-order control if you have multiple windows with tooltip+child flags.).

@mikesart
Copy link
Author

mikesart commented Jan 19, 2018

Got it merged into gpuvis. The only odd behavior is after I click on a pinned tooltip and move it, other tooltips don't pop up when I hover over the event list, etc. Kinda like the pinned tooltip took focus? Not a big deal though - I'm definitely gonna use your code to avoid the merge hassles. Thank you very much for getting this in Omar!

mikesart added a commit to mikesart/gpuvis that referenced this issue Feb 1, 2018
Omar:

As per ocornut/imgui#1345

> Got it merged into gpuvis. The only odd behavior is when I click on a
pinned tooltip and move it, other tooltips don't pop up when I hover
over the event list, etc. Kinda like the pinned tooltip took focus? Not
a big deal though - I'm definitely gonna use your code to avoid the
merge hassles. Thank you very much for getting this in Omar!

Hmm indeed with the patch it is taking focus while previously it would
pass focus to the parent window which happened to be the graph and event
list.

I think in void graph_info_t::init()
You can replace
   mouse_pos = ImGui::IsRootWindowOrAnyChildFocused() ?
 with
    mouse_pos =
ImGui::IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows) ?

And in bool TraceWin::eventlist_handle_mouse()
You can remove the test for ImGui::IsRootWindowOrAnyChildFocused()

But I'm not really sure why those tests are there in the first place,
there are a few custom focus-related code, might break something.

Note that IsItemHovered(), IsWindowFocused(), IsWindowHovered() now have
flags with more options, and they are replacing lots of the previous
entry points
    static inline bool  IsRootWindowOrAnyChildFocused()       { return
IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows); }

(Technically obsolete but will redirect for a while. If you enable
IMGUI_DISABLE_OBSOLETE_FUNCTIONS in imconfig.h you'll see them. Attached
small patches to correct uses of obsolete stuff, apart from those two
calls to IsRootWindowOrAnyChildFocused() above)
@rafaelkrause
Copy link

As alternative, you can set mouse position, than restore it.
`

	//Save Mouse Position
	ImVec2 m = ImGui::GetIO().MousePos;
	//Set Mouse Position
	ImGui::GetIO().MousePos.x = 100;
	ImGui::GetIO().MousePos.y = 100;	
	ImGui::BeginTooltip();
	ImGui::Text("Fixed Tool Tip");
	ImGui::End();
	//Restore Mouse Position
	ImGui::GetIO().MousePos = m;

`

@ocornut
Copy link
Owner

ocornut commented Sep 30, 2019

@rafaelkrause Please note you can use SetNextWindowPos(ImVec2(100,100)) for the same effect as what your code snippet does. This is however different from Mike's discussion as the "pinned tooltip" pattern also those windows to be moved.

@slajerek
Copy link

slajerek commented Oct 12, 2024

Is there a working example that works with most recent ImGui? I see here some patches, considering that topic is quite old I would prefer to check with you guys first what's the correct way to make a regular popup pinned (Context menu in this case)? Would be also good to add this to ImGui demo code.

I see this in the code

const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345)

could you please confirm how to use it?

In ideal world I'd like to have a flag that displays a small "pin" icon available in the context menu e.g. in top-right corner to keep that popup always visible and moveable - thus, convert that to "tool window" as @ocornut mentioned.

Note, I thought to add this myself, i.e. whenever pin button is pressed to change that context menu into a regular window with Begin/End, however I guess there might be better way to do that after reading this topic.

Thanks in advance.

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

No branches or pull requests

4 participants