Skip to content

Commit f7852fa

Browse files
committed
Internals: Extracted GetWindowScrollbarRect() out of Scrollbar() and tidying up code to make it more obvious how to draw over scrollbars. (#3114)
1 parent fb70d90 commit f7852fa

File tree

2 files changed

+57
-43
lines changed

2 files changed

+57
-43
lines changed

imgui_internal.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -1477,7 +1477,7 @@ struct IMGUI_API ImGuiWindow
14771477
ImVec2 ScrollMax;
14781478
ImVec2 ScrollTarget; // target scroll position. stored as cursor position with scrolling canceled out, so the highest point is always 0.0f. (FLT_MAX for no change)
14791479
ImVec2 ScrollTargetCenterRatio; // 0.0f = scroll so that target position is at top, 0.5f = scroll so that target position is centered
1480-
ImVec2 ScrollbarSizes; // Size taken by scrollbars on each axis
1480+
ImVec2 ScrollbarSizes; // Size taken by each scrollbars on their smaller axis. Pay attention! ScrollbarSizes.x == width of the vertical scrollbar, ScrollbarSizes.y = height of the horizontal scrollbar.
14811481
bool ScrollbarX, ScrollbarY; // Are scrollbars visible?
14821482
bool Active; // Set to true on Begin(), unless Collapsed
14831483
bool WasActive;
@@ -1847,6 +1847,7 @@ namespace ImGui
18471847
IMGUI_API bool ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size_arg, ImGuiButtonFlags flags = 0);
18481848
IMGUI_API void Scrollbar(ImGuiAxis axis);
18491849
IMGUI_API bool ScrollbarEx(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* p_scroll_v, float avail_v, float contents_v, ImDrawCornerFlags rounding_corners);
1850+
IMGUI_API ImRect GetWindowScrollbarRect(ImGuiWindow* window, ImGuiAxis axis);
18501851
IMGUI_API ImGuiID GetWindowScrollbarID(ImGuiWindow* window, ImGuiAxis axis);
18511852
IMGUI_API ImGuiID GetWindowResizeID(ImGuiWindow* window, int n); // 0..3: corners, 4..7: borders
18521853
IMGUI_API void SeparatorEx(ImGuiSeparatorFlags flags);

imgui_widgets.cpp

+55-42
Original file line numberDiff line numberDiff line change
@@ -391,8 +391,10 @@ void ImGui::BulletTextV(const char* fmt, va_list args)
391391
// - ArrowButton()
392392
// - CloseButton() [Internal]
393393
// - CollapseButton() [Internal]
394-
// - ScrollbarEx() [Internal]
394+
// - GetWindowScrollbarID() [Internal]
395+
// - GetWindowScrollbarRect() [Internal]
395396
// - Scrollbar() [Internal]
397+
// - ScrollbarEx() [Internal]
396398
// - Image()
397399
// - ImageButton()
398400
// - Checkbox()
@@ -812,6 +814,49 @@ ImGuiID ImGui::GetWindowScrollbarID(ImGuiWindow* window, ImGuiAxis axis)
812814
return window->GetIDNoKeepAlive(axis == ImGuiAxis_X ? "#SCROLLX" : "#SCROLLY");
813815
}
814816

