Browse Source

Merge branch 'master' into Direct2D1LocatorRemoval

pull/1861/head^2^2
Benedikt Schroeder 8 years ago
committed by GitHub
parent
commit
2bbd4b3687
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      samples/ControlCatalog/Pages/CanvasPage.xaml
  2. 4
      src/Avalonia.Controls/Border.cs
  3. 2
      src/Avalonia.Controls/Button.cs
  4. 6
      src/Avalonia.Controls/ButtonSpinner.cs
  5. 4
      src/Avalonia.Controls/ContentControl.cs
  6. 2
      src/Avalonia.Controls/Decorator.cs
  7. 6
      src/Avalonia.Controls/DrawingPresenter.cs
  8. 10
      src/Avalonia.Controls/Expander.cs
  9. 3
      src/Avalonia.Controls/Image.cs
  10. 4
      src/Avalonia.Controls/Presenters/ContentPresenter.cs
  11. 2
      src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs
  12. 2
      src/Avalonia.Controls/Presenters/TextPresenter.cs
  13. 2
      src/Avalonia.Controls/Primitives/AccessText.cs
  14. 4
      src/Avalonia.Controls/Primitives/ScrollBar.cs
  15. 6
      src/Avalonia.Controls/Primitives/ToggleButton.cs
  16. 4
      src/Avalonia.Controls/Primitives/Track.cs
  17. 6
      src/Avalonia.Controls/ProgressBar.cs
  18. 5
      src/Avalonia.Controls/Shapes/Shape.cs
  19. 2
      src/Avalonia.Controls/Slider.cs
  20. 4
      src/Avalonia.Controls/StackPanel.cs
  21. 2
      src/Avalonia.Controls/TabControl.cs
  22. 9
      src/Avalonia.Controls/TextBlock.cs
  23. 2
      src/Avalonia.Controls/TopLevel.cs
  24. 2
      src/Avalonia.Controls/WrapPanel.cs
  25. 6
      src/Avalonia.Input/InputElement.cs
  26. 68
      src/Avalonia.Layout/Layoutable.cs
  27. 45
      src/Avalonia.Styling/StyledElement.cs
  28. 10
      src/Avalonia.Themes.Default/CheckBox.xaml
  29. 8
      src/Avalonia.Themes.Default/TreeView.xaml
  30. 8
      src/Avalonia.Visuals/Animation/CrossFade.cs
  31. 14
      src/Avalonia.Visuals/Animation/PageSlide.cs
  32. 11
      src/Avalonia.Visuals/Rendering/SceneGraph/IVisualNode.cs
  33. 4
      src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs
  34. 5
      src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs
  35. 36
      src/Avalonia.Visuals/Visual.cs
  36. 37
      tests/Avalonia.RenderTests/OpacityMaskTests.cs
  37. BIN
      tests/TestFiles/Direct2D1/OpacityMask/RenderTansform_Applies_To_Opacity_Mask.expected.png
  38. BIN
      tests/TestFiles/Skia/OpacityMask/RenderTansform_Applies_To_Opacity_Mask.expected.png

3
samples/ControlCatalog/Pages/CanvasPage.xaml

@ -11,7 +11,8 @@
<GradientStop Offset="1" Color="Transparent"/> <GradientStop Offset="1" Color="Transparent"/>
</LinearGradientBrush.GradientStops> </LinearGradientBrush.GradientStops>
</LinearGradientBrush> </LinearGradientBrush>
</Rectangle.OpacityMask> </Rectangle> </Rectangle.OpacityMask>
</Rectangle>
<Ellipse Fill="Green" Width="58" Height="58" Canvas.Left="88" Canvas.Top="100"/> <Ellipse Fill="Green" Width="58" Height="58" Canvas.Left="88" Canvas.Top="100"/>
<Path Fill="Orange" Data="M 0,0 c 0,0 50,0 50,-50 c 0,0 50,0 50,50 h -50 v 50 l -50,-50 Z" Canvas.Left="30" Canvas.Top="250"/> <Path Fill="Orange" Data="M 0,0 c 0,0 50,0 50,-50 c 0,0 50,0 50,50 h -50 v 50 l -50,-50 Z" Canvas.Left="30" Canvas.Top="250"/>
<Path Fill="OrangeRed" Canvas.Left="180" Canvas.Top="250"> <Path Fill="OrangeRed" Canvas.Left="180" Canvas.Top="250">

4
src/Avalonia.Controls/Border.cs

@ -43,8 +43,8 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
static Border() static Border()
{ {
AffectsRender(BackgroundProperty, BorderBrushProperty, BorderThicknessProperty, CornerRadiusProperty); AffectsRender<Border>(BackgroundProperty, BorderBrushProperty, BorderThicknessProperty, CornerRadiusProperty);
AffectsMeasure(BorderThicknessProperty); AffectsMeasure<Border>(BorderThicknessProperty);
} }
/// <summary> /// <summary>

2
src/Avalonia.Controls/Button.cs

@ -80,7 +80,7 @@ namespace Avalonia.Controls
FocusableProperty.OverrideDefaultValue(typeof(Button), true); FocusableProperty.OverrideDefaultValue(typeof(Button), true);
CommandProperty.Changed.Subscribe(CommandChanged); CommandProperty.Changed.Subscribe(CommandChanged);
IsDefaultProperty.Changed.Subscribe(IsDefaultChanged); IsDefaultProperty.Changed.Subscribe(IsDefaultChanged);
PseudoClass(IsPressedProperty, ":pressed"); PseudoClass<Button>(IsPressedProperty, ":pressed");
} }
/// <summary> /// <summary>

