mirror of
https://github.com/AtomUI/AtomUI.git
synced 2026-10-18 22:46:38 +08:00
706 lines
28 KiB
C#
706 lines
28 KiB
C#
using Avalonia;
|
|
using Avalonia.Controls;
|
|
using Avalonia.Controls.Presenters;
|
|
using Avalonia.Media;
|
|
using Avalonia.Styling;
|
|
using Avalonia.Threading;
|
|
using Avalonia.VisualTree;
|
|
using Shouldly;
|
|
using Xunit;
|
|
|
|
namespace AtomUI.Desktop.Controls.Tests.Window;
|
|
|
|
public class WindowTitleBarLayoutPanelTests
|
|
{
|
|
static WindowTitleBarLayoutPanelTests()
|
|
{
|
|
AvaloniaTestApp.EnsureInitialized();
|
|
}
|
|
|
|
[Fact]
|
|
public void WindowCenter_Stays_On_The_Full_Frame_Center_With_Asymmetric_Operations()
|
|
{
|
|
var panel = new Desktop.Controls.WindowTitleBarLayoutPanel
|
|
{
|
|
Padding = new Thickness(10, 0),
|
|
HorizontalSpacing = 8,
|
|
TitleAlignment = Desktop.Controls.WindowTitleBarTitleAlignment.WindowCenter
|
|
};
|
|
var leading = AddChild(panel, Desktop.Controls.WindowTitleBarLayoutRole.Leading, 80, 24);
|
|
var title = AddChild(panel, Desktop.Controls.WindowTitleBarLayoutRole.Title, 100, 24);
|
|
var trailing = AddChild(panel, Desktop.Controls.WindowTitleBarLayoutRole.Trailing, 20, 24);
|
|
|
|
Layout(panel, 400, 40);
|
|
|
|
leading.Bounds.ShouldBe(new Rect(10, 0, 80, 40));
|
|
title.Bounds.ShouldBe(new Rect(150, 0, 100, 40));
|
|
trailing.Bounds.ShouldBe(new Rect(370, 0, 20, 40));
|
|
title.Bounds.Center.X.ShouldBe(200);
|
|
}
|
|
|
|
[Fact]
|
|
public void MacOS_Auto_Uses_WindowCenter_With_Asymmetric_Operations()
|
|
{
|
|
var panel = new Desktop.Controls.WindowTitleBarLayoutPanel
|
|
{
|
|
OsType = OsType.macOS,
|
|
HorizontalSpacing = 8,
|
|
TitleAlignment = Desktop.Controls.WindowTitleBarTitleAlignment.Auto
|
|
};
|
|
AddChild(panel, Desktop.Controls.WindowTitleBarLayoutRole.Leading, 80, 24);
|
|
var title = AddChild(panel, Desktop.Controls.WindowTitleBarLayoutRole.Title, 100, 24);
|
|
AddChild(panel, Desktop.Controls.WindowTitleBarLayoutRole.Trailing, 20, 24);
|
|
|
|
Layout(panel, 400, 40);
|
|
|
|
title.Bounds.ShouldBe(new Rect(150, 0, 100, 40));
|
|
}
|
|
|
|
[Fact]
|
|
public void Native_Inset_Preserves_Padding_And_Empty_Leading_AddOn_Adds_No_Spacing()
|
|
{
|
|
var panel = new Desktop.Controls.WindowTitleBarLayoutPanel
|
|
{
|
|
OsType = OsType.macOS,
|
|
Padding = new Thickness(20, 0),
|
|
NativeChromeInsets = new Thickness(80, 0, 30, 0),
|
|
HorizontalSpacing = 8,
|
|
TitleAlignment = Desktop.Controls.WindowTitleBarTitleAlignment.Left
|
|
};
|
|
var emptyLeading = AddChild(panel, Desktop.Controls.WindowTitleBarLayoutRole.Leading, 0, 24);
|
|
var title = AddChild(panel, Desktop.Controls.WindowTitleBarLayoutRole.Title, 100, 24);
|
|
var trailing = AddChild(panel, Desktop.Controls.WindowTitleBarLayoutRole.Trailing, 40, 24);
|
|
|
|
Layout(panel, 400, 40);
|
|
|
|
emptyLeading.Bounds.Width.ShouldBe(0);
|
|
title.Bounds.ShouldBe(new Rect(100, 0, 100, 40));
|
|
trailing.Bounds.ShouldBe(new Rect(310, 0, 40, 40));
|
|
}
|
|
|
|
[Fact]
|
|
public void MacOS_Native_Inset_Preserves_Content_Padding_Before_NonEmpty_Leading_Operation()
|
|
{
|
|
var panel = new Desktop.Controls.WindowTitleBarLayoutPanel
|
|
{
|
|
OsType = OsType.macOS,
|
|
Padding = new Thickness(20, 0),
|
|
NativeChromeInsets = new Thickness(80, 0, 0, 0),
|
|
HorizontalSpacing = 8,
|
|
TitleAlignment = Desktop.Controls.WindowTitleBarTitleAlignment.WindowCenter
|
|
};
|
|
var leading = AddChild(panel, Desktop.Controls.WindowTitleBarLayoutRole.Leading, 60, 24);
|
|
var title = AddChild(panel, Desktop.Controls.WindowTitleBarLayoutRole.Title, 100, 24);
|
|
|
|
Layout(panel, 600, 40);
|
|
|
|
leading.Bounds.ShouldBe(new Rect(100, 0, 60, 40));
|
|
title.Bounds.Center.X.ShouldBe(300);
|
|
}
|
|
|
|
[Fact]
|
|
public void Infinite_Measure_Includes_Effective_Native_Edge_Insets_In_The_Natural_Width()
|
|
{
|
|
var panel = new Desktop.Controls.WindowTitleBarLayoutPanel
|
|
{
|
|
OsType = OsType.macOS,
|
|
Padding = new Thickness(20, 0, 10, 0),
|
|
NativeChromeInsets = new Thickness(80, 0, 30, 0),
|
|
TitleAlignment = Desktop.Controls.WindowTitleBarTitleAlignment.Left
|
|
};
|
|
AddChild(panel, Desktop.Controls.WindowTitleBarLayoutRole.Title, 100, 24);
|
|
|
|
panel.Measure(Size.Infinity);
|
|
|
|
panel.DesiredSize.ShouldBe(new Size(240, 24));
|
|
}
|
|
|
|
[Fact]
|
|
public void FullScreen_Drops_Reported_Native_Insets_From_The_Panel_Safe_Region()
|
|
{
|
|
var panel = new Desktop.Controls.WindowTitleBarLayoutPanel
|
|
{
|
|
OsType = OsType.macOS,
|
|
IsCsdEnabled = false,
|
|
WindowState = WindowState.FullScreen,
|
|
NativeChromeInsets = new Thickness(80, 0, 0, 0),
|
|
TitleAlignment = Desktop.Controls.WindowTitleBarTitleAlignment.Left
|
|
};
|
|
var title = AddChild(panel, Desktop.Controls.WindowTitleBarLayoutRole.Title, 100, 24);
|
|
|
|
Layout(panel, 400, 40);
|
|
|
|
title.Bounds.ShouldBe(new Rect(0, 0, 100, 40));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(Desktop.Controls.WindowTitleBarTitleAlignment.Left, 98)]
|
|
[InlineData(Desktop.Controls.WindowTitleBarTitleAlignment.Center, 180)]
|
|
[InlineData(Desktop.Controls.WindowTitleBarTitleAlignment.WindowCenter, 150)]
|
|
[InlineData(Desktop.Controls.WindowTitleBarTitleAlignment.Right, 262)]
|
|
public void Explicit_Alignment_Uses_The_Shared_Safe_Region_Formula(
|
|
Desktop.Controls.WindowTitleBarTitleAlignment alignment,
|
|
double expectedX)
|
|
{
|
|
var panel = new Desktop.Controls.WindowTitleBarLayoutPanel
|
|
{
|
|
Padding = new Thickness(10, 0),
|
|
HorizontalSpacing = 8,
|
|
TitleAlignment = alignment
|
|
};
|
|
AddChild(panel, Desktop.Controls.WindowTitleBarLayoutRole.Leading, 80, 24);
|
|
var title = AddChild(panel, Desktop.Controls.WindowTitleBarLayoutRole.Title, 100, 24);
|
|
AddChild(panel, Desktop.Controls.WindowTitleBarLayoutRole.Trailing, 20, 24);
|
|
|
|
Layout(panel, 400, 40);
|
|
|
|
title.Bounds.ShouldBe(new Rect(expectedX, 0, 100, 40));
|
|
}
|
|
|
|
[Fact]
|
|
public void Empty_AddOn_Content_Does_Not_Leave_Ghost_Spacing_Across_Content_Changes()
|
|
{
|
|
var panel = new Desktop.Controls.WindowTitleBarLayoutPanel
|
|
{
|
|
HorizontalSpacing = 8,
|
|
TitleAlignment = Desktop.Controls.WindowTitleBarTitleAlignment.Left
|
|
};
|
|
var leading = new Border();
|
|
Desktop.Controls.WindowTitleBarLayoutPanel.SetRole(
|
|
leading,
|
|
Desktop.Controls.WindowTitleBarLayoutRole.Leading);
|
|
panel.Children.Add(leading);
|
|
var title = AddChild(panel, Desktop.Controls.WindowTitleBarLayoutRole.Title, 100, 24);
|
|
|
|
Layout(panel, 400, 40);
|
|
title.Bounds.X.ShouldBe(0);
|
|
|
|
leading.Child = new FixedSizeControl(30, 24);
|
|
panel.InvalidateMeasure();
|
|
Layout(panel, 400, 40);
|
|
title.Bounds.X.ShouldBe(38);
|
|
|
|
leading.Child = null;
|
|
panel.InvalidateMeasure();
|
|
Layout(panel, 400, 40);
|
|
title.Bounds.X.ShouldBe(0);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(OsType.Windows)]
|
|
[InlineData(OsType.Linux)]
|
|
public void Windows_And_Linux_Templated_AddOn_Content_Changes_Recalculate_The_Shared_Title_Formula(
|
|
OsType osType)
|
|
{
|
|
var leftAddOn = new Border();
|
|
var rightAddOn = new Border();
|
|
var titleBar = new Desktop.Controls.WindowTitleBar
|
|
{
|
|
Width = 400,
|
|
Height = 40,
|
|
LeftAddOn = leftAddOn,
|
|
RightAddOn = rightAddOn,
|
|
Title = "Dynamic add-on",
|
|
TitleAlignment = Desktop.Controls.WindowTitleBarTitleAlignment.Left
|
|
};
|
|
titleBar.SetValue(Desktop.Controls.WindowTitleBar.OsTypeProperty, osType);
|
|
Application.Current!.TryFindResource(typeof(Desktop.Controls.WindowTitleBar), out var resource)
|
|
.ShouldBeTrue();
|
|
titleBar.Theme = resource.ShouldBeAssignableTo<ControlTheme>();
|
|
|
|
var host = new Avalonia.Controls.Window
|
|
{
|
|
Width = 400,
|
|
Height = 100,
|
|
Content = titleBar
|
|
};
|
|
|
|
try
|
|
{
|
|
host.Show();
|
|
titleBar.ApplyTemplate();
|
|
host.UpdateLayout();
|
|
|
|
var panel = titleBar.GetVisualDescendants()
|
|
.OfType<Desktop.Controls.WindowTitleBarLayoutPanel>()
|
|
.Single();
|
|
var title = panel.Children.Single(child =>
|
|
Desktop.Controls.WindowTitleBarLayoutPanel.GetRole(child) ==
|
|
Desktop.Controls.WindowTitleBarLayoutRole.Title);
|
|
AssertTitleMatchesCurrentLayout(titleBar, panel, title);
|
|
var emptyX = title.Bounds.X;
|
|
|
|
leftAddOn.Child = new Border { Width = 30, Height = 24 };
|
|
Dispatcher.UIThread.RunJobs();
|
|
host.UpdateLayout();
|
|
AssertTitleMatchesCurrentLayout(titleBar, panel, title);
|
|
title.Bounds.X.ShouldBeGreaterThan(emptyX);
|
|
|
|
leftAddOn.Child = null;
|
|
Dispatcher.UIThread.RunJobs();
|
|
host.UpdateLayout();
|
|
AssertTitleMatchesCurrentLayout(titleBar, panel, title);
|
|
title.Bounds.X.ShouldBe(emptyX);
|
|
|
|
titleBar.TitleAlignment = Desktop.Controls.WindowTitleBarTitleAlignment.Right;
|
|
Dispatcher.UIThread.RunJobs();
|
|
host.UpdateLayout();
|
|
AssertTitleMatchesCurrentLayout(titleBar, panel, title);
|
|
var emptyRightX = title.Bounds.X;
|
|
|
|
rightAddOn.Child = new Border { Width = 30, Height = 24 };
|
|
Dispatcher.UIThread.RunJobs();
|
|
host.UpdateLayout();
|
|
AssertTitleMatchesCurrentLayout(titleBar, panel, title);
|
|
title.Bounds.X.ShouldBeLessThan(emptyRightX);
|
|
|
|
rightAddOn.Child = null;
|
|
Dispatcher.UIThread.RunJobs();
|
|
host.UpdateLayout();
|
|
AssertTitleMatchesCurrentLayout(titleBar, panel, title);
|
|
title.Bounds.X.ShouldBe(emptyRightX);
|
|
}
|
|
finally
|
|
{
|
|
host.Close();
|
|
}
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(OsType.Linux)]
|
|
[InlineData(OsType.Windows)]
|
|
[InlineData(OsType.macOS)]
|
|
public void String_Title_Is_Constrained_To_The_Safe_Region_With_Character_Ellipsis(OsType osType)
|
|
{
|
|
var titleBar = new Desktop.Controls.WindowTitleBar
|
|
{
|
|
Width = 400,
|
|
Height = 40,
|
|
LeftAddOn = new Border { Width = 160, Height = 24 },
|
|
RightAddOn = new Border { Width = 48, Height = 24 },
|
|
Title = "TitleAlignment: WindowCenter",
|
|
TitleAlignment = Desktop.Controls.WindowTitleBarTitleAlignment.WindowCenter,
|
|
LogoVisibility = Desktop.Controls.WindowTitleBarLogoVisibility.Never
|
|
};
|
|
titleBar.SetValue(Desktop.Controls.WindowTitleBar.OsTypeProperty, osType);
|
|
Application.Current!.TryFindResource(typeof(Desktop.Controls.WindowTitleBar), out var resource)
|
|
.ShouldBeTrue();
|
|
titleBar.Theme = resource.ShouldBeAssignableTo<ControlTheme>();
|
|
|
|
var host = new Avalonia.Controls.Window
|
|
{
|
|
Width = 400,
|
|
Height = 100,
|
|
Content = titleBar
|
|
};
|
|
|
|
try
|
|
{
|
|
host.Show();
|
|
titleBar.ApplyTemplate();
|
|
host.UpdateLayout();
|
|
|
|
var titleLayout = titleBar.GetVisualDescendants()
|
|
.OfType<Desktop.Controls.WindowTitleBarLayoutPanel>()
|
|
.Single()
|
|
.Children
|
|
.Single(child =>
|
|
Desktop.Controls.WindowTitleBarLayoutPanel.GetRole(child) ==
|
|
Desktop.Controls.WindowTitleBarLayoutRole.Title);
|
|
var titlePresenter = titleBar.GetVisualDescendants()
|
|
.OfType<ContentPresenter>()
|
|
.Single(presenter => presenter.Name == "PART_ContentPresenter");
|
|
|
|
titlePresenter.Bounds.Width.ShouldBeLessThanOrEqualTo(titleLayout.Bounds.Width);
|
|
titlePresenter.TextTrimming.ShouldBe(TextTrimming.CharacterEllipsis);
|
|
}
|
|
finally
|
|
{
|
|
host.Close();
|
|
}
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(OsType.Windows)]
|
|
[InlineData(OsType.Linux)]
|
|
public void Windows_And_Linux_Templates_Place_Logo_At_The_Physical_Left_Edge_Before_Left_AddOn(
|
|
OsType osType)
|
|
{
|
|
var titleBar = new Desktop.Controls.WindowTitleBar
|
|
{
|
|
Width = 400,
|
|
Height = 40,
|
|
Logo = new Border { Width = 16, Height = 16 },
|
|
LeftAddOn = new Border { Width = 120, Height = 24 },
|
|
TitleAlignment = Desktop.Controls.WindowTitleBarTitleAlignment.Left,
|
|
LogoVisibility = Desktop.Controls.WindowTitleBarLogoVisibility.Always
|
|
};
|
|
titleBar.SetValue(Desktop.Controls.WindowTitleBar.OsTypeProperty, osType);
|
|
Application.Current!.TryFindResource(typeof(Desktop.Controls.WindowTitleBar), out var resource)
|
|
.ShouldBeTrue();
|
|
titleBar.Theme = resource.ShouldBeAssignableTo<ControlTheme>();
|
|
|
|
var host = new Avalonia.Controls.Window
|
|
{
|
|
Width = 400,
|
|
Height = 100,
|
|
Content = titleBar
|
|
};
|
|
|
|
try
|
|
{
|
|
host.Show();
|
|
titleBar.ApplyTemplate();
|
|
host.UpdateLayout();
|
|
|
|
var logoPresenter = titleBar.GetVisualDescendants()
|
|
.OfType<ContentPresenter>()
|
|
.Single(presenter => presenter.Name == "PART_Logo");
|
|
var leftAddOnPresenter = titleBar.GetVisualDescendants()
|
|
.OfType<ContentPresenter>()
|
|
.Single(presenter => presenter.Name == "PART_LeftAddOn");
|
|
var leadingHost = logoPresenter.GetVisualParent().ShouldBeAssignableTo<Control>()!;
|
|
|
|
leftAddOnPresenter.GetVisualParent().ShouldBe(leadingHost);
|
|
Desktop.Controls.WindowTitleBarLayoutPanel.GetRole(leadingHost)
|
|
.ShouldBe(Desktop.Controls.WindowTitleBarLayoutRole.Leading);
|
|
logoPresenter.Bounds.X.ShouldBeLessThan(leftAddOnPresenter.Bounds.X);
|
|
logoPresenter.Bounds.X.ShouldBe(0, 0.5);
|
|
(leftAddOnPresenter.Bounds.X - logoPresenter.Bounds.Right).ShouldBe(4, 0.5);
|
|
}
|
|
finally
|
|
{
|
|
host.Close();
|
|
}
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(OsType.Windows)]
|
|
[InlineData(OsType.Linux)]
|
|
public void Windows_And_Linux_Logo_Left_AddOn_Spacing_Tracks_Presenter_Visibility(
|
|
OsType osType)
|
|
{
|
|
var titleBar = new Desktop.Controls.WindowTitleBar
|
|
{
|
|
Width = 400,
|
|
Height = 40,
|
|
Logo = new Border { Width = 16, Height = 16 },
|
|
LeftAddOn = new Border { Width = 120, Height = 24 },
|
|
TitleAlignment = Desktop.Controls.WindowTitleBarTitleAlignment.Left,
|
|
LogoVisibility = Desktop.Controls.WindowTitleBarLogoVisibility.Always
|
|
};
|
|
titleBar.SetValue(Desktop.Controls.WindowTitleBar.OsTypeProperty, osType);
|
|
Application.Current!.TryFindResource(typeof(Desktop.Controls.WindowTitleBar), out var resource)
|
|
.ShouldBeTrue();
|
|
titleBar.Theme = resource.ShouldBeAssignableTo<ControlTheme>();
|
|
|
|
var host = new Avalonia.Controls.Window
|
|
{
|
|
Width = 400,
|
|
Height = 100,
|
|
Content = titleBar
|
|
};
|
|
|
|
try
|
|
{
|
|
host.Show();
|
|
titleBar.ApplyTemplate();
|
|
host.UpdateLayout();
|
|
|
|
var logoPresenter = titleBar.GetVisualDescendants()
|
|
.OfType<ContentPresenter>()
|
|
.Single(presenter => presenter.Name == "PART_Logo");
|
|
var leftAddOnPresenter = titleBar.GetVisualDescendants()
|
|
.OfType<ContentPresenter>()
|
|
.Single(presenter => presenter.Name == "PART_LeftAddOn");
|
|
var leadingHost = logoPresenter.GetVisualParent().ShouldBeAssignableTo<Control>()!;
|
|
|
|
(leftAddOnPresenter.Bounds.X - logoPresenter.Bounds.Right).ShouldBe(4, 0.5);
|
|
|
|
titleBar.LogoVisibility = Desktop.Controls.WindowTitleBarLogoVisibility.Never;
|
|
Dispatcher.UIThread.RunJobs();
|
|
host.UpdateLayout();
|
|
|
|
logoPresenter.IsVisible.ShouldBeFalse();
|
|
leftAddOnPresenter.Bounds.X.ShouldBe(0, 0.5);
|
|
|
|
titleBar.LogoVisibility = Desktop.Controls.WindowTitleBarLogoVisibility.Always;
|
|
titleBar.LeftAddOn = null;
|
|
Dispatcher.UIThread.RunJobs();
|
|
host.UpdateLayout();
|
|
|
|
logoPresenter.IsVisible.ShouldBeTrue();
|
|
leftAddOnPresenter.IsVisible.ShouldBeFalse();
|
|
leadingHost.Bounds.Width.ShouldBe(logoPresenter.Bounds.Width, 0.5);
|
|
}
|
|
finally
|
|
{
|
|
host.Close();
|
|
}
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(OsType.Windows, Desktop.Controls.WindowTitleBarTitleAlignment.Left)]
|
|
[InlineData(OsType.Windows, Desktop.Controls.WindowTitleBarTitleAlignment.Center)]
|
|
[InlineData(OsType.Windows, Desktop.Controls.WindowTitleBarTitleAlignment.WindowCenter)]
|
|
[InlineData(OsType.Windows, Desktop.Controls.WindowTitleBarTitleAlignment.Right)]
|
|
[InlineData(OsType.Linux, Desktop.Controls.WindowTitleBarTitleAlignment.Left)]
|
|
[InlineData(OsType.Linux, Desktop.Controls.WindowTitleBarTitleAlignment.Center)]
|
|
[InlineData(OsType.Linux, Desktop.Controls.WindowTitleBarTitleAlignment.WindowCenter)]
|
|
[InlineData(OsType.Linux, Desktop.Controls.WindowTitleBarTitleAlignment.Right)]
|
|
public void Windows_And_Linux_Template_Title_Alignment_Still_Uses_The_Shared_Safe_Region_Formula(
|
|
OsType osType,
|
|
Desktop.Controls.WindowTitleBarTitleAlignment alignment)
|
|
{
|
|
var titleBar = new Desktop.Controls.WindowTitleBar
|
|
{
|
|
Width = 800,
|
|
Height = 40,
|
|
Logo = new Border { Width = 16, Height = 16 },
|
|
LeftAddOn = new Border { Width = 64, Height = 24 },
|
|
RightAddOn = new Border { Width = 48, Height = 24 },
|
|
Title = new Border { Width = 100, Height = 24 },
|
|
TitleAlignment = alignment,
|
|
LogoVisibility = Desktop.Controls.WindowTitleBarLogoVisibility.Always
|
|
};
|
|
titleBar.SetValue(Desktop.Controls.WindowTitleBar.OsTypeProperty, osType);
|
|
Application.Current!.TryFindResource(typeof(Desktop.Controls.WindowTitleBar), out var resource)
|
|
.ShouldBeTrue();
|
|
titleBar.Theme = resource.ShouldBeAssignableTo<ControlTheme>();
|
|
|
|
var host = new Avalonia.Controls.Window
|
|
{
|
|
Width = 800,
|
|
Height = 100,
|
|
Content = titleBar
|
|
};
|
|
|
|
try
|
|
{
|
|
host.Show();
|
|
titleBar.ApplyTemplate();
|
|
host.UpdateLayout();
|
|
|
|
var panel = titleBar.GetVisualDescendants()
|
|
.OfType<Desktop.Controls.WindowTitleBarLayoutPanel>()
|
|
.Single();
|
|
var leading = FindRoleChild(panel, Desktop.Controls.WindowTitleBarLayoutRole.Leading);
|
|
var title = FindRoleChild(panel, Desktop.Controls.WindowTitleBarLayoutRole.Title);
|
|
var trailing = FindRoleChild(panel, Desktop.Controls.WindowTitleBarLayoutRole.Trailing);
|
|
|
|
var expectedX = CalculateExpectedTitleX(
|
|
titleBar.Width,
|
|
panel,
|
|
leading.Bounds.Width,
|
|
title.Bounds.Width,
|
|
trailing.Bounds.Width,
|
|
alignment);
|
|
|
|
title.Bounds.X.ShouldBe(expectedX, 0.5);
|
|
if (alignment == Desktop.Controls.WindowTitleBarTitleAlignment.WindowCenter)
|
|
{
|
|
title.Bounds.Center.X.ShouldBe(titleBar.Width / 2, 0.5);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
host.Close();
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Hidden_AddOn_Does_Not_Occupy_The_Title_Safe_Region()
|
|
{
|
|
var panel = new Desktop.Controls.WindowTitleBarLayoutPanel
|
|
{
|
|
HorizontalSpacing = 8,
|
|
TitleAlignment = Desktop.Controls.WindowTitleBarTitleAlignment.Left
|
|
};
|
|
var leading = AddChild(panel, Desktop.Controls.WindowTitleBarLayoutRole.Leading, 40, 24);
|
|
leading.IsVisible = false;
|
|
var title = AddChild(panel, Desktop.Controls.WindowTitleBarLayoutRole.Title, 100, 24);
|
|
|
|
Layout(panel, 400, 40);
|
|
|
|
title.Bounds.X.ShouldBe(0);
|
|
}
|
|
|
|
[Fact]
|
|
public void AddOn_Margin_Is_Measured_Once_Before_The_Conditional_Spacing()
|
|
{
|
|
var panel = new Desktop.Controls.WindowTitleBarLayoutPanel
|
|
{
|
|
HorizontalSpacing = 8,
|
|
TitleAlignment = Desktop.Controls.WindowTitleBarTitleAlignment.Left
|
|
};
|
|
var leading = AddChild(panel, Desktop.Controls.WindowTitleBarLayoutRole.Leading, 20, 24);
|
|
leading.Margin = new Thickness(5, 0);
|
|
var title = AddChild(panel, Desktop.Controls.WindowTitleBarLayoutRole.Title, 100, 24);
|
|
|
|
Layout(panel, 400, 40);
|
|
|
|
leading.DesiredSize.Width.ShouldBe(30);
|
|
title.Bounds.X.ShouldBe(38);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(Desktop.Controls.WindowTitleBarTitleAlignment.Left)]
|
|
[InlineData(Desktop.Controls.WindowTitleBarTitleAlignment.Center)]
|
|
[InlineData(Desktop.Controls.WindowTitleBarTitleAlignment.WindowCenter)]
|
|
[InlineData(Desktop.Controls.WindowTitleBarTitleAlignment.Right)]
|
|
public void Intersecting_Operation_Regions_Collapse_The_Title_First(
|
|
Desktop.Controls.WindowTitleBarTitleAlignment alignment)
|
|
{
|
|
var panel = new Desktop.Controls.WindowTitleBarLayoutPanel
|
|
{
|
|
HorizontalSpacing = 8,
|
|
TitleAlignment = alignment
|
|
};
|
|
AddChild(panel, Desktop.Controls.WindowTitleBarLayoutRole.Leading, 60, 24);
|
|
var title = AddChild(panel, Desktop.Controls.WindowTitleBarLayoutRole.Title, 100, 24);
|
|
AddChild(panel, Desktop.Controls.WindowTitleBarLayoutRole.Trailing, 60, 24);
|
|
|
|
Layout(panel, 100, 40);
|
|
|
|
title.Bounds.Width.ShouldBe(0);
|
|
}
|
|
|
|
[Fact]
|
|
public void Invalid_And_Negative_Metrics_Normalize_To_Zero()
|
|
{
|
|
var panel = new Desktop.Controls.WindowTitleBarLayoutPanel
|
|
{
|
|
Padding = new Thickness(double.NaN, 0, -10, 0),
|
|
NativeChromeInsets = new Thickness(double.PositiveInfinity, 0, double.NegativeInfinity, 0),
|
|
HorizontalSpacing = double.NaN,
|
|
TitleAlignment = Desktop.Controls.WindowTitleBarTitleAlignment.Left
|
|
};
|
|
var title = AddChild(panel, Desktop.Controls.WindowTitleBarLayoutRole.Title, 100, 24);
|
|
|
|
Layout(panel, 400, 40);
|
|
|
|
title.Bounds.ShouldBe(new Rect(0, 0, 100, 40));
|
|
}
|
|
|
|
[Fact]
|
|
public void RightToLeft_Does_Not_Swap_The_Physical_Left_And_Right_Contracts()
|
|
{
|
|
var panel = new Desktop.Controls.WindowTitleBarLayoutPanel
|
|
{
|
|
FlowDirection = FlowDirection.RightToLeft,
|
|
HorizontalSpacing = 8,
|
|
TitleAlignment = Desktop.Controls.WindowTitleBarTitleAlignment.Left
|
|
};
|
|
AddChild(panel, Desktop.Controls.WindowTitleBarLayoutRole.Leading, 40, 24);
|
|
var title = AddChild(panel, Desktop.Controls.WindowTitleBarLayoutRole.Title, 100, 24);
|
|
AddChild(panel, Desktop.Controls.WindowTitleBarLayoutRole.Trailing, 20, 24);
|
|
|
|
Layout(panel, 400, 40);
|
|
|
|
title.Bounds.X.ShouldBe(48);
|
|
}
|
|
|
|
private static FixedSizeControl AddChild(
|
|
Desktop.Controls.WindowTitleBarLayoutPanel panel,
|
|
Desktop.Controls.WindowTitleBarLayoutRole role,
|
|
double width,
|
|
double height)
|
|
{
|
|
var child = new FixedSizeControl(width, height);
|
|
Desktop.Controls.WindowTitleBarLayoutPanel.SetRole(child, role);
|
|
panel.Children.Add(child);
|
|
return child;
|
|
}
|
|
|
|
private static Control FindRoleChild(
|
|
Desktop.Controls.WindowTitleBarLayoutPanel panel,
|
|
Desktop.Controls.WindowTitleBarLayoutRole role)
|
|
{
|
|
return panel.Children.Single(child =>
|
|
Desktop.Controls.WindowTitleBarLayoutPanel.GetRole(child) == role);
|
|
}
|
|
|
|
private static void AssertTitleMatchesCurrentLayout(
|
|
Desktop.Controls.WindowTitleBar titleBar,
|
|
Desktop.Controls.WindowTitleBarLayoutPanel panel,
|
|
Control title)
|
|
{
|
|
var leading = FindRoleChild(panel, Desktop.Controls.WindowTitleBarLayoutRole.Leading);
|
|
var trailing = FindRoleChild(panel, Desktop.Controls.WindowTitleBarLayoutRole.Trailing);
|
|
var expectedX = CalculateExpectedTitleX(
|
|
titleBar.Width,
|
|
panel,
|
|
leading.Bounds.Width,
|
|
title.Bounds.Width,
|
|
trailing.Bounds.Width,
|
|
titleBar.TitleAlignment);
|
|
|
|
title.Bounds.X.ShouldBe(expectedX, 0.5);
|
|
}
|
|
|
|
private static double CalculateExpectedTitleX(
|
|
double width,
|
|
Desktop.Controls.WindowTitleBarLayoutPanel panel,
|
|
double leadingWidth,
|
|
double titleWidth,
|
|
double trailingWidth,
|
|
Desktop.Controls.WindowTitleBarTitleAlignment alignment)
|
|
{
|
|
var effectiveAlignment = alignment == Desktop.Controls.WindowTitleBarTitleAlignment.Auto
|
|
? Desktop.Controls.WindowTitleBarLayoutStrategies.Get(panel.OsType).AutoAlignment
|
|
: alignment;
|
|
var nativeChromeInsets = Desktop.Controls.WindowTitleBarLayoutStrategies.Get(panel.OsType)
|
|
.ResolveNativeChromeInsets(
|
|
width,
|
|
panel.NativeChromeInsets,
|
|
panel.IsCsdEnabled,
|
|
panel.WindowState);
|
|
var leftBoundary = Math.Clamp(
|
|
Normalize(nativeChromeInsets.Left) + Normalize(panel.Padding.Left),
|
|
0,
|
|
width);
|
|
var rightBoundary = Math.Clamp(
|
|
width - Normalize(nativeChromeInsets.Right) - Normalize(panel.Padding.Right),
|
|
0,
|
|
width);
|
|
var horizontalSpacing = Normalize(panel.HorizontalSpacing);
|
|
var leadingOccupation = leadingWidth > 0 ? leadingWidth + horizontalSpacing : 0;
|
|
var trailingOccupation = trailingWidth > 0 ? trailingWidth + horizontalSpacing : 0;
|
|
var left = Math.Clamp(leftBoundary + leadingOccupation, 0, width);
|
|
var right = Math.Clamp(rightBoundary - trailingOccupation, 0, width);
|
|
var availableWidth = Math.Max(0, right - left);
|
|
var effectiveWidth = Math.Min(titleWidth, availableWidth);
|
|
|
|
return effectiveAlignment switch
|
|
{
|
|
Desktop.Controls.WindowTitleBarTitleAlignment.Center =>
|
|
left + (availableWidth - effectiveWidth) / 2,
|
|
Desktop.Controls.WindowTitleBarTitleAlignment.WindowCenter =>
|
|
width / 2 - Math.Min(
|
|
titleWidth,
|
|
2 * Math.Max(0, Math.Min(width / 2 - left, right - width / 2))) / 2,
|
|
Desktop.Controls.WindowTitleBarTitleAlignment.Right => right - effectiveWidth,
|
|
_ => left
|
|
};
|
|
}
|
|
|
|
private static double Normalize(double value)
|
|
{
|
|
return double.IsFinite(value) && value > 0 ? value : 0;
|
|
}
|
|
|
|
private static void Layout(Control control, double width, double height)
|
|
{
|
|
control.Measure(new Size(width, height));
|
|
control.Arrange(new Rect(0, 0, width, height));
|
|
}
|
|
|
|
private sealed class FixedSizeControl(double width, double height) : Control
|
|
{
|
|
protected override Size MeasureOverride(Size availableSize)
|
|
{
|
|
return new Size(Math.Min(width, availableSize.Width), Math.Min(height, availableSize.Height));
|
|
}
|
|
}
|
|
}
|