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;
}
}
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;
}
}