6
src/Avalonia.Controls/ButtonSpinner.cs

@ -85,8 +85,8 @@ namespace Avalonia.Controls
static ButtonSpinner() static ButtonSpinner()
{ {
AllowSpinProperty.Changed.Subscribe(AllowSpinChanged); AllowSpinProperty.Changed.Subscribe(AllowSpinChanged);
PseudoClass(ButtonSpinnerLocationProperty, location => location == Location.Left, ":left"); PseudoClass<ButtonSpinner, Location>(ButtonSpinnerLocationProperty, location => location == Location.Left, ":left");
PseudoClass(ButtonSpinnerLocationProperty, location => location == Location.Right, ":right"); PseudoClass<ButtonSpinner, Location>(ButtonSpinnerLocationProperty, location => location == Location.Right, ":right");
} }
/// <summary> /// <summary>
@ -260,4 +260,4 @@ namespace Avalonia.Controls
} }
} }
} }
} }

4
src/Avalonia.Controls/ContentControl.cs

@ -45,8 +45,8 @@ namespace Avalonia.Controls
static ContentControl() static ContentControl()
{ {
ContentControlMixin.Attach<ContentControl>(ContentProperty, x => x.LogicalChildren); ContentControlMixin.Attach<ContentControl>(ContentProperty, x => x.LogicalChildren);
PseudoClass(ContentProperty, x => x != null, ":valid"); PseudoClass<ContentControl, object>(ContentProperty, x => x != null, ":valid");
PseudoClass(ContentProperty, x => x == null, ":invalid"); PseudoClass<ContentControl, object>(ContentProperty, x => x == null, ":invalid");
} }
/// <summary> /// <summary>

2
src/Avalonia.Controls/Decorator.cs

@ -28,7 +28,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
static Decorator() static Decorator()
{ {
AffectsMeasure(ChildProperty, PaddingProperty); AffectsMeasure<Decorator>(ChildProperty, PaddingProperty);
ChildProperty.Changed.AddClassHandler<Decorator>(x => x.ChildChanged); ChildProperty.Changed.AddClassHandler<Decorator>(x => x.ChildChanged);
} }

6
src/Avalonia.Controls/DrawingPresenter.cs

@ -8,8 +8,8 @@ namespace Avalonia.Controls
{ {
static DrawingPresenter() static DrawingPresenter()
{ {
AffectsMeasure(DrawingProperty); AffectsMeasure<DrawingPresenter>(DrawingProperty);
AffectsRender(DrawingProperty); AffectsRender<DrawingPresenter>(DrawingProperty);
} }
public static readonly StyledProperty<Drawing> DrawingProperty = public static readonly StyledProperty<Drawing> DrawingProperty =
@ -56,4 +56,4 @@ namespace Avalonia.Controls
} }
} }
} }
} }

10
src/Avalonia.Controls/Expander.cs

@ -35,12 +35,12 @@ namespace Avalonia.Controls
static Expander() static Expander()
{ {
PseudoClass(ExpandDirectionProperty, d => d == ExpandDirection.Down, ":down"); PseudoClass<Expander, ExpandDirection>(ExpandDirectionProperty, d => d == ExpandDirection.Down, ":down");
PseudoClass(ExpandDirectionProperty, d => d == ExpandDirection.Up, ":up"); PseudoClass<Expander, ExpandDirection>(ExpandDirectionProperty, d => d == ExpandDirection.Up, ":up");
PseudoClass(ExpandDirectionProperty, d => d == ExpandDirection.Left, ":left"); PseudoClass<Expander, ExpandDirection>(ExpandDirectionProperty, d => d == ExpandDirection.Left, ":left");
PseudoClass(ExpandDirectionProperty, d => d == ExpandDirection.Right, ":right"); PseudoClass<Expander, ExpandDirection>(ExpandDirectionProperty, d => d == ExpandDirection.Right, ":right");
PseudoClass(IsExpandedProperty, ":expanded"); PseudoClass<Expander>(IsExpandedProperty, ":expanded");
IsExpandedProperty.Changed.AddClassHandler<Expander>(x => x.OnIsExpandedChanged); IsExpandedProperty.Changed.AddClassHandler<Expander>(x => x.OnIsExpandedChanged);
} }

3
src/Avalonia.Controls/Image.cs

@ -25,8 +25,7 @@ namespace Avalonia.Controls
static Image() static Image()
{ {
AffectsRender(SourceProperty); AffectsRender<Image>(SourceProperty, StretchProperty);
AffectsRender(StretchProperty);
} }
/// <summary> /// <summary>

4
src/Avalonia.Controls/Presenters/ContentPresenter.cs

