You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

285 lines
10 KiB

using System;
using System.Runtime.InteropServices;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Markup.Xaml;
using ReactiveUI.Avalonia;
using Microsoft.Extensions.Logging;
using AuroraDesk.Presentation.ViewModels;
using AuroraDesk.Presentation.Views.Dialogs;
using ReactiveUI;
using System.Reactive.Disposables;
using System.Threading.Tasks;
namespace AuroraDesk.Presentation.Views;
/// <summary>
/// 主窗口
/// </summary>
public partial class MainWindow : ReactiveWindow<MainWindowViewModel>, IActivatableView
{
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 设计器
/// </summary>
public MainWindow()
{
InitializeComponent();
SetupWindowControls();
}
/// <summary>
/// 构造函数,接受依赖注入的 ViewModel
/// </summary>
/// <param name="viewModel">主窗口的 ViewModel</param>
/// <param name="logger">日志记录器</param>
public MainWindow(MainWindowViewModel viewModel, ILogger<MainWindow>? logger = null)
{
ArgumentNullException.ThrowIfNull(viewModel);
_logger = logger;
InitializeComponent();
ViewModel = viewModel;
SetupWindowControls();
_logger?.LogInformation("MainWindow 已创建,ViewModel 已设置");
// 处理窗口关闭事件,显示确认框
this.Closing += OnWindowClosing;
// 使用 WhenActivated 管理订阅
this.WhenActivated(disposables =>
{
// 注册关闭确认对话框 Interaction Handler
if (ViewModel != null)
{
disposables.Add(
ViewModel.CloseConfirmInteraction
.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);
}));
}
});
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
/// <summary>
/// 设置窗口控制按钮事件
/// </summary>
private void SetupWindowControls()
{
// 设置左侧导航栏拖动功能
var leftNavigationBar = this.FindControl<Border>("LeftNavigationBar");
if (leftNavigationBar != null)
{
leftNavigationBar.PointerPressed += OnLeftNavigationBarPointerPressed;
}
// Linux 平台需要在窗口级别处理鼠标移动和释放事件
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
this.PointerMoved += OnWindowPointerMoved;
this.PointerReleased += OnWindowPointerReleased;
}
// 最小化按钮
var minimizeButton = this.FindControl<Button>("MinimizeButton");
if (minimizeButton != null)
{
minimizeButton.Click += (sender, e) => WindowState = WindowState.Minimized;
}
// 最大化/还原按钮
var maximizeButton = this.FindControl<Button>("MaximizeButton");
if (maximizeButton != null)
{
maximizeButton.Click += (sender, e) =>
{
if (WindowState == WindowState.Maximized)
{
WindowState = WindowState.Normal;
ToolTip.SetTip(maximizeButton, "最大化");
}
else
{
WindowState = WindowState.Maximized;
ToolTip.SetTip(maximizeButton, "还原");
}
};
}
// 关闭按钮 - 显示确认框而不是直接关闭
var closeButton = this.FindControl<Button>("CloseButton");
if (closeButton != null)
{
if (ViewModel != null)
{
// 关闭按钮点击时显示确认框
closeButton.Click += async (sender, e) =>
{
var confirmed = await ViewModel.RequestCloseConfirmAsync();
if (confirmed)
{
// 设置标志,表示用户已确认关闭
_isClosingConfirmed = true;
Close();
}
};
}
else
{
// 设计器模式或无 ViewModel 时,直接关闭(用于设计器预览)
closeButton.Click += (sender, e) => Close();
}
}
}
/// <summary>
/// 处理左侧导航栏鼠标按下事件,实现窗口拖动
/// </summary>
private void OnLeftNavigationBarPointerPressed(object? sender, PointerPressedEventArgs e)
{
if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
{
// 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;
}
}
/// <summary>
/// 处理窗口关闭事件,显示确认框
/// </summary>
private async void OnWindowClosing(object? sender, WindowClosingEventArgs e)
{
// 如果用户已经通过关闭按钮确认关闭,直接允许关闭
if (_isClosingConfirmed)
{
return;
}
if (ViewModel != null)
{
// 取消默认关闭行为
e.Cancel = true;
// 显示确认框
var confirmed = await ViewModel.RequestCloseConfirmAsync();
if (confirmed)
{
// 用户确认关闭,设置标志并允许窗口关闭
_isClosingConfirmed = true;
e.Cancel = false;
// 不需要手动调用 Close(),系统会自动关闭窗口
}
// 如果用户取消,e.Cancel = true 已设置,窗口不会关闭
}
// 如果没有 ViewModel(设计器模式),允许直接关闭
}
}