From 33be903bfd00e4762584e2d4405d872a019572b4 Mon Sep 17 00:00:00 2001 From: "Frank A. Krueger" Date: Thu, 28 Dec 2017 14:01:00 -0600 Subject: [PATCH] Add Anchor, Forms.LinkLabel and LinkView --- Ooui.Forms/Controls/LinkLabel.cs | 20 +++ Ooui.Forms/Controls/LinkView.cs | 20 +++ Ooui.Forms/Exports.cs | 36 ++--- Ooui.Forms/Ooui.Forms.csproj | 1 + Ooui.Forms/Renderers/LinkLabelRenderer.cs | 165 ++++++++++++++++++++++ Ooui.Forms/Renderers/LinkViewRenderer.cs | 36 +++++ Ooui.Forms/Renderers/ViewRenderer.cs | 15 +- Ooui.Forms/VisualElementRenderer.cs | 2 +- Ooui/Anchor.cs | 18 +++ Samples/DisplayAlertPage.xaml | 9 +- 10 files changed, 298 insertions(+), 24 deletions(-) create mode 100644 Ooui.Forms/Controls/LinkLabel.cs create mode 100644 Ooui.Forms/Controls/LinkView.cs create mode 100644 Ooui.Forms/Renderers/LinkLabelRenderer.cs create mode 100644 Ooui.Forms/Renderers/LinkViewRenderer.cs create mode 100644 Ooui/Anchor.cs diff --git a/Ooui.Forms/Controls/LinkLabel.cs b/Ooui.Forms/Controls/LinkLabel.cs new file mode 100644 index 0000000..5371101 --- /dev/null +++ b/Ooui.Forms/Controls/LinkLabel.cs @@ -0,0 +1,20 @@ +using System; +using Xamarin.Forms; + +namespace Ooui.Forms +{ + public class LinkLabel : Xamarin.Forms.Label + { + public static readonly BindableProperty HRefProperty = BindableProperty.Create ("HRef", typeof (string), + typeof (LinkView), string.Empty, BindingMode.OneWay, null, null, null, null); + + public string HRef { + get { return (string)base.GetValue (HRefProperty); } + set { base.SetValue (HRefProperty, value); } + } + + public LinkLabel () + { + } + } +} diff --git a/Ooui.Forms/Controls/LinkView.cs b/Ooui.Forms/Controls/LinkView.cs new file mode 100644 index 0000000..fd51d53 --- /dev/null +++ b/Ooui.Forms/Controls/LinkView.cs @@ -0,0 +1,20 @@ +using System; +using Xamarin.Forms; + +namespace Ooui.Forms +{ + public class LinkView : ContentView + { + public static readonly BindableProperty HRefProperty = BindableProperty.Create ("HRef", typeof (string), + typeof (LinkView), string.Empty, BindingMode.OneWay, null, null, null, null); + + public string HRef { + get { return (string)base.GetValue (HRefProperty); } + set { base.SetValue (HRefProperty, value); } + } + + public LinkView () + { + } + } +} diff --git a/Ooui.Forms/Exports.cs b/Ooui.Forms/Exports.cs index 04c82e6..1904bdd 100644 --- a/Ooui.Forms/Exports.cs +++ b/Ooui.Forms/Exports.cs @@ -3,23 +3,25 @@ using Ooui.Forms; using Ooui.Forms.Renderers; using Xamarin.Forms; -[assembly: Dependency(typeof(ResourcesProvider))] -[assembly: ExportRenderer(typeof(ActivityIndicator), typeof(ActivityIndicatorRenderer))] -[assembly: ExportRenderer(typeof(BoxView), typeof(BoxRenderer))] -[assembly: ExportRenderer(typeof(Button), typeof(ButtonRenderer))] -[assembly: ExportRenderer(typeof(DatePicker), typeof(DatePickerRenderer))] -[assembly: ExportRenderer(typeof(Editor), typeof(EditorRenderer))] -[assembly: ExportRenderer(typeof(Entry), typeof(EntryRenderer))] -[assembly: ExportRenderer(typeof(Frame), typeof(FrameRenderer))] -[assembly: ExportRenderer(typeof(Image), typeof(ImageRenderer))] -[assembly: ExportRenderer(typeof(Label), typeof(LabelRenderer))] -[assembly: ExportRenderer(typeof(ProgressBar), typeof(ProgressBarRenderer))] -[assembly: ExportRenderer(typeof(SearchBar), typeof(SearchBarRenderer))] -[assembly: ExportRenderer(typeof(Switch), typeof(SwitchRenderer))] -[assembly: ExportRenderer(typeof(TimePicker), typeof(TimePickerRenderer))] -[assembly: ExportImageSourceHandler(typeof(FileImageSource), typeof(FileImageSourceHandler))] -[assembly: ExportImageSourceHandler(typeof(StreamImageSource), typeof(StreamImagesourceHandler))] -[assembly: ExportImageSourceHandler(typeof(UriImageSource), typeof(ImageLoaderSourceHandler))] +[assembly: Dependency (typeof (ResourcesProvider))] +[assembly: ExportRenderer (typeof (ActivityIndicator), typeof (ActivityIndicatorRenderer))] +[assembly: ExportRenderer (typeof (BoxView), typeof (BoxRenderer))] +[assembly: ExportRenderer (typeof (Button), typeof (ButtonRenderer))] +[assembly: ExportRenderer (typeof (DatePicker), typeof (DatePickerRenderer))] +[assembly: ExportRenderer (typeof (Editor), typeof (EditorRenderer))] +[assembly: ExportRenderer (typeof (Entry), typeof (EntryRenderer))] +[assembly: ExportRenderer (typeof (Frame), typeof (FrameRenderer))] +[assembly: ExportRenderer (typeof (Image), typeof (ImageRenderer))] +[assembly: ExportRenderer (typeof (Label), typeof (LabelRenderer))] +[assembly: ExportRenderer (typeof (LinkLabel), typeof (LinkLabelRenderer))] +[assembly: ExportRenderer (typeof (LinkView), typeof (LinkViewRenderer))] +[assembly: ExportRenderer (typeof (ProgressBar), typeof (ProgressBarRenderer))] +[assembly: ExportRenderer (typeof (SearchBar), typeof (SearchBarRenderer))] +[assembly: ExportRenderer (typeof (Switch), typeof (SwitchRenderer))] +[assembly: ExportRenderer (typeof (TimePicker), typeof (TimePickerRenderer))] +[assembly: ExportImageSourceHandler (typeof (FileImageSource), typeof (FileImageSourceHandler))] +[assembly: ExportImageSourceHandler (typeof (StreamImageSource), typeof (StreamImagesourceHandler))] +[assembly: ExportImageSourceHandler (typeof (UriImageSource), typeof (ImageLoaderSourceHandler))] namespace Ooui.Forms { diff --git a/Ooui.Forms/Ooui.Forms.csproj b/Ooui.Forms/Ooui.Forms.csproj index 8bcdc99..cacc1d4 100644 --- a/Ooui.Forms/Ooui.Forms.csproj +++ b/Ooui.Forms/Ooui.Forms.csproj @@ -24,6 +24,7 @@ + diff --git a/Ooui.Forms/Renderers/LinkLabelRenderer.cs b/Ooui.Forms/Renderers/LinkLabelRenderer.cs new file mode 100644 index 0000000..3d7fe61 --- /dev/null +++ b/Ooui.Forms/Renderers/LinkLabelRenderer.cs @@ -0,0 +1,165 @@ +using System; +using System.ComponentModel; +using Ooui.Forms.Extensions; +using Xamarin.Forms; + +namespace Ooui.Forms.Renderers +{ + public class LinkLabelRenderer : ViewRenderer + { + SizeRequest _perfectSize; + + bool _perfectSizeValid; + + public override SizeRequest GetDesiredSize (double widthConstraint, double heightConstraint) + { + if (!_perfectSizeValid) { + var size = Element.Text.MeasureSize (Element.FontFamily, Element.FontSize, Element.FontAttributes); + size.Width = Math.Ceiling (size.Width); + size.Height = Math.Ceiling (size.Height * 1.4); + _perfectSize = new SizeRequest (size, size); + _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; + } + + protected override void OnElementChanged (ElementChangedEventArgs e) + { + if (e.NewElement != null) { + if (Control == null) { + SetNativeControl (new Anchor ()); + } + + UpdateHRef (); + + UpdateText (); + UpdateTextColor (); + UpdateFont (); + + UpdateLineBreakMode (); + UpdateAlignment (); + } + + base.OnElementChanged (e); + } + + protected override void OnElementPropertyChanged (object sender, PropertyChangedEventArgs e) + { + base.OnElementPropertyChanged (sender, e); + + if (Control == null) + return; + + if (e.PropertyName == Ooui.Forms.LinkLabel.HRefProperty.PropertyName) + UpdateHRef (); + else 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 UpdateHRef () + { + Control.HRef = Element.HRef; + } + + void UpdateAlignment () + { + this.Style.Display = "table"; + Control.Style.Display = "table-cell"; + this.Style.TextAlign = Element.HorizontalTextAlignment.ToOouiTextAlign (); + Control.Style.VerticalAlign = Element.VerticalTextAlignment.ToOouiVerticalAlign (); + } + + void UpdateLineBreakMode () + { + _perfectSizeValid = false; + } + + 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 (Element.FontFamily, Element.FontSize, Element.FontAttributes, Control.Style); + } + + void UpdateTextColor () + { + if (isTextFormatted) + return; + + _perfectSizeValid = false; + + var textColor = Element.TextColor; + if (textColor.IsDefault) { + Control.Style.Color = null; + } + else { + Control.Style.Color = textColor.ToOouiColor (Xamarin.Forms.Color.Black); + } + } + } +} diff --git a/Ooui.Forms/Renderers/LinkViewRenderer.cs b/Ooui.Forms/Renderers/LinkViewRenderer.cs new file mode 100644 index 0000000..4ee222b --- /dev/null +++ b/Ooui.Forms/Renderers/LinkViewRenderer.cs @@ -0,0 +1,36 @@ +using System; +using System.ComponentModel; + +namespace Ooui.Forms.Renderers +{ + public class LinkViewRenderer : ViewRenderer + { + public LinkViewRenderer () + : base ("a") + { + } + + protected override void OnElementChanged (ElementChangedEventArgs e) + { + base.OnElementChanged (e); + + UpdateHRef (); + } + + protected override void OnElementPropertyChanged (object sender, PropertyChangedEventArgs e) + { + base.OnElementPropertyChanged (sender, e); + + if (Control == null) + return; + + if (e.PropertyName == Ooui.Forms.LinkLabel.HRefProperty.PropertyName) + UpdateHRef (); + } + + void UpdateHRef () + { + this.SetAttribute ("href", Element.HRef); + } + } +} diff --git a/Ooui.Forms/Renderers/ViewRenderer.cs b/Ooui.Forms/Renderers/ViewRenderer.cs index edbe129..b425349 100644 --- a/Ooui.Forms/Renderers/ViewRenderer.cs +++ b/Ooui.Forms/Renderers/ViewRenderer.cs @@ -15,11 +15,16 @@ namespace Ooui.Forms.Renderers 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; + /// + /// Determines whether the native control is disposed of when this renderer is disposed + /// Can be overridden in deriving classes + /// + protected virtual bool ManageNativeControlLifetime => true; + + public ViewRenderer (string tagName = "div") + : base (tagName) + { + } protected override void Dispose (bool disposing) { diff --git a/Ooui.Forms/VisualElementRenderer.cs b/Ooui.Forms/VisualElementRenderer.cs index f6f4548..628d863 100644 --- a/Ooui.Forms/VisualElementRenderer.cs +++ b/Ooui.Forms/VisualElementRenderer.cs @@ -62,7 +62,7 @@ namespace Ooui.Forms } } - public VisualElementRenderer () : base ("div") + public VisualElementRenderer (string tagName = "div") : base (tagName) { _propertyChangedHandler = OnElementPropertyChanged; } diff --git a/Ooui/Anchor.cs b/Ooui/Anchor.cs new file mode 100644 index 0000000..864011b --- /dev/null +++ b/Ooui/Anchor.cs @@ -0,0 +1,18 @@ +using System; + +namespace Ooui +{ + public class Anchor : Element + { + string href = ""; + public string HRef { + get => href; + set => SetProperty (ref href, value ?? "", "href"); + } + + public Anchor () + : base ("a") + { + } + } +} diff --git a/Samples/DisplayAlertPage.xaml b/Samples/DisplayAlertPage.xaml index 63fee3a..e610926 100644 --- a/Samples/DisplayAlertPage.xaml +++ b/Samples/DisplayAlertPage.xaml @@ -1,11 +1,18 @@