Open michaelquigley opened 4 years ago
Interesting where the {0, 0}
point falls in the above screenshot. I ended up having to change the starting Y value to -375
to get closer to the upper left corner:
ImGui::BeginChild("child_2", {0, 0}, false, ImGuiWindowFlags_HorizontalScrollbar);
ImGui::TextUnformatted("child_2");
ImGui::GetWindowDrawList()->AddLine({0, -375}, {500, 500}, 0xFFFFFFFF);
ImGui::SetCursorPos({1500, 1500});
ImGui::TextUnformatted("hello");
ImGui::EndChild();
what is the best way to transform my coordinates into the space of the scrolled region?
ImDrawList positions are always in absolute coordinates. You can use GetWindowPos()
(window upper right corner) or GetCursorScreenPos()
(current layout position) as a reference point and that later one will handle scrolling.
From there you can apply any other transformation you like (scaling, offseting your points etc.).
It sounds like there is a 16-bit limitation on draw list size for some rendering back ends and configurations, so I'd like to be smart about clipping.
That's right, however note that it means you can draw 32768 lines without worrying, so that coarse clipping is more for performances reason. (or ~10922 lines if using <1.77 and thickness >1.0f) and you might not need to care.
I'm naively assuming this means having some sort of basic logic along the lines of:
Correct.
That's what I was looking for. Here is an animated example of a fuller implementation:
I have two side-by-size child windows, one with scrollbars and one without. Vertically scrolling child_2
will vertically scroll child_1
in unison. The intention is that child_1
will contain a "legend" for lanes of information shown in child_2
. Implemented a rough and simple splitter (stolen from https://github.com/ocornut/imgui/issues/125#issuecomment-135775009).
The full example looks like this:
static float w = 200.0f;
static float scroll_y = 0.0f;
ImGui::BeginChild("child_1", {w, 0.0f}, true, ImGuiWindowFlags_NoScrollbar);
auto draw_list = ImGui::GetWindowDrawList();
auto cursor = ImGui::GetCursorScreenPos();
auto legend = ImGui::GetWindowSize();
ImGui::TextUnformatted("child_1");
for(auto i = 1; i < 14; i++) {
draw_list->AddLine(cursor + ImVec2{5, i * 50.0f}, cursor + ImVec2{legend.x - 10, i * 50.0f}, 0xFFFFFFFF);
}
ImGui::SetCursorPos({0, 1500});
ImGui::SetScrollY(scroll_y);
ImGui::EndChild();
ImGui::SameLine();
cursor = ImGui::GetCursorPos();
ImGui::InvisibleButton("v_splitter", {8, ImGui::GetWindowSize().y - 10});
if(ImGui::IsItemActive()) {
w += ImGui::GetIO().MouseDelta.x;
}
auto button = ImGui::GetItemRectSize();
draw_list->AddRect(cursor, cursor + button, 0xFFFFFFFF);
ImGui::SameLine();
ImGui::BeginChild("child_2", {0, 0}, false, ImGuiWindowFlags_HorizontalScrollbar);
draw_list = ImGui::GetWindowDrawList();
cursor = ImGui::GetCursorScreenPos();
ImGui::TextUnformatted("child_2");
for(auto i = 1; i < 14; i++) {
draw_list->AddLine(cursor + ImVec2{5, i * 50.0f}, cursor + ImVec2{1495, i * 50.0f}, 0xFFFFFFFF);
}
draw_list->AddLine(cursor + ImVec2{50, 50}, cursor + ImVec2{1000, 1000}, 0xFFFFFFFF);
ImGui::SetCursorPos({1500, 1500});
scroll_y = ImGui::GetScrollY();
ImGui::EndChild();
Also using an operator on ImVec2
like this:
static inline ImVec2 operator+(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x + rhs.x, lhs.y + rhs.y); }
Is ImGui::SetCursorPos
the best approach for communicating the final size of the child window's content region to ImGui?
I tried to update child_1
's Y scroll position after I grab it in child_2
, but the ImGui::BeginChild
/ImGui::EndChild
for child_1
placed after child_2
, did not seem to work. Known issue? The implementation above is off by 1 frame, but that should be totally livable.
I will also look into 32-bit draw lists (my backend is glfw
+opengl3
), as I suspect I'll end up needing more than 16-bits eventually.
Is ImGui::SetCursorPos the best approach for communicating the final size of the child window's content region to ImGui?
Yes.
I tried to update child_1's Y scroll position after I grab it in child_2, but the ImGui::BeginChild/ImGui::EndChild for child_1 placed after child_2, did not seem to work. Known issue? The implementation above is off by 1 frame, but that should be totally livable.
The effect SetScrollXXX
function are deferred until next frame is you are already
Note that there is a SetNextWindowScroll()
function in imgui_internal.h
you may be able to use. I think it could be moved to imgui.h.
Note that you could technically also just handling scrolling/panning yourself and ignore the provided scrollbars.
The effect SetScrollXXX function are deferred until next frame is you are already Note that there is a SetNextWindowScroll() function in imgui_internal.h you may be able to use. I think it could be moved to imgui.h.
Ahh... well, then I'm off by 2 frames. With child_2
driving the positioning, and child_1
being to the left of child_2
and rendered first, I'm not seeing how I can not have child_1
's Y position off by 1 frame, unless another BeginChild
/EndChild
after child_2
works (it didn't work at all for me).
In other words:
ImGui::BeginChild("child_1");
ImGui::EndChild();
ImGui::BeginChild("child_2)";
ImGui::EndChild();
ImGui::BeginChild("child_1");
ImGui::SetScrollY(...);
ImGui::EndChild();
...did not work for me at all. child_1
did not scroll in unison with child_2
(it did not scroll at all).
What if I draw child_2
first using SetCursorPosition
in the parent, and then draw child_1
? I would have to do my own child sizing, but that might let me draw child_1
after child_2
?
Note that you could technically also just handling scrolling/panning yourself and ignore the provided scrollbars.
I've taken that approach in other interfaces that I've built using ImGui... but those designs didn't need scrollbars at all. This design wants scrollbars on that second child, and I feel like I would end up having to re-create a bunch of scrollbar sizing and position code that the child implementation already provides? I'll try mocking that up too, for the learning if nothing else.
Thank you for the assistance!
Is
ImGui::SetCursorPos
the best approach for communicating the final size of the child window's content region to ImGui?
Hi! This is Omar pretending to a be a bot!
The quoted message (posted 2020/07/17) suggested an incorrect usage pattern which we had to obsolete in 1.89. An item (even a Dummy()
) needs to be submitted to extend parent window/cell boundaries. See #5548 for details.
(Sorry for hijacking this thread, but I think this is sort of related)
I am doing my scrollable area by first creating a Top level window with forced scrollbars, and then adding a child window with the drawing area size I want. I later add an InvisibleButton but I am not really doing anything interactive yet.
I got the above working, rendering and scrolling around works fine. Now for optimization reasons and for example implementing a zoom which would keep the focus area visible, I would like to know what part of my child window is actually visible through the scrollable view?
Is there a direct API for this info, or should I (can I?) just calculate it from the child window size + scroll information?
Replying to my own question, I have found that using GetContentRegionAvail() for area size and GetCursorScreenPos()+GetScrollX/Y() for area corner, seems to give me sufficiently accurate information for my use.
Zooming in and out works well enough after I implemented it so that the zoom slider is used for scrollbar positioning immediately but it is delayed by one frame before it is used in actual rendering. This causes scrollbar change which happens in next frame to be synchronized with rendering. (Noticed the scrollbar behaviour from Omar's comment)
Version:
1.76
Back-ends:imgui_impl_glfw
+imgui_impl_opengl3
Compiler: MSVC 19 Operating System: Windows 10I haven't found a concise example of how best to deal with this situation. I think this might be a useful general use case to document here as an issue.
If I have a child window, and I want to do custom drawing on it with
ImDrawList
calls, what is the best way to transform my coordinates into the space of the scrolled region?It sounds like there is a 16-bit limitation on draw list size for some rendering back ends and configurations, so I'd like to be smart about clipping. I've noticed the term "coarse clipping" mentioned in a few examples and issues... I'm naively assuming this means having some sort of basic logic along the lines of:
If that's not what we mean by coarse clipping, what do we mean?
Is there anything else that should be done to make this sort of drawing on a scrolled child window more efficient?
I'd like to be able to use a child window as a "viewport" showing a region of a larger drawing.
My un-transformed draw list example looks like this:
The line drawn by
AddLine
does not scroll with the position of the scrollbars on the child window: