From c185f35dbd3bc0c87c347fa8c0cd349d1c661d09 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Wed, 14 Apr 2021 03:43:41 -0700 Subject: [PATCH 1/5] Add RadioButtonRenderer --- .../Properties/AssemblyInfo.cs | 1 + .../Renderers/RadioButtonRenderer.cs | 100 ++++++++++++++++++ .../Xamarin.Forms.Platform.GTK.csproj | 1 + 3 files changed, 102 insertions(+) create mode 100644 Xamarin.Forms.Platform.GTK/Renderers/RadioButtonRenderer.cs diff --git a/Xamarin.Forms.Platform.GTK/Properties/AssemblyInfo.cs b/Xamarin.Forms.Platform.GTK/Properties/AssemblyInfo.cs index 055ae0b8998..0b59ff86b77 100644 --- a/Xamarin.Forms.Platform.GTK/Properties/AssemblyInfo.cs +++ b/Xamarin.Forms.Platform.GTK/Properties/AssemblyInfo.cs @@ -32,6 +32,7 @@ [assembly: ExportRenderer(typeof(Page), typeof(PageRenderer))] [assembly: ExportRenderer(typeof(Picker), typeof(PickerRenderer))] [assembly: ExportRenderer(typeof(ProgressBar), typeof(ProgressBarRenderer))] +[assembly: ExportRenderer(typeof(RadioButton), typeof(RadioButtonRenderer))] [assembly: ExportRenderer(typeof(ScrollView), typeof(ScrollViewRenderer))] [assembly: ExportRenderer(typeof(SearchBar), typeof(SearchBarRenderer))] [assembly: ExportRenderer(typeof(Slider), typeof(SliderRenderer))] diff --git a/Xamarin.Forms.Platform.GTK/Renderers/RadioButtonRenderer.cs b/Xamarin.Forms.Platform.GTK/Renderers/RadioButtonRenderer.cs new file mode 100644 index 00000000000..56bebfe89ba --- /dev/null +++ b/Xamarin.Forms.Platform.GTK/Renderers/RadioButtonRenderer.cs @@ -0,0 +1,100 @@ +using System; +using System.ComponentModel; +using Xamarin.Forms.Platform.GTK.Extensions; + +namespace Xamarin.Forms.Platform.GTK.Renderers +{ + public class RadioButtonRenderer : ViewRenderer + { + #region VisualElementRenderer overrides + + protected override void OnElementChanged(ElementChangedEventArgs e) + { + base.OnElementChanged(e); + + if (e.NewElement != null) + { + if (Control == null) + { + var button = new Gtk.RadioButton("label"); + button.Activated += Button_Activated; + } + + //UpdateContent(); + //UpdateFont(); + } + } + + + protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) + { + base.OnElementPropertyChanged(sender, e); + + if (e.PropertyName == RadioButton.ContentProperty.PropertyName) + { + UpdateContent(); + } + else if (e.PropertyName == RadioButton.TextColorProperty.PropertyName) + { + UpdateTextColor(); + } + else if (e.PropertyName == RadioButton.FontFamilyProperty.PropertyName || + e.PropertyName == RadioButton.FontSizeProperty.PropertyName || + e.PropertyName == RadioButton.FontAttributesProperty.PropertyName) + { + UpdateFont(); + } + else if (e.PropertyName == RadioButton.BorderColorProperty.PropertyName) + { + UpdateBorderColor(); + } + else if (e.PropertyName == RadioButton.BorderWidthProperty.PropertyName) + { + UpdateBorderWidth(); + } + else if (e.PropertyName == RadioButton.CornerRadiusProperty.PropertyName) + { + UpdateBorderRadius(); + } + else if (e.PropertyName == RadioButton.PaddingProperty.PropertyName) + { + UpdatePadding(); + } + else if (e.PropertyName == RadioButton.IsCheckedProperty.PropertyName) + { + UpdateCheck(); + } + } + + #endregion VisualElementRenderer overrides + + #region Private methods + + void UpdateContent() { } + + void UpdateTextColor() { } + + void UpdateFont() { } + + void UpdateBorderColor() { } + + void UpdateBorderWidth() { } + + void UpdateBorderRadius() { } + + void UpdatePadding() { } + + void UpdateCheck() { } + + #endregion Private methods + + #region Handlers + + private void Button_Activated(object sender, EventArgs e) + { + throw new NotImplementedException(); + } + + #endregion Handlers + } +} \ No newline at end of file diff --git a/Xamarin.Forms.Platform.GTK/Xamarin.Forms.Platform.GTK.csproj b/Xamarin.Forms.Platform.GTK/Xamarin.Forms.Platform.GTK.csproj index 3d578703ed8..9cf0f8cf1f8 100644 --- a/Xamarin.Forms.Platform.GTK/Xamarin.Forms.Platform.GTK.csproj +++ b/Xamarin.Forms.Platform.GTK/Xamarin.Forms.Platform.GTK.csproj @@ -207,6 +207,7 @@ + From 29b43cda0a28da6a90feb7cc5af4b8041c14435c Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Wed, 14 Apr 2021 05:20:24 -0700 Subject: [PATCH 2/5] Add Controls.RadioButton subtype --- .../Controls/RadioButton.cs | 210 ++++++++++++++++++ .../Renderers/RadioButtonRenderer.cs | 71 +++++- .../Xamarin.Forms.Platform.GTK.csproj | 1 + 3 files changed, 276 insertions(+), 6 deletions(-) create mode 100644 Xamarin.Forms.Platform.GTK/Controls/RadioButton.cs diff --git a/Xamarin.Forms.Platform.GTK/Controls/RadioButton.cs b/Xamarin.Forms.Platform.GTK/Controls/RadioButton.cs new file mode 100644 index 00000000000..db13d79bb54 --- /dev/null +++ b/Xamarin.Forms.Platform.GTK/Controls/RadioButton.cs @@ -0,0 +1,210 @@ +using Gdk; +using Xamarin.Forms.Platform.GTK.Extensions; + +namespace Xamarin.Forms.Platform.GTK.Controls +{ + public class RadioButton : Gtk.RadioButton + { + private Gtk.Alignment _container; + private Gtk.Box _imageAndLabelContainer; + + private Gdk.Color _defaultBorderColor; + private Gdk.Color _defaultBackgroundColor; + private Gdk.Color? _borderColor; + private Gdk.Color? _backgroundColor; + + private Gtk.Image _image; + private Gtk.Label _label; + private uint _imageSpacing = 0; + private uint _borderWidth = 0; + + public RadioButton(Gtk.RadioButton radio_group_member, string label) : base(radio_group_member, label) + { + _defaultBackgroundColor = Style.Backgrounds[(int)Gtk.StateType.Normal]; + _defaultBorderColor = Style.BaseColors[(int)Gtk.StateType.Active]; + + Relief = Gtk.ReliefStyle.None; + + _image = new Gtk.Image(); + _label = new Gtk.Label(); + _container = new Gtk.Alignment(0.5f, 0.5f, 0, 0); + + Add(_container); + + RecreateContainer(); + } + + public RadioButton(string label) : this(null, label) + { + } + + public RadioButton() : this(string.Empty) { } + + #region Properties + + public Gtk.Label LabelWidget => _label; + + public Gtk.Image ImageWidget => _image; + + public uint ImageSpacing + { + get + { + return _imageSpacing; + } + + set + { + _imageSpacing = value; + UpdateImageSpacing(); + } + } + + #endregion Properties + + #region Public methods + + public void SetBackgroundColor(Gdk.Color? color) + { + _backgroundColor = color; + QueueDraw(); + } + + public void ResetBackgroundColor() + { + _backgroundColor = _defaultBackgroundColor; + QueueDraw(); + } + + public void SetForegroundColor(Gdk.Color color) + { + _label.ModifyFg(Gtk.StateType.Normal, color); + _label.ModifyFg(Gtk.StateType.Prelight, color); + _label.ModifyFg(Gtk.StateType.Active, color); + } + + public void SetBorderWidth(uint width) + { + _borderWidth = width; + QueueDraw(); + } + + public void SetBorderColor(Gdk.Color? color) + { + _borderColor = color; + QueueDraw(); + } + + public void ResetBorderColor() + { + _borderColor = _defaultBorderColor; + QueueDraw(); + } + + public void SetImagePosition(Gtk.PositionType position) + { + ImagePosition = position; + RecreateContainer(); + } + + #endregion Public methods + + #region Gtk.RadioButton overrides + + public override void Destroy() + { + base.Destroy(); + + _label = null; + _image = null; + _imageAndLabelContainer = null; + _container = null; + } + + #endregion Gtk.RadioButton overrides + + #region Gtk.Widget overrides + + protected override bool OnExposeEvent(EventExpose evnt) + { + double colorMaxValue = 65535; + + using (var cr = CairoHelper.Create(GdkWindow)) + { + cr.Rectangle(Allocation.Left, Allocation.Top, Allocation.Width, Allocation.Height); + + // Draw BackgroundColor + if (_backgroundColor.HasValue) + { + var color = _backgroundColor.Value; + cr.SetSourceRGBA(color.Red / colorMaxValue, color.Green / colorMaxValue, color.Blue / colorMaxValue, 1.0); + cr.FillPreserve(); + } + + // Draw BorderColor + if (_borderColor.HasValue) + { + cr.LineWidth = _borderWidth; + + var color = _borderColor.Value; + cr.SetSourceRGB(color.Red / colorMaxValue, color.Green / colorMaxValue, color.Blue / colorMaxValue); + cr.Stroke(); + } + } + + return base.OnExposeEvent(evnt); + } + + #endregion Gtk.Widget overrides + + #region Private methods + + private void RecreateContainer() + { + if (_imageAndLabelContainer != null) + { + _imageAndLabelContainer.RemoveFromContainer(_image); + _imageAndLabelContainer.RemoveFromContainer(_label); + _container.RemoveFromContainer(_imageAndLabelContainer); + _imageAndLabelContainer = null; + } + + switch (ImagePosition) + { + case Gtk.PositionType.Left: + _imageAndLabelContainer = new Gtk.HBox(); + _imageAndLabelContainer.PackStart(_image, false, false, _imageSpacing); + _imageAndLabelContainer.PackStart(_label, false, false, 0); + break; + case Gtk.PositionType.Right: + _imageAndLabelContainer = new Gtk.HBox(); + _imageAndLabelContainer.PackStart(_label, false, false, 0); + _imageAndLabelContainer.PackStart(_image, false, false, _imageSpacing); + break; + case Gtk.PositionType.Top: + _imageAndLabelContainer = new Gtk.VBox(); + _imageAndLabelContainer.PackStart(_image, false, false, _imageSpacing); + _imageAndLabelContainer.PackStart(_label, false, false, 0); + break; + case Gtk.PositionType.Bottom: + _imageAndLabelContainer = new Gtk.VBox(); + _imageAndLabelContainer.PackStart(_label, false, false, 0); + _imageAndLabelContainer.PackStart(_image, false, false, _imageSpacing); + break; + } + + if (_imageAndLabelContainer != null) + { + _container.Add(_imageAndLabelContainer); + _container.ShowAll(); + } + } + + private void UpdateImageSpacing() + { + _imageAndLabelContainer.SetChildPacking(_image, false, false, _imageSpacing, Gtk.PackType.Start); + } + + #endregion Private methods + } +} \ No newline at end of file diff --git a/Xamarin.Forms.Platform.GTK/Renderers/RadioButtonRenderer.cs b/Xamarin.Forms.Platform.GTK/Renderers/RadioButtonRenderer.cs index 56bebfe89ba..37586935813 100644 --- a/Xamarin.Forms.Platform.GTK/Renderers/RadioButtonRenderer.cs +++ b/Xamarin.Forms.Platform.GTK/Renderers/RadioButtonRenderer.cs @@ -4,27 +4,53 @@ namespace Xamarin.Forms.Platform.GTK.Renderers { - public class RadioButtonRenderer : ViewRenderer + public class RadioButtonRenderer : ViewRenderer { #region VisualElementRenderer overrides - protected override void OnElementChanged(ElementChangedEventArgs e) + protected override void Dispose(bool disposing) { - base.OnElementChanged(e); + var formsButton = Control; + if (formsButton != null) + { + formsButton.Activated -= Button_Activated; + } + base.Dispose(disposing); + } + + protected override void OnElementChanged(ElementChangedEventArgs e) + { if (e.NewElement != null) { if (Control == null) { - var button = new Gtk.RadioButton("label"); + var button = new Controls.RadioButton(); button.Activated += Button_Activated; + + SetNativeControl(button); } //UpdateContent(); //UpdateFont(); } + + base.OnElementChanged(e); } + protected override bool PreventGestureBubbling { get; set; } = true; + + public override SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint) + { + var req = Control.SizeRequest(); + + var widthFits = widthConstraint >= req.Width; + var heightFits = heightConstraint >= req.Height; + + var size = new Size(widthFits ? req.Width : (int)widthConstraint, heightFits ? req.Height : (int)heightConstraint); + + return new SizeRequest(size); + } protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) { @@ -66,6 +92,31 @@ protected override void OnElementPropertyChanged(object sender, PropertyChangedE } } + protected override void SetAccessibilityLabel() + { + //TODO + base.SetAccessibilityLabel(); + } + + protected override void UpdateBackgroundColor() + { + if (Element == null) + return; + + if (Element.BackgroundColor.IsDefault) + { + Control.ResetBackgroundColor(); + } + else if (Element.BackgroundColor != Color.Transparent) + { + Control.SetBackgroundColor(Element.BackgroundColor.ToGtkColor()); + } + else + { + Control.SetBackgroundColor(null); + } + } + #endregion VisualElementRenderer overrides #region Private methods @@ -84,7 +135,10 @@ void UpdateBorderRadius() { } void UpdatePadding() { } - void UpdateCheck() { } + void UpdateCheck() + { + Control.Active = Element.IsChecked ? true : false; + } #endregion Private methods @@ -92,7 +146,12 @@ void UpdateCheck() { } private void Button_Activated(object sender, EventArgs e) { - throw new NotImplementedException(); + if (Element == null || sender == null) + { + return; + } + + Element.IsChecked = (sender as Controls.RadioButton).Active; } #endregion Handlers diff --git a/Xamarin.Forms.Platform.GTK/Xamarin.Forms.Platform.GTK.csproj b/Xamarin.Forms.Platform.GTK/Xamarin.Forms.Platform.GTK.csproj index 9cf0f8cf1f8..857e7378480 100644 --- a/Xamarin.Forms.Platform.GTK/Xamarin.Forms.Platform.GTK.csproj +++ b/Xamarin.Forms.Platform.GTK/Xamarin.Forms.Platform.GTK.csproj @@ -136,6 +136,7 @@ + From 399d14657f303533a9b39855b8e3e5ed8fd6452e Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Wed, 14 Apr 2021 05:46:05 -0700 Subject: [PATCH 3/5] Use Clicked event instead of Activated --- Xamarin.Forms.Platform.GTK/Controls/RadioButton.cs | 4 ++++ Xamarin.Forms.Platform.GTK/Renderers/RadioButtonRenderer.cs | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Xamarin.Forms.Platform.GTK/Controls/RadioButton.cs b/Xamarin.Forms.Platform.GTK/Controls/RadioButton.cs index db13d79bb54..c3020eb0b28 100644 --- a/Xamarin.Forms.Platform.GTK/Controls/RadioButton.cs +++ b/Xamarin.Forms.Platform.GTK/Controls/RadioButton.cs @@ -29,6 +29,10 @@ public RadioButton(Gtk.RadioButton radio_group_member, string label) : base(radi _label = new Gtk.Label(); _container = new Gtk.Alignment(0.5f, 0.5f, 0, 0); + //TODO: Hack!!! Remove. + var otro = new Gtk.RadioButton("ghost"); + this.Group = otro.Group; + Add(_container); RecreateContainer(); diff --git a/Xamarin.Forms.Platform.GTK/Renderers/RadioButtonRenderer.cs b/Xamarin.Forms.Platform.GTK/Renderers/RadioButtonRenderer.cs index 37586935813..dc889d18110 100644 --- a/Xamarin.Forms.Platform.GTK/Renderers/RadioButtonRenderer.cs +++ b/Xamarin.Forms.Platform.GTK/Renderers/RadioButtonRenderer.cs @@ -13,7 +13,7 @@ protected override void Dispose(bool disposing) var formsButton = Control; if (formsButton != null) { - formsButton.Activated -= Button_Activated; + formsButton.Clicked -= Button_Clicked; } base.Dispose(disposing); @@ -26,7 +26,7 @@ protected override void OnElementChanged(ElementChangedEventArgs e) if (Control == null) { var button = new Controls.RadioButton(); - button.Activated += Button_Activated; + button.Clicked += Button_Clicked; SetNativeControl(button); } @@ -144,7 +144,7 @@ void UpdateCheck() #region Handlers - private void Button_Activated(object sender, EventArgs e) + private void Button_Clicked(object sender, EventArgs e) { if (Element == null || sender == null) { From 589ad88aa12e1329c0f2fa195200c7f48c939c42 Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Fri, 16 Apr 2021 02:01:01 -0700 Subject: [PATCH 4/5] Use ghost button --- .../Controls/RadioButton.cs | 7 +----- .../Renderers/RadioButtonRenderer.cs | 25 +++++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Xamarin.Forms.Platform.GTK/Controls/RadioButton.cs b/Xamarin.Forms.Platform.GTK/Controls/RadioButton.cs index c3020eb0b28..e16469a87fe 100644 --- a/Xamarin.Forms.Platform.GTK/Controls/RadioButton.cs +++ b/Xamarin.Forms.Platform.GTK/Controls/RadioButton.cs @@ -29,18 +29,13 @@ public RadioButton(Gtk.RadioButton radio_group_member, string label) : base(radi _label = new Gtk.Label(); _container = new Gtk.Alignment(0.5f, 0.5f, 0, 0); - //TODO: Hack!!! Remove. - var otro = new Gtk.RadioButton("ghost"); - this.Group = otro.Group; Add(_container); RecreateContainer(); } - public RadioButton(string label) : this(null, label) - { - } + public RadioButton(string label) : this(null, label) { } public RadioButton() : this(string.Empty) { } diff --git a/Xamarin.Forms.Platform.GTK/Renderers/RadioButtonRenderer.cs b/Xamarin.Forms.Platform.GTK/Renderers/RadioButtonRenderer.cs index dc889d18110..d4f688774f7 100644 --- a/Xamarin.Forms.Platform.GTK/Renderers/RadioButtonRenderer.cs +++ b/Xamarin.Forms.Platform.GTK/Renderers/RadioButtonRenderer.cs @@ -6,6 +6,8 @@ namespace Xamarin.Forms.Platform.GTK.Renderers { public class RadioButtonRenderer : ViewRenderer { + Gtk.RadioButton _ghost; + #region VisualElementRenderer overrides protected override void Dispose(bool disposing) @@ -28,11 +30,11 @@ protected override void OnElementChanged(ElementChangedEventArgs e) var button = new Controls.RadioButton(); button.Clicked += Button_Clicked; + _ghost = new Gtk.RadioButton(button); + _ghost.Active = true; + SetNativeControl(button); } - - //UpdateContent(); - //UpdateFont(); } base.OnElementChanged(e); @@ -92,12 +94,6 @@ protected override void OnElementPropertyChanged(object sender, PropertyChangedE } } - protected override void SetAccessibilityLabel() - { - //TODO - base.SetAccessibilityLabel(); - } - protected override void UpdateBackgroundColor() { if (Element == null) @@ -137,7 +133,16 @@ void UpdatePadding() { } void UpdateCheck() { - Control.Active = Element.IsChecked ? true : false; + if (Element.IsChecked) + { + Control.Active = true; + _ghost.Active = false; + } + else + { + _ghost.Active = true; + Control.Active = false; + } } #endregion Private methods From a78f193ec28457a421ac578922c5a5bd7f6b525c Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Fri, 16 Apr 2021 02:05:58 -0700 Subject: [PATCH 5/5] Implement UpdateContent() --- .../Renderers/RadioButtonRenderer.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Xamarin.Forms.Platform.GTK/Renderers/RadioButtonRenderer.cs b/Xamarin.Forms.Platform.GTK/Renderers/RadioButtonRenderer.cs index d4f688774f7..7d87fbe32b5 100644 --- a/Xamarin.Forms.Platform.GTK/Renderers/RadioButtonRenderer.cs +++ b/Xamarin.Forms.Platform.GTK/Renderers/RadioButtonRenderer.cs @@ -35,6 +35,8 @@ protected override void OnElementChanged(ElementChangedEventArgs e) SetNativeControl(button); } + + UpdateContent(); } base.OnElementChanged(e); @@ -117,7 +119,12 @@ protected override void UpdateBackgroundColor() #region Private methods - void UpdateContent() { } + void UpdateContent() + { + var content = Element?.Content; + + Control.Label = content?.ToString(); + } void UpdateTextColor() { }