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

XFEToolBox

【WPF】XFE工具箱

公开
关注 0 Fork 0 Star 0
UTF-8
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Threading;
using XFEToolBox.Client.Utilities;
using XFEToolBox.Client.Views.Windows;

namespace XFEToolBox.Client.Wpf.Test;

public static class QuickSearchInputAlignmentTests
{
    [Test]
    public static void QuickSearchHintSharesTextMetricsAndCaretOriginWithInput()
    {
        Exception? failure = null;
        var thread = new Thread(() =>
        {
            var root = Path.Combine(Path.GetTempPath(), "xfetoolbox-search-input-" + Guid.NewGuid().ToString("N"));
            var scope = Path.Combine(root, "scope");
            var cache = Path.Combine(root, "cache");
            FileSearchRuntime? runtime = null;
            CommandPaletteWindow? window = null;
            try
            {
                Directory.CreateDirectory(scope);
                SynchronizationContext.SetSynchronizationContext(new DispatcherSynchronizationContext(Dispatcher.CurrentDispatcher));
                runtime = new FileSearchRuntime(cache);
                runtime.SetScopeAsync(scope).GetAwaiter().GetResult();
                window = new CommandPaletteWindow(runtime) { ShowActivated = false, ShowInTaskbar = false, Topmost = false,
                    Left = -10000, Top = -10000, WindowStartupLocation = WindowStartupLocation.Manual };
                window.Resources.MergedDictionaries.Add(new ResourceDictionary
                { Source = new Uri("/XFEToolBox.WpfCore;component/Resources/Style/ToolThemeResources.xaml", UriKind.Relative) });
                window.Resources.MergedDictionaries.Add(new ResourceDictionary
                { Source = new Uri("/XFEToolBox;component/Resources/Style/MainStyle.xaml", UriKind.Relative) });
                window.Show();
                var input = (TextBox)window.FindName("SearchBox");
                var hint = (TextBlock)window.FindName("SearchHint");
                var surface = (Border)window.FindName("SearchInputSurface");
                var clear = (Button)window.FindName("ClearSearchButton");
                input.Focus();
                Pump();
                window.UpdateLayout();
                Ensure(hint.FontFamily.Equals(input.FontFamily) && hint.FontSize == input.FontSize && hint.FontWeight == input.FontWeight,
                    "提示文本和输入文字仍使用不同字体度量,光标和 Hint 基线不一致。");
                var rect = input.GetRectFromCharacterIndex(0);
                Ensure(!rect.IsEmpty && rect.Height > 0, "输入框空文本光标没有有效的布局尺寸。");
                var caretTop = input.TranslatePoint(rect.TopLeft, surface);
                var hintTop = hint.TranslatePoint(new Point(0, 0), surface);
                Ensure(Math.Abs(caretTop.X - hintTop.X) <= 1,
                    $"空文本光标与提示左侧未对齐:{caretTop.X:F2}/{hintTop.X:F2}。");
                Ensure(Math.Abs(caretTop.Y + rect.Height / 2 - (hintTop.Y + hint.ActualHeight / 2)) <= 1.5,
                    $"光标和提示垂直中心未对齐:{caretTop.Y + rect.Height / 2:F2}/{hintTop.Y + hint.ActualHeight / 2:F2}。");
                Ensure(input.BorderThickness == new Thickness(0) && input.FocusVisualStyle is null &&
                    !clear.Focusable && !clear.IsTabStop && clear.BorderThickness == new Thickness(0),
                    "整体焦点边框修复回退,或清空按钮重新获得选中边框。");
                input.Text = "中文 report";
                Pump();
                Ensure(hint.Visibility == Visibility.Collapsed, "有输入时提示仍与文字重叠。");
            }
            catch (Exception exception) { failure = exception; }
            finally
            {
                window?.Close();
                runtime?.DisposeAsync().AsTask().GetAwaiter().GetResult();
                Dispatcher.CurrentDispatcher.InvokeShutdown();
                // This fixture only owns its known cache files and the empty scope directory.
                if (Directory.Exists(cache))
                {
                    foreach (var file in Directory.EnumerateFiles(cache)) File.Delete(file);
                    Directory.Delete(cache);
                }
                if (Directory.Exists(scope)) Directory.Delete(scope);
                if (Directory.Exists(root)) Directory.Delete(root);
            }
        }) { IsBackground = true };
        thread.SetApartmentState(ApartmentState.STA);
        thread.Start();
        Ensure(thread.Join(TimeSpan.FromSeconds(30)), "搜索输入框对齐测试超时。");
        if (failure is not null)
        {
            Console.WriteLine(failure);
            throw new InvalidOperationException("搜索输入框 Hint/光标回归失败:" + failure.Message, failure);
        }
    }

    private static void Pump()
    {
        var frame = new DispatcherFrame();
        Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new Action(() => frame.Continue = false));
        Dispatcher.PushFrame(frame);
    }

    private static void Ensure(bool condition, string message)
    {
        if (!condition) throw new InvalidOperationException(message);
    }
}