Internals: Extracted GetWindowScrollbarRect() out of Scrollbar() and tidying up code to make it more obvious how to draw over scrollbars. (#3114)

This commit is contained in:
omar 2020-04-10 11:03:22 +02:00
parent fb70d90fef
commit f7852fa8e8
2 changed files with 57 additions and 43 deletions

View File

@ -1477,7 +1477,7 @@ struct IMGUI_API ImGuiWindow
ImVec2 ScrollMax;
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)
ImVec2 ScrollTargetCenterRatio; // 0.0f = scroll so that target position is at top, 0.5f = scroll so that target position is centered
ImVec2 ScrollbarSizes; // Size taken by scrollbars on each axis
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.
bool ScrollbarX, ScrollbarY; // Are scrollbars visible?
bool Active; // Set to true on Begin(), unless Collapsed
bool WasActive;
@ -1847,6 +1847,7 @@ namespace ImGui
IMGUI_API bool ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size_arg, ImGuiButtonFlags flags = 0);
IMGUI_API void Scrollbar(ImGuiAxis axis);
IMGUI_API bool ScrollbarEx(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* p_scroll_v, float avail_v, float contents_v, ImDrawCornerFlags rounding_corners);
IMGUI_API ImRect GetWindowScrollbarRect(ImGuiWindow* window, ImGuiAxis axis);
IMGUI_API ImGuiID GetWindowScrollbarID(ImGuiWindow* window, ImGuiAxis axis);
IMGUI_API ImGuiID GetWindowResizeID(ImGuiWindow* window, int n); // 0..3: corners, 4..7: borders
IMGUI_API void SeparatorEx(ImGuiSeparatorFlags flags);

View File