@ -90,8 +90,8 @@ namespace Avalonia.Controls.Presenters
/// </summary> /// </summary>
static ContentPresenter() static ContentPresenter()
{ {
AffectsRender(BackgroundProperty, BorderBrushProperty, BorderThicknessProperty, CornerRadiusProperty); AffectsRender<ContentPresenter>(BackgroundProperty, BorderBrushProperty, BorderThicknessProperty, CornerRadiusProperty);
AffectsMeasure(BorderThicknessProperty, PaddingProperty); AffectsMeasure<ContentPresenter>(BorderThicknessProperty, PaddingProperty);
ContentProperty.Changed.AddClassHandler<ContentPresenter>(x => x.ContentChanged); ContentProperty.Changed.AddClassHandler<ContentPresenter>(x => x.ContentChanged);
ContentTemplateProperty.Changed.AddClassHandler<ContentPresenter>(x => x.ContentChanged); ContentTemplateProperty.Changed.AddClassHandler<ContentPresenter>(x => x.ContentChanged);
TemplatedParentProperty.Changed.AddClassHandler<ContentPresenter>(x => x.TemplatedParentChanged); TemplatedParentProperty.Changed.AddClassHandler<ContentPresenter>(x => x.TemplatedParentChanged);

2
src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs

@ -72,7 +72,7 @@ namespace Avalonia.Controls.Presenters
{ {
ClipToBoundsProperty.OverrideDefaultValue(typeof(ScrollContentPresenter), true); ClipToBoundsProperty.OverrideDefaultValue(typeof(ScrollContentPresenter), true);
ChildProperty.Changed.AddClassHandler<ScrollContentPresenter>(x => x.ChildChanged); ChildProperty.Changed.AddClassHandler<ScrollContentPresenter>(x => x.ChildChanged);
AffectsArrange(OffsetProperty); AffectsArrange<ScrollContentPresenter>(OffsetProperty);
} }
/// <summary> /// <summary>

2
src/Avalonia.Controls/Presenters/TextPresenter.cs

@ -38,7 +38,7 @@ namespace Avalonia.Controls.Presenters
static TextPresenter() static TextPresenter()
{ {
AffectsRender(PasswordCharProperty); AffectsRender<TextPresenter>(PasswordCharProperty);
} }
public TextPresenter() public TextPresenter()

2
src/Avalonia.Controls/Primitives/AccessText.cs

@ -28,7 +28,7 @@ namespace Avalonia.Controls.Primitives
/// </summary> /// </summary>
static AccessText() static AccessText()
{ {
AffectsRender(ShowAccessKeyProperty); AffectsRender<AccessText>(ShowAccessKeyProperty);
} }
/// <summary> /// <summary>

4
src/Avalonia.Controls/Primitives/ScrollBar.cs

@ -54,8 +54,8 @@ namespace Avalonia.Controls.Primitives
/// </summary> /// </summary>
static ScrollBar() static ScrollBar()
{ {
PseudoClass(OrientationProperty, o => o == Orientation.Vertical, ":vertical"); PseudoClass<ScrollBar, Orientation>(OrientationProperty, o => o == Orientation.Vertical, ":vertical");
PseudoClass(OrientationProperty, o => o == Orientation.Horizontal, ":horizontal"); PseudoClass<ScrollBar, Orientation>(OrientationProperty, o => o == Orientation.Horizontal, ":horizontal");
Thumb.DragDeltaEvent.AddClassHandler<ScrollBar>(o => o.OnThumbDragDelta, RoutingStrategies.Bubble); Thumb.DragDeltaEvent.AddClassHandler<ScrollBar>(o => o.OnThumbDragDelta, RoutingStrategies.Bubble);
Thumb.DragCompletedEvent.AddClassHandler<ScrollBar>(o => o.OnThumbDragComplete, RoutingStrategies.Bubble); Thumb.DragCompletedEvent.AddClassHandler<ScrollBar>(o => o.OnThumbDragComplete, RoutingStrategies.Bubble);

6
src/Avalonia.Controls/Primitives/ToggleButton.cs

@ -24,9 +24,9 @@ namespace Avalonia.Controls.Primitives
static ToggleButton() static ToggleButton()
{ {
PseudoClass(IsCheckedProperty, c => c == true, ":checked"); PseudoClass<ToggleButton, bool?>(IsCheckedProperty, c => c == true, ":checked");
PseudoClass(IsCheckedProperty, c => c == false, ":unchecked"); PseudoClass<ToggleButton, bool?>(IsCheckedProperty, c => c == false, ":unchecked");
PseudoClass(IsCheckedProperty, c => c == null, ":indeterminate"); PseudoClass<ToggleButton, bool?>(IsCheckedProperty, c => c == null, ":indeterminate");
} }
public bool? IsChecked public bool? IsChecked

4
src/Avalonia.Controls/Primitives/Track.cs

@ -39,10 +39,12 @@ namespace Avalonia.Controls.Primitives
static Track() static Track()
{ {
PseudoClass<Track, Orientation>(OrientationProperty, o => o == Orientation.Vertical, ":vertical");
PseudoClass<Track, Orientation>(OrientationProperty, o => o == Orientation.Horizontal, ":horizontal");
ThumbProperty.Changed.AddClassHandler<Track>(x => x.ThumbChanged); ThumbProperty.Changed.AddClassHandler<Track>(x => x.ThumbChanged);
IncreaseButtonProperty.Changed.AddClassHandler<Track>(x => x.ButtonChanged); IncreaseButtonProperty.Changed.AddClassHandler<Track>(x => x.ButtonChanged);
DecreaseButtonProperty.Changed.AddClassHandler<Track>(x => x.ButtonChanged); DecreaseButtonProperty.Changed.AddClassHandler<Track>(x => x.ButtonChanged);
AffectsArrange(MinimumProperty, MaximumProperty, ValueProperty, OrientationProperty); AffectsArrange<Track>(MinimumProperty, MaximumProperty, ValueProperty, OrientationProperty);
} }
public double Minimum public double Minimum

6
src/Avalonia.Controls/ProgressBar.cs

@ -33,9 +33,9 @@ namespace Avalonia.Controls
static ProgressBar() static ProgressBar()
{ {
PseudoClass(OrientationProperty, o => o == Avalonia.Controls.Orientation.Vertical, ":vertical"); PseudoClass<ProgressBar, Orientation>(OrientationProperty, o => o == Avalonia.Controls.Orientation.Vertical, ":vertical");
PseudoClass(OrientationProperty, o => o == Avalonia.Controls.Orientation.Horizontal, ":horizontal"); PseudoClass<ProgressBar, Orientation>(OrientationProperty, o => o == Avalonia.Controls.Orientation.Horizontal, ":horizontal");
PseudoClass(IsIndeterminateProperty, ":indeterminate"); PseudoClass<ProgressBar>(IsIndeterminateProperty, ":indeterminate");
ValueProperty.Changed.AddClassHandler<ProgressBar>(x => x.ValueChanged); ValueProperty.Changed.AddClassHandler<ProgressBar>(x => x.ValueChanged);
} }

5
src/Avalonia.Controls/Shapes/Shape.cs

@ -30,11 +30,10 @@ namespace Avalonia.Controls.Shapes
private Geometry _renderedGeometry; private Geometry _renderedGeometry;
bool _calculateTransformOnArrange = false; bool _calculateTransformOnArrange = false;
static Shape() static Shape()
{ {
AffectsMeasure(StretchProperty, StrokeThicknessProperty); AffectsMeasure<Shape>(StretchProperty, StrokeThicknessProperty);
AffectsRender(FillProperty, StrokeProperty, StrokeDashArrayProperty); AffectsRender<Shape>(FillProperty, StrokeProperty, StrokeDashArrayProperty);
} }
public Geometry DefiningGeometry public Geometry DefiningGeometry

2
src/Avalonia.Controls/Slider.cs

@ -42,6 +42,8 @@ namespace Avalonia.Controls
static Slider() static Slider()
{ {
OrientationProperty.OverrideDefaultValue(typeof(Slider), Orientation.Horizontal); OrientationProperty.OverrideDefaultValue(typeof(Slider), Orientation.Horizontal);
PseudoClass<Slider, Orientation>(OrientationProperty, o => o == Orientation.Vertical, ":vertical");
PseudoClass<Slider, Orientation>(OrientationProperty, o => o == Orientation.Horizontal, ":horizontal");
Thumb.DragStartedEvent.AddClassHandler<Slider>(x => x.OnThumbDragStarted, RoutingStrategies.Bubble); Thumb.DragStartedEvent.AddClassHandler<Slider>(x => x.OnThumbDragStarted, RoutingStrategies.Bubble);
Thumb.DragDeltaEvent.AddClassHandler<Slider>(x => x.OnThumbDragDelta, RoutingStrategies.Bubble); Thumb.DragDeltaEvent.AddClassHandler<Slider>(x => x.OnThumbDragDelta, RoutingStrategies.Bubble);
Thumb.DragCompletedEvent.AddClassHandler<Slider>(x => x.OnThumbDragCompleted, RoutingStrategies.Bubble); Thumb.DragCompletedEvent.AddClassHandler<Slider>(x => x.OnThumbDragCompleted, RoutingStrategies.Bubble);

4
src/Avalonia.Controls/StackPanel.cs

@ -29,8 +29,8 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
static StackPanel() static StackPanel()
{ {
AffectsMeasure(SpacingProperty); AffectsMeasure<StackPanel>(SpacingProperty);
AffectsMeasure(OrientationProperty); AffectsMeasure<StackPanel>(OrientationProperty);
} }
/// <summary> /// <summary>

2
src/Avalonia.Controls/TabControl.cs

@ -44,7 +44,7 @@ namespace Avalonia.Controls
{ {
SelectionModeProperty.OverrideDefaultValue<TabControl>(SelectionMode.AlwaysSelected); SelectionModeProperty.OverrideDefaultValue<TabControl>(SelectionMode.AlwaysSelected);
FocusableProperty.OverrideDefaultValue<TabControl>(false); FocusableProperty.OverrideDefaultValue<TabControl>(false);
AffectsMeasure(TabStripPlacementProperty); AffectsMeasure<TabControl>(TabStripPlacementProperty);
} }
/// <summary> /// <summary>

9
src/Avalonia.Controls/TextBlock.cs

@ -99,10 +99,11 @@ namespace Avalonia.Controls
static TextBlock() static TextBlock()
{ {
ClipToBoundsProperty.OverrideDefaultValue<TextBlock>(true); ClipToBoundsProperty.OverrideDefaultValue<TextBlock>(true);
AffectsRender(ForegroundProperty); AffectsRender<TextBlock>(
AffectsRender(FontWeightProperty); ForegroundProperty,
AffectsRender(FontSizeProperty); FontWeightProperty,
AffectsRender(FontStyleProperty); FontSizeProperty,
FontStyleProperty);
} }
/// <summary> /// <summary>

2
src/Avalonia.Controls/TopLevel.cs

@ -59,7 +59,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
static TopLevel() static TopLevel()
{ {
AffectsMeasure(ClientSizeProperty); AffectsMeasure<TopLevel>(ClientSizeProperty);
} }
/// <summary> /// <summary>

2
src/Avalonia.Controls/WrapPanel.cs

@ -30,7 +30,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
static WrapPanel() static WrapPanel()
{ {
AffectsMeasure(OrientationProperty); AffectsMeasure<WrapPanel>(OrientationProperty);
} }
/// <summary> /// <summary>

6
src/Avalonia.Input/InputElement.cs

@ -168,9 +168,9 @@ namespace Avalonia.Input
PointerReleasedEvent.AddClassHandler<InputElement>(x => x.OnPointerReleased); PointerReleasedEvent.AddClassHandler<InputElement>(x => x.OnPointerReleased);
PointerWheelChangedEvent.AddClassHandler<InputElement>(x => x.OnPointerWheelChanged); PointerWheelChangedEvent.AddClassHandler<InputElement>(x => x.OnPointerWheelChanged);
PseudoClass(IsEnabledCoreProperty, x => !x, ":disabled"); PseudoClass<InputElement, bool>(IsEnabledCoreProperty, x => !x, ":disabled");
PseudoClass(IsFocusedProperty, ":focus"); PseudoClass<InputElement>(IsFocusedProperty, ":focus");
PseudoClass(IsPointerOverProperty, ":pointerover"); PseudoClass<InputElement>(IsPointerOverProperty, ":pointerover");
} }
/// <summary> /// <summary>

68
src/Avalonia.Layout/Layoutable.cs

@ -140,7 +140,7 @@ namespace Avalonia.Layout
/// </summary> /// </summary>
static Layoutable() static Layoutable()
{ {
AffectsMeasure( AffectsMeasure<Layoutable>(
IsVisibleProperty, IsVisibleProperty,
WidthProperty, WidthProperty,
HeightProperty, HeightProperty,
@ -427,11 +427,32 @@ namespace Avalonia.Layout
/// After a call to this method in a control's static constructor, any change to the /// After a call to this method in a control's static constructor, any change to the
/// property will cause <see cref="InvalidateMeasure"/> to be called on the element. /// property will cause <see cref="InvalidateMeasure"/> to be called on the element.
/// </remarks> /// </remarks>
[Obsolete("Use AffectsMeasure<T> and specify the control type.")]
protected static void AffectsMeasure(params AvaloniaProperty[] properties) protected static void AffectsMeasure(params AvaloniaProperty[] properties)
{ {
AffectsMeasure<Layoutable>(properties);
}
/// <summary>
/// Marks a property as affecting the control's measurement.
/// </summary>
/// <typeparam name="T">The control which the property affects.</typeparam>
/// <param name="properties">The properties.</param>
/// <remarks>
/// After a call to this method in a control's static constructor, any change to the
/// property will cause <see cref="InvalidateMeasure"/> to be called on the element.
/// </remarks>
protected static void AffectsMeasure<T>(params AvaloniaProperty[] properties)
where T : class, ILayoutable
{
void Invalidate(AvaloniaPropertyChangedEventArgs e)
{
(e.Sender as T)?.InvalidateMeasure();
}
foreach (var property in properties) foreach (var property in properties)
{ {
property.Changed.Subscribe(AffectsMeasureInvalidate); property.Changed.Subscribe(Invalidate);
} }
} }
@ -443,11 +464,32 @@ namespace Avalonia.Layout
/// After a call to this method in a control's static constructor, any change to the /// After a call to this method in a control's static constructor, any change to the
/// property will cause <see cref="InvalidateArrange"/> to be called on the element. /// property will cause <see cref="InvalidateArrange"/> to be called on the element.
/// </remarks> /// </remarks>
[Obsolete("Use AffectsArrange<T> and specify the control type.")]
protected static void AffectsArrange(params AvaloniaProperty[] properties) protected static void AffectsArrange(params AvaloniaProperty[] properties)
{ {
AffectsArrange<Layoutable>(properties);
}
/// <summary>
/// Marks a property as affecting the control's arrangement.
/// </summary>
/// <typeparam name="T">The control which the property affects.</typeparam>
/// <param name="properties">The properties.</param>
/// <remarks>
/// After a call to this method in a control's static constructor, any change to the
/// property will cause <see cref="InvalidateArrange"/> to be called on the element.
/// </remarks>
protected static void AffectsArrange<T>(params AvaloniaProperty[] properties)
where T : class, ILayoutable
{
void Invalidate(AvaloniaPropertyChangedEventArgs e)
{
(e.Sender as T)?.InvalidateArrange();
}
foreach (var property in properties) foreach (var property in properties)
{ {
property.Changed.Subscribe(AffectsArrangeInvalidate); property.Changed.Subscribe(Invalidate);
} }
} }
@ -632,26 +674,6 @@ namespace Avalonia.Layout
base.OnVisualParentChanged(oldParent, newParent); base.OnVisualParentChanged(oldParent, newParent);
} }
/// <summary>
/// Calls <see cref="InvalidateMeasure"/> on the control on which a property changed.
/// </summary>
/// <param name="e">The event args.</param>
private static void AffectsMeasureInvalidate(AvaloniaPropertyChangedEventArgs e)
{
ILayoutable control = e.Sender as ILayoutable;
control?.InvalidateMeasure();
}
/// <summary>
/// Calls <see cref="InvalidateArrange"/> on the control on which a property changed.
/// </summary>
/// <param name="e">The event args.</param>
private static void AffectsArrangeInvalidate(AvaloniaPropertyChangedEventArgs e)
{
ILayoutable control = e.Sender as ILayoutable;
control?.InvalidateArrange();
}
/// <summary> /// <summary>
/// Tests whether any of a <see cref="Rect"/>'s properties include negative values, /// Tests whether any of a <see cref="Rect"/>'s properties include negative values,
/// a NaN or Infinity. /// a NaN or Infinity.

45
src/Avalonia.Styling/StyledElement.cs

@ -493,22 +493,53 @@ namespace Avalonia
/// </summary> /// </summary>
/// <param name="property">The property.</param> /// <param name="property">The property.</param>
/// <param name="className">The pseudo-class.</param> /// <param name="className">The pseudo-class.</param>
[Obsolete("Use PseudoClass<TOwner> and specify the control type.")]
protected static void PseudoClass(AvaloniaProperty<bool> property, string className) protected static void PseudoClass(AvaloniaProperty<bool> property, string className)
{ {
PseudoClass(property, x => x, className); PseudoClass<StyledElement>(property, className);
}
/// <summary>
/// Adds a pseudo-class to be set when a property is true.
/// </summary>
/// <typeparam name="TOwner">The type to apply the pseudo-class to.</typeparam>
/// <param name="property">The property.</param>
/// <param name="className">The pseudo-class.</param>
protected static void PseudoClass<TOwner>(AvaloniaProperty<bool> property, string className)
where TOwner : class, IStyledElement
{
PseudoClass<TOwner, bool>(property, x => x, className);
}
/// <summary>
/// Adds a pseudo-class to be set when a property equals a certain value.
/// </summary>
/// <typeparam name="TProperty">The type of the property.</typeparam>
/// <param name="property">The property.</param>
/// <param name="selector">Returns a boolean value based on the property value.</param>
/// <param name="className">The pseudo-class.</param>
[Obsolete("Use PseudoClass<TOwner, TProperty> and specify the control type.")]
protected static void PseudoClass<TProperty>(
AvaloniaProperty<TProperty> property,
Func<TProperty, bool> selector,
string className)
{
PseudoClass<StyledElement, TProperty>(property, selector, className);
} }
/// <summary> /// <summary>
/// Adds a pseudo-class to be set when a property equals a certain value. /// Adds a pseudo-class to be set when a property equals a certain value.
/// </summary> /// </summary>
/// <typeparam name="T">The type of the property.</typeparam> /// <typeparam name="TProperty">The type of the property.</typeparam>
/// <typeparam name="TOwner">The type to apply the pseudo-class to.</typeparam>
/// <param name="property">The property.</param> /// <param name="property">The property.</param>
/// <param name="selector">Returns a boolean value based on the property value.</param> /// <param name="selector">Returns a boolean value based on the property value.</param>
/// <param name="className">The pseudo-class.</param> /// <param name="className">The pseudo-class.</param>
protected static void PseudoClass<T>( protected static void PseudoClass<TOwner, TProperty>(
AvaloniaProperty<T> property, AvaloniaProperty<TProperty> property,
Func<T, bool> selector, Func<TProperty, bool> selector,
string className) string className)
where TOwner : class, IStyledElement
{ {
Contract.Requires<ArgumentNullException>(property != null); Contract.Requires<ArgumentNullException>(property != null);
Contract.Requires<ArgumentNullException>(selector != null); Contract.Requires<ArgumentNullException>(selector != null);
@ -520,10 +551,10 @@ namespace Avalonia
} }
property.Changed.Merge(property.Initialized) property.Changed.Merge(property.Initialized)
.Where(e => e.Sender is StyledElement) .Where(e => e.Sender is TOwner)
.Subscribe(e => .Subscribe(e =>
{ {
if (selector((T)e.NewValue)) if (selector((TProperty)e.NewValue))
{ {
((StyledElement)e.Sender).PseudoClasses.Add(className); ((StyledElement)e.Sender).PseudoClasses.Add(className);
} }

10
src/Avalonia.Themes.Default/CheckBox.xaml

@ -1,12 +1,16 @@
<Styles xmlns="https://github.com/avaloniaui"> <Styles xmlns="https://github.com/avaloniaui">
<Style Selector="CheckBox"> <Style Selector="CheckBox">
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundBrush}"/>
<Setter Property="Background" Value="Transparent"/> <Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderMidBrush}"/> <Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderMidBrush}"/>
<Setter Property="BorderThickness" Value="{DynamicResource ThemeBorderThickness}"/> <Setter Property="BorderThickness" Value="{DynamicResource ThemeBorderThickness}"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="HorizontalContentAlignment" Value="Left"/>
<Setter Property="Template"> <Setter Property="Template">
<ControlTemplate> <ControlTemplate>
<Grid ColumnDefinitions="Auto,*" Background="{TemplateBinding Background}"> <Grid ColumnDefinitions="Auto,*">
<Border Name="border" <Border Name="border"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}" BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}" BorderThickness="{TemplateBinding BorderThickness}"
Width="18" Width="18"
@ -31,10 +35,12 @@
</Panel> </Panel>
</Border> </Border>
<ContentPresenter Name="PART_ContentPresenter" <ContentPresenter Name="PART_ContentPresenter"
TextBlock.Foreground="{TemplateBinding Foreground}"
ContentTemplate="{TemplateBinding ContentTemplate}" ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}" Content="{TemplateBinding Content}"
Margin="4,0,0,0" Margin="4,0,0,0"
VerticalAlignment="Center" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Grid.Column="1"/> Grid.Column="1"/>
</Grid> </Grid>
</ControlTemplate> </ControlTemplate>

8
src/Avalonia.Themes.Default/TreeView.xaml

@ -3,11 +3,15 @@
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderMidBrush}"/> <Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderMidBrush}"/>
<Setter Property="BorderThickness" Value="{DynamicResource ThemeBorderThickness}"/> <Setter Property="BorderThickness" Value="{DynamicResource ThemeBorderThickness}"/>
<Setter Property="Padding" Value="4"/> <Setter Property="Padding" Value="4"/>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="Template"> <Setter Property="Template">
<ControlTemplate> <ControlTemplate>
<Border BorderBrush="{TemplateBinding BorderBrush}" <Border BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"> BorderThickness="{TemplateBinding BorderThickness}">
<ScrollViewer Background="{TemplateBinding Background}"> <ScrollViewer Background="{TemplateBinding Background}"
HorizontalScrollBarVisibility="{TemplateBinding (ScrollViewer.HorizontalScrollBarVisibility)}"
VerticalScrollBarVisibility="{TemplateBinding (ScrollViewer.VerticalScrollBarVisibility)}">
<ItemsPresenter Name="PART_ItemsPresenter" <ItemsPresenter Name="PART_ItemsPresenter"
Items="{TemplateBinding Items}" Items="{TemplateBinding Items}"
ItemsPanel="{TemplateBinding ItemsPanel}" ItemsPanel="{TemplateBinding ItemsPanel}"
@ -17,4 +21,4 @@
</Border> </Border>
</ControlTemplate> </ControlTemplate>
</Setter> </Setter>
</Style> </Style>

8
src/Avalonia.Visuals/Animation/CrossFade.cs

@ -38,11 +38,11 @@ namespace Avalonia.Animation
new Setter new Setter
{ {
Property = Visual.OpacityProperty, Property = Visual.OpacityProperty,
Value = 0.0 Value = 0d
} }
) )
{ {
Cue = new Cue(1.0) Cue = new Cue(1d)
} }
}; };
_fadeInAnimation = new Animation _fadeInAnimation = new Animation
@ -52,11 +52,11 @@ namespace Avalonia.Animation
new Setter new Setter
{ {
Property = Visual.OpacityProperty, Property = Visual.OpacityProperty,
Value = 0.0 Value = 0d
} }
) )
{ {
Cue = new Cue(0.0) Cue = new Cue(0d)
} }
}; };
_fadeOutAnimation.Duration = _fadeInAnimation.Duration = duration; _fadeOutAnimation.Duration = _fadeInAnimation.Duration = duration;

