Skip to content

Commit c4a1e42

Browse files
committed
Stack Layout implementation v2.1
1 parent f185c7c commit c4a1e42

7 files changed

+1399
-4
lines changed

imgui.cpp

+39-4
Original file line numberDiff line numberDiff line change
@@ -1201,6 +1201,7 @@ ImGuiStyle::ImGuiStyle()
12011201
ScrollbarRounding = 9.0f; // Radius of grab corners rounding for scrollbar
12021202
GrabMinSize = 12.0f; // Minimum width/height of a grab box for slider/scrollbar
12031203
GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
1204+
LayoutAlign = 0.5f; // Element alignment inside horizontal and vertical layouts (0.0f - left/top, 1.0f - right/bottom, 0.5f - center).
12041205
LogSliderDeadzone = 4.0f; // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero.
12051206
TabRounding = 4.0f; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
12061207
TabBorderSize = 0.0f; // Thickness of border around tabs.
@@ -3119,6 +3120,7 @@ static const ImGuiDataVarInfo GStyleVarInfo[] =
31193120
{ ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, SeparatorTextBorderSize)},// ImGuiStyleVar_SeparatorTextBorderSize
31203121
{ ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, SeparatorTextAlign) }, // ImGuiStyleVar_SeparatorTextAlign
31213122
{ ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, SeparatorTextPadding) }, // ImGuiStyleVar_SeparatorTextPadding
3123+
{ ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, LayoutAlign) }, // ImGuiStyleVar_LayoutAlign
31223124
};
31233125

31243126
const ImGuiDataVarInfo* ImGui::GetStyleVarInfo(ImGuiStyleVar idx)
@@ -9789,6 +9791,10 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu
97899791
g.NextItemData.Flags = ImGuiNextItemDataFlags_None;
97909792
g.NextItemData.ItemFlags = ImGuiItemFlags_None;
97919793

9794+
#if IMGUI_HAS_STACK_LAYOUT
9795+
ImGuiInternal::UpdateItemRect(window->ID, bb.Min, bb.Max);
9796+
#endif
9797+
97929798
#ifdef IMGUI_ENABLE_TEST_ENGINE
97939799
if (id != 0)
97949800
IMGUI_TEST_ENGINE_ITEM_ADD(id, g.LastItemData.NavRect, &g.LastItemData);
@@ -9873,6 +9879,38 @@ void ImGui::ItemSize(const ImVec2& size, float text_baseline_y)
98739879
if (window->SkipItems)
98749880
return;
98759881

9882+
#if IMGUI_HAS_STACK_LAYOUT
9883+
ImGuiLayoutType layout_type = ImGuiInternal::GetCurrentLayoutType(window->ID);
9884+
#else
9885+
ImGuiLayoutType layout_type = window->DC.LayoutType;
9886+
#endif
9887+
9888+
//if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorPos, 3.0f, IM_COL32(255,255,0,255), 4); // [DEBUG] Widget position
9889+
9890+
// Stack Layouts: Handle horizontal case first to simplify merge in case code handling vertical changes.
9891+
if (layout_type == ImGuiLayoutType_Horizontal)
9892+
{
9893+
const float line_width = ImMax(window->DC.CurrLineSize.x, size.x);
9894+
9895+
// Always align ourselves on pixel boundaries
9896+
//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]
9897+
window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x;
9898+
window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y + size.y;
9899+
window->DC.CursorPos.x = IM_TRUNC(window->DC.CursorPos.x + line_width + g.Style.ItemSpacing.x);
9900+
window->DC.CursorPos.y = IM_TRUNC(window->DC.CursorPosPrevLine.y - size.y);
9901+
window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x - g.Style.ItemSpacing.x);
9902+
window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPosPrevLine.y);
9903+
//if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
9904+
9905+
window->DC.PrevLineSize.x = line_width;
9906+
window->DC.PrevLineSize.y = 0.0f;
9907+
window->DC.CurrLineSize.x = 0.0f;
9908+
window->DC.PrevLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, text_baseline_y);
9909+
window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
9910+
window->DC.IsSameLine = window->DC.IsSetPos = false;
9911+
return;
9912+
}
9913+
98769914
// We increase the height in this function to accommodate for baseline offset.
98779915
// In theory we should be offsetting the starting position (window->DC.CursorPos), that will be the topic of a larger refactor,
98789916
// but since ItemSize() is not yet an API that moves the cursor (to handle e.g. wrapping) enlarging the height has the same effect.
@@ -9891,15 +9929,12 @@ void ImGui::ItemSize(const ImVec2& size, float text_baseline_y)
98919929
window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y);
98929930
//if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
98939931

