System/ImGuiSystem.Layout.cs
using Duccsoft.ImGui.Elements;
using Duccsoft.ImGui.Rendering;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Duccsoft.ImGui;
internal partial class ImGuiSystem
{
public BoundsList CurrentBoundsList { get; private set; } = new();
// Keep the previous frame's bounds list so that we can check whether a widget
// drawn before a window was occluded by that window on the previous frame.
public BoundsList PreviousBoundsList { get; set; } = new();
public int? PreviousHoveredWindowId { get; set; }
public int? PreviousHoveredElementId { get; set; }
public Stack<Window> WindowStack { get; private init; } = new();
private Dictionary<int, ImDrawList> DrawLists { get; set; } = new();
public IdStack IdStack { get; private init; } = new();
private Dictionary<int, Element> CurrentElements { get; set; } = new();
public Dictionary<int, Vector2> CustomWindowPositions { get; private set; } = new();
public Window CurrentWindow
{
get
{
if ( WindowStack.Count < 1 )
return null;
return WindowStack.Peek();
}
}
public Vector2 NextWindowPosition { get; set; }
public Vector2 NextWindowPivot { get; set; }
public Vector2 NextWindowSize { get; set; }
public bool ShouldFocusNextWindow { get; set; } = false;
public int? FocusedWindowId { get; private set; }
public int? FocusedElementId { get; private set; }
public int? ClickedElementId { get; set; }
private void AddElement( Element element )
{
// Items will share an ID in some common cases.
if ( CurrentElements.ContainsKey( element.Id ) )
return;
CurrentElements.Add( element.Id, element );
CurrentBoundsList.AddElement( element, element.Parent );
}
internal Element GetElement( int id )
{
CurrentElements.TryGetValue( id, out var element );
return element;
}
public void Focus( Element element )
{
FocusedWindowId = element?.Window?.Id;
FocusedElementId = element?.Id;
}
private void SwapBounds()
{
PreviousBoundsList = CurrentBoundsList;
CurrentBoundsList = new();
PreviousHoveredWindowId = PreviousBoundsList.TraceWindow( MouseState.Position );
PreviousHoveredElementId = PreviousBoundsList.TraceElement( MouseState.Position );
// Log.Info( $"Hovered window: {PreviousHoveredWindowId}" );
// Log.Info( $"Hovered element: {PreviousHoveredElementId}" );
}
private void ClearElements()
{
CurrentElements.Clear();
WindowStack.Clear();
IdStack.Clear();
}
private void FinalizeBounds()
{
CurrentBoundsList.ApplyElementFlags();
CurrentBoundsList.SortWindows();
}
public void BeginWindow( string name, ref bool open, ImGuiWindowFlags flags )
{
// TODO: Move this method in to Window.Begin()
if ( CustomWindowPositions.TryGetValue( ImGui.GetID( name ), out var customPos ) )
{
NextWindowPosition = customPos;
}
var nextWindow = new Window( name, ref open, NextWindowPosition, NextWindowPivot, NextWindowSize, flags );
if ( !flags.HasFlag( ImGuiWindowFlags.NoTitleBar ) )
{
nextWindow.CursorPosition = Vector2.Zero;
nextWindow.TitleBar = new WindowTitleBar( nextWindow );
nextWindow.CursorPosition += Style.WindowPadding;
nextWindow.CursorStartPosition = nextWindow.CursorPosition;
}
if ( ShouldFocusNextWindow )
{
Focus( nextWindow );
}
if ( nextWindow.IsAppearing && !nextWindow.WindowFlags.HasFlag( ImGuiWindowFlags.NoFocusOnAppearing ) )
{
Focus( nextWindow );
}
ResetNextWindowSettings();
}
private void ResetNextWindowSettings()
{
NextWindowPosition = default;
NextWindowPivot = default;
NextWindowSize = default;
ShouldFocusNextWindow = false;
}
public void EndWindow()
{
// TODO: Move this method in to Window.End()
var popped = WindowStack.Pop();
IdStack.Pop();
popped.OnEnd();
if ( WindowStack.Count > 0 && popped.Id == FocusedWindowId )
{
// TODO: Create a focus stack separate from draw order.
FocusedWindowId = WindowStack.Peek().Id;
}
AddElementRecursive( popped );
}
private void AddElementRecursive( Element element )
{
AddElement( element );
foreach( var child in element.Children )
{
AddElementRecursive( child );
}
}
}