From a828fb9b0b95746ae15cf70896cb85dd4d1ef413 Mon Sep 17 00:00:00 2001 From: "Frank A. Krueger" Date: Thu, 9 Nov 2017 01:38:19 -0800 Subject: [PATCH] Implement VisualElementRenderers --- Ooui.Forms/ElementChangedEventArgs.cs | 26 +++++ Ooui.Forms/Extensions/ColorExtensions.cs | 12 +++ Ooui.Forms/Forms.cs | 14 +++ Ooui.Forms/IVisualElementRenderer.cs | 9 ++ Ooui.Forms/Ooui.Forms.csproj | 1 + Ooui.Forms/Platform.cs | 31 ++++-- Ooui.Forms/PlatformEffect.cs | 9 ++ Ooui.Forms/PlatformRenderer.cs | 17 +++ Ooui.Forms/Renderers/ButtonRenderer.cs | 2 +- Ooui.Forms/Renderers/DefaultRenderer.cs | 2 +- Ooui.Forms/Renderers/ElementRenderer.cs | 42 -------- Ooui.Forms/Renderers/ViewRenderer.cs | 116 +++++++++++++++++++++ Ooui.Forms/VisualElementRenderer.cs | 125 ++++++++++++++++++++++- Ooui.sln | 9 +- Ooui/Color.cs | 19 +++- Ooui/Element.cs | 6 ++ 16 files changed, 380 insertions(+), 60 deletions(-) create mode 100644 Ooui.Forms/ElementChangedEventArgs.cs create mode 100644 Ooui.Forms/Extensions/ColorExtensions.cs create mode 100644 Ooui.Forms/PlatformEffect.cs create mode 100644 Ooui.Forms/PlatformRenderer.cs delete mode 100644 Ooui.Forms/Renderers/ElementRenderer.cs create mode 100644 Ooui.Forms/Renderers/ViewRenderer.cs diff --git a/Ooui.Forms/ElementChangedEventArgs.cs b/Ooui.Forms/ElementChangedEventArgs.cs new file mode 100644 index 0000000..c1c66ee --- /dev/null +++ b/Ooui.Forms/ElementChangedEventArgs.cs @@ -0,0 +1,26 @@ +using System; +using Xamarin.Forms; + +namespace Ooui.Forms +{ + public class VisualElementChangedEventArgs : ElementChangedEventArgs + { + public VisualElementChangedEventArgs (VisualElement oldElement, VisualElement newElement) + : base (oldElement, newElement) + { + } + } + + public class ElementChangedEventArgs : EventArgs where TElement : Xamarin.Forms.Element + { + public ElementChangedEventArgs (TElement oldElement, TElement newElement) + { + OldElement = oldElement; + NewElement = newElement; + } + + public TElement NewElement { get; private set; } + + public TElement OldElement { get; private set; } + } +} diff --git a/Ooui.Forms/Extensions/ColorExtensions.cs b/Ooui.Forms/Extensions/ColorExtensions.cs new file mode 100644 index 0000000..ac81bdd --- /dev/null +++ b/Ooui.Forms/Extensions/ColorExtensions.cs @@ -0,0 +1,12 @@ +using System; + +namespace Ooui.Forms.Extensions +{ + public static class ColorExtensions + { + public static Color ToOouiColor (this Xamarin.Forms.Color color) + { + return new Color ((byte)(color.R * 255.0 + 0.5), (byte)(color.G * 255.0 + 0.5), (byte)(color.B * 255.0 + 0.5), (byte)(color.A * 255.0 + 0.5)); + } + } +} diff --git a/Ooui.Forms/Forms.cs b/Ooui.Forms/Forms.cs index 5399852..786cb33 100644 --- a/Ooui.Forms/Forms.cs +++ b/Ooui.Forms/Forms.cs @@ -7,6 +7,7 @@ using System.IO; using System.Reflection; using System.Threading; using System.Threading.Tasks; +using Ooui; namespace Xamarin.Forms { @@ -33,6 +34,13 @@ namespace Xamarin.Forms }); } + public static event EventHandler ViewInitialized; + + public static void SendViewInitialized (this VisualElement self, Ooui.Element nativeView) + { + ViewInitialized?.Invoke (self, new ViewInitializedEventArgs { View = self, NativeView = nativeView }); + } + class OouiDeviceInfo : DeviceInfo { public override Size PixelScreenSize => new Size (640, 480); @@ -93,5 +101,11 @@ namespace Xamarin.Forms throw new NotImplementedException (); } } + + public class ViewInitializedEventArgs + { + public VisualElement View { get; set; } + public Ooui.Element NativeView { get; set; } + } } } diff --git a/Ooui.Forms/IVisualElementRenderer.cs b/Ooui.Forms/IVisualElementRenderer.cs index dbe6820..d2362ee 100644 --- a/Ooui.Forms/IVisualElementRenderer.cs +++ b/Ooui.Forms/IVisualElementRenderer.cs @@ -5,5 +5,14 @@ namespace Ooui.Forms { public interface IVisualElementRenderer : IRegisterable, IDisposable { + event EventHandler ElementChanged; + + VisualElement Element { get; } + + Ooui.Element NativeView { get; } + + void SetElement (VisualElement element); + + void SetElementSize (Size size); } } diff --git a/Ooui.Forms/Ooui.Forms.csproj b/Ooui.Forms/Ooui.Forms.csproj index aefd842..6a5c90e 100644 --- a/Ooui.Forms/Ooui.Forms.csproj +++ b/Ooui.Forms/Ooui.Forms.csproj @@ -9,6 +9,7 @@ + diff --git a/Ooui.Forms/Platform.cs b/Ooui.Forms/Platform.cs index 1c7dcab..1e1418f 100644 --- a/Ooui.Forms/Platform.cs +++ b/Ooui.Forms/Platform.cs @@ -11,7 +11,9 @@ namespace Ooui.Forms { bool _disposed; - public Element Element { get; private set; } + readonly PlatformRenderer _renderer; + + public Ooui.Element Element => _renderer; public Page Page { get; private set; } @@ -24,7 +26,12 @@ namespace Ooui.Forms var view = bindable as VisualElement; if (view != null) view.IsPlatformEnabled = newvalue != null; - }); + }); + + public Platform () + { + _renderer = new PlatformRenderer (this); + } void IDisposable.Dispose() { @@ -40,7 +47,7 @@ namespace Ooui.Forms public static IVisualElementRenderer CreateRenderer (VisualElement element) { var renderer = Registrar.Registered.GetHandler (element.GetType ()) ?? new DefaultRenderer (); - //renderer.SetElement (element); + renderer.SetElement (element); return renderer; } @@ -77,11 +84,16 @@ namespace Ooui.Forms Page.Platform = this; AddChild (Page); - //Page.DescendantRemoved += HandleChildRemoved; + Page.DescendantRemoved += HandleChildRemoved; Application.Current.NavigationProxy.Inner = this; - } - + } + + void HandleChildRemoved (object sender, ElementEventArgs e) + { + throw new NotImplementedException (); + } + void AddChild (VisualElement view) { if (!Application.IsApplicationOrNull (view.RealParent)) @@ -91,11 +103,8 @@ namespace Ooui.Forms var viewRenderer = CreateRenderer (view); SetRenderer (view, viewRenderer); - //_renderer.View.AddSubview (viewRenderer.NativeView); - //if (viewRenderer.ViewController != null) - // _renderer.AddChildViewController (viewRenderer.ViewController); - //viewRenderer.NativeView.Frame = new RectangleF (0, 0, _renderer.View.Bounds.Width, _renderer.View.Bounds.Height); - //viewRenderer.SetElementSize (new Size (_renderer.View.Bounds.Width, _renderer.View.Bounds.Height)); + _renderer.AppendChild (viewRenderer.NativeView); + viewRenderer.SetElementSize (new Size (640, 480)); } else Console.Error.WriteLine ("Potential view double add"); diff --git a/Ooui.Forms/PlatformEffect.cs b/Ooui.Forms/PlatformEffect.cs new file mode 100644 index 0000000..011fa39 --- /dev/null +++ b/Ooui.Forms/PlatformEffect.cs @@ -0,0 +1,9 @@ +using System; +using Xamarin.Forms; + +namespace Ooui.Forms +{ + public abstract class PlatformEffect : PlatformEffect + { + } +} diff --git a/Ooui.Forms/PlatformRenderer.cs b/Ooui.Forms/PlatformRenderer.cs new file mode 100644 index 0000000..6e8d939 --- /dev/null +++ b/Ooui.Forms/PlatformRenderer.cs @@ -0,0 +1,17 @@ +using System; +using Xamarin.Forms; + +namespace Ooui.Forms +{ + public class PlatformRenderer : Ooui.Div + { + readonly Platform platform; + + public Platform Platform => platform; + + public PlatformRenderer (Platform platform) + { + this.platform = platform; + } + } +} diff --git a/Ooui.Forms/Renderers/ButtonRenderer.cs b/Ooui.Forms/Renderers/ButtonRenderer.cs index f272d7c..542caf9 100644 --- a/Ooui.Forms/Renderers/ButtonRenderer.cs +++ b/Ooui.Forms/Renderers/ButtonRenderer.cs @@ -3,7 +3,7 @@ using Xamarin.Forms; namespace Ooui.Forms.Renderers { - public class ButtonRenderer : ElementRenderer + public class ButtonRenderer : ViewRenderer { public ButtonRenderer() { diff --git a/Ooui.Forms/Renderers/DefaultRenderer.cs b/Ooui.Forms/Renderers/DefaultRenderer.cs index c727ce0..5e987d7 100644 --- a/Ooui.Forms/Renderers/DefaultRenderer.cs +++ b/Ooui.Forms/Renderers/DefaultRenderer.cs @@ -5,7 +5,7 @@ namespace Ooui.Forms.Renderers { public class DefaultRenderer : VisualElementRenderer { - public DefaultRenderer () : base ("div") + public DefaultRenderer () { } } diff --git a/Ooui.Forms/Renderers/ElementRenderer.cs b/Ooui.Forms/Renderers/ElementRenderer.cs deleted file mode 100644 index 75d4d79..0000000 --- a/Ooui.Forms/Renderers/ElementRenderer.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using Xamarin.Forms; - -namespace Ooui.Forms.Renderers -{ - public class ElementRenderer : IVisualElementRenderer where TElement : View where TNativeElement : Ooui.Element - { - - #region IDisposable Support - private bool disposedValue = false; // To detect redundant calls - - protected virtual void Dispose (bool disposing) - { - if (!disposedValue) { - if (disposing) { - // TODO: dispose managed state (managed objects). - } - - // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below. - // TODO: set large fields to null. - - disposedValue = true; - } - } - - // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources. - // ~ElementRenderer() { - // // Do not change this code. Put cleanup code in Dispose(bool disposing) above. - // Dispose(false); - // } - - // This code added to correctly implement the disposable pattern. - public void Dispose () - { - // Do not change this code. Put cleanup code in Dispose(bool disposing) above. - Dispose (true); - // TODO: uncomment the following line if the finalizer is overridden above. - // GC.SuppressFinalize(this); - } - #endregion - } -} \ No newline at end of file diff --git a/Ooui.Forms/Renderers/ViewRenderer.cs b/Ooui.Forms/Renderers/ViewRenderer.cs new file mode 100644 index 0000000..5009a94 --- /dev/null +++ b/Ooui.Forms/Renderers/ViewRenderer.cs @@ -0,0 +1,116 @@ +using System; +using System.ComponentModel; +using Ooui.Forms.Extensions; +using Xamarin.Forms; + +namespace Ooui.Forms.Renderers +{ + public class ViewRenderer : VisualElementRenderer where TElement : View where TNativeElement : Ooui.Element + { + Color _defaultColor; + + public TNativeElement Control { get; private set; } + + /// + /// Determines whether the native control is disposed of when this renderer is disposed + /// Can be overridden in deriving classes + /// + protected virtual bool ManageNativeControlLifetime => true; + + protected override void Dispose (bool disposing) + { + base.Dispose (disposing); + + if (disposing && Control != null && ManageNativeControlLifetime) { + Control = null; + } + } + + protected override void OnElementChanged (ElementChangedEventArgs e) + { + base.OnElementChanged (e); + + if (e.OldElement != null) + e.OldElement.FocusChangeRequested -= ViewOnFocusChangeRequested; + + if (e.NewElement != null) { + if (Control != null && e.OldElement != null && e.OldElement.BackgroundColor != e.NewElement.BackgroundColor || e.NewElement.BackgroundColor != Xamarin.Forms.Color.Default) + SetBackgroundColor (e.NewElement.BackgroundColor); + + e.NewElement.FocusChangeRequested += ViewOnFocusChangeRequested; + } + + UpdateIsEnabled (); + } + + protected override void OnElementPropertyChanged (object sender, PropertyChangedEventArgs e) + { + if (Control != null) { + if (e.PropertyName == VisualElement.IsEnabledProperty.PropertyName) + UpdateIsEnabled (); + else if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName) + SetBackgroundColor (Element.BackgroundColor); + } + + base.OnElementPropertyChanged (sender, e); + } + + protected override void OnRegisterEffect (PlatformEffect effect) + { + base.OnRegisterEffect (effect); + //effect.Control = Control; + } + + protected override void SetAutomationId (string id) + { + if (Control == null) + base.SetAutomationId (id); + else { + } + } + + protected override void SetBackgroundColor (Xamarin.Forms.Color color) + { + if (Control == null) + return; + + if (color == Xamarin.Forms.Color.Default) + Control.Style.BackgroundColor = _defaultColor; + else + Control.Style.BackgroundColor = color.ToOouiColor (); + } + + protected void SetNativeControl (Ooui.Element uiview) + { + _defaultColor = Color.FromStyleValue (uiview.Style.BackgroundColor); + Control = (TNativeElement)uiview; + + if (Element.BackgroundColor != Xamarin.Forms.Color.Default) + SetBackgroundColor (Element.BackgroundColor); + + UpdateIsEnabled (); + + this.AppendChild (uiview); + } + + protected override void SendVisualElementInitialized (VisualElement element, Ooui.Element nativeView) + { + base.SendVisualElementInitialized (element, Control); + } + + void UpdateIsEnabled () + { + if (Element == null || Control == null) + return; + + var uiControl = Control as Ooui.Element; + if (uiControl == null) + return; + uiControl.IsDisabled = !Element.IsEnabled; + } + + void ViewOnFocusChangeRequested (object sender, VisualElement.FocusRequestArgs focusRequestArgs) + { + } + } +} \ No newline at end of file diff --git a/Ooui.Forms/VisualElementRenderer.cs b/Ooui.Forms/VisualElementRenderer.cs index 948a9ad..f6c28ea 100644 --- a/Ooui.Forms/VisualElementRenderer.cs +++ b/Ooui.Forms/VisualElementRenderer.cs @@ -1,12 +1,133 @@ using System; +using System.Collections.Generic; +using System.ComponentModel; +using Ooui.Forms.Extensions; using Xamarin.Forms; namespace Ooui.Forms { - public class VisualElementRenderer : Element, IVisualElementRenderer where TElement : VisualElement + public class VisualElementRenderer : Ooui.Element, IVisualElementRenderer where TElement : VisualElement { - public VisualElementRenderer (string tagName) : base (tagName) + readonly Color _defaultColor = Color.Clear; + + readonly PropertyChangedEventHandler _propertyChangedHandler; + + public TElement Element { get; private set; } + + VisualElement IVisualElementRenderer.Element => Element; + + public Element NativeView => this; + + event EventHandler IVisualElementRenderer.ElementChanged { + add { _elementChangedHandlers.Add (value); } + remove { _elementChangedHandlers.Remove (value); } + } + + readonly List> _elementChangedHandlers = + new List> (); + + public VisualElementRenderer () : base ("div") { + _propertyChangedHandler = OnElementPropertyChanged; + } + + protected virtual void OnElementChanged (ElementChangedEventArgs e) + { + var args = new VisualElementChangedEventArgs (e.OldElement, e.NewElement); + for (int i = 0; i < _elementChangedHandlers.Count; i++) { + _elementChangedHandlers[i] (this, args); + } + + var changed = ElementChanged; + if (changed != null) + changed (this, e); + } + + public event EventHandler> ElementChanged; + + void IVisualElementRenderer.SetElement (VisualElement element) + { + SetElement ((TElement)element); + } + + public void SetElement (TElement element) + { + var oldElement = Element; + Element = element; + + if (oldElement != null) + oldElement.PropertyChanged -= _propertyChangedHandler; + + if (element != null) { + if (element.BackgroundColor != Xamarin.Forms.Color.Default || (oldElement != null && element.BackgroundColor != oldElement.BackgroundColor)) + SetBackgroundColor (element.BackgroundColor); + + //if (_tracker == null) { + // _tracker = new VisualElementTracker (this); + // _tracker.NativeControlUpdated += (sender, e) => UpdateNativeWidget (); + //} + + //if (AutoPackage && _packager == null) { + // _packager = new VisualElementPackager (this); + // _packager.Load (); + //} + + //if (AutoTrack && _events == null) { + // _events = new EventTracker (this); + // _events.LoadEvents (this); + //} + + element.PropertyChanged += _propertyChangedHandler; + } + + OnElementChanged (new ElementChangedEventArgs (oldElement, element)); + + if (element != null) + SendVisualElementInitialized (element, this); + + if (Element != null && !string.IsNullOrEmpty (Element.AutomationId)) + SetAutomationId (Element.AutomationId); + } + + public void SetElementSize (Size size) + { + Xamarin.Forms.Layout.LayoutChildIntoBoundingRegion (Element, new Rectangle (Element.X, Element.Y, size.Width, size.Height)); + } + + protected virtual void OnElementPropertyChanged (object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName) { + SetBackgroundColor (Element.BackgroundColor); + } + else if (e.PropertyName == Layout.IsClippedToBoundsProperty.PropertyName) { + //UpdateClipToBounds (); + } + } + + protected virtual void OnRegisterEffect (PlatformEffect effect) + { + //effect.Container = this; + } + + protected virtual void SetAutomationId (string id) + { + } + + protected virtual void SetBackgroundColor (Xamarin.Forms.Color color) + { + if (color == Xamarin.Forms.Color.Default) + Style.BackgroundColor = _defaultColor; + else + Style.BackgroundColor = color.ToOouiColor (); + } + + protected virtual void UpdateNativeWidget () + { + } + + protected virtual void SendVisualElementInitialized (VisualElement element, Element nativeView) + { + element.SendViewInitialized (nativeView); } #region IDisposable Support diff --git a/Ooui.sln b/Ooui.sln index ea96543..0d488e6 100644 --- a/Ooui.sln +++ b/Ooui.sln @@ -81,7 +81,12 @@ Global $1.scope = text/x-csharp $0.CSharpFormattingPolicy = $2 $2.scope = text/x-csharp - $2.SpacingAfterMethodDeclarationName = True - $2.SpaceAfterMethodCallName = True + $2.NewLinesForBracesInProperties = False + $2.NewLinesForBracesInAccessors = False + $2.NewLinesForBracesInAnonymousMethods = False + $2.NewLinesForBracesInControlBlocks = False + $2.NewLinesForBracesInAnonymousTypes = False + $2.NewLinesForBracesInObjectCollectionArrayInitializers = False + $2.NewLinesForBracesInLambdaExpressionBody = False EndGlobalSection EndGlobal diff --git a/Ooui/Color.cs b/Ooui/Color.cs index bd2846b..b138372 100644 --- a/Ooui/Color.cs +++ b/Ooui/Color.cs @@ -1,11 +1,23 @@ using System; +using StyleValue = System.Object; + namespace Ooui { public struct Color { public byte R, G, B, A; + public static Color Clear => new Color (0, 0, 0, 0); + + public Color (byte r, byte g, byte b, byte a) + { + R = r; + G = g; + B = b; + A = a; + } + public double Red { get => R / 255.0; set => R = value >= 1.0 ? (byte)255 : ((value <= 0.0) ? (byte)0 : (byte)(value * 255.0 + 0.5)); @@ -22,5 +34,10 @@ namespace Ooui get => A / 255.0; set => A = value >= 1.0 ? (byte)255 : ((value <= 0.0) ? (byte)0 : (byte)(value * 255.0 + 0.5)); } - } + + public static Color FromStyleValue (StyleValue styleColor) + { + return Clear; + } + } } diff --git a/Ooui/Element.cs b/Ooui/Element.cs index 253f57d..c6c1d90 100644 --- a/Ooui/Element.cs +++ b/Ooui/Element.cs @@ -25,6 +25,12 @@ namespace Ooui set => SetProperty (ref hidden, value, "hidden"); } + bool disabled = false; + public bool IsDisabled { + get => disabled; + set => SetProperty (ref disabled, value, "disabled"); + } + public event TargetEventHandler Clicked { add => AddEventListener ("click", value); remove => RemoveEventListener ("click", value);