From 2c63ccdd39178cff5ad3279e203c34a81f210068 Mon Sep 17 00:00:00 2001 From: Bret Johnson Date: Tue, 12 Oct 2021 12:05:47 -0400 Subject: [PATCH] Update peer handling --- .../WpfNativeVisualEnvironment.cs | 2 +- ...dardUIControlWpf.cs => StandardControl.cs} | 84 ++++++++----- src/StandardUI.WPF/StandardUIFactory.cs | 4 - .../StandardUIFrameworkElement.cs | 8 +- .../StandardUIUserControlWpf.cs | 41 ------- .../IStandardControlEnvironmentPeer.cs | 7 ++ .../IStandardUIControlEnvironmentPeer.cs | 17 --- .../Controls/StandardControlImplementation.cs | 100 +++++++++++++++ src/StandardUI/Controls/StandardUIControl.cs | 116 ------------------ ...s => StandardUserControlImplementation.cs} | 6 +- src/StandardUI/FactoryStatics.cs | 3 - src/StandardUI/IStandardUIFactory.cs | 4 - .../UnintializedStandardUIFactory.cs | 5 - 13 files changed, 172 insertions(+), 225 deletions(-) rename src/StandardUI.WPF/{StandardUIControlWpf.cs => StandardControl.cs} (57%) delete mode 100644 src/StandardUI.WPF/StandardUIUserControlWpf.cs create mode 100644 src/StandardUI/Controls/IStandardControlEnvironmentPeer.cs delete mode 100644 src/StandardUI/Controls/IStandardUIControlEnvironmentPeer.cs create mode 100644 src/StandardUI/Controls/StandardControlImplementation.cs delete mode 100755 src/StandardUI/Controls/StandardUIControl.cs rename src/StandardUI/Controls/{StandardUIUserControl.cs => StandardUserControlImplementation.cs} (77%) diff --git a/src/StandardUI.VisualEnvironment.WpfNative/WpfNativeVisualEnvironment.cs b/src/StandardUI.VisualEnvironment.WpfNative/WpfNativeVisualEnvironment.cs index e0e5284..c11b0da 100755 --- a/src/StandardUI.VisualEnvironment.WpfNative/WpfNativeVisualEnvironment.cs +++ b/src/StandardUI.VisualEnvironment.WpfNative/WpfNativeVisualEnvironment.cs @@ -4,7 +4,7 @@ namespace Microsoft.StandardUI.VisualEnvironment.WpfNative { public class WpfNativeVisualEnvironment : IVisualEnvironment { - public IVisualizer CreateVisualizer(in Rect cullingRect) => new WpfNativeVisualizer(cullingRect); + public IDrawingContext CreateDrawingContext(in Rect cullingRect) => new WpfNativeVisualizer(cullingRect); public void RenderToBuffer(IVisual visual, IntPtr pixels, int width, int height, int rowBytes) { diff --git a/src/StandardUI.WPF/StandardUIControlWpf.cs b/src/StandardUI.WPF/StandardControl.cs similarity index 57% rename from src/StandardUI.WPF/StandardUIControlWpf.cs rename to src/StandardUI.WPF/StandardControl.cs index 9d1f4f5..3fa69a9 100644 --- a/src/StandardUI.WPF/StandardUIControlWpf.cs +++ b/src/StandardUI.WPF/StandardControl.cs @@ -1,52 +1,60 @@ using Microsoft.StandardUI.Controls; +using System; +using System.Windows; using System.Windows.Media; namespace Microsoft.StandardUI.Wpf { - public class StandardUIControlWpf : System.Windows.Controls.Control, IStandardUIControlEnvironmentPeer + public class StandardControl : System.Windows.Controls.Control, IControl, IStandardControlEnvironmentPeer where TControl : IControl { - private StandardUIControl _standardUIControl; - private ControlTemplateWpf? _controlTemplateWpf; + private StandardControlImplementation _implementation; + private StandardUIFrameworkElement? _buildContent; - public StandardUIControlWpf(StandardUIControl standardUIControl) + protected void InitImplementation(StandardControlImplementation implementation) { - _standardUIControl = standardUIControl; + _implementation = implementation; + + _buildContent = (StandardUIFrameworkElement?)implementation.Build(); - Width = standardUIControl.Width; - MinWidth = standardUIControl.MinWidth; - MaxWidth = standardUIControl.MaxWidth; - Height = standardUIControl.Height; - MinHeight = standardUIControl.MinHeight; - MaxHeight = standardUIControl.MaxHeight; + if (_buildContent != null) + { + AddVisualChild(_buildContent); + AddLogicalChild(_buildContent); + } + } + + void IUIElement.Measure(Size availableSize) + { + Measure(availableSize.ToWpfSize()); + } + + void IUIElement.Arrange(Rect finalRect) + { + Arrange(finalRect.ToWpfRect()); + } + + Size IUIElement.DesiredSize => SizeExtensions.FromWpfSize(DesiredSize); + + IUIPropertyObject? IControl.GetTemplateChild(string childName) + { + throw new System.NotImplementedException(); } protected override System.Windows.Size MeasureOverride(System.Windows.Size constraint) { - _standardUIControl.Measure(new Size(constraint.Width, constraint.Height)); - return _standardUIControl.DesiredSize.ToWpfSize(); + _implementation.Measure(new Size(constraint.Width, constraint.Height)); + return _implementation.DesiredSize.ToWpfSize(); } protected override System.Windows.Size ArrangeOverride(System.Windows.Size arrangeSize) { - _standardUIControl.Arrange(new Rect(0, 0, arrangeSize.Width, arrangeSize.Height)); + _implementation.Arrange(new Rect(0, 0, arrangeSize.Width, arrangeSize.Height)); return arrangeSize; } - /* - protected override int VisualChildrenCount => _standardUIControl.Content != null ? 1 : 0; + double IUIElement.ActualX => throw new System.NotImplementedException(); - protected override Visual GetVisualChild(int index) - { - IUIElement? content = _standardUIControl.Content; - - if (content == null) - throw new ArgumentOutOfRangeException("index", index, "Control has no content"); - if (index != 0) - throw new ArgumentOutOfRangeException("index", index, "Index out of range; control only has a single visual child."); - - return (Visual)content; - } - */ + double IUIElement.ActualY => throw new System.NotImplementedException(); public object GetValue(IUIProperty dp) { @@ -66,7 +74,22 @@ namespace Microsoft.StandardUI.Wpf SetValue(wpfDependencyProperty, value); } - IControlTemplate? IStandardUIControlEnvironmentPeer.Template + protected override int VisualChildrenCount => _buildContent != null ? 1 : 0; + + IUIElement? IStandardControlEnvironmentPeer.BuildContent => _buildContent; + + protected override Visual GetVisualChild(int index) + { + if (_buildContent == null) + throw new ArgumentOutOfRangeException("index", index, "Control returned null from build"); + if (index != 0) + throw new ArgumentOutOfRangeException("index", index, "Index out of range; control only has a single visual child."); + + return _buildContent; + } + +#if false + IControlTemplate? IControl.Template { get { @@ -90,7 +113,9 @@ namespace Microsoft.StandardUI.Wpf _controlTemplateWpf = controlTemplateWpf; } } +#endif +#if false IUIPropertyObject? IStandardUIControlEnvironmentPeer.GetTemplateChild(string childName) { System.Windows.DependencyObject? child = this.GetTemplateChild(childName); @@ -100,5 +125,6 @@ namespace Microsoft.StandardUI.Wpf // TODO:Finish this return null; } +#endif } } diff --git a/src/StandardUI.WPF/StandardUIFactory.cs b/src/StandardUI.WPF/StandardUIFactory.cs index a43d3bf..7d13f35 100644 --- a/src/StandardUI.WPF/StandardUIFactory.cs +++ b/src/StandardUI.WPF/StandardUIFactory.cs @@ -43,10 +43,6 @@ namespace Microsoft.StandardUI.Wpf public IPolyQuadraticBezierSegment CreatePolyQuadraticBezierSegment(Points points) => throw new NotImplementedException(); public IQuadraticBezierSegment CreateQuadraticBezierSegment(in Point point1, in Point point2) => throw new NotImplementedException(); - /*** Environment peers ***/ - - public IStandardUIControlEnvironmentPeer CreateStandardUIControlEnvironmentPeer(StandardUIControl standardUIControl) => new StandardUIControlWpf(standardUIControl); - /*** Infrastructure objects ***/ public IUIPropertyMetadata CreatePropertyMetadata(object defaultValue) => throw new NotImplementedException(); diff --git a/src/StandardUI.WPF/StandardUIFrameworkElement.cs b/src/StandardUI.WPF/StandardUIFrameworkElement.cs index a3dc143..6d77650 100644 --- a/src/StandardUI.WPF/StandardUIFrameworkElement.cs +++ b/src/StandardUI.WPF/StandardUIFrameworkElement.cs @@ -11,12 +11,12 @@ namespace Microsoft.StandardUI.Wpf { StandardUIFrameworkElementHelper _helper = new StandardUIFrameworkElementHelper(); - public void Measure(Size availableSize) + void IUIElement.Measure(Size availableSize) { Measure(availableSize.ToWpfSize()); } - public void Arrange(Rect finalRect) + void IUIElement.Arrange(Rect finalRect) { Arrange(finalRect.ToWpfRect()); } @@ -39,7 +39,7 @@ namespace Microsoft.StandardUI.Wpf Rect cullingRect = new Rect(0, 0, 200, 200); IVisual visual; - using (IVisualizer visualizer = visualEnvironment.CreateVisualizer(cullingRect)) { + using (IDrawingContext visualizer = visualEnvironment.CreateDrawingContext(cullingRect)) { OnVisualize(visualizer); visual = visualizer.End(); } @@ -53,7 +53,7 @@ namespace Microsoft.StandardUI.Wpf InvalidateVisual(); } - public virtual void OnVisualize(IVisualizer visualizer) + public virtual void OnVisualize(IDrawingContext visualizer) { } diff --git a/src/StandardUI.WPF/StandardUIUserControlWpf.cs b/src/StandardUI.WPF/StandardUIUserControlWpf.cs deleted file mode 100644 index ee8d416..0000000 --- a/src/StandardUI.WPF/StandardUIUserControlWpf.cs +++ /dev/null @@ -1,41 +0,0 @@ -using Microsoft.StandardUI.Controls; -using System; -using System.Windows.Media; - -namespace Microsoft.StandardUI.Wpf -{ - public class StandardUIUserControlWpf : StandardUIControlWpf - { - private StandardUIUserControl _userControl; - - public StandardUIUserControlWpf(StandardUIUserControl userControl) : base(userControl) - { - _userControl = userControl; - - Width = userControl.Width; - MinWidth = userControl.MinWidth; - MaxWidth = userControl.MaxWidth; - Height = userControl.Height; - MinHeight = userControl.MinHeight; - MaxHeight = userControl.MaxHeight; - - var content = (System.Windows.UIElement?) userControl.Content; - if (content != null) - AddLogicalChild(content); - } - - protected override int VisualChildrenCount => _userControl.Content != null ? 1 : 0; - - protected override Visual GetVisualChild(int index) - { - IUIElement? content = _userControl.Content; - - if (content == null) - throw new ArgumentOutOfRangeException("index", index, "Control has no content"); - if (index != 0) - throw new ArgumentOutOfRangeException("index", index, "Index out of range; control only has a single visual child."); - - return (Visual)content; - } - } -} diff --git a/src/StandardUI/Controls/IStandardControlEnvironmentPeer.cs b/src/StandardUI/Controls/IStandardControlEnvironmentPeer.cs new file mode 100644 index 0000000..7b7b9ee --- /dev/null +++ b/src/StandardUI/Controls/IStandardControlEnvironmentPeer.cs @@ -0,0 +1,7 @@ +namespace Microsoft.StandardUI.Controls +{ + public interface IStandardControlEnvironmentPeer : IDependencyObjectEnvironmentPeer + { + public IUIElement? BuildContent { get; } + } +} diff --git a/src/StandardUI/Controls/IStandardUIControlEnvironmentPeer.cs b/src/StandardUI/Controls/IStandardUIControlEnvironmentPeer.cs deleted file mode 100644 index 7e02b89..0000000 --- a/src/StandardUI/Controls/IStandardUIControlEnvironmentPeer.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Microsoft.StandardUI.Controls -{ - public interface IStandardUIControlEnvironmentPeer : IDependencyObjectEnvironmentPeer - { - double Width { get; set; } - public double MinWidth { get; set; } - public double MaxWidth { get; set; } - - double Height { get; set; } - public double MinHeight { get; set; } - public double MaxHeight { get; set; } - - public IControlTemplate? Template { get; set; } - - public IUIPropertyObject? GetTemplateChild(string childName); - } -} diff --git a/src/StandardUI/Controls/StandardControlImplementation.cs b/src/StandardUI/Controls/StandardControlImplementation.cs new file mode 100644 index 0000000..97a750e --- /dev/null +++ b/src/StandardUI/Controls/StandardControlImplementation.cs @@ -0,0 +1,100 @@ +using System; + +namespace Microsoft.StandardUI.Controls +{ + public abstract class StandardControlImplementation where TControl : IControl + { + public TControl Control { get; } + + public StandardControlImplementation(TControl control) + { + Control = control; + } + + /// + /// Create the contents of the control. + /// + /// control contents, or null if all content is drawn via Render + public virtual IUIElement? Build() => null; + + /// + /// This method can be overridden to add further graphical elements (not previously defined in a logical tree) to a control. + /// The drawing instructions here aren't displayed immediately; instead they captured in an IVisual, shown later as part + /// of rendering the control. + /// + /// There are two basic ways to create output for a control - create children for it (including shape primitives) + /// or create an IVisual by drawing to the IDrawingContext here. You can also combine approaches. In general, creating + /// children is more flexible, as those children can get input events and be replaced via templates. Each child will + /// also show up in UI hierarchy inspector tooling (like the Live Visual Tree in Visual Studio) as a separate element. + /// On the other hand, drawing to the IDrawingContext is lighter weight - all drawing instructions are combined into a + /// single IVisual, which in some cases is even stored on the GPU. + /// + /// drawing context that should draw to + public virtual void Render(IDrawingContext drawingContext) { } + + /// + /// Retrieves the named element in the instantiated ControlTemplate visual tree. + /// + /// The name of the element to find. + /// The named element from the template, if the element is found. Can + /// return null if no element with name childName was found in the template. + protected IUIPropertyObject GetTemplateChild(string childName) + { + // TODO: Finish this + return null; + } + + public Size DesiredSize { get; private set; } + + public void Measure(Size availableSize) + { + var desiredSize = MeasureOverride(availableSize); + + //enforce that MeasureCore can not return PositiveInfinity size even if given Infinte availabel size. + //Note: NegativeInfinity can not be returned by definition of Size structure. + if (double.IsPositiveInfinity(desiredSize.Width) || double.IsPositiveInfinity(desiredSize.Height)) + throw new InvalidOperationException($"Layout measurement override of element '{GetType().FullName}' should not return PositiveInfinity as its DesiredSize, even if Infinity is passed in as available size."); + + //enforce that MeasureCore cannot return NaN size. + if (double.IsNaN(desiredSize.Width) || double.IsNaN(desiredSize.Height)) + throw new InvalidOperationException($"Layout measurement override of element '{GetType().FullName}' should not return NaN values as its DesiredSize."); + + DesiredSize = desiredSize; + } + + public void Arrange(Rect finalRect) + { + ArrangeOverride(new Size(finalRect.Width, finalRect.Height)); + } + + protected virtual Size MeasureOverride(Size availableSize) + { + IUIElement? buildContent = EnvironmentPeer.BuildContent; + + // By default, return the size of the content + if (buildContent != null) + { + buildContent.Measure(availableSize); + return buildContent.DesiredSize; + } + + return new Size(0.0, 0.0); + } + + protected virtual Size ArrangeOverride(Size finalSize) + { + IUIElement? buildContent = EnvironmentPeer.BuildContent; + + // By default, give all the space to the content + if (buildContent != null) + { + Rect finalRect = new Rect(0, 0, finalSize.Width, finalSize.Height); + buildContent.Arrange(finalRect); + } + + return finalSize; + } + + private IStandardControlEnvironmentPeer EnvironmentPeer => (IStandardControlEnvironmentPeer)Control; + } +} diff --git a/src/StandardUI/Controls/StandardUIControl.cs b/src/StandardUI/Controls/StandardUIControl.cs deleted file mode 100755 index 87836d3..0000000 --- a/src/StandardUI/Controls/StandardUIControl.cs +++ /dev/null @@ -1,116 +0,0 @@ -using System; -using static Microsoft.StandardUI.FactoryStatics; - -namespace Microsoft.StandardUI.Controls -{ - public abstract class StandardUIControl : IUIElement - { - IStandardUIControlEnvironmentPeer _peer; - - public StandardUIControl(IStandardUIControlEnvironmentPeer? peer = null) - { - if (peer == null) - peer = StandardUIControlEnvironmentPeer(this); - _peer = peer; - } - - public double Width - { - get => _peer.Width; - set => _peer.Height = value; - } - - public double MinWidth - { - get => _peer.MinWidth; - set => _peer.MinWidth = value; - } - - public double MaxWidth - { - get => _peer.MaxWidth; - set => _peer.MaxWidth = value; - } - - public double Height - { - get => _peer.Height; - set => _peer.Height = value; - } - - public double MinHeight - { - get => _peer.MinHeight; - set => _peer.MinHeight = value; - } - - public double MaxHeight - { - get => _peer.MaxHeight; - set => _peer.MaxHeight = value; - } - - public IControlTemplate Template - { - get => _peer.Template; - set => _peer.Template = value; - } - - /// - /// Retrieves the named element in the instantiated ControlTemplate visual tree. - /// - /// The name of the element to find. - /// The named element from the template, if the element is found. Can - /// return null if no element with name childName was found in the template. - protected IUIPropertyObject GetTemplateChild(string childName) - { - // TODO: Finish this - return null; - } - - public Size DesiredSize { get; private set; } - - public double ActualX => throw new NotImplementedException(); - - public double ActualY => throw new NotImplementedException(); - - public double ActualWidth => throw new NotImplementedException(); - - public double ActualHeight => throw new NotImplementedException(); - - public void Measure(Size availableSize) - { - var desiredSize = MeasureOverride(availableSize); - - //enforce that MeasureCore can not return PositiveInfinity size even if given Infinte availabel size. - //Note: NegativeInfinity can not be returned by definition of Size structure. - if (double.IsPositiveInfinity(desiredSize.Width) || double.IsPositiveInfinity(desiredSize.Height)) - throw new InvalidOperationException($"Layout measurement override of element '{GetType().FullName}' should not return PositiveInfinity as its DesiredSize, even if Infinity is passed in as available size."); - - //enforce that MeasureCore cannot return NaN size. - if (double.IsNaN(desiredSize.Width) || double.IsNaN(desiredSize.Height)) - throw new InvalidOperationException($"Layout measurement override of element '{GetType().FullName}' should not return NaN values as its DesiredSize."); - - DesiredSize = desiredSize; - } - - public void Arrange(Rect finalRect) - { - ArrangeOverride(new Size(finalRect.Width, finalRect.Height)); - } - - protected abstract Size MeasureOverride(Size availableSize); - protected abstract Size ArrangeOverride(Size finalSize); - - public object GetValue(IUIProperty dp) => _peer.GetValue(dp); - public object ReadLocalValue(IUIProperty dp) => _peer.ReadLocalValue(dp); - public void SetValue(IUIProperty dp, object value) => _peer.SetValue(dp, value); - - /// - /// This method can be overridden to add further graphical elements (not previously defined in a logical tree) to a drawn element. - /// It's similar to the OnRender method in WPF. - /// - /// visualizer that should draw to - public virtual void OnVisualize(IVisualizer visualizer) { } - } -} diff --git a/src/StandardUI/Controls/StandardUIUserControl.cs b/src/StandardUI/Controls/StandardUserControlImplementation.cs similarity index 77% rename from src/StandardUI/Controls/StandardUIUserControl.cs rename to src/StandardUI/Controls/StandardUserControlImplementation.cs index 8962ed1..1523562 100644 --- a/src/StandardUI/Controls/StandardUIUserControl.cs +++ b/src/StandardUI/Controls/StandardUserControlImplementation.cs @@ -1,7 +1,11 @@ namespace Microsoft.StandardUI.Controls { - public class StandardUIUserControl : StandardUIControl + public class StandardUserControlImplementation : StandardControlImplementation where TControl : IControl { + public StandardUserControlImplementation(TControl control) : base(control) + { + } + public IUIElement? Content { get; set; } protected override Size MeasureOverride(Size availableSize) diff --git a/src/StandardUI/FactoryStatics.cs b/src/StandardUI/FactoryStatics.cs index 4fd029f..fffbba9 100755 --- a/src/StandardUI/FactoryStatics.cs +++ b/src/StandardUI/FactoryStatics.cs @@ -30,9 +30,6 @@ namespace Microsoft.StandardUI /*** Environment peers ***/ - public static IStandardUIControlEnvironmentPeer StandardUIControlEnvironmentPeer(StandardUIControl standardUIControl) => - Factory.CreateStandardUIControlEnvironmentPeer(standardUIControl); - /*** Infrastructure objects ***/ public static IUIPropertyMetadata UIPropertyMetdata(object defaultValue) => Factory.CreatePropertyMetadata(defaultValue); diff --git a/src/StandardUI/IStandardUIFactory.cs b/src/StandardUI/IStandardUIFactory.cs index 5363f41..5dd745a 100644 --- a/src/StandardUI/IStandardUIFactory.cs +++ b/src/StandardUI/IStandardUIFactory.cs @@ -42,10 +42,6 @@ namespace Microsoft.StandardUI IPathGeometry CreatePathGeometry(ITransform? transform, IEnumerable figures, FillRule fillRule); IPathFigure CreatePathFigure(IEnumerable segments, Point startPoint, bool isClosed, bool isFilled); - /*** Environment peers ***/ - - IStandardUIControlEnvironmentPeer CreateStandardUIControlEnvironmentPeer(StandardUIControl standardUIControl); - /*** Infrastructure objects ***/ IUIPropertyMetadata CreatePropertyMetadata(object defaultValue); diff --git a/src/StandardUI/UnintializedStandardUIFactory.cs b/src/StandardUI/UnintializedStandardUIFactory.cs index aa63592..98860f6 100644 --- a/src/StandardUI/UnintializedStandardUIFactory.cs +++ b/src/StandardUI/UnintializedStandardUIFactory.cs @@ -44,11 +44,6 @@ namespace Microsoft.StandardUI public IPathGeometry CreatePathGeometry(ITransform? transform, IEnumerable figures, FillRule fillRule) => throw CreateInitNotCalledException(); public IPathFigure CreatePathFigure(IEnumerable segments, Point startPoint, bool isClosed, bool isFilled) => throw CreateInitNotCalledException(); - /*** Environment peers ***/ - - public IStandardUIControlEnvironmentPeer CreateStandardUIControlEnvironmentPeer(StandardUIControl standardUIControl) => - throw CreateInitNotCalledException(); - /*** Infrastructure objects ***/ public IUIPropertyMetadata CreatePropertyMetadata(object defaultValue) => throw CreateInitNotCalledException();