Skip to content

Commit 628c1e8

Browse files
committed
Stack Layout implementation v2.1
1 parent 2d0baaa commit 628c1e8

7 files changed

+1437
-4
lines changed

imgui.cpp

+39-4
Original file line numberDiff line numberDiff line change
@@ -1261,6 +1261,7 @@ ImGuiStyle::ImGuiStyle()
12611261
ScrollbarRounding = 9.0f; // Radius of grab corners rounding for scrollbar
12621262
GrabMinSize = 12.0f; // Minimum width/height of a grab box for slider/scrollbar
12631263
GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
1264+
LayoutAlign = 0.5f; // Element alignment inside horizontal and vertical layouts (0.0f - left/top, 1.0f - right/bottom, 0.5f - center).
12641265
LogSliderDeadzone = 4.0f; // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero.
12651266
TabRounding = 4.0f; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
12661267
TabBorderSize = 0.0f; // Thickness of border around tabs.
@@ -3242,6 +3243,7 @@ static const ImGuiDataVarInfo GStyleVarInfo[] =
32423243
{ ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, ScrollbarRounding) }, // ImGuiStyleVar_ScrollbarRounding
32433244
{ ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, GrabMinSize) }, // ImGuiStyleVar_GrabMinSize
32443245
{ ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, GrabRounding) }, // ImGuiStyleVar_GrabRounding
3246+
{ ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, LayoutAlign) }, // ImGuiStyleVar_LayoutAlign
32453247
{ ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TabRounding) }, // ImGuiStyleVar_TabRounding
32463248
{ ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TabBorderSize) }, // ImGuiStyleVar_TabBorderSize
32473249
{ ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TabBarBorderSize) }, // ImGuiStyleVar_TabBarBorderSize
@@ -10230,6 +10232,10 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu
1023010232
g.NextItemData.Flags = ImGuiNextItemDataFlags_None;
1023110233
g.NextItemData.ItemFlags = ImGuiItemFlags_None;
1023210234

10235+
#if IMGUI_HAS_STACK_LAYOUT
10236+
ImGuiInternal::UpdateItemRect(window->ID, bb.Min, bb.Max);
10237+
#endif
10238+
1023310239
#ifdef IMGUI_ENABLE_TEST_ENGINE
1023410240
if (id != 0)
1023510241
IMGUI_TEST_ENGINE_ITEM_ADD(id, g.LastItemData.NavRect, &g.LastItemData);
@@ -10313,6 +10319,38 @@ void ImGui::ItemSize(const ImVec2& size, float text_baseline_y)
1031310319
if (window->SkipItems)
1031410320
return;
1031510321

10322+
#if IMGUI_HAS_STACK_LAYOUT
10323+
ImGuiLayoutType layout_type = ImGuiInternal::GetCurrentLayoutType(window->ID);
10324+
#else
10325+
ImGuiLayoutType layout_type = window->DC.LayoutType;
10326+
#endif
10327+
10328+
//if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorPos, 3.0f, IM_COL32(255,255,0,255), 4); // [DEBUG] Widget position
10329+
10330+
// Stack Layouts: Handle horizontal case first to simplify merge in case code handling vertical changes.
10331+
if (layout_type == ImGuiLayoutType_Horizontal)
10332+
{
10333+
const float line_width = ImMax(window->DC.CurrLineSize.x, size.x);
10334+
10335+
// Always align ourselves on pixel boundaries
10336+
//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]
10337+
window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x;
10338+
window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y + size.y;
10339+
window->DC.CursorPos.x = IM_TRUNC(window->DC.CursorPos.x + line_width + g.Style.ItemSpacing.x);
10340+
window->DC.CursorPos.y = IM_TRUNC(window->DC.CursorPosPrevLine.y - size.y);
10341+
window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x - g.Style.ItemSpacing.x);
10342+
window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPosPrevLine.y);
10343+
//if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
10344+
10345+
window->DC.PrevLineSize.x = line_width;
10346+
window->DC.PrevLineSize.y = 0.0f;
10347+
window->DC.CurrLineSize.x = 0.0f;
10348+
window->DC.PrevLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, text_baseline_y);
10349+
window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
10350+
window->DC.IsSameLine = window->DC.IsSetPos = false;
10351+
return;
10352+
}
10353+
1031610354
// We increase the height in this function to accommodate for baseline offset.
1031710355
// In theory we should be offsetting the starting position (window->DC.CursorPos), that will be the topic of a larger refactor,
1031810356
// but since ItemSize() is not yet an API that moves the cursor (to handle e.g. wrapping) enlarging the height has the same effect.
@@ -10331,15 +10369,12 @@ void ImGui::ItemSize(const ImVec2& size, float text_baseline_y)
1033110369
window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y);
1033210370
//if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
1033310371

