XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEToolBox

【WPF】XFE工具箱

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interop;
using XFEToolBox.Client.Utilities;
using XFEToolBox.Client.ViewModel.Pages;
using Forms = System.Windows.Forms;

namespace XFEToolBox.Client.Views.Windows;

public partial class CommandPaletteWindow : Window
{
    private int searchGeneration;

    public CommandPaletteWindow() => InitializeComponent();

    public void ShowPalette(string? initialQuery = null)
    {
        PositionOnCurrentScreen();
        if (!IsVisible) Show();
        WindowState = WindowState.Normal;
        Activate();
        SearchBox.Text = initialQuery ?? string.Empty;
        SearchBox.CaretIndex = SearchBox.Text.Length;
        SearchBox.Focus();
        _ = RefreshResultsAsync();
    }

    private async void SearchBox_TextChanged(object sender, TextChangedEventArgs e) => await RefreshResultsAsync();

    private async Task RefreshResultsAsync()
    {
        var generation = ++searchGeneration;
        var items = await LauncherService.SearchAsync(SearchBox.Text);
        if (generation != searchGeneration) return;
        ResultList.ItemsSource = items.Select(item => new LauncherItemViewModel(item)).ToArray();
        EmptyState.Visibility = items.Count == 0 ? Visibility.Visible : Visibility.Collapsed;
        ResultList.SelectedIndex = items.Count > 0 ? 0 : -1;
        StatusText.Text = items.Count == 0 ? "没有匹配结果" : $"{items.Count} 个结果";
    }

    private async void Window_PreviewKeyDown(object sender, KeyEventArgs e)
    {
        switch (e.Key)
        {
            case Key.Escape:
                Hide();
                e.Handled = true;
                break;
            case Key.Down:
                if (ResultList.Items.Count > 0)
                    ResultList.SelectedIndex = Math.Min(ResultList.Items.Count - 1, ResultList.SelectedIndex + 1);
                ResultList.ScrollIntoView(ResultList.SelectedItem);
                e.Handled = true;
                break;
            case Key.Up:
                if (ResultList.Items.Count > 0)
                    ResultList.SelectedIndex = Math.Max(0, ResultList.SelectedIndex - 1);
                ResultList.ScrollIntoView(ResultList.SelectedItem);
                e.Handled = true;
                break;
            case Key.Enter:
                await ExecuteSelectedAsync();
                e.Handled = true;
                break;
        }
    }

    private async void ResultList_MouseDoubleClick(object sender, MouseButtonEventArgs e) => await ExecuteSelectedAsync();

    private async Task ExecuteSelectedAsync()
    {
        if (ResultList.SelectedItem is not LauncherItemViewModel item) return;
        Hide();
        await item.ExecuteAsync();
    }

    private async void PinMenuItem_Click(object sender, RoutedEventArgs e)
    {
        if (sender is not MenuItem { DataContext: LauncherItemViewModel item }) return;
        if (!item.TogglePinned())
            StatusText.Text = $"最多只能固定 {PinnedItemService.MaximumPinnedItems} 项";
        else
            StatusText.Text = item.IsPinned ? "已固定到主页" : "已取消固定";
        await RefreshResultsAsync();
    }

    private void PositionOnCurrentScreen()
    {
        var cursor = Forms.Control.MousePosition;
        var area = Forms.Screen.FromPoint(cursor).WorkingArea;
        var handle = new WindowInteropHelper(this).EnsureHandle();
        CenterNativeWindow(handle, area);
        CenterNativeWindow(handle, area);
    }

    private static void CenterNativeWindow(IntPtr handle, System.Drawing.Rectangle area)
    {
        if (!GetWindowRect(handle, out var bounds)) return;
        var width = bounds.Right - bounds.Left;
        var height = bounds.Bottom - bounds.Top;
        var left = area.Left + (area.Width - width) / 2;
        var top = area.Top + (area.Height - height) / 2;
        SetWindowPos(handle, IntPtr.Zero, left, top, 0, 0, 0x0001 | 0x0004 | 0x0010);
    }

    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool GetWindowRect(IntPtr windowHandle, out NativeRect rectangle);

    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool SetWindowPos(
        IntPtr windowHandle,
        IntPtr insertAfter,
        int x,
        int y,
        int width,
        int height,
        uint flags);

    [StructLayout(LayoutKind.Sequential)]
    private struct NativeRect
    {
        public int Left;
        public int Top;
        public int Right;
        public int Bottom;
    }
}