14
src/Avalonia.Visuals/Animation/PageSlide.cs

@ -86,11 +86,11 @@ namespace Avalonia.Animation
new Setter new Setter
{ {
Property = translateProperty, Property = translateProperty,
Value = 0 Value = 0d
} }
) )
{ {
Cue = new Cue(0.0) Cue = new Cue(0d)
}, },
new KeyFrame new KeyFrame
( (
@ -101,7 +101,7 @@ namespace Avalonia.Animation
} }
) )
{ {
Cue = new Cue(1.0) Cue = new Cue(1d)
} }
}; };
animation.Duration = Duration; animation.Duration = Duration;
@ -119,22 +119,22 @@ namespace Avalonia.Animation
new Setter new Setter
{ {
Property = translateProperty, Property = translateProperty,
Value = forward ? -distance : distance Value = forward ? distance : -distance
} }
) )
{ {
Cue = new Cue(0.0) Cue = new Cue(0d)
}, },
new KeyFrame new KeyFrame
( (
new Setter new Setter
{ {
Property = translateProperty, Property = translateProperty,
Value = 0 Value = 0d
} }
) )
{ {
Cue = new Cue(1.0) Cue = new Cue(1d)
}, },
}; };
animation.Duration = Duration; animation.Duration = Duration;

11
src/Avalonia.Visuals/Rendering/SceneGraph/IVisualNode.cs

