mirror of
https://github.com/ocornut/imgui.git
synced 2026-09-26 16:05:45 +03:00
Stack Layout implementation v2.0
This commit is contained in:
parent
b62cf3894b
commit
3e908cdc92
4 changed files with 1099 additions and 5 deletions
859
imgui.cpp
859
imgui.cpp
|
|
@ -85,6 +85,7 @@ CODE
|
|||
// [SECTION] ERROR CHECKING, STATE RECOVERY
|
||||
// [SECTION] ITEM SUBMISSION
|
||||
// [SECTION] LAYOUT
|
||||
// [SECTION] STACK LAYOUT
|
||||
// [SECTION] SCROLLING
|
||||
// [SECTION] TOOLTIPS
|
||||
// [SECTION] POPUPS
|
||||
|
|
@ -1374,6 +1375,30 @@ static void NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_win
|
|||
static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window);
|
||||
static void NavRestoreLayer(ImGuiNavLayer layer);
|
||||
|
||||
// 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 void BeginLayoutClipRect(ImGuiLayout& layout);
|
||||
static void EndLayoutClipRect(ImGuiLayout& layout);
|
||||
static void ApplyLayoutClipRect(ImGuiLayout& layout);
|
||||
static void MergeLayoutSplitters(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 and Debug Tools
|
||||
static void ErrorCheckNewFrameSanityChecks();
|
||||
static void ErrorCheckEndFrameSanityChecks();
|
||||
|
|
@ -1489,6 +1514,7 @@ ImGuiStyle::ImGuiStyle()
|
|||
ScrollbarPadding = 2.0f; // Padding of scrollbar grab within its frame (same for both axes)
|
||||
GrabMinSize = 12.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.
|
||||
ImageRounding = 0.0f; // Rounding of Image() calls.
|
||||
ImageBorderSize = 0.0f; // Thickness of border around tabs.
|
||||
|
|
@ -3655,6 +3681,7 @@ static const ImGuiStyleVarInfo GStyleVarsInfo[] =
|
|||
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, SeparatorTextBorderSize)}, // ImGuiStyleVar_SeparatorTextBorderSize
|
||||
{ 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, SeparatorTextAlign) }, // ImGuiStyleVar_SeparatorTextAlign
|
||||
{ 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, SeparatorTextPadding) }, // ImGuiStyleVar_SeparatorTextPadding
|
||||
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, LayoutAlign) }, // ImGuiStyleVar_LayoutAlign
|
||||
};
|
||||
|
||||
const ImGuiStyleVarInfo* ImGui::GetStyleVarInfo(ImGuiStyleVar idx)
|
||||
|
|
@ -4589,6 +4616,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);
|
||||
}
|
||||
}
|
||||
|
||||
static void SetCurrentWindow(ImGuiWindow* window)
|
||||
|
|
@ -8085,6 +8118,13 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
|
|||
window->DC.WindowItemStatusFlags |= IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0;
|
||||
SetLastItemDataForWindow(window, 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;
|
||||
}
|
||||
|
||||
// [DEBUG]
|
||||
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
|
||||
if (g.DebugLocateId != 0 && (window->ID == g.DebugLocateId || window->MoveId == g.DebugLocateId))
|
||||
|
|
@ -11003,6 +11043,19 @@ void ImGui::ErrorRecoveryTryToRecoverWindowState(const ImGuiErrorRecoveryStat
|
|||
IM_ASSERT_USER_ERROR(0, "Missing EndMultiSelect()");
|
||||
EndMultiSelect();
|
||||
}
|
||||
while (window->DC.LayoutStack.Size > 0) //-V1044
|
||||
{
|
||||
if (window->DC.LayoutStack.back()->Type == ImGuiLayoutType_Horizontal)
|
||||
{
|
||||
IM_ASSERT_USER_ERROR(0, "Missing EndHorizontal()");
|
||||
EndHorizontal();
|
||||
}
|
||||
else
|
||||
{
|
||||
IM_ASSERT_USER_ERROR(0, "Missing EndVertical()");
|
||||
EndVertical();
|
||||
}
|
||||
}
|
||||
if (window->DC.MenuBarAppending) //-V1044
|
||||
{
|
||||
IM_ASSERT_USER_ERROR(0, "Missing EndMenuBar()");
|
||||
|
|
@ -11260,6 +11313,9 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu
|
|||
g.NextItemData.HasFlags = ImGuiNextItemDataFlags_None;
|
||||
g.NextItemData.ItemFlags = ImGuiItemFlags_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(id, g.LastItemData.NavRect, &g.LastItemData);
|
||||
|
|
@ -11359,6 +11415,36 @@ void ImGui::ItemSize(const ImVec2& size, float text_baseline_y)
|
|||
if (window->SkipItems)
|
||||
return;
|
||||
|
||||
ImGuiLayoutType layout_type = window->DC.LayoutType;
|
||||
if (window->DC.CurrentLayout)
|
||||
layout_type = window->DC.CurrentLayout->Type;
|
||||
|
||||
//if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorPos, 3.0f, IM_COL32(255,255,0,255), 4); // [DEBUG] Widget position
|
||||
|
||||
// Stack Layouts: Handle horizontal case first to simplify merge in case code handling vertical changes.
|
||||
if (layout_type == ImGuiLayoutType_Horizontal)
|
||||
{
|
||||
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_TRUNC(window->DC.CursorPos.x + line_width + g.Style.ItemSpacing.x);
|
||||
window->DC.CursorPos.y = IM_TRUNC(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;
|
||||
window->DC.IsSameLine = window->DC.IsSetPos = false;
|
||||
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.
|
||||
|
|
@ -11377,15 +11463,12 @@ void ImGui::ItemSize(const ImVec2& size, float text_baseline_y)
|
|||
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;
|
||||
window->DC.IsSameLine = window->DC.IsSetPos = false;
|
||||
|
||||
// Horizontal layout mode
|
||||
if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
|
||||
SameLine();
|
||||
}
|
||||
IM_MSVC_RUNTIME_CHECKS_RESTORE
|
||||
|
||||
|
|
@ -11747,6 +11830,774 @@ 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);
|
||||
|
||||
IM_ASSERT(!layout->Live && "BeginHorizontal/BeginVertical with same ID is already live in this frame. Please use PushID() to make ID's unique or rename layout.");
|
||||
|
||||
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;
|
||||
|
||||
BeginLayoutClipRect(*layout);
|
||||
|
||||
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);
|
||||
IM_UNUSED(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;
|
||||
layout->MeasuredSize = measured_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);
|
||||
|
||||
EndLayoutClipRect(*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);
|
||||
IM_UNUSED(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 void ImGui::BeginLayoutClipRect(ImGuiLayout& layout)
|
||||
{
|
||||
ImGuiWindow* window = GetCurrentWindow();
|
||||
|
||||
// Use splitter to collect draw commands in separate channel,
|
||||
// so we can clip them to the layout bounds.
|
||||
layout.Splitter.Split(window->DrawList, 2);
|
||||
layout.Splitter.SetCurrentChannel(window->DrawList, 1);
|
||||
|
||||
// Clip to layout bounds, unrestricted and not measured bounds span
|
||||
// all the way to the edge of the window.
|
||||
ImVec2 clip_rect_min = layout.StartPos;
|
||||
ImVec2 clip_rect_max;
|
||||
clip_rect_max.x = layout.Size.x > 0.0f ? layout.StartPos.x + layout.Size.x : FLT_MAX;
|
||||
clip_rect_max.y = layout.Size.y > 0.0f ? layout.StartPos.y + layout.Size.y : FLT_MAX;
|
||||
|
||||
PushClipRect(clip_rect_min, clip_rect_max, true);
|
||||
}
|
||||
|
||||
static void ImGui::EndLayoutClipRect(ImGuiLayout& layout)
|
||||
{
|
||||
PopClipRect();
|
||||
|
||||
if (layout.Parent != NULL)
|
||||
return;
|
||||
|
||||
ApplyLayoutClipRect(layout);
|
||||
|
||||
MergeLayoutSplitters(layout);
|
||||
}
|
||||
|
||||
static void ImGui::ApplyLayoutClipRect(ImGuiLayout& layout)
|
||||
{
|
||||
for (ImGuiLayout* child = layout.FirstChild; child != NULL; child = child->NextSibling)
|
||||
ApplyLayoutClipRect(*child);
|
||||
|
||||
ImGuiWindow* window = GetCurrentWindow();
|
||||
|
||||
ImVec4 current_clip_rect;
|
||||
current_clip_rect.x = layout.StartPos.x;
|
||||
current_clip_rect.y = layout.StartPos.y;
|
||||
current_clip_rect.z = layout.StartPos.x + layout.MeasuredSize.x;
|
||||
current_clip_rect.w = layout.StartPos.y + layout.MeasuredSize.y;
|
||||
|
||||
layout.Splitter.SetCurrentChannel(window->DrawList, 0);
|
||||
for (ImDrawCmd& cmd : layout.Splitter._Channels[1]._CmdBuffer)
|
||||
{
|
||||
|
||||
if (cmd.ClipRect.x < current_clip_rect.x) cmd.ClipRect.x = current_clip_rect.x;
|
||||
if (cmd.ClipRect.y < current_clip_rect.y) cmd.ClipRect.y = current_clip_rect.y;
|
||||
if (cmd.ClipRect.z > current_clip_rect.z) cmd.ClipRect.z = current_clip_rect.z;
|
||||
if (cmd.ClipRect.w > current_clip_rect.w) cmd.ClipRect.w = current_clip_rect.w;
|
||||
}
|
||||
|
||||
//GetForegroundDrawList()->AddRect(layout.StartPos, layout.StartPos + layout.MeasuredSize, IM_COL32(255,0,0,128)); // [DEBUG]
|
||||
}
|
||||
|
||||
static void ImGui::MergeLayoutSplitters(ImGuiLayout& layout)
|
||||
{
|
||||
for (ImGuiLayout* child = layout.FirstChild; child != NULL; child = child->NextSibling)
|
||||
MergeLayoutSplitters(*child);
|
||||
|
||||
ImGuiWindow* window = GetCurrentWindow();
|
||||
|
||||
layout.Splitter.Merge(window->DrawList);
|
||||
}
|
||||
|
||||
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());
|
||||
IM_UNUSED(window);
|
||||
PopLayout(NULL);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// [SECTION] SCROLLING
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
|
|||
15
imgui.h
15
imgui.h
|
|
@ -33,6 +33,7 @@
|
|||
#define IMGUI_VERSION_NUM 19267
|
||||
#define IMGUI_HAS_TABLE // Added BeginTable() - from IMGUI_VERSION_NUM >= 18000
|
||||
#define IMGUI_HAS_TEXTURES // Added ImGuiBackendFlags_RendererHasTextures - from IMGUI_VERSION_NUM >= 19198
|
||||
#define IMGUI_HAS_STACK_LAYOUT // Stack-Layout PR #846
|
||||
|
||||
/*
|
||||
|
||||
|
|
@ -591,6 +592,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.com/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:
|
||||
|
|
@ -1858,6 +1871,7 @@ enum ImGuiStyleVar_
|
|||
ImGuiStyleVar_SeparatorTextBorderSize, // float SeparatorTextBorderSize
|
||||
ImGuiStyleVar_SeparatorTextAlign, // ImVec2 SeparatorTextAlign
|
||||
ImGuiStyleVar_SeparatorTextPadding, // ImVec2 SeparatorTextPadding
|
||||
ImGuiStyleVar_LayoutAlign, // float LayoutAlign
|
||||
ImGuiStyleVar_COUNT
|
||||
};
|
||||
|
||||
|
|
@ -2312,6 +2326,7 @@ struct ImGuiStyle
|
|||
float ScrollbarPadding; // Padding of scrollbar grab within its frame (same for both axes).
|
||||
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 ImageRounding; // Rounding of Image() calls.
|
||||
float ImageBorderSize; // Thickness of border around Image() calls.
|
||||
|
|
|
|||
148
imgui_demo.cpp
148
imgui_demo.cpp
|
|
@ -5238,6 +5238,154 @@ static void DemoWindowLayout()
|
|||
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
IMGUI_DEMO_MARKER("Layout/Stack Layout");
|
||||
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)
|
||||
{
|
||||
ImGui::Spring(spring_weight);
|
||||
if (!draw_springs)
|
||||
return;
|
||||
|
||||
ImVec2 rect_min = ImGui::GetItemRectMin();
|
||||
ImVec2 rect_max = ImGui::GetItemRectMax();
|
||||
|
||||
ImVec2 rect_size = ImGui::GetItemRectSize();
|
||||
if (rect_size.x <= 0.0f && rect_size.y <= 0.0f)
|
||||
return;
|
||||
|
||||
// Draw zig-zag
|
||||
float width = 0.0f, spacing = 0.0f;
|
||||
ImVec2 direction, origin;
|
||||
ImVec2 spacing_min, spring_max;
|
||||
|
||||
if (horizontal)
|
||||
{
|
||||
spacing = floorf(item_spacing.x);
|
||||
width = rect_size.x - spacing;
|
||||
origin = ImVec2(floorf(rect_min.x), floorf(rect_min.y + (rect_max.y - rect_min.y) / 2));
|
||||
direction = ImVec2(1.0f, 0.0f);
|
||||
spring_max = ImVec2(rect_min.x + width, rect_max.y);
|
||||
spacing_min = ImVec2(rect_min.x + width, rect_min.y);
|
||||
}
|
||||
else
|
||||
{
|
||||
spacing = floorf(item_spacing.y);
|
||||
width = rect_size.y - spacing;
|
||||
origin = ImVec2(floorf(rect_min.x + (rect_max.x - rect_min.x) / 2), floorf(rect_min.y));
|
||||
direction = ImVec2(0.0f, 1.0f);
|
||||
spring_max = ImVec2(rect_max.x, rect_min.y + width);
|
||||
spacing_min = ImVec2(rect_min.x, rect_min.y + width);
|
||||
}
|
||||
|
||||
if (spring_weight <= 0.0f && spacing <= 0.0f)
|
||||
return;
|
||||
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
|
||||
draw_list->PushClipRect(rect_min, rect_max, true);
|
||||
|
||||
draw_list->AddRectFilled(rect_min, spring_max, ImColor(80, 20, 80));
|
||||
draw_list->AddRectFilled(spacing_min, rect_max, ImColor(80, 20, 20));
|
||||
|
||||
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::Checkbox("Draw Springs", &draw_springs); ImGui::SameLine();
|
||||
ImGui::TextUnformatted(" "); ImGui::SameLine();
|
||||
ImGui::ColorButton("- Spring", ImColor(80, 20, 80), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoPicker); ImGui::SameLine();
|
||||
ImGui::TextUnformatted("Spring"); ImGui::SameLine();
|
||||
ImGui::TextUnformatted(" "); ImGui::SameLine();
|
||||
ImGui::ColorButton("- Spacing", ImColor(80, 20, 20), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoPicker); ImGui::SameLine();
|
||||
ImGui::TextUnformatted("Item Spacing");
|
||||
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 = floorf(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, ImVec2(floorf(item_spacing.x), floorf(item_spacing.y)));
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -192,6 +192,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.
|
||||
enum ImGuiLocKey : int; // -> enum ImGuiLocKey // Enum: a localization entry for translation.
|
||||
typedef int ImGuiLayoutType; // -> enum ImGuiLayoutType_ // Enum: Horizontal or vertical
|
||||
typedef int ImGuiLayoutItemType; // -> enum ImGuiLayoutItemType_ // Enum: Item or Spring
|
||||
|
||||
// Flags
|
||||
typedef int ImDrawTextFlags; // -> enum ImDrawTextFlags_ // Flags: for ImTextCalcWordWrapPositionEx()
|
||||
|
|
@ -278,7 +279,7 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer
|
|||
#define IM_F32_TO_INT8_UNBOUND(_VAL) ((int)((_VAL) * 255.0f + ((_VAL)>=0 ? 0.5f : -0.5f))) // Unsaturated, for display purpose
|
||||
#define IM_F32_TO_INT8_SAT(_VAL) ((int)(ImSaturate(_VAL) * 255.0f + 0.5f)) // Saturated, always output 0..255
|
||||
#define IM_TRUNC(_VAL) ((float)(int)(_VAL)) // Positive values only! ImTrunc() is not inlined in MSVC debug builds
|
||||
#define IM_ROUND(_VAL) ((float)(int)((_VAL) + 0.5f)) // Positive values only!
|
||||
#define IM_ROUND(_VAL) ((float)(int)((_VAL) + 0.5f)) // Positive values only!
|
||||
//#define IM_FLOOR IM_TRUNC // [OBSOLETE] Renamed in 1.90.0 (Sept 2023)
|
||||
|
||||
// Hint for branch prediction
|
||||
|
|
@ -1127,6 +1128,12 @@ enum ImGuiLayoutType_
|
|||
ImGuiLayoutType_Vertical = 1
|
||||
};
|
||||
|
||||
enum ImGuiLayoutItemType_
|
||||
{
|
||||
ImGuiLayoutItemType_Item,
|
||||
ImGuiLayoutItemType_Spring
|
||||
};
|
||||
|
||||
// Flags for LogBegin() text capturing function
|
||||
enum ImGuiLogFlags_
|
||||
{
|
||||
|
|
@ -1447,6 +1454,7 @@ struct ImGuiPtrOrIndex
|
|||
ImGuiPtrOrIndex(int index) { Ptr = NULL; Index = index; }
|
||||
};
|
||||
|
||||
|
||||
// Data used by IsItemDeactivated()/IsItemDeactivatedAfterEdit() functions
|
||||
struct ImGuiDeactivatedItemData
|
||||
{
|
||||
|
|
@ -1456,6 +1464,74 @@ struct ImGuiDeactivatedItemData
|
|||
bool IsAlive;
|
||||
};
|
||||
|
||||
// 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.
|
||||
|
||||
ImDrawListSplitter Splitter;
|
||||
|
||||
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] Popup support
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
@ -2633,6 +2709,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;
|
||||
ImU32 ModalDimBgColor;
|
||||
ImGuiItemStatusFlags WindowItemStatusFlags;
|
||||
ImGuiItemStatusFlags ChildItemStatusFlags;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue