Browse Source

Fixed up logical tree stuff.

pull/4/head
Steven Kirk 12 years ago
parent
commit
3d89fd922a
  1. 50
      Perspex.UnitTests/Controls/ContentControlTests.cs
  2. 25
      Perspex/Controls/ContentControl.cs
  3. 25
      Perspex/Controls/Control.cs
  4. 14
      Perspex/Controls/Decorator.cs
  5. 86
      Perspex/Controls/LogicalChildren.cs
  6. 36
      Perspex/Controls/Panel.cs
  7. 12
      Perspex/Visual.cs

50
Perspex.UnitTests/Controls/ContentControlTests.cs

@ -56,6 +56,56 @@ namespace Perspex.UnitTests.Controls
}
}
[TestMethod]
public void Setting_Content_To_Control_Should_Set_Parent()
{
var target = new ContentControl();
var child = new Border();
target.Content = child;
Assert.AreEqual(child.Parent, target);
Assert.AreEqual(((IVisual)child).VisualParent, target);
Assert.AreEqual(((ILogical)child).LogicalParent, target);
}
[TestMethod]
public void Setting_Content_To_Control_Should_Set_Logical_Child()
{
var target = new ContentControl();
var child = new Border();
target.Content = child;
Assert.AreEqual(child, ((ILogical)target).LogicalChildren.Single());
}
[TestMethod]
public void Removing_Control_From_Content_Should_Clear_Parent()
{
var target = new ContentControl();
var child = new Border();
target.Content = child;
target.Content = "foo";
Assert.IsNull(child.Parent);
Assert.IsNull(((IVisual)child).VisualParent);
Assert.IsNull(((ILogical)child).LogicalParent);
}
[TestMethod]
public void Removing_Control_From_Content_Should_Clear_Logical_Child()
{
var target = new ContentControl();
var child = new Border();
target.Content = child;
target.Content = "foo";
Assert.IsFalse(((ILogical)target).LogicalChildren.Any());
}
private void ApplyTemplate(IVisual visual)
{
foreach (IVisual child in visual.VisualChildren)

25
Perspex/Controls/ContentControl.cs

@ -10,13 +10,27 @@ namespace Perspex.Controls
using System.Collections.Generic;
using System.Linq;
public class ContentControl : TemplatedControl
public class ContentControl : TemplatedControl, ILogical
{
public static readonly PerspexProperty<object> ContentProperty =
PerspexProperty.Register<ContentControl, object>("Content");
public ContentControl()
{
this.GetObservableWithHistory(ContentProperty).Subscribe(x =>
{
if (x.Item1 is Control)
{
((IVisual)x.Item1).VisualParent = null;
((ILogical)x.Item1).LogicalParent = null;
}
if (x.Item2 is Control)
{
((IVisual)x.Item2).VisualParent = this;
((ILogical)x.Item2).LogicalParent = this;
}
});
}
public object Content
@ -25,6 +39,15 @@ namespace Perspex.Controls
set { this.SetValue(ContentProperty, value); }
}
IEnumerable<ILogical> ILogical.LogicalChildren
{
get
{
ILogical logicalChild = this.Content as ILogical;
return Enumerable.Repeat(logicalChild, logicalChild != null ? 1 : 0);
}
}
protected override Size ArrangeContent(Size finalSize)
{
Control child = ((IVisual)this).VisualChildren.SingleOrDefault() as Control;

25
Perspex/Controls/Control.cs

@ -33,7 +33,7 @@ namespace Perspex.Controls
Bottom,
}
public class Control : Interactive, ILayoutable, IStyleable, IStyled
public class Control : Interactive, ILayoutable, ILogical, IStyleable, IStyled
{
public static readonly PerspexProperty<Brush> BackgroundProperty =
PerspexProperty.Register<Control, Brush>("Background", inherits: true);
@ -56,6 +56,9 @@ namespace Perspex.Controls
public static readonly PerspexProperty<Thickness> MarginProperty =
PerspexProperty.Register<Control, Thickness>("Margin");
public static readonly ReadOnlyPerspexProperty<Control> ParentProperty =
new ReadOnlyPerspexProperty<Control>(ParentPropertyRW);
public static readonly PerspexProperty<VerticalAlignment> VerticalAlignmentProperty =
PerspexProperty.Register<Control, VerticalAlignment>("VerticalAlignment");
@ -65,6 +68,9 @@ namespace Perspex.Controls
public static readonly RoutedEvent<MouseEventArgs> MouseLeftButtonUpEvent =
RoutedEvent.Register<Control, MouseEventArgs>("MouseLeftButtonUp", RoutingStrategy.Bubble);
internal static readonly PerspexProperty<Control> ParentPropertyRW =
PerspexProperty.Register<Control, Control>("Parent");
private Classes classes;
private string id;
@ -208,6 +214,12 @@ namespace Perspex.Controls
set { this.SetValue(MarginProperty, value); }
}
public Control Parent
{
get { return this.GetValue(ParentPropertyRW); }
protected set { this.SetValue(ParentPropertyRW, value); }
}
public Styles Styles
{
get
@ -238,6 +250,17 @@ namespace Perspex.Controls
set { this.SetValue(VerticalAlignmentProperty, value); }
}
ILogical ILogical.LogicalParent
{
get { return this.Parent; }
set { this.Parent = (Control)value; }
}
IEnumerable<ILogical> ILogical.LogicalChildren
{
get { return Enumerable.Empty<ILogical>(); }
}
public ILayoutRoot GetLayoutRoot()
{
return this.GetVisualAncestorOrSelf<ILayoutRoot>();

14
Perspex/Controls/Decorator.cs

@ -21,12 +21,18 @@ namespace Perspex.Controls
public Decorator()
{
// TODO: Unset old content's visual parent.
this.GetObservable(ContentProperty).OfType<IVisual>().Subscribe(x =>
this.GetObservableWithHistory(ContentProperty).Subscribe(x =>
{
if (x != null)
if (x.Item1 != null)
{
x.VisualParent = this;
((IVisual)x.Item1).VisualParent = null;
((ILogical)x.Item1).LogicalParent = null;
}
if (x.Item2 != null)
{
((IVisual)x.Item2).VisualParent = this;
((ILogical)x.Item2).LogicalParent = this;
}
});
}

86
Perspex/Controls/LogicalChildren.cs

@ -0,0 +1,86 @@
// -----------------------------------------------------------------------
// <copyright file="LogicalChildren.cs" company="Steven Kirk">
// Copyright 2014 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Controls
{
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
/// <summary>
/// Manages parenting for a collection of logical child controls.
/// </summary>
/// <typeparam name="T">The type of the controls.</typeparam>
/// <remarks>
/// Unfortunately, because of ObservableCollection's handling of clear (the cleared items
/// aren't passed to the CollectionChanged event) we have to hold two lists of child controls:
/// the ones in Panel.Children and the ones here - held in case the ObservableCollection
/// gets cleared. It's either that or write a proper PerspexList which is too much work
/// for now.
/// </remarks>
internal class LogicalChildren<T> where T : class, ILogical, IVisual
{
private T parent;
private List<T> inner = new List<T>();
public LogicalChildren(T parent, INotifyCollectionChanged childrenCollection)
{
this.parent = parent;
childrenCollection.CollectionChanged += CollectionChanged;
}
private void Add(IEnumerable<T> items)
{
foreach (T item in items)
{
this.inner.Add(item);
item.LogicalParent = this.parent;
item.VisualParent = this.parent;
}
}
private void Remove(IEnumerable<T> items)
{
foreach (T item in items)
{
this.inner.Remove(item);
item.LogicalParent = null;
item.VisualParent = null;
}
}
private void Reset(IEnumerable<T> newState)
{
this.Add(newState.Except(this.inner));
this.Remove(this.inner.Except(newState));
}
private void CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
this.Add(e.NewItems.Cast<T>());
break;
case NotifyCollectionChangedAction.Remove:
this.Remove(e.OldItems.Cast<T>());
break;
case NotifyCollectionChangedAction.Replace:
this.Remove(e.OldItems.Cast<T>());
this.Add(e.NewItems.Cast<T>());
break;
case NotifyCollectionChangedAction.Reset:
this.Reset((IEnumerable<T>)sender);
break;
}
}
}
}

36
Perspex/Controls/Panel.cs

@ -0,0 +1,36 @@
// -----------------------------------------------------------------------
// <copyright file="Panel.cs" company="Steven Kirk">
// Copyright 2014 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Controls
{
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
/// <summary>
/// Base class for controls that can contain multiple children.
/// </summary>
public class Panel : Control
{
private LogicalChildren<Control> logicalChildren;
public Panel()
{
this.Children = new ObservableCollection<Control>();
this.logicalChildren = new LogicalChildren<Control>(this, this.Children);
}
public ObservableCollection<Control> Children
{
get;
private set;
}
}
}

12
Perspex/Visual.cs

@ -15,12 +15,6 @@ namespace Perspex
public abstract class Visual : PerspexObject, IVisual
{
public static readonly ReadOnlyPerspexProperty<Control> ParentProperty =
new ReadOnlyPerspexProperty<Control>(ParentPropertyRW);
internal static readonly PerspexProperty<Control> ParentPropertyRW =
PerspexProperty.Register<Control, Control>("Parent");
private IVisual visualParent;
public Rect Bounds
@ -29,12 +23,6 @@ namespace Perspex
protected set;
}
public Control Parent
{
get { return this.GetValue(ParentPropertyRW); }
protected set { this.SetValue(ParentPropertyRW, value); }
}
IEnumerable<IVisual> IVisual.ExistingVisualChildren
{
get { return Enumerable.Empty<Visual>(); }

Loading…
Cancel
Save