Skip to content
This repository has been archived by the owner on May 1, 2024. It is now read-only.

Commit

Permalink
[Gtk] Implement RadioButton (#14166)
Browse files Browse the repository at this point in the history
* Add RadioButtonRenderer

* Add Controls.RadioButton subtype

* Use Clicked event instead of Activated

* Use ghost button

* Implement UpdateContent()
  • Loading branch information
JunielKatarn committed Jan 14, 2022
1 parent ddbe4d3 commit f01d21d
Show file tree
Hide file tree
Showing 4 changed files with 383 additions and 0 deletions.
209 changes: 209 additions & 0 deletions Xamarin.Forms.Platform.GTK/Controls/RadioButton.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
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
}
}
1 change: 1 addition & 0 deletions Xamarin.Forms.Platform.GTK/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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))]
Expand Down
171 changes: 171 additions & 0 deletions Xamarin.Forms.Platform.GTK/Renderers/RadioButtonRenderer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
using System;
using System.ComponentModel;
using Xamarin.Forms.Platform.GTK.Extensions;

namespace Xamarin.Forms.Platform.GTK.Renderers
{
public class RadioButtonRenderer : ViewRenderer<RadioButton, Controls.RadioButton>
{
Gtk.RadioButton _ghost;

#region VisualElementRenderer overrides

protected override void Dispose(bool disposing)
{
var formsButton = Control;
if (formsButton != null)
{
formsButton.Clicked -= Button_Clicked;
}

base.Dispose(disposing);
}

protected override void OnElementChanged(ElementChangedEventArgs<RadioButton> e)
{
if (e.NewElement != null)
{
if (Control == null)
{
var button = new Controls.RadioButton();
button.Clicked += Button_Clicked;

_ghost = new Gtk.RadioButton(button);
_ghost.Active = true;

SetNativeControl(button);
}

UpdateContent();
}

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)
{
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();
}
}

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

void UpdateContent()
{
var content = Element?.Content;

Control.Label = content?.ToString();
}

void UpdateTextColor() { }

void UpdateFont() { }

void UpdateBorderColor() { }

void UpdateBorderWidth() { }

void UpdateBorderRadius() { }

void UpdatePadding() { }

void UpdateCheck()
{
if (Element.IsChecked)
{
Control.Active = true;
_ghost.Active = false;
}
else
{
_ghost.Active = true;
Control.Active = false;
}
}

#endregion Private methods

#region Handlers

private void Button_Clicked(object sender, EventArgs e)
{
if (Element == null || sender == null)
{
return;
}

Element.IsChecked = (sender as Controls.RadioButton).Active;
}

#endregion Handlers
}
}
Loading

0 comments on commit f01d21d

Please sign in to comment.