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

[Gtk] Implement RadioButton #14166

Merged
merged 5 commits into from
Jan 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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