2017-09-28 14:52:27 +03:00
|
|
|
|
using Gtk;
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.ComponentModel;
|
|
|
|
|
using Xamarin.Forms.Platform.GTK.Extensions;
|
2018-10-05 04:24:22 +03:00
|
|
|
|
using Container = Xamarin.Forms.Platform.GTK.GtkFormsContainer;
|
2017-09-28 14:52:27 +03:00
|
|
|
|
using Control = Gtk.Widget;
|
|
|
|
|
|
|
|
|
|
namespace Xamarin.Forms.Platform.GTK
|
|
|
|
|
{
|
2018-09-20 22:11:20 +03:00
|
|
|
|
public class VisualElementRenderer<TElement, TNativeElement> : Container, IVisualElementRenderer, IDisposable, IEffectControlProvider
|
|
|
|
|
where TElement : VisualElement
|
|
|
|
|
where TNativeElement : Control
|
|
|
|
|
{
|
|
|
|
|
private bool _disposed;
|
|
|
|
|
private readonly PropertyChangedEventHandler _propertyChangedHandler;
|
|
|
|
|
private readonly List<EventHandler<VisualElementChangedEventArgs>> _elementChangedHandlers = new List<EventHandler<VisualElementChangedEventArgs>>();
|
|
|
|
|
private VisualElementTracker<TElement, TNativeElement> _tracker;
|
|
|
|
|
private string _defaultAccessibilityLabel;
|
|
|
|
|
private string _defaultAccessibilityHint;
|
|
|
|
|
|
|
|
|
|
protected VisualElementRenderer()
|
|
|
|
|
{
|
|
|
|
|
_propertyChangedHandler = OnElementPropertyChanged;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected VisualElementTracker<TElement, TNativeElement> Tracker
|
|
|
|
|
{
|
|
|
|
|
get { return _tracker; }
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (_tracker == value)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (_tracker != null)
|
|
|
|
|
{
|
|
|
|
|
_tracker.Dispose();
|
|
|
|
|
_tracker.Updated -= OnTrackerUpdated;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_tracker = value;
|
|
|
|
|
|
|
|
|
|
if (_tracker != null)
|
|
|
|
|
{
|
|
|
|
|
_tracker.Updated += OnTrackerUpdated;
|
|
|
|
|
UpdateTracker();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public TNativeElement Control { get; set; }
|
|
|
|
|
|
|
|
|
|
public TElement Element { get; set; }
|
|
|
|
|
|
|
|
|
|
public Container Container => this;
|
|
|
|
|
|
|
|
|
|
public bool Disposed { get { return _disposed; } }
|
|
|
|
|
|
|
|
|
|
VisualElement IVisualElementRenderer.Element
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return Element;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected IElementController ElementController => Element as IElementController;
|
|
|
|
|
|
|
|
|
|
protected virtual bool PreventGestureBubbling { get; set; } = false;
|
|
|
|
|
|
|
|
|
|
event EventHandler<VisualElementChangedEventArgs> IVisualElementRenderer.ElementChanged
|
|
|
|
|
{
|
|
|
|
|
add { _elementChangedHandlers.Add(value); }
|
|
|
|
|
remove { _elementChangedHandlers.Remove(value); }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public event EventHandler<ElementChangedEventArgs<TElement>> ElementChanged;
|
|
|
|
|
|
|
|
|
|
void IEffectControlProvider.RegisterEffect(Effect effect)
|
|
|
|
|
{
|
|
|
|
|
var platformEffect = effect as PlatformEffect;
|
|
|
|
|
if (platformEffect != null)
|
|
|
|
|
OnRegisterEffect(platformEffect);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void IVisualElementRenderer.SetElement(VisualElement element)
|
|
|
|
|
{
|
|
|
|
|
SetElement((TElement)element);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void SetElement(TElement element)
|
|
|
|
|
{
|
|
|
|
|
var oldElement = Element;
|
|
|
|
|
Element = element;
|
|
|
|
|
|
|
|
|
|
if (oldElement != null)
|
|
|
|
|
{
|
|
|
|
|
oldElement.FocusChangeRequested -= OnElementFocusChangeRequested;
|
|
|
|
|
oldElement.PropertyChanged -= _propertyChangedHandler;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (element != null)
|
|
|
|
|
{
|
|
|
|
|
element.PropertyChanged += _propertyChangedHandler;
|
|
|
|
|
element.FocusChangeRequested += OnElementFocusChangeRequested;
|
|
|
|
|
|
|
|
|
|
if (Tracker == null)
|
|
|
|
|
{
|
|
|
|
|
Tracker = new VisualElementTracker<TElement, TNativeElement>();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OnElementChanged(new ElementChangedEventArgs<TElement>(oldElement, element));
|
|
|
|
|
|
|
|
|
|
SetAccessibilityLabel();
|
|
|
|
|
SetAccessibilityHint();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void SetElementSize(Size size)
|
|
|
|
|
{
|
|
|
|
|
Layout.LayoutChildIntoBoundingRegion(Element,
|
|
|
|
|
new Rectangle(Element.X, Element.Y, size.Width, size.Height));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public virtual SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
|
|
|
|
|
{
|
|
|
|
|
if (Children.Length == 0)
|
|
|
|
|
return new SizeRequest();
|
|
|
|
|
|
|
|
|
|
return Control.GetDesiredSize(widthConstraint, heightConstraint);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void OnShown()
|
|
|
|
|
{
|
|
|
|
|
base.OnShown();
|
|
|
|
|
|
|
|
|
|
UpdateIsVisible();
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-30 20:06:47 +03:00
|
|
|
|
public override void Destroy()
|
2018-09-20 22:11:20 +03:00
|
|
|
|
{
|
2018-10-30 20:06:47 +03:00
|
|
|
|
base.Destroy();
|
2018-09-20 22:11:20 +03:00
|
|
|
|
Dispose(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void OnSizeAllocated(Gdk.Rectangle allocation)
|
|
|
|
|
{
|
|
|
|
|
base.OnSizeAllocated(allocation);
|
|
|
|
|
|
|
|
|
|
double width, height, translationX, translationY;
|
|
|
|
|
Rectangle bounds = Element.Bounds;
|
|
|
|
|
|
|
|
|
|
translationX = Element.TranslationX;
|
|
|
|
|
translationY = Element.TranslationY;
|
|
|
|
|
|
|
|
|
|
width = bounds.Width >= -1 ? bounds.Width : 0;
|
|
|
|
|
height = bounds.Height >= -1 ? bounds.Height : 0;
|
|
|
|
|
|
|
|
|
|
Container.SetSize(width, height);
|
|
|
|
|
Container.MoveTo((int)bounds.X + translationX, (int)bounds.Y + translationY);
|
|
|
|
|
|
|
|
|
|
for (var i = 0; i < ElementController.LogicalChildren.Count; i++)
|
|
|
|
|
{
|
|
|
|
|
var child = ElementController.LogicalChildren[i] as VisualElement;
|
|
|
|
|
|
|
|
|
|
if (child != null)
|
|
|
|
|
{
|
|
|
|
|
var renderer = Platform.GetRenderer(child);
|
|
|
|
|
|
|
|
|
|
if (renderer != null)
|
|
|
|
|
{
|
|
|
|
|
width = child.Bounds.Width >= -1 ? child.Bounds.Width : 0;
|
|
|
|
|
height = child.Bounds.Height >= -1 ? child.Bounds.Height : 0;
|
|
|
|
|
|
|
|
|
|
translationX = child.TranslationX;
|
|
|
|
|
translationY = child.TranslationY;
|
|
|
|
|
|
|
|
|
|
renderer.Container.SetSize(width, height);
|
|
|
|
|
renderer.Container.MoveTo(child.Bounds.X + translationX, child.Bounds.Y + translationY);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected virtual void OnRegisterEffect(PlatformEffect effect)
|
|
|
|
|
{
|
|
|
|
|
effect.SetContainer(this);
|
|
|
|
|
effect.SetControl(Container);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected virtual void OnElementChanged(ElementChangedEventArgs<TElement> e)
|
|
|
|
|
{
|
|
|
|
|
var args = new VisualElementChangedEventArgs(e.OldElement, e.NewElement);
|
|
|
|
|
for (var i = 0; i < _elementChangedHandlers.Count; i++)
|
|
|
|
|
_elementChangedHandlers[i](this, args);
|
|
|
|
|
|
|
|
|
|
ElementChanged?.Invoke(this, e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected virtual void SetNativeControl(TNativeElement view)
|
|
|
|
|
{
|
|
|
|
|
Control = view;
|
|
|
|
|
|
|
|
|
|
UpdateBackgroundColor();
|
|
|
|
|
UpdateIsVisible();
|
|
|
|
|
UpdateSensitive();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected virtual void Dispose(bool disposing)
|
|
|
|
|
{
|
|
|
|
|
if (!disposing || _disposed)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
_disposed = true;
|
|
|
|
|
|
|
|
|
|
Tracker?.Dispose();
|
|
|
|
|
Tracker = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected virtual void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (e.PropertyName == VisualElement.IsVisibleProperty.PropertyName)
|
|
|
|
|
UpdateIsVisible();
|
|
|
|
|
else if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName)
|
|
|
|
|
UpdateBackgroundColor();
|
|
|
|
|
else if (e.PropertyName == VisualElement.IsEnabledProperty.PropertyName)
|
|
|
|
|
UpdateSensitive();
|
|
|
|
|
else if (e.PropertyName == AutomationProperties.HelpTextProperty.PropertyName)
|
|
|
|
|
SetAccessibilityHint();
|
|
|
|
|
else if (e.PropertyName == AutomationProperties.NameProperty.PropertyName)
|
|
|
|
|
SetAccessibilityLabel();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected virtual void UpdateBackgroundColor()
|
|
|
|
|
{
|
|
|
|
|
if (_disposed || Element == null || Control == null)
|
|
|
|
|
return;
|
|
|
|
|
|
2018-10-05 04:24:22 +03:00
|
|
|
|
Container.SetBackgroundColor(Element.BackgroundColor);
|
2018-09-20 22:11:20 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected virtual void SetAccessibilityHint()
|
|
|
|
|
{
|
|
|
|
|
if (_disposed || Element == null || Control == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (_defaultAccessibilityHint == null)
|
|
|
|
|
_defaultAccessibilityHint = Accessible.Name;
|
|
|
|
|
|
|
|
|
|
var helpText = (string)Element.GetValue(AutomationProperties.HelpTextProperty) ?? _defaultAccessibilityHint;
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(helpText))
|
|
|
|
|
{
|
|
|
|
|
Accessible.Name = helpText;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected virtual void SetAccessibilityLabel()
|
|
|
|
|
{
|
|
|
|
|
if (_disposed || Element == null || Control == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (_defaultAccessibilityLabel == null)
|
|
|
|
|
_defaultAccessibilityLabel = Accessible.Description;
|
|
|
|
|
|
|
|
|
|
var name = (string)Element.GetValue(AutomationProperties.NameProperty) ?? _defaultAccessibilityLabel;
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(name))
|
|
|
|
|
{
|
|
|
|
|
Accessible.Description = name;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected virtual void UpdateNativeControl()
|
|
|
|
|
{
|
|
|
|
|
UpdateSensitive();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal virtual void OnElementFocusChangeRequested(object sender, VisualElement.FocusRequestArgs args)
|
|
|
|
|
{
|
|
|
|
|
var control = Control as Control;
|
|
|
|
|
|
|
|
|
|
if (control == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (args.Focus)
|
|
|
|
|
args.Result = control.IsFocus = true;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
control.IsFocus = false;
|
|
|
|
|
args.Result = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void UpdateIsVisible()
|
|
|
|
|
{
|
|
|
|
|
if (_disposed || Element == null || Control == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
Container.Visible = Element.IsVisible;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void UpdateSensitive()
|
|
|
|
|
{
|
|
|
|
|
if (_disposed || Element == null || Control == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
Control.Sensitive = Element.IsEnabled;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void OnTrackerUpdated(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
UpdateNativeControl();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void UpdateTracker()
|
|
|
|
|
{
|
|
|
|
|
if (_tracker == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
_tracker.PreventGestureBubbling = PreventGestureBubbling;
|
|
|
|
|
_tracker.Control = Control;
|
|
|
|
|
_tracker.Element = Element;
|
|
|
|
|
_tracker.Container = Container;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|