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

Clipper & Vertical scrolling. Unsure how to scroll to active item when it's no longer visible? #3578

Closed
vexe opened this issue Nov 7, 2020 · 5 comments

Comments

@vexe
Copy link

vexe commented Nov 7, 2020

Version/Branch of Dear ImGui

Version: 1.79 WIP (17803)
Branch: docking

Back-end/Renderer/Compiler/OS

Back-ends: imgui_impl_win32.cpp + imgui_impl_dx11.cpp
Compiler: MSVC
Operating System: Win7 64-bit & Win10 64-bit

My Issue/Question:

Context:
I basically have file fuzzy finder/searcher/browser thing going on in my text editor, the purpose is to navigate to the selected file upon enter or double click. I give it a list of files from the current directory, I apply a filter to that list using an input text, then I draw the results as a Selectable. The drawing is wrapped around ImGuiListClipper.Begin/End.

The text input should always be the item that has focus/navigation, I type in part of a file name I want, I get filtered results populated, I hit UP or DOWN arrow to move the 'Active Index' up or down. The 'Active Index' is in filtered space. (i.e. ActiveIndex of 0 refers to the first in the FILTERED list)

Problem:
When I move down past the last visible item in the clipper, the window does not automatically scroll. I'm not sure what's the best way to do it.

I tried something like this at the end of the clipper loop:

if (Clipper.StepNo==3 &&
   State->ActiveIndex >= Clipper.DisplayEnd)
{
    ImGui::SetScrollHere();
}

And this at the beginning of it:

if (Clipper.StepNo==3 &&
    State->ActiveIndex <= Clipper.DisplayStart)
    {
         ImGui::SetScrollHere();
    }

It kinda works, but it's pretty ugly, uses internal state and it messes up with mouse wheel scrolling, as I scroll the mouse the "Active Index" becomes out of range, and it keeps trying to SetScrollHere...

Note that previously I didn't use a clipper and the problem was much easier to solve, just "if (ItemIndex==ActiveIndex && !ImGui::IsItemVisible()) ImGui::SetScrollHere();" obviously this doesn't work with the clipper.

Here's a video demonstrating the issue: https://youtu.be/ZNooiltKJIc

And here's my code: https://pastebin.com/yxXYLzjv (wanted you to see full code and not snippets but also bring down the noise in here, but if you prefer to have the code pasted here just let me know!)

I'm pretty sure there's an easier way to do this that I'm missing.
Any help is appreciated, thanks!

@vexe
Copy link
Author

vexe commented Nov 7, 2020

If I comment out all the SetKeyboardFocusHere (that were forcing the text field to have navigation/focus) and click on one of the selectables and then move up/down (which uses imgui default navigation), the windows scrolls just fine... Here's a video https://youtu.be/ej-1tOPPzQ0

This is basically the exact behavior I want, but I want to force focus to be always on the text field, and use my own custom ActiveIndex to indicate which is the selected/highlighted item...

@ocornut
Copy link
Owner

ocornut commented Sep 7, 2021

Hello,

Sorry for my late answer. At first glance this look actually easy to handle on your end.
When using the clipper you won't iterate through all your items so SetScrollHere() can't work.
However you can probably do this after finishing your loop:

float item_pos_y = clipper.StartPosY + clipper.ItemsHeight * (State->ActiveIndex * 0.5f);
ImGui::SetScrollFromPosY(item_pos_y - ImGui::GetWindowPos().y);

The - ImGui::GetWindowPos().y is unfortunately because SetScrollFromPosY() use window-relative position (some awkward/legacy thing).

@DoomT-AliW
Copy link

DoomT-AliW commented Sep 7, 2021

Hey Omar this is vexe, just my work account.

The suggested fix has the same mouse wheel scrolling problem, I just can't scroll with the mouse anymore as it keeps overriding it with SetScrollFromPosY. Ideally this whole thing would run only if I try to navigate up/down the menu and the current item is not visible. Like I expect to be able to scroll freely with the mouse even making the active item invisible completely, then if I hit up/down, it snaps back into view if that makes sense.

Unless I put the code in the wrong spot? I put it after the loop, tried both before and after the Clipper.End()

@ocornut ocornut modified the milestones: v1.85, v1.86 Oct 12, 2021
ocornut added a commit that referenced this issue Nov 8, 2021
@ocornut
Copy link
Owner

ocornut commented Nov 8, 2021

@vexe @TTALIA
Sorry for late answer.
Recently I've reworked the scrolling functions.

From imgui_internal you can use:

void ScrollToItem(ImGuiScrollFlags flags = 0);
void ScrollToRect(ImGuiWindow* window, const ImRect& rect, ImGuiScrollFlags flags = 0);

In particular the flags that are interesting for you:

ImGuiScrollFlags_KeepVisibleEdgeY       = 1 << 1,       // If item is not visible: scroll as little as possible on Y axis to bring item back into view [default for Y axis for windows that are already visible]
ImGuiScrollFlags_KeepVisibleCenterY     = 1 << 3,       // If item is not visible: scroll to make the item centered on Y axis
ImGuiScrollFlags_AlwaysCenterY          = 1 << 5,       // Always center the result item on Y axis [default for Y axis for appearing window)

Using default is good as while the window is kept visible it'll use ImGuiScrollFlags_KeepVisibleEdgeY anyway.

SetKeyboardFocusHere() used to do the equivalent of ImGuiScrollFlags_AlwaysCenterY now it does that selection,
From 1.85 Changelog:

Nav: Fixed using SetKeyboardFocusHere() on non-visible/clipped items. It now works and will scroll
toward the item. When called during a frame where the parent window is appearing, scrolling will
aim to center the item in the window. When calling during a frame where the parent window is already
visible, scrolling will aim to scroll as little as possible to make the item visible. We will later
expose scroll functions and flags in public API to select those behaviors. (#343, #4079, #2352)`

That should allow you to request a "scroll enough to keep visible" on any Up/Down press.

In addition, I have reintroduced the clipper.ForceDisplayRangeByIndices() function (bce1ba4) which was dropped from another PR (per #3841 (comment)) as I believe it can be a useful tool for situation like yours.

I think between those two your issue is now fully fixable.

@ocornut ocornut closed this as completed Nov 8, 2021
ButtonHeck pushed a commit to ButtonHeck/imgui that referenced this issue Nov 10, 2021
actondev pushed a commit to actondev/imgui that referenced this issue Nov 26, 2021
@ocornut
Copy link
Owner

ocornut commented May 15, 2023

📣 FYI the function ForceDisplayRangeByIndices() was now renamed to IncludeRangeByIndices() in 1.89.6 WIP (commit ecb0aaa).

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

3 participants