817+
// Return scrollbar rectangle, must only be called for corresponding axis if window->ScrollbarX/Y is set.
818+
ImRect ImGui::GetWindowScrollbarRect(ImGuiWindow* window, ImGuiAxis axis)
819+
{
820+
const ImRect outer_rect = window->Rect();
821+
const ImRect inner_rect = window->InnerRect;
822+
const float border_size = window->WindowBorderSize;
823+
const float scrollbar_size = window->ScrollbarSizes[axis ^ 1]; // (ScrollbarSizes.x = width of Y scrollbar; ScrollbarSizes.y = height of X scrollbar)
824+
IM_ASSERT(scrollbar_size > 0.0f);
825+
if (axis == ImGuiAxis_X)
826+
return ImRect(inner_rect.Min.x, ImMax(outer_rect.Min.y, outer_rect.Max.y - border_size - scrollbar_size), inner_rect.Max.x, outer_rect.Max.y);
827+
else
828+
return ImRect(ImMax(outer_rect.Min.x, outer_rect.Max.x - border_size - scrollbar_size), inner_rect.Min.y, outer_rect.Max.x, inner_rect.Max.y);
829+
}
830+
831+
void ImGui::Scrollbar(ImGuiAxis axis)
832+
{
833+
ImGuiContext& g = *GImGui;
834+
ImGuiWindow* window = g.CurrentWindow;
835+
836+
const ImGuiID id = GetWindowScrollbarID(window, axis);
837+
KeepAliveID(id);
838+
839+
// Calculate scrollbar bounding box
840+
ImRect bb = GetWindowScrollbarRect(window, axis);
841+
ImDrawCornerFlags rounding_corners = 0;
842+
if (axis == ImGuiAxis_X)
843+
{
844+
rounding_corners |= ImDrawCornerFlags_BotLeft;
845+
if (!window->ScrollbarY)
846+
rounding_corners |= ImDrawCornerFlags_BotRight;
847+
}
848+
else
849+
{
850+
if ((window->Flags & ImGuiWindowFlags_NoTitleBar) && !(window->Flags & ImGuiWindowFlags_MenuBar))
851+
rounding_corners |= ImDrawCornerFlags_TopRight;
852+
if (!window->ScrollbarX)
853+
rounding_corners |= ImDrawCornerFlags_BotRight;
854+
}
855+
float size_avail = window->InnerRect.Max[axis] - window->InnerRect.Min[axis];
856+
float size_contents = window->ContentSize[axis] + window->WindowPadding[axis] * 2.0f;
857+
ScrollbarEx(bb, id, axis, &window->Scroll[axis], size_avail, size_contents, rounding_corners);
858+
}
859+
815860
// Vertical/Horizontal scrollbar
816861
// The entire piece of code below is rather confusing because:
817862
// - We handle absolute seeking (when first clicking outside the grab) and relative manipulation (afterward or when clicking inside the grab)
@@ -830,7 +875,7 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, floa
830875
if (bb_frame_width <= 0.0f || bb_frame_height <= 0.0f)
831876
return false;
832877

833-
// When we are too small, start hiding and disabling the grab (this reduce visual noise on very small window and facilitate using the resize grab)
878+
// When we are too small, start hiding and disabling the grab (this reduce visual noise on very small window and facilitate using the window resize grab)
834879
float alpha = 1.0f;
835880
if ((axis == ImGuiAxis_Y) && bb_frame_height < g.FontSize + g.Style.FramePadding.y * 2.0f)
836881
alpha = ImSaturate((bb_frame_height - g.FontSize) / (g.Style.FramePadding.y * 2.0f));
@@ -839,13 +884,12 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, floa
839884

840885
const ImGuiStyle& style = g.Style;
841886
const bool allow_interaction = (alpha >= 1.0f);
842-
const bool horizontal = (axis == ImGuiAxis_X);
843887

844888
ImRect bb = bb_frame;
845889
bb.Expand(ImVec2(-ImClamp(IM_FLOOR((bb_frame_width - 2.0f) * 0.5f), 0.0f, 3.0f), -ImClamp(IM_FLOOR((bb_frame_height - 2.0f) * 0.5f), 0.0f, 3.0f)));
846890

847891
// V denote the main, longer axis of the scrollbar (= height for a vertical scrollbar)
848-
const float scrollbar_size_v = horizontal ? bb.GetWidth() : bb.GetHeight();
892+
const float scrollbar_size_v = (axis == ImGuiAxis_X) ? bb.GetWidth() : bb.GetHeight();
849893

850894
// Calculate the height of our grabbable box. It generally represent the amount visible (vs the total scrollable amount)
851895
// But we maintain a minimum size in pixel to allow for the user to still aim inside.
@@ -861,11 +905,11 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, floa
861905

862906
float scroll_max = ImMax(1.0f, size_contents_v - size_avail_v);
863907
float scroll_ratio = ImSaturate(*p_scroll_v / scroll_max);
864-
float grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v;
908+
float grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v; // Grab position in normalized space
865909
if (held && allow_interaction && grab_h_norm < 1.0f)
866910
{
867-
float scrollbar_pos_v = horizontal ? bb.Min.x : bb.Min.y;
868-
float mouse_pos_v = horizontal ? g.IO.MousePos.x : g.IO.MousePos.y;
911+
float scrollbar_pos_v = bb.Min[axis];
912+
float mouse_pos_v = g.IO.MousePos[axis];
869913

870914
// Click position in scrollbar normalized space (0.0f->1.0f)
871915
const float clicked_v_norm = ImSaturate((mouse_pos_v - scrollbar_pos_v) / scrollbar_size_v);
@@ -882,7 +926,7 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, floa
882926
g.ScrollbarClickDeltaToGrabCenter = clicked_v_norm - grab_v_norm - grab_h_norm * 0.5f;
883927
}
884928

