Skip to content

Commit ebf3a08

Browse files
thedmdpthom
authored andcommitted
thedmd: Stack Layout implementation v2.1 / pthom: revert clipping
See ocornut#846 (comment) : clipping has an issue, when mixed with your node editor and springs.
1 parent d1da769 commit ebf3a08

File tree

7 files changed

+1401
-4
lines changed

7 files changed

+1401
-4
lines changed

imgui.cpp

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1314,6 +1314,7 @@ ImGuiStyle::ImGuiStyle()
13141314
ScrollbarRounding = 9.0f; // Radius of grab corners rounding for scrollbar
13151315
GrabMinSize = 12.0f; // Minimum width/height of a grab box for slider/scrollbar
13161316
GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
1317+
LayoutAlign = 0.5f; // Element alignment inside horizontal and vertical layouts (0.0f - left/top, 1.0f - right/bottom, 0.5f - center).
13171318
LogSliderDeadzone = 4.0f; // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero.
13181319
TabRounding = 4.0f; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
13191320
TabBorderSize = 0.0f; // Thickness of border around tabs.
@@ -3403,6 +3404,7 @@ static const ImGuiDataVarInfo GStyleVarInfo[] =
34033404
{ ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, ScrollbarRounding) }, // ImGuiStyleVar_ScrollbarRounding
34043405
{ ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, GrabMinSize) }, // ImGuiStyleVar_GrabMinSize
34053406
{ ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, GrabRounding) }, // ImGuiStyleVar_GrabRounding
3407+
{ ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, LayoutAlign) }, // ImGuiStyleVar_LayoutAlign
34063408
{ ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TabRounding) }, // ImGuiStyleVar_TabRounding
34073409
{ ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TabBorderSize) }, // ImGuiStyleVar_TabBorderSize
34083410
{ ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TabBarBorderSize) }, // ImGuiStyleVar_TabBarBorderSize
@@ -11215,6 +11217,10 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu
1121511217
g.NextItemData.Flags = ImGuiNextItemDataFlags_None;
1121611218
g.NextItemData.ItemFlags = ImGuiItemFlags_None;
1121711219

11220+
#if IMGUI_HAS_STACK_LAYOUT
11221+
ImGuiInternal::UpdateItemRect(window->ID, bb.Min, bb.Max);
11222+
#endif
11223+
1121811224
#ifdef IMGUI_ENABLE_TEST_ENGINE
1121911225
if (id != 0)
1122011226
IMGUI_TEST_ENGINE_ITEM_ADD(id, g.LastItemData.NavRect, &g.LastItemData);
@@ -11296,6 +11302,38 @@ void ImGui::ItemSize(const ImVec2& size, float text_baseline_y)
1129611302
if (window->SkipItems)
1129711303
return;
1129811304

11305+
#if IMGUI_HAS_STACK_LAYOUT
11306+
ImGuiLayoutType layout_type = ImGuiInternal::GetCurrentLayoutType(window->ID);
11307+
#else
11308+
ImGuiLayoutType layout_type = window->DC.LayoutType;
11309+
#endif
11310+
11311+
//if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorPos, 3.0f, IM_COL32(255,255,0,255), 4); // [DEBUG] Widget position
11312+
11313+
// Stack Layouts: Handle horizontal case first to simplify merge in case code handling vertical changes.
11314+
if (layout_type == ImGuiLayoutType_Horizontal)
11315+
{
11316+
const float line_width = ImMax(window->DC.CurrLineSize.x, size.x);
11317+
11318+
// Always align ourselves on pixel boundaries
11319+
//if (g.IO.KeyAlt) window->DrawList->AddRect(window->DC.CursorPos, window->DC.CursorPos + ImVec2(size.x, line_height), IM_COL32(255,0,0,200)); // [DEBUG]
11320+
window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x;
11321+
window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y + size.y;
11322+
window->DC.CursorPos.x = IM_TRUNC(window->DC.CursorPos.x + line_width + g.Style.ItemSpacing.x);
11323+
window->DC.CursorPos.y = IM_TRUNC(window->DC.CursorPosPrevLine.y - size.y);
11324+
window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x - g.Style.ItemSpacing.x);
11325+
window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPosPrevLine.y);
11326+
//if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
11327+
11328+
window->DC.PrevLineSize.x = line_width;
11329+
window->DC.PrevLineSize.y = 0.0f;
11330+
window->DC.CurrLineSize.x = 0.0f;
11331+
window->DC.PrevLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, text_baseline_y);
11332+
window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
11333+
window->DC.IsSameLine = window->DC.IsSetPos = false;
11334+
return;
11335+
}
11336+
1129911337
// We increase the height in this function to accommodate for baseline offset.
1130011338
// In theory we should be offsetting the starting position (window->DC.CursorPos), that will be the topic of a larger refactor,
1130111339
// but since ItemSize() is not yet an API that moves the cursor (to handle e.g. wrapping) enlarging the height has the same effect.
@@ -11314,15 +11352,12 @@ void ImGui::ItemSize(const ImVec2& size, float text_baseline_y)
1131411352
window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y);
1131511353
//if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
1131611354

