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