Combo, Slider: Improve rendering in situation when there's there's very little space available.

This commit is contained in:
omar 2019-04-18 15:00:40 +02:00
parent 1aeee9d40f
commit 240dddff87
2 changed files with 39 additions and 26 deletions

View File

@ -52,6 +52,7 @@ Other Changes:
- PlotLines, PlotHistogram: Ignore NaN values when calculating min/max bounds. (#2485) - PlotLines, PlotHistogram: Ignore NaN values when calculating min/max bounds. (#2485)
- Window: Window close button is horizontally aligned with style.FramePadding.x. - Window: Window close button is horizontally aligned with style.FramePadding.x.
- Columns: Fixed boundary of clipping being off by 1 pixel within the left column. - Columns: Fixed boundary of clipping being off by 1 pixel within the left column.
- Combo, Slider, Scrollbar: Improve rendering in situation when there's only a few pixels available (<3 pixels).
- Misc: Added IM_MALLOC/IM_FREE macros mimicking IM_NEW/IM_DELETE so user doesn't need to revert - Misc: Added IM_MALLOC/IM_FREE macros mimicking IM_NEW/IM_DELETE so user doesn't need to revert
to using the ImGui::MemAlloc()/MemFree() calls directly. to using the ImGui::MemAlloc()/MemFree() calls directly.
- Metrics: Added "Show windows rectangles" tool to visualize the different rectangles. - Metrics: Added "Show windows rectangles" tool to visualize the different rectangles.

View File

@ -788,16 +788,19 @@ void ImGui::Scrollbar(ImGuiAxis axis)
// Render background // Render background
bool other_scrollbar = (horizontal ? window->ScrollbarY : window->ScrollbarX); bool other_scrollbar = (horizontal ? window->ScrollbarY : window->ScrollbarX);
float other_scrollbar_size_w = other_scrollbar ? style.ScrollbarSize : 0.0f; float other_scrollbar_size_w = other_scrollbar ? style.ScrollbarSize : 0.0f;
const ImRect window_rect = window->Rect(); const ImRect host_rect = window->Rect();
const float border_size = window->WindowBorderSize; const float border_size = window->WindowBorderSize;
ImRect bb = horizontal ImRect bb = horizontal
? ImRect(window->Pos.x + border_size, window_rect.Max.y - style.ScrollbarSize, window_rect.Max.x - other_scrollbar_size_w - border_size, window_rect.Max.y - border_size) ? ImRect(host_rect.Min.x + border_size, host_rect.Max.y - style.ScrollbarSize, host_rect.Max.x - other_scrollbar_size_w - border_size, host_rect.Max.y - border_size)
: ImRect(window_rect.Max.x - style.ScrollbarSize, window->Pos.y + border_size, window_rect.Max.x - border_size, window_rect.Max.y - other_scrollbar_size_w - border_size); : ImRect(host_rect.Max.x - style.ScrollbarSize, host_rect.Min.y + border_size, host_rect.Max.x - border_size, host_rect.Max.y - other_scrollbar_size_w - border_size);
bb.Min.x = ImMax(host_rect.Min.x, bb.Min.x); // Handle case where the host rectangle is smaller than the scrollbar
bb.Min.y = ImMax(host_rect.Min.y, bb.Min.y);
if (!horizontal) if (!horizontal)
bb.Min.y += window->TitleBarHeight() + ((window->Flags & ImGuiWindowFlags_MenuBar) ? window->MenuBarHeight() : 0.0f); // FIXME: InnerRect? bb.Min.y += window->TitleBarHeight() + ((window->Flags & ImGuiWindowFlags_MenuBar) ? window->MenuBarHeight() : 0.0f); // FIXME: InnerRect?
const float bb_width = bb.GetWidth();
const float bb_height = bb.GetHeight(); const float bb_height = bb.GetHeight();
if (bb.GetWidth() <= 0.0f || bb_height <= 0.0f) if (bb_width <= 0.0f || bb_height <= 0.0f)
return; return;
// 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 resize grab)
@ -816,7 +819,7 @@ void ImGui::Scrollbar(ImGuiAxis axis)
else else
window_rounding_corners = (((window->Flags & ImGuiWindowFlags_NoTitleBar) && !(window->Flags & ImGuiWindowFlags_MenuBar)) ? ImDrawCornerFlags_TopRight : 0) | (other_scrollbar ? 0 : ImDrawCornerFlags_BotRight); window_rounding_corners = (((window->Flags & ImGuiWindowFlags_NoTitleBar) && !(window->Flags & ImGuiWindowFlags_MenuBar)) ? ImDrawCornerFlags_TopRight : 0) | (other_scrollbar ? 0 : ImDrawCornerFlags_BotRight);
window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_ScrollbarBg), window->WindowRounding, window_rounding_corners); window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_ScrollbarBg), window->WindowRounding, window_rounding_corners);
bb.Expand(ImVec2(-ImClamp((float)(int)((bb.Max.x - bb.Min.x - 2.0f) * 0.5f), 0.0f, 3.0f), -ImClamp((float)(int)((bb.Max.y - bb.Min.y - 2.0f) * 0.5f), 0.0f, 3.0f))); bb.Expand(ImVec2(-ImClamp((float)(int)((bb_width - 2.0f) * 0.5f), 0.0f, 3.0f), -ImClamp((float)(int)((bb_height - 2.0f) * 0.5f), 0.0f, 3.0f)));
// V denote the main, longer axis of the scrollbar (= height for a vertical scrollbar) // V denote the main, longer axis of the scrollbar (= height for a vertical scrollbar)
float scrollbar_size_v = horizontal ? bb.GetWidth() : bb.GetHeight(); float scrollbar_size_v = horizontal ? bb.GetWidth() : bb.GetHeight();
@ -887,9 +890,9 @@ void ImGui::Scrollbar(ImGuiAxis axis)
const ImU32 grab_col = GetColorU32(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab, alpha); const ImU32 grab_col = GetColorU32(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab, alpha);
ImRect grab_rect; ImRect grab_rect;
if (horizontal) if (horizontal)
grab_rect = ImRect(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm), bb.Min.y, ImMin(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm) + grab_h_pixels, window_rect.Max.x), bb.Max.y); grab_rect = ImRect(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm), bb.Min.y, ImMin(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm) + grab_h_pixels, host_rect.Max.x), bb.Max.y);
else else
grab_rect = ImRect(bb.Min.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm), bb.Max.x, ImMin(ImLerp(bb.Min.y, bb.Max.y, grab_v_norm) + grab_h_pixels, window_rect.Max.y)); grab_rect = ImRect(bb.Min.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm), bb.Max.x, ImMin(ImLerp(bb.Min.y, bb.Max.y, grab_v_norm) + grab_h_pixels, host_rect.Max.y));
window->DrawList->AddRectFilled(grab_rect.Min, grab_rect.Max, grab_col, style.ScrollbarRounding); window->DrawList->AddRectFilled(grab_rect.Min, grab_rect.Max, grab_col, style.ScrollbarRounding);
} }
@ -1361,19 +1364,19 @@ bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboF
bool pressed = ButtonBehavior(frame_bb, id, &hovered, &held); bool pressed = ButtonBehavior(frame_bb, id, &hovered, &held);
bool popup_open = IsPopupOpen(id); bool popup_open = IsPopupOpen(id);
const ImRect value_bb(frame_bb.Min, frame_bb.Max - ImVec2(arrow_size, 0.0f));
const ImU32 frame_col = GetColorU32(hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); const ImU32 frame_col = GetColorU32(hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
const float value_x2 = ImMax(frame_bb.Min.x, frame_bb.Max.x - arrow_size);
RenderNavHighlight(frame_bb, id); RenderNavHighlight(frame_bb, id);
if (!(flags & ImGuiComboFlags_NoPreview)) if (!(flags & ImGuiComboFlags_NoPreview))
window->DrawList->AddRectFilled(frame_bb.Min, ImVec2(frame_bb.Max.x - arrow_size, frame_bb.Max.y), frame_col, style.FrameRounding, ImDrawCornerFlags_Left); window->DrawList->AddRectFilled(frame_bb.Min, ImVec2(value_x2, frame_bb.Max.y), frame_col, style.FrameRounding, ImDrawCornerFlags_Left);
if (!(flags & ImGuiComboFlags_NoArrowButton)) if (!(flags & ImGuiComboFlags_NoArrowButton))
{ {
window->DrawList->AddRectFilled(ImVec2(frame_bb.Max.x - arrow_size, frame_bb.Min.y), frame_bb.Max, GetColorU32((popup_open || hovered) ? ImGuiCol_ButtonHovered : ImGuiCol_Button), style.FrameRounding, (w <= arrow_size) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Right); window->DrawList->AddRectFilled(ImVec2(value_x2, frame_bb.Min.y), frame_bb.Max, GetColorU32((popup_open || hovered) ? ImGuiCol_ButtonHovered : ImGuiCol_Button), style.FrameRounding, (w <= arrow_size) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Right);
RenderArrow(ImVec2(frame_bb.Max.x - arrow_size + style.FramePadding.y, frame_bb.Min.y + style.FramePadding.y), ImGuiDir_Down); RenderArrow(ImVec2(value_x2 + style.FramePadding.y, frame_bb.Min.y + style.FramePadding.y), ImGuiDir_Down);
} }
RenderFrameBorder(frame_bb.Min, frame_bb.Max, style.FrameRounding); RenderFrameBorder(frame_bb.Min, frame_bb.Max, style.FrameRounding);
if (preview_value != NULL && !(flags & ImGuiComboFlags_NoPreview)) if (preview_value != NULL && !(flags & ImGuiComboFlags_NoPreview))
RenderTextClipped(frame_bb.Min + style.FramePadding, value_bb.Max, preview_value, NULL, NULL, ImVec2(0.0f,0.0f)); RenderTextClipped(frame_bb.Min + style.FramePadding, ImVec2(value_x2, frame_bb.Max.y), preview_value, NULL, NULL, ImVec2(0.0f,0.0f));
if (label_size.x > 0) if (label_size.x > 0)
RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
@ -2223,16 +2226,16 @@ bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_typ
grab_sz = ImMax((float)(slider_sz / (v_range + 1)), style.GrabMinSize); // For integer sliders: if possible have the grab size represent 1 unit grab_sz = ImMax((float)(slider_sz / (v_range + 1)), style.GrabMinSize); // For integer sliders: if possible have the grab size represent 1 unit
grab_sz = ImMin(grab_sz, slider_sz); grab_sz = ImMin(grab_sz, slider_sz);
const float slider_usable_sz = slider_sz - grab_sz; const float slider_usable_sz = slider_sz - grab_sz;
const float slider_usable_pos_min = bb.Min[axis] + grab_padding + grab_sz*0.5f; const float slider_usable_pos_min = bb.Min[axis] + grab_padding + grab_sz * 0.5f;
const float slider_usable_pos_max = bb.Max[axis] - grab_padding - grab_sz*0.5f; const float slider_usable_pos_max = bb.Max[axis] - grab_padding - grab_sz * 0.5f;
// For power curve sliders that cross over sign boundary we want the curve to be symmetric around 0.0f // For power curve sliders that cross over sign boundary we want the curve to be symmetric around 0.0f
float linear_zero_pos; // 0.0->1.0f float linear_zero_pos; // 0.0->1.0f
if (is_power && v_min * v_max < 0.0f) if (is_power && v_min * v_max < 0.0f)
{ {
// Different sign // Different sign
const FLOATTYPE linear_dist_min_to_0 = ImPow(v_min >= 0 ? (FLOATTYPE)v_min : -(FLOATTYPE)v_min, (FLOATTYPE)1.0f/power); const FLOATTYPE linear_dist_min_to_0 = ImPow(v_min >= 0 ? (FLOATTYPE)v_min : -(FLOATTYPE)v_min, (FLOATTYPE)1.0f / power);
const FLOATTYPE linear_dist_max_to_0 = ImPow(v_max >= 0 ? (FLOATTYPE)v_max : -(FLOATTYPE)v_max, (FLOATTYPE)1.0f/power); const FLOATTYPE linear_dist_max_to_0 = ImPow(v_max >= 0 ? (FLOATTYPE)v_max : -(FLOATTYPE)v_max, (FLOATTYPE)1.0f / power);
linear_zero_pos = (float)(linear_dist_min_to_0 / (linear_dist_min_to_0 + linear_dist_max_to_0)); linear_zero_pos = (float)(linear_dist_min_to_0 / (linear_dist_min_to_0 + linear_dist_max_to_0));
} }
else else
@ -2355,15 +2358,22 @@ bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_typ
} }
} }
if (slider_sz < 1.0f)
{
*out_grab_bb = ImRect(bb.Min, bb.Min);
}
else
{
// Output grab position so it can be displayed by the caller // Output grab position so it can be displayed by the caller
float grab_t = SliderCalcRatioFromValueT<TYPE,FLOATTYPE>(data_type, *v, v_min, v_max, power, linear_zero_pos); float grab_t = SliderCalcRatioFromValueT<TYPE, FLOATTYPE>(data_type, *v, v_min, v_max, power, linear_zero_pos);
if (axis == ImGuiAxis_Y) if (axis == ImGuiAxis_Y)
grab_t = 1.0f - grab_t; grab_t = 1.0f - grab_t;
const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t); const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t);
if (axis == ImGuiAxis_X) if (axis == ImGuiAxis_X)
*out_grab_bb = ImRect(grab_pos - grab_sz*0.5f, bb.Min.y + grab_padding, grab_pos + grab_sz*0.5f, bb.Max.y - grab_padding); *out_grab_bb = ImRect(grab_pos - grab_sz * 0.5f, bb.Min.y + grab_padding, grab_pos + grab_sz * 0.5f, bb.Max.y - grab_padding);
else else
*out_grab_bb = ImRect(bb.Min.x + grab_padding, grab_pos - grab_sz*0.5f, bb.Max.x - grab_padding, grab_pos + grab_sz*0.5f); *out_grab_bb = ImRect(bb.Min.x + grab_padding, grab_pos - grab_sz * 0.5f, bb.Max.x - grab_padding, grab_pos + grab_sz * 0.5f);
}
return value_changed; return value_changed;
} }
@ -2465,6 +2475,7 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* v, co
MarkItemEdited(id); MarkItemEdited(id);
// Render grab // Render grab
if (grab_bb.Max.x > grab_bb.Min.x)
window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding); window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding);
// Display value using user-provided display format so user can add prefix/suffix/decorations to the value. // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
@ -2605,6 +2616,7 @@ bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType d
MarkItemEdited(id); MarkItemEdited(id);
// Render grab // Render grab
if (grab_bb.Max.y > grab_bb.Min.y)
window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding); window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding);
// Display value using user-provided display format so user can add prefix/suffix/decorations to the value. // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.