From 36367a31584a82b0d28a3271d77caef3e43ae4c4 Mon Sep 17 00:00:00 2001 From: "Frank A. Krueger" Date: Thu, 9 Nov 2017 16:09:48 -0800 Subject: [PATCH] Implement buttons and labels --- Ooui.Forms/Exports.cs | 1 + Ooui.Forms/Extensions/ColorExtensions.cs | 7 + Ooui.Forms/Extensions/ElementExtensions.cs | 16 ++ Ooui.Forms/Extensions/FontExtensions.cs | 25 +++ Ooui.Forms/Renderers/ButtonRenderer.cs | 130 ++++++++++++++- Ooui.Forms/Renderers/LabelRenderer.cs | 180 +++++++++++++++++++++ Ooui.Forms/VisualElementRenderer.cs | 7 +- Ooui/Color.cs | 6 +- Ooui/Colors.cs | 10 ++ Ooui/Style.cs | 7 + Samples/XamarinFormsSample.cs | 2 + 11 files changed, 385 insertions(+), 6 deletions(-) create mode 100644 Ooui.Forms/Extensions/ElementExtensions.cs create mode 100644 Ooui.Forms/Extensions/FontExtensions.cs create mode 100644 Ooui.Forms/Renderers/LabelRenderer.cs create mode 100644 Ooui/Colors.cs diff --git a/Ooui.Forms/Exports.cs b/Ooui.Forms/Exports.cs index 128ce70..fc910e4 100644 --- a/Ooui.Forms/Exports.cs +++ b/Ooui.Forms/Exports.cs @@ -6,6 +6,7 @@ using Xamarin.Forms.Internals; [assembly: Dependency (typeof (ResourcesProvider))] [assembly: ExportRenderer (typeof (Button), typeof (ButtonRenderer))] +[assembly: ExportRenderer (typeof (Label), typeof (LabelRenderer))] namespace Ooui.Forms { diff --git a/Ooui.Forms/Extensions/ColorExtensions.cs b/Ooui.Forms/Extensions/ColorExtensions.cs index ac81bdd..547fc56 100644 --- a/Ooui.Forms/Extensions/ColorExtensions.cs +++ b/Ooui.Forms/Extensions/ColorExtensions.cs @@ -8,5 +8,12 @@ namespace Ooui.Forms.Extensions { 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)); } + + public static Color ToOouiColor (this Xamarin.Forms.Color color, Xamarin.Forms.Color defaultColor) + { + if (color == Xamarin.Forms.Color.Default) + return defaultColor.ToOouiColor (); + return color.ToOouiColor (); + } } } diff --git a/Ooui.Forms/Extensions/ElementExtensions.cs b/Ooui.Forms/Extensions/ElementExtensions.cs new file mode 100644 index 0000000..663cf52 --- /dev/null +++ b/Ooui.Forms/Extensions/ElementExtensions.cs @@ -0,0 +1,16 @@ +using System; +using Xamarin.Forms; + +namespace Ooui.Forms.Extensions +{ + public static class ElementExtensions + { + public static SizeRequest GetSizeRequest (this Ooui.Element self, double widthConstraint, double heightConstraint, + double minimumWidth = -1, double minimumHeight = -1) + { + var request = new Size (double.PositiveInfinity, double.PositiveInfinity); + var minimum = new Size (double.PositiveInfinity, double.PositiveInfinity); + return new SizeRequest (request, minimum); + } + } +} diff --git a/Ooui.Forms/Extensions/FontExtensions.cs b/Ooui.Forms/Extensions/FontExtensions.cs new file mode 100644 index 0000000..e2232f5 --- /dev/null +++ b/Ooui.Forms/Extensions/FontExtensions.cs @@ -0,0 +1,25 @@ +using System; +using Xamarin.Forms; + +namespace Ooui.Forms.Extensions +{ + public static class FontExtensions + { + public static void SetStyleFont (this View view, Style style) + { + } + + public static string ToOouiTextAlign (this TextAlignment align) + { + switch (align) { + case TextAlignment.Start: + default: + return "start"; + case TextAlignment.Center: + return "center"; + case TextAlignment.End: + return "end"; + } + } + } +} diff --git a/Ooui.Forms/Renderers/ButtonRenderer.cs b/Ooui.Forms/Renderers/ButtonRenderer.cs index 3ccfd95..7989ab1 100644 --- a/Ooui.Forms/Renderers/ButtonRenderer.cs +++ b/Ooui.Forms/Renderers/ButtonRenderer.cs @@ -1,12 +1,140 @@ using System; +using System.ComponentModel; +using System.Diagnostics; +using Ooui.Forms.Extensions; using Xamarin.Forms; namespace Ooui.Forms.Renderers { public class ButtonRenderer : ViewRenderer { - public ButtonRenderer () + Ooui.Color _buttonTextColorDefaultDisabled; + Ooui.Color _buttonTextColorDefaultHighlighted; + Ooui.Color _buttonTextColorDefaultNormal; + + protected override void Dispose (bool disposing) { + if (Control != null) { + Control.Clicked -= OnButtonTouchUpInside; + } + + base.Dispose (disposing); + } + + protected override void OnElementChanged (ElementChangedEventArgs e) + { + base.OnElementChanged (e); + + if (e.NewElement != null) { + if (Control == null) { + SetNativeControl (new Ooui.Button ()); + + Debug.Assert (Control != null, "Control != null"); + + _buttonTextColorDefaultNormal = Ooui.Colors.Black; + _buttonTextColorDefaultHighlighted = Ooui.Colors.Black; + _buttonTextColorDefaultDisabled = Ooui.Colors.Black; + + Control.Clicked += OnButtonTouchUpInside; + } + + UpdateText (); + UpdateFont (); + UpdateBorder (); + UpdateImage (); + UpdateTextColor (); + } + } + + protected override void OnElementPropertyChanged (object sender, PropertyChangedEventArgs e) + { + base.OnElementPropertyChanged (sender, e); + + if (e.PropertyName == Xamarin.Forms.Button.TextProperty.PropertyName) + UpdateText (); + else if (e.PropertyName == Xamarin.Forms.Button.TextColorProperty.PropertyName) + UpdateTextColor (); + else if (e.PropertyName == Xamarin.Forms.Button.FontProperty.PropertyName) + UpdateFont (); + else if (e.PropertyName == Xamarin.Forms.Button.BorderWidthProperty.PropertyName || e.PropertyName == Xamarin.Forms.Button.BorderRadiusProperty.PropertyName || e.PropertyName == Xamarin.Forms.Button.BorderColorProperty.PropertyName) + UpdateBorder (); + else if (e.PropertyName == Xamarin.Forms.Button.ImageProperty.PropertyName) + UpdateImage (); + } + + void OnButtonTouchUpInside (object sender, EventArgs eventArgs) + { + ((IButtonController)Element)?.SendPressed (); + ((IButtonController)Element)?.SendReleased (); + ((IButtonController)Element)?.SendClicked (); + } + + void UpdateBorder () + { + var uiButton = Control; + var button = Element; + + if (button.BorderColor != Xamarin.Forms.Color.Default) + uiButton.Style.BorderColor = button.BorderColor.ToOouiColor (); + + uiButton.Style.BorderWidth = Math.Max (0f, (float)button.BorderWidth); + uiButton.Style.BorderRadius = button.BorderRadius; + } + + void UpdateFont () + { + Element.SetStyleFont (Control.Style); + } + + void UpdateImage () + { + //IImageSourceHandler handler; + //FileImageSource source = Element.Image; + //if (source != null && (handler = Internals.Registrar.Registered.GetHandlerForObject (source)) != null) { + // UIImage uiimage; + // try { + // uiimage = await handler.LoadImageAsync (source, scale: (float)UIScreen.MainScreen.Scale); + // } + // catch (OperationCanceledException) { + // uiimage = null; + // } + // Ooui.Button button = Control; + // if (button != null && uiimage != null) { + // button.SetImage (uiimage.ImageWithRenderingMode (UIImageRenderingMode.AlwaysOriginal), UIControlState.Normal); + + // button.ImageView.ContentMode = UIViewContentMode.ScaleAspectFit; + + // ComputeEdgeInsets (Control, Element.ContentLayout); + // } + //} + //else { + // Control.SetImage (null, UIControlState.Normal); + // ClearEdgeInsets (Control); + //} + //((IVisualElementController)Element).NativeSizeChanged (); + } + + void UpdateText () + { + var newText = Element.Text; + + if (Control.Text != newText) { + Control.Text = Element.Text; + } + } + + void UpdateTextColor () + { + if (Element.TextColor == Xamarin.Forms.Color.Default) { + Control.Style.Color = _buttonTextColorDefaultNormal; + Control.Style.Color = _buttonTextColorDefaultHighlighted; + Control.Style.Color = _buttonTextColorDefaultDisabled; + } + else { + Control.Style.Color = Element.TextColor.ToOouiColor (); + Control.Style.Color = Element.TextColor.ToOouiColor (); + Control.Style.Color = _buttonTextColorDefaultDisabled; + } } } } diff --git a/Ooui.Forms/Renderers/LabelRenderer.cs b/Ooui.Forms/Renderers/LabelRenderer.cs new file mode 100644 index 0000000..1806078 --- /dev/null +++ b/Ooui.Forms/Renderers/LabelRenderer.cs @@ -0,0 +1,180 @@ +using System; +using System.ComponentModel; +using System.Diagnostics; +using Ooui.Forms.Extensions; +using Xamarin.Forms; + +using NativeLabel = Ooui.Span; + +namespace Ooui.Forms.Renderers +{ + public class LabelRenderer : ViewRenderer + { + SizeRequest _perfectSize; + + bool _perfectSizeValid; + + public override SizeRequest GetDesiredSize (double widthConstraint, double heightConstraint) + { + if (!_perfectSizeValid) { + _perfectSize = GetPerfectSize (); + _perfectSize.Minimum = new Size (Math.Min (10, _perfectSize.Request.Width), _perfectSize.Request.Height); + _perfectSizeValid = true; + } + + var widthFits = widthConstraint >= _perfectSize.Request.Width; + var heightFits = heightConstraint >= _perfectSize.Request.Height; + + if (widthFits && heightFits) + return _perfectSize; + + var result = base.GetDesiredSize (widthConstraint, heightConstraint); + var tinyWidth = Math.Min (10, result.Request.Width); + result.Minimum = new Size (tinyWidth, result.Request.Height); + + if (widthFits || Element.LineBreakMode == LineBreakMode.NoWrap) + return result; + + bool containerIsNotInfinitelyWide = !double.IsInfinity (widthConstraint); + + if (containerIsNotInfinitelyWide) { + bool textCouldHaveWrapped = Element.LineBreakMode == LineBreakMode.WordWrap || Element.LineBreakMode == LineBreakMode.CharacterWrap; + bool textExceedsContainer = result.Request.Width > widthConstraint; + + if (textExceedsContainer || textCouldHaveWrapped) { + var expandedWidth = Math.Max (tinyWidth, widthConstraint); + result.Request = new Size (expandedWidth, result.Request.Height); + } + } + + return result; + } + + SizeRequest GetPerfectSize () + { + return new SizeRequest (new Size (100, 22)); + } + + protected override void OnElementChanged (ElementChangedEventArgs e) + { + if (e.NewElement != null) { + if (Control == null) { + SetNativeControl (new NativeLabel ()); + } + + UpdateText (); + UpdateTextColor (); + UpdateFont (); + + UpdateLineBreakMode (); + UpdateAlignment (); + } + + base.OnElementChanged (e); + } + + protected override void OnElementPropertyChanged (object sender, PropertyChangedEventArgs e) + { + base.OnElementPropertyChanged (sender, e); + + if (e.PropertyName == Xamarin.Forms.Label.HorizontalTextAlignmentProperty.PropertyName) + UpdateAlignment (); + else if (e.PropertyName == Xamarin.Forms.Label.VerticalTextAlignmentProperty.PropertyName) + UpdateAlignment (); + else if (e.PropertyName == Xamarin.Forms.Label.TextColorProperty.PropertyName) + UpdateTextColor (); + else if (e.PropertyName == Xamarin.Forms.Label.FontProperty.PropertyName) + UpdateFont (); + else if (e.PropertyName == Xamarin.Forms.Label.TextProperty.PropertyName) + UpdateText (); + else if (e.PropertyName == Xamarin.Forms.Label.FormattedTextProperty.PropertyName) + UpdateText (); + else if (e.PropertyName == Xamarin.Forms.Label.LineBreakModeProperty.PropertyName) + UpdateLineBreakMode (); + } + + protected override void SetBackgroundColor (Xamarin.Forms.Color color) + { + if (color == Xamarin.Forms.Color.Default) + Style.BackgroundColor = Colors.Clear; + else + Style.BackgroundColor = color.ToOouiColor(); + } + + void UpdateAlignment () + { + Control.Style.TextAlign = Element.HorizontalTextAlignment.ToOouiTextAlign (); + Control.Style.VerticalAlign = Element.VerticalTextAlignment.ToOouiTextAlign (); + } + + void UpdateLineBreakMode () + { + _perfectSizeValid = false; + //switch (Element.LineBreakMode) + //{ + // case LineBreakMode.NoWrap: + // Control.LineBreakMode = UILineBreakMode.Clip; + // Control.Lines = 1; + // break; + // case LineBreakMode.WordWrap: + // Control.LineBreakMode = UILineBreakMode.WordWrap; + // Control.Lines = 0; + // break; + // case LineBreakMode.CharacterWrap: + // Control.LineBreakMode = UILineBreakMode.CharacterWrap; + // Control.Lines = 0; + // break; + // case LineBreakMode.HeadTruncation: + // Control.LineBreakMode = UILineBreakMode.HeadTruncation; + // Control.Lines = 1; + // break; + // case LineBreakMode.MiddleTruncation: + // Control.LineBreakMode = UILineBreakMode.MiddleTruncation; + // Control.Lines = 1; + // break; + // case LineBreakMode.TailTruncation: + // Control.LineBreakMode = UILineBreakMode.TailTruncation; + // Control.Lines = 1; + // break; + //} + } + + bool isTextFormatted; + void UpdateText () + { + _perfectSizeValid = false; + + var values = Element.GetValues (Xamarin.Forms.Label.FormattedTextProperty, Xamarin.Forms.Label.TextProperty, Xamarin.Forms.Label.TextColorProperty); + var formatted = values[0] as FormattedString; + if (formatted != null) { + Control.Text = (string)values[1]; + isTextFormatted = true; + } + else { + Control.Text = (string)values[1]; + isTextFormatted = false; + } + } + + void UpdateFont () + { + if (isTextFormatted) + return; + _perfectSizeValid = false; + + Element.SetStyleFont (Control.Style); + } + + void UpdateTextColor () + { + if (isTextFormatted) + return; + + _perfectSizeValid = false; + + var textColor = (Xamarin.Forms.Color)Element.GetValue (Xamarin.Forms.Label.TextColorProperty); + + Control.Style.Color = textColor.ToOouiColor(Xamarin.Forms.Color.Black); + } + } +} diff --git a/Ooui.Forms/VisualElementRenderer.cs b/Ooui.Forms/VisualElementRenderer.cs index 484e5ac..36c4825 100644 --- a/Ooui.Forms/VisualElementRenderer.cs +++ b/Ooui.Forms/VisualElementRenderer.cs @@ -18,7 +18,7 @@ namespace Ooui.Forms { bool disposedValue = false; // To detect redundant calls - readonly Color _defaultColor = Color.Clear; + readonly Color _defaultColor = Colors.Clear; readonly PropertyChangedEventHandler _propertyChangedHandler; @@ -165,6 +165,11 @@ namespace Ooui.Forms element.SendViewInitialized (nativeView); } + public virtual SizeRequest GetDesiredSize (double widthConstraint, double heightConstraint) + { + return NativeView.GetSizeRequest (widthConstraint, heightConstraint); + } + protected virtual void Dispose (bool disposing) { if (!disposedValue) { diff --git a/Ooui/Color.cs b/Ooui/Color.cs index b138372..7b914ad 100644 --- a/Ooui/Color.cs +++ b/Ooui/Color.cs @@ -8,9 +8,7 @@ namespace Ooui { 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) + public Color (byte r, byte g, byte b, byte a) { R = r; G = g; @@ -37,7 +35,7 @@ namespace Ooui public static Color FromStyleValue (StyleValue styleColor) { - return Clear; + return Colors.Clear; } } } diff --git a/Ooui/Colors.cs b/Ooui/Colors.cs new file mode 100644 index 0000000..163e2f5 --- /dev/null +++ b/Ooui/Colors.cs @@ -0,0 +1,10 @@ +using System; + +namespace Ooui +{ + public static class Colors + { + public static readonly Color Clear = new Color (0, 0, 0, 0); + public static readonly Color Black = new Color (0, 0, 0, 255); + } +} diff --git a/Ooui/Style.cs b/Ooui/Style.cs index 1f97952..0a8cb08 100644 --- a/Ooui/Style.cs +++ b/Ooui/Style.cs @@ -107,6 +107,13 @@ namespace Ooui set => this["border-left-width"] = value; } + public Value BorderRadius { + get => this["border-radius"]; + set { + this["border-radius"] = value; + } + } + public Value BorderWidth { get => this["border-top-width"]; set { diff --git a/Samples/XamarinFormsSample.cs b/Samples/XamarinFormsSample.cs index 0e644ed..509b6c8 100644 --- a/Samples/XamarinFormsSample.cs +++ b/Samples/XamarinFormsSample.cs @@ -15,6 +15,8 @@ namespace Samples BackgroundColor = Color.Gold, }; var countButton = new Button { + Text = "Increase", + VerticalOptions = LayoutOptions.FillAndExpand, }; countButton.Clicked += (sender, e) => { var v = int.Parse (countLabel.Text);