Cherry-pick 99d4b0f44 from thedmd/imgui/layouts

docking
thedmd 6 years ago committed by TheCherno
parent edc2f15f5b
commit a961958bdc

@ -69,6 +69,7 @@ CODE
// [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
// [SECTION] ERROR CHECKING
// [SECTION] LAYOUT
// [SECTION] STACK LAYOUT
// [SECTION] SCROLLING
// [SECTION] TOOLTIPS
// [SECTION] POPUPS
@ -933,6 +934,26 @@ static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window);
static void NavRestoreLayer(ImGuiNavLayer layer);
static int FindWindowFocusIndex(ImGuiWindow* window);
// Stack Layout
static ImGuiLayout* FindLayout(ImGuiID id, ImGuiLayoutType type);
static ImGuiLayout* CreateNewLayout(ImGuiID id, ImGuiLayoutType type, ImVec2 size);
static void BeginLayout(ImGuiID id, ImGuiLayoutType type, ImVec2 size, float align);
static void EndLayout(ImGuiLayoutType type);
static void PushLayout(ImGuiLayout* layout);
static void PopLayout(ImGuiLayout* layout);
static void BalanceLayoutSprings(ImGuiLayout& layout);
static ImVec2 BalanceLayoutItemAlignment(ImGuiLayout& layout, ImGuiLayoutItem& item);
static void BalanceLayoutItemsAlignment(ImGuiLayout& layout);
static void BalanceChildLayouts(ImGuiLayout& layout);
static ImVec2 CalculateLayoutSize(ImGuiLayout& layout, bool collapse_springs);
static ImGuiLayoutItem* GenerateLayoutItem(ImGuiLayout& layout, ImGuiLayoutItemType type);
static float CalculateLayoutItemAlignmentOffset(ImGuiLayout& layout, ImGuiLayoutItem& item);
static void TranslateLayoutItem(ImGuiLayoutItem& item, const ImVec2& offset);
static void BeginLayoutItem(ImGuiLayout& layout);
static void EndLayoutItem(ImGuiLayout& layout);
static void AddLayoutSpring(ImGuiLayout& layout, float weight, float spacing);
static void SignedIndent(float indent);
// Error Checking
static void ErrorCheckNewFrameSanityChecks();
static void ErrorCheckEndFrameSanityChecks();
@ -1038,6 +1059,7 @@ ImGuiStyle::ImGuiStyle()
ScrollbarRounding = 9.0f; // Radius of grab corners rounding for scrollbar
GrabMinSize = 10.0f; // Minimum width/height of a grab box for slider/scrollbar
GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
LayoutAlign = 0.5f; // Element alignment inside horizontal and vertical layouts (0.0f - left/top, 1.0f - right/bottom, 0.5f - center).
LogSliderDeadzone = 4.0f; // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero.
TabRounding = 4.0f; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
TabBorderSize = 0.0f; // Thickness of border around tabs.
@ -2599,6 +2621,7 @@ static const ImGuiStyleVarInfo GStyleVarInfo[] =
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, TabRounding) }, // ImGuiStyleVar_TabRounding
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, SelectableTextAlign) }, // ImGuiStyleVar_SelectableTextAlign
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, LayoutAlign) }, // ImGuiStyleVar_LayoutAlign
};
static const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx)
@ -3001,6 +3024,12 @@ ImGuiWindow::~ImGuiWindow()
IM_ASSERT(DrawList == &DrawListInst);
IM_DELETE(Name);
ColumnsStorage.clear_destruct();
for (int i = 0; i < DC.Layouts.Data.Size; i++)
{
ImGuiLayout* layout = (ImGuiLayout*)DC.Layouts.Data[i].val_p;
IM_DELETE(layout);
}
}
ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
@ -6849,6 +6878,13 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
else
SetLastItemData(window->MoveId, g.CurrentItemFlags, IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0, title_bar_rect);
// Mark all layouts as dead. They may be revived in this frame.
for (int i = 0; i < window->DC.Layouts.Data.Size; i++)
{
ImGuiLayout* layout = (ImGuiLayout*)window->DC.Layouts.Data[i].val_p;
layout->Live = false;
}
#ifdef IMGUI_ENABLE_TEST_ENGINE
if (!(window->Flags & ImGuiWindowFlags_NoTitleBar))
IMGUI_TEST_ENGINE_ITEM_ADD(g.LastItemData.Rect, g.LastItemData.ID);
@ -8009,6 +8045,8 @@ void ImGuiStackSizes::CompareWithCurrentState()
// Window stacks
// NOT checking: DC.ItemWidth, DC.TextWrapPos (per window) to allow user to conveniently push once and not pop (they are cleared on Begin)
IM_ASSERT(SizeOfIDStack == window->IDStack.Size && "PushID/PopID or TreeNode/TreePop Mismatch!");
IM_ASSERT(0 == window->DC.LayoutStack.Size && (!window->DC.LayoutStack.Size || window->DC.LayoutStack.back()->Type == ImGuiLayoutType_Horizontal) && "BeginHorizontal/EndHorizontal Mismatch!");
IM_ASSERT(0 == window->DC.LayoutStack.Size && (!window->DC.LayoutStack.Size || window->DC.LayoutStack.back()->Type == ImGuiLayoutType_Vertical) && "BeginVertical/EndVertical Mismatch!");
// Global stacks
// For color, style and font stacks there is an incentive to use Push/Begin/Pop/.../End patterns, so we relax our checks a little to allow them.
@ -8064,30 +8102,54 @@ void ImGui::ItemSize(const ImVec2& size, float text_baseline_y)
if (window->SkipItems)
return;
// We increase the height in this function to accommodate for baseline offset.
// In theory we should be offsetting the starting position (window->DC.CursorPos), that will be the topic of a larger refactor,
// but since ItemSize() is not yet an API that moves the cursor (to handle e.g. wrapping) enlarging the height has the same effect.
const float offset_to_match_baseline_y = (text_baseline_y >= 0) ? ImMax(0.0f, window->DC.CurrLineTextBaseOffset - text_baseline_y) : 0.0f;
const float line_height = ImMax(window->DC.CurrLineSize.y, size.y + offset_to_match_baseline_y);
// Always align ourselves on pixel boundaries
//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]
window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x + size.x;
window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y;
window->DC.CursorPos.x = IM_FLOOR(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); // Next line
window->DC.CursorPos.y = IM_FLOOR(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y); // Next line
window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x);
window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y);
//if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
window->DC.PrevLineSize.y = line_height;
window->DC.CurrLineSize.y = 0.0f;
window->DC.PrevLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, text_baseline_y);
window->DC.CurrLineTextBaseOffset = 0.0f;
// Horizontal layout mode
if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
SameLine();
ImGuiLayoutType layout_type = window->DC.LayoutType;
if (window->DC.CurrentLayout)
layout_type = window->DC.CurrentLayout->Type;
if (layout_type == ImGuiLayoutType_Vertical)
{
// We increase the height in this function to accommodate for baseline offset.
// In theory we should be offsetting the starting position (window->DC.CursorPos), that will be the topic of a larger refactor,
// but since ItemSize() is not yet an API that moves the cursor (to handle e.g. wrapping) enlarging the height has the same effect.
const float offset_to_match_baseline_y = (text_baseline_y >= 0) ? ImMax(0.0f, window->DC.CurrLineTextBaseOffset - text_baseline_y) : 0.0f;
const float line_height = ImMax(window->DC.CurrLineSize.y, size.y + offset_to_match_baseline_y);
// Always align ourselves on pixel boundaries
//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]
window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x + size.x;
window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y;
window->DC.CursorPos.x = IM_FLOOR(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); // Next line
window->DC.CursorPos.y = IM_FLOOR(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y); // Next line
window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x);
window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y);
//if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
window->DC.PrevLineSize.x = 0.0f;
window->DC.PrevLineSize.y = line_height;
window->DC.CurrLineSize.y = 0.0f;
window->DC.PrevLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, text_baseline_y);
window->DC.CurrLineTextBaseOffset = 0.0f;
}
else
{
const float line_width = ImMax(window->DC.CurrLineSize.x, size.x);
// Always align ourselves on pixel boundaries
//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]
window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x;
window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y + size.y;
window->DC.CursorPos.x = IM_FLOOR(window->DC.CursorPos.x + line_width + g.Style.ItemSpacing.x);
window->DC.CursorPos.y = IM_FLOOR(window->DC.CursorPosPrevLine.y - size.y);
window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x - g.Style.ItemSpacing.x);
window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPosPrevLine.y);
//if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
window->DC.PrevLineSize.x = line_width;
window->DC.PrevLineSize.y = 0.0f;
window->DC.CurrLineSize.x = 0.0f;
window->DC.PrevLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, text_baseline_y);
window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
}
}
void ImGui::ItemSize(const ImRect& bb, float text_baseline_y)
@ -8138,6 +8200,9 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu
}
g.NextItemData.Flags = ImGuiNextItemDataFlags_None;
if (window->DC.CurrentLayoutItem)
window->DC.CurrentLayoutItem->MeasuredBounds.Max = ImMax(window->DC.CurrentLayoutItem->MeasuredBounds.Max, bb.Max);
#ifdef IMGUI_ENABLE_TEST_ENGINE
if (id != 0)
IMGUI_TEST_ENGINE_ITEM_ADD(nav_bb_arg ? *nav_bb_arg : bb, id);
@ -8512,6 +8577,697 @@ void ImGui::EndGroup()
}
//-----------------------------------------------------------------------------
// [SECTION] STACK LAYOUT
//-----------------------------------------------------------------------------
static ImGuiLayout* ImGui::FindLayout(ImGuiID id, ImGuiLayoutType type)
{
IM_ASSERT(type == ImGuiLayoutType_Horizontal || type == ImGuiLayoutType_Vertical);
ImGuiWindow* window = GetCurrentWindow();
ImGuiLayout* layout = (ImGuiLayout*)window->DC.Layouts.GetVoidPtr(id);
if (!layout)
return NULL;
if (layout->Type != type)
{
layout->Type = type;
layout->MinimumSize = ImVec2(0.0f, 0.0f);
layout->Items.clear();
}
return layout;
}
static ImGuiLayout* ImGui::CreateNewLayout(ImGuiID id, ImGuiLayoutType type, ImVec2 size)
{
IM_ASSERT(type == ImGuiLayoutType_Horizontal || type == ImGuiLayoutType_Vertical);
ImGuiWindow* window = GetCurrentWindow();
ImGuiLayout* layout = IM_NEW(ImGuiLayout)(id, type);
layout->Size = size;
window->DC.Layouts.SetVoidPtr(id, layout);
return layout;
}
static void ImGui::BeginLayout(ImGuiID id, ImGuiLayoutType type, ImVec2 size, float align)
{
ImGuiWindow* window = GetCurrentWindow();
PushID(id);
// Find or create
ImGuiLayout* layout = FindLayout(id, type);
if (!layout)
layout = CreateNewLayout(id, type, size);
layout->Live = true;
PushLayout(layout);
if (layout->Size.x != size.x || layout->Size.y != size.y)
layout->Size = size;
if (align < 0.0f)
layout->Align = -1.0f;
else
layout->Align = ImClamp(align, 0.0f, 1.0f);
// Start capture
layout->CurrentItemIndex = 0;
layout->CurrentSize.x = layout->Size.x > 0.0f ? layout->Size.x : layout->MinimumSize.x;
layout->CurrentSize.y = layout->Size.y > 0.0f ? layout->Size.y : layout->MinimumSize.y;
layout->StartPos = window->DC.CursorPos;
layout->StartCursorMaxPos = window->DC.CursorMaxPos;
if (type == ImGuiLayoutType_Vertical)
{
// Push empty item to recalculate cursor position.
PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f));
Dummy(ImVec2(0.0f, 0.0f));
PopStyleVar();
// Indent horizontal position to match edge of the layout.
layout->Indent = layout->StartPos.x - window->DC.CursorPos.x;
SignedIndent(layout->Indent);
}
BeginLayoutItem(*layout);
}
static void ImGui::EndLayout(ImGuiLayoutType type)
{
ImGuiWindow* window = GetCurrentWindow();
IM_ASSERT(window->DC.CurrentLayout);
IM_ASSERT(window->DC.CurrentLayout->Type == type);
ImGuiLayout* layout = window->DC.CurrentLayout;
EndLayoutItem(*layout);
if (layout->CurrentItemIndex < layout->Items.Size)
layout->Items.resize(layout->CurrentItemIndex);
if (layout->Type == ImGuiLayoutType_Vertical)
SignedIndent(-layout->Indent);
PopLayout(layout);
const bool auto_width = layout->Size.x <= 0.0f;
const bool auto_height = layout->Size.y <= 0.0f;
ImVec2 new_size = layout->Size;
if (auto_width)
new_size.x = layout->CurrentSize.x;
if (auto_height)
new_size.y = layout->CurrentSize.y;
ImVec2 new_minimum_size = CalculateLayoutSize(*layout, true);
if (new_minimum_size.x != layout->MinimumSize.x || new_minimum_size.y != layout->MinimumSize.y)
{
layout->MinimumSize = new_minimum_size;
// Shrink
if (auto_width)
new_size.x = new_minimum_size.x;
if (auto_height)
new_size.y = new_minimum_size.y;
}
if (!auto_width)
new_size.x = layout->Size.x;
if (!auto_height)
new_size.y = layout->Size.y;
layout->CurrentSize = new_size;
ImVec2 measured_size = new_size;
if ((auto_width || auto_height) && layout->Parent)
{
if (layout->Type == ImGuiLayoutType_Horizontal && auto_width && layout->Parent->CurrentSize.x > 0)
layout->CurrentSize.x = layout->Parent->CurrentSize.x;
else if (layout->Type == ImGuiLayoutType_Vertical && auto_height && layout->Parent->CurrentSize.y > 0)
layout->CurrentSize.y = layout->Parent->CurrentSize.y;
BalanceLayoutSprings(*layout);
measured_size = layout->CurrentSize;
}
layout->CurrentSize = new_size;
PopID();
ImVec2 current_layout_item_max = ImVec2(0.0f, 0.0f);
if (window->DC.CurrentLayoutItem)
current_layout_item_max = ImMax(window->DC.CurrentLayoutItem->MeasuredBounds.Max, layout->StartPos + new_size);
window->DC.CursorPos = layout->StartPos;
window->DC.CursorMaxPos = layout->StartCursorMaxPos;
ItemSize(new_size);
ItemAdd(ImRect(layout->StartPos, layout->StartPos + measured_size), 0);
if (window->DC.CurrentLayoutItem)
window->DC.CurrentLayoutItem->MeasuredBounds.Max = current_layout_item_max;
if (layout->Parent == NULL)
BalanceChildLayouts(*layout);
//window->DrawList->AddRect(layout->StartPos, layout->StartPos + measured_size, IM_COL32(0,255,0,255)); // [DEBUG]
//window->DrawList->AddRect(window->DC.LastItemRect.Min, window->DC.LastItemRect.Max, IM_COL32(255,255,0,255)); // [DEBUG]
}
static ImVec2 ImGui::CalculateLayoutSize(ImGuiLayout& layout, bool collapse_springs)
{
ImVec2 bounds = ImVec2(0.0f, 0.0f);
if (layout.Type == ImGuiLayoutType_Vertical)
{
for (int i = 0; i < layout.Items.Size; i++)
{
ImGuiLayoutItem& item = layout.Items[i];
ImVec2 item_size = item.MeasuredBounds.GetSize();
if (item.Type == ImGuiLayoutItemType_Item)
{
bounds.x = ImMax(bounds.x, item_size.x);
bounds.y += item_size.y;
}
else
{
bounds.y += ImFloor(item.SpringSpacing);
if (!collapse_springs)
bounds.y += item.SpringSize;
}
}
}
else
{
for (int i = 0; i < layout.Items.Size; i++)
{
ImGuiLayoutItem& item = layout.Items[i];
ImVec2 item_size = item.MeasuredBounds.GetSize();
if (item.Type == ImGuiLayoutItemType_Item)
{
bounds.x += item_size.x;
bounds.y = ImMax(bounds.y, item_size.y);
}
else
{
bounds.x += ImFloor(item.SpringSpacing);
if (!collapse_springs)
bounds.x += item.SpringSize;
}
}
}
return bounds;
}
static void ImGui::PushLayout(ImGuiLayout* layout)
{
ImGuiWindow* window = GetCurrentWindow();
if (layout)
{
layout->Parent = window->DC.CurrentLayout;
if (layout->Parent != NULL)
layout->ParentItemIndex = layout->Parent->CurrentItemIndex;
if (window->DC.CurrentLayout)
{
layout->NextSibling = window->DC.CurrentLayout->FirstChild;
layout->FirstChild = NULL;
window->DC.CurrentLayout->FirstChild = layout;
}
else
{
layout->NextSibling = NULL;
layout->FirstChild = NULL;
}
}
window->DC.LayoutStack.push_back(layout);
window->DC.CurrentLayout = layout;
window->DC.CurrentLayoutItem = NULL;
}
static void ImGui::PopLayout(ImGuiLayout* layout)
{
ImGuiWindow* window = GetCurrentWindow();
IM_ASSERT(!window->DC.LayoutStack.empty());
IM_ASSERT(window->DC.LayoutStack.back() == layout);
window->DC.LayoutStack.pop_back();
if (!window->DC.LayoutStack.empty())
{
window->DC.CurrentLayout = window->DC.LayoutStack.back();
window->DC.CurrentLayoutItem = &window->DC.CurrentLayout->Items[window->DC.CurrentLayout->CurrentItemIndex];
}
else
{
window->DC.CurrentLayout = NULL;
window->DC.CurrentLayoutItem = NULL;
}
}
static void ImGui::BalanceLayoutSprings(ImGuiLayout& layout)
{
// Accumulate amount of occupied space and springs weights
float total_spring_weight = 0.0f;
int last_spring_item_index = -1;
for (int i = 0; i < layout.Items.Size; i++)
{
ImGuiLayoutItem& item = layout.Items[i];
if (item.Type == ImGuiLayoutItemType_Spring)
{
total_spring_weight += item.SpringWeight;
last_spring_item_index = i;
}
}
// Determine occupied space and available space depending on layout type
const bool is_horizontal = (layout.Type == ImGuiLayoutType_Horizontal);
const bool is_auto_sized = ((is_horizontal ? layout.Size.x : layout.Size.y) <= 0.0f) && (layout.Parent == NULL);
const float occupied_space = is_horizontal ? layout.MinimumSize.x : layout.MinimumSize.y;
const float available_space = is_auto_sized ? occupied_space : (is_horizontal ? layout.CurrentSize.x : layout.CurrentSize.y);
const float free_space = ImMax(available_space - occupied_space, 0.0f);
float span_start = 0.0f;
float current_weight = 0.0f;
for (int i = 0; i < layout.Items.Size; i++)
{
ImGuiLayoutItem& item = layout.Items[i];
if (item.Type != ImGuiLayoutItemType_Spring)
continue;
float last_spring_size = item.SpringSize;
if (free_space > 0.0f && total_spring_weight > 0.0f)
{
float next_weight = current_weight + item.SpringWeight;
float span_end = ImFloor((i == last_spring_item_index) ? free_space : (free_space * next_weight / total_spring_weight));
float spring_size = span_end - span_start;
item.SpringSize = spring_size;
span_start = span_end;
current_weight = next_weight;
}
else
{
item.SpringSize = 0.0f;
}
// If spring changed its size, fix positioning of following items to avoid one frame visual bugs.
if (last_spring_size != item.SpringSize)
{
float difference = item.SpringSize - last_spring_size;
ImVec2 offset = is_horizontal ? ImVec2(difference, 0.0f) : ImVec2(0.0f, difference);
item.MeasuredBounds.Max += offset;
for (int j = i + 1; j < layout.Items.Size; j++)
{
ImGuiLayoutItem& translated_item = layout.Items[j];
TranslateLayoutItem(translated_item, offset);
translated_item.MeasuredBounds.Min += offset;
translated_item.MeasuredBounds.Max += offset;
}
}
}
}
static ImVec2 ImGui::BalanceLayoutItemAlignment(ImGuiLayout& layout, ImGuiLayoutItem& item)
{
// Fixup item alignment if necessary.
ImVec2 position_correction = ImVec2(0.0f, 0.0f);
if (item.CurrentAlign > 0.0f)
{
float item_align_offset = CalculateLayoutItemAlignmentOffset(layout, item);
if (item.CurrentAlignOffset != item_align_offset)
{
float offset = item_align_offset - item.CurrentAlignOffset;
if (layout.Type == ImGuiLayoutType_Horizontal)
position_correction.y = offset;
else
position_correction.x = offset;
TranslateLayoutItem(item, position_correction);
item.CurrentAlignOffset = item_align_offset;
}
}
return position_correction;
}
static void ImGui::BalanceLayoutItemsAlignment(ImGuiLayout& layout)
{
for (int i = 0; i < layout.Items.Size; ++i)
{
ImGuiLayoutItem& item = layout.Items[i];
BalanceLayoutItemAlignment(layout, item);
}
}
static bool HasAnyNonZeroSpring(ImGuiLayout& layout)
{
for (int i = 0; i < layout.Items.Size; ++i)
{
ImGuiLayoutItem& item = layout.Items[i];
if (item.Type != ImGuiLayoutItemType_Spring)
continue;
if (item.SpringWeight > 0)
return true;
}
return false;
}
static void ImGui::BalanceChildLayouts(ImGuiLayout& layout)
{
for (ImGuiLayout* child = layout.FirstChild; child != NULL; child = child->NextSibling)
{
//ImVec2 child_layout_size = child->CurrentSize;
// Propagate layout size down to child layouts.
//
// TODO: Distribution assume inner layout is only
// element inside parent item and assigns
// all available space to it.
//
// Investigate how to split space between
// adjacent layouts.
//
// Investigate how to measure non-layout items
// to treat them as fixed size blocks.
//
if (child->Type == ImGuiLayoutType_Horizontal && child->Size.x <= 0.0f)
child->CurrentSize.x = layout.CurrentSize.x;
else if (child->Type == ImGuiLayoutType_Vertical && child->Size.y <= 0.0f)
child->CurrentSize.y = layout.CurrentSize.y;
BalanceChildLayouts(*child);
//child->CurrentSize = child_layout_size;
if (HasAnyNonZeroSpring(*child))
{
// Expand item measured bounds to make alignment correct.
ImGuiLayoutItem& item = layout.Items[child->ParentItemIndex];
if (child->Type == ImGuiLayoutType_Horizontal && child->Size.x <= 0.0f)
item.MeasuredBounds.Max.x = ImMax(item.MeasuredBounds.Max.x, item.MeasuredBounds.Min.x + layout.CurrentSize.x);
else if (child->Type == ImGuiLayoutType_Vertical && child->Size.y <= 0.0f)
item.MeasuredBounds.Max.y = ImMax(item.MeasuredBounds.Max.y, item.MeasuredBounds.Min.y + layout.CurrentSize.y);
}
}
BalanceLayoutSprings(layout);
BalanceLayoutItemsAlignment(layout);
}
static ImGuiLayoutItem* ImGui::GenerateLayoutItem(ImGuiLayout& layout, ImGuiLayoutItemType type)
{
ImGuiContext& g = *GImGui;
IM_ASSERT(layout.CurrentItemIndex <= layout.Items.Size);
if (layout.CurrentItemIndex < layout.Items.Size)
{
ImGuiLayoutItem& item = layout.Items[layout.CurrentItemIndex];
if (item.Type != type)
item = ImGuiLayoutItem(type);
}
else
{
layout.Items.push_back(ImGuiLayoutItem(type));
}
g.CurrentWindow->DC.CurrentLayoutItem = &layout.Items[layout.CurrentItemIndex];
return &layout.Items[layout.CurrentItemIndex];
}
// Calculate how many pixels from top/left layout edge item need to be moved to match
// layout alignment.
static float ImGui::CalculateLayoutItemAlignmentOffset(ImGuiLayout& layout, ImGuiLayoutItem& item)
{
if (item.CurrentAlign <= 0.0f)
return 0.0f;
ImVec2 item_size = item.MeasuredBounds.GetSize();
float layout_extent = (layout.Type == ImGuiLayoutType_Horizontal) ? layout.CurrentSize.y : layout.CurrentSize.x;
float item_extent = (layout.Type == ImGuiLayoutType_Horizontal) ? item_size.y : item_size.x;
if (item_extent <= 0/* || layout_extent <= item_extent*/)
return 0.0f;
float align_offset = ImFloor(item.CurrentAlign * (layout_extent - item_extent));
return align_offset;
}
static void ImGui::TranslateLayoutItem(ImGuiLayoutItem& item, const ImVec2& offset)
{
if ((offset.x == 0.0f && offset.y == 0.0f) || (item.VertexIndexBegin == item.VertexIndexEnd))
return;
//IMGUI_DEBUG_LOG("TranslateLayoutItem by %f,%f\n", offset.x, offset.y);
ImDrawList* draw_list = GetWindowDrawList();
ImDrawVert* begin = draw_list->VtxBuffer.Data + item.VertexIndexBegin;
ImDrawVert* end = draw_list->VtxBuffer.Data + item.VertexIndexEnd;
for (ImDrawVert* vtx = begin; vtx < end; ++vtx)
{
vtx->pos.x += offset.x;
vtx->pos.y += offset.y;
}
}
static void ImGui::SignedIndent(float indent)
{
if (indent > 0.0f)
Indent(indent);
else if (indent < 0.0f)
Unindent(-indent);
}
static void ImGui::BeginLayoutItem(ImGuiLayout& layout)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
ImGuiLayoutItem& item = *GenerateLayoutItem(layout, ImGuiLayoutItemType_Item);
item.CurrentAlign = layout.Align;
if (item.CurrentAlign < 0.0f)
item.CurrentAlign = ImClamp(g.Style.LayoutAlign, 0.0f, 1.0f);
// Align item according to data from previous frame.
// If layout changes in current frame alignment will
// be corrected in EndLayout() to it visualy coherent.
item.CurrentAlignOffset = CalculateLayoutItemAlignmentOffset(layout, item);
if (item.CurrentAlign > 0.0f)
{
if (layout.Type == ImGuiLayoutType_Horizontal)
{
window->DC.CursorPos.y += item.CurrentAlignOffset;
}
else
{
float new_position = window->DC.CursorPos.x + item.CurrentAlignOffset;
// Make placement behave like in horizontal case when next
// widget is placed at very same Y position. This indent
// make sure for vertical layout placed widgets has same X position.
SignedIndent(item.CurrentAlignOffset);
window->DC.CursorPos.x = new_position;
}
}
item.MeasuredBounds.Min = item.MeasuredBounds.Max = window->DC.CursorPos;
item.VertexIndexBegin = item.VertexIndexEnd = window->DrawList->_VtxCurrentIdx;
}
static void ImGui::EndLayoutItem(ImGuiLayout& layout)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
IM_ASSERT(layout.CurrentItemIndex < layout.Items.Size);
ImGuiLayoutItem& item = layout.Items[layout.CurrentItemIndex];
ImDrawList* draw_list = window->DrawList;
item.VertexIndexEnd = draw_list->_VtxCurrentIdx;
if (item.CurrentAlign > 0.0f && layout.Type == ImGuiLayoutType_Vertical)
SignedIndent(-item.CurrentAlignOffset);
// Fixup item alignment in case item size changed in current frame.
ImVec2 position_correction = BalanceLayoutItemAlignment(layout, item);
item.MeasuredBounds.Min += position_correction;
item.MeasuredBounds.Max += position_correction;
if (layout.Type == ImGuiLayoutType_Horizontal)
window->DC.CursorPos.y = layout.StartPos.y;
else
window->DC.CursorPos.x = layout.StartPos.x;
layout.CurrentItemIndex++;
}
static void ImGui::AddLayoutSpring(ImGuiLayout& layout, float weight, float spacing)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
ImGuiLayoutItem* previous_item = &layout.Items[layout.CurrentItemIndex];
// Undo item padding, spring should consume all space between items.
if (layout.Type == ImGuiLayoutType_Horizontal)
window->DC.CursorPos.x = previous_item->MeasuredBounds.Max.x;
else
window->DC.CursorPos.y = previous_item->MeasuredBounds.Max.y;
previous_item = NULL; // may be invalid after call to GenerateLayoutItem()
EndLayoutItem(layout);
ImGuiLayoutItem* spring_item = GenerateLayoutItem(layout, ImGuiLayoutItemType_Spring);
spring_item->MeasuredBounds.Min = spring_item->MeasuredBounds.Max = window->DC.CursorPos;
if (weight < 0.0f)
weight = 0.0f;
if (spring_item->SpringWeight != weight)
spring_item->SpringWeight = weight;
if (spacing < 0.0f)
{
ImVec2 style_spacing = g.Style.ItemSpacing;
if (layout.Type == ImGuiLayoutType_Horizontal)
spacing = style_spacing.x;
else
spacing = style_spacing.y;
}
if (spring_item->SpringSpacing != spacing)
spring_item->SpringSpacing = spacing;
if (spring_item->SpringSize > 0.0f || spacing > 0.0f)
{
ImVec2 spring_size, spring_spacing;
if (layout.Type == ImGuiLayoutType_Horizontal)
{
spring_spacing = ImVec2(0.0f, g.Style.ItemSpacing.y);
spring_size = ImVec2(spacing + spring_item->SpringSize, layout.CurrentSize.y);
}
else
{
spring_spacing = ImVec2(g.Style.ItemSpacing.x, 0.0f);
spring_size = ImVec2(layout.CurrentSize.x, spacing + spring_item->SpringSize);
}
PushStyleVar(ImGuiStyleVar_ItemSpacing, ImFloor(spring_spacing));
Dummy(ImFloor(spring_size));
PopStyleVar();
}
layout.CurrentItemIndex++;
BeginLayoutItem(layout);
}
void ImGui::BeginHorizontal(const char* str_id, const ImVec2& size/* = ImVec2(0, 0)*/, float align/* = -1*/)
{
ImGuiWindow* window = GetCurrentWindow();
BeginLayout(window->GetID(str_id), ImGuiLayoutType_Horizontal, size, align);
}
void ImGui::BeginHorizontal(const void* ptr_id, const ImVec2& size/* = ImVec2(0, 0)*/, float align/* = -1*/)
{
ImGuiWindow* window = GetCurrentWindow();
BeginLayout(window->GetID(ptr_id), ImGuiLayoutType_Horizontal, size, align);
}
void ImGui::BeginHorizontal(int id, const ImVec2& size/* = ImVec2(0, 0)*/, float align/* = -1*/)
{
ImGuiWindow* window = GetCurrentWindow();
BeginLayout(window->GetID((void*)(intptr_t)id), ImGuiLayoutType_Horizontal, size, align);
}
void ImGui::EndHorizontal()
{
EndLayout(ImGuiLayoutType_Horizontal);
}
void ImGui::BeginVertical(const char* str_id, const ImVec2& size/* = ImVec2(0, 0)*/, float align/* = -1*/)
{
ImGuiWindow* window = GetCurrentWindow();
BeginLayout(window->GetID(str_id), ImGuiLayoutType_Vertical, size, align);
}
void ImGui::BeginVertical(const void* ptr_id, const ImVec2& size/* = ImVec2(0, 0)*/, float align/* = -1*/)
{
ImGuiWindow* window = GetCurrentWindow();
BeginLayout(window->GetID(ptr_id), ImGuiLayoutType_Vertical, size, align);
}
void ImGui::BeginVertical(int id, const ImVec2& size/* = ImVec2(0, 0)*/, float align/* = -1*/)
{
ImGuiWindow* window = GetCurrentWindow();
BeginLayout(window->GetID((void*)(intptr_t)id), ImGuiLayoutType_Vertical, size, align);
}
void ImGui::EndVertical()
{
EndLayout(ImGuiLayoutType_Vertical);
}
// Inserts spring separator in layout
// weight <= 0 : spring will always have zero size
// weight > 0 : power of current spring
// spacing < 0 : use default spacing if pos_x == 0, no spacing if pos_x != 0
// spacing >= 0 : enforce spacing amount
void ImGui::Spring(float weight/* = 1.0f*/, float spacing/* = -1.0f*/)
{
ImGuiWindow* window = GetCurrentWindow();
IM_ASSERT(window->DC.CurrentLayout);
AddLayoutSpring(*window->DC.CurrentLayout, weight, spacing);
}
void ImGui::SuspendLayout()
{
PushLayout(NULL);
}
void ImGui::ResumeLayout()
{
ImGuiWindow* window = GetCurrentWindow();
IM_ASSERT(!window->DC.CurrentLayout);
IM_ASSERT(!window->DC.LayoutStack.empty());
PopLayout(NULL);
}
//-----------------------------------------------------------------------------
// [SECTION] SCROLLING
//-----------------------------------------------------------------------------