@ -30,19 +30,20 @@ namespace Avalonia.Rendering.SceneGraph
Matrix Transform { get; } Matrix Transform { get; }
/// <summary> /// <summary>
/// Gets the bounds for the node's geometry in global coordinates. /// Gets the bounds of the node's geometry in global coordinates.
/// </summary> /// </summary>
Rect Bounds { get; } Rect Bounds { get; }
/// <summary> /// <summary>
/// Gets the clip bounds for the node in global coordinates. /// Gets the clip bounds for the node in global coordinates.
/// </summary> /// </summary>
/// <remarks>
/// This clip does not take into account parent clips, to find the absolute clip bounds
/// it is necessary to traverse the tree.
/// </remarks>
Rect ClipBounds { get; } Rect ClipBounds { get; }
/// <summary>
/// Gets the layout bounds for the node in global coordinates.
/// </summary>
Rect LayoutBounds { get; }
/// <summary> /// <summary>
/// Whether the node is clipped to <see cref="ClipBounds"/>. /// Whether the node is clipped to <see cref="ClipBounds"/>.
/// </summary> /// </summary>

4
src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs

@ -167,8 +167,9 @@ namespace Avalonia.Rendering.SceneGraph
using (context.PushPostTransform(m)) using (context.PushPostTransform(m))
using (context.PushTransformContainer()) using (context.PushTransformContainer())
{ {
var globalBounds = bounds.TransformToAABB(contextImpl.Transform);
var clipBounds = clipToBounds ? var clipBounds = clipToBounds ?
bounds.TransformToAABB(contextImpl.Transform).Intersect(clip) : globalBounds.Intersect(clip) :
clip; clip;
forceRecurse = forceRecurse || forceRecurse = forceRecurse ||
@ -179,6 +180,7 @@ namespace Avalonia.Rendering.SceneGraph
node.Transform = contextImpl.Transform; node.Transform = contextImpl.Transform;
node.ClipBounds = clipBounds; node.ClipBounds = clipBounds;
node.ClipToBounds = clipToBounds; node.ClipToBounds = clipToBounds;
node.LayoutBounds = globalBounds;
node.GeometryClip = visual.Clip?.PlatformImpl; node.GeometryClip = visual.Clip?.PlatformImpl;
node.Opacity = opacity; node.Opacity = opacity;

5
src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs

@ -58,6 +58,9 @@ namespace Avalonia.Rendering.SceneGraph
/// <inheritdoc/> /// <inheritdoc/>
public Rect ClipBounds { get; set; } public Rect ClipBounds { get; set; }
/// <inheritdoc/>
public Rect LayoutBounds { get; set; }
/// <inheritdoc/> /// <inheritdoc/>
public bool ClipToBounds { get; set; } public bool ClipToBounds { get; set; }
@ -266,7 +269,7 @@ namespace Avalonia.Rendering.SceneGraph
if (OpacityMask != null) if (OpacityMask != null)
{ {
context.PushOpacityMask(OpacityMask, ClipBounds); context.PushOpacityMask(OpacityMask, LayoutBounds);
} }
} }

