mirror of
https://github.com/AtomUI/AtomUI.git
synced 2026-10-29 07:16:33 +08:00
fix(ImagePreviewer): route title bar toolbar requests from CSD overlay
This commit is contained in:
@@ -2,6 +2,8 @@ using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.LogicalTree;
|
||||
using Avalonia.VisualTree;
|
||||
|
||||
namespace AtomUI.Desktop.Controls;
|
||||
|
||||
@@ -300,7 +302,7 @@ internal class ImagePreviewBaseToolbar : TemplatedControl
|
||||
}
|
||||
else if (sender == _fitToWindowButton)
|
||||
{
|
||||
RaiseEvent(new ImageFitToWindowEventArgs(_fitToWindowButton?.IsChecked == true)
|
||||
RaiseToolbarEvent(new ImageFitToWindowEventArgs(_fitToWindowButton?.IsChecked == true)
|
||||
{
|
||||
RoutedEvent = FitToWindowRequestEvent
|
||||
});
|
||||
@@ -309,9 +311,33 @@ internal class ImagePreviewBaseToolbar : TemplatedControl
|
||||
|
||||
private void RaiseRequestEvent(RoutedEvent<ImagePreviewToolbarRequestEventArgs> routedEvent)
|
||||
{
|
||||
RaiseEvent(new ImagePreviewToolbarRequestEventArgs(ToolbarSource)
|
||||
RaiseToolbarEvent(new ImagePreviewToolbarRequestEventArgs(ToolbarSource)
|
||||
{
|
||||
RoutedEvent = routedEvent
|
||||
});
|
||||
}
|
||||
|
||||
private void RaiseToolbarEvent(RoutedEventArgs args)
|
||||
{
|
||||
if (ResolveCsdTitleBarHostWindow() is { } hostWindow)
|
||||
{
|
||||
hostWindow.RaiseRoutedEventFromOverlay(this, args);
|
||||
return;
|
||||
}
|
||||
|
||||
RaiseEvent(args);
|
||||
}
|
||||
|
||||
private Window? ResolveCsdTitleBarHostWindow()
|
||||
{
|
||||
if (ToolbarSource != ImagePreviewToolbarSource.TitleBar)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var titleBar = this.FindLogicalAncestorOfType<WindowTitleBar>() ??
|
||||
this.FindAncestorOfType<WindowTitleBar>();
|
||||
var hostWindow = titleBar?.HostWindow;
|
||||
return hostWindow is { IsCsdEnabled: true } ? hostWindow : null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -904,100 +904,6 @@ public class WindowDialogPresenterTests
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Native_User_Client_Size_Changes_Update_The_Window_Dialog_Normal_Size()
|
||||
{
|
||||
var owner = new AtomUI.Desktop.Controls.Window
|
||||
{
|
||||
Width = 800,
|
||||
Height = 600
|
||||
};
|
||||
var dialog = new AtomUI.Desktop.Controls.Dialog
|
||||
{
|
||||
IsModal = false,
|
||||
IsMotionEnabled = false,
|
||||
IsResizable = true,
|
||||
HostWidth = 360,
|
||||
HostHeight = 220
|
||||
};
|
||||
var presenter = new WindowDialogPresenter(dialog, owner);
|
||||
|
||||
try
|
||||
{
|
||||
owner.Show();
|
||||
WaitWithDispatcherPump(presenter.ShowAsync(CancellationToken.None).AsTask());
|
||||
var surface = GetSurface(presenter);
|
||||
var chrome = GetWindowChromeSize(presenter.HostWindow);
|
||||
var platformSurfaceSize = new Size(500, 280);
|
||||
|
||||
presenter.HostWindow.BeginNativeUserResize();
|
||||
presenter.HostWindow.SetPlatformChromeClientSize(platformSurfaceSize + chrome);
|
||||
Dispatcher.UIThread.RunJobs();
|
||||
presenter.HostWindow.CompleteNativeUserResize();
|
||||
Dispatcher.UIThread.RunJobs();
|
||||
|
||||
surface.Bounds.Size.ShouldBe(platformSurfaceSize);
|
||||
|
||||
presenter.HostWindow.Padding = new Thickness(3, 4, 5, 6);
|
||||
Dispatcher.UIThread.RunJobs();
|
||||
|
||||
var updatedChrome = GetWindowChromeSize(presenter.HostWindow);
|
||||
surface.Bounds.Size.ShouldBe(platformSurfaceSize);
|
||||
presenter.HostWindow.ClientSize.ShouldBe(platformSurfaceSize + updatedChrome);
|
||||
}
|
||||
finally
|
||||
{
|
||||
WaitWithDispatcherPump(presenter.CloseAsync().AsTask());
|
||||
WaitWithDispatcherPump(presenter.DisposeAsync().AsTask());
|
||||
owner.Close();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Sizing_Refresh_Does_Not_Treat_Stale_Client_Size_As_User_Resize()
|
||||
{
|
||||
var owner = new AtomUI.Desktop.Controls.Window
|
||||
{
|
||||
Width = 800,
|
||||
Height = 600
|
||||
};
|
||||
var dialog = new AtomUI.Desktop.Controls.Dialog
|
||||
{
|
||||
IsModal = false,
|
||||
IsMotionEnabled = false,
|
||||
IsResizable = true,
|
||||
HostWidth = 360,
|
||||
HostHeight = 220
|
||||
};
|
||||
var presenter = new WindowDialogPresenter(dialog, owner);
|
||||
|
||||
try
|
||||
{
|
||||
owner.Show();
|
||||
WaitWithDispatcherPump(presenter.ShowAsync(CancellationToken.None).AsTask());
|
||||
var surface = GetSurface(presenter);
|
||||
var normalSurfaceSize = surface.Bounds.Size;
|
||||
var chrome = GetWindowChromeSize(presenter.HostWindow);
|
||||
var staleSurfaceSize = normalSurfaceSize + new Size(120, 80);
|
||||
|
||||
presenter.HostWindow.SetPlatformChromeClientSize(staleSurfaceSize + chrome);
|
||||
Dispatcher.UIThread.RunJobs();
|
||||
|
||||
presenter.HostWindow.Padding = new Thickness(3, 4, 5, 6);
|
||||
Dispatcher.UIThread.RunJobs();
|
||||
|
||||
var updatedChrome = GetWindowChromeSize(presenter.HostWindow);
|
||||
surface.Bounds.Size.ShouldBe(normalSurfaceSize);
|
||||
presenter.HostWindow.ClientSize.ShouldBe(normalSurfaceSize + updatedChrome);
|
||||
}
|
||||
finally
|
||||
{
|
||||
WaitWithDispatcherPump(presenter.CloseAsync().AsTask());
|
||||
WaitWithDispatcherPump(presenter.DisposeAsync().AsTask());
|
||||
owner.Close();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Csd_Constraints_Ignore_The_Managed_TitleBar_Minimum_Width()
|
||||
{
|
||||
|
||||
@@ -183,6 +183,73 @@ public class ImagePreviewerTitleTests
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ImagePreviewerDialog_Handles_TitleBar_Toolbar_Requests_Relayed_From_Csd_Overlay()
|
||||
{
|
||||
Dispatcher.UIThread.Invoke(() =>
|
||||
{
|
||||
var dialog = new TestImagePreviewerDialog
|
||||
{
|
||||
ItemsSource =
|
||||
[
|
||||
new ImagePreviewItem(new UriImagePreviewSource("avares://AtomUI.Tests/Assets/first.png")),
|
||||
new ImagePreviewItem(new UriImagePreviewSource("avares://AtomUI.Tests/Assets/second.png"))
|
||||
],
|
||||
CurrentImage = LoadedImageSource.CreateSvg("<svg />", new Avalonia.Size(10, 10)),
|
||||
ImageScaleX = 1.0,
|
||||
ImageScaleY = 1.0
|
||||
};
|
||||
var titleBar = new ImagePreviewerTitleBar();
|
||||
|
||||
dialog.ConfigureTitleBar(titleBar);
|
||||
var toolbar = titleBar.LeftAddOn.ShouldNotBeNull()
|
||||
.ShouldBeAssignableTo<ImagePreviewToolbar>();
|
||||
|
||||
var rotateArgs = RelayToolbarRequest(dialog, toolbar, ImagePreviewBaseToolbar.RotateRightRequestEvent);
|
||||
rotateArgs.Handled.ShouldBeTrue();
|
||||
dialog.ImageRotate.ShouldBe(Math.PI / 2, 0.000001);
|
||||
|
||||
var flipArgs = RelayToolbarRequest(dialog, toolbar, ImagePreviewBaseToolbar.HorizontalFlipRequestEvent);
|
||||
flipArgs.Handled.ShouldBeTrue();
|
||||
dialog.ImageScaleX.ShouldBe(-1.0);
|
||||
|
||||
var fitArgs = new ImageFitToWindowEventArgs(false)
|
||||
{
|
||||
RoutedEvent = ImagePreviewBaseToolbar.FitToWindowRequestEvent
|
||||
};
|
||||
dialog.RaiseRoutedEventFromOverlay(toolbar, fitArgs);
|
||||
|
||||
fitArgs.Handled.ShouldBeTrue();
|
||||
dialog.IsImageFitToWindow.ShouldBeFalse();
|
||||
|
||||
var nextArgs = RelayToolbarRequest(dialog, toolbar, ImagePreviewBaseToolbar.NextRequestEvent);
|
||||
nextArgs.Handled.ShouldBeTrue();
|
||||
dialog.CurrentIndex.ShouldBe(1);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ImagePreviewToolbar_TitleBar_Requests_Use_Host_Window_Route_In_Csd()
|
||||
{
|
||||
var toolbarSource = File.ReadAllText(GetRepoFile(
|
||||
"src/AtomUI.Desktop.Controls/ImagePreviewer/ImagePreviewBaseToolbar.cs"));
|
||||
var titleBarSource = File.ReadAllText(GetRepoFile(
|
||||
"src/AtomUI.Desktop.Controls/ImagePreviewer/ImagePreviewerTitleBar.cs"));
|
||||
var dialogSource = File.ReadAllText(GetRepoFile(
|
||||
"src/AtomUI.Desktop.Controls/ImagePreviewer/ImagePreviewerDialog.cs"));
|
||||
|
||||
toolbarSource.ShouldContain("ResolveCsdTitleBarHostWindow()");
|
||||
toolbarSource.ShouldContain("ToolbarSource != ImagePreviewToolbarSource.TitleBar");
|
||||
toolbarSource.ShouldContain("hostWindow.RaiseRoutedEventFromOverlay(this, args);");
|
||||
toolbarSource.ShouldContain("RaiseEvent(args);");
|
||||
toolbarSource.IndexOf("hostWindow.RaiseRoutedEventFromOverlay(this, args);", StringComparison.Ordinal)
|
||||
.ShouldBeLessThan(toolbarSource.IndexOf("RaiseEvent(args);", StringComparison.Ordinal));
|
||||
|
||||
titleBarSource.ShouldNotContain("ToolbarHorizontalFlipRequest");
|
||||
dialogSource.ShouldNotContain("_titleBarRequestSource");
|
||||
dialogSource.ShouldContain("ImagePreviewBaseToolbar.NextRequestEvent.AddClassHandler<ImagePreviewerDialog>");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ImagePreviewerOverlayHost_ItemsSource_Change_Preserves_CurrentIndex()
|
||||
{
|
||||
@@ -268,6 +335,19 @@ public class ImagePreviewerTitleTests
|
||||
throw new FileNotFoundException($"Could not find repository file: {relativePath}");
|
||||
}
|
||||
|
||||
private static ImagePreviewToolbarRequestEventArgs RelayToolbarRequest(
|
||||
ImagePreviewerDialog dialog,
|
||||
ImagePreviewToolbar toolbar,
|
||||
Avalonia.Interactivity.RoutedEvent<ImagePreviewToolbarRequestEventArgs> routedEvent)
|
||||
{
|
||||
var args = new ImagePreviewToolbarRequestEventArgs(ImagePreviewToolbarSource.TitleBar)
|
||||
{
|
||||
RoutedEvent = routedEvent
|
||||
};
|
||||
dialog.RaiseRoutedEventFromOverlay(toolbar, args);
|
||||
return args;
|
||||
}
|
||||
|
||||
private sealed class PrefixTitleResolver : IImagePreviewTitleResolver
|
||||
{
|
||||
private readonly string _prefix;
|
||||
|
||||
Reference in New Issue
Block a user