diff --git a/Perspex.Controls/ControlExtensions.cs b/Perspex.Controls/ControlExtensions.cs index 5eb2dc8a18..26f494e2d8 100644 --- a/Perspex.Controls/ControlExtensions.cs +++ b/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; diff --git a/Perspex.Controls/Panel.cs b/Perspex.Controls/Panel.cs index bd4033e1e9..faef20c742 100644 --- a/Perspex.Controls/Panel.cs +++ b/Perspex.Controls/Panel.cs @@ -76,6 +76,14 @@ namespace Perspex.Controls set { this.childLogicalParent = value; } } + protected virtual void OnChildrenAdded(IEnumerable child) + { + } + + protected virtual void OnChildrenRemoved(IEnumerable child) + { + } + private void ClearLogicalParent(IEnumerable controls) { foreach (var control in controls) @@ -94,23 +102,31 @@ namespace Perspex.Controls private void ChildrenChanged(object sender, NotifyCollectionChangedEventArgs e) { + List controls; + // TODO: Handle Replace. switch (e.Action) { case NotifyCollectionChangedAction.Add: - this.SetLogicalParent(e.NewItems.OfType()); + controls = e.NewItems.OfType().ToList(); + this.SetLogicalParent(controls); this.AddVisualChildren(e.NewItems.OfType()); + this.OnChildrenAdded(controls); break; case NotifyCollectionChangedAction.Remove: + controls = e.OldItems.OfType().ToList(); this.ClearLogicalParent(e.OldItems.OfType()); this.RemoveVisualChildren(e.OldItems.OfType()); + this.OnChildrenRemoved(controls); break; case NotifyCollectionChangedAction.Reset: - this.ClearLogicalParent(e.OldItems.OfType()); + controls = e.OldItems.OfType().ToList(); + this.ClearLogicalParent(controls); this.ClearVisualChildren(); this.AddVisualChildren(this.children); + this.OnChildrenAdded(controls); break; } diff --git a/Perspex.Controls/Perspex.Controls.csproj b/Perspex.Controls/Perspex.Controls.csproj index a09cad280f..da3cd831b9 100644 --- a/Perspex.Controls/Perspex.Controls.csproj +++ b/Perspex.Controls/Perspex.Controls.csproj @@ -44,6 +44,8 @@ + + diff --git a/Perspex.Controls/Presenters/ContentPresenter.cs b/Perspex.Controls/Presenters/ContentPresenter.cs index ccf0713746..96e5e1a966 100644 --- a/Perspex.Controls/Presenters/ContentPresenter.cs +++ b/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); } diff --git a/Perspex.Controls/Presenters/ItemsPresenter.cs b/Perspex.Controls/Presenters/ItemsPresenter.cs index 554900b013..a79ee9e4ab 100644 --- a/Perspex.Controls/Presenters/ItemsPresenter.cs +++ b/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; diff --git a/Perspex.Controls/Primitives/AdornerDecorator.cs b/Perspex.Controls/Primitives/AdornerDecorator.cs new file mode 100644 index 0000000000..c47f925c92 --- /dev/null +++ b/Perspex.Controls/Primitives/AdornerDecorator.cs @@ -0,0 +1,35 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2015 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +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); + } + } +} diff --git a/Perspex.Controls/Primitives/AdornerLayer.cs b/Perspex.Controls/Primitives/AdornerLayer.cs new file mode 100644 index 0000000000..02be0fd492 --- /dev/null +++ b/Perspex.Controls/Primitives/AdornerLayer.cs @@ -0,0 +1,75 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2015 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +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 AdornedElementProperty = + PerspexProperty.RegisterAttached("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() + .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 child) + { + this.InvalidateArrange(); + } + + protected override void OnChildrenRemoved(IEnumerable child) + { + this.InvalidateArrange(); + } + } +} diff --git a/Perspex.Controls/Templates/DataTemplateExtensions.cs b/Perspex.Controls/Templates/DataTemplateExtensions.cs index b70d1ec004..374d1971ff 100644 --- a/Perspex.Controls/Templates/DataTemplateExtensions.cs +++ b/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) { diff --git a/Perspex.Diagnostics/DevTools.cs b/Perspex.Diagnostics/DevTools.cs index 085afe0786..b99b11f2b8 100644 --- a/Perspex.Diagnostics/DevTools.cs +++ b/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 RootProperty = PerspexProperty.Register("Root"); + private Control adorner; + public DevTools() { TabStrip tabStrip; @@ -55,8 +61,8 @@ namespace Perspex.Diagnostics { DataTemplates = new DataTemplates { - new TreeDataTemplate(GetHeader, x => x.Children), - new TreeDataTemplate(GetHeader, x => x.Children), + new TreeDataTemplate(this.GetHeader, x => x.Children), + new TreeDataTemplate(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; + } } } } diff --git a/Perspex.Interactivity/Interactive.cs b/Perspex.Interactivity/Interactive.cs index 493b0c3380..1924c9fde8 100644 --- a/Perspex.Interactivity/Interactive.cs +++ b/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; diff --git a/Perspex.Interactivity/RoutedEvent.cs b/Perspex.Interactivity/RoutedEvent.cs index 5a523593f8..6441713280 100644 --- a/Perspex.Interactivity/RoutedEvent.cs +++ b/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 diff --git a/Perspex.SceneGraph/Visual.cs b/Perspex.SceneGraph/Visual.cs index 591da36471..6b95efc035 100644 --- a/Perspex.SceneGraph/Visual.cs +++ b/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) diff --git a/Perspex.Themes.Default/WindowStyle.cs b/Perspex.Themes.Default/WindowStyle.cs index a6380b5017..c1e4e7545c 100644 --- a/Perspex.Themes.Default/WindowStyle.cs +++ b/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], + } } }; }