@ -391,8 +391,10 @@ void ImGui::BulletTextV(const char* fmt, va_list args)
// - ArrowButton()
// - CloseButton() [Internal]
// - CollapseButton() [Internal]
// - ScrollbarEx() [Internal]
// - GetWindowScrollbarID() [Internal]
// - GetWindowScrollbarRect() [Internal]
// - Scrollbar() [Internal]
// - ScrollbarEx() [Internal]
// - Image()
// - ImageButton()
// - Checkbox()
@ -812,6 +814,49 @@ ImGuiID ImGui::GetWindowScrollbarID(ImGuiWindow* window, ImGuiAxis axis)
return window->GetIDNoKeepAlive(axis == ImGuiAxis_X ? "#SCROLLX" : "#SCROLLY");
}
// Return scrollbar rectangle, must only be called for corresponding axis if window->ScrollbarX/Y is set.
ImRect ImGui::GetWindowScrollbarRect(ImGuiWindow* window, ImGuiAxis axis)
{
const ImRect outer_rect = window->Rect();
const ImRect inner_rect = window->InnerRect;
const float border_size = window->WindowBorderSize;
const float scrollbar_size = window->ScrollbarSizes[axis ^ 1]; // (ScrollbarSizes.x = width of Y scrollbar; ScrollbarSizes.y = height of X scrollbar)
IM_ASSERT(scrollbar_size > 0.0f);
if (axis == ImGuiAxis_X)
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);
else
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);
}
void ImGui::Scrollbar(ImGuiAxis axis)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
const ImGuiID id = GetWindowScrollbarID(window, axis);
KeepAliveID(id);
// Calculate scrollbar bounding box
ImRect bb = GetWindowScrollbarRect(window, axis);
ImDrawCornerFlags rounding_corners = 0;
if (axis == ImGuiAxis_X)
{
rounding_corners |= ImDrawCornerFlags_BotLeft;
if (!window->ScrollbarY)
rounding_corners |= ImDrawCornerFlags_BotRight;
}
else
{
if ((window->Flags & ImGuiWindowFlags_NoTitleBar) && !(window->Flags & ImGuiWindowFlags_MenuBar))
rounding_corners |= ImDrawCornerFlags_TopRight;
if (!window->ScrollbarX)
rounding_corners |= ImDrawCornerFlags_BotRight;
}
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);
}
// Vertical/Horizontal scrollbar
// The entire piece of code below is rather confusing because:
// - 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
if (bb_frame_width <= 0.0f || bb_frame_height <= 0.0f)
return false;
// 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)
// 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)
float alpha = 1.0f;
if ((axis == ImGuiAxis_Y) && bb_frame_height < g.FontSize + g.Style.FramePadding.y * 2.0f)
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
const ImGuiStyle& style = g.Style;
const bool allow_interaction = (alpha >= 1.0f);
const bool horizontal = (axis == ImGuiAxis_X);
ImRect bb = bb_frame;
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)));
// V denote the main, longer axis of the scrollbar (= height for a vertical scrollbar)
const float scrollbar_size_v = horizontal ? bb.GetWidth() : bb.GetHeight();
const float scrollbar_size_v = (axis == ImGuiAxis_X) ? bb.GetWidth() : bb.GetHeight();
// Calculate the height of our grabbable box. It generally represent the amount visible (vs the total scrollable amount)
// 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
float scroll_max = ImMax(1.0f, size_contents_v - size_avail_v);
float scroll_ratio = ImSaturate(*p_scroll_v / scroll_max);
float grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v;
float grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v; // Grab position in normalized space
if (held && allow_interaction && grab_h_norm < 1.0f)
{
float scrollbar_pos_v = horizontal ? bb.Min.x : bb.Min.y;
float mouse_pos_v = horizontal ? g.IO.MousePos.x : g.IO.MousePos.y;
float scrollbar_pos_v = bb.Min[axis];
float mouse_pos_v = g.IO.MousePos[axis];
// Click position in scrollbar normalized space (0.0f->1.0f)
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
g.ScrollbarClickDeltaToGrabCenter = clicked_v_norm - grab_v_norm - grab_h_norm * 0.5f;
}
// Apply scroll
// Apply scroll (p_scroll_v will generally point on one member of window->Scroll)
// 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
const float scroll_v_norm = ImSaturate((clicked_v_norm - g.ScrollbarClickDeltaToGrabCenter - grab_h_norm * 0.5f) / (1.0f - grab_h_norm));
*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
}
// Render
window->DrawList->AddRectFilled(bb_frame.Min, bb_frame.Max, GetColorU32(ImGuiCol_ScrollbarBg), window->WindowRounding, rounding_corners);
const ImU32 bg_col = GetColorU32(ImGuiCol_ScrollbarBg);
const ImU32 grab_col = GetColorU32(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab, alpha);
window->DrawList->AddRectFilled(bb_frame.Min, bb_frame.Max, bg_col, window->WindowRounding, rounding_corners);
ImRect grab_rect;
if (horizontal)
if (axis == ImGuiAxis_X)
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);
else
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
return held;
}
void ImGui::Scrollbar(ImGuiAxis axis)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
const ImGuiID id = GetWindowScrollbarID(window, axis);
KeepAliveID(id);
// Calculate scrollbar bounding box
const ImRect outer_rect = window->Rect();
const ImRect inner_rect = window->InnerRect;
const float border_size = window->WindowBorderSize;
const float scrollbar_size = window->ScrollbarSizes[axis ^ 1];
IM_ASSERT(scrollbar_size > 0.0f);
const float other_scrollbar_size = window->ScrollbarSizes[axis];
ImDrawCornerFlags rounding_corners = (other_scrollbar_size <= 0.0f) ? ImDrawCornerFlags_BotRight : 0;
ImRect bb;
if (axis == ImGuiAxis_X)
{
bb.Min = ImVec2(inner_rect.Min.x, ImMax(outer_rect.Min.y, outer_rect.Max.y - border_size - scrollbar_size));
bb.Max = ImVec2(inner_rect.Max.x, outer_rect.Max.y);
rounding_corners |= ImDrawCornerFlags_BotLeft;
}
else
{
bb.Min = ImVec2(ImMax(outer_rect.Min.x, outer_rect.Max.x - border_size - scrollbar_size), inner_rect.Min.y);
bb.Max = ImVec2(outer_rect.Max.x, window->InnerRect.Max.y);
rounding_corners |= ((window->Flags & ImGuiWindowFlags_NoTitleBar) && !(window->Flags & ImGuiWindowFlags_MenuBar)) ? ImDrawCornerFlags_TopRight : 0;
}
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);
}
void ImGui::Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col)
{
ImGuiWindow* window = GetCurrentWindow();