зеркало из https://github.com/DeGsoft/maui-linux.git
1058 строки
33 KiB
C#
1058 строки
33 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Runtime.CompilerServices;
|
|
using Xamarin.Forms.Internals;
|
|
|
|
namespace Xamarin.Forms
|
|
{
|
|
public partial class VisualElement : NavigableElement, IAnimatable, IVisualElementController, IResourcesProvider, IStyleElement, IFlowDirectionController, IPropertyPropagationController, IVisualController
|
|
{
|
|
public new static readonly BindableProperty NavigationProperty = NavigableElement.NavigationProperty;
|
|
|
|
public new static readonly BindableProperty StyleProperty = NavigableElement.StyleProperty;
|
|
|
|
public static readonly BindableProperty InputTransparentProperty = BindableProperty.Create("InputTransparent", typeof(bool), typeof(VisualElement), default(bool));
|
|
|
|
public static readonly BindableProperty IsEnabledProperty = BindableProperty.Create("IsEnabled", typeof(bool),
|
|
typeof(VisualElement), true, propertyChanged: OnIsEnabledPropertyChanged);
|
|
|
|
static readonly BindablePropertyKey XPropertyKey = BindableProperty.CreateReadOnly("X", typeof(double), typeof(VisualElement), default(double));
|
|
|
|
public static readonly BindableProperty XProperty = XPropertyKey.BindableProperty;
|
|
|
|
static readonly BindablePropertyKey YPropertyKey = BindableProperty.CreateReadOnly("Y", typeof(double), typeof(VisualElement), default(double));
|
|
|
|
public static readonly BindableProperty YProperty = YPropertyKey.BindableProperty;
|
|
|
|
public static readonly BindableProperty AnchorXProperty = BindableProperty.Create("AnchorX", typeof(double), typeof(VisualElement), .5d);
|
|
|
|
public static readonly BindableProperty AnchorYProperty = BindableProperty.Create("AnchorY", typeof(double), typeof(VisualElement), .5d);
|
|
|
|
public static readonly BindableProperty TranslationXProperty = BindableProperty.Create("TranslationX", typeof(double), typeof(VisualElement), 0d);
|
|
|
|
public static readonly BindableProperty TranslationYProperty = BindableProperty.Create("TranslationY", typeof(double), typeof(VisualElement), 0d);
|
|
|
|
static readonly BindablePropertyKey WidthPropertyKey = BindableProperty.CreateReadOnly("Width", typeof(double), typeof(VisualElement), -1d,
|
|
coerceValue: (bindable, value) => double.IsNaN((double)value) ? 0d : value);
|
|
|
|
public static readonly BindableProperty WidthProperty = WidthPropertyKey.BindableProperty;
|
|
|
|
static readonly BindablePropertyKey HeightPropertyKey = BindableProperty.CreateReadOnly("Height", typeof(double), typeof(VisualElement), -1d,
|
|
coerceValue: (bindable, value) => double.IsNaN((double)value) ? 0d : value);
|
|
|
|
public static readonly BindableProperty HeightProperty = HeightPropertyKey.BindableProperty;
|
|
|
|
public static readonly BindableProperty RotationProperty = BindableProperty.Create("Rotation", typeof(double), typeof(VisualElement), default(double));
|
|
|
|
public static readonly BindableProperty RotationXProperty = BindableProperty.Create("RotationX", typeof(double), typeof(VisualElement), default(double));
|
|
|
|
public static readonly BindableProperty RotationYProperty = BindableProperty.Create("RotationY", typeof(double), typeof(VisualElement), default(double));
|
|
|
|
public static readonly BindableProperty ScaleProperty = BindableProperty.Create(nameof(Scale), typeof(double), typeof(VisualElement), 1d);
|
|
|
|
public static readonly BindableProperty ScaleXProperty = BindableProperty.Create(nameof(ScaleX), typeof(double), typeof(VisualElement), 1d);
|
|
|
|
public static readonly BindableProperty ScaleYProperty = BindableProperty.Create(nameof(ScaleY), typeof(double), typeof(VisualElement), 1d);
|
|
|
|
internal static readonly BindableProperty TransformProperty = BindableProperty.Create("Transform", typeof(string), typeof(VisualElement), null, propertyChanged: OnTransformChanged);
|
|
|
|
public static readonly BindableProperty VisualProperty =
|
|
BindableProperty.Create(nameof(Visual), typeof(IVisual), typeof(VisualElement), Forms.VisualMarker.MatchParent,
|
|
validateValue: (b, v) => v != null, propertyChanged: OnVisualChanged);
|
|
|
|
static IVisual _defaultVisual = Xamarin.Forms.VisualMarker.Default;
|
|
IVisual _effectiveVisual = _defaultVisual;
|
|
|
|
public IVisual Visual
|
|
{
|
|
get { return (IVisual)GetValue(VisualProperty); }
|
|
set { SetValue(VisualProperty, value); }
|
|
}
|
|
|
|
internal static void SetDefaultVisual(IVisual visual)
|
|
{
|
|
_defaultVisual = visual;
|
|
}
|
|
|
|
IVisual IVisualController.EffectiveVisual
|
|
{
|
|
get { return _effectiveVisual; }
|
|
set
|
|
{
|
|
if (value == _effectiveVisual)
|
|
return;
|
|
|
|
_effectiveVisual = value;
|
|
OnPropertyChanged(VisualProperty.PropertyName);
|
|
}
|
|
}
|
|
|
|
static void OnTransformChanged(BindableObject bindable, object oldValue, object newValue)
|
|
{
|
|
if ((string)newValue == "none") {
|
|
bindable.ClearValue(TranslationXProperty);
|
|
bindable.ClearValue(TranslationYProperty);
|
|
bindable.ClearValue(RotationProperty);
|
|
bindable.ClearValue(RotationXProperty);
|
|
bindable.ClearValue(RotationYProperty);
|
|
bindable.ClearValue(ScaleProperty);
|
|
bindable.ClearValue(ScaleXProperty);
|
|
bindable.ClearValue(ScaleYProperty);
|
|
return;
|
|
}
|
|
var transforms = ((string)newValue).Split(' ');
|
|
foreach (var transform in transforms) {
|
|
if (string.IsNullOrEmpty(transform) || transform.IndexOf('(') < 0 || transform.IndexOf(')') < 0)
|
|
throw new FormatException("Format for transform is 'none | transform(value) [transform(value) ]*'");
|
|
var transformName = transform.Substring(0, transform.IndexOf('('));
|
|
var value = transform.Substring(transform.IndexOf('(') + 1, transform.IndexOf(')') - transform.IndexOf('(') - 1);
|
|
double translationX, translationY, scaleX, scaleY, rotateX, rotateY, rotate;
|
|
if (transformName.StartsWith("translateX", StringComparison.OrdinalIgnoreCase) && double.TryParse(value, out translationX))
|
|
bindable.SetValue(TranslationXProperty, translationX);
|
|
else if (transformName.StartsWith("translateY", StringComparison.OrdinalIgnoreCase) && double.TryParse(value, out translationY))
|
|
bindable.SetValue(TranslationYProperty, translationY);
|
|
else if (transformName.StartsWith("translate", StringComparison.OrdinalIgnoreCase)) {
|
|
var translate = value.Split(',');
|
|
if (double.TryParse(translate[0], out translationX) && double.TryParse(translate[1], out translationY)) {
|
|
bindable.SetValue(TranslationXProperty, translationX);
|
|
bindable.SetValue(TranslationYProperty, translationY);
|
|
}
|
|
}
|
|
else if (transformName.StartsWith("scaleX", StringComparison.OrdinalIgnoreCase) && double.TryParse(value, out scaleX))
|
|
bindable.SetValue(ScaleXProperty, scaleX);
|
|
else if (transformName.StartsWith("scaleY", StringComparison.OrdinalIgnoreCase) && double.TryParse(value, out scaleY))
|
|
bindable.SetValue(ScaleYProperty, scaleY);
|
|
else if (transformName.StartsWith("scale", StringComparison.OrdinalIgnoreCase)) {
|
|
var scale = value.Split(',');
|
|
if (double.TryParse(scale[0], out scaleX) && double.TryParse(scale[1], out scaleY)) {
|
|
bindable.SetValue(ScaleXProperty, scaleX);
|
|
bindable.SetValue(ScaleYProperty, scaleY);
|
|
}
|
|
}
|
|
else if (transformName.StartsWith("rotateX", StringComparison.OrdinalIgnoreCase) && double.TryParse(value, out rotateX))
|
|
bindable.SetValue(RotationXProperty, rotateX);
|
|
else if (transformName.StartsWith("rotateY", StringComparison.OrdinalIgnoreCase) && double.TryParse(value, out rotateY))
|
|
bindable.SetValue(RotationYProperty, rotateY);
|
|
else if (transformName.StartsWith("rotate", StringComparison.OrdinalIgnoreCase) && double.TryParse(value, out rotate))
|
|
bindable.SetValue(RotationProperty, rotate);
|
|
else
|
|
throw new FormatException("Invalid transform name");
|
|
}
|
|
}
|
|
|
|
internal static readonly BindableProperty TransformOriginProperty =
|
|
BindableProperty.Create("TransformOrigin", typeof(Point), typeof(VisualElement), new Point(.5d, .5d),
|
|
propertyChanged: (b, o, n) => { (((VisualElement)b).AnchorX, ((VisualElement)b).AnchorY) = (Point)n; });
|
|
|
|
public static readonly BindableProperty IsVisibleProperty = BindableProperty.Create("IsVisible", typeof(bool), typeof(VisualElement), true,
|
|
propertyChanged: (bindable, oldvalue, newvalue) => ((VisualElement)bindable).OnIsVisibleChanged((bool)oldvalue, (bool)newvalue));
|
|
|
|
public static readonly BindableProperty OpacityProperty = BindableProperty.Create("Opacity", typeof(double), typeof(VisualElement), 1d, coerceValue: (bindable, value) => ((double)value).Clamp(0, 1));
|
|
|
|
public static readonly BindableProperty BackgroundColorProperty = BindableProperty.Create("BackgroundColor", typeof(Color), typeof(VisualElement), Color.Default);
|
|
|
|
internal static readonly BindablePropertyKey BehaviorsPropertyKey = BindableProperty.CreateReadOnly("Behaviors", typeof(IList<Behavior>), typeof(VisualElement), default(IList<Behavior>),
|
|
defaultValueCreator: bindable =>
|
|
{
|
|
var collection = new AttachedCollection<Behavior>();
|
|
collection.AttachTo(bindable);
|
|
return collection;
|
|
});
|
|
|
|
public static readonly BindableProperty BehaviorsProperty = BehaviorsPropertyKey.BindableProperty;
|
|
|
|
internal static readonly BindablePropertyKey TriggersPropertyKey = BindableProperty.CreateReadOnly("Triggers", typeof(IList<TriggerBase>), typeof(VisualElement), default(IList<TriggerBase>),
|
|
defaultValueCreator: bindable =>
|
|
{
|
|
var collection = new AttachedCollection<TriggerBase>();
|
|
collection.AttachTo(bindable);
|
|
return collection;
|
|
});
|
|
|
|
public static readonly BindableProperty TriggersProperty = TriggersPropertyKey.BindableProperty;
|
|
|
|
|
|
public static readonly BindableProperty WidthRequestProperty = BindableProperty.Create("WidthRequest", typeof(double), typeof(VisualElement), -1d, propertyChanged: OnRequestChanged);
|
|
|
|
public static readonly BindableProperty HeightRequestProperty = BindableProperty.Create("HeightRequest", typeof(double), typeof(VisualElement), -1d, propertyChanged: OnRequestChanged);
|
|
|
|
public static readonly BindableProperty MinimumWidthRequestProperty = BindableProperty.Create("MinimumWidthRequest", typeof(double), typeof(VisualElement), -1d, propertyChanged: OnRequestChanged);
|
|
|
|
public static readonly BindableProperty MinimumHeightRequestProperty = BindableProperty.Create("MinimumHeightRequest", typeof(double), typeof(VisualElement), -1d, propertyChanged: OnRequestChanged);
|
|
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public static readonly BindablePropertyKey IsFocusedPropertyKey = BindableProperty.CreateReadOnly("IsFocused",
|
|
typeof(bool), typeof(VisualElement), default(bool), propertyChanged: OnIsFocusedPropertyChanged);
|
|
|
|
public static readonly BindableProperty IsFocusedProperty = IsFocusedPropertyKey.BindableProperty;
|
|
|
|
public static readonly BindableProperty FlowDirectionProperty = BindableProperty.Create(nameof(FlowDirection), typeof(FlowDirection), typeof(VisualElement), FlowDirection.MatchParent, propertyChanged: FlowDirectionChanged);
|
|
|
|
public static readonly BindableProperty TabIndexProperty =
|
|
BindableProperty.Create(nameof(TabIndex),
|
|
typeof(int),
|
|
typeof(VisualElement),
|
|
defaultValue: 0,
|
|
propertyChanged: OnTabIndexPropertyChanged,
|
|
defaultValueCreator: TabIndexDefaultValueCreator);
|
|
|
|
public static readonly BindableProperty IsTabStopProperty =
|
|
BindableProperty.Create(nameof(IsTabStop),
|
|
typeof(bool),
|
|
typeof(VisualElement),
|
|
defaultValue: true,
|
|
propertyChanged: OnTabStopPropertyChanged,
|
|
defaultValueCreator: TabStopDefaultValueCreator);
|
|
|
|
static void OnTabIndexPropertyChanged(BindableObject bindable, object oldValue, object newValue) =>
|
|
((VisualElement)bindable).OnTabIndexPropertyChanged((int)oldValue, (int)newValue);
|
|
|
|
static object TabIndexDefaultValueCreator(BindableObject bindable) =>
|
|
((VisualElement)bindable).TabIndexDefaultValueCreator();
|
|
|
|
static void OnTabStopPropertyChanged(BindableObject bindable, object oldValue, object newValue) =>
|
|
((VisualElement)bindable).OnTabStopPropertyChanged((bool)oldValue, (bool)newValue);
|
|
|
|
static object TabStopDefaultValueCreator(BindableObject bindable) =>
|
|
((VisualElement)bindable).TabStopDefaultValueCreator();
|
|
|
|
IFlowDirectionController FlowController => this;
|
|
IPropertyPropagationController PropertyPropagationController => this;
|
|
|
|
public FlowDirection FlowDirection
|
|
{
|
|
get { return (FlowDirection)GetValue(FlowDirectionProperty); }
|
|
set { SetValue(FlowDirectionProperty, value); }
|
|
}
|
|
|
|
EffectiveFlowDirection _effectiveFlowDirection = default(EffectiveFlowDirection);
|
|
EffectiveFlowDirection IFlowDirectionController.EffectiveFlowDirection
|
|
{
|
|
get { return _effectiveFlowDirection; }
|
|
set
|
|
{
|
|
if (value == _effectiveFlowDirection)
|
|
return;
|
|
|
|
_effectiveFlowDirection = value;
|
|
InvalidateMeasureInternal(InvalidationTrigger.Undefined);
|
|
OnPropertyChanged(FlowDirectionProperty.PropertyName);
|
|
}
|
|
}
|
|
|
|
EffectiveFlowDirection IVisualElementController.EffectiveFlowDirection => FlowController.EffectiveFlowDirection;
|
|
|
|
readonly Dictionary<Size, SizeRequest> _measureCache = new Dictionary<Size, SizeRequest>();
|
|
|
|
|
|
|
|
int _batched;
|
|
LayoutConstraint _computedConstraint;
|
|
|
|
bool _isInNativeLayout;
|
|
|
|
bool _isNativeStateConsistent = true;
|
|
|
|
bool _isPlatformEnabled;
|
|
|
|
double _mockHeight = -1;
|
|
|
|
double _mockWidth = -1;
|
|
|
|
double _mockX = -1;
|
|
|
|
double _mockY = -1;
|
|
|
|
LayoutConstraint _selfConstraint;
|
|
|
|
internal VisualElement()
|
|
{
|
|
|
|
}
|
|
|
|
public double AnchorX
|
|
{
|
|
get { return (double)GetValue(AnchorXProperty); }
|
|
set { SetValue(AnchorXProperty, value); }
|
|
}
|
|
|
|
public double AnchorY
|
|
{
|
|
get { return (double)GetValue(AnchorYProperty); }
|
|
set { SetValue(AnchorYProperty, value); }
|
|
}
|
|
|
|
public Color BackgroundColor
|
|
{
|
|
get { return (Color)GetValue(BackgroundColorProperty); }
|
|
set { SetValue(BackgroundColorProperty, value); }
|
|
}
|
|
|
|
public IList<Behavior> Behaviors
|
|
{
|
|
get { return (IList<Behavior>)GetValue(BehaviorsProperty); }
|
|
}
|
|
|
|
public Rectangle Bounds
|
|
{
|
|
get { return new Rectangle(X, Y, Width, Height); }
|
|
private set
|
|
{
|
|
if (value.X == X && value.Y == Y && value.Height == Height && value.Width == Width)
|
|
return;
|
|
BatchBegin();
|
|
X = value.X;
|
|
Y = value.Y;
|
|
SetSize(value.Width, value.Height);
|
|
BatchCommit();
|
|
}
|
|
}
|
|
|
|
public double Height
|
|
{
|
|
get { return _mockHeight == -1 ? (double)GetValue(HeightProperty) : _mockHeight; }
|
|
private set { SetValue(HeightPropertyKey, value); }
|
|
}
|
|
|
|
public double HeightRequest
|
|
{
|
|
get { return (double)GetValue(HeightRequestProperty); }
|
|
set { SetValue(HeightRequestProperty, value); }
|
|
}
|
|
|
|
public bool InputTransparent
|
|
{
|
|
get { return (bool)GetValue(InputTransparentProperty); }
|
|
set { SetValue(InputTransparentProperty, value); }
|
|
}
|
|
|
|
public bool IsEnabled
|
|
{
|
|
get { return (bool)GetValue(IsEnabledProperty); }
|
|
set { SetValue(IsEnabledProperty, value); }
|
|
}
|
|
|
|
public bool IsFocused
|
|
{
|
|
get { return (bool)GetValue(IsFocusedProperty); }
|
|
}
|
|
|
|
[TypeConverter(typeof(VisibilityConverter))]
|
|
public bool IsVisible
|
|
{
|
|
get { return (bool)GetValue(IsVisibleProperty); }
|
|
set { SetValue(IsVisibleProperty, value); }
|
|
}
|
|
|
|
public double MinimumHeightRequest
|
|
{
|
|
get { return (double)GetValue(MinimumHeightRequestProperty); }
|
|
set { SetValue(MinimumHeightRequestProperty, value); }
|
|
}
|
|
|
|
public double MinimumWidthRequest
|
|
{
|
|
get { return (double)GetValue(MinimumWidthRequestProperty); }
|
|
set { SetValue(MinimumWidthRequestProperty, value); }
|
|
}
|
|
|
|
public double Opacity
|
|
{
|
|
get { return (double)GetValue(OpacityProperty); }
|
|
set { SetValue(OpacityProperty, value); }
|
|
}
|
|
|
|
public double Rotation
|
|
{
|
|
get { return (double)GetValue(RotationProperty); }
|
|
set { SetValue(RotationProperty, value); }
|
|
}
|
|
|
|
public double RotationX
|
|
{
|
|
get { return (double)GetValue(RotationXProperty); }
|
|
set { SetValue(RotationXProperty, value); }
|
|
}
|
|
|
|
public double RotationY
|
|
{
|
|
get { return (double)GetValue(RotationYProperty); }
|
|
set { SetValue(RotationYProperty, value); }
|
|
}
|
|
|
|
public double Scale
|
|
{
|
|
get => (double)GetValue(ScaleProperty);
|
|
set => SetValue(ScaleProperty, value);
|
|
}
|
|
|
|
public double ScaleX
|
|
{
|
|
get => (double)GetValue(ScaleXProperty);
|
|
set => SetValue(ScaleXProperty, value);
|
|
}
|
|
|
|
public double ScaleY
|
|
{
|
|
get => (double)GetValue(ScaleYProperty);
|
|
set => SetValue(ScaleYProperty, value);
|
|
}
|
|
|
|
public int TabIndex
|
|
{
|
|
get => (int)GetValue(TabIndexProperty);
|
|
set => SetValue(TabIndexProperty, value);
|
|
}
|
|
|
|
protected virtual void OnTabIndexPropertyChanged(int oldValue, int newValue) { }
|
|
|
|
protected virtual int TabIndexDefaultValueCreator() => 0;
|
|
|
|
public bool IsTabStop
|
|
{
|
|
get => (bool)GetValue(IsTabStopProperty);
|
|
set => SetValue(IsTabStopProperty, value);
|
|
}
|
|
|
|
protected virtual void OnTabStopPropertyChanged(bool oldValue, bool newValue) { }
|
|
|
|
protected virtual bool TabStopDefaultValueCreator() => true;
|
|
|
|
public double TranslationX
|
|
{
|
|
get { return (double)GetValue(TranslationXProperty); }
|
|
set { SetValue(TranslationXProperty, value); }
|
|
}
|
|
|
|
public double TranslationY
|
|
{
|
|
get { return (double)GetValue(TranslationYProperty); }
|
|
set { SetValue(TranslationYProperty, value); }
|
|
}
|
|
|
|
public IList<TriggerBase> Triggers
|
|
{
|
|
get { return (IList<TriggerBase>)GetValue(TriggersProperty); }
|
|
}
|
|
|
|
public double Width
|
|
{
|
|
get { return _mockWidth == -1 ? (double)GetValue(WidthProperty) : _mockWidth; }
|
|
private set { SetValue(WidthPropertyKey, value); }
|
|
}
|
|
|
|
public double WidthRequest
|
|
{
|
|
get { return (double)GetValue(WidthRequestProperty); }
|
|
set { SetValue(WidthRequestProperty, value); }
|
|
}
|
|
|
|
public double X
|
|
{
|
|
get { return _mockX == -1 ? (double)GetValue(XProperty) : _mockX; }
|
|
private set { SetValue(XPropertyKey, value); }
|
|
}
|
|
|
|
public double Y
|
|
{
|
|
get { return _mockY == -1 ? (double)GetValue(YProperty) : _mockY; }
|
|
private set { SetValue(YPropertyKey, value); }
|
|
}
|
|
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public bool Batched
|
|
{
|
|
get { return _batched > 0; }
|
|
}
|
|
|
|
internal LayoutConstraint ComputedConstraint
|
|
{
|
|
get { return _computedConstraint; }
|
|
set
|
|
{
|
|
if (_computedConstraint == value)
|
|
return;
|
|
|
|
LayoutConstraint oldConstraint = Constraint;
|
|
_computedConstraint = value;
|
|
LayoutConstraint newConstraint = Constraint;
|
|
if (oldConstraint != newConstraint)
|
|
OnConstraintChanged(oldConstraint, newConstraint);
|
|
}
|
|
}
|
|
|
|
internal LayoutConstraint Constraint
|
|
{
|
|
get { return ComputedConstraint | SelfConstraint; }
|
|
}
|
|
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public bool DisableLayout { get; set; }
|
|
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public bool IsInNativeLayout
|
|
{
|
|
get
|
|
{
|
|
if (_isInNativeLayout)
|
|
return true;
|
|
|
|
Element parent = RealParent;
|
|
if (parent != null)
|
|
{
|
|
var visualElement = parent as VisualElement;
|
|
if (visualElement != null && visualElement.IsInNativeLayout)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
set { _isInNativeLayout = value; }
|
|
}
|
|
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public bool IsNativeStateConsistent
|
|
{
|
|
get { return _isNativeStateConsistent; }
|
|
set
|
|
{
|
|
if (_isNativeStateConsistent == value)
|
|
return;
|
|
_isNativeStateConsistent = value;
|
|
if (value && IsPlatformEnabled)
|
|
InvalidateMeasureInternal(InvalidationTrigger.RendererReady);
|
|
}
|
|
}
|
|
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public bool IsPlatformEnabled
|
|
{
|
|
get { return _isPlatformEnabled; }
|
|
set
|
|
{
|
|
if (value == _isPlatformEnabled)
|
|
return;
|
|
|
|
_isPlatformEnabled = value;
|
|
if (value && IsNativeStateConsistent)
|
|
InvalidateMeasureInternal(InvalidationTrigger.RendererReady);
|
|
|
|
OnIsPlatformEnabledChanged();
|
|
}
|
|
}
|
|
|
|
internal LayoutConstraint SelfConstraint
|
|
{
|
|
get { return _selfConstraint; }
|
|
set
|
|
{
|
|
if (_selfConstraint == value)
|
|
return;
|
|
|
|
LayoutConstraint oldConstraint = Constraint;
|
|
_selfConstraint = value;
|
|
LayoutConstraint newConstraint = Constraint;
|
|
if (oldConstraint != newConstraint)
|
|
{
|
|
OnConstraintChanged(oldConstraint, newConstraint);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void BatchBegin()
|
|
{
|
|
_batched++;
|
|
}
|
|
|
|
public void BatchCommit()
|
|
{
|
|
_batched = Math.Max(0, _batched - 1);
|
|
if (!Batched)
|
|
BatchCommitted?.Invoke(this, new EventArg<VisualElement>(this));
|
|
}
|
|
|
|
ResourceDictionary _resources;
|
|
bool IResourcesProvider.IsResourcesCreated => _resources != null;
|
|
|
|
public ResourceDictionary Resources
|
|
{
|
|
get
|
|
{
|
|
if (_resources != null)
|
|
return _resources;
|
|
_resources = new ResourceDictionary();
|
|
((IResourceDictionary)_resources).ValuesChanged += OnResourcesChanged;
|
|
return _resources;
|
|
}
|
|
set
|
|
{
|
|
if (_resources == value)
|
|
return;
|
|
OnPropertyChanging();
|
|
if (_resources != null)
|
|
((IResourceDictionary)_resources).ValuesChanged -= OnResourcesChanged;
|
|
_resources = value;
|
|
OnResourcesChanged(value);
|
|
if (_resources != null)
|
|
((IResourceDictionary)_resources).ValuesChanged += OnResourcesChanged;
|
|
OnPropertyChanged();
|
|
}
|
|
}
|
|
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public void NativeSizeChanged()
|
|
{
|
|
InvalidateMeasureInternal(InvalidationTrigger.MeasureChanged);
|
|
}
|
|
|
|
public event EventHandler ChildrenReordered;
|
|
|
|
public bool Focus()
|
|
{
|
|
if (IsFocused)
|
|
return true;
|
|
|
|
if (FocusChangeRequested == null)
|
|
return false;
|
|
|
|
var arg = new FocusRequestArgs { Focus = true };
|
|
FocusChangeRequested(this, arg);
|
|
return arg.Result;
|
|
}
|
|
|
|
public event EventHandler<FocusEventArgs> Focused;
|
|
|
|
[Obsolete("OnSizeRequest is obsolete as of version 2.2.0. Please use OnMeasure instead.")]
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public virtual SizeRequest GetSizeRequest(double widthConstraint, double heightConstraint)
|
|
{
|
|
SizeRequest cachedResult;
|
|
var constraintSize = new Size(widthConstraint, heightConstraint);
|
|
if (_measureCache.TryGetValue(constraintSize, out cachedResult))
|
|
{
|
|
return cachedResult;
|
|
}
|
|
|
|
double widthRequest = WidthRequest;
|
|
double heightRequest = HeightRequest;
|
|
if (widthRequest >= 0)
|
|
widthConstraint = Math.Min(widthConstraint, widthRequest);
|
|
if (heightRequest >= 0)
|
|
heightConstraint = Math.Min(heightConstraint, heightRequest);
|
|
|
|
SizeRequest result = OnMeasure(widthConstraint, heightConstraint);
|
|
bool hasMinimum = result.Minimum != result.Request;
|
|
Size request = result.Request;
|
|
Size minimum = result.Minimum;
|
|
|
|
if (heightRequest != -1)
|
|
{
|
|
request.Height = heightRequest;
|
|
if (!hasMinimum)
|
|
minimum.Height = heightRequest;
|
|
}
|
|
|
|
if (widthRequest != -1)
|
|
{
|
|
request.Width = widthRequest;
|
|
if (!hasMinimum)
|
|
minimum.Width = widthRequest;
|
|
}
|
|
|
|
double minimumHeightRequest = MinimumHeightRequest;
|
|
double minimumWidthRequest = MinimumWidthRequest;
|
|
|
|
if (minimumHeightRequest != -1)
|
|
minimum.Height = minimumHeightRequest;
|
|
if (minimumWidthRequest != -1)
|
|
minimum.Width = minimumWidthRequest;
|
|
|
|
minimum.Height = Math.Min(request.Height, minimum.Height);
|
|
minimum.Width = Math.Min(request.Width, minimum.Width);
|
|
|
|
var r = new SizeRequest(request, minimum);
|
|
|
|
if (r.Request.Width > 0 && r.Request.Height > 0)
|
|
{
|
|
_measureCache[constraintSize] = r;
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
public void Layout(Rectangle bounds)
|
|
{
|
|
Bounds = bounds;
|
|
}
|
|
|
|
public SizeRequest Measure(double widthConstraint, double heightConstraint, MeasureFlags flags = MeasureFlags.None)
|
|
{
|
|
bool includeMargins = (flags & MeasureFlags.IncludeMargins) != 0;
|
|
Thickness margin = default(Thickness);
|
|
if (includeMargins)
|
|
{
|
|
var view = this as View;
|
|
if (view != null)
|
|
margin = view.Margin;
|
|
widthConstraint = Math.Max(0, widthConstraint - margin.HorizontalThickness);
|
|
heightConstraint = Math.Max(0, heightConstraint - margin.VerticalThickness);
|
|
}
|
|
#pragma warning disable 0618 // retain until GetSizeRequest removed
|
|
SizeRequest result = GetSizeRequest(widthConstraint, heightConstraint);
|
|
#pragma warning restore 0618
|
|
|
|
if (includeMargins)
|
|
{
|
|
if (!margin.IsEmpty)
|
|
{
|
|
result.Minimum = new Size(result.Minimum.Width + margin.HorizontalThickness, result.Minimum.Height + margin.VerticalThickness);
|
|
result.Request = new Size(result.Request.Width + margin.HorizontalThickness, result.Request.Height + margin.VerticalThickness);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public event EventHandler MeasureInvalidated;
|
|
|
|
public event EventHandler SizeChanged;
|
|
|
|
public void Unfocus()
|
|
{
|
|
if (!IsFocused)
|
|
return;
|
|
|
|
EventHandler<FocusRequestArgs> unfocus = FocusChangeRequested;
|
|
if (unfocus != null)
|
|
{
|
|
unfocus(this, new FocusRequestArgs());
|
|
}
|
|
}
|
|
|
|
public event EventHandler<FocusEventArgs> Unfocused;
|
|
|
|
protected virtual void InvalidateMeasure()
|
|
{
|
|
InvalidateMeasureInternal(InvalidationTrigger.MeasureChanged);
|
|
}
|
|
|
|
protected override void OnChildAdded(Element child)
|
|
{
|
|
base.OnChildAdded(child);
|
|
var view = child as View;
|
|
if (view != null)
|
|
ComputeConstraintForView(view);
|
|
}
|
|
|
|
protected override void OnChildRemoved(Element child)
|
|
{
|
|
base.OnChildRemoved(child);
|
|
var view = child as View;
|
|
if (view != null)
|
|
view.ComputedConstraint = LayoutConstraint.None;
|
|
}
|
|
|
|
protected void OnChildrenReordered()
|
|
=> ChildrenReordered?.Invoke(this, EventArgs.Empty);
|
|
|
|
protected virtual SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
|
|
{
|
|
#pragma warning disable 0618 // retain until OnSizeRequest removed
|
|
return OnSizeRequest(widthConstraint, heightConstraint);
|
|
#pragma warning restore 0618
|
|
}
|
|
|
|
protected virtual void OnSizeAllocated(double width, double height)
|
|
{
|
|
}
|
|
|
|
[Obsolete("OnSizeRequest is obsolete as of version 2.2.0. Please use OnMeasure instead.")]
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
protected virtual SizeRequest OnSizeRequest(double widthConstraint, double heightConstraint)
|
|
{
|
|
if (!IsPlatformEnabled)
|
|
{
|
|
return new SizeRequest(new Size(-1, -1));
|
|
}
|
|
|
|
return Device.PlatformServices.GetNativeSize(this, widthConstraint, heightConstraint);
|
|
}
|
|
|
|
protected void SizeAllocated(double width, double height)
|
|
{
|
|
OnSizeAllocated(width, height);
|
|
}
|
|
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public event EventHandler<EventArg<VisualElement>> BatchCommitted;
|
|
|
|
internal void ComputeConstrainsForChildren()
|
|
{
|
|
for (var i = 0; i < LogicalChildrenInternal.Count; i++)
|
|
{
|
|
var child = LogicalChildrenInternal[i] as View;
|
|
if (child != null)
|
|
ComputeConstraintForView(child);
|
|
}
|
|
}
|
|
|
|
internal virtual void ComputeConstraintForView(View view)
|
|
{
|
|
view.ComputedConstraint = LayoutConstraint.None;
|
|
}
|
|
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public event EventHandler<FocusRequestArgs> FocusChangeRequested;
|
|
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public void InvalidateMeasureNonVirtual(InvalidationTrigger trigger)
|
|
{
|
|
InvalidateMeasureInternal(trigger);
|
|
}
|
|
internal virtual void InvalidateMeasureInternal(InvalidationTrigger trigger)
|
|
{
|
|
_measureCache.Clear();
|
|
MeasureInvalidated?.Invoke(this, new InvalidationEventArgs(trigger));
|
|
}
|
|
|
|
void IVisualElementController.InvalidateMeasure(InvalidationTrigger trigger)
|
|
{
|
|
InvalidateMeasureInternal(trigger);
|
|
}
|
|
|
|
internal void MockBounds(Rectangle bounds)
|
|
{
|
|
#if NETSTANDARD2_0
|
|
(_mockX, _mockY, _mockWidth, _mockHeight) = bounds;
|
|
#else
|
|
_mockX = bounds.X;
|
|
_mockY = bounds.Y;
|
|
_mockWidth = bounds.Width;
|
|
_mockHeight = bounds.Height;
|
|
#endif
|
|
}
|
|
|
|
internal virtual void OnConstraintChanged(LayoutConstraint oldConstraint, LayoutConstraint newConstraint)
|
|
{
|
|
ComputeConstrainsForChildren();
|
|
}
|
|
|
|
internal virtual void OnIsPlatformEnabledChanged()
|
|
{
|
|
}
|
|
|
|
internal virtual void OnIsVisibleChanged(bool oldValue, bool newValue)
|
|
{
|
|
InvalidateMeasureInternal(InvalidationTrigger.Undefined);
|
|
}
|
|
|
|
internal override void OnResourcesChanged(object sender, ResourcesChangedEventArgs e)
|
|
{
|
|
if (e == ResourcesChangedEventArgs.StyleSheets)
|
|
ApplyStyleSheets();
|
|
else
|
|
base.OnResourcesChanged(sender, e);
|
|
}
|
|
|
|
internal override void OnParentResourcesChanged(IEnumerable<KeyValuePair<string, object>> values)
|
|
{
|
|
if (values == null)
|
|
return;
|
|
|
|
if (!((IResourcesProvider)this).IsResourcesCreated || Resources.Count == 0)
|
|
{
|
|
base.OnParentResourcesChanged(values);
|
|
return;
|
|
}
|
|
|
|
var innerKeys = new HashSet<string>();
|
|
var changedResources = new List<KeyValuePair<string, object>>();
|
|
foreach (KeyValuePair<string, object> c in Resources)
|
|
innerKeys.Add(c.Key);
|
|
foreach (KeyValuePair<string, object> value in values)
|
|
{
|
|
if (innerKeys.Add(value.Key))
|
|
changedResources.Add(value);
|
|
else if (value.Key.StartsWith(Style.StyleClassPrefix, StringComparison.Ordinal))
|
|
{
|
|
var mergedClassStyles = new List<Style>(Resources[value.Key] as List<Style>);
|
|
mergedClassStyles.AddRange(value.Value as List<Style>);
|
|
changedResources.Add(new KeyValuePair<string, object>(value.Key, mergedClassStyles));
|
|
}
|
|
}
|
|
if (changedResources.Count != 0)
|
|
OnResourcesChanged(changedResources);
|
|
}
|
|
|
|
internal void UnmockBounds()
|
|
{
|
|
_mockX = _mockY = _mockWidth = _mockHeight = -1;
|
|
}
|
|
|
|
void OnFocused()
|
|
{
|
|
EventHandler<FocusEventArgs> focus = Focused;
|
|
if (focus != null)
|
|
focus(this, new FocusEventArgs(this, true));
|
|
}
|
|
|
|
internal void ChangeVisualStateInternal() => ChangeVisualState();
|
|
|
|
|
|
protected internal virtual void ChangeVisualState()
|
|
{
|
|
if (!IsEnabled)
|
|
{
|
|
VisualStateManager.GoToState(this, VisualStateManager.CommonStates.Disabled);
|
|
}
|
|
else if (IsFocused)
|
|
{
|
|
VisualStateManager.GoToState(this, VisualStateManager.CommonStates.Focused);
|
|
}
|
|
else
|
|
{
|
|
VisualStateManager.GoToState(this, VisualStateManager.CommonStates.Normal);
|
|
}
|
|
}
|
|
|
|
static void OnVisualChanged(BindableObject bindable, object oldValue, object newValue)
|
|
{
|
|
var self = bindable as IVisualController;
|
|
var newVisual = (IVisual)newValue;
|
|
|
|
if (newVisual.IsMatchParent())
|
|
self.EffectiveVisual = Xamarin.Forms.VisualMarker.Default;
|
|
else
|
|
self.EffectiveVisual = (IVisual)newValue;
|
|
|
|
(self as IPropertyPropagationController)?.PropagatePropertyChanged(VisualElement.VisualProperty.PropertyName);
|
|
}
|
|
|
|
static void FlowDirectionChanged(BindableObject bindable, object oldValue, object newValue)
|
|
{
|
|
var self = bindable as IFlowDirectionController;
|
|
|
|
if (self.EffectiveFlowDirection.IsExplicit() && oldValue == newValue)
|
|
return;
|
|
|
|
var newFlowDirection = (FlowDirection)newValue;
|
|
|
|
self.EffectiveFlowDirection = newFlowDirection.ToEffectiveFlowDirection(isExplicit: true);
|
|
|
|
(self as IPropertyPropagationController)?.PropagatePropertyChanged(VisualElement.FlowDirectionProperty.PropertyName);
|
|
}
|
|
|
|
|
|
static void OnIsEnabledPropertyChanged(BindableObject bindable, object oldValue, object newValue)
|
|
{
|
|
var element = (VisualElement)bindable;
|
|
|
|
if (element == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var isEnabled = (bool)newValue;
|
|
|
|
element.ChangeVisualState();
|
|
}
|
|
|
|
static void OnIsFocusedPropertyChanged(BindableObject bindable, object oldvalue, object newvalue)
|
|
{
|
|
var element = (VisualElement)bindable;
|
|
|
|
if (element == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var isFocused = (bool)newvalue;
|
|
if (isFocused)
|
|
{
|
|
element.OnFocused();
|
|
}
|
|
else
|
|
{
|
|
element.OnUnfocus();
|
|
}
|
|
|
|
element.ChangeVisualState();
|
|
}
|
|
|
|
static void OnRequestChanged(BindableObject bindable, object oldvalue, object newvalue)
|
|
{
|
|
var constraint = LayoutConstraint.None;
|
|
var element = (VisualElement)bindable;
|
|
if (element.WidthRequest >= 0 && element.MinimumWidthRequest >= 0)
|
|
{
|
|
constraint |= LayoutConstraint.HorizontallyFixed;
|
|
}
|
|
if (element.HeightRequest >= 0 && element.MinimumHeightRequest >= 0)
|
|
{
|
|
constraint |= LayoutConstraint.VerticallyFixed;
|
|
}
|
|
|
|
element.SelfConstraint = constraint;
|
|
((VisualElement)bindable).InvalidateMeasureInternal(InvalidationTrigger.SizeRequestChanged);
|
|
}
|
|
|
|
void OnUnfocus()
|
|
{
|
|
EventHandler<FocusEventArgs> unFocus = Unfocused;
|
|
if (unFocus != null)
|
|
unFocus(this, new FocusEventArgs(this, false));
|
|
}
|
|
|
|
bool IFlowDirectionController.ApplyEffectiveFlowDirectionToChildContainer => true;
|
|
|
|
|
|
void IPropertyPropagationController.PropagatePropertyChanged(string propertyName)
|
|
{
|
|
PropertyPropagationExtensions.PropagatePropertyChanged(propertyName, this, LogicalChildren);
|
|
}
|
|
|
|
void SetSize(double width, double height)
|
|
{
|
|
if (Width == width && Height == height)
|
|
return;
|
|
|
|
Width = width;
|
|
Height = height;
|
|
|
|
SizeAllocated(width, height);
|
|
SizeChanged?.Invoke(this, EventArgs.Empty);
|
|
}
|
|
|
|
public class FocusRequestArgs : EventArgs
|
|
{
|
|
public bool Focus { get; set; }
|
|
|
|
public bool Result { get; set; }
|
|
}
|
|
|
|
public class VisibilityConverter : TypeConverter
|
|
{
|
|
public override object ConvertFromInvariantString(string value)
|
|
{
|
|
value = value?.Trim();
|
|
if (!string.IsNullOrEmpty(value))
|
|
{
|
|
if (value.Equals(Boolean.TrueString, StringComparison.OrdinalIgnoreCase))
|
|
return true;
|
|
if (value.Equals("visible", StringComparison.OrdinalIgnoreCase))
|
|
return true;
|
|
if (value.Equals(Boolean.FalseString, StringComparison.OrdinalIgnoreCase))
|
|
return false;
|
|
if (value.Equals("hidden", StringComparison.OrdinalIgnoreCase))
|
|
return false;
|
|
if (value.Equals("collapse", StringComparison.OrdinalIgnoreCase))
|
|
return false;
|
|
}
|
|
throw new InvalidOperationException(string.Format("Cannot convert \"{0}\" into {1}", value, typeof(bool)));
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|