public void Should_Track_Bounds() { var target = new BoundsTracker(); var control = default(Rectangle); var tree = new Decorator { Padding = new Thickness(10), Child = new Decorator { Padding = new Thickness(5), Child = control = new Rectangle { Width = 15, Height = 15, }, } }; tree.Measure(Size.Infinity); tree.Arrange(new Rect(0, 0, 100, 100)); var track = target.Track(control, tree); var results = new List <TransformedBounds>(); track.Subscribe(results.Add); Assert.Equal(new Rect(15, 15, 15, 15), results.Last().Bounds); tree.Padding = new Thickness(15); tree.Measure(Size.Infinity); tree.Arrange(new Rect(0, 0, 100, 100), true); Assert.Equal(new Rect(20, 20, 15, 15), results.Last().Bounds); }
private static void ClearTransformedBounds(IVisual visual) { foreach (var e in visual.GetSelfAndVisualDescendents()) { BoundsTracker.SetTransformedBounds((Visual)visual, null); } }
static IEnumerable <IVisual> HitTest( IVisual visual, Point p, Func <IVisual, bool> filter) { Contract.Requires <ArgumentNullException>(visual != null); if (filter?.Invoke(visual) != false) { bool containsPoint = BoundsTracker.GetTransformedBounds((Visual)visual)?.Contains(p) == true; if ((containsPoint || !visual.ClipToBounds) && visual.VisualChildren.Count > 0) { foreach (var child in visual.VisualChildren.SortByZIndex()) { foreach (var result in HitTest(child, p, filter)) { yield return(result); } } } if (containsPoint) { yield return(visual); } } }
private static void Deindex(Scene scene, VisualNode node) { scene.Remove(node); node.SubTreeUpdated = true; scene.Layers[node.LayerRoot].Dirty.Add(node.Bounds); if (node.Visual is Visual v) { BoundsTracker.SetTransformedBounds(v, null); } foreach (VisualNode child in node.Children) { var geometry = child as IDrawOperation; if (child is VisualNode visual) { Deindex(scene, visual); } } if (node.LayerRoot == node.Visual && node.Visual != scene.Root.Visual) { scene.Layers.Remove(node.LayerRoot); } }
public void Should_Track_Bounds() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var target = new BoundsTracker(); var control = default(Rectangle); var tree = new Decorator { Padding = new Thickness(10), Child = new Decorator { Padding = new Thickness(5), Child = control = new Rectangle { Width = 15, Height = 15, }, } }; var context = new DrawingContext(Mock.Of <IDrawingContextImpl>()); tree.Measure(Size.Infinity); tree.Arrange(new Rect(0, 0, 100, 100)); ImmediateRenderer.Render(tree, context); var track = target.Track(control); var results = new List <TransformedBounds?>(); track.Subscribe(results.Add); Assert.Equal(new Rect(0, 0, 15, 15), results[0].Value.Bounds); Assert.Equal(Matrix.CreateTranslation(42, 42), results[0].Value.Transform); } }
private void rdoWindow_MouseUp(object sender, MouseEventArgs e) { if (this.windowFinder.IsFinding) { this.Cursor = Cursors.Default; IntPtr hWnd = this.windowFinder.EndFind(); if (e.Button == MouseButtons.Left) { if (hWnd != IntPtr.Zero) { this.BoundsTracker = new BoundsTracker(hWnd); } else { this.BoundsTracker = this.prevBoundsTracker; this.prevBoundsTracker = null; } } else { this.BoundsTracker = this.prevBoundsTracker; this.prevBoundsTracker = null; } } }
/// <summary> /// Returns the active input elements at a point on an <see cref="IInputElement"/>. /// </summary> /// <param name="element">The element to test.</param> /// <param name="p">The point on <paramref name="element"/>.</param> /// <returns> /// The active input elements found at the point, ordered topmost first. /// </returns> public static IEnumerable <IInputElement> GetInputElementsAt(this IInputElement element, Point p) { Contract.Requires <ArgumentNullException>(element != null); if (element.IsVisible && element.IsHitTestVisible && element.IsEnabledCore) { bool containsPoint = BoundsTracker.GetTransformedBounds((Visual)element).Contains(p); if ((containsPoint || !element.ClipToBounds) && element.VisualChildren.Any()) { foreach (var child in ZSort(element.VisualChildren.OfType <IInputElement>())) { foreach (var result in child.GetInputElementsAt(p)) { yield return(result); } } } if (containsPoint) { yield return(element); } } }
/// <summary> /// Renders the specified visual. /// </summary> /// <param name="visual">The visual to render.</param> /// <param name="context">The drawing context.</param> /// <param name="clipRect"> /// The current clip rect, in coordinates relative to <paramref name="visual"/>. /// </param> private static void Render(this DrawingContext context, IVisual visual, Rect clipRect) { var opacity = visual.Opacity; var clipToBounds = visual.ClipToBounds; var bounds = new Rect(visual.Bounds.Size); if (visual.IsVisible && opacity > 0) { var m = Matrix.CreateTranslation(visual.Bounds.Position); var renderTransform = Matrix.Identity; if (visual.RenderTransform != null) { var origin = visual.RenderTransformOrigin.ToPixels(new Size(visual.Bounds.Width, visual.Bounds.Height)); var offset = Matrix.CreateTranslation(origin); renderTransform = (-offset) * visual.RenderTransform.Value * (offset); } m = renderTransform * m; if (clipToBounds) { clipRect = clipRect.Intersect(new Rect(visual.Bounds.Size)); } using (context.PushPostTransform(m)) using (context.PushOpacity(opacity)) using (clipToBounds ? context.PushClip(bounds) : default(DrawingContext.PushedState)) using (visual.Clip != null ? context.PushGeometryClip(visual.Clip) : default(DrawingContext.PushedState)) using (visual.OpacityMask != null ? context.PushOpacityMask(visual.OpacityMask, bounds) : default(DrawingContext.PushedState)) using (context.PushTransformContainer()) { visual.Render(context); var transformed = new TransformedBounds(bounds, new Rect(), context.CurrentContainerTransform); if (visual is Visual) { BoundsTracker.SetTransformedBounds((Visual)visual, transformed); } var lst = GetSortedVisualList(visual.VisualChildren); foreach (var child in lst) { var childBounds = GetTransformedBounds(child); if (!child.ClipToBounds || clipRect.Intersects(childBounds)) { var childClipRect = clipRect.Translate(-childBounds.Position); context.Render(child, childClipRect); } } ReturnListToPool(lst); } } }
private void rdoWindow_MouseDown(object sender, MouseEventArgs e) { if (!this.windowFinder.IsFinding && e.Button == MouseButtons.Left) { this.windowFinder.BeginFind(); // Keep current tracker in case of cancellation. this.prevBoundsTracker = this.boundsTracker; // Update radio buttons state this.BoundsTracker = new BoundsTracker(IntPtr.Zero); // Change cursor this.Cursor = Cursors.Cross; } }
/// <summary> /// Initialize host. /// </summary> private void Initialize() { this.Built += (sender, e) => { var root = VisualRoot as TopLevel; if (root == null) { throw new NotSupportedException("The visual root is not a toplevel."); } // Track change in bounds. var disposable = new BoundsTracker() .Track(this) .Subscribe(UpdateLayout); _disposables.Add(disposable); }; }
private static void Update(DrawingContext context, Scene scene, VisualNode node, Rect clip, bool forceRecurse) { var visual = node.Visual; var opacity = visual.Opacity; var clipToBounds = visual.ClipToBounds; var bounds = new Rect(visual.Bounds.Size); var contextImpl = (DeferredDrawingContextImpl)context.PlatformImpl; contextImpl.Layers.Find(node.LayerRoot)?.Dirty.Add(node.Bounds); if (visual.IsVisible) { var m = Matrix.CreateTranslation(visual.Bounds.Position); var renderTransform = Matrix.Identity; if (visual.RenderTransform != null) { var origin = visual.RenderTransformOrigin.ToPixels(new Size(visual.Bounds.Width, visual.Bounds.Height)); var offset = Matrix.CreateTranslation(origin); renderTransform = (-offset) * visual.RenderTransform.Value * (offset); } m = renderTransform * m; using (contextImpl.BeginUpdate(node)) using (context.PushPostTransform(m)) using (context.PushTransformContainer()) { var clipBounds = bounds.TransformToAABB(contextImpl.Transform).Intersect(clip); forceRecurse = forceRecurse || node.Transform != contextImpl.Transform || node.ClipBounds != clipBounds; node.Transform = contextImpl.Transform; node.ClipBounds = clipBounds; node.ClipToBounds = clipToBounds; node.GeometryClip = visual.Clip?.PlatformImpl; node.Opacity = opacity; // TODO: Check equality between node.OpacityMask and visual.OpacityMask before assigning. node.OpacityMask = visual.OpacityMask?.ToImmutable(); if (ShouldStartLayer(visual)) { if (node.LayerRoot != visual) { MakeLayer(scene, node); } else { UpdateLayer(node, scene.Layers[node.LayerRoot]); } } else if (node.LayerRoot == node.Visual && node.Parent != null) { ClearLayer(scene, node); } if (node.ClipToBounds) { clip = clip.Intersect(node.ClipBounds); } try { visual.Render(context); } catch { } if (visual is Visual) { var transformed = new TransformedBounds(new Rect(visual.Bounds.Size), clip, node.Transform); BoundsTracker.SetTransformedBounds((Visual)visual, transformed); } if (forceRecurse) { foreach (var child in visual.VisualChildren.OrderBy(x => x, ZIndexComparer.Instance)) { var childNode = scene.FindNode(child) ?? CreateNode(scene, child, node); Update(context, scene, (VisualNode)childNode, clip, forceRecurse); } node.SubTreeUpdated = true; contextImpl.TrimChildren(); } } } }
private void Render(DrawingContext context, IVisual visual, Rect clipRect) { var opacity = visual.Opacity; var clipToBounds = visual.ClipToBounds; var bounds = new Rect(visual.Bounds.Size); if (visual.IsVisible && opacity > 0) { var m = Matrix.CreateTranslation(visual.Bounds.Position); var renderTransform = Matrix.Identity; if (visual.RenderTransform != null) { var origin = visual.RenderTransformOrigin.ToPixels(new Size(visual.Bounds.Width, visual.Bounds.Height)); var offset = Matrix.CreateTranslation(origin); renderTransform = (-offset) * visual.RenderTransform.Value * (offset); } m = renderTransform * m; if (clipToBounds) { clipRect = clipRect.Intersect(new Rect(visual.Bounds.Size)); } using (context.PushPostTransform(m)) using (context.PushOpacity(opacity)) using (clipToBounds ? context.PushClip(bounds) : default(DrawingContext.PushedState)) using (visual.Clip != null ? context.PushGeometryClip(visual.Clip) : default(DrawingContext.PushedState)) using (visual.OpacityMask != null ? context.PushOpacityMask(visual.OpacityMask, bounds) : default(DrawingContext.PushedState)) using (context.PushTransformContainer()) { visual.Render(context); #pragma warning disable 0618 var transformed = new TransformedBounds(bounds, new Rect(), context.CurrentContainerTransform); #pragma warning restore 0618 if (visual is Visual) { BoundsTracker.SetTransformedBounds((Visual)visual, transformed); } foreach (var child in visual.VisualChildren.OrderBy(x => x, ZIndexComparer.Instance)) { var childBounds = GetTransformedBounds(child); if (!child.ClipToBounds || clipRect.Intersects(childBounds)) { var childClipRect = clipRect.Translate(-childBounds.Position); Render(context, child, childClipRect); } else { ClearTransformedBounds(child); } } } } if (!visual.IsVisible) { ClearTransformedBounds(visual); } }
/// <summary> /// Determines whether the specified element is currently visible to the user. /// </summary> /// <param name="element">The element.</param> /// <returns><c>true</c> if if the specified element is currently visible to the user; otherwise, <c>false</c>.</returns> private static bool IsUserVisible(Control element) { return(element.IsEffectivelyVisible && BoundsTracker.GetTransformedBounds(element).HasValue); }