11355+
window->DC.PrevLineSize.x = 0.0f;
1131711356
window->DC.PrevLineSize.y = line_height;
1131811357
window->DC.CurrLineSize.y = 0.0f;
1131911358
window->DC.PrevLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, text_baseline_y);
1132011359
window->DC.CurrLineTextBaseOffset = 0.0f;
1132111360
window->DC.IsSameLine = window->DC.IsSetPos = false;
11322-
11323-
// Horizontal layout mode
11324-
if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
11325-
SameLine();
1132611361
}
1132711362
IM_MSVC_RUNTIME_CHECKS_RESTORE
1132811363

imgui.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#define IMGUI_HAS_TABLE
3434
#define IMGUI_HAS_VIEWPORT // Viewport WIP branch
3535
#define IMGUI_HAS_DOCK // Docking WIP branch
36+
#define IMGUI_HAS_STACK_LAYOUT 1 // Stack-Layout PR #846
3637

3738
//
3839
// Adaptations for ImGui Bundle are noted with [ADAPT_IMGUI_BUNDLE]
@@ -1856,6 +1857,7 @@ enum ImGuiStyleVar_
18561857
ImGuiStyleVar_ScrollbarRounding, // float ScrollbarRounding
18571858
ImGuiStyleVar_GrabMinSize, // float GrabMinSize
18581859
ImGuiStyleVar_GrabRounding, // float GrabRounding
1860+
ImGuiStyleVar_LayoutAlign, // float LayoutAlign
18591861
ImGuiStyleVar_TabRounding, // float TabRounding
18601862
ImGuiStyleVar_TabBorderSize, // float TabBorderSize
18611863
ImGuiStyleVar_TabBarBorderSize, // float TabBarBorderSize
@@ -2339,6 +2341,7 @@ struct ImGuiStyle
23392341
float ScrollbarRounding; // Radius of grab corners for scrollbar.
23402342
float GrabMinSize; // Minimum width/height of a grab box for slider/scrollbar.
23412343
float GrabRounding; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
2344+
float LayoutAlign; // Element alignment inside horizontal and vertical layouts (0.0f - left/top, 1.0f - right/bottom, 0.5f - center).
23422345
float LogSliderDeadzone; // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero.
23432346
float TabRounding; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
23442347
float TabBorderSize; // Thickness of border around tabs.
@@ -4110,6 +4113,10 @@ namespace ImGui
41104113
#pragma warning (pop)
41114114
#endif
41124115

4116+
#if IMGUI_HAS_STACK_LAYOUT
4117+
#include "imgui_stacklayout.h"
4118+
#endif
4119+
41134120
// Include imgui_user.h at the end of imgui.h
41144121
// May be convenient for some users to only explicitly include vanilla imgui.h and have extra stuff included.
41154122
#ifdef IMGUI_INCLUDE_IMGUI_USER_H

imgui_demo.cpp

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4836,6 +4836,156 @@ static void ShowDemoWindowLayout()
48364836

