From 3b19bc25b6b381b6452f2ade32622c0fae9a1685 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 16 Sep 2014 23:07:36 +0200 Subject: [PATCH] checkpoint --- .../Input/WindowsKeyboardDevice.cs | 4 + Perspex.Windows/Window.cs | 16 ++++ Perspex/Application.cs | 1 - Perspex/Controls/Control.cs | 4 +- Perspex/Controls/Decorator.cs | 8 +- Perspex/Controls/TemplatedControl.cs | 2 - Perspex/Controls/TreeViewItem.cs | 3 +- Perspex/Diagnostics/Debug.cs | 18 +++-- Perspex/Diagnostics/DevTools.cs | 14 ++++ Perspex/IVisual.cs | 47 +++++++++-- Perspex/Input/IInputElement.cs | 2 + Perspex/Input/InputElement.cs | 3 + Perspex/Input/KeyboardDevice.cs | 25 ++++-- Perspex/Interactive.cs | 30 +++++-- Perspex/Perspex.csproj | 1 + Perspex/Visual.cs | 81 ++++++++++++------- 16 files changed, 194 insertions(+), 65 deletions(-) create mode 100644 Perspex/Diagnostics/DevTools.cs diff --git a/Perspex.Windows/Input/WindowsKeyboardDevice.cs b/Perspex.Windows/Input/WindowsKeyboardDevice.cs index f805fc312f..63927affdd 100644 --- a/Perspex.Windows/Input/WindowsKeyboardDevice.cs +++ b/Perspex.Windows/Input/WindowsKeyboardDevice.cs @@ -55,6 +55,10 @@ namespace Perspex.Windows.Input } } + public void WindowActivated(Window window) + { + this.FocusedElement = window; + } public string StringFromVirtualKey(uint virtualKey) { diff --git a/Perspex.Windows/Window.cs b/Perspex.Windows/Window.cs index ab1db3d630..e81ca04d80 100644 --- a/Perspex.Windows/Window.cs +++ b/Perspex.Windows/Window.cs @@ -80,6 +80,8 @@ namespace Perspex.Windows }); } + public event EventHandler Activated; + public event EventHandler Closed; public Size ClientSize @@ -173,6 +175,16 @@ namespace Perspex.Windows } } + private void OnActivated() + { + WindowsKeyboardDevice.Instance.WindowActivated(this); + + if (this.Activated != null) + { + this.Activated(this, EventArgs.Empty); + } + } + private void OnClosed() { if (this.Closed != null) @@ -190,6 +202,10 @@ namespace Perspex.Windows switch ((UnmanagedMethods.WindowsMessage)msg) { + case UnmanagedMethods.WindowsMessage.WM_ACTIVATE: + this.OnActivated(); + break; + case UnmanagedMethods.WindowsMessage.WM_DESTROY: this.OnClosed(); break; diff --git a/Perspex/Application.cs b/Perspex/Application.cs index 13dcfe0c7d..a1e1e39e38 100644 --- a/Perspex/Application.cs +++ b/Perspex/Application.cs @@ -6,7 +6,6 @@ namespace Perspex { - using System.Threading; using Perspex.Controls; using Perspex.Input; using Perspex.Styling; diff --git a/Perspex/Controls/Control.cs b/Perspex/Controls/Control.cs index cc41890c05..9aa60ccdbf 100644 --- a/Perspex/Controls/Control.cs +++ b/Perspex/Controls/Control.cs @@ -11,6 +11,7 @@ namespace Perspex.Controls using System.Linq; using System.Reactive.Linq; using Perspex.Input; + using Perspex.Layout; using Perspex.Media; using Perspex.Styling; using Splat; @@ -190,11 +191,10 @@ namespace Perspex.Controls get { return Enumerable.Empty(); } } - protected override void OnAttachedToVisualTree() + protected override void OnAttachedToVisualTree(ILayoutRoot root) { IStyler styler = Locator.Current.GetService(); styler.ApplyStyles(this); - base.OnAttachedToVisualTree(); } protected void AddPseudoClass(PerspexProperty property, string className) diff --git a/Perspex/Controls/Decorator.cs b/Perspex/Controls/Decorator.cs index cd2e648f8e..19284479be 100644 --- a/Perspex/Controls/Decorator.cs +++ b/Perspex/Controls/Decorator.cs @@ -26,14 +26,14 @@ namespace Perspex.Controls { if (x.Item1 != null) { - ((IVisual)x.Item1).VisualParent = null; - ((ILogical)x.Item1).LogicalParent = null; + ((IVisual)this).RemoveVisualChild(x.Item1); + ((ILogical)this).RemoveLogicalChild(x.Item1); } if (x.Item2 != null) { - ((IVisual)x.Item2).VisualParent = this; - ((ILogical)x.Item2).LogicalParent = this; + ((IVisual)this).AddVisualChild(x.Item2); + ((ILogical)this).AddLogicalChild(x.Item2); } }); } diff --git a/Perspex/Controls/TemplatedControl.cs b/Perspex/Controls/TemplatedControl.cs index 8c4d426332..1b0b0b94ed 100644 --- a/Perspex/Controls/TemplatedControl.cs +++ b/Perspex/Controls/TemplatedControl.cs @@ -17,8 +17,6 @@ namespace Perspex.Controls public static readonly PerspexProperty TemplateProperty = PerspexProperty.Register("Template"); - private IVisual visualChild; - public ControlTemplate Template { get { return this.GetValue(TemplateProperty); } diff --git a/Perspex/Controls/TreeViewItem.cs b/Perspex/Controls/TreeViewItem.cs index 543f082e7e..acaed18b2b 100644 --- a/Perspex/Controls/TreeViewItem.cs +++ b/Perspex/Controls/TreeViewItem.cs @@ -46,9 +46,8 @@ namespace Perspex.Controls } } - protected override void OnAttachedToVisualTree() + protected override void OnVisualParentChanged(IVisual oldParent) { - base.OnAttachedToVisualTree(); this.parent = this.GetVisualAncestor(); } } diff --git a/Perspex/Diagnostics/Debug.cs b/Perspex/Diagnostics/Debug.cs index 1a612e679b..d656aa6405 100644 --- a/Perspex/Diagnostics/Debug.cs +++ b/Perspex/Diagnostics/Debug.cs @@ -1,12 +1,18 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Perspex.Controls; +// ----------------------------------------------------------------------- +// +// Copyright 2014 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- namespace Perspex.Diagnostics { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + using Perspex.Controls; + public static class Debug { public static string PrintVisualTree(IVisual visual) diff --git a/Perspex/Diagnostics/DevTools.cs b/Perspex/Diagnostics/DevTools.cs new file mode 100644 index 0000000000..2e6796dce6 --- /dev/null +++ b/Perspex/Diagnostics/DevTools.cs @@ -0,0 +1,14 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2014 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Diagnostics +{ + using Perspex.Controls; + + public class DevTools : Control + { + } +} diff --git a/Perspex/IVisual.cs b/Perspex/IVisual.cs index 96fb13bf0d..a6119915e9 100644 --- a/Perspex/IVisual.cs +++ b/Perspex/IVisual.cs @@ -6,28 +6,61 @@ namespace Perspex { - using System; - using System.Collections.Generic; using Perspex.Media; + /// + /// Represents a node in the visual scene graph. + /// + /// + /// The interface defines the interface required for a renderer to + /// render a scene graph. You should not usually need to reference this interface unless + /// you are writing a renderer; instead use the extension methods defined in + /// to traverse the scene graph. This interface is + /// implemented by . It should not be necessary to implement it + /// anywhere else. + /// public interface IVisual { - Rect Bounds { get; set; } - - IEnumerable ExistingVisualChildren { get; } + /// + /// Gets the bounds of the scene graph node. + /// + /// + Rect Bounds { get; } + /// + /// Gets a value indicating whether this scene graph node is visible. + /// bool IsVisible { get; } + /// + /// Gets the opacity of the scene graph node. + /// double Opacity { get; } + /// + /// Gets the render transform of the scene graph node. + /// Transform RenderTransform { get; } + /// + /// Gets the transform origin of the scene graph node. + /// Origin TransformOrigin { get; } - IEnumerable VisualChildren { get; } + /// + /// Gets the scene graph node's child nodes. + /// + PerspexList VisualChildren { get; } - IVisual VisualParent { get; set; } + /// + /// Gets the scene graph node's parent node. + /// + IVisual VisualParent { get; } + /// + /// Renders the scene graph node to a . + /// + /// The context. void Render(IDrawingContext context); } } diff --git a/Perspex/Input/IInputElement.cs b/Perspex/Input/IInputElement.cs index 6244ee2997..728c23ce91 100644 --- a/Perspex/Input/IInputElement.cs +++ b/Perspex/Input/IInputElement.cs @@ -31,5 +31,7 @@ namespace Perspex.Input bool IsPointerOver { get; } void Focus(); + + void RaiseEvent(RoutedEventArgs e); } } diff --git a/Perspex/Input/InputElement.cs b/Perspex/Input/InputElement.cs index 063acde0a7..0aedd05436 100644 --- a/Perspex/Input/InputElement.cs +++ b/Perspex/Input/InputElement.cs @@ -37,6 +37,9 @@ namespace Perspex.Input public static readonly RoutedEvent KeyDownEvent = RoutedEvent.Register("KeyDown", RoutingStrategy.Bubble); + public static readonly RoutedEvent PreviewKeyDownEvent = + RoutedEvent.Register("PreviewKeyDown", RoutingStrategy.Tunnel); + public static readonly RoutedEvent PointerEnterEvent = RoutedEvent.Register("PointerEnter", RoutingStrategy.Direct); diff --git a/Perspex/Input/KeyboardDevice.cs b/Perspex/Input/KeyboardDevice.cs index 1bb0e11b14..4069f60173 100644 --- a/Perspex/Input/KeyboardDevice.cs +++ b/Perspex/Input/KeyboardDevice.cs @@ -33,25 +33,40 @@ namespace Perspex.Input get { return Locator.Current.GetService(); } } + public IInputElement FocusedElement + { + get; + protected set; + } + public abstract ModifierKeys Modifiers { get; } private void ProcessRawEvent(RawKeyEventArgs e) { - Interactive interactive = FocusManager.Current as Interactive; + IInputElement element = this.FocusedElement; - if (interactive != null) + if (element != null) { switch (e.Type) { case RawKeyEventType.KeyDown: - interactive.RaiseEvent(new KeyEventArgs + element.RaiseEvent(new KeyEventArgs + { + RoutedEvent = Control.PreviewKeyDownEvent, + Device = this, + Key = e.Key, + Text = e.Text, + Source = element, + OriginalSource = element, + }); + element.RaiseEvent(new KeyEventArgs { RoutedEvent = Control.KeyDownEvent, Device = this, Key = e.Key, Text = e.Text, - Source = interactive, - OriginalSource = interactive, + Source = element, + OriginalSource = element, }); break; } diff --git a/Perspex/Interactive.cs b/Perspex/Interactive.cs index 4860820056..60c57b8b11 100644 --- a/Perspex/Interactive.cs +++ b/Perspex/Interactive.cs @@ -8,6 +8,7 @@ namespace Perspex { using System; using System.Collections.Generic; + using System.Linq; using System.Reactive; using System.Reactive.Linq; using Perspex.Layout; @@ -68,8 +69,9 @@ namespace Perspex case RoutingStrategy.Direct: this.RaiseEventImpl(e); break; - default: - throw new NotImplementedException(); + case RoutingStrategy.Tunnel: + this.TunnelEvent(e); + break; } } } @@ -78,12 +80,29 @@ namespace Perspex { Contract.Requires(e != null); - Interactive target = this; + foreach (var target in this.GetVisualAncestorsAndSelf().OfType()) + { + target.RaiseEventImpl(e); + + if (e.Handled) + { + break; + } + } + } + + private void TunnelEvent(RoutedEventArgs e) + { + Contract.Requires(e != null); - while (target != null) + foreach (var target in this.GetVisualAncestorsAndSelf().OfType().Reverse()) { target.RaiseEventImpl(e); - target = target.GetVisualAncestor(); + + if (e.Handled) + { + break; + } } } @@ -97,7 +116,6 @@ namespace Perspex { foreach (Delegate handler in delegates) { - // TODO: Implement the Handled stuff. handler.DynamicInvoke(this, e); } } diff --git a/Perspex/Perspex.csproj b/Perspex/Perspex.csproj index 0f06f881dc..fdce3cf2f8 100644 --- a/Perspex/Perspex.csproj +++ b/Perspex/Perspex.csproj @@ -101,6 +101,7 @@ + diff --git a/Perspex/Visual.cs b/Perspex/Visual.cs index 430e83baae..7749601160 100644 --- a/Perspex/Visual.cs +++ b/Perspex/Visual.cs @@ -8,6 +8,7 @@ namespace Perspex { using System; using System.Collections.Generic; + using System.Collections.Specialized; using System.Linq; using Perspex.Layout; using Perspex.Media; @@ -28,10 +29,12 @@ namespace Perspex public static readonly PerspexProperty TransformOriginProperty = PerspexProperty.Register("TransformOrigin", defaultValue: Origin.Default); - private IVisual visualParent; - private Rect bounds; + private PerspexList visualChildren; + + private IVisual visualParent; + static Visual() { AffectsRender(IsVisibleProperty); @@ -69,38 +72,28 @@ namespace Perspex IEnumerable IVisual.ExistingVisualChildren { - get { return ((IVisual)this).VisualChildren; } + get { return this.visualChildren != null ? this.visualChildren : Enumerable.Empty(); } } - IEnumerable IVisual.VisualChildren - { - get { return Enumerable.Empty(); } - } - - IVisual IVisual.VisualParent + PerspexList IVisual.VisualChildren { - get - { - return this.visualParent; - } - - set + get { - if (this.visualParent != value) + if (this.visualChildren == null) { - IVisual oldValue = this.visualParent; - this.visualParent = value; - this.InheritanceParent = (PerspexObject)value; - this.OnVisualParentChanged(oldValue, value); - - if (this.GetVisualAncestor() != null) - { - this.NotifyAttachedToVisualTree(); - } + this.visualChildren = new PerspexList(this.CreateVisualChildren()); + this.visualChildren.CollectionChanged += VisualChildrenChanged; } + + return this.visualChildren; } } + IVisual IVisual.VisualParent + { + get { return this.visualParent; } + } + protected static void AffectsRender(PerspexProperty property) { property.Changed.Subscribe(AffectsRenderInvalidate); @@ -131,26 +124,54 @@ namespace Perspex Contract.Requires(context != null); } - protected virtual void OnAttachedToVisualTree() + private IEnumerable CreateVisualChildren() + { + return Enumerable.Empty(); + } + + protected virtual void OnAttachedToVisualTree(ILayoutRoot root) { } - protected virtual void OnVisualParentChanged(IVisual oldValue, IVisual newValue) + protected virtual void OnDetachedFromVisualTree(ILayoutRoot oldRoot) { } - private void NotifyAttachedToVisualTree() + protected virtual void OnVisualParentChanged(IVisual oldParent) + { + } + + private void VisualChildrenChanged(object sender, NotifyCollectionChangedEventArgs e) + { + } + + private void NotifyAttachedToVisualTree(ILayoutRoot root) { this.Log().Debug(string.Format( "Attached {0} (#{1:x8}) to visual tree", this.GetType().Name, this.GetHashCode())); - this.OnAttachedToVisualTree(); + this.OnAttachedToVisualTree(root); + + foreach (Visual child in ((IVisual)this).ExistingVisualChildren.OfType()) + { + child.NotifyAttachedToVisualTree(root); + } + } + + private void NotifyDetachedFromVisualTree(ILayoutRoot oldRoot) + { + this.Log().Debug(string.Format( + "Detached {0} (#{1:x8}) from visual tree", + this.GetType().Name, + this.GetHashCode())); + + this.OnDetachedFromVisualTree(oldRoot); foreach (Visual child in ((IVisual)this).ExistingVisualChildren.OfType()) { - child.NotifyAttachedToVisualTree(); + child.NotifyDetachedFromVisualTree(oldRoot); } } }