@ -67,6 +67,7 @@ Index of this file:
#define IMGUI_HAS_TABLE
#define IMGUI_HAS_VIEWPORT // Viewport WIP branch
#define IMGUI_HAS_DOCK // Docking WIP branch
#define IMGUI_HAS_STACK_LAYOUT 1 // Stack-Layout PR #846
// Define attributes of all API symbols declarations (e.g. for DLL under Windows)
// IMGUI_API is used for core imgui functions, IMGUI_IMPL_API is used for the default backends files (imgui_impl_xxx.h)
@ -456,6 +457,18 @@ namespace ImGui
IMGUI_API float GetFrameHeight(); // ~ FontSize + style.FramePadding.y * 2
IMGUI_API float GetFrameHeightWithSpacing(); // ~ FontSize + style.FramePadding.y * 2 + style.ItemSpacing.y (distance in pixels between 2 consecutive lines of framed widgets)
IMGUI_API void BeginHorizontal(const char* str_id, const ImVec2& size = ImVec2(0, 0), float align = -1.0f);
IMGUI_API void BeginHorizontal(const void* ptr_id, const ImVec2& size = ImVec2(0, 0), float align = -1.0f);
IMGUI_API void BeginHorizontal(int id, const ImVec2& size = ImVec2(0, 0), float align = -1);
IMGUI_API void EndHorizontal();
IMGUI_API void BeginVertical(const char* str_id, const ImVec2& size = ImVec2(0, 0), float align = -1.0f);
IMGUI_API void BeginVertical(const void* ptr_id, const ImVec2& size = ImVec2(0, 0), float align = -1.0f);
IMGUI_API void BeginVertical(int id, const ImVec2& size = ImVec2(0, 0), float align = -1);
IMGUI_API void EndVertical();
IMGUI_API void Spring(float weight = 1.0f, float spacing = -1.0f);
IMGUI_API void SuspendLayout();
IMGUI_API void ResumeLayout();
// ID stack/scopes
// Read the FAQ (docs/FAQ.md or http://dearimgui.org/faq) for more details about how ID are handled in dear imgui.
// - Those questions are answered and impacted by understanding of the ID stack system:
@ -1598,6 +1611,7 @@ enum ImGuiStyleVar_
ImGuiStyleVar_TabRounding, // float TabRounding
ImGuiStyleVar_ButtonTextAlign, // ImVec2 ButtonTextAlign
ImGuiStyleVar_SelectableTextAlign, // ImVec2 SelectableTextAlign
ImGuiStyleVar_LayoutAlign, // float LayoutAlign
ImGuiStyleVar_COUNT
};
@ -1845,6 +1859,7 @@ struct ImGuiStyle
float ScrollbarRounding; // Radius of grab corners for scrollbar.
float GrabMinSize; // Minimum width/height of a grab box for slider/scrollbar.
float GrabRounding; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
float LayoutAlign; // Element alignment inside horizontal and vertical layouts (0.0f - left/top, 1.0f - right/bottom, 0.5f - center).
float LogSliderDeadzone; // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero.
float TabRounding; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
float TabBorderSize; // Thickness of border around tabs.

