Browse Source

Update Viewbox to use internal visual and no layout rounding

amwx 3 years ago
parent
commit
c82a01090d
2 changed files with 57 additions and 18 deletions
  1. 2 1
      src/Avalonia.Controls/ApiCompatBaseline.txt
  2. 55 17
      src/Avalonia.Controls/Viewbox.cs

+ 2 - 1
src/Avalonia.Controls/ApiCompatBaseline.txt

@@ -30,6 +30,7 @@ MembersMustExist : Member 'public System.Double Avalonia.Controls.NumericUpDownV
 MembersMustExist : Member 'public System.Double Avalonia.Controls.NumericUpDownValueChangedEventArgs.OldValue.get()' does not exist in the implementation but it does exist in the contract.
 MembersMustExist : Member 'public Avalonia.StyledProperty<System.Boolean> Avalonia.StyledProperty<System.Boolean> Avalonia.Controls.ScrollViewer.AllowAutoHideProperty' does not exist in the implementation but it does exist in the contract.
 CannotRemoveBaseTypeOrInterface : Type 'Avalonia.Controls.TopLevel' does not implement interface 'Avalonia.Utilities.IWeakSubscriber<Avalonia.Controls.ResourcesChangedEventArgs>' in the implementation but it does in the contract.
+CannotRemoveBaseTypeOrInterface : Type 'Avalonia.Controls.Viewbox' does not inherit from base type 'Avalonia.Controls.Decorator' in the implementation but it does in the contract.
 MembersMustExist : Member 'public Avalonia.AvaloniaProperty<Avalonia.Media.Stretch> Avalonia.AvaloniaProperty<Avalonia.Media.Stretch> Avalonia.Controls.Viewbox.StretchProperty' does not exist in the implementation but it does exist in the contract.
 CannotRemoveBaseTypeOrInterface : Type 'Avalonia.Controls.Window' does not implement interface 'Avalonia.Utilities.IWeakSubscriber<Avalonia.Controls.ResourcesChangedEventArgs>' in the implementation but it does in the contract.
 CannotRemoveBaseTypeOrInterface : Type 'Avalonia.Controls.WindowBase' does not implement interface 'Avalonia.Utilities.IWeakSubscriber<Avalonia.Controls.ResourcesChangedEventArgs>' in the implementation but it does in the contract.
@@ -68,4 +69,4 @@ InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platfor
 MembersMustExist : Member 'public void Avalonia.Platform.IWindowImpl.Resize(Avalonia.Size)' does not exist in the implementation but it does exist in the contract.
 InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IWindowImpl.Resize(Avalonia.Size, Avalonia.Platform.PlatformResizeReason)' is present in the implementation but not in the contract.
 InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.ITrayIconImpl Avalonia.Platform.IWindowingPlatform.CreateTrayIcon()' is present in the implementation but not in the contract.
-Total Issues: 69
+Total Issues: 70

+ 55 - 17
src/Avalonia.Controls/Viewbox.cs

@@ -1,13 +1,15 @@
 using Avalonia.Media;
+using Avalonia.Metadata;
 
 namespace Avalonia.Controls
 {
     /// <summary>
     /// Viewbox is used to scale single child to fit in the available space.
     /// </summary>
-    /// <seealso cref="Avalonia.Controls.Decorator" />
-    public class Viewbox : Decorator
+    public class Viewbox : Control
     {
+        private Decorator _containerVisual;
+
         /// <summary>
         /// Defines the <see cref="Stretch"/> property.
         /// </summary>
@@ -20,12 +22,27 @@ namespace Avalonia.Controls
         public static readonly StyledProperty<StretchDirection> StretchDirectionProperty =
             AvaloniaProperty.Register<Viewbox, StretchDirection>(nameof(StretchDirection), StretchDirection.Both);
 
+        /// <summary>
+        /// Defines the <see cref="Child"/> property
+        /// </summary>
+        public static readonly StyledProperty<IControl?> ChildProperty =
+            Decorator.ChildProperty.AddOwner<Viewbox>();
+
         static Viewbox()
         {
             ClipToBoundsProperty.OverrideDefaultValue<Viewbox>(true);
+            UseLayoutRoundingProperty.OverrideDefaultValue<Viewbox>(true);
             AffectsMeasure<Viewbox>(StretchProperty, StretchDirectionProperty);
         }
 
+        public Viewbox()
+        {
+            _containerVisual = new Decorator();
+            _containerVisual.RenderTransformOrigin = RelativePoint.TopLeft;
+            LogicalChildren.Add(_containerVisual);
+            VisualChildren.Add(_containerVisual);
+        }
+
         /// <summary>
         /// Gets or sets the stretch mode, 
         /// which determines how child fits into the available space.
@@ -45,9 +62,40 @@ namespace Avalonia.Controls
             set => SetValue(StretchDirectionProperty, value);
         }
 
+        /// <summary>
+        /// Gets or sets the child of the Viewbox
+        /// </summary>
+        [Content]
+        public IControl? Child
+        {
+            get => GetValue(ChildProperty);
+            set => SetValue(ChildProperty, value);
+        }
+
+        /// <summary>
+        /// Gets or sets the transform applied to the container visual that
+        /// hosts the child of the Viewbox
+        /// </summary>
+        protected ITransform? InternalTransform
+        {
+            get => _containerVisual.RenderTransform;
+            set => _containerVisual.RenderTransform = value;
+        }
+
+        protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
+        {
+            base.OnPropertyChanged(change);
+
+            if (change.Property == ChildProperty)
+            {
+                _containerVisual.Child = change.NewValue.GetValueOrDefault<IControl>();
+                InvalidateMeasure();
+            }
+        }
+
         protected override Size MeasureOverride(Size availableSize)
         {
-            var child = Child;
+            var child = _containerVisual;
 
             if (child != null)
             {
@@ -57,7 +105,7 @@ namespace Avalonia.Controls
 
                 var size = Stretch.CalculateSize(availableSize, childSize, StretchDirection);
 
-                return size.Constrain(availableSize);
+                return size;
             }
 
             return new Size();
@@ -65,31 +113,21 @@ namespace Avalonia.Controls
 
         protected override Size ArrangeOverride(Size finalSize)
         {
-            var child = Child;
+            var child = _containerVisual;
 
             if (child != null)
             {
                 var childSize = child.DesiredSize;
                 var scale = Stretch.CalculateScaling(finalSize, childSize, StretchDirection);
 
-                // TODO: Viewbox should have another decorator as a child so we won't affect other render transforms.
-                var scaleTransform = child.RenderTransform as ScaleTransform;
-
-                if (scaleTransform == null)
-                {
-                    child.RenderTransform = scaleTransform = new ScaleTransform(scale.X, scale.Y);
-                    child.RenderTransformOrigin = RelativePoint.TopLeft;
-                }
-
-                scaleTransform.ScaleX = scale.X;
-                scaleTransform.ScaleY = scale.Y;
+                InternalTransform = new ScaleTransform(scale.X, scale.Y);
 
                 child.Arrange(new Rect(childSize));
 
                 return childSize * scale;
             }
 
-            return new Size();
+            return finalSize;
         }
     }
 }