2014-09-18 02:20:45 +04:00
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
// <copyright file="Visual.cs" company="Steven Kirk">
|
|
|
|
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
|
|
|
|
// </copyright>
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
namespace Perspex
|
|
|
|
|
{
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Collections.Specialized;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using Perspex.Media;
|
|
|
|
|
using Perspex.Rendering;
|
|
|
|
|
using Splat;
|
|
|
|
|
|
2014-09-18 03:45:40 +04:00
|
|
|
|
public class Visual : PerspexObject, IVisual
|
2014-09-18 02:20:45 +04:00
|
|
|
|
{
|
|
|
|
|
public static readonly PerspexProperty<bool> IsVisibleProperty =
|
|
|
|
|
PerspexProperty.Register<Visual, bool>("IsVisible", true);
|
|
|
|
|
|
|
|
|
|
public static readonly PerspexProperty<double> OpacityProperty =
|
|
|
|
|
PerspexProperty.Register<Visual, double>("Opacity", 1);
|
|
|
|
|
|
|
|
|
|
public static readonly PerspexProperty<ITransform> RenderTransformProperty =
|
|
|
|
|
PerspexProperty.Register<Visual, ITransform>("RenderTransform");
|
|
|
|
|
|
|
|
|
|
public static readonly PerspexProperty<Origin> TransformOriginProperty =
|
|
|
|
|
PerspexProperty.Register<Visual, Origin>("TransformOrigin", defaultValue: Origin.Default);
|
|
|
|
|
|
|
|
|
|
private Rect bounds;
|
|
|
|
|
|
2014-09-25 22:15:49 +04:00
|
|
|
|
private PerspexList<IVisual> visualChildren;
|
2014-09-18 02:20:45 +04:00
|
|
|
|
|
2014-09-18 03:45:40 +04:00
|
|
|
|
private Visual visualParent;
|
2014-09-18 02:20:45 +04:00
|
|
|
|
|
|
|
|
|
static Visual()
|
|
|
|
|
{
|
|
|
|
|
AffectsRender(IsVisibleProperty);
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-30 18:42:15 +03:00
|
|
|
|
public Visual()
|
|
|
|
|
{
|
|
|
|
|
this.visualChildren = new PerspexList<IVisual>();
|
2014-12-06 00:32:08 +03:00
|
|
|
|
this.visualChildren.CollectionChanged += this.VisualChildrenChanged;
|
2014-11-30 18:42:15 +03:00
|
|
|
|
}
|
|
|
|
|
|
2014-09-18 02:20:45 +04:00
|
|
|
|
public bool IsVisible
|
|
|
|
|
{
|
|
|
|
|
get { return this.GetValue(IsVisibleProperty); }
|
|
|
|
|
set { this.SetValue(IsVisibleProperty, value); }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public double Opacity
|
|
|
|
|
{
|
|
|
|
|
get { return this.GetValue(OpacityProperty); }
|
|
|
|
|
set { this.SetValue(OpacityProperty, value); }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public ITransform RenderTransform
|
|
|
|
|
{
|
|
|
|
|
get { return this.GetValue(RenderTransformProperty); }
|
|
|
|
|
set { this.SetValue(RenderTransformProperty, value); }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Origin TransformOrigin
|
|
|
|
|
{
|
|
|
|
|
get { return this.GetValue(TransformOriginProperty); }
|
|
|
|
|
set { this.SetValue(TransformOriginProperty, value); }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Rect IVisual.Bounds
|
|
|
|
|
{
|
|
|
|
|
get { return this.bounds; }
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-25 22:15:49 +04:00
|
|
|
|
IReadOnlyPerspexList<IVisual> IVisual.VisualChildren
|
2014-09-18 02:20:45 +04:00
|
|
|
|
{
|
2014-11-30 18:42:15 +03:00
|
|
|
|
get { return this.visualChildren; }
|
2014-09-18 02:20:45 +04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IVisual IVisual.VisualParent
|
|
|
|
|
{
|
2014-09-18 03:45:40 +04:00
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return this.visualParent;
|
|
|
|
|
}
|
2014-09-18 02:20:45 +04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void InvalidateVisual()
|
|
|
|
|
{
|
|
|
|
|
IRenderRoot root = this.GetSelfAndVisualAncestors()
|
|
|
|
|
.OfType<IRenderRoot>()
|
|
|
|
|
.FirstOrDefault();
|
|
|
|
|
|
|
|
|
|
if (root != null && root.RenderManager != null)
|
|
|
|
|
{
|
|
|
|
|
root.RenderManager.InvalidateRender(this);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public virtual void Render(IDrawingContext context)
|
|
|
|
|
{
|
|
|
|
|
Contract.Requires<ArgumentNullException>(context != null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected static void AffectsRender(PerspexProperty property)
|
|
|
|
|
{
|
|
|
|
|
property.Changed.Subscribe(AffectsRenderInvalidate);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected void AddVisualChild(Visual visual)
|
|
|
|
|
{
|
2014-09-18 23:13:16 +04:00
|
|
|
|
Contract.Requires<ArgumentNullException>(visual != null);
|
|
|
|
|
|
2014-09-18 02:20:45 +04:00
|
|
|
|
this.visualChildren.Add(visual);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected void AddVisualChildren(IEnumerable<Visual> visuals)
|
|
|
|
|
{
|
2014-09-18 23:13:16 +04:00
|
|
|
|
Contract.Requires<ArgumentNullException>(visuals != null);
|
|
|
|
|
|
2014-09-18 02:20:45 +04:00
|
|
|
|
this.visualChildren.AddRange(visuals);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected void ClearVisualChildren()
|
|
|
|
|
{
|
2014-09-18 03:45:40 +04:00
|
|
|
|
// TODO: Just call visualChildren.Clear() when we have a PerspexList that notifies of
|
|
|
|
|
// the removed items.
|
|
|
|
|
while (this.visualChildren.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
this.visualChildren.RemoveAt(this.visualChildren.Count - 1);
|
|
|
|
|
}
|
2014-09-18 02:20:45 +04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected void RemoveVisualChild(Visual visual)
|
|
|
|
|
{
|
2014-09-18 23:13:16 +04:00
|
|
|
|
Contract.Requires<ArgumentNullException>(visual != null);
|
|
|
|
|
|
2014-09-18 02:20:45 +04:00
|
|
|
|
this.visualChildren.Remove(visual);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-19 17:45:37 +04:00
|
|
|
|
protected void RemoveVisualChildren(IEnumerable<Visual> visuals)
|
|
|
|
|
{
|
|
|
|
|
Contract.Requires<ArgumentNullException>(visuals != null);
|
|
|
|
|
|
|
|
|
|
foreach (var v in visuals)
|
|
|
|
|
{
|
|
|
|
|
this.visualChildren.Remove(v);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-18 02:20:45 +04:00
|
|
|
|
protected void SetVisualBounds(Rect bounds)
|
|
|
|
|
{
|
|
|
|
|
this.bounds = bounds;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected virtual void OnAttachedToVisualTree(IRenderRoot root)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected virtual void OnDetachedFromVisualTree(IRenderRoot oldRoot)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-18 03:45:40 +04:00
|
|
|
|
protected virtual void OnVisualParentChanged(Visual oldParent)
|
2014-09-18 02:20:45 +04:00
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void AffectsRenderInvalidate(PerspexPropertyChangedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
Visual visual = e.Sender as Visual;
|
|
|
|
|
|
|
|
|
|
if (visual != null)
|
|
|
|
|
{
|
|
|
|
|
visual.InvalidateVisual();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-18 03:45:40 +04:00
|
|
|
|
private void SetVisualParent(Visual value)
|
|
|
|
|
{
|
|
|
|
|
if (this.visualParent != value)
|
|
|
|
|
{
|
|
|
|
|
var old = this.visualParent;
|
|
|
|
|
var oldRoot = this.GetVisualAncestors().OfType<IRenderRoot>().FirstOrDefault();
|
|
|
|
|
var newRoot = default(IRenderRoot);
|
|
|
|
|
|
|
|
|
|
if (value != null)
|
|
|
|
|
{
|
|
|
|
|
newRoot = value.GetSelfAndVisualAncestors().OfType<IRenderRoot>().FirstOrDefault();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.visualParent = value;
|
|
|
|
|
this.OnVisualParentChanged(old);
|
|
|
|
|
|
|
|
|
|
if (oldRoot != null)
|
|
|
|
|
{
|
|
|
|
|
this.NotifyDetachedFromVisualTree(oldRoot);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (newRoot != null)
|
|
|
|
|
{
|
|
|
|
|
this.NotifyAttachedToVisualTree(newRoot);
|
|
|
|
|
}
|
2014-09-18 02:20:45 +04:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void VisualChildrenChanged(object sender, NotifyCollectionChangedEventArgs e)
|
|
|
|
|
{
|
2014-09-18 03:45:40 +04:00
|
|
|
|
switch (e.Action)
|
|
|
|
|
{
|
|
|
|
|
case NotifyCollectionChangedAction.Add:
|
|
|
|
|
foreach (Visual v in e.NewItems)
|
|
|
|
|
{
|
2014-09-18 22:39:22 +04:00
|
|
|
|
v.InheritanceParent = this;
|
2014-09-18 03:45:40 +04:00
|
|
|
|
v.SetVisualParent(this);
|
|
|
|
|
}
|
2014-12-06 00:32:08 +03:00
|
|
|
|
|
2014-09-18 03:45:40 +04:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case NotifyCollectionChangedAction.Remove:
|
|
|
|
|
foreach (Visual v in e.OldItems)
|
|
|
|
|
{
|
2014-09-18 22:39:22 +04:00
|
|
|
|
v.InheritanceParent = null;
|
2014-09-18 03:45:40 +04:00
|
|
|
|
v.SetVisualParent(null);
|
|
|
|
|
}
|
2014-12-06 00:32:08 +03:00
|
|
|
|
|
2014-09-18 03:45:40 +04:00
|
|
|
|
break;
|
|
|
|
|
}
|
2014-09-18 02:20:45 +04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void NotifyAttachedToVisualTree(IRenderRoot root)
|
|
|
|
|
{
|
2014-09-23 20:47:46 +04:00
|
|
|
|
this.Log().Debug(
|
2014-09-18 02:20:45 +04:00
|
|
|
|
"Attached {0} (#{1:x8}) to visual tree",
|
|
|
|
|
this.GetType().Name,
|
2014-09-23 20:47:46 +04:00
|
|
|
|
this.GetHashCode());
|
2014-09-18 02:20:45 +04:00
|
|
|
|
|
|
|
|
|
this.OnAttachedToVisualTree(root);
|
|
|
|
|
|
|
|
|
|
if (this.visualChildren != null)
|
|
|
|
|
{
|
|
|
|
|
foreach (Visual child in this.visualChildren.OfType<Visual>())
|
|
|
|
|
{
|
|
|
|
|
child.NotifyAttachedToVisualTree(root);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void NotifyDetachedFromVisualTree(IRenderRoot oldRoot)
|
|
|
|
|
{
|
2014-09-23 20:47:46 +04:00
|
|
|
|
this.Log().Debug(
|
2014-09-18 02:20:45 +04:00
|
|
|
|
"Detached {0} (#{1:x8}) from visual tree",
|
|
|
|
|
this.GetType().Name,
|
2014-09-23 20:47:46 +04:00
|
|
|
|
this.GetHashCode());
|
2014-09-18 02:20:45 +04:00
|
|
|
|
|
|
|
|
|
this.OnDetachedFromVisualTree(oldRoot);
|
|
|
|
|
|
|
|
|
|
if (this.visualChildren != null)
|
|
|
|
|
{
|
|
|
|
|
foreach (Visual child in this.visualChildren.OfType<Visual>())
|
|
|
|
|
{
|
|
|
|
|
child.NotifyDetachedFromVisualTree(oldRoot);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|