9932+
window->DC.PrevLineSize.x = 0.0f;
98949933
window->DC.PrevLineSize.y = line_height;
98959934
window->DC.CurrLineSize.y = 0.0f;
98969935
window->DC.PrevLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, text_baseline_y);
98979936
window->DC.CurrLineTextBaseOffset = 0.0f;
98989937
window->DC.IsSameLine = window->DC.IsSetPos = false;
9899-
9900-
// Horizontal layout mode
9901-
if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
9902-
SameLine();
99039938
}
99049939

99059940
// Gets back to previous line and continue with horizontal layout

imgui.h

+7
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#define IMGUI_VERSION "1.90.2 WIP"
2727
#define IMGUI_VERSION_NUM 19014
2828
#define IMGUI_HAS_TABLE
29+
#define IMGUI_HAS_STACK_LAYOUT 1 // Stack-Layout PR #846
2930

3031
/*
3132
@@ -1573,6 +1574,7 @@ enum ImGuiStyleVar_
15731574
ImGuiStyleVar_SeparatorTextBorderSize,// float SeparatorTextBorderSize
15741575
ImGuiStyleVar_SeparatorTextAlign, // ImVec2 SeparatorTextAlign
15751576
ImGuiStyleVar_SeparatorTextPadding,// ImVec2 SeparatorTextPadding
1577+
ImGuiStyleVar_LayoutAlign, // float LayoutAlign
15761578
ImGuiStyleVar_COUNT
15771579
};
15781580

@@ -1995,6 +1997,7 @@ struct ImGuiStyle
19951997
float ScrollbarRounding; // Radius of grab corners for scrollbar.
19961998
float GrabMinSize; // Minimum width/height of a grab box for slider/scrollbar.
19971999
float GrabRounding; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
2000+
float LayoutAlign; // Element alignment inside horizontal and vertical layouts (0.0f - left/top, 1.0f - right/bottom, 0.5f - center).
19982001
float LogSliderDeadzone; // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero.
19992002
float TabRounding; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
20002003
float TabBorderSize; // Thickness of border around tabs.
@@ -3288,6 +3291,10 @@ enum ImGuiModFlags_ { ImGuiModFlags_None = 0, ImGuiModFlags_Ctrl = ImGuiMod_Ctrl
32883291
#pragma warning (pop)
32893292
#endif
32903293

3294+
#if IMGUI_HAS_STACK_LAYOUT
3295+
#include "imgui_stacklayout.h"
3296+
#endif
3297+
32913298
// Include imgui_user.h at the end of imgui.h
32923299
// May be convenient for some users to only explicitly include vanilla imgui.h and have extra stuff included.
32933300
#ifdef IMGUI_INCLUDE_IMGUI_USER_H

imgui_demo.cpp

+150
Original file line numberDiff line numberDiff line change
@@ -3579,6 +3579,156 @@ static void ShowDemoWindowLayout()
35793579

35803580
ImGui::TreePop();
35813581
}
3582+
3583+
#if IMGUI_HAS_STACK_LAYOUT
3584+
IMGUI_DEMO_MARKER("Layout/Stack Layout");
3585+
if (ImGui::TreeNode("Stack Layout"))
3586+
{
3587+
static bool widget_a = true, widget_b = true, widget_c = true;
3588+
static bool spring_a = true, spring_ab = true, spring_bc = true, spring_c = true;
3589+
static bool minimize_width = false, minimize_height = true;
3590+
static bool horizontal = true, draw_springs = true;
3591+
static ImVec2 item_spacing = ImGui::GetStyle().ItemSpacing;
3592+
static float a_c_spring_weight = 0.0f;
3593+
static float ab_spring_weight = 0.5f;
3594+
static float alignment = 0.5f;
3595+
3596+
struct funcs
3597+
{
3598+
static void VisibleSpring(float spring_weight)
3599+
{
3600+
ImGui::Spring(spring_weight);
3601+
if (!draw_springs)
3602+
return;
3603+
3604+
ImVec2 rect_min = ImGui::GetItemRectMin();
3605+
ImVec2 rect_max = ImGui::GetItemRectMax();
3606+
3607+
ImVec2 rect_size = ImGui::GetItemRectSize();
3608+
if (rect_size.x <= 0.0f && rect_size.y <= 0.0f)
3609+
return;
3610+
3611+
// Draw zig-zag
3612+
float width = 0.0f, spacing = 0.0f;
3613+
ImVec2 direction, origin;
3614+
ImVec2 spacing_min, spring_max;
3615+
3616+
if (horizontal)
3617+
{
3618+
spacing = floorf(item_spacing.x);
3619+
width = rect_size.x - spacing;
3620+
origin = ImVec2(floorf(rect_min.x), floorf(rect_min.y + (rect_max.y - rect_min.y) / 2));
3621+
direction = ImVec2(1.0f, 0.0f);
3622+
spring_max = ImVec2(rect_min.x + width, rect_max.y);
3623+
spacing_min = ImVec2(rect_min.x + width, rect_min.y);
3624+
}
3625+
else
3626+
{
3627+
spacing = floorf(item_spacing.y);
3628+
width = rect_size.y - spacing;
3629+
origin = ImVec2(floorf(rect_min.x + (rect_max.x - rect_min.x) / 2), floorf(rect_min.y));
3630+
direction = ImVec2(0.0f, 1.0f);
3631+
spring_max = ImVec2(rect_max.x, rect_min.y + width);
3632+
spacing_min = ImVec2(rect_min.x, rect_min.y + width);
3633+
}
3634+
3635+
if (spring_weight <= 0.0f && spacing <= 0.0f)
3636+
return;
3637+
3638+
ImDrawList* draw_list = ImGui::GetWindowDrawList();
3639+
3640+
draw_list->PushClipRect(rect_min, rect_max, true);
3641+
3642+
draw_list->AddRectFilled(rect_min, spring_max, ImColor(80, 20, 80));
3643+
draw_list->AddRectFilled(spacing_min, rect_max, ImColor(80, 20, 20));
3644+
3645+
const float zig_zag_size = 3;
3646+
ImVec2 normal = ImVec2(-direction.y, direction.x);
3647+
3648+
draw_list->PathClear();
3649+
origin.x += 0.5f;
3650+
origin.y += 0.5f;
3651+
draw_list->PathLineTo(origin);
3652+
for (float x = zig_zag_size * 0.5f; x <= width; x += zig_zag_size)
3653+
{
3654+
ImVec2 p;
3655+
p.x = origin.x + direction.x * x + normal.x * zig_zag_size;
3656+
p.y = origin.y + direction.y * x + normal.y * zig_zag_size;
3657+
draw_list->PathLineTo(p);
3658+
normal = ImVec2(-normal.x, -normal.y);
3659+
}
3660+
draw_list->PathStroke(ImColor(255, 255, 255, 190), false, 1.0f);
3661+
3662+
draw_list->PopClipRect();
3663+
}
3664+
};
3665+
3666+
ImGui::Checkbox("Widget A", &widget_a); ImGui::SameLine();
3667+
ImGui::Checkbox("Widget B", &widget_b); ImGui::SameLine();
3668+
ImGui::Checkbox("Widget C", &widget_c);
3669+
ImGui::Checkbox("Spring A", &spring_a); ImGui::SameLine();
3670+
ImGui::Checkbox("Spring AB", &spring_ab); ImGui::SameLine();
3671+
ImGui::Checkbox("Spring BC", &spring_bc); ImGui::SameLine();
3672+
ImGui::Checkbox("Spring C", &spring_c);
3673+
ImGui::Checkbox("Horizontal", &horizontal); ImGui::SameLine();
3674+
ImGui::Checkbox("Minimize Width", &minimize_width); ImGui::SameLine();
3675+
ImGui::Checkbox("Minimize Height", &minimize_height);
3676+
ImGui::Checkbox("Draw Springs", &draw_springs); ImGui::SameLine();
3677+
ImGui::TextUnformatted(" "); ImGui::SameLine();
3678+
ImGui::ColorButton("- Spring", ImColor(80, 20, 80), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoPicker); ImGui::SameLine();
3679+
ImGui::TextUnformatted("Spring"); ImGui::SameLine();
3680+
ImGui::TextUnformatted(" "); ImGui::SameLine();
3681+
ImGui::ColorButton("- Spacing", ImColor(80, 20, 20), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoPicker); ImGui::SameLine();
3682+
ImGui::TextUnformatted("Item Spacing");
3683+
ImGui::DragFloat("Item Spacing", horizontal ? &item_spacing.x : &item_spacing.y, 0.1f, 0.0f, 50.0f);
3684+
ImGui::DragFloat("A & C Spring Weight", &a_c_spring_weight, 0.002f, 0.0f, 1.0f);
3685+
ImGui::DragFloat("AB Spring Weight", &ab_spring_weight, 0.002f, 0.0f, 1.0f);
3686+
if (ImGui::IsItemHovered()) ImGui::SetTooltip("BC Spring Weight = 1 - AB Spring Weight");
3687+
ImGui::DragFloat("Minor Axis Alignment", &alignment, 0.002f, 0.0f, 1.0f);
3688+
if (ImGui::IsItemHovered()) ImGui::SetTooltip("This is vertical alignment for horizontal layouts and horizontal alignment for vertical layouts.");
3689+
ImGui::Text("Layout widgets:");
3690+
ImGui::Text("| Spring A | Widget A | Spring AB | Widget B | Spring BC | Widget C | Spring C |");
3691+
3692+
ImGui::Spacing();
3693+
3694+
ImVec2 widget_size;
3695+
widget_size.x = floorf(ImGui::GetContentRegionAvail().x / 4);
3696+
widget_size.y = horizontal ? floorf(widget_size.x / 3) : widget_size.x;
3697+
3698+
ImVec2 small_widget_size = widget_size;
3699+
if (horizontal)
3700+
small_widget_size.y = floorf(small_widget_size.y / 2);
3701+
else
3702+
small_widget_size.x = floorf(small_widget_size.x / 2);
3703+
3704+
ImVec2 layout_size = ImVec2(widget_size.x * 4, widget_size.y * 4);
3705+
if (minimize_width) layout_size.x = 0.0f;
3706+
if (minimize_height) layout_size.y = 0.0f;
3707+
3708+
// Minor axis alignment can be set by style or directly in BeginHorizontal/BeginVertical
3709+
// Example:
3710+
// ImGui::PushStyleVar(ImGuiStyleVar_LayoutAlign, alignment);
3711+
3712+
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(floorf(item_spacing.x), floorf(item_spacing.y)));
3713+
3714+
if (horizontal) { ImGui::BeginHorizontal("h1", layout_size, alignment); } else { ImGui::BeginVertical("v1", layout_size, alignment); }
3715+
if (spring_a) { funcs::VisibleSpring(a_c_spring_weight); }
3716+
if (widget_a) { ImGui::Button("Widget A", widget_size); }
3717+
if (spring_ab) { funcs::VisibleSpring(ab_spring_weight); }
3718+
if (widget_b) { ImGui::Button("Widget B", small_widget_size); }
3719+
if (spring_bc) { funcs::VisibleSpring(1.0f - ab_spring_weight); }
3720+
if (widget_c) { ImGui::Button("Widget C", widget_size); }
3721+
if (spring_c) { funcs::VisibleSpring(a_c_spring_weight); }
3722+
if (horizontal) { ImGui::EndHorizontal(); } else { ImGui::EndVertical(); }
3723+
3724+
ImGui::PopStyleVar();
3725+
3726+
ImDrawList* draw_list = ImGui::GetWindowDrawList();
3727+
draw_list->AddRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), ImGui::GetColorU32(ImGuiCol_Border));
3728+
3729+
ImGui::TreePop();
3730+
}
3731+
#endif // IMGUI_HAS_STACK_LAYOUT
35823732
}
35833733

35843734
static void ShowDemoWindowPopups()

imgui_internal.h

+9
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
@@ -207,6 +208,14 @@ namespace ImStb
207208

208209
} // namespace ImStb
209210

211+
//-------------------------------------------------------------------------
212+
// [SECTION] Stack Layout includes
213+
//-------------------------------------------------------------------------
214+
215+
#if IMGUI_HAS_STACK_LAYOUT
216+
# include "imgui_stacklayout_internal.h"
217+
#endif
218+
210219
//-----------------------------------------------------------------------------
211220
// [SECTION] Macros
212221
//-----------------------------------------------------------------------------

0 commit comments

Comments
 (0)