Skip to content

Commit ca1a107

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 63ab70f commit ca1a107

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
@@ -1351,6 +1351,7 @@ ImGuiStyle::ImGuiStyle()
13511351
ScrollbarRounding = 9.0f; // Radius of grab corners rounding for scrollbar
13521352
GrabMinSize = 12.0f; // Minimum width/height of a grab box for slider/scrollbar
13531353
GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
1354+
LayoutAlign = 0.5f; // Element alignment inside horizontal and vertical layouts (0.0f - left/top, 1.0f - right/bottom, 0.5f - center).
13541355
LogSliderDeadzone = 4.0f; // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero.
13551356
TabRounding = 4.0f; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
13561357
TabBorderSize = 0.0f; // Thickness of border around tabs.
@@ -3472,6 +3473,7 @@ static const ImGuiDataVarInfo GStyleVarInfo[] =
34723473
{ ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, ScrollbarRounding) }, // ImGuiStyleVar_ScrollbarRounding
34733474
{ ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, GrabMinSize) }, // ImGuiStyleVar_GrabMinSize
34743475
{ ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, GrabRounding) }, // ImGuiStyleVar_GrabRounding
3476+
{ ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, LayoutAlign) }, // ImGuiStyleVar_LayoutAlign
34753477
{ ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TabRounding) }, // ImGuiStyleVar_TabRounding
34763478
{ ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TabBorderSize) }, // ImGuiStyleVar_TabBorderSize
34773479
{ ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TabBarBorderSize) }, // ImGuiStyleVar_TabBarBorderSize
@@ -11578,6 +11580,10 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu
1157811580
g.NextItemData.HasFlags = ImGuiNextItemDataFlags_None;
1157911581
g.NextItemData.ItemFlags = ImGuiItemFlags_None;
1158011582

11583+
#if IMGUI_HAS_STACK_LAYOUT
11584+
ImGuiInternal::UpdateItemRect(window->ID, bb.Min, bb.Max);
11585+
#endif
11586+
1158111587
#ifdef IMGUI_ENABLE_TEST_ENGINE
1158211588
if (id != 0)
1158311589
IMGUI_TEST_ENGINE_ITEM_ADD(id, g.LastItemData.NavRect, &g.LastItemData);
@@ -11659,6 +11665,38 @@ void ImGui::ItemSize(const ImVec2& size, float text_baseline_y)
1165911665
if (window->SkipItems)
1166011666
return;
1166111667

11668+
#if IMGUI_HAS_STACK_LAYOUT
11669+
ImGuiLayoutType layout_type = ImGuiInternal::GetCurrentLayoutType(window->ID);
11670+
#else
11671+
ImGuiLayoutType layout_type = window->DC.LayoutType;
11672+
#endif
11673+
11674+
//if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorPos, 3.0f, IM_COL32(255,255,0,255), 4); // [DEBUG] Widget position
11675+
11676+
// Stack Layouts: Handle horizontal case first to simplify merge in case code handling vertical changes.
11677+
if (layout_type == ImGuiLayoutType_Horizontal)
11678+
{
11679+
const float line_width = ImMax(window->DC.CurrLineSize.x, size.x);
11680+
11681+
// Always align ourselves on pixel boundaries
11682+
//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]
11683+
window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x;
11684+
window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y + size.y;
11685+
window->DC.CursorPos.x = IM_TRUNC(window->DC.CursorPos.x + line_width + g.Style.ItemSpacing.x);
11686+
window->DC.CursorPos.y = IM_TRUNC(window->DC.CursorPosPrevLine.y - size.y);
11687+
window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x - g.Style.ItemSpacing.x);
11688+
window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPosPrevLine.y);
11689+
//if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
11690+
11691+
window->DC.PrevLineSize.x = line_width;
11692+
window->DC.PrevLineSize.y = 0.0f;
11693+
window->DC.CurrLineSize.x = 0.0f;
11694+
window->DC.PrevLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, text_baseline_y);
11695+
window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
11696+
window->DC.IsSameLine = window->DC.IsSetPos = false;
11697+
return;
11698+
}
11699+
1166211700
// We increase the height in this function to accommodate for baseline offset.
1166311701
// In theory we should be offsetting the starting position (window->DC.CursorPos), that will be the topic of a larger refactor,
1166411702
// but since ItemSize() is not yet an API that moves the cursor (to handle e.g. wrapping) enlarging the height has the same effect.
@@ -11677,15 +11715,12 @@ void ImGui::ItemSize(const ImVec2& size, float text_baseline_y)
1167711715
window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y);
1167811716
//if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
1167911717

