Browse Source

Initial implementation of adorners.

Adorn elements when moused over in DevTools. Still a fair few things
missing including:

- Need to track position of adorned elements and move the adorner if
they move.
- Adorner is shown beneath AdornerDecorator contents. Need to implement
Z-index to allow it to be shown above.
pull/39/head
Steven Kirk 12 years ago
parent
commit
fcb746a1c4
  1. 1
      Perspex.Controls/ControlExtensions.cs
  2. 20
      Perspex.Controls/Panel.cs
  3. 2
      Perspex.Controls/Perspex.Controls.csproj
  4. 1
      Perspex.Controls/Presenters/ContentPresenter.cs
  5. 1
      Perspex.Controls/Presenters/ItemsPresenter.cs
  6. 35
      Perspex.Controls/Primitives/AdornerDecorator.cs
  7. 75
      Perspex.Controls/Primitives/AdornerLayer.cs
  8. 1
      Perspex.Controls/Templates/DataTemplateExtensions.cs
  9. 58
      Perspex.Diagnostics/DevTools.cs
  10. 2
      Perspex.Interactivity/Interactive.cs
  11. 6
      Perspex.Interactivity/RoutedEvent.cs
  12. 2
      Perspex.SceneGraph/Visual.cs
  13. 10
      Perspex.Themes.Default/WindowStyle.cs

1
Perspex.Controls/ControlExtensions.cs

@ -10,6 +10,7 @@ namespace Perspex.Controls
using System.Collections.Generic;
using System.Linq;
using Perspex.Controls;
using Perspex.Controls.Primitives;
using Perspex.LogicalTree;
using Perspex.Styling;
using Perspex.VisualTree;

20
Perspex.Controls/Panel.cs

@ -76,6 +76,14 @@ namespace Perspex.Controls
set { this.childLogicalParent = value; }
}
protected virtual void OnChildrenAdded(IEnumerable<Control> child)
{
}
protected virtual void OnChildrenRemoved(IEnumerable<Control> child)
{
}
private void ClearLogicalParent(IEnumerable<Control> controls)
{
foreach (var control in controls)
@ -94,23 +102,31 @@ namespace Perspex.Controls
private void ChildrenChanged(object sender, NotifyCollectionChangedEventArgs e)
{
List<Control> controls;
// TODO: Handle Replace.
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
this.SetLogicalParent(e.NewItems.OfType<Control>());
controls = e.NewItems.OfType<Control>().ToList();
this.SetLogicalParent(controls);
this.AddVisualChildren(e.NewItems.OfType<Visual>());
this.OnChildrenAdded(controls);
break;
case NotifyCollectionChangedAction.Remove:
controls = e.OldItems.OfType<Control>().ToList();
this.ClearLogicalParent(e.OldItems.OfType<Control>());
this.RemoveVisualChildren(e.OldItems.OfType<Visual>());
this.OnChildrenRemoved(controls);
break;
case NotifyCollectionChangedAction.Reset:
this.ClearLogicalParent(e.OldItems.OfType<Control>());
controls = e.OldItems.OfType<Control>().ToList();
this.ClearLogicalParent(controls);
this.ClearVisualChildren();
this.AddVisualChildren(this.children);
this.OnChildrenAdded(controls);
break;
}

2
Perspex.Controls/Perspex.Controls.csproj

@ -44,6 +44,8 @@
<Compile Include="Popup.cs" />
<Compile Include="IDataTemplate.cs" />
<Compile Include="Presenters\IPresenter.cs" />
<Compile Include="Primitives\AdornerDecorator.cs" />
<Compile Include="Primitives\AdornerLayer.cs" />
<Compile Include="RadioButton.cs" />
<Compile Include="CheckBox.cs" />
<Compile Include="ColumnDefinition.cs" />

1
Perspex.Controls/Presenters/ContentPresenter.cs

@ -94,7 +94,6 @@ namespace Perspex.Controls.Presenters
templatedParent = templatedParent.TemplatedParent as TemplatedControl;
}
result.DataContext = content;
result.TemplatedParent = templatedParent;
this.AddVisualChild(result);
}

1
Perspex.Controls/Presenters/ItemsPresenter.cs

