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

Virtual scrollable area. #8215

Open
cyberkm opened this issue Dec 8, 2024 · 5 comments
Open

Virtual scrollable area. #8215

cyberkm opened this issue Dec 8, 2024 · 5 comments

Comments

@cyberkm
Copy link

cyberkm commented Dec 8, 2024

Version/Branch of Dear ImGui:

1.91 main branch

Back-ends:

glfw + opengl

Compiler, OS:

Windows/Clang

Full config/build information:

No response

Details:

Virtual Scrollable Area

I am working with an enormous scene rendered using orthographic projection. The scene is so large that it exceeds the precision limits of floating-point numbers, requiring custom streaming and paging techniques to determine what gets rendered at any given time. The scene is rendered onto a fixed-size texture, which occupies an entire child element and displays a specific portion of the scene.

Panning is applied directly to the scene using orthographic projection. Users can move the view by panning the scene (left, right, up, or down) via arrow keys and zoom in or out by adjusting the orthographic scale with the mouse wheel.

Given the precise dimensions of the scene and the applied scaling factor, I aim to add vertical and horizontal scrollbars to the child element containing the texture. These scrollbars will provide an intuitive way to navigate the entire scene space. However, due to the immense size of the scene, relying on standard scrollbar mechanisms or dummy objects results in floating-point precision issues. To address this, I plan to implement a custom, "normalized" scrollbar system that maps user interactions to the scene's orthographic panning and scaling, ensuring smooth and accurate navigation across the entire scene.

Does anyone know how to effectively implement this functionality while avoiding floating-point precision issues?

@ocornut
Copy link
Owner

ocornut commented Dec 9, 2024

ScrollbarEx() internally was implemented using 64-bit integers partly to address it, but we haven't really went the last mile of connecting all the glue.

First of all: Scrolling is mostly a way to (1) offset initial cursor position + (2) interface with scrollbars and other standardized functions/features related to scrolling. But technically you could decide to ignore imgui concept of Scrolling and implement your own. That said, it would probably be healthy that we improve the current system further instead of pushing out on its edge.

One reason I was reluctant to make the stored window->Scroll values use double instead of float, is that the layout system would probably fail in several other places when we start using large values, and I am reluctant to switch everything to double. But I presume if we switch window->Scroll to double even tho it won't solve everything, it would facilitate some use cases such as yours?

(I was also partly worried about introducing doubles internally, because I don't know if it may overly affect some of our lower-end targets (e.g. perhaps some low-end targets would need a software emulation lib for double etc. but I'm only guessing here). Right now the only double we forcefully use internally are for storing absolute time value, so we do have something. I suppose we can use a typedef for those cases if needed)

I think the first tangible step would to be either (A) switch scrolling values to double OR (B) provide a proof of concept for submitting your own scrollbars.

(Note for myself: The end goal would be to massage the current window->DecoOuterSizeXXX code which is currently a bit brittle and hard-coded, toward allowing external/user code to modify it (very analogous to BeginViewportSidebar() but inside of a window). It would facilitate the use of status bars, remove the need for ImGuiWindowFlags_MenuBar bar etc. basically a generalization of "claim space from the non-scrolling menu layer". If you can claim that space easily then you can position custom widgets or call ScrollbarEx() in there trivially)

In the following post I will provide an easy workaround for (B) that doesn't require the desired refactor of window->DecoOuterSizeXXX stuff.

(For cross-linking: #4143, #3609, #2404, #543, but I don't think they would help you directly)

@cyberkm
Copy link
Author

cyberkm commented Dec 9, 2024

I think that even just being able to manually draw scrollbars when needed with custom values at the edge of the area is good enough, as the actual content of the area remains the same image with the same size. Only what I show inside the image moves and scales, and I want the scrollbars to correspond to it.
Unfortunately, I haven't found any way to draw scrollbars directly.

@ocornut
Copy link
Owner

ocornut commented Dec 9, 2024

In the following post I will provide an easy workaround for (B) that doesn't require the desired refactor of window->DecoOuterSizeXXX stuff.

Those are custom large-range scrollbars that don't rely/alter window->Scroll values directly:

#include "imgui_internal.h"

ImGui::Begin("Custom scrollbar");

float scrollbar_size = ImGui::GetStyle().ScrollbarSize;

ImGuiWindow* parent_window = ImGui::GetCurrentWindow();
static ImS64 scroll[2] = { 0, 0 };
ImS64 scroll_max[2] = { 1000000000000, 1000000000000 };
ImS64 scroll_visible_size[2] = { (ImS64)(ImGui::GetContentRegionAvail().x - scrollbar_size), (ImS64)(ImGui::GetContentRegionAvail().y - scrollbar_size) };

ImGui::GetIO().ConfigScrollbarScrollByPage = false; // You may want to disable this, or not?
parent_window->ScrollbarSizes.x = parent_window->ScrollbarSizes.y = scrollbar_size; // Hack to use GetWindowScrollbarRect()
for (int axis = 0; axis < 2; axis++) // ImGuiAxis_X then ImGuiAxis_Y
{
    ImRect scrollbar_rect = ImGui::GetWindowScrollbarRect(parent_window, (ImGuiAxis)axis);
    ImGuiID scrollbar_id = ImGui::GetWindowScrollbarID(parent_window, (ImGuiAxis)axis);
    ImGui::ScrollbarEx(scrollbar_rect, scrollbar_id, (ImGuiAxis)axis, &scroll[axis], scroll_visible_size[axis], scroll_max[axis]);
}
parent_window->ScrollbarSizes.x = parent_window->ScrollbarSizes.y = 0.0f; // Restore modified value
ImGui::GetIO().ConfigScrollbarScrollByPage = true;

// Scrolling region use remaining space
ImGui::BeginChild("ScrollingRegion", ImVec2(-scrollbar_size, -scrollbar_size), ImGuiChildFlags_Borders);
ImGui::Text("%lld,%lld", scroll[0], scroll[1]);
ImGui::EndChild();

ImGui::End();

image

There's a bug in this version, both scrollbars will overlap in the bottom-right corner, as GetWindowScrollbarRect() relies on window->InnerRect which implicitly gets moved by the other scrollbar.
It may be simpler to reimplement a custom version of GetWindowScrollbarRect() and then you don't have to temporarily modify window->ScrollbarSizes[].

As for setting io.ConfigScrollbarScrollByPage : you may decide you want a different behavior for this specific window vs other windows in your apps, hence potentially setting it in temporarily.

@ocornut
Copy link
Owner

ocornut commented Dec 9, 2024

...and you may want to remove the ImGuiChildFlags_Borders flag in your actual code.

I'd like to keep it open as a reminder that I want to promote it toward being simpler and potentially not using internal API. I should at some point have a pass at merging/closing the other similar issues and move the information to a single one.

I also pushed a small tweak 3f3c62a to clarify the last parameter of ScrolllbarEx() and make it default to zero, so you may make it zero.

@cyberkm
Copy link
Author

cyberkm commented Dec 9, 2024

Thank you so much. I've managed to make it work according to your example:)

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

2 participants