11718+
window->DC.PrevLineSize.x = 0.0f;
1168011719
window->DC.PrevLineSize.y = line_height;
1168111720
window->DC.CurrLineSize.y = 0.0f;
1168211721
window->DC.PrevLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, text_baseline_y);
1168311722
window->DC.CurrLineTextBaseOffset = 0.0f;
1168411723
window->DC.IsSameLine = window->DC.IsSetPos = false;
11685-
11686-
// Horizontal layout mode
11687-
if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
11688-
SameLine();
1168911724
}
1169011725
IM_MSVC_RUNTIME_CHECKS_RESTORE
1169111726

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]
@@ -1846,6 +1847,7 @@ enum ImGuiStyleVar_
18461847
ImGuiStyleVar_ScrollbarRounding, // float ScrollbarRounding
18471848
ImGuiStyleVar_GrabMinSize, // float GrabMinSize
18481849
ImGuiStyleVar_GrabRounding, // float GrabRounding
1850+
ImGuiStyleVar_LayoutAlign, // float LayoutAlign
18491851
ImGuiStyleVar_TabRounding, // float TabRounding
18501852
ImGuiStyleVar_TabBorderSize, // float TabBorderSize
18511853
ImGuiStyleVar_TabBarBorderSize, // float TabBarBorderSize
@@ -2329,6 +2331,7 @@ struct ImGuiStyle
23292331
float ScrollbarRounding; // Radius of grab corners for scrollbar.
23302332
float GrabMinSize; // Minimum width/height of a grab box for slider/scrollbar.
23312333
float GrabRounding; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
2334+
float LayoutAlign; // Element alignment inside horizontal and vertical layouts (0.0f - left/top, 1.0f - right/bottom, 0.5f - center).
23322335
float LogSliderDeadzone; // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero.
23332336
float TabRounding; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
23342337
float TabBorderSize; // Thickness of border around tabs.
@@ -4151,6 +4154,10 @@ namespace ImGui
41514154
#pragma warning (pop)
41524155
#endif
41534156

4157+
#if IMGUI_HAS_STACK_LAYOUT
4158+
#include "imgui_stacklayout.h"
4159+
#endif
4160+
41544161
// Include imgui_user.h at the end of imgui.h
41554162
// May be convenient for some users to only explicitly include vanilla imgui.h and have extra stuff included.
41564163
#ifdef IMGUI_INCLUDE_IMGUI_USER_H

imgui_demo.cpp

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

