using System; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using Microsoft.Data.Sqlite; using Xunit; using ytLive.Controls; using ytLive.Models; using ytLive.Services; using ytLive.ViewModels; namespace ytLive.Tests; [Collection("RealApp")] public sealed class SelectedLayerPreviewHitTestingTests { private readonly RealAppHost _app; public SelectedLayerPreviewHitTestingTests(RealAppHost app) => _app = app; [Fact] public void ListSelectedBrandLogo_WinsPreviewHitTesting_UntilDeselected() { _app.Run(Run); } private static void Run() { var tempDb = System.IO.Path.Combine(System.IO.Path.GetTempPath(), $"ytLlive-selected-layer-{Guid.NewGuid():N}.db"); MainViewModel.LayoutPathOverride = tempDb; MainWindow? window = null; try { using (var schema = new LayoutStore(tempDb)) { } SqliteConnection.ClearAllPools(); window = new MainWindow(); var vm = (MainViewModel)window.DataContext; var scene = vm.StagedScene!; var logo = scene.Elements.OfType().Single(source => source.IsBrandLogo); logo.X = 420; logo.Y = 300; logo.Width = 300; logo.Height = 180; var webcam = new WebcamSceneConfig { WebcamId = "test-camera", Name = "Webcam", X = logo.X, Y = logo.Y, Width = logo.Width, Height = logo.Height, }; scene.Elements.Add(webcam); var originalOrder = scene.Elements.ToArray(); window.Show(); window.Activate(); window.Topmost = true; window.UpdateLayout(); var leftPanel = (LeftPanel)window.FindName("LeftPanel")!; var sourceList = (ListBox)leftPanel.FindName("SourceList")!; var preview = (PreviewPane)window.FindName("PreviewPane")!; var canvasGrid = (FrameworkElement)preview.FindName("CanvasGrid")!; var overlayElements = (ItemsControl)preview.FindName("OverlayElements")!; var logoContainer = (UIElement)overlayElements.ItemContainerGenerator.ContainerFromItem(logo)!; var webcamContainer = (UIElement)overlayElements.ItemContainerGenerator.ContainerFromItem(webcam)!; var pointInCanvas = new Point(logo.X + 100, logo.Y + 60); var pointInWindow = canvasGrid.TransformToVisual(window).Transform(pointInCanvas); Assert.True(ReferenceEquals(webcam, ElementAt(window.InputHitTest(pointInWindow)))); Assert.Same(webcam, preview.HitElementAt(vm, pointInCanvas)); Assert.Equal(0, Panel.GetZIndex(logoContainer)); sourceList.SelectedItem = logo; window.UpdateLayout(); Assert.True(ReferenceEquals(logo, vm.SelectedElement), "Selecting the layer-list row must select the brand logo."); Assert.True(Panel.GetZIndex(logoContainer) > Panel.GetZIndex(webcamContainer)); Assert.Same(logo, ElementAt(window.InputHitTest(pointInWindow))); Assert.Same(logo, preview.HitElementAt(vm, pointInCanvas)); Assert.Equal(originalOrder, scene.Elements); sourceList.SelectedItem = null; window.UpdateLayout(); Assert.Null(vm.SelectedElement); Assert.Equal(0, Panel.GetZIndex(logoContainer)); Assert.Same(webcam, ElementAt(window.InputHitTest(pointInWindow))); Assert.Same(webcam, preview.HitElementAt(vm, pointInCanvas)); Assert.Equal(originalOrder, scene.Elements); } finally { window?.Close(); MainViewModel.LayoutPathOverride = null; SqliteConnection.ClearAllPools(); try { System.IO.File.Delete(tempDb); } catch { } } } private static SceneElement? ElementAt(System.Windows.IInputElement? hit) { DependencyObject? current = hit as DependencyObject; while (current != null) { if (current is FrameworkElement { DataContext: SceneElement element }) return element; current = current is Visual or System.Windows.Media.Media3D.Visual3D ? VisualTreeHelper.GetParent(current) : LogicalTreeHelper.GetParent(current); } return null; } }