885-
// Apply scroll
929+
// Apply scroll (p_scroll_v will generally point on one member of window->Scroll)
886930
// It is ok to modify Scroll here because we are being called in Begin() after the calculation of ContentSize and before setting up our starting position
887931
const float scroll_v_norm = ImSaturate((clicked_v_norm - g.ScrollbarClickDeltaToGrabCenter - grab_h_norm * 0.5f) / (1.0f - grab_h_norm));
888932
*p_scroll_v = IM_ROUND(scroll_v_norm * scroll_max);//(win_size_contents_v - win_size_v));
@@ -897,10 +941,11 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, floa
897941
}
898942

899943
// Render
900-
window->DrawList->AddRectFilled(bb_frame.Min, bb_frame.Max, GetColorU32(ImGuiCol_ScrollbarBg), window->WindowRounding, rounding_corners);
944+
const ImU32 bg_col = GetColorU32(ImGuiCol_ScrollbarBg);
901945
const ImU32 grab_col = GetColorU32(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab, alpha);
946+
window->DrawList->AddRectFilled(bb_frame.Min, bb_frame.Max, bg_col, window->WindowRounding, rounding_corners);
902947
ImRect grab_rect;
903-
if (horizontal)
948+
if (axis == ImGuiAxis_X)
904949
grab_rect = ImRect(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm), bb.Min.y, ImLerp(bb.Min.x, bb.Max.x, grab_v_norm) + grab_h_pixels, bb.Max.y);
905950
else
906951
grab_rect = ImRect(bb.Min.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm), bb.Max.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm) + grab_h_pixels);
@@ -909,38 +954,6 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, floa
909954
return held;
910955
}
911956

912-
void ImGui::Scrollbar(ImGuiAxis axis)
913-
{
914-
ImGuiContext& g = *GImGui;
915-
ImGuiWindow* window = g.CurrentWindow;
916-
917-
const ImGuiID id = GetWindowScrollbarID(window, axis);
918-
KeepAliveID(id);
919-
920-
// Calculate scrollbar bounding box
921-
const ImRect outer_rect = window->Rect();
922-
const ImRect inner_rect = window->InnerRect;
923-
const float border_size = window->WindowBorderSize;
924-
const float scrollbar_size = window->ScrollbarSizes[axis ^ 1];
925-
IM_ASSERT(scrollbar_size > 0.0f);
926-
const float other_scrollbar_size = window->ScrollbarSizes[axis];
927-
ImDrawCornerFlags rounding_corners = (other_scrollbar_size <= 0.0f) ? ImDrawCornerFlags_BotRight : 0;
928-
ImRect bb;
929-
if (axis == ImGuiAxis_X)
930-
{
931-
bb.Min = ImVec2(inner_rect.Min.x, ImMax(outer_rect.Min.y, outer_rect.Max.y - border_size - scrollbar_size));
932-
bb.Max = ImVec2(inner_rect.Max.x, outer_rect.Max.y);
933-
rounding_corners |= ImDrawCornerFlags_BotLeft;
934-
}
935-
else
936-
{
937-
bb.Min = ImVec2(ImMax(outer_rect.Min.x, outer_rect.Max.x - border_size - scrollbar_size), inner_rect.Min.y);
938-
bb.Max = ImVec2(outer_rect.Max.x, window->InnerRect.Max.y);
939-
rounding_corners |= ((window->Flags & ImGuiWindowFlags_NoTitleBar) && !(window->Flags & ImGuiWindowFlags_MenuBar)) ? ImDrawCornerFlags_TopRight : 0;
940-
}
941-
ScrollbarEx(bb, id, axis, &window->Scroll[axis], inner_rect.Max[axis] - inner_rect.Min[axis], window->ContentSize[axis] + window->WindowPadding[axis] * 2.0f, rounding_corners);
942-
}
943-
944957
void ImGui::Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col)
945958
{
946959
ImGuiWindow* window = GetCurrentWindow();

0 commit comments

Comments
 (0)