@ -97,6 +97,7 @@ namespace Perspex.Controls.Presenters
if (value.Item1 != null)
{
generator.RemoveAll();
this.Panel.Children.Clear();
INotifyCollectionChanged incc = value.Item1 as INotifyCollectionChanged;

35
Perspex.Controls/Primitives/AdornerDecorator.cs

@ -0,0 +1,35 @@
// -----------------------------------------------------------------------
// <copyright file="AdornerDecorator.cs" company="Steven Kirk">
// Copyright 2015 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Controls.Primitives
{
public class AdornerDecorator : Decorator
{
public AdornerDecorator()
{
this.AdornerLayer = new AdornerLayer();
this.AdornerLayer.Parent = this;
this.AddVisualChild(this.AdornerLayer);
}
public AdornerLayer AdornerLayer
{
get;
}
protected override Size MeasureOverride(Size availableSize)
{
this.AdornerLayer.Measure(availableSize);
return base.MeasureOverride(availableSize);
}
protected override Size ArrangeOverride(Size finalSize)
{
this.AdornerLayer.Arrange(new Rect(finalSize));
return base.ArrangeOverride(finalSize);
}
}
}

75
Perspex.Controls/Primitives/AdornerLayer.cs

@ -0,0 +1,75 @@
// -----------------------------------------------------------------------
// <copyright file="AdornerLayer.cs" company="Steven Kirk">
// Copyright 2015 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Controls.Primitives
{
using System.Collections.Generic;
using System.Linq;
using Perspex.VisualTree;
// TODO: Need to track position of adorned elements and move the adorner if they move.
public class AdornerLayer : Panel
{
public static PerspexProperty<Visual> AdornedElementProperty =
PerspexProperty.RegisterAttached<AdornerLayer, Visual, Visual>("AdornedElement");
static AdornerLayer()
{
IsHitTestVisibleProperty.OverrideDefaultValue(typeof(AdornerLayer), false);
}
public static Visual GetAdornedElement(Visual adorner)
{
return adorner.GetValue(AdornedElementProperty);
}
public static void SetAdornedElement(Visual adorner, Visual adorned)
{
adorner.SetValue(AdornedElementProperty, adorned);
}
public static AdornerLayer GetAdornerLayer(IVisual visual)
{
return visual.GetVisualAncestors()
.OfType<AdornerDecorator>()
.FirstOrDefault()
?.AdornerLayer;
}
protected override Size ArrangeOverride(Size finalSize)
{
var parent = this.Parent;
foreach (var child in this.Children)
{
var adorned = GetAdornedElement(child);
if (adorned != null)
{
var transform = adorned.TransformToVisual(parent);
var position = new Point(0, 0) * transform;
child.Arrange(new Rect(position, child.DesiredSize.Value));
}
else
{
child.Arrange(new Rect(child.DesiredSize.Value));
}
}
return finalSize;
}
protected override void OnChildrenAdded(IEnumerable<Control> child)
{
this.InvalidateArrange();
}
protected override void OnChildrenRemoved(IEnumerable<Control> child)
{
this.InvalidateArrange();
}
}
}

1
Perspex.Controls/Templates/DataTemplateExtensions.cs

@ -20,6 +20,7 @@ namespace Perspex.Controls.Templates
if (template != null)
{
result = template.Build(data);
result.DataContext = data;
}
else if (data is Control)
{

58
Perspex.Diagnostics/DevTools.cs

@ -11,8 +11,12 @@ namespace Perspex.Diagnostics
using System.Reactive.Linq;
using Perspex.Controls;
using Perspex.Controls.Primitives;
using Perspex.Controls.Shapes;
using Perspex.Diagnostics.ViewModels;
using Perspex.Input;
using Perspex.Interactivity;
using Perspex.Layout;
using Perspex.Media;
using ReactiveUI;
public class DevTools : Decorator
@ -20,6 +24,8 @@ namespace Perspex.Diagnostics
public static readonly PerspexProperty<Control> RootProperty =
PerspexProperty.Register<DevTools, Control>("Root");
private Control adorner;
public DevTools()
{
TabStrip tabStrip;
@ -55,8 +61,8 @@ namespace Perspex.Diagnostics
{
DataTemplates = new DataTemplates
{
new TreeDataTemplate<LogicalTreeNode>(GetHeader, x => x.Children),
new TreeDataTemplate<VisualTreeNode>(GetHeader, x => x.Children),
new TreeDataTemplate<LogicalTreeNode>(this.GetHeader, x => x.Children),
new TreeDataTemplate<VisualTreeNode>(this.GetHeader, x => x.Children),
},
[!TreeView.ItemsProperty] = tabStrip.WhenAnyValue(x => x.SelectedTab.Tag),
[Grid.RowProperty] = 1,
@ -152,9 +158,9 @@ namespace Perspex.Diagnostics
};
}
private static Control GetHeader(LogicalTreeNode node)
private Control GetHeader(LogicalTreeNode node)
{
return new StackPanel
var result = new StackPanel
{
Orientation = Orientation.Horizontal,
Gap = 8,
@ -170,11 +176,16 @@ namespace Perspex.Diagnostics
}
}
};
result.PointerEnter += this.AddAdorner;
result.PointerLeave += this.RemoveAdorner;
return result;
}
private static Control GetHeader(VisualTreeNode node)
private Control GetHeader(VisualTreeNode node)
{
return new StackPanel
var result = new StackPanel
{
Orientation = Orientation.Horizontal,
Gap = 8,
@ -191,6 +202,41 @@ namespace Perspex.Diagnostics
}
}
};
result.PointerEnter += this.AddAdorner;
result.PointerLeave += this.RemoveAdorner;
return result;
}
private void AddAdorner(object sender, PointerEventArgs e)
{
var node = (TreeNode)((Control)sender).DataContext;
var layer = AdornerLayer.GetAdornerLayer(node.Control);
if (layer != null)
{
var size = node.Control.GetObservable(Visual.BoundsProperty).Select(x => x.Size);
this.adorner = new Rectangle
{
Fill = new SolidColorBrush(0x80a0c5e8),
[!Layoutable.WidthProperty] = size.Select(x => (object)x.Width),
[!Layoutable.HeightProperty] = size.Select(x => (object)x.Height),
[AdornerLayer.AdornedElementProperty] = node.Control,
};
layer.Children.Add(this.adorner);
}
}
private void RemoveAdorner(object sender, PointerEventArgs e)
{
if (this.adorner != null)
{
((Panel)this.adorner.Parent).Children.Remove(this.adorner);
this.adorner = null;
}
}
}
}

