Skip to content

Commit

Permalink
Window: Resizing from edges (with io.ConfigResizeWindowsFromEdges Bet…
Browse files Browse the repository at this point in the history
…a 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. (ocornut#1495, ocornut#822, ocornut#2110)
  • Loading branch information
ocornut committed Oct 2, 2018
1 parent 76e31bd commit ae7f833
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 25 deletions.
3 changes: 3 additions & 0 deletions docs/CHANGELOG.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
64 changes: 39 additions & 25 deletions imgui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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];
Expand All @@ -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;
Expand Down Expand Up @@ -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();
}
Expand All @@ -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);
Expand All @@ -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;

Expand All @@ -4416,20 +4431,19 @@ 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)
resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);
}
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;
Expand All @@ -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);
}
}
Expand Down

0 comments on commit ae7f833

Please sign in to comment.