@ -3173,6 +3173,139 @@ static void ShowDemoWindowLayout()
ImGui::TreePop();
}
if (ImGui::TreeNode("Stack Layout"))
{
static bool widget_a = true, widget_b = true, widget_c = true;
static bool spring_a = true, spring_ab = true, spring_bc = true, spring_c = true;
static bool minimize_width = false, minimize_height = true;
static bool horizontal = true, draw_springs = true;
static ImVec2 item_spacing = ImGui::GetStyle().ItemSpacing;
static float a_c_spring_weight = 0.0f;
static float ab_spring_weight = 0.5f;
static float alignment = 0.5f;
struct funcs
{
static void VisibleSpring(float spring_weight)
{
ImVec2 start_cursor_pos = ImGui::GetCursorScreenPos();
ImGui::Spring(spring_weight);
ImVec2 end_cursor_pos = ImGui::GetCursorScreenPos();
if (!draw_springs)
return;
if (spring_weight <= 0.0f)
return;
if (fabsf(start_cursor_pos.x - end_cursor_pos.x) < 1.0f && fabsf(start_cursor_pos.y - end_cursor_pos.y) < 1.0f)
return;
// Draw zig-zag
ImDrawList* draw_list = ImGui::GetWindowDrawList();
ImVec2 rect_min = ImGui::GetItemRectMin();
ImVec2 rect_max = ImGui::GetItemRectMax();
draw_list->PushClipRect(rect_min, rect_max, true);
float width = 0.0f;
ImVec2 direction, origin;
if (horizontal)
{
width = rect_max.x - rect_min.x;
origin = ImVec2(floorf(rect_min.x), floorf(rect_min.y + (rect_max.y - rect_min.y) / 2));
direction = ImVec2(1.0f, 0.0f);
}
else
{
width = rect_max.y - rect_min.y;
origin = ImVec2(floorf(rect_min.x + (rect_max.x - rect_min.x) / 2), floorf(rect_min.y));
direction = ImVec2(0.0f, 1.0f);
}
draw_list->AddRectFilled(rect_min, rect_max, ImColor(255, 128, 255, 40));
const float zig_zag_size = 3;
ImVec2 normal = ImVec2(-direction.y, direction.x);
draw_list->PathClear();
origin.x += 0.5f;
origin.y += 0.5f;
draw_list->PathLineTo(origin);
for (float x = zig_zag_size * 0.5f; x <= width; x += zig_zag_size)
{
ImVec2 p;
p.x = origin.x + direction.x * x + normal.x * zig_zag_size;
p.y = origin.y + direction.y * x + normal.y * zig_zag_size;
draw_list->PathLineTo(p);
normal = ImVec2(-normal.x, -normal.y);
}
draw_list->PathStroke(ImColor(255, 255, 255, 190), false, 1.0f);
draw_list->PopClipRect();
}
};
ImGui::Checkbox("Widget A", &widget_a); ImGui::SameLine();
ImGui::Checkbox("Widget B", &widget_b); ImGui::SameLine();
ImGui::Checkbox("Widget C", &widget_c);
ImGui::Checkbox("Spring A", &spring_a); ImGui::SameLine();
ImGui::Checkbox("Spring AB", &spring_ab); ImGui::SameLine();
ImGui::Checkbox("Spring BC", &spring_bc); ImGui::SameLine();
ImGui::Checkbox("Spring C", &spring_c);
ImGui::Checkbox("Horizontal", &horizontal); ImGui::SameLine();
ImGui::Checkbox("Minimize Width", &minimize_width); ImGui::SameLine();
ImGui::Checkbox("Minimize Height", &minimize_height);
ImGui::DragFloat("Item Spacing", horizontal ? &item_spacing.x : &item_spacing.y, 0.1f, 0.0f, 50.0f);
ImGui::DragFloat("A & C Spring Weight", &a_c_spring_weight, 0.002f, 0.0f, 1.0f);
ImGui::DragFloat("AB Spring Weight", &ab_spring_weight, 0.002f, 0.0f, 1.0f);
if (ImGui::IsItemHovered()) ImGui::SetTooltip("BC Spring Weight = 1 - AB Spring Weight");
ImGui::DragFloat("Minor Axis Alignment", &alignment, 0.002f, 0.0f, 1.0f);
if (ImGui::IsItemHovered()) ImGui::SetTooltip("This is vertical alignment for horizontal layouts and horizontal alignment for vertical layouts.");
ImGui::Text("Layout widgets:");
ImGui::Text("| Spring A | Widget A | Spring AB | Widget B | Spring BC | Widget C | Spring C |");
ImGui::Spacing();
ImVec2 widget_size;
widget_size.x = ImGui::GetContentRegionAvail().x / 4;
widget_size.y = horizontal ? floorf(widget_size.x / 3) : widget_size.x;
ImVec2 small_widget_size = widget_size;
if (horizontal)
small_widget_size.y = floorf(small_widget_size.y / 2);
else
small_widget_size.x = floorf(small_widget_size.x / 2);
ImVec2 layout_size = ImVec2(widget_size.x * 4, widget_size.y * 4);
if (minimize_width) layout_size.x = 0.0f;
if (minimize_height) layout_size.y = 0.0f;
// Minor axis alignment can be set by style or directly in BeginHorizontal/BeginVertical
// Example:
// ImGui::PushStyleVar(ImGuiStyleVar_LayoutAlign, alignment);
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, item_spacing);
if (horizontal) { ImGui::BeginHorizontal("h1", layout_size, alignment); } else { ImGui::BeginVertical("v1", layout_size, alignment); }
if (spring_a) { funcs::VisibleSpring(a_c_spring_weight); }
if (widget_a) { ImGui::Button("Widget A", widget_size); }
if (spring_ab) { funcs::VisibleSpring(ab_spring_weight); }
if (widget_b) { ImGui::Button("Widget B", small_widget_size); }
if (spring_bc) { funcs::VisibleSpring(1.0f - ab_spring_weight); }
if (widget_c) { ImGui::Button("Widget C", widget_size); }
if (spring_c) { funcs::VisibleSpring(a_c_spring_weight); }
if (horizontal) { ImGui::EndHorizontal(); } else { ImGui::EndVertical(); }
ImGui::PopStyleVar();
ImDrawList* draw_list = ImGui::GetWindowDrawList();
draw_list->AddRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), ImGui::GetColorU32(ImGuiCol_Border));
ImGui::TreePop();
}
}
static void ShowDemoWindowPopups()

