using Avalonia;
using Avalonia.Input;
using Avalonia.VisualTree;
using Irihi.Avalonia.Shared.Helpers;
using Irihi.Avalonia.Shared.Shapes;
using Ursa.Controls.OverlayShared;
using Ursa.Helpers;
namespace Ursa.Controls;
public partial class OverlayDialogHost
{
public Thickness SnapThickness { get; set; } = new(0);
private static void ResetDialogPosition(DialogControlBase control, Size newSize)
{
control.MaxWidth = newSize.Width;
control.MaxHeight = newSize.Height;
if (control.IsFullScreen)
{
control.Width = newSize.Width;
control.Height = newSize.Height;
SetLeft(control, 0);
SetTop(control, 0);
return;
}
var width = newSize.Width - control.Bounds.Width;
var height = newSize.Height - control.Bounds.Height;
var newLeft = width * control.HorizontalOffsetRatio ?? 0;
var newTop = height * control.VerticalOffsetRatio ?? 0;
if (control.ActualHorizontalAnchor == HorizontalPosition.Left) newLeft = 0;
if (control.ActualHorizontalAnchor == HorizontalPosition.Right) newLeft = newSize.Width - control.Bounds.Width;
if (control.ActualVerticalAnchor == VerticalPosition.Top) newTop = 0;
if (control.ActualVerticalAnchor == VerticalPosition.Bottom) newTop = newSize.Height - control.Bounds.Height;
SetLeft(control, Math.Max(0.0, newLeft));
SetTop(control, Math.Max(0.0, newTop));
}
internal void AddDialog(DialogControlBase control)
{
PureRectangle? mask = null;
if (control.CanLightDismiss) mask = CreateOverlayMask(false, control.CanLightDismiss);
if (mask is not null) Children.Add(mask);
Children.Add(control);
_layers.Add(new DialogPair(mask, control, false));
if (control.IsFullScreen)
{
control.Width = Bounds.Width;
control.Height = Bounds.Height;
}
control.MaxWidth = Bounds.Width;
control.MaxHeight = Bounds.Height;
control.Measure(Bounds.Size);
control.Arrange(new Rect(control.DesiredSize));
SetToPosition(control);
control.AddHandler(OverlayFeedbackElement.ClosedEvent, OnDialogControlClosing);
control.AddHandler(DialogControlBase.LayerChangedEvent, OnDialogLayerChanged);
ResetZIndices();
}
private async void OnDialogControlClosing(object? sender, object? e)
{
if (sender is not DialogControlBase control) return;
var layer = _layers.FirstOrDefault(a => a.Element == control);
if (layer is null) return;
_layers.Remove(layer);
control.RemoveHandler(OverlayFeedbackElement.ClosedEvent, OnDialogControlClosing);
control.RemoveHandler(DialogControlBase.LayerChangedEvent, OnDialogLayerChanged);
layer.Mask?.RemoveHandler(PointerPressedEvent, DragMaskToMoveWindow);
Children.Remove(control);
if (layer.Mask is not null)
{
Children.Remove(layer.Mask);
if (layer.Modal)
{
_modalCount--;
IsInModalStatus = _modalCount > 0;
if (!IsAnimationDisabled) await MaskDisappearAnimation.RunAsync(layer.Mask);
}
}
ResetZIndices();
}
///
/// Add a dialog as a modal dialog to the host
///
///
internal void AddModalDialog(DialogControlBase control)
{
var mask = CreateOverlayMask(true, control.CanLightDismiss);
_layers.Add(new DialogPair(mask, control));
control.SetAsModal(true);
ResetZIndices();
Children.Add(mask);
Children.Add(control);
if (control.IsFullScreen)
{
control.Width = Bounds.Width;
control.Height = Bounds.Height;
}
control.MaxWidth = Bounds.Width;
control.MaxHeight = Bounds.Height;
control.Measure(Bounds.Size);
control.Arrange(new Rect(control.DesiredSize));
SetToPosition(control);
control.AddHandler(OverlayFeedbackElement.ClosedEvent, OnDialogControlClosing);
control.AddHandler(DialogControlBase.LayerChangedEvent, OnDialogLayerChanged);
// Notice: mask animation here is not really awaited, because currently dialogs appears immediately.
if (!IsAnimationDisabled) MaskAppearAnimation.RunAsync(mask);
var element = control.GetVisualDescendants().OfType()
.FirstOrDefault(FocusHelper.GetDialogFocusHint);
if (element is null)
{
element = control.GetVisualDescendants().OfType().FirstOrDefault(a => a.Focusable);
}
element?.Focus();
_modalCount++;
IsInModalStatus = _modalCount > 0;
control.IsClosed = false;
// control.Focus();
}
// Handle dialog layer change event
private void OnDialogLayerChanged(object? sender, DialogLayerChangeEventArgs e)
{
if (sender is not DialogControlBase control)
return;
var layer = _layers.FirstOrDefault(a => a.Element == control);
if (layer is null) return;
var index = _layers.IndexOf(layer);
_layers.Remove(layer);
var newIndex = index;
switch (e.ChangeType)
{
case DialogLayerChangeType.BringForward:
newIndex = MathHelpers.SafeClamp(index + 1, 0, _layers.Count);
break;
case DialogLayerChangeType.SendBackward:
newIndex = MathHelpers.SafeClamp(index - 1, 0, _layers.Count);
break;
case DialogLayerChangeType.BringToFront:
newIndex = _layers.Count;
break;
case DialogLayerChangeType.SendToBack:
newIndex = 0;
break;
}
_layers.Insert(newIndex, layer);
ResetZIndices();
}
private void SetToPosition(DialogControlBase? control)
{
if (control is null) return;
var left = GetLeftPosition(control);
var top = GetTopPosition(control);
SetLeft(control, left);
SetTop(control, top);
control.AnchorAndUpdatePositionInfo();
}
private double GetLeftPosition(DialogControlBase control)
{
var offset = Math.Max(0, control.HorizontalOffset ?? 0);
var left = Bounds.Width - control.Bounds.Width;
if (control.HorizontalAnchor == HorizontalPosition.Center)
{
left *= 0.5;
left = MathHelpers.SafeClamp(left, 0, Bounds.Width * 0.5);
}
else if (control.HorizontalAnchor == HorizontalPosition.Left)
{
left = MathHelpers.SafeClamp(left, 0, offset);
}
else if (control.HorizontalAnchor == HorizontalPosition.Right)
{
var leftOffset = Bounds.Width - control.Bounds.Width - offset;
leftOffset = Math.Max(0, leftOffset);
if (control.HorizontalOffset.HasValue) left = MathHelpers.SafeClamp(left, 0, leftOffset);
}
return left;
}
private double GetTopPosition(DialogControlBase control)
{
var offset = Math.Max(0, control.VerticalOffset ?? 0);
var top = Bounds.Height - control.Bounds.Height;
if (control.VerticalAnchor == VerticalPosition.Center)
{
top *= 0.5;
top = MathHelpers.SafeClamp(top, 0, Bounds.Height * 0.5);
}
else if (control.VerticalAnchor == VerticalPosition.Top)
{
top = MathHelpers.SafeClamp(top, 0, offset);
}
else if (control.VerticalAnchor == VerticalPosition.Bottom)
{
var topOffset = Math.Max(0, Bounds.Height - control.Bounds.Height - offset);
top = MathHelpers.SafeClamp(top, 0, topOffset);
}
return top;
}
}