|
|
|
@ -1,4 +1,5 @@ |
|
|
|
using System; |
|
|
|
using System.Runtime.InteropServices; |
|
|
|
using Avalonia.Controls; |
|
|
|
using Avalonia.Controls.Primitives; |
|
|
|
using Avalonia.Input; |
|
|
|
@ -20,6 +21,10 @@ public partial class MainWindow : ReactiveWindow<MainWindowViewModel>, IActivata |
|
|
|
{ |
|
|
|
private readonly ILogger<MainWindow>? _logger; |
|
|
|
private bool _isClosingConfirmed = false; |
|
|
|
private bool _isDragging = false; |
|
|
|
private Avalonia.PixelPoint _dragStartScreenPoint; |
|
|
|
private Avalonia.PixelPoint _dragStartWindowPosition; |
|
|
|
private Avalonia.Point _lastWindowPoint; |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// 无参构造函数,用于 XAML 设计器
|
|
|
|
@ -59,6 +64,29 @@ public partial class MainWindow : ReactiveWindow<MainWindowViewModel>, IActivata |
|
|
|
.RegisterHandler(async interaction => |
|
|
|
{ |
|
|
|
var dialog = new CloseConfirmDialog(interaction.Input); |
|
|
|
|
|
|
|
// 在 Linux 上,确保窗口在显示前已准备好
|
|
|
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) |
|
|
|
{ |
|
|
|
// 先准备好窗口,但不显示
|
|
|
|
dialog.ShowActivated = false; |
|
|
|
dialog.Opacity = 0; |
|
|
|
|
|
|
|
// 等待窗口初始化完成
|
|
|
|
await System.Threading.Tasks.Task.Delay(10); |
|
|
|
|
|
|
|
// 触发布局,确保内容已渲染
|
|
|
|
dialog.InvalidateMeasure(); |
|
|
|
dialog.InvalidateArrange(); |
|
|
|
|
|
|
|
// 等待布局和渲染完成
|
|
|
|
await System.Threading.Tasks.Task.Delay(50); |
|
|
|
|
|
|
|
// 设置透明度为1,准备显示
|
|
|
|
dialog.Opacity = 1; |
|
|
|
dialog.ShowActivated = true; |
|
|
|
} |
|
|
|
|
|
|
|
var result = await dialog.ShowDialog<bool>(this); |
|
|
|
interaction.SetOutput(result); |
|
|
|
}) |
|
|
|
@ -77,11 +105,18 @@ public partial class MainWindow : ReactiveWindow<MainWindowViewModel>, IActivata |
|
|
|
/// </summary>
|
|
|
|
private void SetupWindowControls() |
|
|
|
{ |
|
|
|
// 设置标题栏拖动功能
|
|
|
|
var titleBarGrid = this.FindControl<Grid>("TitleBarGrid"); |
|
|
|
if (titleBarGrid != null) |
|
|
|
// 设置左侧导航栏拖动功能
|
|
|
|
var leftNavigationBar = this.FindControl<Border>("LeftNavigationBar"); |
|
|
|
if (leftNavigationBar != null) |
|
|
|
{ |
|
|
|
titleBarGrid.PointerPressed += OnTitleBarPointerPressed; |
|
|
|
leftNavigationBar.PointerPressed += OnLeftNavigationBarPointerPressed; |
|
|
|
} |
|
|
|
|
|
|
|
// Linux 平台需要在窗口级别处理鼠标移动和释放事件
|
|
|
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) |
|
|
|
{ |
|
|
|
this.PointerMoved += OnWindowPointerMoved; |
|
|
|
this.PointerReleased += OnWindowPointerReleased; |
|
|
|
} |
|
|
|
|
|
|
|
// 最小化按钮
|
|
|
|
@ -137,13 +172,83 @@ public partial class MainWindow : ReactiveWindow<MainWindowViewModel>, IActivata |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// 处理标题栏鼠标按下事件,实现窗口拖动
|
|
|
|
/// 处理左侧导航栏鼠标按下事件,实现窗口拖动
|
|
|
|
/// </summary>
|
|
|
|
private void OnTitleBarPointerPressed(object? sender, PointerPressedEventArgs e) |
|
|
|
private void OnLeftNavigationBarPointerPressed(object? sender, PointerPressedEventArgs e) |
|
|
|
{ |
|
|
|
if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) |
|
|
|
{ |
|
|
|
BeginMoveDrag(e); |
|
|
|
// Linux 平台需要手动实现拖动
|
|
|
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) |
|
|
|
{ |
|
|
|
_isDragging = true; |
|
|
|
// 记录按下时的窗口位置和鼠标屏幕坐标
|
|
|
|
var pointerPoint = e.GetCurrentPoint(this); |
|
|
|
var windowPoint = pointerPoint.Position; |
|
|
|
// 计算鼠标的屏幕坐标:窗口位置 + 鼠标在窗口内的位置
|
|
|
|
_dragStartScreenPoint = new Avalonia.PixelPoint( |
|
|
|
Position.X + (int)Math.Round(windowPoint.X), |
|
|
|
Position.Y + (int)Math.Round(windowPoint.Y)); |
|
|
|
_dragStartWindowPosition = Position; |
|
|
|
_lastWindowPoint = windowPoint; |
|
|
|
// 捕获指针到窗口,确保拖动流畅
|
|
|
|
pointerPoint.Pointer.Capture(this); |
|
|
|
e.Handled = true; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
// Windows 和其他平台使用系统方法
|
|
|
|
BeginMoveDrag(e); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// 处理窗口鼠标移动事件(仅 Linux 平台)
|
|
|
|
/// </summary>
|
|
|
|
private void OnWindowPointerMoved(object? sender, PointerEventArgs e) |
|
|
|
{ |
|
|
|
if (_isDragging && e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) |
|
|
|
{ |
|
|
|
// 获取当前鼠标在窗口内的位置
|
|
|
|
var currentPoint = e.GetCurrentPoint(this); |
|
|
|
var currentWindowPoint = currentPoint.Position; |
|
|
|
|
|
|
|
// 计算鼠标在窗口内的移动量(相对于上次记录的位置)
|
|
|
|
var deltaWindowX = currentWindowPoint.X - _lastWindowPoint.X; |
|
|
|
var deltaWindowY = currentWindowPoint.Y - _lastWindowPoint.Y; |
|
|
|
|
|
|
|
// 只有当鼠标在窗口内实际移动了超过0.5像素时才更新
|
|
|
|
if (Math.Abs(deltaWindowX) > 0.5 || Math.Abs(deltaWindowY) > 0.5) |
|
|
|
{ |
|
|
|
// 根据鼠标在窗口内的移动量更新窗口位置
|
|
|
|
var newX = Position.X + (int)Math.Round(deltaWindowX); |
|
|
|
var newY = Position.Y + (int)Math.Round(deltaWindowY); |
|
|
|
var newPosition = new Avalonia.PixelPoint(newX, newY); |
|
|
|
|
|
|
|
// 更新窗口位置
|
|
|
|
Position = newPosition; |
|
|
|
|
|
|
|
// 更新记录的上次鼠标位置
|
|
|
|
_lastWindowPoint = currentWindowPoint; |
|
|
|
} |
|
|
|
|
|
|
|
e.Handled = true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// 处理窗口鼠标释放事件(仅 Linux 平台)
|
|
|
|
/// </summary>
|
|
|
|
private void OnWindowPointerReleased(object? sender, PointerReleasedEventArgs e) |
|
|
|
{ |
|
|
|
if (_isDragging) |
|
|
|
{ |
|
|
|
_isDragging = false; |
|
|
|
// 释放指针捕获
|
|
|
|
var pointerPoint = e.GetCurrentPoint(this); |
|
|
|
pointerPoint.Pointer.Capture(null); |
|
|
|
e.Handled = true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|