From dd15544751c6717044deba44a15f6d179eb724f4 Mon Sep 17 00:00:00 2001 From: Andersama Date: Fri, 6 Sep 2024 08:51:26 -0700 Subject: [PATCH] Add minimize/maximize window flags TODO: actual work of rendering window --- imgui.cpp | 60 +++++++++++++++++++++++++++++++++++++++ imgui.h | 5 ++++ imgui_internal.h | 6 ++++ imgui_widgets.cpp | 72 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 143 insertions(+) diff --git a/imgui.cpp b/imgui.cpp index 1d9d8b899..964ac881c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6470,6 +6470,8 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl const bool has_close_button = (p_open != NULL); const bool has_collapse_button = !(flags & ImGuiWindowFlags_NoCollapse) && (style.WindowMenuButtonPosition != ImGuiDir_None); + const bool has_minimize_button = flags & ImGuiWindowFlags_Minimize; + const bool has_maximize_button = flags & ImGuiWindowFlags_Maximize; // Close & Collapse button are on the Menu NavLayer and don't default focus (unless there's nothing else on that layer) // FIXME-NAV: Might want (or not?) to set the equivalent of ImGuiButtonFlags_NoNavFocus so that mouse clicks on standard title bar items don't necessarily set nav/keyboard ref? @@ -6483,12 +6485,24 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl float pad_r = style.FramePadding.x; float button_sz = g.FontSize; ImVec2 close_button_pos; + ImVec2 minimize_button_pos; + ImVec2 maximize_button_pos; ImVec2 collapse_button_pos; if (has_close_button) { close_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - button_sz, title_bar_rect.Min.y + style.FramePadding.y); pad_r += button_sz + style.ItemInnerSpacing.x; } + if (has_maximize) + { + maximize_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - button_sz, title_bar_rect.Min.y + style.FramePadding.y); + pad_r += button_sz + style.ItemInnerSpacing.x; + } + if (has_minimize) + { + minimize_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - button_sz, title_bar_rect.Min.y + style.FramePadding.y); + pad_r += button_sz + style.ItemInnerSpacing.x; + } if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Right) { collapse_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - button_sz, title_bar_rect.Min.y + style.FramePadding.y); @@ -6510,6 +6524,20 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl if (CloseButton(window->GetID("#CLOSE"), close_button_pos)) *p_open = false; + // Maximize button + if (has_maximize_button) { + if (CloseButton(window->GetID("#MAXIMIZE"), maximize_button_pos, window->Maximized)) { + window->WantMaximizeToggle = true; + } + } + + // Minimize button + if (has_minimize_button) { + if (CloseButton(window->GetID("#MINIMIZE"), minimize_button_pos, window->Minimized)) { + window->WantMinimizeToggle = true; + } + } + window->DC.NavLayerCurrent = ImGuiNavLayer_Main; g.CurrentItemFlags = item_flags_backup; @@ -6924,6 +6952,26 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } window->WantCollapseToggle = false; + if (flags & ImGuiWindowFlags_Maximize) { + if (window->WantMaximizeToggle) { + window->Maximized = !window->Maximized; + MarkIniSettingsDirty(window); + } + } else { + window->Maximized = false; + } + window->WantMaximizeToggle = false; + + if (flags & ImGuiWindowFlags_Minimize) { + if (window->WantMinimizeToggle) { + window->Minimized = !window->Minimized; + MarkIniSettingsDirty(window); + } + } else { + window->Minimized = false; + } + window->WantMinimizeToggle = false; + // SIZE // Outer Decoration Sizes @@ -8033,6 +8081,18 @@ bool ImGui::IsWindowCollapsed() return window->Collapsed; } +bool ImGui::IsWindowMinimized() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return window->Minimized; +} + +bool ImGui::IsWindowMaximized() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return window->Maximized; +} + bool ImGui::IsWindowAppearing() { ImGuiWindow* window = GetCurrentWindowRead(); diff --git a/imgui.h b/imgui.h index 0394967ee..4196a0cd5 100644 --- a/imgui.h +++ b/imgui.h @@ -392,6 +392,8 @@ namespace ImGui // - 'current window' = the window we are appending into while inside a Begin()/End() block. 'next window' = next window we will Begin() into. IMGUI_API bool IsWindowAppearing(); IMGUI_API bool IsWindowCollapsed(); + IMGUI_API bool IsWindowMinimized(); + IMGUI_API bool IsWindowMaximized(); IMGUI_API bool IsWindowFocused(ImGuiFocusedFlags flags=0); // is current window focused? or its root/child, depending on flags. see flags for options. IMGUI_API bool IsWindowHovered(ImGuiHoveredFlags flags=0); // is current window hovered and hoverable (e.g. not blocked by a popup/modal)? See ImGuiHoveredFlags_ for options. IMPORTANT: If you are trying to check whether your mouse should be dispatched to Dear ImGui or to your underlying app, you should not use this function! Use the 'io.WantCaptureMouse' boolean for that! Refer to FAQ entry "How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?" for details. IMGUI_API ImDrawList* GetWindowDrawList(); // get draw list associated to the current window, to append your own drawing primitives @@ -1079,6 +1081,9 @@ enum ImGuiWindowFlags_ ImGuiWindowFlags_NoNavInputs = 1 << 16, // No gamepad/keyboard navigation within the window ImGuiWindowFlags_NoNavFocus = 1 << 17, // No focusing toward this window with gamepad/keyboard navigation (e.g. skipped by CTRL+TAB) ImGuiWindowFlags_UnsavedDocument = 1 << 18, // Display a dot next to the title. When used in a tab/docking context, tab is selected when clicking the X + closure is not assumed (will wait for user to stop submitting the tab). Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar. + ImGuiWindowFlags_Minimize = 1 << 19, // Enable minimize button + ImGuiWindowFlags_Maximize = 1 << 20, // Enable maximize and restore button + ImGuiWindowFlags_NoNav = ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus, ImGuiWindowFlags_NoDecoration = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoCollapse, ImGuiWindowFlags_NoInputs = ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus, diff --git a/imgui_internal.h b/imgui_internal.h index 5d23ebfef..def3c853f 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2639,6 +2639,10 @@ struct IMGUI_API ImGuiWindow bool WriteAccessed; // Set to true when any widget access the current window bool Collapsed; // Set when collapsing window to become only title-bar bool WantCollapseToggle; + bool Minimized; + bool WantMinimizeToggle; + bool Maximized; + bool WantMaximizeToggle; bool SkipItems; // Set when items can safely be all clipped (e.g. window not visible or collapsed) bool SkipRefresh; // [EXPERIMENTAL] Reuse previous frame drawn contents, Begin() returns false. bool Appearing; // Set during the frame where the window is appearing (or re-appearing) @@ -3545,6 +3549,8 @@ namespace ImGui // Widgets: Window Decorations IMGUI_API bool CloseButton(ImGuiID id, const ImVec2& pos); IMGUI_API bool CollapseButton(ImGuiID id, const ImVec2& pos); + IMGUI_API bool MinimizeButton(ImGuiID id, const ImVec2& pos, bool minimized); + IMGUI_API bool MaximizeButton(ImGuiID id, const ImVec2& pos, bool maximized); IMGUI_API void Scrollbar(ImGuiAxis axis); IMGUI_API bool ScrollbarEx(const ImRect& bb, ImGuiID id, ImGuiAxis axis, ImS64* p_scroll_v, ImS64 avail_v, ImS64 contents_v, ImDrawFlags flags); IMGUI_API ImRect GetWindowScrollbarRect(ImGuiWindow* window, ImGuiAxis axis); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 7b86fb8be..1c99d012f 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -863,6 +863,78 @@ bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos) return pressed; } +// Button to close a window +bool ImGui::MinimizeButton(ImGuiID id, const ImVec2& pos, bool minimized) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + + // Tweak 1: Shrink hit-testing area if button covers an abnormally large proportion of the visible region. That's in order to facilitate moving the window away. (#3825) + // This may better be applied as a general hit-rect reduction mechanism for all widgets to ensure the area to move window is always accessible? + const ImRect bb(pos, pos + ImVec2(g.FontSize, g.FontSize)); + ImRect bb_interact = bb; + const float area_to_visible_ratio = window->OuterRectClipped.GetArea() / bb.GetArea(); + if (area_to_visible_ratio < 1.5f) + bb_interact.Expand(ImTrunc(bb_interact.GetSize() * -0.25f)); + + // Tweak 2: We intentionally allow interaction when clipped so that a mechanical Alt,Right,Activate sequence can always close a window. + // (this isn't the common behavior of buttons, but it doesn't affect the user because navigation tends to keep items visible in scrolling layer). + bool is_clipped = !ItemAdd(bb_interact, id); + + bool hovered, held; + bool pressed = ButtonBehavior(bb_interact, id, &hovered, &held); + if (is_clipped) + return pressed; + + // Render + ImU32 bg_col = GetColorU32(held ? ImGuiCol_ButtonActive : ImGuiCol_ButtonHovered); + if (hovered) + window->DrawList->AddRectFilled(bb.Min, bb.Max, bg_col); + RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_Compact); + ImU32 cross_col = GetColorU32(ImGuiCol_Text); + ImVec2 cross_center = bb.GetCenter() - ImVec2(0.5f, 0.5f); + float cross_extent = g.FontSize * 0.5f * 0.7071f - 1.0f; + window->DrawList->AddLine(cross_center + ImVec2(+cross_extent, 0.0f), cross_center + ImVec2(-cross_extent, 0.0f), cross_col, 1.0f); + + return pressed; +} + +// Button to close a window +bool ImGui::MaximizeButton(ImGuiID id, const ImVec2& pos, bool maximized) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + + // Tweak 1: Shrink hit-testing area if button covers an abnormally large proportion of the visible region. That's in order to facilitate moving the window away. (#3825) + // This may better be applied as a general hit-rect reduction mechanism for all widgets to ensure the area to move window is always accessible? + const ImRect bb(pos, pos + ImVec2(g.FontSize, g.FontSize)); + ImRect bb_interact = bb; + const float area_to_visible_ratio = window->OuterRectClipped.GetArea() / bb.GetArea(); + if (area_to_visible_ratio < 1.5f) + bb_interact.Expand(ImTrunc(bb_interact.GetSize() * -0.25f)); + + // Tweak 2: We intentionally allow interaction when clipped so that a mechanical Alt,Right,Activate sequence can always close a window. + // (this isn't the common behavior of buttons, but it doesn't affect the user because navigation tends to keep items visible in scrolling layer). + bool is_clipped = !ItemAdd(bb_interact, id); + + bool hovered, held; + bool pressed = ButtonBehavior(bb_interact, id, &hovered, &held); + if (is_clipped) + return pressed; + + // Render + ImU32 bg_col = GetColorU32(held ? ImGuiCol_ButtonActive : ImGuiCol_ButtonHovered); + if (hovered) + window->DrawList->AddRectFilled(bb.Min, bb.Max, bg_col); + RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_Compact); + ImU32 cross_col = GetColorU32(ImGuiCol_Text); + ImVec2 cross_center = bb.GetCenter() - ImVec2(0.5f, 0.5f); + float cross_extent = g.FontSize * 0.5f * 0.7071f - 1.0f; + window->DrawList->AddRect(cross_center + ImVec2(-cross_extent, -cross_extent), cross_center + ImVec2( + cross_extent, + cross_extent), cross_col, 0.0f, 0, 1.0f); + + return pressed; +} + bool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos) { ImGuiContext& g = *GImGui;