mirror of
				https://github.com/Drezil/imgui.git
				synced 2025-10-31 05:01:05 +01:00 
			
		
		
		
	Added SetNextWindowSizeConstraint() + demo code (#668)
This commit is contained in:
		
							
								
								
									
										48
									
								
								imgui.cpp
									
									
									
									
									
								
							
							
						
						
									
										48
									
								
								imgui.cpp
									
									
									
									
									
								
							| @@ -457,7 +457,6 @@ | ||||
|  The list below consist mostly of notes of things to do before they are requested/discussed by users (at that point it usually happens on the github) | ||||
|  | ||||
|  - doc: add a proper documentation+regression testing system (#435) | ||||
|  - window: maximum window size settings (per-axis). for large popups in particular user may not want the popup to fill all space. | ||||
|  - window: add a way for very transient windows (non-saved, temporary overlay over hundreds of objects) to "clean" up from the global window list. perhaps a lightweight explicit cleanup pass. | ||||
|  - window: calling SetNextWindowSize() every frame with <= 0 doesn't do anything, may be useful to allow (particularly when used for a single axis). | ||||
|  - window: auto-fit feedback loop when user relies on any dynamic layout (window width multiplier, column) appears weird to end-user. clarify. | ||||
| @@ -3416,7 +3415,7 @@ static inline void ClearSetNextWindowData() | ||||
| { | ||||
|     ImGuiContext& g = *GImGui; | ||||
|     g.SetNextWindowPosCond = g.SetNextWindowSizeCond = g.SetNextWindowContentSizeCond = g.SetNextWindowCollapsedCond = 0; | ||||
|     g.SetNextWindowFocus = false; | ||||
|     g.SetNextWindowSizeConstraint = g.SetNextWindowFocus = false; | ||||
| } | ||||
|  | ||||
| static bool BeginPopupEx(const char* str_id, ImGuiWindowFlags extra_flags) | ||||
| @@ -3731,6 +3730,31 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFl | ||||
|     return window; | ||||
| } | ||||
|  | ||||
| static void ApplySizeFullWithConstraint(ImGuiWindow* window, ImVec2 new_size) | ||||
| { | ||||
|     ImGuiContext& g = *GImGui; | ||||
|     if (g.SetNextWindowSizeConstraint) | ||||
|     { | ||||
|         // Using -1,-1 on either X/Y axis to preserve the current size. | ||||
|         ImRect cr = g.SetNextWindowSizeConstraintRect; | ||||
|         new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x; | ||||
|         new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y; | ||||
|         if (g.SetNextWindowSizeConstraintCallback) | ||||
|         { | ||||
|             ImGuiSizeConstraintCallbackData data; | ||||
|             data.UserData = g.SetNextWindowSizeConstraintCallbackUserData; | ||||
|             data.Pos = window->Pos; | ||||
|             data.CurrentSize = window->SizeFull; | ||||
|             data.DesiredSize = new_size; | ||||
|             g.SetNextWindowSizeConstraintCallback(&data); | ||||
|             new_size = data.DesiredSize; | ||||
|         } | ||||
|     } | ||||
|     if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize))) | ||||
|         new_size = ImMax(new_size, g.Style.WindowMinSize); | ||||
|     window->SizeFull = new_size; | ||||
| } | ||||
|  | ||||
| // Push a new ImGui window to add widgets to. | ||||
| // - A default window called "Debug" is automatically stacked at the beginning of every frame so you can use widgets without explicitly calling a Begin/End pair. | ||||
| // - Begin/End can be called multiple times during the frame with the same window name to append content. | ||||
| @@ -3972,9 +3996,8 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Apply window size constraints and final size | ||||
|         if (!(flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize))) | ||||
|             window->SizeFull = ImMax(window->SizeFull, style.WindowMinSize); | ||||
|         // Apply minimum/maximum window size constraints and final size | ||||
|         ApplySizeFullWithConstraint(window, window->SizeFull); | ||||
|         window->Size = window->Collapsed ? window->TitleBarRect().GetSize() : window->SizeFull; | ||||
|          | ||||
|         // POSITION | ||||
| @@ -4096,7 +4119,7 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us | ||||
|                 if (g.HoveredWindow == window && held && g.IO.MouseDoubleClicked[0]) | ||||
|                 { | ||||
|                     // Manual auto-fit when double-clicking | ||||
|                     window->SizeFull = size_auto_fit; | ||||
|                     ApplySizeFullWithConstraint(window, size_auto_fit); | ||||
|                     if (!(flags & ImGuiWindowFlags_NoSavedSettings)) | ||||
|                         MarkSettingsDirty(); | ||||
|                     SetActiveID(0); | ||||
| @@ -4104,7 +4127,7 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us | ||||
|                 else if (held) | ||||
|                 { | ||||
|                     // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position | ||||
|                     window->SizeFull = ImMax((g.IO.MousePos - g.ActiveIdClickOffset + resize_rect.GetSize()) - window->Pos, style.WindowMinSize); | ||||
|                     ApplySizeFullWithConstraint(window, (g.IO.MousePos - g.ActiveIdClickOffset + resize_rect.GetSize()) - window->Pos); | ||||
|                     if (!(flags & ImGuiWindowFlags_NoSavedSettings)) | ||||
|                         MarkSettingsDirty(); | ||||
|                 } | ||||
| @@ -4177,6 +4200,7 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us | ||||
|  | ||||
|         // Setup drawing context | ||||
|         window->DC.IndentX = 0.0f + window->WindowPadding.x - window->Scroll.x; | ||||
| 		window->DC.GroupOffsetX = 0.0f; | ||||
|         window->DC.ColumnsOffsetX = 0.0f; | ||||
|         window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.IndentX + window->DC.ColumnsOffsetX, window->TitleBarHeight() + window->MenuBarHeight() + window->WindowPadding.y - window->Scroll.y); | ||||
|         window->DC.CursorPos = window->DC.CursorStartPos; | ||||
| @@ -4268,6 +4292,7 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us | ||||
|     if (first_begin_of_the_frame) | ||||
|         window->Accessed = false; | ||||
|     window->BeginCount++; | ||||
|     g.SetNextWindowSizeConstraint = false; | ||||
|  | ||||
|     // Child window can be out of sight and have "negative" clip windows. | ||||
|     // Mark them as collapsed so commands are skipped earlier (we can't manually collapse because they have no title bar). | ||||
| @@ -4906,6 +4931,15 @@ void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiSetCond cond) | ||||
|     g.SetNextWindowSizeCond = cond ? cond : ImGuiSetCond_Always; | ||||
| } | ||||
|  | ||||
| void ImGui::SetNextWindowSizeConstraint(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeConstraintCallback custom_callback, void* custom_callback_user_data) | ||||
| { | ||||
|     ImGuiContext& g = *GImGui; | ||||
|     g.SetNextWindowSizeConstraint = true; | ||||
|     g.SetNextWindowSizeConstraintRect = ImRect(size_min, size_max); | ||||
|     g.SetNextWindowSizeConstraintCallback = custom_callback; | ||||
|     g.SetNextWindowSizeConstraintCallbackUserData = custom_callback_user_data; | ||||
| } | ||||
|  | ||||
| void ImGui::SetNextWindowContentSize(const ImVec2& size) | ||||
| { | ||||
|     ImGuiContext& g = *GImGui; | ||||
|   | ||||
							
								
								
									
										15
									
								
								imgui.h
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								imgui.h
									
									
									
									
									
								
							| @@ -52,7 +52,8 @@ struct ImGuiStorage;                // Simple custom key value storage | ||||
| struct ImGuiStyle;                  // Runtime data for styling/colors | ||||
| struct ImGuiTextFilter;             // Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]" | ||||
| struct ImGuiTextBuffer;             // Text buffer for logging/accumulating text | ||||
| struct ImGuiTextEditCallbackData;   // Shared state of ImGui::InputText() when using custom callbacks (advanced) | ||||
| struct ImGuiTextEditCallbackData;   // Shared state of ImGui::InputText() when using custom ImGuiTextEditCallback (rare/advanced use) | ||||
| struct ImGuiSizeConstraintCallbackData;// Structure used to constraint window size in custom ways when using custom ImGuiSizeConstraintCallback (rare/advanced use) | ||||
| struct ImGuiListClipper;            // Helper to manually clip large list of items | ||||
| struct ImGuiContext;                // ImGui context (opaque) | ||||
|  | ||||
| @@ -73,6 +74,7 @@ typedef int ImGuiInputTextFlags;    // flags for InputText*()               // e | ||||
| typedef int ImGuiSelectableFlags;   // flags for Selectable()               // enum ImGuiSelectableFlags_ | ||||
| typedef int ImGuiTreeNodeFlags;     // flags for TreeNode*(), Collapsing*() // enum ImGuiTreeNodeFlags_ | ||||
| typedef int (*ImGuiTextEditCallback)(ImGuiTextEditCallbackData *data); | ||||
| typedef void (*ImGuiSizeConstraintCallback)(ImGuiSizeConstraintCallbackData* data); | ||||
|  | ||||
| // Others helpers at bottom of the file: | ||||
| // class ImVector<>                 // Lightweight std::vector like class. | ||||
| @@ -138,6 +140,7 @@ namespace ImGui | ||||
|     IMGUI_API void          SetNextWindowPos(const ImVec2& pos, ImGuiSetCond cond = 0);         // set next window position. call before Begin() | ||||
|     IMGUI_API void          SetNextWindowPosCenter(ImGuiSetCond cond = 0);                      // set next window position to be centered on screen. call before Begin() | ||||
|     IMGUI_API void          SetNextWindowSize(const ImVec2& size, ImGuiSetCond cond = 0);       // set next window size. set axis to 0.0f to force an auto-fit on this axis. call before Begin() | ||||
|     IMGUI_API void          SetNextWindowSizeConstraint(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeConstraintCallback custom_callback = NULL, void* custom_callback_data = NULL); // set next window size limits. use -1,-1 on either X/Y axis to preserve the current size. Use callback to apply non-trivial programmatic constraints. | ||||
|     IMGUI_API void          SetNextWindowContentSize(const ImVec2& size);                       // set next window content size (enforce the range of scrollbars). set axis to 0.0f to leave it automatic. call before Begin() | ||||
|     IMGUI_API void          SetNextWindowContentWidth(float width);                             // set next window content width (enforce the range of horizontal scrollbar). call before Begin() | ||||
|     IMGUI_API void          SetNextWindowCollapsed(bool collapsed, ImGuiSetCond cond = 0);      // set next window collapsed state. call before Begin() | ||||
| @@ -1029,6 +1032,16 @@ struct ImGuiTextEditCallbackData | ||||
|     bool    HasSelection() const { return SelectionStart != SelectionEnd; } | ||||
| }; | ||||
|  | ||||
| // Resizing callback data to apply custom constraint. As enabled by SetNextWindowSizeConstraint(). Callback is called during the next Begin(). | ||||
| // NB: For basic min/max size constraint on each axis you don't need to use the callback! The SetNextWindowSizeConstraint() parameters are enough. | ||||
| struct ImGuiSizeConstraintCallbackData | ||||
| { | ||||
|     void*   UserData;       // Read-only.   What user passed to SetNextWindowSizeConstraint() | ||||
|     ImVec2  Pos;            // Read-only.	Window position, for reference. | ||||
|     ImVec2  CurrentSize;    // Read-only.	Current window size. | ||||
|     ImVec2  DesiredSize;    // Read-write.  Desired size, based on user's mouse position. Write to this field to restrain resizing. | ||||
| }; | ||||
|  | ||||
| // ImColor() helper to implicity converts colors to either ImU32 (packed 4x1 byte) or ImVec4 (4x1 float) | ||||
| // Prefer using IM_COL32() macros if you want a guaranteed compile-time ImU32 for usage with ImDrawList API. | ||||
| // Avoid storing ImColor! Store either u32 of ImVec4. This is not a full-featured color class. | ||||
|   | ||||
| @@ -46,6 +46,7 @@ | ||||
| #endif | ||||
|  | ||||
| #define IM_ARRAYSIZE(_ARR)  ((int)(sizeof(_ARR)/sizeof(*_ARR))) | ||||
| #define IM_MAX(_A,_B)       (((_A) >= (_B)) ? (_A) : (_B)) | ||||
|  | ||||
| //----------------------------------------------------------------------------- | ||||
| // DEMO CODE | ||||
| @@ -59,6 +60,7 @@ static void ShowExampleAppLayout(bool* p_open); | ||||
| static void ShowExampleAppPropertyEditor(bool* p_open); | ||||
| static void ShowExampleAppLongText(bool* p_open); | ||||
| static void ShowExampleAppAutoResize(bool* p_open); | ||||
| static void ShowExampleAppConstrainedResize(bool* p_open); | ||||
| static void ShowExampleAppFixedOverlay(bool* p_open); | ||||
| static void ShowExampleAppManipulatingWindowTitle(bool* p_open); | ||||
| static void ShowExampleAppCustomRendering(bool* p_open); | ||||
| @@ -105,6 +107,7 @@ void ImGui::ShowTestWindow(bool* p_open) | ||||
|     static bool show_app_property_editor = false; | ||||
|     static bool show_app_long_text = false; | ||||
|     static bool show_app_auto_resize = false; | ||||
|     static bool show_app_constrained_resize = false; | ||||
|     static bool show_app_fixed_overlay = false; | ||||
|     static bool show_app_manipulating_window_title = false; | ||||
|     static bool show_app_custom_rendering = false; | ||||
| @@ -120,6 +123,7 @@ void ImGui::ShowTestWindow(bool* p_open) | ||||
|     if (show_app_property_editor) ShowExampleAppPropertyEditor(&show_app_property_editor); | ||||
|     if (show_app_long_text) ShowExampleAppLongText(&show_app_long_text); | ||||
|     if (show_app_auto_resize) ShowExampleAppAutoResize(&show_app_auto_resize); | ||||
|     if (show_app_constrained_resize) ShowExampleAppConstrainedResize(&show_app_constrained_resize); | ||||
|     if (show_app_fixed_overlay) ShowExampleAppFixedOverlay(&show_app_fixed_overlay); | ||||
|     if (show_app_manipulating_window_title) ShowExampleAppManipulatingWindowTitle(&show_app_manipulating_window_title); | ||||
|     if (show_app_custom_rendering) ShowExampleAppCustomRendering(&show_app_custom_rendering); | ||||
| @@ -183,6 +187,7 @@ void ImGui::ShowTestWindow(bool* p_open) | ||||
|             ImGui::MenuItem("Property editor", NULL, &show_app_property_editor); | ||||
|             ImGui::MenuItem("Long text display", NULL, &show_app_long_text); | ||||
|             ImGui::MenuItem("Auto-resizing window", NULL, &show_app_auto_resize); | ||||
|             ImGui::MenuItem("Constrained-resizing window", NULL, &show_app_constrained_resize); | ||||
|             ImGui::MenuItem("Simple overlay", NULL, &show_app_fixed_overlay); | ||||
|             ImGui::MenuItem("Manipulating window title", NULL, &show_app_manipulating_window_title); | ||||
|             ImGui::MenuItem("Custom rendering", NULL, &show_app_custom_rendering); | ||||
| @@ -1793,6 +1798,43 @@ static void ShowExampleAppAutoResize(bool* p_open) | ||||
|     ImGui::End(); | ||||
| } | ||||
|  | ||||
| static void ShowExampleAppConstrainedResize(bool* p_open) | ||||
| { | ||||
|     struct CustomConstraints // Helper functions to demonstrate programmatic constraints | ||||
|     { | ||||
|         static void Square(ImGuiSizeConstraintCallbackData* data) { data->DesiredSize = ImVec2(IM_MAX(data->DesiredSize.x, data->DesiredSize.y), IM_MAX(data->DesiredSize.x, data->DesiredSize.y)); } | ||||
|         static void Step(ImGuiSizeConstraintCallbackData* data)   { float step = (float)(int)data->UserData; data->DesiredSize = ImVec2((int)(data->DesiredSize.x / step + 0.5f) * step, (int)(data->DesiredSize.y / step + 0.5f) * step); } | ||||
|     }; | ||||
|  | ||||
|     static int type = 0; | ||||
|     if (type == 0) ImGui::SetNextWindowSizeConstraint(ImVec2(-1, 0),    ImVec2(-1, FLT_MAX));      // Vertical only | ||||
|     if (type == 1) ImGui::SetNextWindowSizeConstraint(ImVec2(0, -1),    ImVec2(FLT_MAX, -1));      // Horizontal only | ||||
|     if (type == 2) ImGui::SetNextWindowSizeConstraint(ImVec2(100, 100), ImVec2(FLT_MAX, FLT_MAX)); // Width > 100, Height > 100 | ||||
|     if (type == 3) ImGui::SetNextWindowSizeConstraint(ImVec2(300, 0),   ImVec2(400, FLT_MAX));     // Width 300-400 | ||||
|     if (type == 4) ImGui::SetNextWindowSizeConstraint(ImVec2(0, 0),     ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::Square);          // Always Square | ||||
|     if (type == 5) ImGui::SetNextWindowSizeConstraint(ImVec2(0, 0),     ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::Step, (void*)100);// Fixed Step | ||||
|  | ||||
|     if (ImGui::Begin("Example: Constrained Resize", p_open)) | ||||
|     { | ||||
|         const char* desc[] =  | ||||
|         { | ||||
|             "Resize vertical only", | ||||
|             "Resize horizontal only", | ||||
|             "Width > 100, Height > 100", | ||||
|             "Width 300-400", | ||||
|             "Custom: Always Square", | ||||
|             "Custom: Fixed Steps (100)", | ||||
|         }; | ||||
|         ImGui::Combo("Constraint", &type, desc, IM_ARRAYSIZE(desc));  | ||||
|         if (ImGui::Button("200x200")) ImGui::SetWindowSize(ImVec2(200,200)); ImGui::SameLine(); | ||||
|         if (ImGui::Button("500x500")) ImGui::SetWindowSize(ImVec2(500,500)); ImGui::SameLine(); | ||||
|         if (ImGui::Button("800x200")) ImGui::SetWindowSize(ImVec2(800,200)); | ||||
|         for (int i = 0; i < 10; i++)  | ||||
|             ImGui::Text("Hello, sailor! Making this line long enough for the example."); | ||||
|     } | ||||
|     ImGui::End(); | ||||
| } | ||||
|  | ||||
| static void ShowExampleAppFixedOverlay(bool* p_open) | ||||
| { | ||||
|     ImGui::SetNextWindowPos(ImVec2(10,10)); | ||||
|   | ||||
| @@ -394,6 +394,10 @@ struct ImGuiContext | ||||
|     ImGuiSetCond            SetNextWindowSizeCond; | ||||
|     ImGuiSetCond            SetNextWindowContentSizeCond; | ||||
|     ImGuiSetCond            SetNextWindowCollapsedCond; | ||||
|     ImRect                  SetNextWindowSizeConstraintRect;           // Valid if 'SetNextWindowSizeConstraint' is true | ||||
|     ImGuiSizeConstraintCallback SetNextWindowSizeConstraintCallback; | ||||
|     void*                       SetNextWindowSizeConstraintCallbackUserData; | ||||
|     bool                    SetNextWindowSizeConstraint; | ||||
|     bool                    SetNextWindowFocus; | ||||
|     bool                    SetNextTreeNodeOpenVal; | ||||
|     ImGuiSetCond            SetNextTreeNodeOpenCond; | ||||
| @@ -472,6 +476,8 @@ struct ImGuiContext | ||||
|         SetNextWindowContentSizeCond = 0; | ||||
|         SetNextWindowCollapsedCond = 0; | ||||
|         SetNextWindowFocus = false; | ||||
|         SetNextWindowSizeConstraintCallback = NULL; | ||||
|         SetNextWindowSizeConstraintCallbackUserData = NULL; | ||||
|         SetNextTreeNodeOpenVal = false; | ||||
|         SetNextTreeNodeOpenCond = 0; | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user