1207 lines
41 KiB
C++
1207 lines
41 KiB
C++
// dear imgui, v1.86 WIP
|
|
// (stack layout code)
|
|
|
|
/*
|
|
|
|
Index of this file:
|
|
|
|
// [SECTION] Commentary
|
|
// [SECTION] Header mess
|
|
// [SECTION] Stack layout: Forward declarations
|
|
// [SECTION] Stack layout: flags, enums, data structures
|
|
// [SECTION] Stack Layout: Context forward declarations
|
|
// [SECTION] Stack Layout: Internal code forward declarations
|
|
// [SECTION] Stack Layout: Context
|
|
// [SECTION] Stack Layout: Internal code
|
|
// [SECTION] Stack Layout: Internal API
|
|
// [SECTION] Stack Layout: Public API
|
|
|
|
*/
|
|
|
|
// Navigating this file:
|
|
// - In Visual Studio IDE: CTRL+comma ("Edit.NavigateTo") can follow symbols in comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot.
|
|
// - With Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols in comments.
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// [SECTION] Commentary
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Typical call flow: (root level is generally public API):
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// - BeginHorizontal/BeginVertical() user begin into a layout
|
|
// - [...] user emit contents
|
|
// - | Spring() - separate widget groups in layout
|
|
// - | SuspendLayout() - stop any layout operations
|
|
// - | ResumeLayout() - resume layout operations
|
|
//-----------------------------------------------------------------------------
|
|
// - EndHorizontal/EndVertical() user ends the layout
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// [SECTION] Header mess
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
|
|
#define _CRT_SECURE_NO_WARNINGS
|
|
#endif
|
|
|
|
#ifndef IMGUI_DEFINE_MATH_OPERATORS
|
|
#define IMGUI_DEFINE_MATH_OPERATORS
|
|
#endif
|
|
|
|
#include "imgui.h"
|
|
#ifndef IMGUI_DISABLE
|
|
#include "imgui_internal.h"
|
|
|
|
#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
|
|
#include <stddef.h> // intptr_t
|
|
#else
|
|
#include <stdint.h> // intptr_t
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// [SECTION] Stack layout: Forward declarations
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Use your programming IDE "Go to definition" facility on the names of the center columns to find the actual flags/enum lists.
|
|
typedef int ImGuiLayoutItemType; // -> enum ImGuiLayoutItemType_ // Enum: Item or Spring
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// [SECTION] Stack layout: flags, enums, data structures
|
|
//-----------------------------------------------------------------------------
|
|
|
|
enum ImGuiLayoutItemType_
|
|
{
|
|
ImGuiLayoutItemType_Item,
|
|
ImGuiLayoutItemType_Spring
|
|
};
|
|
|
|
// 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);
|
|
}
|
|
};
|
|
|
|
struct ImGuiLayoutWindowState
|
|
{
|
|
ImGuiWindow* Window;
|
|
ImGuiLayout* CurrentLayout;
|
|
ImGuiLayoutItem* CurrentLayoutItem;
|
|
ImVector<ImGuiLayout*> LayoutStack;
|
|
ImGuiStorage Layouts;
|
|
|
|
ImGuiLayoutWindowState()
|
|
{
|
|
Window = NULL;
|
|
CurrentLayout = NULL;
|
|
CurrentLayoutItem = NULL;
|
|
}
|
|
};
|
|
|
|
struct ImGuiLayoutState
|
|
{
|
|
ImGuiStorage LayoutWindowStates;
|
|
|
|
ImGuiContext* Context;
|
|
ImGuiID ContextID;
|
|
ImGuiID NewFramePreID;
|
|
ImGuiID EndFramePreID;
|
|
ImGuiID ShutdownHookID;
|
|
|
|
ImGuiLayoutState()
|
|
{
|
|
Context = NULL;
|
|
ContextID = 0;
|
|
NewFramePreID = 0;
|
|
EndFramePreID = 0;
|
|
ShutdownHookID = 0;
|
|
}
|
|
};
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// [SECTION] Stack Layout: Context forward declarations
|
|
//-----------------------------------------------------------------------------
|
|
|
|
static ImGuiStorage GStackLayoutStates;
|
|
|
|
// Context management/hooks
|
|
static ImGuiID GetContextID(ImGuiContext* context); // FIXME: ImGuiContext probably should have their own ID assigned
|
|
static ImGuiLayoutState* GetCurrentLayoutState();
|
|
static ImGuiLayoutState* GetLayoutState(ImGuiContext* context);
|
|
static ImGuiLayoutState* CreateLayoutState(ImGuiID context_id, ImGuiContext* context);
|
|
static void StackLayout_NewFramePreCallback(ImGuiContext* ctx, ImGuiContextHook* hook);
|
|
static void StackLayout_EndFramePreCallback(ImGuiContext* ctx, ImGuiContextHook* hook);
|
|
static void StackLayout_ShutdownCallback(ImGuiContext* ctx, ImGuiContextHook* hook);
|
|
|
|
static ImGuiLayoutWindowState* GetWindowLayoutState(ImGuiID window_id, ImGuiWindow* window = NULL);
|
|
static ImGuiLayoutWindowState* GetCurrentWindowLayoutState();
|
|
static void WindowLayoutState_OnNewFrame(ImGuiLayoutWindowState* state);
|
|
static void WindowLayoutState_OnEndFrame(ImGuiLayoutWindowState* state);
|
|
static void WindowLayoutState_OnShutdown(ImGuiLayoutWindowState* state);
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// [SECTION] Stack Layout: Internal code forward declarations
|
|
//-----------------------------------------------------------------------------
|
|
|
|
namespace ImGui
|
|
{
|
|
// 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 ImVec2 CalculateLayoutSize(ImGuiLayout& layout, bool collapse_springs);
|
|
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 bool HasAnyNonZeroSpring(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 ImGuiLayoutItem* GenerateLayoutItem(ImGuiLayout& layout, ImGuiLayoutItemType type);
|
|
static float CalculateLayoutItemAlignmentOffset(ImGuiLayout& layout, ImGuiLayoutItem& item);
|
|
static void TranslateLayoutItem(ImGuiLayoutItem& item, const ImVec2& offset);
|
|
static void SignedIndent(float indent);
|
|
static void BeginLayoutItem(ImGuiLayout& layout);
|
|
static void EndLayoutItem(ImGuiLayout& layout);
|
|
static void AddLayoutSpring(ImGuiLayout& layout, float weight, float spacing);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// [SECTION] Stack Layout: Context
|
|
//-----------------------------------------------------------------------------
|
|
|
|
static ImGuiID GetContextID(ImGuiContext* context)
|
|
{
|
|
// Hash pointer to ImGuiContext which is unique
|
|
return ImHashData(&context, sizeof(context));
|
|
}
|
|
|
|
static ImGuiLayoutState* GetCurrentLayoutState()
|
|
{
|
|
return GetLayoutState(ImGui::GetCurrentContext());
|
|
}
|
|
|
|
static ImGuiLayoutState* GetLayoutState(ImGuiContext* context)
|
|
{
|
|
if (context == NULL)
|
|
return NULL;
|
|
|
|
ImGuiID context_id = GetContextID(context);
|
|
|
|
ImGuiLayoutState* state = (ImGuiLayoutState*)GStackLayoutStates.GetVoidPtr(context_id);
|
|
if (state == NULL)
|
|
state = CreateLayoutState(context_id, context);
|
|
|
|
return state;
|
|
}
|
|
|
|
static ImGuiLayoutState* CreateLayoutState(ImGuiID context_id, ImGuiContext* context)
|
|
{
|
|
ImGuiLayoutState* state = IM_NEW(ImGuiLayoutState);
|
|
state->Context = context;
|
|
state->ContextID = context_id;
|
|
|
|
ImGuiContextHook new_frame_pre_hook;
|
|
new_frame_pre_hook.Type = ImGuiContextHookType_NewFramePre;
|
|
new_frame_pre_hook.UserData = state;
|
|
new_frame_pre_hook.Callback = StackLayout_NewFramePreCallback;
|
|
state->NewFramePreID = ImGui::AddContextHook(context, &new_frame_pre_hook);
|
|
|
|
ImGuiContextHook end_frame_pre_hook;
|
|
end_frame_pre_hook.Type = ImGuiContextHookType_EndFramePre;
|
|
end_frame_pre_hook.UserData = state;
|
|
end_frame_pre_hook.Callback = StackLayout_EndFramePreCallback;
|
|
state->EndFramePreID = ImGui::AddContextHook(context, &end_frame_pre_hook);
|
|
|
|
ImGuiContextHook shutdown_hook;
|
|
shutdown_hook.Type = ImGuiContextHookType_Shutdown;
|
|
shutdown_hook.UserData = state;
|
|
shutdown_hook.Callback = StackLayout_ShutdownCallback;
|
|
state->ShutdownHookID = ImGui::AddContextHook(context, &shutdown_hook);
|
|
|
|
GStackLayoutStates.SetVoidPtr(context_id, state);
|
|
|
|
return state;
|
|
}
|
|
|
|
static void StackLayout_NewFramePreCallback(ImGuiContext* ctx, ImGuiContextHook* hook)
|
|
{
|
|
ImGuiLayoutState* layout_state = (ImGuiLayoutState*)hook->UserData;
|
|
|
|
for (int i = 0; i < layout_state->LayoutWindowStates.Data.Size; ++i)
|
|
{
|
|
ImGuiLayoutWindowState* layout_window_state = (ImGuiLayoutWindowState*)layout_state->LayoutWindowStates.Data[i].val_p;
|
|
|
|
WindowLayoutState_OnNewFrame(layout_window_state);
|
|
}
|
|
}
|
|
|
|
static void StackLayout_EndFramePreCallback(ImGuiContext* ctx, ImGuiContextHook* hook)
|
|
{
|
|
ImGuiLayoutState* layout_state = (ImGuiLayoutState*)hook->UserData;
|
|
|
|
for (int i = 0; i < layout_state->LayoutWindowStates.Data.Size; ++i)
|
|
{
|
|
ImGuiLayoutWindowState* layout_window_state = (ImGuiLayoutWindowState*)layout_state->LayoutWindowStates.Data[i].val_p;
|
|
|
|
WindowLayoutState_OnEndFrame(layout_window_state);
|
|
}
|
|
}
|
|
|
|
static void StackLayout_ShutdownCallback(ImGuiContext* ctx, ImGuiContextHook* hook)
|
|
{
|
|
ImGuiLayoutState* layout_state = (ImGuiLayoutState*)hook->UserData;
|
|
|
|
for (int i = 0; i < layout_state->LayoutWindowStates.Data.Size; ++i)
|
|
{
|
|
ImGuiLayoutWindowState* layout_window_state = (ImGuiLayoutWindowState*)layout_state->LayoutWindowStates.Data[i].val_p;
|
|
|
|
WindowLayoutState_OnShutdown(layout_window_state);
|
|
|
|
IM_DELETE(layout_window_state);
|
|
}
|
|
|
|
GStackLayoutStates.SetVoidPtr(layout_state->ContextID, nullptr);
|
|
|
|
IM_DELETE(layout_state);
|
|
}
|
|
|
|
|
|
static ImGuiLayoutWindowState* GetWindowLayoutState(ImGuiID window_id, ImGuiWindow* window)
|
|
{
|
|
ImGuiLayoutState* layout_state = GetCurrentLayoutState();
|
|
if (layout_state == NULL)
|
|
return NULL;
|
|
|
|
ImGuiLayoutWindowState* window_layout_state = (ImGuiLayoutWindowState*)layout_state->LayoutWindowStates.GetVoidPtr(window_id);
|
|
if (window_layout_state == NULL)
|
|
{
|
|
if (window == NULL)
|
|
{
|
|
window = ImGui::FindWindowByID(window_id);
|
|
IM_ASSERT(window && "GetWindowLayoutState called with invalid Window ID.");
|
|
}
|
|
|
|
window_layout_state = IM_NEW(ImGuiLayoutWindowState);
|
|
window_layout_state->Window = window;
|
|
layout_state->LayoutWindowStates.SetVoidPtr(window_id, window_layout_state);
|
|
}
|
|
|
|
return window_layout_state;
|
|
|
|
}
|
|
|
|
|
|
static ImGuiLayoutWindowState* GetCurrentWindowLayoutState()
|
|
{
|
|
ImGuiWindow* current_window = ImGui::GetCurrentWindow();
|
|
if (current_window == NULL)
|
|
return NULL;
|
|
|
|
return GetWindowLayoutState(current_window->ID, current_window);
|
|
}
|
|
|
|
static void WindowLayoutState_OnNewFrame(ImGuiLayoutWindowState* state)
|
|
{
|
|
// Mark all layouts as dead. They may be revived in this frame.
|
|
for (int i = 0; i < state->Layouts.Data.Size; i++)
|
|
{
|
|
ImGuiLayout* layout = (ImGuiLayout*)state->Layouts.Data[i].val_p;
|
|
layout->Live = false;
|
|
}
|
|
}
|
|
|
|
static void WindowLayoutState_OnEndFrame(ImGuiLayoutWindowState* state)
|
|
{
|
|
// Check stacks (like ImGuiStackSizes::CompareWithCurrentState() does)
|
|
IM_ASSERT(0 == state->LayoutStack.Size && (!state->LayoutStack.Size || state->LayoutStack.back()->Type == ImGuiLayoutType_Horizontal) && "BeginHorizontal/EndHorizontal Mismatch!");
|
|
IM_ASSERT(0 == state->LayoutStack.Size && (!state->LayoutStack.Size || state->LayoutStack.back()->Type == ImGuiLayoutType_Vertical) && "BeginVertical/EndVertical Mismatch!");
|
|
}
|
|
|
|
static void WindowLayoutState_OnShutdown(ImGuiLayoutWindowState* state)
|
|
{
|
|
for (int i = 0; i < state->Layouts.Data.Size; i++)
|
|
{
|
|
ImGuiLayout* layout = (ImGuiLayout*)state->Layouts.Data[i].val_p;
|
|
IM_DELETE(layout);
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// [SECTION] Stack Layout: Internal code
|
|
//-----------------------------------------------------------------------------
|
|
|
|
static ImGuiLayout* ImGui::FindLayout(ImGuiID id, ImGuiLayoutType type)
|
|
{
|
|
IM_ASSERT(type == ImGuiLayoutType_Horizontal || type == ImGuiLayoutType_Vertical);
|
|
|
|
ImGuiLayoutWindowState* window_state = GetCurrentWindowLayoutState();
|
|
ImGuiLayout* layout = (ImGuiLayout*)window_state->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);
|
|
|
|
ImGuiLayoutWindowState* window_state = GetCurrentWindowLayoutState();
|
|
|
|
ImGuiLayout* layout = IM_NEW(ImGuiLayout)(id, type);
|
|
layout->Size = size;
|
|
|
|
window_state->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)
|
|
{
|
|
ImGuiLayoutWindowState* window_state = GetCurrentWindowLayoutState();
|
|
IM_ASSERT(window_state->CurrentLayout);
|
|
IM_ASSERT(window_state->CurrentLayout->Type == type);
|
|
IM_UNUSED(type);
|
|
|
|
ImGuiLayout* layout = window_state->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_state->CurrentLayoutItem)
|
|
current_layout_item_max = ImMax(window_state->CurrentLayoutItem->MeasuredBounds.Max, layout->StartPos + new_size);
|
|
|
|
window_state->Window->DC.CursorPos = layout->StartPos;
|
|
window_state->Window->DC.CursorMaxPos = layout->StartCursorMaxPos;
|
|
ItemSize(new_size);
|
|
ItemAdd(ImRect(layout->StartPos, layout->StartPos + measured_size), 0);
|
|
|
|
if (window_state->CurrentLayoutItem)
|
|
window_state->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)
|
|
{
|
|
ImGuiLayoutWindowState* window_state = GetCurrentWindowLayoutState();
|
|
|
|
if (layout)
|
|
{
|
|
layout->Parent = window_state->CurrentLayout;
|
|
if (layout->Parent != NULL)
|
|
layout->ParentItemIndex = layout->Parent->CurrentItemIndex;
|
|
if (window_state->CurrentLayout)
|
|
{
|
|
layout->NextSibling = window_state->CurrentLayout->FirstChild;
|
|
layout->FirstChild = NULL;
|
|
window_state->CurrentLayout->FirstChild = layout;
|
|
}
|
|
else
|
|
{
|
|
layout->NextSibling = NULL;
|
|
layout->FirstChild = NULL;
|
|
}
|
|
}
|
|
|
|
window_state->LayoutStack.push_back(layout);
|
|
window_state->CurrentLayout = layout;
|
|
window_state->CurrentLayoutItem = NULL;
|
|
}
|
|
|
|
static void ImGui::PopLayout(ImGuiLayout* layout)
|
|
{
|
|
ImGuiLayoutWindowState* window_state = GetCurrentWindowLayoutState();
|
|
|
|
IM_ASSERT(!window_state->LayoutStack.empty());
|
|
IM_ASSERT(window_state->LayoutStack.back() == layout);
|
|
IM_UNUSED(layout);
|
|
|
|
window_state->LayoutStack.pop_back();
|
|
|
|
if (!window_state->LayoutStack.empty())
|
|
{
|
|
window_state->CurrentLayout = window_state->LayoutStack.back();
|
|
window_state->CurrentLayoutItem = &window_state->CurrentLayout->Items[window_state->CurrentLayout->CurrentItemIndex];
|
|
}
|
|
else
|
|
{
|
|
window_state->CurrentLayout = NULL;
|
|
window_state->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 ImGui::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));
|
|
}
|
|
|
|
ImGuiLayoutWindowState* window_state = GetCurrentWindowLayoutState();
|
|
window_state->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);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// [SECTION] Stack Layout: Internal API
|
|
//-----------------------------------------------------------------------------
|
|
|
|
ImGuiLayoutType ImGuiInternal::GetCurrentLayoutType(ImGuiID window_id)
|
|
{
|
|
ImGuiLayoutWindowState* state = GetWindowLayoutState(window_id);
|
|
|
|
ImGuiLayoutType layout_type = state->Window->DC.LayoutType;
|
|
if (state->CurrentLayout)
|
|
layout_type = state->CurrentLayout->Type;
|
|
|
|
return layout_type;
|
|
}
|
|
|
|
void ImGuiInternal::UpdateItemRect(ImGuiID window_id, const ImVec2& min, const ImVec2& max)
|
|
{
|
|
ImGuiLayoutWindowState* state = GetWindowLayoutState(window_id);
|
|
|
|
if (state->CurrentLayoutItem)
|
|
state->CurrentLayoutItem->MeasuredBounds.Max = ImMax(state->CurrentLayoutItem->MeasuredBounds.Max, max);
|
|
|
|
IM_UNUSED(min);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// [SECTION] Stack Layout: Public API
|
|
//-----------------------------------------------------------------------------
|
|
|
|
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*/)
|
|
{
|
|
ImGuiLayoutWindowState* window_state = GetCurrentWindowLayoutState();
|
|
IM_ASSERT(window_state->CurrentLayout);
|
|
|
|
AddLayoutSpring(*window_state->CurrentLayout, weight, spacing);
|
|
}
|
|
|
|
void ImGui::SuspendLayout()
|
|
{
|
|
PushLayout(NULL);
|
|
}
|
|
|
|
void ImGui::ResumeLayout()
|
|
{
|
|
ImGuiLayoutWindowState* window_state = GetCurrentWindowLayoutState();
|
|
IM_ASSERT(!window_state->CurrentLayout);
|
|
IM_ASSERT(!window_state->LayoutStack.empty());
|
|
IM_UNUSED(window_state);
|
|
PopLayout(NULL);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#endif // #ifndef IMGUI_DISABLE
|