49034903
ImGui::TreePop();
49044904
}
4905+
4906+
#if IMGUI_HAS_STACK_LAYOUT
4907+
IMGUI_DEMO_MARKER("Layout/Stack Layout");
4908+
if (ImGui::TreeNode("Stack Layout"))
4909+
{
4910+
static bool widget_a = true, widget_b = true, widget_c = true;
4911+
static bool spring_a = true, spring_ab = true, spring_bc = true, spring_c = true;
4912+
static bool minimize_width = false, minimize_height = true;
4913+
static bool horizontal = true, draw_springs = true;
4914+
static ImVec2 item_spacing = ImGui::GetStyle().ItemSpacing;
4915+
static float a_c_spring_weight = 0.0f;
4916+
static float ab_spring_weight = 0.5f;
4917+
static float alignment = 0.5f;
4918+
4919+
struct funcs
4920+
{
4921+
static void VisibleSpring(float spring_weight)
4922+
{
4923+
ImGui::Spring(spring_weight);
4924+
if (!draw_springs)
4925+
return;
4926+
4927+
ImVec2 rect_min = ImGui::GetItemRectMin();
4928+
ImVec2 rect_max = ImGui::GetItemRectMax();
4929+
4930+
ImVec2 rect_size = ImGui::GetItemRectSize();
4931+
if (rect_size.x <= 0.0f && rect_size.y <= 0.0f)
4932+
return;
4933+
4934+
// Draw zig-zag
4935+
float width = 0.0f, spacing = 0.0f;
4936+
ImVec2 direction, origin;
4937+
ImVec2 spacing_min, spring_max;
4938+
4939+
if (horizontal)
4940+
{
4941+
spacing = floorf(item_spacing.x);
4942+
width = rect_size.x - spacing;
4943+
origin = ImVec2(floorf(rect_min.x), floorf(rect_min.y + (rect_max.y - rect_min.y) / 2));
4944+
direction = ImVec2(1.0f, 0.0f);
4945+
spring_max = ImVec2(rect_min.x + width, rect_max.y);
4946+
spacing_min = ImVec2(rect_min.x + width, rect_min.y);
4947+
}
4948+
else
4949+
{
4950+
spacing = floorf(item_spacing.y);
4951+
width = rect_size.y - spacing;
4952+
origin = ImVec2(floorf(rect_min.x + (rect_max.x - rect_min.x) / 2), floorf(rect_min.y));
4953+
direction = ImVec2(0.0f, 1.0f);
4954+
spring_max = ImVec2(rect_max.x, rect_min.y + width);
4955+
spacing_min = ImVec2(rect_min.x, rect_min.y + width);
4956+
}
4957+
4958+
if (spring_weight <= 0.0f && spacing <= 0.0f)
4959+
return;
4960+
4961+
ImDrawList* draw_list = ImGui::GetWindowDrawList();
4962+
4963+
draw_list->PushClipRect(rect_min, rect_max, true);
4964+
4965+
draw_list->AddRectFilled(rect_min, spring_max, ImColor(80, 20, 80));
4966+
draw_list->AddRectFilled(spacing_min, rect_max, ImColor(80, 20, 20));
4967+
4968+
const float zig_zag_size = 3;
4969+
ImVec2 normal = ImVec2(-direction.y, direction.x);
4970+
4971+
draw_list->PathClear();
4972+
origin.x += 0.5f;
4973+
origin.y += 0.5f;
4974+
draw_list->PathLineTo(origin);
4975+
for (float x = zig_zag_size * 0.5f; x <= width; x += zig_zag_size)
4976+
{
4977+
ImVec2 p;
4978+
p.x = origin.x + direction.x * x + normal.x * zig_zag_size;
4979+
p.y = origin.y + direction.y * x + normal.y * zig_zag_size;
4980+
draw_list->PathLineTo(p);
4981+
normal = ImVec2(-normal.x, -normal.y);
4982+
}
4983+
draw_list->PathStroke(ImColor(255, 255, 255, 190), false, 1.0f);
4984+
4985+
draw_list->PopClipRect();
4986+
}
4987+
};
4988+
4989+
ImGui::Checkbox("Widget A", &widget_a); ImGui::SameLine();
4990+
ImGui::Checkbox("Widget B", &widget_b); ImGui::SameLine();
4991+
ImGui::Checkbox("Widget C", &widget_c);
4992+
ImGui::Checkbox("Spring A", &spring_a); ImGui::SameLine();
4993+
ImGui::Checkbox("Spring AB", &spring_ab); ImGui::SameLine();
4994+
ImGui::Checkbox("Spring BC", &spring_bc); ImGui::SameLine();
4995+
ImGui::Checkbox("Spring C", &spring_c);
4996+
ImGui::Checkbox("Horizontal", &horizontal); ImGui::SameLine();
4997+
ImGui::Checkbox("Minimize Width", &minimize_width); ImGui::SameLine();
4998+
ImGui::Checkbox("Minimize Height", &minimize_height);
4999+
ImGui::Checkbox("Draw Springs", &draw_springs); ImGui::SameLine();
5000+
ImGui::TextUnformatted(" "); ImGui::SameLine();
5001+
ImGui::ColorButton("- Spring", ImColor(80, 20, 80), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoPicker); ImGui::SameLine();
5002+
ImGui::TextUnformatted("Spring"); ImGui::SameLine();
5003+
ImGui::TextUnformatted(" "); ImGui::SameLine();
5004+
ImGui::ColorButton("- Spacing", ImColor(80, 20, 20), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoPicker); ImGui::SameLine();
5005+
ImGui::TextUnformatted("Item Spacing");
5006+
ImGui::DragFloat("Item Spacing", horizontal ? &item_spacing.x : &item_spacing.y, 0.1f, 0.0f, 50.0f);
5007+
ImGui::DragFloat("A & C Spring Weight", &a_c_spring_weight, 0.002f, 0.0f, 1.0f);
5008+
ImGui::DragFloat("AB Spring Weight", &ab_spring_weight, 0.002f, 0.0f, 1.0f);
5009+
if (ImGui::IsItemHovered()) ImGui::SetTooltip("BC Spring Weight = 1 - AB Spring Weight");
5010+
ImGui::DragFloat("Minor Axis Alignment", &alignment, 0.002f, 0.0f, 1.0f);
5011+
if (ImGui::IsItemHovered()) ImGui::SetTooltip("This is vertical alignment for horizontal layouts and horizontal alignment for vertical layouts.");
5012+
ImGui::Text("Layout widgets:");
5013+
ImGui::Text("| Spring A | Widget A | Spring AB | Widget B | Spring BC | Widget C | Spring C |");
5014+
5015+
ImGui::Spacing();
5016+
5017+
ImVec2 widget_size;
5018+
widget_size.x = floorf(ImGui::GetContentRegionAvail().x / 4);
5019+
widget_size.y = horizontal ? floorf(widget_size.x / 3) : widget_size.x;
5020+
5021+
ImVec2 small_widget_size = widget_size;
5022+
if (horizontal)
5023+
small_widget_size.y = floorf(small_widget_size.y / 2);
5024+
else
5025+
small_widget_size.x = floorf(small_widget_size.x / 2);
5026+
5027+
ImVec2 layout_size = ImVec2(widget_size.x * 4, widget_size.y * 4);
5028+
if (minimize_width) layout_size.x = 0.0f;
5029+
if (minimize_height) layout_size.y = 0.0f;
5030+
5031+
// Minor axis alignment can be set by style or directly in BeginHorizontal/BeginVertical
5032+
// Example:
5033+
// ImGui::PushStyleVar(ImGuiStyleVar_LayoutAlign, alignment);
5034+
5035+
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(floorf(item_spacing.x), floorf(item_spacing.y)));
5036+
5037+
if (horizontal) { ImGui::BeginHorizontal("h1", layout_size, alignment); } else { ImGui::BeginVertical("v1", layout_size, alignment); }
5038+
if (spring_a) { funcs::VisibleSpring(a_c_spring_weight); }
5039+
if (widget_a) { ImGui::Button("Widget A", widget_size); }
5040+
if (spring_ab) { funcs::VisibleSpring(ab_spring_weight); }
5041+
if (widget_b) { ImGui::Button("Widget B", small_widget_size); }
5042+
if (spring_bc) { funcs::VisibleSpring(1.0f - ab_spring_weight); }
5043+
if (widget_c) { ImGui::Button("Widget C", widget_size); }
5044+
if (spring_c) { funcs::VisibleSpring(a_c_spring_weight); }
5045+
if (horizontal) { ImGui::EndHorizontal(); } else { ImGui::EndVertical(); }
5046+
5047+
ImGui::PopStyleVar();
5048+
5049+
ImDrawList* draw_list = ImGui::GetWindowDrawList();
5050+
draw_list->AddRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), ImGui::GetColorU32(ImGuiCol_Border));
5051+
5052+
ImGui::TreePop();
5053+
}
5054+
#endif // IMGUI_HAS_STACK_LAYOUT
49055055
}
49065056

49075057
//-----------------------------------------------------------------------------

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
@@ -229,6 +230,14 @@ using ImGuiErrorStringCallback = std::function<void(const std::string&)>;
229230
extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer
230231
#endif
231232

233+
//-------------------------------------------------------------------------
234+
// [SECTION] Stack Layout includes
235+
//-------------------------------------------------------------------------
236+
237+
#if IMGUI_HAS_STACK_LAYOUT
238+
# include "imgui_stacklayout_internal.h"
239+
#endif
240+
232241
//-----------------------------------------------------------------------------
233242
// [SECTION] Macros
234243
//-----------------------------------------------------------------------------

0 commit comments

Comments
 (0)