ocornut/imgui

Decorating scroll bars

wolfpld opened this issue · 4 comments

I would like to add decorations to a (child) window scroll bar. Something alike to the following examples:

VTune (let's ignore that decorations are placed next to the scroll bar here):
scroll

MSVC:
scroll2

I can't see anything in the API, which would allow doing so. I would imagine at least the following functions should be added:

if( ImGui::IsDisplayingScrollBar() ) {
    auto draw = ImGui::GetScrollDrawList();
    auto pos = ImGui::GetScrollScreenPos();
    auto region = ImGui::GetScrollSize();
    ...
}

A bit of thought is required to decide in which place user elements should be placed: before scroll bar is drawn, after scroll bar is drawn, or between drawing scroll background and scroll grab element. It may be best to expose all the possibilities through a parameter to GetScrollDrawList(), but to get things running quickly it seems reasonable to use the third option. This way user elements won't ever be obscuring the grab element.

Hey @wolfpld,
There is no built in way to do that. However you may cheat a little. Window scrollbars are rendered as part of Begin() call. You could try rendering extra information on to scrollbar rect right after Begin(), it might yield similar results like in MSVC screenshot. If that is not enough i am afraid you would have to modify scrollbar rendering in imgui itself.

I have pushed a first change toward that: f7852fa

  • There now a GetWindowScrollbarRect() function in imgui_internal.h
  • Tweaked various scrollbar related functions and comments to ease understanding of what's going on.
    (This commit merges in docking without conflicts so you may cherry-pick it today).

Using imgui_internal.h:

ImGui::Begin("Hello, world!");
ImGuiWindow* window = ImGui::GetCurrentWindow();
if (window->ScrollbarX || window->ScrollbarY)
{
    ImGui::PushClipRect(window->OuterRectClipped.Min, window->OuterRectClipped.Max, false);
    if (window->ScrollbarX)
    {
        ImRect r = ImGui::GetWindowScrollbarRect(window, ImGuiAxis_X);
        window->DrawList->AddRect(r.Min, r.Max, IM_COL32(255, 0, 0, 255));
    }
    if (window->ScrollbarY)
    {
        ImRect r = ImGui::GetWindowScrollbarRect(window, ImGuiAxis_Y);
        window->DrawList->AddRect(r.Min, r.Max, IM_COL32(255, 0, 0, 255));
    }
    ImGui::PopClipRect();
}
[...]

EDIT If you don't use any ImGui:: interactive/function in the drawing, you can replace ImGui::PushClipRect/PopClipRect by the lower-level window->DrawList->PushClipRect() equivalent.

image

  • If you now look at the ImGui::Scrollbar() function in imgui_widgets.cpp (after the change I just pushed), stripping away a few details you don't care about, those 5 lines are probably the essential info you would need to correctly "project" your rendering.
ImGuiID id = GetWindowScrollbarID(window, axis);
ImRect bb = GetWindowScrollbarRect(window, axis);
float size_avail = window->InnerRect.Max[axis] - window->InnerRect.Min[axis];
float size_contents = window->ContentSize[axis] + window->WindowPadding[axis] * 2.0f;
ScrollbarEx(bb, id, axis, &window->Scroll[axis], size_avail, size_contents, rounding_corners);
  • The scrollbar interaction will always run on Begin(). If you want to render OVER the scrollbar then you can use the info above. If you somehow need to render UNDER the scrollbar then we have a bit of a problem but an easy workaround would be to set all those colors to zero:
ImGui::PushStyleColor(ImGuiCol_ScrollbarBg, ImVec4(0, 0, 0, 0));
ImGui::PushStyleColor(ImGuiCol_ScrollbarGrab, ImVec4(0, 0, 0, 0));
ImGui::PushStyleColor(ImGuiCol_ScrollbarGrabActive, ImVec4(0, 0, 0, 0));
ImGui::PushStyleColor(ImGuiCol_ScrollbarGrabHovered, ImVec4(0, 0, 0, 0));
ImGui::Begin(...)
ImGui::PopStyleColor(4);

And then you have the render it yourself, which is a little bit awkward but not super complicated (and you don't necessarily need to match all edge cases behavior handled by ScrollbarEx).

  • If you eventually wanted to draw next to the scrollbar, and therefore move the scrollbar, as in the first shot (VTune), the simpler hack imho would be to add an extra window (Begin+BeginChild).

I hope this is useful and I'd be interested in seeing that you can do with the above information. I'm happy to make further change or add more internal helpers. Don't hesitate to post your result or link to the code if you do end with something, even if you don't have explicit requests it may be helpful as a resources and I may still come up with improvements on our side of the fence.

This everything I need. Usage example:

auto win = ImGui::GetCurrentWindow();
if( win->ScrollbarY )
{
    auto draw = ImGui::GetWindowDrawList();
    auto rect = ImGui::GetWindowScrollbarRect( win, ImGuiAxis_Y );
    ImGui::PushClipRect( rect.Min, rect.Max, false );
    if( m_selectedLine != 0 )
    {
        auto ly = round( rect.Min.y + ( m_selectedLine - 0.5f ) / m_lines.size() * rect.GetHeight() );
        draw->AddLine( ImVec2( rect.Min.x, ly ), ImVec2( rect.Max.x, ly ), 0x8899994C, 3 );
    }
    ImGui::PopClipRect();
}

I will post a screenshot when I'm finished with everything I want to have here. Thanks.

There are two kinds of information shown with scroll bar decorations on the screenshot below:
1a. Currently selected source line and the corresponding assembly instructions.
1b. Currently hovered source line and the corresponding assembly instructions.
2. Relative "hotness" of source/assembly lines.

obraz