48374837
ImGui::TreePop();
48384838
}
4839+
4840+
#if IMGUI_HAS_STACK_LAYOUT
4841+
IMGUI_DEMO_MARKER("Layout/Stack Layout");
4842+
if (ImGui::TreeNode("Stack Layout"))
4843+
{
4844+
static bool widget_a = true, widget_b = true, widget_c = true;
4845+
static bool spring_a = true, spring_ab = true, spring_bc = true, spring_c = true;
4846+
static bool minimize_width = false, minimize_height = true;
4847+
static bool horizontal = true, draw_springs = true;
4848+
static ImVec2 item_spacing = ImGui::GetStyle().ItemSpacing;
4849+
static float a_c_spring_weight = 0.0f;
4850+
static float ab_spring_weight = 0.5f;
4851+
static float alignment = 0.5f;
4852+
4853+
struct funcs
4854+
{
4855+
static void VisibleSpring(float spring_weight)
4856+
{
4857+
ImGui::Spring(spring_weight);
4858+
if (!draw_springs)
4859+
return;
4860+
4861+
ImVec2 rect_min = ImGui::GetItemRectMin();
4862+
ImVec2 rect_max = ImGui::GetItemRectMax();
4863+
4864+
ImVec2 rect_size = ImGui::GetItemRectSize();
4865+
if (rect_size.x <= 0.0f && rect_size.y <= 0.0f)
4866+
return;
4867+
4868+
// Draw zig-zag
4869+
float width = 0.0f, spacing = 0.0f;
4870+
ImVec2 direction, origin;
4871+
ImVec2 spacing_min, spring_max;
4872+
4873+
if (horizontal)
4874+
{
4875+
spacing = floorf(item_spacing.x);
4876+
width = rect_size.x - spacing;
4877+
origin = ImVec2(floorf(rect_min.x), floorf(rect_min.y + (rect_max.y - rect_min.y) / 2));
4878+
direction = ImVec2(1.0f, 0.0f);
4879+
spring_max = ImVec2(rect_min.x + width, rect_max.y);
4880+
spacing_min = ImVec2(rect_min.x + width, rect_min.y);
4881+
}
4882+
else
4883+
{
4884+
spacing = floorf(item_spacing.y);
4885+
width = rect_size.y - spacing;
4886+
origin = ImVec2(floorf(rect_min.x + (rect_max.x - rect_min.x) / 2), floorf(rect_min.y));
4887+
direction = ImVec2(0.0f, 1.0f);
4888+
spring_max = ImVec2(rect_max.x, rect_min.y + width);
4889+
spacing_min = ImVec2(rect_min.x, rect_min.y + width);
4890+
}
4891+
4892+
if (spring_weight <= 0.0f && spacing <= 0.0f)
4893+
return;
4894+
4895+
ImDrawList* draw_list = ImGui::GetWindowDrawList();
4896+
4897+
draw_list->PushClipRect(rect_min, rect_max, true);
4898+
4899+
draw_list->AddRectFilled(rect_min, spring_max, ImColor(80, 20, 80));
4900+
draw_list->AddRectFilled(spacing_min, rect_max, ImColor(80, 20, 20));
4901+
4902+
const float zig_zag_size = 3;
4903+
ImVec2 normal = ImVec2(-direction.y, direction.x);
4904+
4905+
draw_list->PathClear();
4906+
origin.x += 0.5f;
4907+
origin.y += 0.5f;
4908+
draw_list->PathLineTo(origin);
4909+
for (float x = zig_zag_size * 0.5f; x <= width; x += zig_zag_size)
4910+
{
4911+
ImVec2 p;
4912+
p.x = origin.x + direction.x * x + normal.x * zig_zag_size;
4913+
p.y = origin.y + direction.y * x + normal.y * zig_zag_size;
4914+
draw_list->PathLineTo(p);
4915+
normal = ImVec2(-normal.x, -normal.y);
4916+
}
4917+
draw_list->PathStroke(ImColor(255, 255, 255, 190), false, 1.0f);
4918+
4919+
draw_list->PopClipRect();
4920+
}
4921+
};
4922+
4923+
ImGui::Checkbox("Widget A", &widget_a); ImGui::SameLine();
4924+
ImGui::Checkbox("Widget B", &widget_b); ImGui::SameLine();
4925+
ImGui::Checkbox("Widget C", &widget_c);
4926+
ImGui::Checkbox("Spring A", &spring_a); ImGui::SameLine();
4927+
ImGui::Checkbox("Spring AB", &spring_ab); ImGui::SameLine();
4928+
ImGui::Checkbox("Spring BC", &spring_bc); ImGui::SameLine();
4929+
ImGui::Checkbox("Spring C", &spring_c);
4930+
ImGui::Checkbox("Horizontal", &horizontal); ImGui::SameLine();
4931+
ImGui::Checkbox("Minimize Width", &minimize_width); ImGui::SameLine();
4932+
ImGui::Checkbox("Minimize Height", &minimize_height);
4933+
ImGui::Checkbox("Draw Springs", &draw_springs); ImGui::SameLine();
4934+
ImGui::TextUnformatted(" "); ImGui::SameLine();
4935+
ImGui::ColorButton("- Spring", ImColor(80, 20, 80), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoPicker); ImGui::SameLine();
4936+
ImGui::TextUnformatted("Spring"); ImGui::SameLine();
4937+
ImGui::TextUnformatted(" "); ImGui::SameLine();
4938+
ImGui::ColorButton("- Spacing", ImColor(80, 20, 20), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoPicker); ImGui::SameLine();
4939+
ImGui::TextUnformatted("Item Spacing");
4940+
ImGui::DragFloat("Item Spacing", horizontal ? &item_spacing.x : &item_spacing.y, 0.1f, 0.0f, 50.0f);
4941+
ImGui::DragFloat("A & C Spring Weight", &a_c_spring_weight, 0.002f, 0.0f, 1.0f);
4942+
ImGui::DragFloat("AB Spring Weight", &ab_spring_weight, 0.002f, 0.0f, 1.0f);
4943+
if (ImGui::IsItemHovered()) ImGui::SetTooltip("BC Spring Weight = 1 - AB Spring Weight");
4944+
ImGui::DragFloat("Minor Axis Alignment", &alignment, 0.002f, 0.0f, 1.0f);
4945+
if (ImGui::IsItemHovered()) ImGui::SetTooltip("This is vertical alignment for horizontal layouts and horizontal alignment for vertical layouts.");
4946+
ImGui::Text("Layout widgets:");
4947+
ImGui::Text("| Spring A | Widget A | Spring AB | Widget B | Spring BC | Widget C | Spring C |");
4948+
4949+
ImGui::Spacing();
4950+
4951+
ImVec2 widget_size;
4952+
widget_size.x = floorf(ImGui::GetContentRegionAvail().x / 4);
4953+
widget_size.y = horizontal ? floorf(widget_size.x / 3) : widget_size.x;
4954+
4955+
ImVec2 small_widget_size = widget_size;
4956+
if (horizontal)
4957+
small_widget_size.y = floorf(small_widget_size.y / 2);
4958+
else
4959+
small_widget_size.x = floorf(small_widget_size.x / 2);
4960+
4961+
ImVec2 layout_size = ImVec2(widget_size.x * 4, widget_size.y * 4);
4962+
if (minimize_width) layout_size.x = 0.0f;
4963+
if (minimize_height) layout_size.y = 0.0f;
4964+
4965+
// Minor axis alignment can be set by style or directly in BeginHorizontal/BeginVertical
4966+
// Example:
4967+
// ImGui::PushStyleVar(ImGuiStyleVar_LayoutAlign, alignment);
4968+
4969+
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(floorf(item_spacing.x), floorf(item_spacing.y)));
4970+
4971+
if (horizontal) { ImGui::BeginHorizontal("h1", layout_size, alignment); } else { ImGui::BeginVertical("v1", layout_size, alignment); }
4972+
if (spring_a) { funcs::VisibleSpring(a_c_spring_weight); }
4973+
if (widget_a) { ImGui::Button("Widget A", widget_size); }
4974+
if (spring_ab) { funcs::VisibleSpring(ab_spring_weight); }
4975+
if (widget_b) { ImGui::Button("Widget B", small_widget_size); }
4976+
if (spring_bc) { funcs::VisibleSpring(1.0f - ab_spring_weight); }
4977+
if (widget_c) { ImGui::Button("Widget C", widget_size); }
4978+
if (spring_c) { funcs::VisibleSpring(a_c_spring_weight); }
4979+
if (horizontal) { ImGui::EndHorizontal(); } else { ImGui::EndVertical(); }
4980+
4981+
ImGui::PopStyleVar();
4982+
4983+
ImDrawList* draw_list = ImGui::GetWindowDrawList();
4984+
draw_list->AddRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), ImGui::GetColorU32(ImGuiCol_Border));
4985+
4986+
ImGui::TreePop();
4987+
}
4988+
#endif // IMGUI_HAS_STACK_LAYOUT
48394989
}
48404990

48414991
//-----------------------------------------------------------------------------

imgui_internal.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Index of this file:
1111
// [SECTION] Forward declarations
1212
// [SECTION] Context pointer
1313
// [SECTION] STB libraries includes
14+
// [SECTION] Stack Layout includes
1415
// [SECTION] Macros
1516
// [SECTION] Generic helpers
1617
// [SECTION] ImDrawList support
@@ -222,6 +223,14 @@ using ImGuiErrorStringCallback = std::function<void(const std::string&)>;
222223
extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer
223224
#endif
224225

226+
//-------------------------------------------------------------------------
227+
// [SECTION] Stack Layout includes
228+
//-------------------------------------------------------------------------
229+
230+
#if IMGUI_HAS_STACK_LAYOUT
231+
# include "imgui_stacklayout_internal.h"
232+
#endif
233+
225234
//-----------------------------------------------------------------------------
226235
// [SECTION] Macros
227236
//-----------------------------------------------------------------------------

0 commit comments

Comments
 (0)