-
-
Notifications
You must be signed in to change notification settings - Fork 10.5k
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
Windows should allow dragging to scroll their contents #3379
Comments
EDIT See last version from 2023-08-15: #3379 (comment)Hello, Right now a possible workaround on a per-window basis: #include "imgui_internal.h"
void ScrollWhenDraggingOnVoid(const ImVec2& delta)
{
ImGuiContext& g = *ImGui::GetCurrentContext();
ImGuiWindow* window = g.CurrentWindow;
bool hovered = false;
bool held = false;
if (g.HoveredId == 0) // If nothing hovered so far in the frame (not same as IsAnyItemHovered()!)
ImGui::ButtonBehavior(window->Rect(), window->GetID("##scrolldraggingoverlay"), &hovered, &held, ImGuiButtonFlags_MouseButtonLeft);
if (held)
{
window->Scroll.x += delta.x;
window->Scroll.y += delta.y;
}
} Call before End(), e.g. only scroll vertically:
[[
]] |
…on the edge of scroll limits. (#3379) + Demo: Rename "Layout" to "Layout & Scrolling".
EDIT See last version from 2023-08-15: #3379 (comment)FYI have fixed to snapped issue mentioned above, so I can confirm that this work-around will work: #include "imgui_internal.h"
void ScrollWhenDraggingOnVoid(const ImVec2& delta, ImGuiMouseButton mouse_button)
{
ImGuiContext& g = *ImGui::GetCurrentContext();
ImGuiWindow* window = g.CurrentWindow;
bool hovered = false;
bool held = false;
ImGuiButtonFlags button_flags = (mouse_button == 0) ? ImGuiButtonFlags_MouseButtonLeft : (mouse_button == 1) ? ImGuiButtonFlags_MouseButtonRight : ImGuiButtonFlags_MouseButtonMiddle;
if (g.HoveredId == 0) // If nothing hovered so far in the frame (not same as IsAnyItemHovered()!)
ImGui::ButtonBehavior(window->Rect(), window->GetID("##scrolldraggingoverlay"), &hovered, &held, button_flags);
if (held && delta.x != 0.0f)
ImGui::SetScrollX(window, window->Scroll.x + delta.x);
if (held && delta.y != 0.0f)
ImGui::SetScrollY(window, window->Scroll.y + delta.y);
} Needs to be called JUST before ImGui::End(). ImVec2 mouse_delta = ImGui::GetIO().MouseDelta;
ScrollWhenDraggingOnVoid(ImVec2(0.0f, -mouse_delta.y), ImGuiMouseButton_Middle);
ImGui::End(); I think we can probably easily implement something globally as desired, it's mostly a matter of design. If we have config options to scroll this way it may hinder other inputs. I'll have to think of what is the safest way to design that option. |
Is this method only available in the root window? |
@YuDang1024 I have confirmed that the code in #3379 works perfectly with child window. |
The code doesn't work with touch screens since hover never happens. As a result, scroll jumps back to original position every time I touch the window. |
My workaround:
|
@ocornut trying this on a touchscreen, your workaround works but only using two fingers. (Using mouseButtonLeft). How can I make it work with one finger? @PentagramPro 's version, on the other hand, didn't work for me at all. |
TODO: refactor scrollWhenDraggingOnVoid and windowDragScroll functions ocornut/imgui#3379
Hello I'm using this version: void ScrollWhenDragging(const ImVec2& aDeltaMult,ImGuiMouseButton aMouseButton)
{
ImGuiContext& g = *ImGui::GetCurrentContext();
if(g.MovingWindow!=nullptr)
{
return;
}
ImGuiWindow* window=g.CurrentWindow;
if(!window->ScrollbarX && !window->ScrollbarY) // Nothing to scroll
{
return;
}
ImGuiIO& im_io=ImGui::GetIO();
bool hovered = false;
bool held = false;
const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow;
bool window_highlight = (window_to_highlight && (window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight || (window->DockNode && window->DockNode == window_to_highlight->DockNode)));
ImGuiButtonFlags button_flags = (aMouseButton==0) ? ImGuiButtonFlags_MouseButtonLeft : (aMouseButton==1) ? ImGuiButtonFlags_MouseButtonRight : ImGuiButtonFlags_MouseButtonMiddle;
if( g.HoveredId==0 // If nothing hovered so far in the frame (not same as IsAnyItemHovered()!)
&& im_io.MouseDown[aMouseButton] // Mouse pressed
&& window_highlight // Window active
)
{
ImGui::ButtonBehavior(window->InnerClipRect,window->GetID("##scrolldraggingoverlay"),&hovered,&held,button_flags);
if((window->InnerClipRect.Contains(im_io.MousePos)))
{
held=true;
}
else if(window->InnerClipRect.Contains(im_io.MouseClickedPos[aMouseButton]) ) // If mouse has moved outside window, check if click was inside
{
held=true;
}
else
{
held=false;
}
}
if (held && aDeltaMult.x != 0.0f)
ImGui::SetScrollX(window, window->Scroll.x+ aDeltaMult.x*im_io.MouseDelta.x);
if (held && aDeltaMult.y != 0.0f)
ImGui::SetScrollY(window, window->Scroll.y+ aDeltaMult.y*im_io.MouseDelta.y);
}
And calling
just before End() or EndChild(). I tried it in PC, Android and iOS with a custom back end. For my use case works perfectly but in general you need a little care if you start dragging in a button or something similar. It would be perfect that ImGui had an option to disable interaction temporarily while the content is scrolling. @PentagramPro I also had this problem, but it is fixed in imgui 1.89.5 calling
|
I've found myself needing this touch support for scrolling - particularly inside of combo boxes since the slider width can be very small. The scrolling is being extremely temperamental, particularly at high FPS - the held value is resetting to 0 even if the mouse-button is continually held down, thus stopping scrolling entirely. This has been done using 1.89.4. Each mouse button "press and hold" registers as held = 1 for two frames and then resets to 0. Verified that "out-held" is being replaced by held which defaults to 'false'. @ocornut has this been deprecated by further updates - or are we missing a particular flag in the solution? |
I don't understand. Your code checks
@zcfearns You can and should probably increase
I used "Debug Log->ActiveId" to investigate this: [01739] SetActiveID() old:0x00000000 (window "") -> new:0x1C3EFF00 (window "Dear ImGui Demo")
[01741] NewFrame(): ClearActiveID() because it isn't marked alive anymore!
[01741] SetActiveID() old:0x1C3EFF00 (window "Dear ImGui Demo") -> new:0x00000000 (window "") In 1.88 had this breaking change:
I have fixed the snippet from #3379 (comment) by changing it to: #include "imgui_internal.h"
void ScrollWhenDraggingOnVoid(const ImVec2& delta, ImGuiMouseButton mouse_button)
{
ImGuiContext& g = *ImGui::GetCurrentContext();
ImGuiWindow* window = g.CurrentWindow;
bool hovered = false;
bool held = false;
ImGuiID id = window->GetID("##scrolldraggingoverlay");
ImGui::KeepAliveID(id);
ImGuiButtonFlags button_flags = (mouse_button == 0) ? ImGuiButtonFlags_MouseButtonLeft : (mouse_button == 1) ? ImGuiButtonFlags_MouseButtonRight : ImGuiButtonFlags_MouseButtonMiddle;
if (g.HoveredId == 0) // If nothing hovered so far in the frame (not same as IsAnyItemHovered()!)
ImGui::ButtonBehavior(window->Rect(), id, &hovered, &held, button_flags);
if (held && delta.x != 0.0f)
ImGui::SetScrollX(window, window->Scroll.x + delta.x);
if (held && delta.y != 0.0f)
ImGui::SetScrollY(window, window->Scroll.y + delta.y);
} Usage ImVec2 mouse_delta = ImGui::GetIO().MouseDelta;
ScrollWhenDraggingOnVoid(ImVec2(0.0f, -mouse_delta.y), ImGuiMouseButton_Middle);
ImGui::End(); (or EndChild()) |
@zcfearns I did not test my version with combo, but it does work Ok, It has the problem that it also scrolls the parent window, I have to look at it. I have only tested it in Windows, Android should be the same. @ocornut the issue is that if I start scrolling in a widget and end also in it, then the interaction happens. What I wanted to say is that if I start scrolling, when I stop that should not happen. I have recorded a gif to show the issue. I start scrolling dragging in a check box, if I stop dragging in that check box its value is changed. I'm using 1.89.6 WIP 18953. |
Are you sure you are using the code I posted? My code shouldn’t do that, you cannot start dragging over a checkbox. |
No, I was using the code I posted on May 6. The version posted at that time did not work for me. I have tested with the new version and it works fine. As you said, it does not allow to start draggin over a checkbox (or butoo, tree text....) but sometimes this behaviour is more clunsy as you start dragging and nothing happens and you have to reposition the mouse and start again. For example, in the case of the combo box list it does not scroll because everything is a widget. With the version I'm using you can start dragging and it works. The problem is that you can end draggin over the same widget and activate it. For me, the ideal behaviour would be that you press anywhere and if you drag more that a certaint amount, then the widgets do not react to the end dragging. Don't know if this can be done easily. |
Is there any way to achieve this without using any internal stuff? Since cimgui doesn't generate the wrappers for internals, it seems like this solution is only usable from C++, otherwise you're basically screwed? |
The solution is that your binding should generate internal stuff. AFAIK cimgui does it with an option, but it ihmo very awkwardly includes the output in the same cimgui.h file instead of eg a cimgui_internal.h. I find that rather bad and it is one of the reason that prompted us to develop “dear bindings”, although it doesn’t support internal yet. |
That said you can perfectly manually include that code in a C++ source file and make it accessible to your C sources. In other words if you can link with cimgui you can perfectly generate one more function manually. |
That's true, unfortunately though, for a more clean solution, cimgui is only half of the puzzle, since there is yet another binding from cimgui-generated C code to the target language. In my case, it's ImGui.NET, which doesn't really support internals either. Which is, obviously, a problem completely outside of the Dear ImGui domain, but this is just to say - using even basic internal stuff from outside C++ is a major PITA, involving vendoring multiple packages, hacking stuff together, and then maintaining all that, which is something no one is really going to do unless it's some absolutely critical functionality. For most non-C++ projects, internals are an automatic no-go, so it's often not a solution... At least until most bindings support internals. (P.S. The output of 'dear bindings' looks sooo clean! I'm getting a strong itch to start making a new wrapper for .NET based on that.) |
That’s the direction to head toward. It makes me want to add more cool stuff in internals until bindings and pipelines catch up :) i find it really regrettable that eg updating/building imgui.net yourself is not an obvious/trivial task, but if more people head into doing that it it’s more likely for that path to be improved and streamlined until it becomes trivial. |
@ocornut , I'm using the code you posted and it's working wonderfully. However, it seems to disable table right-click context menus ( My solution was to add an extra |
@ZimM-LostPolygon FYI adding new cimgui externs to C# is actually pretty easy now with You can see this project I'm working on for an example: https://github.com/b-effort/b_led/blob/main/b_led/Interop/cimgui.cs |
Is this updated version meant to work with ListBox? I'm calling ScrollWhenDraggingOnVoid() right before EndListBox() but can't get it to do anything. |
In addition to having a scrollbar, add methods to a Window to allow dragging to scroll contents, as if you were using a browser on mobile.
The text was updated successfully, but these errors were encountered: