summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Platform.Tizen/Renderers/VisualElementRenderer.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Xamarin.Forms.Platform.Tizen/Renderers/VisualElementRenderer.cs')
-rw-r--r--Xamarin.Forms.Platform.Tizen/Renderers/VisualElementRenderer.cs946
1 files changed, 946 insertions, 0 deletions
diff --git a/Xamarin.Forms.Platform.Tizen/Renderers/VisualElementRenderer.cs b/Xamarin.Forms.Platform.Tizen/Renderers/VisualElementRenderer.cs
new file mode 100644
index 00000000..8c5c0be0
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Renderers/VisualElementRenderer.cs
@@ -0,0 +1,946 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.ComponentModel;
+using ElmSharp;
+using ELayout = ElmSharp.Layout;
+using EColor = ElmSharp.Color;
+using ESize = ElmSharp.Size;
+using ERect = ElmSharp.Rect;
+using ERectangle = ElmSharp.Rectangle;
+using EBox = ElmSharp.Box;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ /// <summary>
+ /// Base class for rendering of a Xamarin element.
+ /// </summary>
+ public abstract class VisualElementRenderer<TElement> : IVisualElementRenderer, IEffectControlProvider where TElement : VisualElement
+ {
+ /// <summary>
+ /// Holds registered element changed handlers.
+ /// </summary>
+ readonly List<EventHandler<VisualElementChangedEventArgs>> _elementChangedHandlers = new List<EventHandler<VisualElementChangedEventArgs>>();
+
+ /// <summary>
+ /// Handler for property changed events.
+ /// </summary>
+ PropertyChangedEventHandler _propertyChangedHandler;
+
+ EventHandler<EventArg<VisualElement>> _batchCommittedHandler;
+
+ /// <summary>
+ /// Flags which control status of renderer.
+ /// </summary>
+ VisualElementRendererFlags _flags = VisualElementRendererFlags.None;
+
+ /// <summary>
+ /// Holds the native view.
+ /// </summary>
+ EvasObject _view;
+
+ Dictionary<string, Action<bool>> _propertyHandlersWithInit = new Dictionary<string, Action<bool>>();
+
+ Dictionary<string, Action> _propertyHandlers = new Dictionary<string, Action>();
+
+ HashSet<string> _batchedProperties = new HashSet<string>();
+
+ ERectangle _opacityLayer;
+
+ internal GestureHandler _gestureHandler;
+
+ /// <summary>
+ /// Default constructor.
+ /// </summary>
+ protected VisualElementRenderer()
+ {
+ RegisterPropertyHandler(VisualElement.IsVisibleProperty, UpdateIsVisible);
+ RegisterPropertyHandler(VisualElement.OpacityProperty, UpdateOpacity);
+ RegisterPropertyHandler(VisualElement.IsEnabledProperty, UpdateIsEnabled);
+ RegisterPropertyHandler(VisualElement.InputTransparentProperty, UpdateInputTransparent);
+ RegisterPropertyHandler(VisualElement.BackgroundColorProperty, UpdateBackgroundColor);
+
+ RegisterPropertyHandler(VisualElement.AnchorXProperty, ApplyTransformation);
+ RegisterPropertyHandler(VisualElement.AnchorYProperty, ApplyTransformation);
+ RegisterPropertyHandler(VisualElement.ScaleProperty, ApplyTransformation);
+ RegisterPropertyHandler(VisualElement.RotationProperty, ApplyTransformation);
+ RegisterPropertyHandler(VisualElement.RotationXProperty, ApplyTransformation);
+ RegisterPropertyHandler(VisualElement.RotationYProperty, ApplyTransformation);
+ RegisterPropertyHandler(VisualElement.TranslationXProperty, ApplyTransformation);
+ RegisterPropertyHandler(VisualElement.TranslationYProperty, ApplyTransformation);
+ }
+
+ ~VisualElementRenderer()
+ {
+ Dispose(false);
+ }
+
+ event EventHandler<VisualElementChangedEventArgs> ElementChanged
+ {
+ add
+ {
+ _elementChangedHandlers.Add(value);
+ }
+ remove
+ {
+ _elementChangedHandlers.Remove(value);
+ }
+ }
+
+ /// <summary>
+ /// Gets the Xamarin element associated with this renderer.
+ /// </summary>
+ public TElement Element
+ {
+ get;
+ private set;
+ }
+
+ VisualElement IVisualElementRenderer.Element
+ {
+ get
+ {
+ return this.Element;
+ }
+ }
+
+ public EvasObject NativeView
+ {
+ get
+ {
+ return _view;
+ }
+ }
+
+ protected bool IsDisposed => (_flags == VisualElementRendererFlags.Disposed);
+
+ /// <summary>
+ /// Releases all resource used by the <see cref="Xamarin.Forms.Platform.Tizen.VisualElementRenderer"/> object.
+ /// </summary>
+ /// <remarks>Call <see cref="Dispose"/> when you are finished using the
+ /// <see cref="Xamarin.Forms.Platform.Tizen.VisualElementRenderer"/>. The <see cref="Dispose"/> method
+ /// leaves the <see cref="Xamarin.Forms.Platform.Tizen.VisualElementRenderer"/> in an unusable state.
+ /// After calling <see cref="Dispose"/>, you must release all references to the
+ /// <see cref="Xamarin.Forms.Platform.Tizen.VisualElementRenderer"/> so the garbage collector can reclaim
+ /// the memory that the <see cref="Xamarin.Forms.Platform.Tizen.VisualElementRenderer"/> was occupying.</remarks>
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ public SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
+ {
+ if (null == NativeView)
+ {
+ return new SizeRequest(new Size(0, 0));
+ }
+ else
+ {
+ int availableWidth = ToNativeDimension(widthConstraint);
+ int availableHeight = ToNativeDimension(heightConstraint);
+ ESize measured;
+
+ var nativeViewMeasurable = NativeView as Native.IMeasurable;
+ if (nativeViewMeasurable != null)
+ {
+ measured = nativeViewMeasurable.Measure(availableWidth, availableHeight);
+ }
+ else
+ {
+ measured = Measure(availableWidth, availableHeight);
+ }
+
+ return new SizeRequest(new Size(measured.Width, measured.Height), MinimumSize());
+ }
+ }
+
+ /// <summary>
+ /// Sets the element associated with this renderer.
+ /// </summary>
+ public void SetElement(TElement newElement)
+ {
+ if (newElement == null)
+ {
+ throw new ArgumentNullException("newElement");
+ }
+
+ TElement oldElement = Element;
+ if (oldElement != null)
+ {
+ throw new InvalidOperationException("oldElement");
+ }
+
+ Element = newElement;
+ if (_propertyChangedHandler == null)
+ {
+ _propertyChangedHandler = new PropertyChangedEventHandler(OnElementPropertyChanged);
+ }
+
+ if (_batchCommittedHandler == null)
+ {
+ _batchCommittedHandler = OnBatchCommitted;
+ }
+
+ // send notification
+ OnElementChanged(new ElementChangedEventArgs<TElement>(oldElement, newElement));
+
+ // store renderer for the new element
+ Platform.SetRenderer(newElement, this);
+
+ // add children
+ var logicalChildren = (newElement as IElementController).LogicalChildren;
+ foreach (Element child in logicalChildren)
+ {
+ AddChild(child);
+ }
+
+ OnElementReady();
+ }
+
+ public void UpdateNativeGeometry()
+ {
+ var x = ComputeAbsoluteX(Element);
+ var y = ComputeAbsoluteY(Element);
+ NativeView.Geometry = new ERect(ToNativeDimension(x), ToNativeDimension(y), ToNativeDimension(Element.Width), ToNativeDimension(Element.Height));
+ ApplyTransformation();
+ UpdateOpacityLayer();
+ }
+
+ void IVisualElementRenderer.SetElement(VisualElement element)
+ {
+ TElement tElement = element as TElement;
+ if (tElement == null)
+ {
+ throw new ArgumentException("Element is not of type " + typeof(TElement), "Element");
+ }
+ SetElement(tElement);
+ }
+
+ /// <summary>
+ /// Registers the effect with the element by establishing the parent-child relations needed for rendering on the specific platform.
+ /// </summary>
+ /// <param name="effect">The effect to register.</param>
+ void IEffectControlProvider.RegisterEffect(Effect effect)
+ {
+ RegisterEffect(effect);
+ }
+
+ /// <summary>
+ /// Registers the effect with the element by establishing the parent-child relations needed for rendering on the specific platform.
+ /// </summary>
+ /// <param name="effect">The effect to register.</param>
+ protected void RegisterEffect(Effect effect)
+ {
+ var platformEffect = effect as PlatformEffect;
+ if (platformEffect != null)
+ {
+ OnRegisterEffect(platformEffect);
+ }
+ }
+
+
+ protected virtual void UpdateLayout()
+ {
+ // we're updating the coordinates of native control only if they were modified
+ // via Xamarin (Settings.IgnoreBatchCommitted is set to false);
+ // otherwise native control is already in the right place
+ if (!Settings.IgnoreBatchCommitted && null != NativeView)
+ {
+ UpdateNativeGeometry();
+ }
+
+ // we're updating just immediate children
+ var logicalChildren = (Element as IElementController).LogicalChildren;
+ foreach (var child in logicalChildren)
+ {
+ Platform.GetRenderer(child)?.UpdateNativeGeometry();
+ }
+ }
+
+ /// <summary>
+ /// Disposes of underlying resources.
+ /// </summary>
+ /// <param name="disposing">True if the memory release was requested on demand.</param>
+ protected virtual void Dispose(bool disposing)
+ {
+ if ((_flags & VisualElementRendererFlags.Disposed) != 0)
+ {
+ return;
+ }
+
+ _flags |= VisualElementRendererFlags.Disposed;
+
+ if (disposing)
+ {
+ if (Element != null)
+ {
+ Element.PropertyChanged -= _propertyChangedHandler;
+ Element.BatchCommitted -= _batchCommittedHandler;
+
+ Element.ChildAdded -= OnChildAdded;
+ Element.ChildRemoved -= OnChildRemoved;
+ Element.ChildrenReordered -= OnChildrenReordered;
+
+ Element.FocusChangeRequested -= OnFocusChangeRequested;
+
+ Settings.StartIgnoringBatchCommitted();
+ Element.Layout(new Rectangle(0, 0, -1, -1));
+ Settings.StopIgnoringBatchCommitted();
+
+ var logicalChildren = (Element as IElementController).LogicalChildren;
+ foreach (var child in logicalChildren)
+ {
+ Platform.GetRenderer(child)?.Dispose();
+ }
+
+ if (Platform.GetRenderer(Element) == this)
+ {
+ Platform.SetRenderer(Element, (IVisualElementRenderer)null);
+ }
+ Element = default(TElement);
+ }
+
+ if (NativeView != null)
+ {
+ NativeView.Deleted -= NativeViewDeleted;
+ EnsureOpacityLayerIsDestroyed();
+ NativeView.Unrealize();
+ SetNativeView(null);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Notification that the associated element has changed.
+ /// </summary>
+ /// <param name="e">Event parameters.</param>
+ protected virtual void OnElementChanged(ElementChangedEventArgs<TElement> e)
+ {
+ if (null != e.OldElement)
+ {
+ e.OldElement.PropertyChanged -= _propertyChangedHandler;
+ e.OldElement.BatchCommitted -= _batchCommittedHandler;
+
+ e.OldElement.ChildAdded -= OnChildAdded;
+ e.OldElement.ChildRemoved -= OnChildRemoved;
+ e.OldElement.ChildrenReordered -= OnChildrenReordered;
+
+ e.OldElement.FocusChangeRequested -= OnFocusChangeRequested;
+
+ Settings.StartIgnoringBatchCommitted();
+ Element.Layout(new Rectangle(0, 0, -1, -1));
+ Settings.StopIgnoringBatchCommitted();
+
+ var controller = e.OldElement as IElementController;
+ if (controller != null && controller.EffectControlProvider == this)
+ {
+ controller.EffectControlProvider = null;
+ }
+ }
+
+ if (null != e.NewElement)
+ {
+ e.NewElement.PropertyChanged += _propertyChangedHandler;
+ e.NewElement.BatchCommitted += _batchCommittedHandler;
+
+ e.NewElement.ChildAdded += OnChildAdded;
+ e.NewElement.ChildRemoved += OnChildRemoved;
+ e.NewElement.ChildrenReordered += OnChildrenReordered;
+
+ e.NewElement.FocusChangeRequested += OnFocusChangeRequested;
+
+ UpdateAllProperties(true);
+
+ var controller = e.NewElement as IElementController;
+ if (controller != null)
+ {
+ controller.EffectControlProvider = this;
+ }
+ }
+
+ // TODO: handle the event
+ }
+
+ /// <summary>
+ /// Notification that the property of the associated element has changed.
+ /// </summary>
+ /// <param name="sender">Object which sent the notification.</param>
+ /// <param name="e">Event parameters.</param>
+ protected virtual void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (Element.Batched)
+ {
+ if (e.PropertyName == VisualElement.XProperty.PropertyName ||
+ e.PropertyName == VisualElement.YProperty.PropertyName ||
+ e.PropertyName == VisualElement.WidthProperty.PropertyName ||
+ e.PropertyName == VisualElement.HeightProperty.PropertyName)
+ {
+ _flags |= VisualElementRendererFlags.NeedsLayout;
+ }
+ else if (e.PropertyName == VisualElement.TranslationXProperty.PropertyName ||
+ e.PropertyName == VisualElement.TranslationYProperty.PropertyName ||
+ e.PropertyName == VisualElement.RotationProperty.PropertyName ||
+ e.PropertyName == VisualElement.RotationXProperty.PropertyName ||
+ e.PropertyName == VisualElement.RotationYProperty.PropertyName ||
+ e.PropertyName == VisualElement.ScaleProperty.PropertyName ||
+ e.PropertyName == VisualElement.AnchorXProperty.PropertyName ||
+ e.PropertyName == VisualElement.AnchorYProperty.PropertyName)
+ {
+ _flags |= VisualElementRendererFlags.NeedsTransformation;
+ }
+ else
+ {
+ _batchedProperties.Add(e.PropertyName);
+ }
+ return;
+ }
+
+ Action<bool> init;
+ if (_propertyHandlersWithInit.TryGetValue(e.PropertyName, out init))
+ {
+ init(false);
+ }
+ else
+ {
+ Action handler;
+ if (_propertyHandlers.TryGetValue(e.PropertyName, out handler))
+ {
+ handler();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Updates the attached event handlers, sets the native control.
+ /// </summary>
+ protected void SetNativeControl(EvasObject control)
+ {
+ if (NativeView != null)
+ {
+ NativeView.Moved -= OnMoved;
+ NativeView.Deleted -= NativeViewDeleted;
+ EnsureOpacityLayerIsDestroyed();
+ }
+
+ Widget widget = NativeView as Widget;
+ if (widget != null)
+ {
+ widget.Focused -= OnFocused;
+ widget.Unfocused -= OnUnfocused;
+ }
+
+ SetNativeView(control);
+
+ if (NativeView != null)
+ {
+ NativeView.Deleted += NativeViewDeleted;
+ NativeView.Moved += OnMoved;
+ }
+
+ widget = NativeView as Widget;
+ if (widget != null)
+ {
+ widget.Focused += OnFocused;
+ widget.Unfocused += OnUnfocused;
+ }
+ }
+
+ void NativeViewDeleted(object sender, EventArgs e)
+ {
+ Dispose();
+ }
+
+ void OnBatchCommitted(object sender, EventArg<VisualElement> e)
+ {
+ if (_flags.HasFlag(VisualElementRendererFlags.NeedsLayout))
+ {
+ if (!Settings.IgnoreBatchCommitted)
+ {
+ UpdateLayout();
+ // UpdateLayout already updates transformation, clear NeedsTranformation flag then
+ _flags &= ~VisualElementRendererFlags.NeedsTransformation;
+ }
+ _flags ^= VisualElementRendererFlags.NeedsLayout;
+ }
+ if (_flags.HasFlag(VisualElementRendererFlags.NeedsTransformation))
+ {
+ ApplyTransformation();
+ _flags ^= VisualElementRendererFlags.NeedsTransformation;
+ }
+
+ foreach (string property in _batchedProperties)
+ {
+ OnElementPropertyChanged(this, new PropertyChangedEventArgs(property));
+ }
+ _batchedProperties.Clear();
+ }
+
+ /// <summary>
+ /// Registers a handler which is executed when specified property changes.
+ /// </summary>
+ /// <param name="property">Handled property.</param>
+ /// <param name="handler">Action to be executed when property changes.</param>
+ protected void RegisterPropertyHandler(BindableProperty property, Action<bool> handler)
+ {
+ RegisterPropertyHandler(property.PropertyName, handler);
+ }
+
+ /// <summary>
+ /// Registers a handler which is executed when specified property changes.
+ /// </summary>
+ /// <param name="name">Name of the handled property.</param>
+ /// <param name="handler">Action to be executed when property changes.</param>
+ protected void RegisterPropertyHandler(string name, Action<bool> handler)
+ {
+ _propertyHandlersWithInit.Add(name, handler);
+ }
+
+ /// <summary>
+ /// Registers a handler which is executed when specified property changes.
+ /// </summary>
+ /// <param name="property">Handled property.</param>
+ /// <param name="handler">Action to be executed when property changes.</param>
+ protected void RegisterPropertyHandler(BindableProperty property, Action handler)
+ {
+ RegisterPropertyHandler(property.PropertyName, handler);
+ }
+
+ /// <summary>
+ /// Registers a handler which is executed when specified property changes.
+ /// </summary>
+ /// <param name="name">Name of the handled property.</param>
+ /// <param name="handler">Action to be executed when property changes.</param>
+ protected void RegisterPropertyHandler(string name, Action handler)
+ {
+ _propertyHandlers.Add(name, handler);
+ }
+
+ /// <summary>
+ /// Updates all registered properties.
+ /// </summary>
+ /// <param name="initialization">If set to <c>true</c> the method is called for an uninitialized object.</param>
+ protected void UpdateAllProperties(bool initialization)
+ {
+ foreach (KeyValuePair<string, Action<bool>> kvp in _propertyHandlersWithInit)
+ {
+ kvp.Value(initialization);
+ }
+
+ foreach (KeyValuePair<string, Action> kvp in _propertyHandlers)
+ {
+ kvp.Value();
+ }
+ }
+
+ /// <summary>
+ /// Called when Element has been set and its native counterpart
+ /// is properly initialized.
+ /// </summary>
+ protected virtual void OnElementReady()
+ {
+ }
+
+ protected void DoLayout(Native.LayoutEventArgs e)
+ {
+ Settings.StartIgnoringBatchCommitted();
+ Element.Layout(new Rectangle(Element.X, Element.Y, e.Width, e.Height));
+ if (e.HasChanged)
+ {
+ UpdateLayout();
+ }
+ Settings.StopIgnoringBatchCommitted();
+ }
+
+ protected virtual Size MinimumSize()
+ {
+ return new Size();
+ }
+
+ /// <summary>
+ /// Calculates how much space this element should take, given how much room there is.
+ /// </summary>
+ /// <returns>a desired dimensions of the element</returns>
+ protected virtual ESize Measure(int availableWidth, int availableHeight)
+ {
+ return new ESize(NativeView.MinimumWidth, NativeView.MinimumHeight);
+ }
+
+ protected virtual void UpdateBackgroundColor()
+ {
+ if (NativeView is Widget)
+ {
+ (NativeView as Widget).BackgroundColor = Element.BackgroundColor.ToNative();
+ }
+ else
+ {
+ Log.Warn("{0} uses {1} which does not support background color", this, NativeView);
+ }
+ }
+
+ /// <summary>
+ /// Converts provided value to native dimension.
+ /// </summary>
+ /// <param name="v">value to be converted.</param>
+ /// <returns>converted value</returns>
+ protected static int ToNativeDimension(double v)
+ {
+ return (int)Math.Round(v);
+ }
+
+ static double ComputeAbsoluteX(VisualElement e)
+ {
+ return e.X + (e.RealParent is VisualElement ? Platform.GetRenderer(e.RealParent).NativeView.Geometry.X : 0.0);
+ }
+
+ static double ComputeAbsoluteY(VisualElement e)
+ {
+ return e.Y + (e.RealParent is VisualElement ? Platform.GetRenderer(e.RealParent).NativeView.Geometry.Y : 0.0);
+ }
+
+ /// <summary>
+ /// Handles focus events.
+ /// </summary>
+ void OnFocused(object sender, EventArgs e)
+ {
+ if (null != Element)
+ {
+ Element.SetValue(VisualElement.IsFocusedPropertyKey, true);
+ }
+ }
+
+ /// <summary>
+ /// Handles unfocus events.
+ /// </summary>
+ void OnUnfocused(object sender, EventArgs e)
+ {
+ if (null != Element)
+ {
+ Element.SetValue(VisualElement.IsFocusedPropertyKey, false);
+ }
+ }
+
+ /// <summary>
+ /// Sets the native control, updates the control's properties.
+ /// </summary>
+ void SetNativeView(EvasObject control)
+ {
+ _view = control;
+ }
+
+ /// <summary>
+ /// Adds a new child if it's derived from the VisualElement class. Otherwise this method does nothing.
+ /// </summary>
+ /// <param name="child">Child to be added.</param>
+ void AddChild(Element child)
+ {
+ VisualElement vElement = child as VisualElement;
+ if (vElement != null)
+ {
+ var childRenderer = Platform.GetOrCreateRenderer(vElement);
+
+ // if the native view can have children, attach the new child
+ if (NativeView is Native.IContainable<EvasObject>)
+ {
+ (NativeView as Native.IContainable<EvasObject>).Children.Add(childRenderer.NativeView);
+ }
+ }
+ }
+
+ void RemoveChild(VisualElement view)
+ {
+ var renderer = Platform.GetRenderer(view);
+ var containerObject = NativeView as Native.IContainable<EvasObject>;
+ if (containerObject != null)
+ {
+ containerObject.Children.Remove(renderer.NativeView);
+ }
+
+ renderer.Dispose();
+ }
+
+ void OnChildAdded(object sender, ElementEventArgs e)
+ {
+ var view = e.Element as VisualElement;
+ if (view != null)
+ {
+ AddChild(view);
+ }
+
+ // changing the order makes sense only in case of Layouts
+ if (Element is Layout)
+ {
+ IElementController controller = Element as IElementController;
+ if (controller.LogicalChildren[controller.LogicalChildren.Count - 1] != view)
+ {
+ EnsureChildOrder();
+ }
+ }
+ }
+
+ void OnChildRemoved(object sender, ElementEventArgs e)
+ {
+ var view = e.Element as VisualElement;
+ if (view != null)
+ {
+ RemoveChild(view);
+ }
+ }
+
+ void OnChildrenReordered(object sender, EventArgs e)
+ {
+ EnsureChildOrder();
+ Layout layout = Element as Layout;
+ if (layout != null)
+ {
+ layout.InvalidateMeasureInternal(Internals.InvalidationTrigger.MeasureChanged);
+ layout.ForceLayout();
+ }
+ }
+
+ void OnFocusChangeRequested(object sender, VisualElement.FocusRequestArgs e)
+ {
+ Widget widget = NativeView as Widget;
+ if (widget == null)
+ {
+ Log.Warn("{0} is not a widget, it cannot receive focus", NativeView);
+ return;
+ }
+
+ widget.SetFocus(e.Focus);
+ e.Result = true;
+ }
+
+ /// <summary>
+ /// On register the effect
+ /// </summary>
+ /// <param name="effect">The effect to register.</param>
+ void OnRegisterEffect(PlatformEffect effect)
+ {
+ effect.Container = Element.Parent == null ? null : Platform.GetRenderer(Element.Parent).NativeView;
+ effect.Control = NativeView;
+ }
+
+ void OnMoved(object sender, EventArgs e)
+ {
+ UpdateOpacityLayer();
+ ApplyTransformation();
+ _gestureHandler?.UpdateHitBox();
+ }
+
+ void EnsureChildOrder()
+ {
+ var logicalChildren = (Element as IElementController).LogicalChildren;
+ for (var i = logicalChildren.Count - 1; i >= 0; --i)
+ {
+ var element = logicalChildren[i] as VisualElement;
+ if (element != null)
+ {
+ Platform.GetRenderer(element).NativeView?.Lower();
+ }
+ }
+ }
+
+ void UpdateIsVisible()
+ {
+ if (null != NativeView)
+ {
+ if (Element.IsVisible)
+ {
+ NativeView.Show();
+ }
+ else
+ {
+ NativeView.Hide();
+ }
+ }
+ }
+
+ void UpdateOpacity()
+ {
+ if (null != NativeView)
+ {
+ if (Element.Opacity < 1.0)
+ {
+ EnsureOpacityLayerExists();
+
+ var alpha = (int)(Element.Opacity * 255.0);
+ _opacityLayer.Color = new EColor(255, 255, 255, alpha);
+ }
+ else
+ {
+ EnsureOpacityLayerIsDestroyed();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Updates the IsEnabled property.
+ /// </summary>
+ void UpdateIsEnabled()
+ {
+ Widget widget = NativeView as Widget;
+ if (widget != null)
+ {
+ widget.IsEnabled = Element.IsEnabled;
+ }
+ }
+
+ /// <summary>
+ /// Updates the InputTransparent property.
+ /// </summary>
+ void UpdateInputTransparent()
+ {
+ NativeView.PassEvents = Element.InputTransparent;
+ }
+
+ void ApplyRotation(EvasMap map, ERect geometry, ref bool changed)
+ {
+ var rotationX = Element.RotationX;
+ var rotationY = Element.RotationY;
+ var rotationZ = Element.Rotation;
+ var anchorX = Element.AnchorX;
+ var anchorY = Element.AnchorY;
+
+ // apply rotations
+ if (rotationX != 0 || rotationY != 0 || rotationZ != 0)
+ {
+ map.Rotate3D(rotationX, rotationY, rotationZ, (int)(geometry.X + geometry.Width * anchorX),
+ (int)(geometry.Y + geometry.Height * anchorY), 0);
+ changed = true;
+ }
+ }
+
+ void ApplyScale(EvasMap map, ERect geometry, ref bool changed)
+ {
+ var scale = Element.Scale;
+
+ // apply scale factor
+ if (scale != 1.0)
+ {
+ map.Zoom(scale, scale,
+ geometry.X + (int)(geometry.Width * Element.AnchorX),
+ geometry.Y + (int)(geometry.Height * Element.AnchorY));
+ changed = true;
+ }
+ }
+
+ void ApplyTranslation(EvasMap map, ERect geometry, ref bool changed)
+ {
+ var shiftX = ToNativeDimension(Element.TranslationX);
+ var shiftY = ToNativeDimension(Element.TranslationY);
+
+ // apply translation, i.e. move/shift the object a little
+ if (shiftX != 0 || shiftY != 0)
+ {
+ if (changed)
+ {
+ // special care is taken to apply the translation last
+ Point3D p;
+ for (int i = 0; i < 4; i++)
+ {
+ p = map.GetPointCoordinate(i);
+ p.X += shiftX;
+ p.Y += shiftY;
+ map.SetPointCoordinate(i, p);
+ }
+ }
+ else
+ {
+ // in case when we only need translation, then construct the map in a simpler way
+ geometry.X += shiftX;
+ geometry.Y += shiftY;
+ map.PopulatePoints(geometry, 0);
+
+ changed = true;
+ }
+ }
+ }
+
+ public void ApplyTransformation()
+ {
+ if (null == NativeView)
+ {
+ Log.Error("Trying to apply transformation to the non-existent native control");
+ return;
+ }
+
+ // prepare the EFL effect structure
+ ERect geometry = NativeView.Geometry;
+ EvasMap map = new EvasMap(4);
+ map.PopulatePoints(geometry, 0);
+
+ bool changed = false;
+ ApplyRotation(map, geometry, ref changed);
+ ApplyScale(map, geometry, ref changed);
+ ApplyTranslation(map, geometry, ref changed);
+
+ NativeView.IsMapEnabled = changed;
+
+ if (changed)
+ {
+ NativeView.EvasMap = map;
+
+ if (_opacityLayer != null)
+ {
+ _opacityLayer.IsMapEnabled = true;
+ _opacityLayer.EvasMap = map;
+ }
+ }
+ _gestureHandler?.UpdateHitBox();
+ }
+
+ protected virtual void UpdateOpacityLayer()
+ {
+ if (_opacityLayer != null)
+ {
+ _opacityLayer.Geometry = NativeView.Geometry;
+ NativeView.SetClip(_opacityLayer);
+ }
+ }
+
+ void EnsureOpacityLayerExists()
+ {
+ if (_opacityLayer == null)
+ {
+ _opacityLayer = new ERectangle(NativeView);
+ UpdateOpacityLayer();
+ _opacityLayer.Show();
+ }
+ }
+
+ void EnsureOpacityLayerIsDestroyed()
+ {
+ if (_opacityLayer != null)
+ {
+ NativeView.SetClip(null);
+ _opacityLayer.Unrealize();
+ _opacityLayer = null;
+ }
+ }
+ }
+
+ internal static class Settings
+ {
+ static int s_ignoreCount = 0;
+
+ public static bool IgnoreBatchCommitted
+ {
+ get
+ {
+ return s_ignoreCount != 0;
+ }
+ }
+
+ public static void StartIgnoringBatchCommitted()
+ {
+ ++s_ignoreCount;
+ }
+
+ public static void StopIgnoringBatchCommitted()
+ {
+ Debug.Assert(s_ignoreCount > 0);
+ --s_ignoreCount;
+ }
+ }
+}