From ae7f833c69f71dc9b8ad1fd3e33f7c992776906f Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 2 Oct 2018 18:43:10 +0200 Subject: [PATCH] Window: Resizing from edges (with io.ConfigResizeWindowsFromEdges Beta flag) extends the hit region of root floating windows outside the window, making it easier to resize windows. Resize grips are also extended accordingly so there are no discontinuity when hovering between borders and corners. (#1495, #822, #2110) --- docs/CHANGELOG.txt | 3 +++ imgui.cpp | 64 ++++++++++++++++++++++++++++------------------ 2 files changed, 42 insertions(+), 25 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 6cf5a1625..bc6677124 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -49,6 +49,9 @@ Other Changes: erroneously wrapped the value to one of the min/max edge. (#2024, #708, #320, #2075). - DragFloat: Disabled using power curve when one edge is FLT_MAX (broken in 1.61). (#2024) - DragFloat: Disabled setting a default drag speed when one edge is FLT_MAX. (#2024) +- Window: Resizing from edges (with io.ConfigResizeWindowsFromEdges Beta flag) extends the hit region + of root floating windows outside the window, making it easier to resize windows. Resize grips are also + extended accordingly so there are no discontinuity when hovering between borders and corners. (#1495, #822) - BeginChild(): Fixed BeginChild(const char*, ...) variation erroneously not applying the ID stack to the provided string to uniquely identify the child window. This was undoing an intentional change introduced in 1.50 and broken in 1.60. (#1698, #894, #713). diff --git a/imgui.cpp b/imgui.cpp index d85e1d9f5..b64837455 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -889,8 +889,12 @@ CODE #endif // When using CTRL+TAB (or Gamepad Square+L/R) we delay the visual a little in order to reduce visual noise doing a fast switch. -static const float NAV_WINDOWING_HIGHLIGHT_DELAY = 0.20f; // Time before the highlight and screen dimming starts fading in -static const float NAV_WINDOWING_LIST_APPEAR_DELAY = 0.15f; // Time before the window list starts to appear +static const float NAV_WINDOWING_HIGHLIGHT_DELAY = 0.20f; // Time before the highlight and screen dimming starts fading in +static const float NAV_WINDOWING_LIST_APPEAR_DELAY = 0.15f; // Time before the window list starts to appear + +// Window resizing from edges (when io.ConfigResizeWindowsFromEdges = true) +static const float RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS = 4.0f; // Extend outside and inside windows. Affect FindHoveredWindow(). +static const float RESIZE_WINDOWS_FROM_EDGES_FEEDBACK_TIMER = 0.04f; // Reduce visual noise by only highlighting the border after a certain time. //------------------------------------------------------------------------- // [SECTION] FORWARD DECLARATIONS @@ -3698,6 +3702,8 @@ static void FindHoveredWindow() if (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoInputs)) hovered_window = g.MovingWindow; + ImVec2 padding_regular = g.Style.TouchExtraPadding; + ImVec2 padding_for_resize_from_edges = g.IO.ConfigResizeWindowsFromEdges ? ImMax(g.Style.TouchExtraPadding, ImVec2(RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS, RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS)) : padding_regular; for (int i = g.Windows.Size - 1; i >= 0 && hovered_window == NULL; i--) { ImGuiWindow* window = g.Windows[i]; @@ -3707,14 +3713,19 @@ static void FindHoveredWindow() continue; // Using the clipped AABB, a child window will typically be clipped by its parent (not always) - ImRect bb(window->OuterRectClipped.Min - g.Style.TouchExtraPadding, window->OuterRectClipped.Max + g.Style.TouchExtraPadding); - if (bb.Contains(g.IO.MousePos)) - { - if (hovered_window == NULL) - hovered_window = window; - if (hovered_window) - break; - } + ImRect bb(window->OuterRectClipped); + if ((window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_NoResize)) + bb.Expand(padding_regular); + else + bb.Expand(padding_for_resize_from_edges); + if (!bb.Contains(g.IO.MousePos)) + continue; + + // Those seemingly unnecessary extra tests are because the code here is a little different in viewport/docking branches. + if (hovered_window == NULL) + hovered_window = window; + if (hovered_window) + break; } g.HoveredWindow = hovered_window; @@ -4367,10 +4378,10 @@ static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_ { ImRect rect = window->Rect(); if (thickness == 0.0f) rect.Max -= ImVec2(1,1); - if (border_n == 0) return ImRect(rect.Min.x + perp_padding, rect.Min.y, rect.Max.x - perp_padding, rect.Min.y + thickness); - if (border_n == 1) return ImRect(rect.Max.x - thickness, rect.Min.y + perp_padding, rect.Max.x, rect.Max.y - perp_padding); - if (border_n == 2) return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness, rect.Max.x - perp_padding, rect.Max.y); - if (border_n == 3) return ImRect(rect.Min.x, rect.Min.y + perp_padding, rect.Min.x + thickness, rect.Max.y - perp_padding); + if (border_n == 0) return ImRect(rect.Min.x + perp_padding, rect.Min.y - thickness, rect.Max.x - perp_padding, rect.Min.y + thickness); + if (border_n == 1) return ImRect(rect.Max.x - thickness, rect.Min.y + perp_padding, rect.Max.x + thickness, rect.Max.y - perp_padding); + if (border_n == 2) return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness, rect.Max.x - perp_padding, rect.Max.y + thickness); + if (border_n == 3) return ImRect(rect.Min.x - thickness, rect.Min.y + perp_padding, rect.Min.x + thickness, rect.Max.y - perp_padding); IM_ASSERT(0); return ImRect(); } @@ -4382,10 +4393,13 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au ImGuiWindowFlags flags = window->Flags; if ((flags & ImGuiWindowFlags_NoResize) || (flags & ImGuiWindowFlags_AlwaysAutoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0) return; + if (window->WasActive == false) // Early out to avoid running this code for e.g. an hidden implicit Debug window. + return; const int resize_border_count = g.IO.ConfigResizeWindowsFromEdges ? 4 : 0; const float grip_draw_size = (float)(int)ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f); - const float grip_hover_size = (float)(int)(grip_draw_size * 0.75f); + const float grip_hover_inner_size = (float)(int)(grip_draw_size * 0.75f); + const float grip_hover_outer_size = g.IO.ConfigResizeWindowsFromEdges ? RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS : 0.0f; ImVec2 pos_target(FLT_MAX, FLT_MAX); ImVec2 size_target(FLT_MAX, FLT_MAX); @@ -4398,11 +4412,12 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPos); // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window - ImRect resize_rect(corner, corner + grip.InnerDir * grip_hover_size); + ImRect resize_rect(corner - grip.InnerDir * grip_hover_outer_size, corner + grip.InnerDir * grip_hover_inner_size); if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x); if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y); bool hovered, held; ButtonBehavior(resize_rect, window->GetID((void*)(intptr_t)resize_grip_n), &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus); + //GetOverlayDrawList()->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255)); if (hovered || held) g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE; @@ -4416,7 +4431,7 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au { // Resize from any of the four corners // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position - ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + resize_rect.GetSize() * grip.CornerPos; // Corner of the window corresponding to our corner grip + ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + ImLerp(grip.InnerDir * grip_hover_outer_size, grip.InnerDir * -grip_hover_inner_size, grip.CornerPos); // Corner of the window corresponding to our corner grip CalcResizePosSizeFromAnyCorner(window, corner_target, grip.CornerPos, &pos_target, &size_target); } if (resize_grip_n == 0 || held || hovered) @@ -4424,12 +4439,11 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au } for (int border_n = 0; border_n < resize_border_count; border_n++) { - const float BORDER_SIZE = 5.0f; // FIXME: Only works _inside_ window because of HoveredWindow check. - const float BORDER_APPEAR_TIMER = 0.05f; // Reduce visual noise bool hovered, held; - ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_size, BORDER_SIZE); + ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS); ButtonBehavior(border_rect, window->GetID((void*)(intptr_t)(border_n + 4)), &hovered, &held, ImGuiButtonFlags_FlattenChildren); - if ((hovered && g.HoveredIdTimer > BORDER_APPEAR_TIMER) || held) + //GetOverlayDrawList()->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255)); + if ((hovered && g.HoveredIdTimer > RESIZE_WINDOWS_FROM_EDGES_FEEDBACK_TIMER) || held) { g.MouseCursor = (border_n & 1) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS; if (held) *border_held = border_n; @@ -4438,10 +4452,10 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au { ImVec2 border_target = window->Pos; ImVec2 border_posn; - if (border_n == 0) { border_posn = ImVec2(0, 0); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y); } - if (border_n == 1) { border_posn = ImVec2(1, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + BORDER_SIZE); } - if (border_n == 2) { border_posn = ImVec2(0, 1); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + BORDER_SIZE); } - if (border_n == 3) { border_posn = ImVec2(0, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x); } + if (border_n == 0) { border_posn = ImVec2(0, 0); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS); } + if (border_n == 1) { border_posn = ImVec2(1, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS); } + if (border_n == 2) { border_posn = ImVec2(0, 1); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS); } + if (border_n == 3) { border_posn = ImVec2(0, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS); } CalcResizePosSizeFromAnyCorner(window, border_target, border_posn, &pos_target, &size_target); } }