ImGui window management and rendering code, part of a vendored immediate-mode GUI backend. It creates, finds and configures windows and child windows, computes sizing/placement constraints, handles resizing, popups, title bar, scrollbars, collapse/close buttons and Begin/End lifecycle.
// Vendored from zeljkovranjes/sbox-imgui; see LICENSE and PROVENANCE.md in this folder.
using Sandbox;
#pragma warning disable CS0649 // Upstream reserves fields for optional widgets and features.
namespace SboxSuperuser.Engine.Vendor.ImGui;
internal static partial class ImGui
{
private const float FLT_MAX = float.MaxValue;
private const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f;
private static float WindowsHoverPadding => MathF.Max( G.Style.TouchExtraPadding.x, G.Style.WindowBorderHoverPadding );
internal static float Axis( Vector2 v, int axis ) => axis == 0 ? v.x : v.y;
internal static Vector2 WithAxis( Vector2 v, int axis, float value ) => axis == 0 ? new Vector2( value, v.y ) : new Vector2( v.x, value );
private struct ResizeGripDef
{
public Vector2 CornerPosN;
public Vector2 InnerDir;
public int AngleMin12, AngleMax12;
}
private static readonly ResizeGripDef[] ResizeGripDefs =
{
new() { CornerPosN = new Vector2( 1, 1 ), InnerDir = new Vector2( -1, -1 ), AngleMin12 = 0, AngleMax12 = 3 }, // Lower-right
new() { CornerPosN = new Vector2( 0, 1 ), InnerDir = new Vector2( +1, -1 ), AngleMin12 = 3, AngleMax12 = 6 }, // Lower-left
};
private struct ResizeBorderDef
{
public Vector2 SegmentN1, SegmentN2;
}
private static readonly ResizeBorderDef[] ResizeBorderDefs =
{
new() { SegmentN1 = new Vector2( 0, 1 ), SegmentN2 = new Vector2( 0, 0 ) }, // Left
new() { SegmentN1 = new Vector2( 1, 0 ), SegmentN2 = new Vector2( 1, 1 ) }, // Right
new() { SegmentN1 = new Vector2( 0, 0 ), SegmentN2 = new Vector2( 1, 0 ) }, // Up
new() { SegmentN1 = new Vector2( 1, 1 ), SegmentN2 = new Vector2( 0, 1 ) }, // Down
};
#region Window lookup / creation
internal static ImGuiWindow FindWindowByID( int id )
{
G.WindowsById.TryGetValue( id, out var window );
return window;
}
internal static ImGuiWindow FindWindowByName( string name ) => FindWindowByID( ImHashStr( name, 0 ) );
private static ImGuiWindow CreateNewWindow( string name, ImGuiWindowFlags flags )
{
var g = G;
var window = new ImGuiWindow( g, name ) { Flags = flags };
g.WindowsById[window.ID] = window;
window.Pos = ImTrunc( new Vector2( 60, 60 ) * g.AppliedStyleScale );
if ( (flags & ImGuiWindowFlags.NoSavedSettings) == 0 && g.SettingsWindows.TryGetValue( window.ID, out var settings ) )
{
window.Pos = settings.Pos;
window.Size = window.SizeFull = settings.Size;
window.Collapsed = settings.Collapsed;
SetWindowConditionAllowFlags( window, ImGuiCond.FirstUseEver, false );
}
if ( (flags & ImGuiWindowFlags.AlwaysAutoResize) != 0 )
{
window.AutoFitFramesX = window.AutoFitFramesY = 2;
window.AutoFitOnlyGrows = false;
}
else
{
if ( window.Size.x <= 0.0f ) window.AutoFitFramesX = 2;
if ( window.Size.y <= 0.0f ) window.AutoFitFramesY = 2;
window.AutoFitOnlyGrows = window.AutoFitFramesX > 0 || window.AutoFitFramesY > 0;
}
if ( (flags & ImGuiWindowFlags.ChildWindow) == 0 )
{
g.WindowsFocusOrder.Add( window );
window.FocusOrder = g.WindowsFocusOrder.Count - 1;
}
if ( (flags & ImGuiWindowFlags.NoBringToFrontOnFocus) != 0 )
g.Windows.Insert( 0, window );
else
g.Windows.Add( window );
return window;
}
private static void SetWindowConditionAllowFlags( ImGuiWindow window, ImGuiCond flags, bool enabled )
{
if ( enabled )
{
window.SetWindowPosAllowFlags |= flags;
window.SetWindowSizeAllowFlags |= flags;
window.SetWindowCollapsedAllowFlags |= flags;
}
else
{
window.SetWindowPosAllowFlags &= ~flags;
window.SetWindowSizeAllowFlags &= ~flags;
window.SetWindowCollapsedAllowFlags &= ~flags;
}
}
private static void UpdateWindowParentAndRootLinks( ImGuiWindow window, ImGuiWindowFlags flags, ImGuiWindow parentWindow )
{
window.ParentWindow = parentWindow;
window.RootWindow = window.RootWindowPopupTree = window.RootWindowForTitleBarHighlight = window;
if ( parentWindow is not null && (flags & ImGuiWindowFlags.ChildWindow) != 0 && (flags & ImGuiWindowFlags.Tooltip) == 0 )
window.RootWindow = parentWindow.RootWindow;
if ( parentWindow is not null && (flags & ImGuiWindowFlags.Popup) != 0 )
window.RootWindowPopupTree = parentWindow.RootWindowPopupTree;
if ( parentWindow is not null && (flags & ImGuiWindowFlags.Modal) == 0 && (flags & (ImGuiWindowFlags.ChildWindow | ImGuiWindowFlags.Popup)) != 0 )
window.RootWindowForTitleBarHighlight = parentWindow.RootWindowForTitleBarHighlight;
}
#endregion
#region Window sizing
private static void CalcWindowContentSizes( ImGuiWindow window, out Vector2 contentSizeCurrent, out Vector2 contentSizeIdeal )
{
if ( window.Collapsed && window.AutoFitFramesX <= 0 && window.AutoFitFramesY <= 0 )
{
contentSizeCurrent = window.ContentSize;
contentSizeIdeal = window.ContentSizeIdeal;
return;
}
if ( window.HiddenFramesCannotSkipItems == 0 && window.HiddenFramesCanSkipItems > 0 )
{
contentSizeCurrent = window.ContentSize;
contentSizeIdeal = window.ContentSizeIdeal;
return;
}
var dc = window.DC;
contentSizeCurrent = new Vector2(
window.ContentSizeExplicit.x != 0.0f ? window.ContentSizeExplicit.x : ImTrunc( dc.CursorMaxPos.x - dc.CursorStartPos.x ),
window.ContentSizeExplicit.y != 0.0f ? window.ContentSizeExplicit.y : ImTrunc( dc.CursorMaxPos.y - dc.CursorStartPos.y ) );
contentSizeIdeal = new Vector2(
window.ContentSizeExplicit.x != 0.0f ? window.ContentSizeExplicit.x : ImTrunc( MathF.Max( dc.CursorMaxPos.x, dc.IdealMaxPos.x ) - dc.CursorStartPos.x ),
window.ContentSizeExplicit.y != 0.0f ? window.ContentSizeExplicit.y : ImTrunc( MathF.Max( dc.CursorMaxPos.y, dc.IdealMaxPos.y ) - dc.CursorStartPos.y ) );
}
private static Vector2 CalcWindowSizeAfterConstraint( ImGuiWindow window, Vector2 sizeDesired )
{
var g = G;
var newSize = sizeDesired;
if ( (g.NextWindowData.Flags & ImGuiNextWindowDataFlags.HasSizeConstraint) != 0 )
{
var cr = g.NextWindowData.SizeConstraintRect;
newSize.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp( newSize.x, cr.Min.x, cr.Max.x ) : window.SizeFull.x;
newSize.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp( newSize.y, cr.Min.y, cr.Max.y ) : window.SizeFull.y;
if ( g.NextWindowData.SizeCallback is not null )
{
var data = new ImGuiSizeCallbackData { Pos = window.Pos, CurrentSize = window.SizeFull, DesiredSize = newSize };
g.NextWindowData.SizeCallback( data );
newSize = data.DesiredSize;
}
newSize = ImTrunc( newSize );
}
if ( (window.Flags & (ImGuiWindowFlags.ChildWindow | ImGuiWindowFlags.AlwaysAutoResize)) == 0 )
{
newSize = ImMax( newSize, g.Style.WindowMinSize );
float minimumHeight = window.TitleBarHeight + window.MenuBarHeight + MathF.Max( 0.0f, g.Style.WindowRounding - 1.0f );
newSize.y = MathF.Max( newSize.y, minimumHeight );
}
return newSize;
}
private static Vector2 CalcWindowAutoFitSize( ImGuiWindow window, Vector2 sizeContents )
{
var g = G;
var style = g.Style;
var sizeDecorations = new Vector2( window.DecoOuterSizeX1 + window.DecoOuterSizeX2, window.DecoOuterSizeY1 + window.DecoOuterSizeY2 );
var sizePad = window.WindowPadding * 2.0f;
var sizeDesired = sizeContents + sizePad + sizeDecorations;
if ( (window.Flags & ImGuiWindowFlags.Tooltip) != 0 )
return sizeDesired;
var sizeMin = style.WindowMinSize;
if ( (window.Flags & (ImGuiWindowFlags.Popup | ImGuiWindowFlags.ChildWindow)) != 0 )
sizeMin = ImMin( sizeMin, new Vector2( 4.0f, 4.0f ) );
var availSize = g.IO.DisplaySize;
var sizeAutoFit = ImClamp( sizeDesired, sizeMin, ImMax( sizeMin, availSize - style.DisplaySafeAreaPadding * 2.0f ) );
var sizeAutoFitAfterConstraint = CalcWindowSizeAfterConstraint( window, sizeAutoFit );
bool willHaveScrollbarX = (sizeAutoFitAfterConstraint.x - sizePad.x - sizeDecorations.x < sizeContents.x && (window.Flags & ImGuiWindowFlags.NoScrollbar) == 0 && (window.Flags & ImGuiWindowFlags.HorizontalScrollbar) != 0) || (window.Flags & ImGuiWindowFlags.AlwaysHorizontalScrollbar) != 0;
bool willHaveScrollbarY = (sizeAutoFitAfterConstraint.y - sizePad.y - sizeDecorations.y < sizeContents.y && (window.Flags & ImGuiWindowFlags.NoScrollbar) == 0) || (window.Flags & ImGuiWindowFlags.AlwaysVerticalScrollbar) != 0;
if ( willHaveScrollbarX ) sizeAutoFit.y += style.ScrollbarSize;
if ( willHaveScrollbarY ) sizeAutoFit.x += style.ScrollbarSize;
return sizeAutoFit;
}
internal static Vector2 CalcWindowNextAutoFitSize( ImGuiWindow window )
{
CalcWindowContentSizes( window, out _, out var sizeContentsIdeal );
var sizeAutoFit = CalcWindowAutoFitSize( window, sizeContentsIdeal );
return CalcWindowSizeAfterConstraint( window, sizeAutoFit );
}
private static void ClampWindowPos( ImGuiWindow window, ImRect visibilityRect )
{
var g = G;
var sizeForClamping = window.Size;
if ( g.IO.ConfigWindowsMoveFromTitleBarOnly && (window.Flags & ImGuiWindowFlags.NoTitleBar) == 0 )
sizeForClamping.y = GetFrameHeight();
window.Pos = ImClamp( window.Pos, visibilityRect.Min - sizeForClamping, visibilityRect.Max );
}
internal static void SetWindowPos( ImGuiWindow window, Vector2 pos, ImGuiCond cond = ImGuiCond.None )
{
if ( cond != ImGuiCond.None && (window.SetWindowPosAllowFlags & cond) == 0 )
return;
window.SetWindowPosAllowFlags &= ~(ImGuiCond.Once | ImGuiCond.FirstUseEver | ImGuiCond.Appearing);
window.SetWindowPosVal = new Vector2( FLT_MAX, FLT_MAX );
var oldPos = window.Pos;
window.Pos = ImTrunc( pos );
var offset = window.Pos - oldPos;
if ( offset.x == 0f && offset.y == 0f )
return;
window.DC.CursorPos += offset;
window.DC.CursorMaxPos += offset;
window.DC.IdealMaxPos += offset;
window.DC.CursorStartPos += offset;
}
internal static void SetWindowSize( ImGuiWindow window, Vector2 size, ImGuiCond cond = ImGuiCond.None )
{
if ( cond != ImGuiCond.None && (window.SetWindowSizeAllowFlags & cond) == 0 )
return;
window.SetWindowSizeAllowFlags &= ~(ImGuiCond.Once | ImGuiCond.FirstUseEver | ImGuiCond.Appearing);
bool canAutoFit = (window.Flags & ImGuiWindowFlags.ChildWindow) == 0 || window.Appearing || (window.ChildFlags & ImGuiChildFlags.AlwaysAutoResize) != 0;
if ( canAutoFit )
{
window.AutoFitFramesX = size.x <= 0.0f ? 2 : 0;
window.AutoFitFramesY = size.y <= 0.0f ? 2 : 0;
}
if ( size.x <= 0.0f )
window.AutoFitOnlyGrows = false;
else
window.SizeFull.x = ImTrunc( size.x );
if ( size.y <= 0.0f )
window.AutoFitOnlyGrows = false;
else
window.SizeFull.y = ImTrunc( size.y );
}
internal static void SetWindowCollapsed( ImGuiWindow window, bool collapsed, ImGuiCond cond = ImGuiCond.None )
{
if ( cond != ImGuiCond.None && (window.SetWindowCollapsedAllowFlags & cond) == 0 )
return;
window.SetWindowCollapsedAllowFlags &= ~(ImGuiCond.Once | ImGuiCond.FirstUseEver | ImGuiCond.Appearing);
window.Collapsed = collapsed;
}
private static ImRect GetResizeBorderRect( ImGuiWindow window, int borderN, float perpPadding, float thickness )
{
var rect = window.Rect();
if ( thickness == 0.0f )
rect.Max -= Vector2.One;
return borderN switch
{
0 => new ImRect( rect.Min.x - thickness, rect.Min.y + perpPadding, rect.Min.x + thickness, rect.Max.y - perpPadding ),
1 => new ImRect( rect.Max.x - thickness, rect.Min.y + perpPadding, rect.Max.x + thickness, rect.Max.y - perpPadding ),
2 => new ImRect( rect.Min.x + perpPadding, rect.Min.y - thickness, rect.Max.x - perpPadding, rect.Min.y + thickness ),
_ => new ImRect( rect.Min.x + perpPadding, rect.Max.y - thickness, rect.Max.x - perpPadding, rect.Max.y + thickness ),
};
}
private static void CalcResizePosSizeFromAnyCorner( ImGuiWindow window, Vector2 cornerTarget, Vector2 cornerNorm, out Vector2 outPos, out Vector2 outSize )
{
var posMin = ImLerp( cornerTarget, window.Pos, cornerNorm );
var posMax = ImLerp( window.Pos + window.Size, cornerTarget, cornerNorm );
var sizeExpected = posMax - posMin;
var sizeConstrained = CalcWindowSizeAfterConstraint( window, sizeExpected );
outPos = posMin;
if ( cornerNorm.x == 0.0f ) outPos.x -= sizeConstrained.x - sizeExpected.x;
if ( cornerNorm.y == 0.0f ) outPos.y -= sizeConstrained.y - sizeExpected.y;
outSize = sizeConstrained;
}
private static bool UpdateWindowManualResize( ImGuiWindow window, Vector2 sizeAutoFit, ref int borderHovered, ref int borderHeld, int resizeGripCount, Color32[] resizeGripCol, ImRect visibilityRect )
{
var g = G;
var flags = window.Flags;
if ( (flags & ImGuiWindowFlags.NoResize) != 0 || (flags & ImGuiWindowFlags.AlwaysAutoResize) != 0 || window.AutoFitFramesX > 0 || window.AutoFitFramesY > 0 )
return false;
if ( !window.WasActive )
return false;
bool retAutoFit = false;
int resizeBorderCount = g.IO.ConfigWindowsResizeFromEdges ? 4 : 0;
float gripDrawSize = ImTrunc( MathF.Max( g.FontSize * 1.35f, window.WindowRounding + 1.0f + g.FontSize * 0.2f ) );
float gripHoverInnerSize = ImTrunc( gripDrawSize * 0.75f );
float gripHoverOuterSize = g.IO.ConfigWindowsResizeFromEdges ? WindowsHoverPadding : 0.0f;
var posTarget = new Vector2( FLT_MAX, FLT_MAX );
var sizeTarget = new Vector2( FLT_MAX, FLT_MAX );
window.IDStack.Add( window.GetID( "#RESIZE" ) );
for ( int resizeGripN = 0; resizeGripN < resizeGripCount; resizeGripN++ )
{
var def = ResizeGripDefs[resizeGripN];
var corner = ImLerp( window.Pos, window.Pos + window.Size, def.CornerPosN );
var resizeRect = new ImRect( corner - def.InnerDir * gripHoverOuterSize, corner + def.InnerDir * gripHoverInnerSize );
if ( resizeRect.Min.x > resizeRect.Max.x ) (resizeRect.Min.x, resizeRect.Max.x) = (resizeRect.Max.x, resizeRect.Min.x);
if ( resizeRect.Min.y > resizeRect.Max.y ) (resizeRect.Min.y, resizeRect.Max.y) = (resizeRect.Max.y, resizeRect.Min.y);
int resizeGripId = window.GetID( resizeGripN );
ItemAdd( resizeRect, resizeGripId, null, ImGuiItemFlags.NoNav );
ButtonBehavior( resizeRect, resizeGripId, out bool hovered, out bool held, ImGuiButtonFlags.FlattenChildren | ImGuiButtonFlags.NoNavFocus );
if ( hovered || held )
g.MouseCursor = (resizeGripN & 1) != 0 ? ImGuiMouseCursor.ResizeNESW : ImGuiMouseCursor.ResizeNWSE;
if ( held && g.IO.MouseClickedCount[0] == 2 && resizeGripN == 0 )
{
sizeTarget = CalcWindowSizeAfterConstraint( window, sizeAutoFit );
retAutoFit = true;
ClearActiveID();
}
else if ( held )
{
var clampMin = new Vector2( def.CornerPosN.x == 1.0f ? visibilityRect.Min.x : -FLT_MAX, def.CornerPosN.y == 1.0f ? visibilityRect.Min.y : -FLT_MAX );
var clampMax = new Vector2( def.CornerPosN.x == 0.0f ? visibilityRect.Max.x : FLT_MAX, def.CornerPosN.y == 0.0f ? visibilityRect.Max.y : FLT_MAX );
var cornerTarget = g.IO.MousePos - g.ActiveIdClickOffset + ImLerp( def.InnerDir * gripHoverOuterSize, def.InnerDir * -gripHoverInnerSize, def.CornerPosN );
cornerTarget = new Vector2( Math.Clamp( cornerTarget.x, clampMin.x, clampMax.x ), Math.Clamp( cornerTarget.y, clampMin.y, clampMax.y ) );
CalcResizePosSizeFromAnyCorner( window, cornerTarget, def.CornerPosN, out posTarget, out sizeTarget );
}
if ( resizeGripN == 0 || held || hovered )
resizeGripCol[resizeGripN] = GetColorU32Internal( held ? ImGuiCol.ResizeGripActive : hovered ? ImGuiCol.ResizeGripHovered : ImGuiCol.ResizeGrip );
}
for ( int borderN = 0; borderN < resizeBorderCount; borderN++ )
{
var def = ResizeBorderDefs[borderN];
int axis = borderN == 0 || borderN == 1 ? 0 : 1;
var borderRect = GetResizeBorderRect( window, borderN, gripHoverInnerSize, WindowsHoverPadding );
int borderId = window.GetID( borderN + 4 );
ItemAdd( borderRect, borderId, null, ImGuiItemFlags.NoNav );
ButtonBehavior( borderRect, borderId, out bool hovered, out bool held, ImGuiButtonFlags.FlattenChildren | ImGuiButtonFlags.NoNavFocus );
if ( hovered && g.HoveredIdTimer <= WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER )
hovered = false;
if ( hovered || held )
g.MouseCursor = axis == 0 ? ImGuiMouseCursor.ResizeEW : ImGuiMouseCursor.ResizeNS;
if ( held )
{
var clampMin = new Vector2( borderN == 1 ? visibilityRect.Min.x : -FLT_MAX, borderN == 3 ? visibilityRect.Min.y : -FLT_MAX );
var clampMax = new Vector2( borderN == 0 ? visibilityRect.Max.x : FLT_MAX, borderN == 2 ? visibilityRect.Max.y : FLT_MAX );
var borderTarget = WithAxis( window.Pos, axis, Axis( g.IO.MousePos, axis ) - Axis( g.ActiveIdClickOffset, axis ) + WindowsHoverPadding );
borderTarget = new Vector2( Math.Clamp( borderTarget.x, clampMin.x, clampMax.x ), Math.Clamp( borderTarget.y, clampMin.y, clampMax.y ) );
CalcResizePosSizeFromAnyCorner( window, borderTarget, ImMin( def.SegmentN1, def.SegmentN2 ), out posTarget, out sizeTarget );
}
if ( hovered ) borderHovered = borderN;
if ( held ) borderHeld = borderN;
}
window.IDStack.RemoveAt( window.IDStack.Count - 1 );
if ( sizeTarget.x != FLT_MAX )
{
window.SizeFull = sizeTarget;
MarkIniSettingsDirty( window );
}
if ( posTarget.x != FLT_MAX )
{
window.Pos = ImTrunc( posTarget );
MarkIniSettingsDirty( window );
}
window.Size = window.SizeFull;
return retAutoFit;
}
internal static void MarkIniSettingsDirty( ImGuiWindow window )
{
if ( (window.Flags & (ImGuiWindowFlags.NoSavedSettings | ImGuiWindowFlags.ChildWindow | ImGuiWindowFlags.Popup | ImGuiWindowFlags.Tooltip)) != 0 )
return;
G.SettingsWindows[window.ID] = new ImGuiContext.WindowSettings { Pos = window.Pos, Size = window.SizeFull, Collapsed = window.Collapsed };
}
#endregion
#region Popup positioning
internal enum ImGuiPopupPositionPolicy
{
Default,
ComboBox,
Tooltip,
}
internal static ImRect GetPopupAllowedExtentRect()
{
var g = G;
var r = new ImRect( Vector2.Zero, g.IO.DisplaySize );
var padding = g.Style.DisplaySafeAreaPadding;
r.Expand( new Vector2( r.Width > padding.x * 2 ? -padding.x : 0.0f, r.Height > padding.y * 2 ? -padding.y : 0.0f ) );
return r;
}
internal static Vector2 FindBestWindowPosForPopup( ImGuiWindow window )
{
var g = G;
var rOuter = GetPopupAllowedExtentRect();
if ( (window.Flags & ImGuiWindowFlags.ChildMenu) != 0 )
{
var parentWindow = window.ParentWindow;
float horizontalOverlap = g.Style.ItemInnerSpacing.x;
ImRect rAvoid;
if ( parentWindow.DC.MenuBarAppending > 0 )
rAvoid = new ImRect( -FLT_MAX, parentWindow.ClipRect.Min.y, FLT_MAX, parentWindow.ClipRect.Max.y );
else
rAvoid = new ImRect( parentWindow.Pos.x + horizontalOverlap, -FLT_MAX, parentWindow.Pos.x + parentWindow.Size.x - horizontalOverlap - parentWindow.ScrollbarSizes.x, FLT_MAX );
return FindBestWindowPosForPopupEx( window.Pos, window.Size, ref window.AutoPosLastDirection, rOuter, rAvoid, ImGuiPopupPositionPolicy.Default );
}
if ( (window.Flags & ImGuiWindowFlags.Popup) != 0 )
return FindBestWindowPosForPopupEx( window.Pos, window.Size, ref window.AutoPosLastDirection, rOuter, new ImRect( window.Pos, window.Pos ), ImGuiPopupPositionPolicy.Default );
if ( (window.Flags & ImGuiWindowFlags.Tooltip) != 0 )
{
float sc = g.Style.MouseCursorScale;
var refPos = g.IO.MousePos;
var rAvoid = new ImRect( refPos.x - 16, refPos.y - 8, refPos.x + 24 * sc, refPos.y + 24 * sc );
return FindBestWindowPosForPopupEx( refPos, window.Size, ref window.AutoPosLastDirection, rOuter, rAvoid, ImGuiPopupPositionPolicy.Tooltip );
}
return window.Pos;
}
internal static Vector2 FindBestWindowPosForPopupEx( Vector2 refPos, Vector2 size, ref ImGuiDir lastDir, ImRect rOuter, ImRect rAvoid, ImGuiPopupPositionPolicy policy )
{
var basePosClamped = ImClamp( refPos, rOuter.Min, rOuter.Max - size );
if ( policy == ImGuiPopupPositionPolicy.ComboBox )
{
ImGuiDir[] dirPreferedOrder = { ImGuiDir.Down, ImGuiDir.Right, ImGuiDir.Left, ImGuiDir.Up };
for ( int n = lastDir != ImGuiDir.None ? -1 : 0; n < 4; n++ )
{
var dir = n == -1 ? lastDir : dirPreferedOrder[n];
if ( n != -1 && dir == lastDir )
continue;
Vector2 pos = default;
if ( dir == ImGuiDir.Down ) pos = new Vector2( rAvoid.Min.x, rAvoid.Max.y );
if ( dir == ImGuiDir.Right ) pos = new Vector2( rAvoid.Min.x, rAvoid.Min.y - size.y );
if ( dir == ImGuiDir.Left ) pos = new Vector2( rAvoid.Max.x - size.x, rAvoid.Max.y );
if ( dir == ImGuiDir.Up ) pos = new Vector2( rAvoid.Max.x - size.x, rAvoid.Min.y - size.y );
if ( !rOuter.Contains( new ImRect( pos, pos + size ) ) )
continue;
lastDir = dir;
return pos;
}
}
if ( policy == ImGuiPopupPositionPolicy.Tooltip || policy == ImGuiPopupPositionPolicy.Default )
{
ImGuiDir[] dirPreferedOrder = { ImGuiDir.Right, ImGuiDir.Down, ImGuiDir.Up, ImGuiDir.Left };
for ( int n = lastDir != ImGuiDir.None ? -1 : 0; n < 4; n++ )
{
var dir = n == -1 ? lastDir : dirPreferedOrder[n];
if ( n != -1 && dir == lastDir )
continue;
float availW = (dir == ImGuiDir.Left ? rAvoid.Min.x : rOuter.Max.x) - (dir == ImGuiDir.Right ? rAvoid.Max.x : rOuter.Min.x);
float availH = (dir == ImGuiDir.Up ? rAvoid.Min.y : rOuter.Max.y) - (dir == ImGuiDir.Down ? rAvoid.Max.y : rOuter.Min.y);
if ( availW < size.x && (dir == ImGuiDir.Left || dir == ImGuiDir.Right) )
continue;
if ( availH < size.y && (dir == ImGuiDir.Up || dir == ImGuiDir.Down) )
continue;
var pos = new Vector2(
dir == ImGuiDir.Left ? rAvoid.Min.x - size.x : (dir == ImGuiDir.Right ? rAvoid.Max.x : basePosClamped.x),
dir == ImGuiDir.Up ? rAvoid.Min.y - size.y : (dir == ImGuiDir.Down ? rAvoid.Max.y : basePosClamped.y) );
pos.x = MathF.Max( pos.x, rOuter.Min.x );
pos.y = MathF.Max( pos.y, rOuter.Min.y );
lastDir = dir;
return pos;
}
}
lastDir = ImGuiDir.None;
if ( policy == ImGuiPopupPositionPolicy.Tooltip )
return refPos + new Vector2( 2, 2 );
var fallback = refPos;
fallback.x = MathF.Max( MathF.Min( fallback.x + size.x, rOuter.Max.x ) - size.x, rOuter.Min.x );
fallback.y = MathF.Max( MathF.Min( fallback.y + size.y, rOuter.Max.y ) - size.y, rOuter.Min.y );
return fallback;
}
#endregion
#region Begin / End
/// <summary>
/// Push a window to the stack and start appending to it. Always call End() even if this returns false
/// (false means the window is collapsed or fully clipped, and you can skip submitting contents).
/// </summary>
public static bool Begin( string name, ImGuiWindowFlags flags = ImGuiWindowFlags.None )
{
bool dummy = true;
return BeginImpl( name, false, ref dummy, flags );
}
/// <summary>
/// Begin a window with a close button. When the close button is clicked, <paramref name="open"/> is set to false.
/// </summary>
public static bool Begin( string name, ref bool open, ImGuiWindowFlags flags = ImGuiWindowFlags.None )
{
return BeginImpl( name, true, ref open, flags );
}
internal static bool BeginImpl( string name, bool hasCloseButton, ref bool pOpen, ImGuiWindowFlags flags )
{
var g = G;
var style = g.Style;
if ( string.IsNullOrEmpty( name ) )
throw new ArgumentException( "Window name cannot be empty", nameof( name ) );
if ( !g.WithinFrameScope )
throw new InvalidOperationException( "ImGui.Begin() called outside of a frame. Call ImGui functions from OnUpdate." );
var window = FindWindowByName( name );
bool windowJustCreated = window is null;
if ( windowJustCreated )
window = CreateNewWindow( name, flags );
if ( (flags & ImGuiWindowFlags.NoInputs) == ImGuiWindowFlags.NoInputs )
flags |= ImGuiWindowFlags.NoMove | ImGuiWindowFlags.NoResize;
int currentFrame = g.FrameCount;
bool firstBeginOfTheFrame = window.LastFrameActive != currentFrame;
window.IsFallbackWindow = g.CurrentWindowStack.Count == 0 && g.WithinFrameScopeWithImplicitWindow;
bool windowJustActivatedByUser = window.LastFrameActive < currentFrame - 1;
if ( (flags & ImGuiWindowFlags.Popup) != 0 )
{
var popupRef0 = g.OpenPopupStack[g.BeginPopupStack.Count];
windowJustActivatedByUser |= window.PopupId != popupRef0.PopupId;
windowJustActivatedByUser |= window != popupRef0.Window;
}
if ( firstBeginOfTheFrame )
{
window.Flags = flags;
window.ChildFlags = (g.NextWindowData.Flags & ImGuiNextWindowDataFlags.HasChildFlags) != 0 ? g.NextWindowData.ChildFlags : ImGuiChildFlags.None;
window.LastFrameActive = currentFrame;
window.LastTimeActive = (float)g.Time;
window.BeginOrderWithinParent = 0;
window.BeginOrderWithinContext = g.WindowsActiveCount++;
}
else
{
flags = window.Flags;
}
var parentWindowInStack = g.CurrentWindowStack.Count > 0 ? g.CurrentWindowStack[^1].Window : null;
var parentWindow = firstBeginOfTheFrame
? ((flags & (ImGuiWindowFlags.ChildWindow | ImGuiWindowFlags.Popup)) != 0 ? parentWindowInStack : null)
: window.ParentWindow;
window.Appearing = windowJustActivatedByUser;
if ( window.Appearing )
SetWindowConditionAllowFlags( window, ImGuiCond.Appearing, true );
// Add to stack
g.CurrentWindow = window;
g.CurrentWindowStack.Add( new ImGuiWindowStackData
{
Window = window,
ParentLastItemDataBackup = g.LastItemData,
StackSizesColorStack = g.ColorStack.Count,
StackSizesStyleVarStack = g.StyleVarStack.Count,
StackSizesItemFlagsStack = g.ItemFlagsStack.Count,
StackSizesGroupStack = g.GroupStack.Count,
StackSizesBeginPopupStack = g.BeginPopupStack.Count,
StackSizesDisabledStack = g.DisabledStackSize,
BackupItemFlags = g.CurrentItemFlags,
} );
if ( (flags & ImGuiWindowFlags.ChildMenu) != 0 )
g.BeginMenuDepth++;
if ( (flags & ImGuiWindowFlags.Popup) != 0 )
{
var popupRef = g.OpenPopupStack[g.BeginPopupStack.Count];
popupRef.Window = window;
g.BeginPopupStack.Add( popupRef );
window.PopupId = popupRef.PopupId;
}
// Process SetNextWindow***() calls
bool windowPosSetByApi = false;
bool windowSizeXSetByApi = false, windowSizeYSetByApi = false;
var nwd = g.NextWindowData;
if ( (nwd.Flags & ImGuiNextWindowDataFlags.HasPos) != 0 )
{
windowPosSetByApi = (window.SetWindowPosAllowFlags & nwd.PosCond) != 0;
if ( windowPosSetByApi && ImLengthSqr( nwd.PosPivotVal ) > 0.00001f )
{
window.SetWindowPosVal = nwd.PosVal;
window.SetWindowPosPivot = nwd.PosPivotVal;
window.SetWindowPosAllowFlags &= ~(ImGuiCond.Once | ImGuiCond.FirstUseEver | ImGuiCond.Appearing);
}
else
{
SetWindowPos( window, nwd.PosVal, nwd.PosCond );
}
}
if ( (nwd.Flags & ImGuiNextWindowDataFlags.HasSize) != 0 )
{
windowSizeXSetByApi = (window.SetWindowSizeAllowFlags & nwd.SizeCond) != 0 && nwd.SizeVal.x > 0.0f;
windowSizeYSetByApi = (window.SetWindowSizeAllowFlags & nwd.SizeCond) != 0 && nwd.SizeVal.y > 0.0f;
var sizeVal = nwd.SizeVal;
// Child windows resized by the user keep their user size.
if ( (window.ChildFlags & ImGuiChildFlags.ResizeX) != 0 && window.SizeFull.x > 0 && !windowJustCreated )
sizeVal.x = window.SizeFull.x;
if ( (window.ChildFlags & ImGuiChildFlags.ResizeY) != 0 && window.SizeFull.y > 0 && !windowJustCreated )
sizeVal.y = window.SizeFull.y;
SetWindowSize( window, sizeVal, nwd.SizeCond );
}
if ( (nwd.Flags & ImGuiNextWindowDataFlags.HasScroll) != 0 )
{
if ( nwd.ScrollVal.x >= 0.0f )
{
window.ScrollTarget.x = nwd.ScrollVal.x;
window.ScrollTargetCenterRatio.x = 0.0f;
}
if ( nwd.ScrollVal.y >= 0.0f )
{
window.ScrollTarget.y = nwd.ScrollVal.y;
window.ScrollTargetCenterRatio.y = 0.0f;
}
}
if ( (nwd.Flags & ImGuiNextWindowDataFlags.HasContentSize) != 0 )
window.ContentSizeExplicit = nwd.ContentSizeVal;
else if ( firstBeginOfTheFrame )
window.ContentSizeExplicit = Vector2.Zero;
if ( (nwd.Flags & ImGuiNextWindowDataFlags.HasCollapsed) != 0 )
SetWindowCollapsed( window, nwd.CollapsedVal, nwd.CollapsedCond );
if ( (nwd.Flags & ImGuiNextWindowDataFlags.HasFocus) != 0 )
FocusWindow( window );
if ( window.Appearing )
SetWindowConditionAllowFlags( window, ImGuiCond.Appearing, false );
if ( firstBeginOfTheFrame )
{
bool windowIsChildTooltip = (flags & ImGuiWindowFlags.ChildWindow) != 0 && (flags & ImGuiWindowFlags.Tooltip) != 0;
UpdateWindowParentAndRootLinks( window, flags, parentWindow );
window.ParentWindowInBeginStack = parentWindowInStack;
window.Active = true;
window.HasCloseButton = hasCloseButton;
window.ClipRect = new ImRect( -FLT_MAX, -FLT_MAX, FLT_MAX, FLT_MAX );
window.IDStack.Clear();
window.IDStack.Add( window.ID );
window.DrawList.ResetForNewFrame();
window.Name = name;
// UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS
CalcWindowContentSizes( window, out window.ContentSize, out window.ContentSizeIdeal );
if ( window.HiddenFramesCanSkipItems > 0 ) window.HiddenFramesCanSkipItems--;
if ( window.HiddenFramesCannotSkipItems > 0 ) window.HiddenFramesCannotSkipItems--;
if ( window.HiddenFramesForRenderOnly > 0 ) window.HiddenFramesForRenderOnly--;
if ( windowJustCreated && (!windowSizeXSetByApi || !windowSizeYSetByApi) )
window.HiddenFramesCannotSkipItems = 1;
if ( windowJustActivatedByUser && (flags & (ImGuiWindowFlags.Popup | ImGuiWindowFlags.Tooltip)) != 0 )
{
window.HiddenFramesCannotSkipItems = 1;
if ( (flags & ImGuiWindowFlags.AlwaysAutoResize) != 0 )
{
if ( !windowSizeXSetByApi ) { window.Size.x = window.SizeFull.x = 0f; }
if ( !windowSizeYSetByApi ) { window.Size.y = window.SizeFull.y = 0f; }
window.ContentSize = window.ContentSizeIdeal = Vector2.Zero;
}
}
// UPDATE DECORATION SIZES
window.WindowPadding = style.WindowPadding;
if ( (flags & ImGuiWindowFlags.ChildWindow) != 0 && (window.ChildFlags & (ImGuiChildFlags.AlwaysUseWindowPadding | ImGuiChildFlags.Borders | ImGuiChildFlags.FrameStyle)) == 0 && (flags & ImGuiWindowFlags.Popup) == 0 )
window.WindowPadding = new Vector2( 0.0f, (flags & ImGuiWindowFlags.MenuBar) != 0 ? style.WindowPadding.y : 0.0f );
window.DC.MenuBarOffset = new Vector2(
MathF.Max( MathF.Max( window.WindowPadding.x, style.ItemSpacing.x ), nwd.MenuBarOffsetMinVal.x ),
nwd.MenuBarOffsetMinVal.y );
window.TitleBarHeight = (flags & ImGuiWindowFlags.NoTitleBar) != 0 ? 0.0f : g.FontSize + style.FramePadding.y * 2.0f;
window.MenuBarHeight = (flags & ImGuiWindowFlags.MenuBar) != 0 ? window.DC.MenuBarOffset.y + g.FontSize + style.FramePadding.y * 2.0f : 0.0f;
// Collapse window by double-clicking on title bar
if ( (flags & ImGuiWindowFlags.NoTitleBar) == 0 && (flags & ImGuiWindowFlags.NoCollapse) == 0 )
{
var titleBarRect0 = window.TitleBarRect();
if ( g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && g.ActiveId == 0 && IsMouseHoveringRect( titleBarRect0.Min, titleBarRect0.Max ) && g.IO.MouseClicked[0] && g.IO.MouseClickedCount[0] == 2 )
window.WantCollapseToggle = true;
if ( window.WantCollapseToggle )
{
window.Collapsed = !window.Collapsed;
MarkIniSettingsDirty( window );
}
}
else
{
window.Collapsed = false;
}
window.WantCollapseToggle = false;
// SIZE
window.DecoOuterSizeX1 = 0.0f;
window.DecoOuterSizeX2 = 0.0f;
window.DecoOuterSizeY1 = window.TitleBarHeight + window.MenuBarHeight;
window.DecoOuterSizeY2 = 0.0f;
var scrollbarSizesFromLastFrame = window.ScrollbarSizes;
window.ScrollbarSizes = Vector2.Zero;
var sizeAutoFit = CalcWindowAutoFitSize( window, window.ContentSizeIdeal );
bool useCurrentSizeForScrollbarX = windowJustCreated;
bool useCurrentSizeForScrollbarY = windowJustCreated;
{
bool autoFitXAlways = !windowSizeXSetByApi && (flags & ImGuiWindowFlags.AlwaysAutoResize) != 0 && !window.Collapsed;
bool autoFitYAlways = !windowSizeYSetByApi && (flags & ImGuiWindowFlags.AlwaysAutoResize) != 0 && !window.Collapsed;
bool autoFitXCurrent = !windowSizeXSetByApi && window.AutoFitFramesX > 0;
bool autoFitYCurrent = !windowSizeYSetByApi && window.AutoFitFramesY > 0;
if ( autoFitXAlways || autoFitXCurrent )
{
window.SizeFull.x = window.AutoFitOnlyGrows && !autoFitXAlways ? MathF.Max( window.SizeFull.x, sizeAutoFit.x ) : sizeAutoFit.x;
useCurrentSizeForScrollbarX = true;
}
if ( autoFitYAlways || autoFitYCurrent )
{
window.SizeFull.y = window.AutoFitOnlyGrows && !autoFitYAlways ? MathF.Max( window.SizeFull.y, sizeAutoFit.y ) : sizeAutoFit.y;
useCurrentSizeForScrollbarY = true;
}
}
window.SizeFull = CalcWindowSizeAfterConstraint( window, window.SizeFull );
window.Size = window.Collapsed && (flags & ImGuiWindowFlags.ChildWindow) == 0 ? window.TitleBarRect().Size : window.SizeFull;
// POSITION
if ( windowJustActivatedByUser )
{
window.AutoPosLastDirection = ImGuiDir.None;
if ( (flags & ImGuiWindowFlags.Popup) != 0 && (flags & ImGuiWindowFlags.Modal) == 0 && !windowPosSetByApi )
window.Pos = g.BeginPopupStack[^1].OpenPopupPos;
}
if ( (flags & ImGuiWindowFlags.ChildWindow) != 0 )
{
parentWindow.DC.ChildWindows.Add( window );
window.BeginOrderWithinParent = parentWindow.DC.ChildWindows.Count - 1;
if ( (flags & ImGuiWindowFlags.Popup) == 0 && !windowPosSetByApi && !windowIsChildTooltip )
window.Pos = parentWindow.DC.CursorPos;
}
bool windowPosWithPivot = window.SetWindowPosVal.x != FLT_MAX && window.HiddenFramesCannotSkipItems == 0;
if ( windowPosWithPivot )
SetWindowPos( window, window.SetWindowPosVal - window.Size * window.SetWindowPosPivot );
else if ( (flags & ImGuiWindowFlags.ChildMenu) != 0 )
window.Pos = FindBestWindowPosForPopup( window );
else if ( (flags & ImGuiWindowFlags.Popup) != 0 && !windowPosSetByApi )
window.Pos = FindBestWindowPosForPopup( window );
else if ( (flags & ImGuiWindowFlags.Tooltip) != 0 && !windowPosSetByApi && !windowIsChildTooltip )
window.Pos = FindBestWindowPosForPopup( window );
// Clamp position so the window stays visible
var viewportRect = new ImRect( Vector2.Zero, g.IO.DisplaySize );
var visibilityPadding = ImMax( style.DisplayWindowPadding, style.DisplaySafeAreaPadding );
var visibilityRect = new ImRect( viewportRect.Min + visibilityPadding, viewportRect.Max - visibilityPadding );
if ( visibilityRect.Min.x >= visibilityRect.Max.x || visibilityRect.Min.y >= visibilityRect.Max.y )
visibilityRect = viewportRect;
if ( !windowPosSetByApi && (flags & ImGuiWindowFlags.ChildWindow) == 0 )
if ( g.IO.DisplaySize.x > 0.0f && g.IO.DisplaySize.y > 0.0f )
ClampWindowPos( window, visibilityRect );
window.Pos = ImTrunc( window.Pos );
// Lock window rounding/border for the frame
if ( (flags & ImGuiWindowFlags.ChildWindow) != 0 )
window.WindowRounding = (window.ChildFlags & ImGuiChildFlags.FrameStyle) != 0 ? style.FrameRounding : style.ChildRounding;
else
window.WindowRounding = (flags & ImGuiWindowFlags.Popup) != 0 && (flags & ImGuiWindowFlags.Modal) == 0 ? style.PopupRounding : style.WindowRounding;
if ( (flags & ImGuiWindowFlags.ChildWindow) != 0 )
window.WindowBorderSize = (window.ChildFlags & (ImGuiChildFlags.Borders | ImGuiChildFlags.FrameStyle)) != 0 ? style.ChildBorderSize : 0.0f;
else
window.WindowBorderSize = (flags & (ImGuiWindowFlags.Popup | ImGuiWindowFlags.Tooltip)) != 0 && (flags & ImGuiWindowFlags.Modal) == 0 ? style.PopupBorderSize : style.WindowBorderSize;
// Apply window focus (new and reactivated windows are moved to front)
bool wantFocus = false;
if ( windowJustActivatedByUser && (flags & ImGuiWindowFlags.NoFocusOnAppearing) == 0 )
{
if ( (flags & ImGuiWindowFlags.Popup) != 0 )
wantFocus = true;
else if ( (flags & (ImGuiWindowFlags.ChildWindow | ImGuiWindowFlags.Tooltip)) == 0 )
wantFocus = true;
}
// Handle manual resize
int borderHovered = -1, borderHeld = -1;
var resizeGripCol = new Color32[2];
int resizeGripCount = (flags & ImGuiWindowFlags.ChildWindow) != 0 ? 0 : (g.IO.ConfigWindowsResizeFromEdges ? 2 : 1);
float resizeGripDrawSize = ImTrunc( MathF.Max( g.FontSize * 1.10f, window.WindowRounding + 1.0f + g.FontSize * 0.2f ) );
if ( !window.Collapsed )
if ( UpdateWindowManualResize( window, sizeAutoFit, ref borderHovered, ref borderHeld, resizeGripCount, resizeGripCol, visibilityRect ) )
useCurrentSizeForScrollbarX = useCurrentSizeForScrollbarY = true;
window.ResizeBorderHovered = borderHovered;
window.ResizeBorderHeld = borderHeld;
if ( (flags & ImGuiWindowFlags.ChildWindow) != 0 && (window.ChildFlags & (ImGuiChildFlags.ResizeX | ImGuiChildFlags.ResizeY)) != 0 && !window.Collapsed )
UpdateChildWindowResize( window );
// SCROLLBAR VISIBILITY
if ( !window.Collapsed )
{
var availSizeFromCurrentFrame = new Vector2( window.SizeFull.x, window.SizeFull.y - (window.DecoOuterSizeY1 + window.DecoOuterSizeY2) );
var availSizeFromLastFrame = window.InnerRect.Size + scrollbarSizesFromLastFrame;
var neededSizeFromLastFrame = windowJustCreated ? Vector2.Zero : window.ContentSize + window.WindowPadding * 2.0f;
float sizeXForScrollbars = useCurrentSizeForScrollbarX ? availSizeFromCurrentFrame.x : availSizeFromLastFrame.x;
float sizeYForScrollbars = useCurrentSizeForScrollbarY ? availSizeFromCurrentFrame.y : availSizeFromLastFrame.y;
window.ScrollbarY = (flags & ImGuiWindowFlags.AlwaysVerticalScrollbar) != 0 || (neededSizeFromLastFrame.y > sizeYForScrollbars && (flags & ImGuiWindowFlags.NoScrollbar) == 0);
window.ScrollbarX = (flags & ImGuiWindowFlags.AlwaysHorizontalScrollbar) != 0 || (neededSizeFromLastFrame.x > sizeXForScrollbars - (window.ScrollbarY ? style.ScrollbarSize : 0.0f) && (flags & ImGuiWindowFlags.NoScrollbar) == 0 && (flags & ImGuiWindowFlags.HorizontalScrollbar) != 0);
if ( window.ScrollbarX && !window.ScrollbarY )
window.ScrollbarY = neededSizeFromLastFrame.y > sizeYForScrollbars - style.ScrollbarSize && (flags & ImGuiWindowFlags.NoScrollbar) == 0;
window.ScrollbarSizes = new Vector2( window.ScrollbarY ? style.ScrollbarSize : 0.0f, window.ScrollbarX ? style.ScrollbarSize : 0.0f );
window.DecoOuterSizeX2 = window.ScrollbarSizes.x;
window.DecoOuterSizeY2 = window.ScrollbarSizes.y;
}
// UPDATE RECTANGLES (1- THOSE NOT AFFECTED BY SCROLLING)
var outerRect = window.Rect();
var titleBarRect = window.TitleBarRect();
var hostRect = (flags & ImGuiWindowFlags.ChildWindow) != 0 && (flags & ImGuiWindowFlags.Popup) == 0 && !windowIsChildTooltip ? parentWindow.ClipRect : viewportRect;
window.OuterRectClipped = outerRect;
window.OuterRectClipped.ClipWith( hostRect );
window.InnerRect = new ImRect(
window.Pos.x + window.DecoOuterSizeX1,
window.Pos.y + window.DecoOuterSizeY1,
window.Pos.x + window.Size.x - window.DecoOuterSizeX2,
window.Pos.y + window.Size.y - window.DecoOuterSizeY2 );
float topBorderSize = (flags & ImGuiWindowFlags.MenuBar) != 0 || (flags & ImGuiWindowFlags.NoTitleBar) == 0 ? style.FrameBorderSize : window.WindowBorderSize;
window.InnerClipRect = new ImRect(
ImTrunc( 0.5f + window.InnerRect.Min.x + window.WindowBorderSize * 0.5f ),
ImTrunc( 0.5f + window.InnerRect.Min.y + topBorderSize * 0.5f ),
ImTrunc( window.InnerRect.Max.x - window.WindowBorderSize * 0.5f ),
ImTrunc( window.InnerRect.Max.y - window.WindowBorderSize * 0.5f ) );
window.InnerClipRect.ClipWithFull( hostRect );
// SCROLLING
window.ScrollMax = new Vector2(
MathF.Max( 0.0f, window.ContentSize.x + window.WindowPadding.x * 2.0f - window.InnerRect.Width ),
MathF.Max( 0.0f, window.ContentSize.y + window.WindowPadding.y * 2.0f - window.InnerRect.Height ) );
window.Scroll = CalcNextScrollFromScrollTargetAndClamp( window );
window.ScrollTarget = new Vector2( FLT_MAX, FLT_MAX );
window.DecoInnerSizeX1 = window.DecoInnerSizeY1 = 0.0f;
// DRAWING
window.DrawList.PushClipRect( hostRect.Min, hostRect.Max, false );
{
var windowToHighlight = g.NavWindow;
bool titleBarIsHighlight = wantFocus || (windowToHighlight is not null && window.RootWindowForTitleBarHighlight == windowToHighlight.RootWindowForTitleBarHighlight);
RenderWindowDecorations( window, titleBarRect, titleBarIsHighlight, resizeGripCount, resizeGripCol, resizeGripDrawSize );
}
// UPDATE RECTANGLES (2- THOSE AFFECTED BY SCROLLING)
bool allowScrollbarX = (flags & ImGuiWindowFlags.NoScrollbar) == 0 && (flags & ImGuiWindowFlags.HorizontalScrollbar) != 0;
bool allowScrollbarY = (flags & ImGuiWindowFlags.NoScrollbar) == 0;
float workRectSizeX = window.ContentSizeExplicit.x != 0.0f ? window.ContentSizeExplicit.x : MathF.Max( allowScrollbarX ? window.ContentSize.x : 0.0f, window.Size.x - window.WindowPadding.x * 2.0f - (window.DecoOuterSizeX1 + window.DecoOuterSizeX2) );
float workRectSizeY = window.ContentSizeExplicit.y != 0.0f ? window.ContentSizeExplicit.y : MathF.Max( allowScrollbarY ? window.ContentSize.y : 0.0f, window.Size.y - window.WindowPadding.y * 2.0f - (window.DecoOuterSizeY1 + window.DecoOuterSizeY2) );
window.WorkRect.Min = new Vector2(
ImTrunc( window.InnerRect.Min.x - window.Scroll.x + MathF.Max( window.WindowPadding.x, window.WindowBorderSize ) ),
ImTrunc( window.InnerRect.Min.y - window.Scroll.y + MathF.Max( window.WindowPadding.y, window.WindowBorderSize ) ) );
window.WorkRect.Max = window.WorkRect.Min + new Vector2( workRectSizeX, workRectSizeY );
window.ParentWorkRect = window.WorkRect;
window.ContentRegionRect.Min = new Vector2(
window.Pos.x - window.Scroll.x + window.WindowPadding.x + window.DecoOuterSizeX1,
window.Pos.y - window.Scroll.y + window.WindowPadding.y + window.DecoOuterSizeY1 );
window.ContentRegionRect.Max = window.ContentRegionRect.Min + new Vector2(
window.ContentSizeExplicit.x != 0.0f ? window.ContentSizeExplicit.x : (window.Size.x - window.WindowPadding.x * 2.0f - (window.DecoOuterSizeX1 + window.DecoOuterSizeX2)),
window.ContentSizeExplicit.y != 0.0f ? window.ContentSizeExplicit.y : (window.Size.y - window.WindowPadding.y * 2.0f - (window.DecoOuterSizeY1 + window.DecoOuterSizeY2)) );
// Setup drawing context
var dc = window.DC;
dc.Indent = window.DecoOuterSizeX1 + window.WindowPadding.x - window.Scroll.x;
dc.GroupOffset = 0.0f;
dc.ColumnsOffset = 0.0f;
dc.CursorStartPos = new Vector2(
window.Pos.x + window.WindowPadding.x - window.Scroll.x + window.DecoOuterSizeX1,
window.Pos.y + window.WindowPadding.y - window.Scroll.y + window.DecoOuterSizeY1 );
dc.CursorPos = dc.CursorStartPos;
dc.CursorPosPrevLine = dc.CursorPos;
dc.CursorMaxPos = dc.CursorStartPos;
dc.IdealMaxPos = dc.CursorStartPos;
dc.CurrLineSize = dc.PrevLineSize = Vector2.Zero;
dc.CurrLineTextBaseOffset = dc.PrevLineTextBaseOffset = 0.0f;
dc.IsSameLine = dc.IsSetPos = false;
dc.MenuBarAppending = 0;
dc.MenuColumns.Update( style.ItemSpacing.x, windowJustActivatedByUser );
dc.TreeDepth = 0;
dc.TreeHasStackDataDepthMask = 0;
dc.ChildWindows.Clear();
dc.StateStorage = window.StateStorage;
dc.CurrentColumns = null;
dc.CurrentTableIdx = -1;
dc.LayoutType = ImGuiLayoutType.Vertical;
dc.ParentLayoutType = parentWindow?.DC.LayoutType ?? ImGuiLayoutType.Vertical;
if ( window.Size.x > 0.0f && (flags & ImGuiWindowFlags.Tooltip) == 0 && (flags & ImGuiWindowFlags.AlwaysAutoResize) == 0 )
window.ItemWidthDefault = ImTrunc( window.Size.x * 0.65f );
else
window.ItemWidthDefault = ImTrunc( g.FontSize * 16.0f );
dc.ItemWidth = window.ItemWidthDefault;
dc.TextWrapPos = -1.0f;
dc.ItemWidthStack.Clear();
dc.TextWrapPosStack.Clear();
if ( window.AutoFitFramesX > 0 ) window.AutoFitFramesX--;
if ( window.AutoFitFramesY > 0 ) window.AutoFitFramesY--;
if ( wantFocus )
FocusWindow( window );
// Title bar
if ( (flags & ImGuiWindowFlags.NoTitleBar) == 0 )
RenderWindowTitleBarContents( window, new ImRect( titleBarRect.Min.x + window.WindowBorderSize, titleBarRect.Min.y, titleBarRect.Max.x - window.WindowBorderSize, titleBarRect.Max.y ), name, hasCloseButton, ref pOpen );
// Fill last item data with the title bar, so IsItemHovered()/IsItemActive() work right after Begin().
g.LastItemData.ID = window.MoveId;
g.LastItemData.InFlags = g.CurrentItemFlags;
g.LastItemData.StatusFlags = IsMouseHoveringRect( titleBarRect.Min, titleBarRect.Max, false ) ? ImGuiItemStatusFlags.HoveredRect : ImGuiItemStatusFlags.None;
g.LastItemData.Rect = titleBarRect;
}
// Clip contents to the inner area of the window
PushClipRect( window.InnerClipRect.Min, window.InnerClipRect.Max, true );
window.WriteAccessed = false;
window.BeginCount++;
g.NextWindowData.ClearFlags();
if ( firstBeginOfTheFrame )
{
if ( (flags & ImGuiWindowFlags.ChildWindow) != 0 && (flags & ImGuiWindowFlags.ChildMenu) == 0 )
{
if ( window.OuterRectClipped.Min.x >= window.OuterRectClipped.Max.x || window.OuterRectClipped.Min.y >= window.OuterRectClipped.Max.y )
window.HiddenFramesCanSkipItems = 1;
if ( parentWindow is not null && (parentWindow.Collapsed || parentWindow.HiddenFramesCanSkipItems > 0) )
window.HiddenFramesCanSkipItems = 1;
if ( parentWindow is not null && parentWindow.HiddenFramesCannotSkipItems > 0 )
window.HiddenFramesCannotSkipItems = 1;
}
if ( style.Alpha <= 0.0f )
window.HiddenFramesCanSkipItems = 1;
bool hiddenRegular = window.HiddenFramesCanSkipItems > 0 || window.HiddenFramesCannotSkipItems > 0;
window.Hidden = hiddenRegular || window.HiddenFramesForRenderOnly > 0;
bool skipItems = false;
if ( window.Collapsed || !window.Active || hiddenRegular )
if ( window.AutoFitFramesX <= 0 && window.AutoFitFramesY <= 0 && window.HiddenFramesCannotSkipItems <= 0 )
skipItems = true;
window.SkipItems = skipItems;
}
return !window.SkipItems;
}
private static void UpdateChildWindowResize( ImGuiWindow window )
{
var g = G;
float handleSize = MathF.Max( 4.0f, g.Style.WindowBorderHoverPadding );
for ( int axis = 0; axis < 2; axis++ )
{
if ( axis == 0 && (window.ChildFlags & ImGuiChildFlags.ResizeX) == 0 ) continue;
if ( axis == 1 && (window.ChildFlags & ImGuiChildFlags.ResizeY) == 0 ) continue;
var r = window.Rect();
var bb = axis == 0
? new ImRect( r.Max.x - handleSize, r.Min.y, r.Max.x + handleSize, r.Max.y )
: new ImRect( r.Min.x, r.Max.y - handleSize, r.Max.x, r.Max.y + handleSize );
int id = window.GetID( axis == 0 ? "#CHILDRESIZEX" : "#CHILDRESIZEY" );
var backupWindow = g.CurrentWindow;
g.CurrentWindow = window.ParentWindow;
ItemAdd( bb, id, null, ImGuiItemFlags.NoNav );
ButtonBehavior( bb, id, out bool hovered, out bool held, ImGuiButtonFlags.FlattenChildren | ImGuiButtonFlags.NoNavFocus );
g.CurrentWindow = backupWindow;
if ( hovered || held )
g.MouseCursor = axis == 0 ? ImGuiMouseCursor.ResizeEW : ImGuiMouseCursor.ResizeNS;
if ( held )
{
float target = Axis( g.IO.MousePos, axis ) - Axis( g.ActiveIdClickOffset, axis ) + handleSize - Axis( window.Pos, axis );
target = MathF.Max( target, g.FontSize );
window.SizeFull = WithAxis( window.SizeFull, axis, ImTrunc( target ) );
window.Size = window.SizeFull;
}
if ( hovered || held )
window.ParentWindow.DrawList.AddLine(
axis == 0 ? new Vector2( r.Max.x, r.Min.y ) : new Vector2( r.Min.x, r.Max.y ), r.Max,
GetColorU32Internal( held ? ImGuiCol.SeparatorActive : ImGuiCol.SeparatorHovered ), 2f );
}
}
private static ImGuiCol GetWindowBgColorIdx( ImGuiWindow window )
{
if ( (window.Flags & (ImGuiWindowFlags.Tooltip | ImGuiWindowFlags.Popup)) != 0 )
return ImGuiCol.PopupBg;
if ( (window.Flags & ImGuiWindowFlags.ChildWindow) != 0 )
return ImGuiCol.ChildBg;
return ImGuiCol.WindowBg;
}
private static void RenderWindowDecorations( ImGuiWindow window, ImRect titleBarRect, bool titleBarIsHighlight, int resizeGripCount, Color32[] resizeGripCol, float resizeGripDrawSize )
{
var g = G;
var style = g.Style;
var flags = window.Flags;
window.SkipItems = false;
float windowRounding = window.WindowRounding;
float windowBorderSize = window.WindowBorderSize;
if ( window.Collapsed )
{
var titleBarCol = GetColorU32Internal( titleBarIsHighlight ? ImGuiCol.TitleBgActive : ImGuiCol.TitleBgCollapsed );
window.DrawList.AddRectFilled( titleBarRect.Min, titleBarRect.Max, titleBarCol, windowRounding );
if ( windowBorderSize > 0f )
window.DrawList.AddRect( titleBarRect.Min, titleBarRect.Max, GetColorU32Internal( ImGuiCol.Border ), windowRounding, ImDrawFlags.None, windowBorderSize );
return;
}
// Window background
if ( (flags & ImGuiWindowFlags.NoBackground) == 0 )
{
var bgCol = GetColorU32Internal( GetWindowBgColorIdx( window ) );
if ( (g.NextWindowData.Flags & ImGuiNextWindowDataFlags.HasBgAlpha) != 0 )
bgCol = bgCol with { a = (byte)(ImSaturate( g.NextWindowData.BgAlphaVal ) * 255f) };
window.DrawList.AddRectFilled( window.Pos + new Vector2( 0, window.TitleBarHeight ), window.Pos + window.Size, bgCol, windowRounding,
(flags & ImGuiWindowFlags.NoTitleBar) != 0 ? ImDrawFlags.None : ImDrawFlags.RoundCornersBottom );
}
// Title bar
if ( (flags & ImGuiWindowFlags.NoTitleBar) == 0 )
{
var titleBarCol = GetColorU32Internal( titleBarIsHighlight ? ImGuiCol.TitleBgActive : ImGuiCol.TitleBg );
window.DrawList.AddRectFilled( titleBarRect.Min, titleBarRect.Max, titleBarCol, windowRounding, ImDrawFlags.RoundCornersTop );
}
// Menu bar
if ( (flags & ImGuiWindowFlags.MenuBar) != 0 )
{
var menuBarRect = window.MenuBarRect();
menuBarRect.ClipWith( window.Rect() );
window.DrawList.AddRectFilled( menuBarRect.Min + new Vector2( windowBorderSize, 0 ), menuBarRect.Max - new Vector2( windowBorderSize, 0 ), GetColorU32Internal( ImGuiCol.MenuBarBg ),
(flags & ImGuiWindowFlags.NoTitleBar) != 0 ? windowRounding : 0.0f, ImDrawFlags.RoundCornersTop );
if ( style.FrameBorderSize > 0.0f && menuBarRect.Max.y < window.Pos.y + window.Size.y )
window.DrawList.AddLine( menuBarRect.BL, menuBarRect.BR, GetColorU32Internal( ImGuiCol.Border ), style.FrameBorderSize );
}
// Scrollbars
if ( window.ScrollbarX ) Scrollbar( ImGuiAxis.X );
if ( window.ScrollbarY ) Scrollbar( ImGuiAxis.Y );
// Resize grips
if ( (flags & ImGuiWindowFlags.NoResize) == 0 )
{
for ( int resizeGripN = 0; resizeGripN < resizeGripCount; resizeGripN++ )
{
var col = resizeGripCol[resizeGripN];
if ( col.a == 0 )
continue;
var grip = ResizeGripDefs[resizeGripN];
var corner = ImLerp( window.Pos, window.Pos + window.Size, grip.CornerPosN );
var dl = window.DrawList;
dl.PathLineTo( corner + ImMul( grip.InnerDir, (resizeGripN & 1) != 0 ? new Vector2( windowBorderSize, resizeGripDrawSize ) : new Vector2( resizeGripDrawSize, windowBorderSize ) ) );
dl.PathLineTo( corner + ImMul( grip.InnerDir, (resizeGripN & 1) != 0 ? new Vector2( resizeGripDrawSize, windowBorderSize ) : new Vector2( windowBorderSize, resizeGripDrawSize ) ) );
dl.PathArcToFast( new Vector2( corner.x + grip.InnerDir.x * (windowRounding + windowBorderSize), corner.y + grip.InnerDir.y * (windowRounding + windowBorderSize) ), windowRounding, grip.AngleMin12, grip.AngleMax12 );
dl.PathFillConvex( col );
}
}
RenderWindowOuterBorders( window );
}
private static void RenderWindowOuterBorders( ImGuiWindow window )
{
var g = G;
float rounding = window.WindowRounding;
float borderSize = window.WindowBorderSize;
if ( borderSize > 0.0f && (window.Flags & ImGuiWindowFlags.NoBackground) == 0 )
window.DrawList.AddRect( window.Pos, window.Pos + window.Size, GetColorU32Internal( ImGuiCol.Border ), rounding, ImDrawFlags.None, borderSize );
int borderHeld = window.ResizeBorderHeld;
int border = borderHeld != -1 ? borderHeld : window.ResizeBorderHovered;
if ( border != -1 )
{
var r = window.Rect();
Vector2 a, b;
switch ( border )
{
case 0: a = r.TL; b = r.BL; break;
case 1: a = r.TR; b = r.BR; break;
case 2: a = r.TL; b = r.TR; break;
default: a = r.BL; b = r.BR; break;
}
window.DrawList.AddLine( a, b, GetColorU32Internal( borderHeld != -1 ? ImGuiCol.SeparatorActive : ImGuiCol.SeparatorHovered ), MathF.Max( 2.0f, borderSize ) );
}
if ( g.Style.FrameBorderSize > 0 && (window.Flags & ImGuiWindowFlags.NoTitleBar) == 0 )
{
float y = window.Pos.y + window.TitleBarHeight - 1;
window.DrawList.AddLine( new Vector2( window.Pos.x + borderSize, y ), new Vector2( window.Pos.x + window.Size.x - borderSize, y ), GetColorU32Internal( ImGuiCol.Border ), g.Style.FrameBorderSize );
}
}
private static void RenderWindowTitleBarContents( ImGuiWindow window, ImRect titleBarRect, string name, bool hasCloseButton, ref bool pOpen )
{
var g = G;
var style = g.Style;
var flags = window.Flags;
bool hasCollapseButton = (flags & ImGuiWindowFlags.NoCollapse) == 0 && style.WindowMenuButtonPosition != ImGuiDir.None;
var itemFlagsBackup = g.CurrentItemFlags;
g.CurrentItemFlags |= ImGuiItemFlags.NoNavDefaultFocus;
float padL = style.FramePadding.x;
float padR = style.FramePadding.x;
float buttonSz = g.FontSize;
Vector2 closeButtonPos = default, collapseButtonPos = default;
if ( hasCloseButton )
{
closeButtonPos = new Vector2( titleBarRect.Max.x - padR - buttonSz, titleBarRect.Min.y + style.FramePadding.y );
padR += buttonSz + style.ItemInnerSpacing.x;
}
if ( hasCollapseButton && style.WindowMenuButtonPosition == ImGuiDir.Right )
{
collapseButtonPos = new Vector2( titleBarRect.Max.x - padR - buttonSz, titleBarRect.Min.y + style.FramePadding.y );
padR += buttonSz + style.ItemInnerSpacing.x;
}
if ( hasCollapseButton && style.WindowMenuButtonPosition == ImGuiDir.Left )
{
collapseButtonPos = new Vector2( titleBarRect.Min.x + padL, titleBarRect.Min.y + style.FramePadding.y );
padL += buttonSz + style.ItemInnerSpacing.x;
}
if ( hasCollapseButton )
if ( CollapseButton( window.GetID( "#COLLAPSE" ), collapseButtonPos ) )
window.WantCollapseToggle = true;
if ( hasCloseButton )
if ( CloseButton( window.GetID( "#CLOSE" ), closeButtonPos ) )
pOpen = false;
g.CurrentItemFlags = itemFlagsBackup;
float markerSizeX = (flags & ImGuiWindowFlags.UnsavedDocument) != 0 ? buttonSz * 0.80f : 0.0f;
var textSize = CalcTextSize( name, true ) + new Vector2( markerSizeX, 0.0f );
if ( padL > style.FramePadding.x ) padL += style.ItemInnerSpacing.x;
if ( padR > style.FramePadding.x ) padR += style.ItemInnerSpacing.x;
if ( style.WindowTitleAlign.x > 0.0f && style.WindowTitleAlign.x < 1.0f )
{
float centerness = ImSaturate( 1.0f - MathF.Abs( style.WindowTitleAlign.x - 0.5f ) * 2.0f );
float padExtend = MathF.Min( MathF.Max( padL, padR ), titleBarRect.Width - padL - padR - textSize.x );
padL = MathF.Max( padL, padExtend * centerness );
padR = MathF.Max( padR, padExtend * centerness );
}
var layoutR = new ImRect( titleBarRect.Min.x + padL, titleBarRect.Min.y, titleBarRect.Max.x - padR, titleBarRect.Max.y );
var clipR = new ImRect( layoutR.Min.x, layoutR.Min.y, MathF.Min( layoutR.Max.x + style.ItemInnerSpacing.x, titleBarRect.Max.x ), layoutR.Max.y );
if ( (flags & ImGuiWindowFlags.UnsavedDocument) != 0 )
{
var markerPos = new Vector2( MathF.Max( layoutR.Min.x, layoutR.Min.x + (layoutR.Width - textSize.x) * style.WindowTitleAlign.x ) + textSize.x - markerSizeX * 0.5f, layoutR.Center.y );
RenderBullet( window.DrawList, markerPos, GetColorU32Internal( ImGuiCol.Text ) );
}
RenderTextClippedEx( window.DrawList, layoutR.Min, layoutR.Max, LabelText( name ), textSize - new Vector2( markerSizeX, 0f ), style.WindowTitleAlign, clipR );
}
internal static bool CollapseButton( int id, Vector2 pos )
{
var g = G;
var window = g.CurrentWindow;
var bb = new ImRect( pos, pos + new Vector2( g.FontSize, g.FontSize ) );
bool isClipped = !ItemAdd( bb, id );
bool pressed = ButtonBehavior( bb, id, out bool hovered, out bool held, ImGuiButtonFlags.None );
if ( isClipped )
return pressed;
var bgCol = GetColorU32Internal( held && hovered ? ImGuiCol.ButtonActive : hovered ? ImGuiCol.ButtonHovered : ImGuiCol.Button );
var textCol = GetColorU32Internal( ImGuiCol.Text );
if ( hovered || held )
window.DrawList.AddCircleFilled( bb.Center, g.FontSize * 0.5f + 1f, bgCol );
RenderArrow( window.DrawList, bb.Min, textCol, window.Collapsed ? ImGuiDir.Right : ImGuiDir.Down, 1.0f );
if ( IsItemActive() && IsMouseDragging( ImGuiMouseButton.Left ) )
StartMouseMovingWindow( window );
return pressed;
}
internal static bool CloseButton( int id, Vector2 pos )
{
var g = G;
var window = g.CurrentWindow;
var bb = new ImRect( pos, pos + new Vector2( g.FontSize, g.FontSize ) );
var bbInteract = bb;
float areaToVisibleRatio = window.OuterRectClipped.Area / MathF.Max( 1f, bb.Area );
if ( areaToVisibleRatio < 1.5f )
bbInteract.Expand( ImTrunc( bbInteract.Size * -0.25f ) );
bool isClipped = !ItemAdd( bbInteract, id );
bool pressed = ButtonBehavior( bbInteract, id, out bool hovered, out bool held );
if ( isClipped )
return pressed;
var bgCol = GetColorU32Internal( held ? ImGuiCol.ButtonActive : ImGuiCol.ButtonHovered );
if ( hovered )
window.DrawList.AddCircleFilled( bb.Center, g.FontSize * 0.5f + 1f, bgCol );
float crossExtent = g.FontSize * 0.5f * 0.7071f - 1.0f;
var crossCol = GetColorU32Internal( ImGuiCol.Text );
var crossCenter = bb.Center - new Vector2( 0.5f, 0.5f );
window.DrawList.AddLine( crossCenter + new Vector2( +crossExtent, +crossExtent ), crossCenter + new Vector2( -crossExtent, -crossExtent ), crossCol, 1.0f );
window.DrawList.AddLine( crossCenter + new Vector2( +crossExtent, -crossExtent ), crossCenter + new Vector2( -crossExtent, +crossExtent ), crossCol, 1.0f );
return pressed;
}
public static void End()
{
var g = G;
var window = g.CurrentWindow;
if ( window is null )
throw new InvalidOperationException( "ImGui.End() called without a matching Begin()." );
if ( g.CurrentWindowStack.Count <= 1 && g.WithinFrameScopeWithImplicitWindow )
throw new InvalidOperationException( "ImGui.End() called too many times!" );
if ( window.DC.CurrentColumns is not null )
EndColumns();
PopClipRect();
var stackData = g.CurrentWindowStack[^1];
ErrorCheckEndWindowRecover( stackData );
g.LastItemData = stackData.ParentLastItemDataBackup;
if ( (window.Flags & ImGuiWindowFlags.ChildMenu) != 0 )
g.BeginMenuDepth--;
if ( (window.Flags & ImGuiWindowFlags.Popup) != 0 )
g.BeginPopupStack.RemoveAt( g.BeginPopupStack.Count - 1 );
g.CurrentWindowStack.RemoveAt( g.CurrentWindowStack.Count - 1 );
g.CurrentWindow = g.CurrentWindowStack.Count == 0 ? null : g.CurrentWindowStack[^1].Window;
}
/// <summary>
/// Gracefully recover from unbalanced Push/Pop calls inside a window instead of corrupting state.
/// </summary>
private static void ErrorCheckEndWindowRecover( ImGuiWindowStackData stackData )
{
var g = G;
var window = g.CurrentWindow;
while ( g.GroupStack.Count > stackData.StackSizesGroupStack )
{
Log.Warning( $"ImGui: missing EndGroup() in '{window.Name}'" );
EndGroup();
}
while ( g.ColorStack.Count > stackData.StackSizesColorStack )
{
Log.Warning( $"ImGui: missing PopStyleColor() in '{window.Name}'" );
PopStyleColor();
}
while ( g.StyleVarStack.Count > stackData.StackSizesStyleVarStack )
{
Log.Warning( $"ImGui: missing PopStyleVar() in '{window.Name}'" );
PopStyleVar();
}
while ( g.DisabledStackSize > stackData.StackSizesDisabledStack )
{
Log.Warning( $"ImGui: missing EndDisabled() in '{window.Name}'" );
EndDisabled();
}
while ( g.ItemFlagsStack.Count > stackData.StackSizesItemFlagsStack )
{
Log.Warning( $"ImGui: missing PopItemFlag() in '{window.Name}'" );
PopItemFlag();
}
while ( window.DC.TreeDepth > 0 )
{
Log.Warning( $"ImGui: missing TreePop() in '{window.Name}'" );
TreePop();
}
while ( window.IDStack.Count > 1 )
{
Log.Warning( $"ImGui: missing PopID() in '{window.Name}'" );
window.IDStack.RemoveAt( window.IDStack.Count - 1 );
}
}
#endregion
#region Child windows
public static bool BeginChild( string strId, Vector2 size = default, ImGuiChildFlags childFlags = ImGuiChildFlags.None, ImGuiWindowFlags windowFlags = ImGuiWindowFlags.None )
{
var window = GetCurrentWindow();
return BeginChildEx( strId, window.GetID( strId ), size, childFlags, windowFlags );
}
public static bool BeginChild( int id, Vector2 size = default, ImGuiChildFlags childFlags = ImGuiChildFlags.None, ImGuiWindowFlags windowFlags = ImGuiWindowFlags.None )
=> BeginChildEx( null, id, size, childFlags, windowFlags );
/// <summary>Legacy overload using a bool for borders.</summary>
public static bool BeginChild( string strId, Vector2 size, bool border, ImGuiWindowFlags windowFlags = ImGuiWindowFlags.None )
=> BeginChild( strId, size, border ? ImGuiChildFlags.Borders : ImGuiChildFlags.None, windowFlags );
internal static bool BeginChildEx( string name, int id, Vector2 sizeArg, ImGuiChildFlags childFlags, ImGuiWindowFlags windowFlags )
{
var g = G;
var parentWindow = g.CurrentWindow;
windowFlags |= ImGuiWindowFlags.ChildWindow | ImGuiWindowFlags.NoTitleBar;
windowFlags |= parentWindow.Flags & ImGuiWindowFlags.NoMove;
if ( (childFlags & ImGuiChildFlags.AlwaysAutoResize) != 0 && (childFlags & (ImGuiChildFlags.AutoResizeX | ImGuiChildFlags.AutoResizeY)) != 0 )
windowFlags |= ImGuiWindowFlags.AlwaysAutoResize;
if ( (childFlags & (ImGuiChildFlags.ResizeX | ImGuiChildFlags.ResizeY)) != 0 )
childFlags |= ImGuiChildFlags.Borders;
if ( (childFlags & ImGuiChildFlags.FrameStyle) != 0 )
{
PushStyleColor( ImGuiCol.ChildBg, g.Style.Colors[(int)ImGuiCol.FrameBg] );
PushStyleVar( ImGuiStyleVar.ChildRounding, g.Style.FrameRounding );
PushStyleVar( ImGuiStyleVar.ChildBorderSize, g.Style.FrameBorderSize );
PushStyleVar( ImGuiStyleVar.WindowPadding, g.Style.FramePadding );
childFlags |= ImGuiChildFlags.Borders | ImGuiChildFlags.AlwaysUseWindowPadding;
windowFlags |= ImGuiWindowFlags.NoMove;
}
g.NextWindowData.Flags |= ImGuiNextWindowDataFlags.HasChildFlags;
g.NextWindowData.ChildFlags = childFlags;
var sizeAvail = GetContentRegionAvail();
var sizeDefault = new Vector2( (childFlags & ImGuiChildFlags.AutoResizeX) != 0 ? 0.0f : sizeAvail.x, (childFlags & ImGuiChildFlags.AutoResizeY) != 0 ? 0.0f : sizeAvail.y );
var size = CalcItemSize( sizeArg, sizeDefault.x, sizeDefault.y );
if ( (g.NextWindowData.Flags & ImGuiNextWindowDataFlags.HasSize) == 0 )
SetNextWindowSize( size );
string tempWindowName = name is not null
? $"{parentWindow.Name}/{name}_{id:X8}"
: $"{parentWindow.Name}/{id:X8}";
float backupBorderSize = g.Style.ChildBorderSize;
if ( (childFlags & ImGuiChildFlags.Borders) == 0 )
g.Style.ChildBorderSize = 0.0f;
bool ret = Begin( tempWindowName, windowFlags );
g.Style.ChildBorderSize = backupBorderSize;
if ( (childFlags & ImGuiChildFlags.FrameStyle) != 0 )
{
// The child window is now current: pop style changes made for its creation without warnings.
PopStyleVar( 3 );
PopStyleColor();
}
var childWindow = g.CurrentWindow;
childWindow.ChildId = id;
if ( childWindow.BeginCount == 1 )
parentWindow.DC.CursorPos = childWindow.Pos;
return ret;
}
public static void EndChild()
{
var g = G;
var childWindow = g.CurrentWindow;
if ( (childWindow.Flags & ImGuiWindowFlags.ChildWindow) == 0 )
throw new InvalidOperationException( "EndChild() called without a matching BeginChild()." );
var childSize = childWindow.Size;
End();
if ( childWindow.BeginCount == 1 )
{
var parentWindow = g.CurrentWindow;
var bb = new ImRect( parentWindow.DC.CursorPos, parentWindow.DC.CursorPos + childSize );
ItemSize( childSize );
ItemAdd( bb, childWindow.ChildId, null, ImGuiItemFlags.NoNav );
if ( g.HoveredWindow == childWindow )
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags.HoveredWindow;
}
}
#endregion
#region Scrollbars
internal static ImRect GetWindowScrollbarRect( ImGuiWindow window, ImGuiAxis axis )
{
var outerRect = window.Rect();
var innerRect = window.InnerRect;
float borderSize = window.WindowBorderSize;
float scrollbarSize = axis == ImGuiAxis.X ? window.ScrollbarSizes.y : window.ScrollbarSizes.x;
if ( axis == ImGuiAxis.X )
return new ImRect( innerRect.Min.x, MathF.Max( outerRect.Min.y, outerRect.Max.y - borderSize - scrollbarSize ), innerRect.Max.x - borderSize, outerRect.Max.y - borderSize );
return new ImRect( MathF.Max( outerRect.Min.x, outerRect.Max.x - borderSize - scrollbarSize ), innerRect.Min.y, outerRect.Max.x - borderSize, innerRect.Max.y - borderSize );
}
internal static void Scrollbar( ImGuiAxis axis )
{
var g = G;
var window = g.CurrentWindow;
int id = window.GetID( axis == ImGuiAxis.X ? "#SCROLLX" : "#SCROLLY" );
var bb = GetWindowScrollbarRect( window, axis );
var roundingCorners = ImDrawFlags.RoundCornersNone;
if ( axis == ImGuiAxis.X )
{
roundingCorners |= ImDrawFlags.RoundCornersBottomLeft;
if ( !window.ScrollbarY )
roundingCorners |= ImDrawFlags.RoundCornersBottomRight;
}
else
{
if ( (window.Flags & ImGuiWindowFlags.NoTitleBar) != 0 && (window.Flags & ImGuiWindowFlags.MenuBar) == 0 )
roundingCorners |= ImDrawFlags.RoundCornersTopRight;
if ( !window.ScrollbarX )
roundingCorners |= ImDrawFlags.RoundCornersBottomRight;
}
int a = (int)axis;
float sizeVisible = Axis( window.InnerRect.Max, a ) - Axis( window.InnerRect.Min, a );
float sizeContents = Axis( window.ContentSize, a ) + Axis( window.WindowPadding, a ) * 2.0f;
float scroll = Axis( window.Scroll, a );
ScrollbarEx( bb, id, axis, ref scroll, sizeVisible, sizeContents, roundingCorners );
window.Scroll = WithAxis( window.Scroll, a, scroll );
}
internal static bool ScrollbarEx( ImRect bbFrame, int id, ImGuiAxis axis, ref float pScrollV, float sizeVisibleV, float sizeContentsV, ImDrawFlags drawRoundingFlags )
{
var g = G;
var window = g.CurrentWindow;
if ( window.SkipItems )
return false;
float bbFrameWidth = bbFrame.Width;
float bbFrameHeight = bbFrame.Height;
if ( bbFrameWidth <= 0.0f || bbFrameHeight <= 0.0f )
return false;
float alpha = 1.0f;
if ( axis == ImGuiAxis.Y && bbFrameHeight < g.FontSize + g.Style.FramePadding.y * 2.0f )
alpha = ImSaturate( (bbFrameHeight - g.FontSize) / (g.Style.FramePadding.y * 2.0f) );
if ( alpha <= 0.0f )
return false;
var style = g.Style;
bool allowInteraction = alpha >= 1.0f;
var bb = bbFrame;
bb.Expand( new Vector2( -Math.Clamp( ImTrunc( (bbFrameWidth - 2.0f) * 0.5f ), 0.0f, 3.0f ), -Math.Clamp( ImTrunc( (bbFrameHeight - 2.0f) * 0.5f ), 0.0f, 3.0f ) ) );
int a = (int)axis;
float scrollbarSizeV = axis == ImGuiAxis.X ? bb.Width : bb.Height;
float winSizeV = MathF.Max( MathF.Max( sizeContentsV, sizeVisibleV ), 1.0f );
float grabHPixels = Math.Clamp( scrollbarSizeV * (sizeVisibleV / winSizeV), MathF.Min( style.GrabMinSize, scrollbarSizeV ), scrollbarSizeV );
float grabHNorm = grabHPixels / scrollbarSizeV;
ItemAdd( bbFrame, id, null, ImGuiItemFlags.NoNav );
ButtonBehavior( bb, id, out bool hovered, out bool held, ImGuiButtonFlags.NoNavFocus );
float scrollMax = MathF.Max( 1.0f, sizeContentsV - sizeVisibleV );
float scrollRatio = ImSaturate( pScrollV / scrollMax );
float grabVNorm = scrollRatio * (scrollbarSizeV - grabHPixels) / scrollbarSizeV;
if ( held && allowInteraction && grabHNorm < 1.0f )
{
float scrollbarPosV = Axis( bb.Min, a );
float mousePosV = Axis( g.IO.MousePos, a );
float clickedVNorm = ImSaturate( (mousePosV - scrollbarPosV) / scrollbarSizeV );
int heldDir = clickedVNorm < grabVNorm ? -1 : (clickedVNorm > grabVNorm + grabHNorm ? +1 : 0);
if ( g.ActiveIdIsJustActivated )
g.ScrollbarClickDeltaToGrabCenterF = (heldDir == 0 && !g.IO.KeyShift) ? clickedVNorm - grabVNorm - grabHNorm * 0.5f : 0.0f;
float scrollVNorm = ImSaturate( (clickedVNorm - g.ScrollbarClickDeltaToGrabCenterF - grabHNorm * 0.5f) / (1.0f - grabHNorm) );
pScrollV = MathF.Round( scrollVNorm * scrollMax );
scrollRatio = ImSaturate( pScrollV / scrollMax );
grabVNorm = scrollRatio * (scrollbarSizeV - grabHPixels) / scrollbarSizeV;
}
var bgCol = GetColorU32Internal( ImGuiCol.ScrollbarBg );
var grabCol = GetColorU32Internal( held ? ImGuiCol.ScrollbarGrabActive : hovered ? ImGuiCol.ScrollbarGrabHovered : ImGuiCol.ScrollbarGrab, alpha );
window.DrawList.AddRectFilled( bbFrame.Min, bbFrame.Max, bgCol, window.WindowRounding, drawRoundingFlags );
ImRect grabRect;
if ( axis == ImGuiAxis.X )
grabRect = new ImRect( ImLerp( bb.Min.x, bb.Max.x, grabVNorm ), bb.Min.y, ImLerp( bb.Min.x, bb.Max.x, grabVNorm ) + grabHPixels, bb.Max.y );
else
grabRect = new ImRect( bb.Min.x, ImLerp( bb.Min.y, bb.Max.y, grabVNorm ), bb.Max.x, ImLerp( bb.Min.y, bb.Max.y, grabVNorm ) + grabHPixels );
window.DrawList.AddRectFilled( grabRect.Min, grabRect.Max, grabCol, style.ScrollbarRounding );
return held;
}
#endregion
#region Scrolling
private static float CalcScrollEdgeSnap( float target, float snapMin, float snapMax, float snapThreshold, float centerRatio )
{
if ( target <= snapMin + snapThreshold )
return ImLerp( snapMin, target, centerRatio );
if ( target >= snapMax - snapThreshold )
return ImLerp( target, snapMax, centerRatio );
return target;
}
private static Vector2 CalcNextScrollFromScrollTargetAndClamp( ImGuiWindow window )
{
var scroll = window.Scroll;
var decorationSize = new Vector2( window.DecoOuterSizeX1 + window.DecoInnerSizeX1 + window.DecoOuterSizeX2, window.DecoOuterSizeY1 + window.DecoInnerSizeY1 + window.DecoOuterSizeY2 );
for ( int axis = 0; axis < 2; axis++ )
{
float target = Axis( window.ScrollTarget, axis );
if ( target < FLT_MAX )
{
float centerRatio = Axis( window.ScrollTargetCenterRatio, axis );
float scrollTarget = target;
if ( Axis( window.ScrollTargetEdgeSnapDist, axis ) > 0.0f )
{
float snapMax = Axis( window.ScrollMax, axis ) + Axis( window.SizeFull, axis ) - Axis( decorationSize, axis );
scrollTarget = CalcScrollEdgeSnap( scrollTarget, 0.0f, snapMax, Axis( window.ScrollTargetEdgeSnapDist, axis ), centerRatio );
}
scroll = WithAxis( scroll, axis, scrollTarget - centerRatio * (Axis( window.SizeFull, axis ) - Axis( decorationSize, axis )) );
}
scroll = WithAxis( scroll, axis, MathF.Round( MathF.Max( Axis( scroll, axis ), 0.0f ) ) );
if ( !window.Collapsed && !window.SkipItems )
scroll = WithAxis( scroll, axis, MathF.Min( Axis( scroll, axis ), Axis( window.ScrollMax, axis ) ) );
}
return scroll;
}
public static float GetScrollX() => G.CurrentWindow.Scroll.x;
public static float GetScrollY() => G.CurrentWindow.Scroll.y;
public static float GetScrollMaxX() => G.CurrentWindow.ScrollMax.x;
public static float GetScrollMaxY() => G.CurrentWindow.ScrollMax.y;
internal static void SetScrollX( ImGuiWindow window, float scrollX )
{
window.ScrollTarget.x = scrollX;
window.ScrollTargetCenterRatio.x = 0.0f;
window.ScrollTargetEdgeSnapDist.x = 0.0f;
}
internal static void SetScrollY( ImGuiWindow window, float scrollY )
{
window.ScrollTarget.y = scrollY;
window.ScrollTargetCenterRatio.y = 0.0f;
window.ScrollTargetEdgeSnapDist.y = 0.0f;
}
public static void SetScrollX( float scrollX ) => SetScrollX( G.CurrentWindow, scrollX );
public static void SetScrollY( float scrollY ) => SetScrollY( G.CurrentWindow, scrollY );
internal static void SetScrollFromPosX( ImGuiWindow window, float localX, float centerXRatio )
{
window.ScrollTarget.x = ImTrunc( localX - window.DecoOuterSizeX1 - window.DecoInnerSizeX1 + window.Scroll.x );
window.ScrollTargetCenterRatio.x = centerXRatio;
window.ScrollTargetEdgeSnapDist.x = 0.0f;
}
internal static void SetScrollFromPosY( ImGuiWindow window, float localY, float centerYRatio )
{
window.ScrollTarget.y = ImTrunc( localY - window.DecoOuterSizeY1 - window.DecoInnerSizeY1 + window.Scroll.y );
window.ScrollTargetCenterRatio.y = centerYRatio;
window.ScrollTargetEdgeSnapDist.y = 0.0f;
}
public static void SetScrollFromPosX( float localX, float centerXRatio = 0.5f ) => SetScrollFromPosX( G.CurrentWindow, localX, centerXRatio );
public static void SetScrollFromPosY( float localY, float centerYRatio = 0.5f ) => SetScrollFromPosY( G.CurrentWindow, localY, centerYRatio );
public static void SetScrollHereX( float centerXRatio = 0.5f )
{
var g = G;
var window = g.CurrentWindow;
float spacingX = MathF.Max( window.WindowPadding.x, g.Style.ItemSpacing.x );
float targetPosX = ImLerp( g.LastItemData.Rect.Min.x - spacingX, g.LastItemData.Rect.Max.x + spacingX, centerXRatio );
SetScrollFromPosX( window, targetPosX - window.Pos.x, centerXRatio );
window.ScrollTargetEdgeSnapDist.x = MathF.Max( 0.0f, window.WindowPadding.x - spacingX );
}
public static void SetScrollHereY( float centerYRatio = 0.5f )
{
var g = G;
var window = g.CurrentWindow;
float spacingY = MathF.Max( window.WindowPadding.y, g.Style.ItemSpacing.y );
float targetPosY = ImLerp( window.DC.CursorPosPrevLine.y - spacingY, window.DC.CursorPosPrevLine.y + window.DC.PrevLineSize.y + spacingY, centerYRatio );
SetScrollFromPosY( window, targetPosY - window.Pos.y, centerYRatio );
window.ScrollTargetEdgeSnapDist.y = MathF.Max( 0.0f, window.WindowPadding.y - spacingY );
}
/// <summary>Scroll so that the given rect becomes visible in the window.</summary>
internal static void ScrollToRect( ImGuiWindow window, ImRect rect )
{
var windowRect = new ImRect( window.InnerRect.Min - Vector2.One, window.InnerRect.Max + Vector2.One );
if ( windowRect.Contains( rect ) )
return;
if ( rect.Min.y < windowRect.Min.y )
SetScrollFromPosY( window, rect.Min.y - window.Pos.y - G.Style.ItemSpacing.y, 0.0f );
else if ( rect.Max.y >= windowRect.Max.y )
SetScrollFromPosY( window, rect.Max.y - window.Pos.y + G.Style.ItemSpacing.y, 1.0f );
if ( rect.Min.x < windowRect.Min.x )
SetScrollFromPosX( window, rect.Min.x - window.Pos.x - G.Style.ItemSpacing.x, 0.0f );
else if ( rect.Max.x >= windowRect.Max.x )
SetScrollFromPosX( window, rect.Max.x - window.Pos.x + G.Style.ItemSpacing.x, 1.0f );
}
#endregion
#region Window queries / setters
public static bool IsWindowAppearing() => G.CurrentWindow?.Appearing == true;
public static bool IsWindowCollapsed() => G.CurrentWindow?.Collapsed == true;
public static bool IsWindowFocused( ImGuiFocusedFlags flags = ImGuiFocusedFlags.None )
{
var g = G;
var refWindow = g.NavWindow;
var curWindow = g.CurrentWindow;
if ( refWindow is null )
return false;
if ( (flags & ImGuiFocusedFlags.AnyWindow) != 0 )
return true;
if ( curWindow is null )
return false;
bool popupHierarchy = (flags & ImGuiFocusedFlags.NoPopupHierarchy) == 0;
if ( (flags & ImGuiFocusedFlags.RootWindow) != 0 )
curWindow = popupHierarchy ? curWindow.RootWindowPopupTree : curWindow.RootWindow;
if ( (flags & ImGuiFocusedFlags.ChildWindows) != 0 )
return IsWindowChildOf( refWindow, curWindow, popupHierarchy );
return refWindow == curWindow;
}
public static bool IsWindowHovered( ImGuiHoveredFlags flags = ImGuiHoveredFlags.None )
{
var g = G;
var refWindow = g.HoveredWindow;
var curWindow = g.CurrentWindow;
if ( refWindow is null )
return false;
if ( (flags & ImGuiHoveredFlags.AnyWindow) == 0 )
{
if ( curWindow is null )
return false;
bool popupHierarchy = (flags & ImGuiHoveredFlags.NoPopupHierarchy) == 0;
if ( (flags & ImGuiHoveredFlags.RootWindow) != 0 )
curWindow = popupHierarchy ? curWindow.RootWindowPopupTree : curWindow.RootWindow;
bool result = (flags & ImGuiHoveredFlags.ChildWindows) != 0
? IsWindowChildOf( refWindow, curWindow, popupHierarchy )
: refWindow == curWindow;
if ( !result )
return false;
}
if ( !IsWindowContentHoverable( refWindow, flags ) )
return false;
if ( (flags & ImGuiHoveredFlags.AllowWhenBlockedByActiveItem) == 0 )
if ( g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != refWindow.MoveId )
return false;
return true;
}
public static ImDrawList GetWindowDrawList() => GetCurrentWindow().DrawList;
public static Vector2 GetWindowPos() => G.CurrentWindow.Pos;
public static Vector2 GetWindowSize() => G.CurrentWindow.Size;
public static float GetWindowWidth() => G.CurrentWindow.Size.x;
public static float GetWindowHeight() => G.CurrentWindow.Size.y;
public static void SetNextWindowPos( Vector2 pos, ImGuiCond cond = ImGuiCond.None, Vector2 pivot = default )
{
var g = G;
g.NextWindowData.Flags |= ImGuiNextWindowDataFlags.HasPos;
g.NextWindowData.PosVal = pos;
g.NextWindowData.PosPivotVal = pivot;
g.NextWindowData.PosCond = cond != ImGuiCond.None ? cond : ImGuiCond.Always;
}
public static void SetNextWindowSize( Vector2 size, ImGuiCond cond = ImGuiCond.None )
{
var g = G;
g.NextWindowData.Flags |= ImGuiNextWindowDataFlags.HasSize;
g.NextWindowData.SizeVal = size;
g.NextWindowData.SizeCond = cond != ImGuiCond.None ? cond : ImGuiCond.Always;
}
/// <summary>
/// Set next window size limits. Use -1 on an axis to preserve the current size. Use float.MaxValue for no maximum.
/// </summary>
public static void SetNextWindowSizeConstraints( Vector2 sizeMin, Vector2 sizeMax, Action<ImGuiSizeCallbackData> customCallback = null )
{
var g = G;
g.NextWindowData.Flags |= ImGuiNextWindowDataFlags.HasSizeConstraint;
g.NextWindowData.SizeConstraintRect = new ImRect( sizeMin, sizeMax );
g.NextWindowData.SizeCallback = customCallback;
}
public static void SetNextWindowContentSize( Vector2 size )
{
var g = G;
g.NextWindowData.Flags |= ImGuiNextWindowDataFlags.HasContentSize;
g.NextWindowData.ContentSizeVal = ImTrunc( size );
}
public static void SetNextWindowCollapsed( bool collapsed, ImGuiCond cond = ImGuiCond.None )
{
var g = G;
g.NextWindowData.Flags |= ImGuiNextWindowDataFlags.HasCollapsed;
g.NextWindowData.CollapsedVal = collapsed;
g.NextWindowData.CollapsedCond = cond != ImGuiCond.None ? cond : ImGuiCond.Always;
}
/// <summary>Causes the next window to be focused. Should be called before Begin().</summary>
public static void SetNextWindowFocus()
{
G.NextWindowData.Flags |= ImGuiNextWindowDataFlags.HasFocus;
}
public static void SetNextWindowScroll( Vector2 scroll )
{
var g = G;
g.NextWindowData.Flags |= ImGuiNextWindowDataFlags.HasScroll;
g.NextWindowData.ScrollVal = scroll;
}
public static void SetNextWindowBgAlpha( float alpha )
{
var g = G;
g.NextWindowData.Flags |= ImGuiNextWindowDataFlags.HasBgAlpha;
g.NextWindowData.BgAlphaVal = alpha;
}
public static void SetWindowPos( Vector2 pos, ImGuiCond cond = ImGuiCond.None ) => SetWindowPos( G.CurrentWindow, pos, cond );
public static void SetWindowSize( Vector2 size, ImGuiCond cond = ImGuiCond.None ) => SetWindowSize( G.CurrentWindow, size, cond );
public static void SetWindowCollapsed( bool collapsed, ImGuiCond cond = ImGuiCond.None ) => SetWindowCollapsed( G.CurrentWindow, collapsed, cond );
public static void SetWindowFocus() => FocusWindow( G.CurrentWindow );
public static void SetWindowPos( string name, Vector2 pos, ImGuiCond cond = ImGuiCond.None )
{
var window = FindWindowByName( name );
if ( window is not null ) SetWindowPos( window, pos, cond );
}
public static void SetWindowSize( string name, Vector2 size, ImGuiCond cond = ImGuiCond.None )
{
var window = FindWindowByName( name );
if ( window is not null ) SetWindowSize( window, size, cond );
}
public static void SetWindowCollapsed( string name, bool collapsed, ImGuiCond cond = ImGuiCond.None )
{
var window = FindWindowByName( name );
if ( window is not null ) SetWindowCollapsed( window, collapsed, cond );
}
public static void SetWindowFocus( string name )
{
if ( name is null )
{
FocusWindow( null );
return;
}
var window = FindWindowByName( name );
if ( window is not null ) FocusWindow( window );
}
/// <summary>Per-window font scale. Prefer <see cref="ImGuiIO.FontGlobalScale"/> or PushFont for most uses.</summary>
public static void SetWindowFontScale( float scale )
{
var g = G;
var window = GetCurrentWindow();
window.FontWindowScale = scale;
g.FontSize = window.CalcFontSize();
}
#endregion
#region Focus
internal static void FocusWindow( ImGuiWindow window )
{
var g = G;
if ( g.NavWindow != window )
{
g.NavWindow = window;
g.NavId = 0;
}
ClosePopupsOverWindow( window, false );
if ( window is null )
return;
var focusFrontWindow = window.RootWindow;
var displayFrontWindow = window.RootWindow;
if ( g.ActiveId != 0 && g.ActiveIdWindow is not null && g.ActiveIdWindow.RootWindow != focusFrontWindow )
if ( !g.ActiveIdNoClearOnFocusLoss )
ClearActiveID();
BringWindowToFocusFront( focusFrontWindow );
if ( ((window.Flags | displayFrontWindow.Flags) & ImGuiWindowFlags.NoBringToFrontOnFocus) == 0 )
BringWindowToDisplayFront( displayFrontWindow );
}
internal static void FocusTopMostWindowUnderOne( ImGuiWindow underThisWindow, ImGuiWindow ignoreWindow )
{
var g = G;
int startIdx = g.WindowsFocusOrder.Count - 1;
if ( underThisWindow is not null )
{
int offset = -1;
while ( (underThisWindow.Flags & ImGuiWindowFlags.ChildWindow) != 0 )
{
underThisWindow = underThisWindow.ParentWindow;
offset = 0;
}
startIdx = underThisWindow.FocusOrder + offset;
}
for ( int i = Math.Min( startIdx, g.WindowsFocusOrder.Count - 1 ); i >= 0; i-- )
{
var window = g.WindowsFocusOrder[i];
if ( window == ignoreWindow || !window.WasActive )
continue;
if ( (window.Flags & (ImGuiWindowFlags.Popup | ImGuiWindowFlags.Tooltip)) != 0 )
continue;
if ( (window.Flags & ImGuiWindowFlags.NoMouseInputs) == 0 )
{
FocusWindow( window );
return;
}
}
FocusWindow( null );
}
private static void BringWindowToFocusFront( ImGuiWindow window )
{
var g = G;
if ( g.WindowsFocusOrder.Count == 0 || g.WindowsFocusOrder[^1] == window )
return;
int idx = g.WindowsFocusOrder.IndexOf( window );
if ( idx < 0 )
return;
g.WindowsFocusOrder.RemoveAt( idx );
g.WindowsFocusOrder.Add( window );
for ( int i = idx; i < g.WindowsFocusOrder.Count; i++ )
g.WindowsFocusOrder[i].FocusOrder = i;
}
private static void BringWindowToDisplayFront( ImGuiWindow window )
{
var g = G;
if ( g.Windows.Count == 0 || g.Windows[^1] == window )
return;
int idx = g.Windows.IndexOf( window );
if ( idx < 0 )
return;
g.Windows.RemoveAt( idx );
g.Windows.Add( window );
}
internal static void StartMouseMovingWindow( ImGuiWindow window )
{
var g = G;
FocusWindow( window );
SetActiveID( window.MoveId, window );
g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - window.RootWindow.Pos;
g.ActiveIdNoClearOnFocusLoss = true;
bool canMoveWindow = (window.Flags & ImGuiWindowFlags.NoMove) == 0 && (window.RootWindow.Flags & ImGuiWindowFlags.NoMove) == 0;
if ( canMoveWindow )
g.MovingWindow = window;
}
internal static void StopMouseMovingWindow()
{
var g = G;
var window = g.MovingWindow;
if ( window?.RootWindow is not null )
MarkIniSettingsDirty( window.RootWindow );
g.MovingWindow = null;
ClearActiveID();
}
#endregion
}