@ -153,6 +153,7 @@ struct ImGuiWindowSettings; // Storage for a window .ini settings (we ke
// Use your programming IDE "Go to definition" facility on the names of the center columns to find the actual flags/enum lists.
typedef int ImGuiDataAuthority; // -> enum ImGuiDataAuthority_ // Enum: for storing the source authority (dock node vs window) of a field
typedef int ImGuiLayoutType; // -> enum ImGuiLayoutType_ // Enum: Horizontal or vertical
typedef int ImGuiLayoutItemType; // -> enum ImGuiLayoutItemType_ // Enum: Item or Spring
typedef int ImGuiItemFlags; // -> enum ImGuiItemFlags_ // Flags: for PushItemFlag()
typedef int ImGuiItemAddFlags; // -> enum ImGuiItemAddFlags_ // Flags: for ItemAdd()
typedef int ImGuiItemStatusFlags; // -> enum ImGuiItemStatusFlags_ // Flags: for DC.LastItemStatusFlags
@ -888,6 +889,12 @@ enum ImGuiLayoutType_
ImGuiLayoutType_Vertical = 1
};
enum ImGuiLayoutItemType_
{
ImGuiLayoutItemType_Item,
ImGuiLayoutItemType_Spring
};
enum ImGuiLogType
{
ImGuiLogType_None = 0,
@ -1233,6 +1240,72 @@ struct ImGuiPtrOrIndex
ImGuiPtrOrIndex(int index) { Ptr = NULL; Index = index; }
};
// sizeof() == 48
struct ImGuiLayoutItem
{
ImGuiLayoutItemType Type; // Type of an item
ImRect MeasuredBounds;
float SpringWeight; // Weight of a spring
float SpringSpacing; // Spring spacing
float SpringSize; // Calculated spring size
float CurrentAlign;
float CurrentAlignOffset;
unsigned int VertexIndexBegin;
unsigned int VertexIndexEnd;
ImGuiLayoutItem(ImGuiLayoutItemType type)
{
Type = type;
MeasuredBounds = ImRect(0, 0, 0, 0); // FIXME: @thedmd are you sure the default ImRect value FLT_MAX,FLT_MAX,-FLT_MAX,-FLT_MAX aren't enough here?
SpringWeight = 1.0f;
SpringSpacing = -1.0f;
SpringSize = 0.0f;
CurrentAlign = 0.0f;
CurrentAlignOffset = 0.0f;
VertexIndexBegin = VertexIndexEnd = (ImDrawIdx)0;
}
};
struct ImGuiLayout
{
ImGuiID Id;
ImGuiLayoutType Type;
bool Live;
ImVec2 Size; // Size passed to BeginLayout
ImVec2 CurrentSize; // Bounds of layout known at the beginning the frame.
ImVec2 MinimumSize; // Minimum possible size when springs are collapsed.
ImVec2 MeasuredSize; // Measured size with springs expanded.
ImVector<ImGuiLayoutItem> Items;
int CurrentItemIndex;
int ParentItemIndex;
ImGuiLayout* Parent;
ImGuiLayout* FirstChild;
ImGuiLayout* NextSibling;
float Align; // Current item alignment.
float Indent; // Indent used to align items in vertical layout.
ImVec2 StartPos; // Initial cursor position when BeginLayout is called.
ImVec2 StartCursorMaxPos; // Maximum cursor position when BeginLayout is called.
ImGuiLayout(ImGuiID id, ImGuiLayoutType type)
{
Id = id;
Type = type;
Live = false;
Size = CurrentSize = MinimumSize = MeasuredSize = ImVec2(0, 0);
CurrentItemIndex = 0;
ParentItemIndex = 0;
Parent = FirstChild = NextSibling = NULL;
Align = -1.0f;
Indent = 0.0f;
StartPos = ImVec2(0, 0);
StartCursorMaxPos = ImVec2(0, 0);
}
};
//-----------------------------------------------------------------------------
// [SECTION] Columns support
//-----------------------------------------------------------------------------
@ -2052,6 +2125,10 @@ struct IMGUI_API ImGuiWindowTempData
int CurrentTableIdx; // Current table index (into g.Tables)
ImGuiLayoutType LayoutType;
ImGuiLayoutType ParentLayoutType; // Layout type of parent window at the time of Begin()
ImGuiLayout* CurrentLayout;
ImGuiLayoutItem* CurrentLayoutItem;
ImVector<ImGuiLayout*> LayoutStack;
ImGuiStorage Layouts;
int FocusCounterRegular; // (Legacy Focus/Tabbing system) Sequential counter, start at -1 and increase as assigned via FocusableItemRegister() (FIXME-NAV: Needs redesign)
int FocusCounterTabStop; // (Legacy Focus/Tabbing system) Same, but only count widgets which you can Tab through.

Loading…
Cancel
Save