2
Perspex.Interactivity/Interactive.cs

@ -147,7 +147,7 @@ namespace Perspex.Interactivity
foreach (var sub in subscriptions.ToList())
{
bool correctRoute =
(e.Route == RoutingStrategies.Direct && sub.Routes == RoutingStrategies.Direct) ||
(e.Route == RoutingStrategies.Direct && (sub.Routes & RoutingStrategies.Direct) != 0) ||
(e.Route != RoutingStrategies.Direct && (e.Route & sub.Routes) != 0);
bool notFinished = !e.Handled || sub.AlsoIfHandled;

6
Perspex.Interactivity/RoutedEvent.cs

@ -14,9 +14,9 @@ namespace Perspex.Interactivity
[Flags]
public enum RoutingStrategies
{
Direct = 0,
Tunnel = 1,
Bubble = 2,
Direct = 0x01,
Tunnel = 0x02,
Bubble = 0x04,
}
public class RoutedEvent

2
Perspex.SceneGraph/Visual.cs

@ -131,7 +131,7 @@ namespace Perspex
{
var thisOffset = GetOffsetFromRoot(this).Item2;
var thatOffset = GetOffsetFromRoot(visual).Item2;
return Matrix.Translation(-thisOffset) * Matrix.Translation(thatOffset);
return Matrix.Translation(-thatOffset) * Matrix.Translation(thisOffset);
}
protected static void AffectsRender(PerspexProperty property)

10
Perspex.Themes.Default/WindowStyle.cs

@ -9,6 +9,7 @@ namespace Perspex.Themes.Default
using System.Linq;
using Perspex.Controls;
using Perspex.Controls.Presenters;
using Perspex.Controls.Primitives;
using Perspex.Media;
using Perspex.Styling;
@ -35,10 +36,13 @@ namespace Perspex.Themes.Default
return new Border
{
[~Border.BackgroundProperty] = control[~Window.BackgroundProperty],
Content = new ContentPresenter
Content = new AdornerDecorator
{
Id = "contentPresenter",
[~ContentPresenter.ContentProperty] = control[~Window.ContentProperty],
Content = new ContentPresenter
{
Id = "contentPresenter",
[~ContentPresenter.ContentProperty] = control[~Window.ContentProperty],
}
}
};
}

Loading…
Cancel
Save