10372+
window->DC.PrevLineSize.x = 0.0f;
1033410373
window->DC.PrevLineSize.y = line_height;
1033510374
window->DC.CurrLineSize.y = 0.0f;
1033610375
window->DC.PrevLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, text_baseline_y);
1033710376
window->DC.CurrLineTextBaseOffset = 0.0f;
1033810377
window->DC.IsSameLine = window->DC.IsSetPos = false;
10339-
10340-
// Horizontal layout mode
10341-
if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
10342-
SameLine();
1034310378
}
1034410379
IM_MSVC_RUNTIME_CHECKS_RESTORE
1034510380

imgui.h

+7
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#define IMGUI_VERSION "1.91.0 WIP"
3131
#define IMGUI_VERSION_NUM 19093
3232
#define IMGUI_HAS_TABLE
33+
#define IMGUI_HAS_STACK_LAYOUT 1 // Stack-Layout PR #846
3334

3435
/*
3536
@@ -1664,6 +1665,7 @@ enum ImGuiStyleVar_
16641665
ImGuiStyleVar_ScrollbarRounding, // float ScrollbarRounding
16651666
ImGuiStyleVar_GrabMinSize, // float GrabMinSize
16661667
ImGuiStyleVar_GrabRounding, // float GrabRounding
1668+
ImGuiStyleVar_LayoutAlign, // float LayoutAlign
16671669
ImGuiStyleVar_TabRounding, // float TabRounding
16681670
ImGuiStyleVar_TabBorderSize, // float TabBorderSize
16691671
ImGuiStyleVar_TabBarBorderSize, // float TabBarBorderSize
@@ -2105,6 +2107,7 @@ struct ImGuiStyle
21052107
float ScrollbarRounding; // Radius of grab corners for scrollbar.
21062108
float GrabMinSize; // Minimum width/height of a grab box for slider/scrollbar.
21072109
float GrabRounding; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
2110+
float LayoutAlign; // Element alignment inside horizontal and vertical layouts (0.0f - left/top, 1.0f - right/bottom, 0.5f - center).
21082111
float LogSliderDeadzone; // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero.
21092112
float TabRounding; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
21102113
float TabBorderSize; // Thickness of border around tabs.
@@ -3423,6 +3426,10 @@ namespace ImGui
34233426
#pragma warning (pop)
34243427
#endif
34253428

3429+
#if IMGUI_HAS_STACK_LAYOUT
3430+
#include "imgui_stacklayout.h"
3431+
#endif
3432+
34263433
// Include imgui_user.h at the end of imgui.h
34273434
// May be convenient for some users to only explicitly include vanilla imgui.h and have extra stuff included.
34283435
#ifdef IMGUI_INCLUDE_IMGUI_USER_H

imgui_demo.cpp

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

36423642
ImGui::TreePop();
36433643
}
3644+
3645+
#if IMGUI_HAS_STACK_LAYOUT
3646+
IMGUI_DEMO_MARKER("Layout/Stack Layout");
3647+
if (ImGui::TreeNode("Stack Layout"))
3648+
{
3649+
static bool widget_a = true, widget_b = true, widget_c = true;
3650+
static bool spring_a = true, spring_ab = true, spring_bc = true, spring_c = true;
3651+
static bool minimize_width = false, minimize_height = true;
3652+
static bool horizontal = true, draw_springs = true;
3653+
static ImVec2 item_spacing = ImGui::GetStyle().ItemSpacing;
3654+
static float a_c_spring_weight = 0.0f;
3655+
static float ab_spring_weight = 0.5f;
3656+
static float alignment = 0.5f;
3657+
3658+
struct funcs
3659+
{
3660+
static void VisibleSpring(float spring_weight)
3661+
{
3662+
ImGui::Spring(spring_weight);
3663+
if (!draw_springs)
3664+
return;
3665+
3666+
ImVec2 rect_min = ImGui::GetItemRectMin();
3667+
ImVec2 rect_max = ImGui::GetItemRectMax();
3668+
3669+
ImVec2 rect_size = ImGui::GetItemRectSize();
3670+
if (rect_size.x <= 0.0f && rect_size.y <= 0.0f)
3671+
return;
3672+
3673+
// Draw zig-zag
3674+
float width = 0.0f, spacing = 0.0f;
3675+
ImVec2 direction, origin;
3676+
ImVec2 spacing_min, spring_max;
3677+
3678+
if (horizontal)
3679+
{
3680+
spacing = floorf(item_spacing.x);
3681+
width = rect_size.x - spacing;
3682+
origin = ImVec2(floorf(rect_min.x), floorf(rect_min.y + (rect_max.y - rect_min.y) / 2));
3683+
direction = ImVec2(1.0f, 0.0f);
3684+
spring_max = ImVec2(rect_min.x + width, rect_max.y);
3685+
spacing_min = ImVec2(rect_min.x + width, rect_min.y);
3686+
}
3687+
else
3688+
{
3689+
spacing = floorf(item_spacing.y);
3690+
width = rect_size.y - spacing;
3691+
origin = ImVec2(floorf(rect_min.x + (rect_max.x - rect_min.x) / 2), floorf(rect_min.y));
3692+
direction = ImVec2(0.0f, 1.0f);
3693+
spring_max = ImVec2(rect_max.x, rect_min.y + width);
3694+
spacing_min = ImVec2(rect_min.x, rect_min.y + width);
3695+
}
3696+
3697+
if (spring_weight <= 0.0f && spacing <= 0.0f)
3698+
return;
3699+
3700+
ImDrawList* draw_list = ImGui::GetWindowDrawList();
3701+
3702+
draw_list->PushClipRect(rect_min, rect_max, true);
3703+
3704+
draw_list->AddRectFilled(rect_min, spring_max, ImColor(80, 20, 80));
3705+
draw_list->AddRectFilled(spacing_min, rect_max, ImColor(80, 20, 20));
3706+
3707+
const float zig_zag_size = 3;
3708+
ImVec2 normal = ImVec2(-direction.y, direction.x);
3709+
3710+
draw_list->PathClear();
3711+
origin.x += 0.5f;
3712+
origin.y += 0.5f;
3713+
draw_list->PathLineTo(origin);
3714+
for (float x = zig_zag_size * 0.5f; x <= width; x += zig_zag_size)
3715+
{
3716+
ImVec2 p;
3717+
p.x = origin.x + direction.x * x + normal.x * zig_zag_size;
3718+
p.y = origin.y + direction.y * x + normal.y * zig_zag_size;
3719+
draw_list->PathLineTo(p);
3720+
normal = ImVec2(-normal.x, -normal.y);
3721+
}
3722+
draw_list->PathStroke(ImColor(255, 255, 255, 190), false, 1.0f);
3723+
3724+
draw_list->PopClipRect();
3725+
}
3726+
};
3727+
3728+
ImGui::Checkbox("Widget A", &widget_a); ImGui::SameLine();
3729+
ImGui::Checkbox("Widget B", &widget_b); ImGui::SameLine();
3730+
ImGui::Checkbox("Widget C", &widget_c);
3731+
ImGui::Checkbox("Spring A", &spring_a); ImGui::SameLine();
3732+
ImGui::Checkbox("Spring AB", &spring_ab); ImGui::SameLine();
3733+
ImGui::Checkbox("Spring BC", &spring_bc); ImGui::SameLine();
3734+
ImGui::Checkbox("Spring C", &spring_c);
3735+
ImGui::Checkbox("Horizontal", &horizontal); ImGui::SameLine();
3736+
ImGui::Checkbox("Minimize Width", &minimize_width); ImGui::SameLine();
3737+
ImGui::Checkbox("Minimize Height", &minimize_height);
3738+
ImGui::Checkbox("Draw Springs", &draw_springs); ImGui::SameLine();
3739+
ImGui::TextUnformatted(" "); ImGui::SameLine();
3740+
ImGui::ColorButton("- Spring", ImColor(80, 20, 80), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoPicker); ImGui::SameLine();
3741+
ImGui::TextUnformatted("Spring"); ImGui::SameLine();
3742+
ImGui::TextUnformatted(" "); ImGui::SameLine();
3743+
ImGui::ColorButton("- Spacing", ImColor(80, 20, 20), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoPicker); ImGui::SameLine();
3744+
ImGui::TextUnformatted("Item Spacing");
3745+
ImGui::DragFloat("Item Spacing", horizontal ? &item_spacing.x : &item_spacing.y, 0.1f, 0.0f, 50.0f);
3746+
ImGui::DragFloat("A & C Spring Weight", &a_c_spring_weight, 0.002f, 0.0f, 1.0f);
3747+
ImGui::DragFloat("AB Spring Weight", &ab_spring_weight, 0.002f, 0.0f, 1.0f);
3748+
if (ImGui::IsItemHovered()) ImGui::SetTooltip("BC Spring Weight = 1 - AB Spring Weight");
3749+
ImGui::DragFloat("Minor Axis Alignment", &alignment, 0.002f, 0.0f, 1.0f);
3750+
if (ImGui::IsItemHovered()) ImGui::SetTooltip("This is vertical alignment for horizontal layouts and horizontal alignment for vertical layouts.");
3751+
ImGui::Text("Layout widgets:");
3752+
ImGui::Text("| Spring A | Widget A | Spring AB | Widget B | Spring BC | Widget C | Spring C |");
3753+
3754+
ImGui::Spacing();
3755+
3756+
ImVec2 widget_size;
3757+
widget_size.x = floorf(ImGui::GetContentRegionAvail().x / 4);
3758+
widget_size.y = horizontal ? floorf(widget_size.x / 3) : widget_size.x;
3759+
3760+
ImVec2 small_widget_size = widget_size;
3761+
if (horizontal)
3762+
small_widget_size.y = floorf(small_widget_size.y / 2);
3763+
else
3764+
small_widget_size.x = floorf(small_widget_size.x / 2);
3765+
3766+
ImVec2 layout_size = ImVec2(widget_size.x * 4, widget_size.y * 4);
3767+
if (minimize_width) layout_size.x = 0.0f;
3768+
if (minimize_height) layout_size.y = 0.0f;
3769+
3770+
// Minor axis alignment can be set by style or directly in BeginHorizontal/BeginVertical
3771+
// Example:
3772+
// ImGui::PushStyleVar(ImGuiStyleVar_LayoutAlign, alignment);
3773+
3774+
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(floorf(item_spacing.x), floorf(item_spacing.y)));
3775+
3776+
if (horizontal) { ImGui::BeginHorizontal("h1", layout_size, alignment); } else { ImGui::BeginVertical("v1", layout_size, alignment); }
3777+
if (spring_a) { funcs::VisibleSpring(a_c_spring_weight); }
3778+
if (widget_a) { ImGui::Button("Widget A", widget_size); }
3779+
if (spring_ab) { funcs::VisibleSpring(ab_spring_weight); }
3780+
if (widget_b) { ImGui::Button("Widget B", small_widget_size); }
3781+
if (spring_bc) { funcs::VisibleSpring(1.0f - ab_spring_weight); }
3782+
if (widget_c) { ImGui::Button("Widget C", widget_size); }
3783+
if (spring_c) { funcs::VisibleSpring(a_c_spring_weight); }
3784+
if (horizontal) { ImGui::EndHorizontal(); } else { ImGui::EndVertical(); }
3785+
3786+
ImGui::PopStyleVar();
3787+
3788+
ImDrawList* draw_list = ImGui::GetWindowDrawList();
3789+
draw_list->AddRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), ImGui::GetColorU32(ImGuiCol_Border));
3790+
3791+
ImGui::TreePop();
3792+
}
3793+
#endif // IMGUI_HAS_STACK_LAYOUT
36443794
}
36453795

36463796
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
@@ -212,6 +213,14 @@ namespace ImStb
212213

213214
} // namespace ImStb
214215

216+
//-------------------------------------------------------------------------
217+
// [SECTION] Stack Layout includes
218+
//-------------------------------------------------------------------------
219+
220+
#if IMGUI_HAS_STACK_LAYOUT
221+
# include "imgui_stacklayout_internal.h"
222+
#endif
223+
215224
//-----------------------------------------------------------------------------
216225
// [SECTION] Macros
217226
//-----------------------------------------------------------------------------

0 commit comments

Comments
 (0)