36
src/Avalonia.Visuals/Visual.cs

@ -100,7 +100,7 @@ namespace Avalonia
/// </summary> /// </summary>
static Visual() static Visual()
{ {
AffectsRender( AffectsRender<Visual>(
BoundsProperty, BoundsProperty,
ClipProperty, ClipProperty,
ClipToBoundsProperty, ClipToBoundsProperty,
@ -320,11 +320,34 @@ namespace Avalonia
/// on the control which when changed should cause a redraw. This is similar to WPF's /// on the control which when changed should cause a redraw. This is similar to WPF's
/// FrameworkPropertyMetadata.AffectsRender flag. /// FrameworkPropertyMetadata.AffectsRender flag.
/// </remarks> /// </remarks>
[Obsolete("Use AffectsRender<T> and specify the control type.")]
protected static void AffectsRender(params AvaloniaProperty[] properties) protected static void AffectsRender(params AvaloniaProperty[] properties)
{ {
AffectsRender<Visual>(properties);
}
/// <summary>
/// Indicates that a property change should cause <see cref="InvalidateVisual"/> to be
/// called.
/// </summary>
/// <typeparam name="T">The control which the property affects.</typeparam>
/// <param name="properties">The properties.</param>
/// <remarks>
/// This method should be called in a control's static constructor with each property
/// on the control which when changed should cause a redraw. This is similar to WPF's
/// FrameworkPropertyMetadata.AffectsRender flag.
/// </remarks>
protected static void AffectsRender<T>(params AvaloniaProperty[] properties)
where T : class, IVisual
{
void Invalidate(AvaloniaPropertyChangedEventArgs e)
{
(e.Sender as T)?.InvalidateVisual();
}
foreach (var property in properties) foreach (var property in properties)
{ {
property.Changed.Subscribe(AffectsRenderInvalidate); property.Changed.Subscribe(Invalidate);
} }
} }
@ -412,15 +435,6 @@ namespace Avalonia
RaisePropertyChanged(VisualParentProperty, oldParent, newParent, BindingPriority.LocalValue); RaisePropertyChanged(VisualParentProperty, oldParent, newParent, BindingPriority.LocalValue);
} }
/// <summary>
/// Called when a property changes that should invalidate the visual.
/// </summary>
/// <param name="e">The event args.</param>
private static void AffectsRenderInvalidate(AvaloniaPropertyChangedEventArgs e)
{
(e.Sender as Visual)?.InvalidateVisual();
}
/// <summary> /// <summary>
/// Gets the visual offset from the specified ancestor. /// Gets the visual offset from the specified ancestor.
/// </summary> /// </summary>

37
tests/Avalonia.RenderTests/OpacityMaskTests.cs

@ -55,5 +55,42 @@ namespace Avalonia.Direct2D1.RenderTests
await RenderToFile(target); await RenderToFile(target);
CompareImages(); CompareImages();
} }
[Fact]
public async Task RenderTansform_Applies_To_Opacity_Mask()
{
var target = new Canvas
{
OpacityMask = new LinearGradientBrush
{
StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative),
EndPoint = new RelativePoint(1, 1, RelativeUnit.Relative),
GradientStops = new List<GradientStop>
{
new GradientStop(Color.FromUInt32(0xffffffff), 0),
new GradientStop(Color.FromUInt32(0x00ffffff), 1)
}
},
RenderTransform = new RotateTransform(90),
Width = 76,
Height = 76,
Children =
{
new Path
{
Width = 32,
Height = 40,
[Canvas.LeftProperty] = 23,
[Canvas.TopProperty] = 18,
Stretch = Stretch.Fill,
Fill = Brushes.Red,
Data = StreamGeometry.Parse("F1 M 27,18L 23,26L 33,30L 24,38L 33,46L 23,50L 27,58L 45,58L 55,38L 45,18L 27,18 Z")
}
}
};
await RenderToFile(target);
CompareImages();
}
} }
} }

BIN
tests/TestFiles/Direct2D1/OpacityMask/RenderTansform_Applies_To_Opacity_Mask.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

BIN
tests/TestFiles/Skia/OpacityMask/RenderTansform_Applies_To_Opacity_Mask.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 834 B

Loading…
Cancel
Save