Hi,
I guess this is more of a general question of how I can accomplish this. Imagine that you want to show disassembly at a given address (say 0x500) What one usually do is the get a bunch of instructions before and after that so you perhaps have a range of 0x400 - 0x600 to show the surrounding instructions. Then If the user scrolls up one will fetch more instructions (from 0x300, 0x200 and so on)
What I'm getting at is I wonder how to implement this in ImGui? I can show the range 0x400 - 0x600 of course with no problem but I would need a scrollbar that the user can drag so I can fetch more instructions either before or after the range.
I guess this is really similar to showing a big text file as well (where you don't actually read the whole file to memory but try to fetch data on the fly as needed)
For some inspiration of how this could work is to do some debugging in VS, switch to disassembly, drag the scrollbar around a bit and watch how it behaves.
Cheers!
So suppose you have loaded 0x1000...0x4000 worth of disassembly and displaying 0x2200...0x2400
You could set the cursor to (0x2200-0x1000 == 0x1200), display 0x200 worth of contents, then set the cursor to (0x4000-0x1000 == 0x3000), the size of your content.
ImVec4 clip_rect = GetWindowDrawList()->clip_rect_stack.back();
in clip_rect.y and clip_rect.w you get the minimum and maximum visible bound of what's visible.
So say if clip_rect.w - GetWindowPos().y < frame_height then you are at the top of your disassembly, if clip_rect.y - GetWindowPos().y > content_height-frame_height then you are at the bottom.
You'll get an issue because holding mouse button on the scrollbar will always set your scroll to that % of the scroll range, so if you hold mouse at the top it will keep loading. We could improve the scrollbar to cope with resizing better.
What Visual appears to do is that it only sets the scroll % when you click or move the mouse, so holding doesn't reapply the scrolling until mouse move again (the threshold is set to like 1 pixel which is a little dodgy compared to how mouse threshold are usually handled, but works). That's also something we could do. For now what I would do on your application side to get started would be to use a timer to minimize reload. Bit odd but it would work and may feel more reliable than what Visual does.
With all the info above you should be able to get it to work.
Edited with fixes and for readability.
Thanks for a great reply :)
I will try this out within a few days and see how it works out.
Cheers!
I've done a few improvements as logged above.
You can now query the scroll position / max.
The scrollbar has been rewritten. Took a lot more time than expected to do it right (precision issues, etc.).
The grab now has a minimum size so it always shows even if you have thousands of lines.
When clicking on the grab it doesn't initially apply a scroll (it used to center the grab on the mouse position which was sort of destructive). From there movement are relative.
When clicking outside of the grab it does an absolute repositioning, but also clamp the grab if you clicked near the edges and switch to relative movement after that.
It's written in a way where it should kind of work ok if the window contents change while holding the grab, but some of the details are still undecided (should we lock the scroll in absolute pixels value (instead of %), overriding programmatic changes, and only updating the scroll if the mouse move?).
I'd appreciate if someone tried the new scrollbar, the change was sufficiently tedious that I might have broken something subtle (hopefully not).
Awesome! I will give it a try as soon as I can (hopefully tomorrow)
Closing this (hopefully all the info you need is there for when you need it, else please reopen)
FYI i have added a higher level helper ImGuiListClipper to handle the most common pattern of using CalcListClipping().
https://github.com/ocornut/imgui/commit/eb4ffd5dbd7f6f9f4260e5d66df2347e7ef12270
So code like
int displayStart, displayEnd;
ImGui::CalcListClipping(lines_count, lines_height, &displayStart, &displayEnd);
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + (displayStart * lines_height));
for (int i = displayStart; i < displayEnd; ++i)
[...]
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ((lines_count - displayEnd) * lines_height));
Can become
ImGuiListClipper clipper(lines_count, lines_height);
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; ++i)
[...]
clipper.End();
CalcListClipping() is still available as the lower level function.
I see that I missed this! Thanks for adding it :)
I will try it out now for my Scintilla impl as I currently have no way of scrolling the text (scrolling the text using a scrollbar that is) as it doesn't behave like a regular ImGui widget I need to be able to set a line height so this should hopefully work fine for it.
Hi again,
So using this I can now scroll my text just fine which is great :)
I have one question though. Say that the user presses down / up / home / etc on the keyboard I want make sure that this is handled correctly (and that the scrollbar gets updated etc)
I tried to do:
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ItemHeight);
On the keypress but that doesn't seem to work correct. I wonder if you have any pointers on how to do it?
Thanks!
Friendly poke @ocornut :)
SetCursorPosY move your "draw position" for rendering subsequent widgets. If you want to scroll you probably don't want to touch the cursor position but rather the scroll position. A few calls are missing to freely manipulate the scroll position though. (working on it)
Yeah I use the ImGuiListClipper as outlined above and everything works just fine as long as the user scrolls using the scrollbar on the side. The problem comes if I want (from code) reset the position (say the user press up/down/goto to start of text/etc)
See what I mean?
Yes you need a missing call like SetScrollPosY().
Right now there's only SetScrollPosHere() which center scrolling on the current cursor pos.
I need to get back to that. There is a problem with using scrolling amount vs cursor positions (which starts at 0.0f-scroll_y) they are in different spaces. I may want to unify that preferably to use cursor positions in which case I would have to change the unit of GetScrollPosY()/GetScrollMaxY() - may not even affect your code. Not sure what's the best way yet, will get back to it.
Alright. Sounds good.
I think it's good now. I needed to make some fixes to the existing API.
Hopefully the new set of functions allows to do everything:
float GetScrollY(); // get scrolling amount [0..GetScrollMaxY()]
float GetScrollMaxY(); // get maximum scrolling amount == ContentSize.Y - WindowSize.Y
void SetScrollY(float scroll_y); // set scrolling amount [0..GetScrollMaxY()]
void SetScrollHere(float center_y_ratio = 0.5f); // adjust scrolling amount to make current cursor position visible. center_y_ratio=0.0: top, 0.5: center, 1.0: bottom.
void SetScrollFromPosY(float pos_y, float center_y_ratio = 0.5f); // adjust scrolling amount to make given position valid. use GetCursorPos() or GetCursorStartPos()+offset to get valid positions.
static inline void SetScrollPosHere() { SetScrollFromCursorPos(); } // OBSOLETE 1.42+
The only thing I'm not confident about is the effect of horizontal scrolling.on the optional parameter of SetScrollHere(). I suppose it'll have to be a second optional parameter which will be weird when that happen.
The new parameter center_y_ratio=0.5f allows to specify how to set the scrolling (center by default), e.g.

So to answer you original request, so you can do things like:
// page down-ish
imGui::SetScrollY(ImGui::GetScrollY() + ImGui::GetWindowSize().y);
Or based on an actual cursor position (item position)
ImGui::SetScrollFromPosY(my_pos + 100)
Sweet! Thanks. I will try it out
Works perfect. Thanks!
Most helpful comment
So to answer you original request, so you can do things like:
Or based on an actual cursor position (item position)