// This source file is adapted from the WinUI project.
// (https://github.com/microsoft/microsoft-ui-xaml)
//
// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
using System;
namespace Avalonia.Layout
{
///
/// Represents the base class for an object that sizes and arranges child elements for a host.
///
public abstract class AttachedLayout : AvaloniaObject
{
internal string LayoutId { get; set; }
///
/// Occurs when the measurement state (layout) has been invalidated.
///
public event EventHandler MeasureInvalidated;
///
/// Occurs when the arrange state (layout) has been invalidated.
///
public event EventHandler ArrangeInvalidated;
///
/// Initializes any per-container state the layout requires when it is attached to an
/// container.
///
///
/// The context object that facilitates communication between the layout and its host
/// container.
///
///
/// Container elements that support attached layouts should call this method when a layout
/// instance is first assigned. The container is expected to give the attached layout
/// instance a way to store and retrieve any per-container state by way of the provided
/// context. It is also the responsibility of the container to not reuse the context, or
/// otherwise expose the state from one layout to another.
///
/// When an attached layout is removed the container should release any reference to the
/// layout state it stored.
///
/// Override or
/// to provide the behavior for
/// this method in a derived class.
///
public void InitializeForContext(LayoutContext context)
{
if (this is VirtualizingLayout virtualizingLayout)
{
var virtualizingContext = GetVirtualizingLayoutContext(context);
virtualizingLayout.InitializeForContextCore(virtualizingContext);
}
else if (this is NonVirtualizingLayout nonVirtualizingLayout)
{
var nonVirtualizingContext = GetNonVirtualizingLayoutContext(context);
nonVirtualizingLayout.InitializeForContextCore(nonVirtualizingContext);
}
else
{
throw new NotSupportedException();
}
}
///
/// Removes any state the layout previously stored on the ILayoutable container.
///
///
/// The context object that facilitates communication between the layout and its host
/// container.
///
public void UninitializeForContext(LayoutContext context)
{
if (this is VirtualizingLayout virtualizingLayout)
{
var virtualizingContext = GetVirtualizingLayoutContext(context);
virtualizingLayout.UninitializeForContextCore(virtualizingContext);
}
else if (this is NonVirtualizingLayout nonVirtualizingLayout)
{
var nonVirtualizingContext = GetNonVirtualizingLayoutContext(context);
nonVirtualizingLayout.UninitializeForContextCore(nonVirtualizingContext);
}
else
{
throw new NotSupportedException();
}
}
///
/// Suggests a DesiredSize for a container element. A container element that supports
/// attached layouts should call this method from their own MeasureOverride implementations
/// to form a recursive layout update. The attached layout is expected to call the Measure
/// for each of the container’s ILayoutable children.
///
///
/// The context object that facilitates communication between the layout and its host
/// container.
///
///
/// The available space that a container can allocate to a child object. A child object can
/// request a larger space than what is available; the provided size might be accommodated
/// if scrolling or other resize behavior is possible in that particular container.
///
///
public Size Measure(LayoutContext context, Size availableSize)
{
if (this is VirtualizingLayout virtualizingLayout)
{
var virtualizingContext = GetVirtualizingLayoutContext(context);
return virtualizingLayout.MeasureOverride(virtualizingContext, availableSize);
}
else if (this is NonVirtualizingLayout nonVirtualizingLayout)
{
var nonVirtualizingContext = GetNonVirtualizingLayoutContext(context);
return nonVirtualizingLayout.MeasureOverride(nonVirtualizingContext, availableSize);
}
else
{
throw new NotSupportedException();
}
}
///
/// Positions child elements and determines a size for a container UIElement. Container
/// elements that support attached layouts should call this method from their layout
/// override implementations to form a recursive layout update.
///
///
/// The context object that facilitates communication between the layout and its host
/// container.
///
///
/// The final size that the container computes for the child in layout.
///
/// The actual size that is used after the element is arranged in layout.
public Size Arrange(LayoutContext context, Size finalSize)
{
if (this is VirtualizingLayout virtualizingLayout)
{
var virtualizingContext = GetVirtualizingLayoutContext(context);
return virtualizingLayout.ArrangeOverride(virtualizingContext, finalSize);
}
else if (this is NonVirtualizingLayout nonVirtualizingLayout)
{
var nonVirtualizingContext = GetNonVirtualizingLayoutContext(context);
return nonVirtualizingLayout.ArrangeOverride(nonVirtualizingContext, finalSize);
}
else
{
throw new NotSupportedException();
}
}
///
/// Invalidates the measurement state (layout) for all ILayoutable containers that reference
/// this layout.
///
protected void InvalidateMeasure() => MeasureInvalidated?.Invoke(this, EventArgs.Empty);
///
/// Invalidates the arrange state (layout) for all UIElement containers that reference this
/// layout. After the invalidation, the UIElement will have its layout updated, which
/// occurs asynchronously.
///
protected void InvalidateArrange() => ArrangeInvalidated?.Invoke(this, EventArgs.Empty);
private VirtualizingLayoutContext GetVirtualizingLayoutContext(LayoutContext context)
{
if (context is VirtualizingLayoutContext virtualizingContext)
{
return virtualizingContext;
}
else if (context is NonVirtualizingLayoutContext nonVirtualizingContext)
{
return nonVirtualizingContext.GetVirtualizingContextAdapter();
}
else
{
throw new NotSupportedException();
}
}
private NonVirtualizingLayoutContext GetNonVirtualizingLayoutContext(LayoutContext context)
{
if (context is NonVirtualizingLayoutContext nonVirtualizingContext)
{
return nonVirtualizingContext;
}
else if (context is VirtualizingLayoutContext virtualizingContext)
{
return virtualizingContext.GetNonVirtualizingContextAdapter();
}
else
{
throw new NotSupportedException();
}
}
}
}