diff --git a/appveyor.yml b/appveyor.yml
index 7f2b1bb395..4bf7f7f157 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -13,6 +13,8 @@ environment:
MYGET_API_KEY:
secure: OtVfyN3ErqQrDTnWH2HDfJDlCiu/i4/X4wFmK3ZXXP7HmCiXYPSbTjMPwwdOxRaK
MYGET_API_URL: https://www.myget.org/F/avalonia-ci/api/v2/package
+init:
+- ps: (New-Object Net.WebClient).DownloadFile('https://raw.githubusercontent.com/appveyor/ci/master/scripts/xamarin-vs2017-151-fixed.targets', "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Microsoft.Common.Targets\ImportAfter\Xamarin.Common.targets")
install:
- if not exist gtk-sharp-2.12.26.msi appveyor DownloadFile http://download.xamarin.com/GTKforWindows/Windows/gtk-sharp-2.12.26.msi
- if not exist dotnet-1.0.1.exe appveyor DownloadFile https://go.microsoft.com/fwlink/?linkid=843448 -FileName "dotnet-1.0.1.exe"
diff --git a/src/Avalonia.Visuals/Avalonia.Visuals.csproj b/src/Avalonia.Visuals/Avalonia.Visuals.csproj
index c820a83c2a..dd9a795937 100644
--- a/src/Avalonia.Visuals/Avalonia.Visuals.csproj
+++ b/src/Avalonia.Visuals/Avalonia.Visuals.csproj
@@ -2,6 +2,7 @@
netstandard1.1
false
+ Avalonia
true
diff --git a/src/Avalonia.Visuals/Media/LinearGradientBrush.cs b/src/Avalonia.Visuals/Media/LinearGradientBrush.cs
index 6c0d500343..344e05e16e 100644
--- a/src/Avalonia.Visuals/Media/LinearGradientBrush.cs
+++ b/src/Avalonia.Visuals/Media/LinearGradientBrush.cs
@@ -1,6 +1,8 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
+using System;
+
namespace Avalonia.Media
{
///
diff --git a/src/Avalonia.Visuals/Media/RadialGradientBrush.cs b/src/Avalonia.Visuals/Media/RadialGradientBrush.cs
index 9f9c2733ec..003e2e05f9 100644
--- a/src/Avalonia.Visuals/Media/RadialGradientBrush.cs
+++ b/src/Avalonia.Visuals/Media/RadialGradientBrush.cs
@@ -4,8 +4,7 @@
namespace Avalonia.Media
{
///
- /// Paints an area with a radial gradient. A focal point defines the beginning of the gradient,
- /// and a circle defines the end point of the gradient.
+ /// Paints an area with a radial gradient.
///
public sealed class RadialGradientBrush : GradientBrush, IRadialGradientBrush, IMutableBrush
{
@@ -56,6 +55,7 @@ namespace Avalonia.Media
/// Gets or sets the horizontal and vertical radius of the outermost circle of the radial
/// gradient.
///
+ // TODO: This appears to always be relative so should use a RelativeSize struct or something.
public double Radius
{
get { return GetValue(RadiusProperty); }
diff --git a/src/Avalonia.Visuals/Visual.cs b/src/Avalonia.Visuals/Visual.cs
index ae5e385169..20772d8f0d 100644
--- a/src/Avalonia.Visuals/Visual.cs
+++ b/src/Avalonia.Visuals/Visual.cs
@@ -10,7 +10,6 @@ using Avalonia.Collections;
using Avalonia.Data;
using Avalonia.Logging;
using Avalonia.Media;
-using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.VisualTree;
@@ -20,10 +19,10 @@ namespace Avalonia
/// Base class for controls that provides rendering and related visual properties.
///
///
- /// The class acts as a node in the Avalonia scene graph and holds
- /// all the information needed for an to render the control.
- /// To traverse the scene graph (aka Visual Tree), use the extension methods defined
- /// in .
+ /// The class represents elements that have a visual on-screen
+ /// representation and stores all the information needed for an
+ /// to render the control. To traverse the visual tree, use the
+ /// extension methods defined in .
///
public class Visual : Animatable, IVisual
{
@@ -88,6 +87,7 @@ namespace Avalonia
AvaloniaProperty.Register(nameof(ZIndex));
private Rect _bounds;
+ private IRenderRoot _visualRoot;
private IVisual _visualParent;
///
@@ -95,7 +95,12 @@ namespace Avalonia
///
static Visual()
{
- AffectsRender(BoundsProperty, IsVisibleProperty, OpacityProperty);
+ AffectsRender(
+ BoundsProperty,
+ ClipProperty,
+ ClipToBoundsProperty,
+ IsVisibleProperty,
+ OpacityProperty);
RenderTransformProperty.Changed.Subscribe(RenderTransformChanged);
}
@@ -122,7 +127,7 @@ namespace Avalonia
public event EventHandler DetachedFromVisualTree;
///
- /// Gets the bounds of the scene graph node relative to its parent.
+ /// Gets the bounds of the control relative to its parent.
///
public Rect Bounds
{
@@ -131,7 +136,7 @@ namespace Avalonia
}
///
- /// Gets a value indicating whether the scene graph node should be clipped to its bounds.
+ /// Gets a value indicating whether the control should be clipped to its bounds.
///
public bool ClipToBounds
{
@@ -149,7 +154,7 @@ namespace Avalonia
}
///
- /// Gets a value indicating whether this scene graph node and all its parents are visible.
+ /// Gets a value indicating whether this control and all its parents are visible.
///
public bool IsEffectivelyVisible
{
@@ -157,7 +162,7 @@ namespace Avalonia
}
///
- /// Gets a value indicating whether this scene graph node is visible.
+ /// Gets a value indicating whether this control is visible.
///
public bool IsVisible
{
@@ -166,7 +171,7 @@ namespace Avalonia
}
///
- /// Gets the opacity of the scene graph node.
+ /// Gets the opacity of the control.
///
public double Opacity
{
@@ -174,9 +179,8 @@ namespace Avalonia
set { SetValue(OpacityProperty, value); }
}
-
///
- /// Gets the opacity mask of the scene graph node.
+ /// Gets the opacity mask of the control.
///
public IBrush OpacityMask
{
@@ -185,7 +189,7 @@ namespace Avalonia
}
///
- /// Gets the render transform of the scene graph node.
+ /// Gets the render transform of the control.
///
public Transform RenderTransform
{
@@ -194,7 +198,7 @@ namespace Avalonia
}
///
- /// Gets the transform origin of the scene graph node.
+ /// Gets the transform origin of the control.
///
public RelativePoint RenderTransformOrigin
{
@@ -203,7 +207,7 @@ namespace Avalonia
}
///
- /// Gets the Z index of the node.
+ /// Gets the Z index of the control.
///
///
/// Controls with a higher will appear in front of controls with
@@ -217,7 +221,7 @@ namespace Avalonia
}
///
- /// Gets the control's visual children.
+ /// Gets the control's child visuals.
///
protected IAvaloniaList VisualChildren
{
@@ -228,24 +232,20 @@ namespace Avalonia
///
/// Gets the root of the visual tree, if the control is attached to a visual tree.
///
- protected IRenderRoot VisualRoot
- {
- get;
- private set;
- }
+ protected IRenderRoot VisualRoot => _visualRoot ?? (this as IRenderRoot);
///
- /// Gets a value indicating whether this scene graph node is attached to a visual root.
+ /// Gets a value indicating whether this control is attached to a visual root.
///
bool IVisual.IsAttachedToVisualTree => VisualRoot != null;
///
- /// Gets the scene graph node's child nodes.
+ /// Gets the control's child controls.
///
IAvaloniaReadOnlyList IVisual.VisualChildren => VisualChildren;
///
- /// Gets the scene graph node's parent node.
+ /// Gets the control's parent visual.
///
IVisual IVisual.VisualParent => _visualParent;
@@ -321,7 +321,7 @@ namespace Avalonia
{
Logger.Verbose(LogArea.Visual, this, "Attached to visual tree");
- VisualRoot = e.Root;
+ _visualRoot = e.Root;
if (RenderTransform != null)
{
@@ -329,6 +329,7 @@ namespace Avalonia
}
OnAttachedToVisualTree(e);
+ InvalidateVisual();
if (VisualChildren != null)
{
@@ -348,7 +349,7 @@ namespace Avalonia
{
Logger.Verbose(LogArea.Visual, this, "Detached from visual tree");
- VisualRoot = null;
+ _visualRoot = null;
if (RenderTransform != null)
{
@@ -356,6 +357,7 @@ namespace Avalonia
}
OnDetachedFromVisualTree(e);
+ e.Root?.Renderer?.AddDirty(this);
if (VisualChildren != null)
{
@@ -492,11 +494,11 @@ namespace Avalonia
{
return;
}
-
+
var old = _visualParent;
_visualParent = value;
- if (VisualRoot != null)
+ if (_visualRoot != null)
{
var e = new VisualTreeAttachmentEventArgs(old, VisualRoot);
OnDetachedFromVisualTreeCore(e);
diff --git a/src/Avalonia.Visuals/VisualTree/IVisual.cs b/src/Avalonia.Visuals/VisualTree/IVisual.cs
index b9b37b1a20..2047996c3e 100644
--- a/src/Avalonia.Visuals/VisualTree/IVisual.cs
+++ b/src/Avalonia.Visuals/VisualTree/IVisual.cs
@@ -9,13 +9,13 @@ using Avalonia.Rendering;
namespace Avalonia.VisualTree
{
///
- /// Represents a node in the visual scene graph.
+ /// Represents control that has a visual on-screen representation.
///
///
/// The interface defines the interface required for a renderer to
- /// render a scene graph. You should not usually need to reference this interface unless
+ /// render a control. You should not usually need to reference this interface unless
/// you are writing a renderer; instead use the extension methods defined in
- /// to traverse the scene graph. This interface is
+ /// to traverse the visual tree. This interface is
/// implemented by . It should not be necessary to implement it
/// anywhere else.
///
@@ -32,12 +32,12 @@ namespace Avalonia.VisualTree
event EventHandler DetachedFromVisualTree;
///
- /// Gets the bounds of the scene graph node relative to its parent.
+ /// Gets the bounds of the control relative to its parent.
///
Rect Bounds { get; }
///
- /// Gets a value indicating whether the scene graph node should be clipped to its bounds.
+ /// Gets a value indicating whether the control should be clipped to its bounds.
///
bool ClipToBounds { get; set; }
@@ -47,47 +47,47 @@ namespace Avalonia.VisualTree
Geometry Clip { get; set; }
///
- /// Gets a value indicating whether this scene graph node is attached to a visual root.
+ /// Gets a value indicating whether this control is attached to a visual root.
///
bool IsAttachedToVisualTree { get; }
///
- /// Gets a value indicating whether this scene graph node and all its parents are visible.
+ /// Gets a value indicating whether this control and all its parents are visible.
///
bool IsEffectivelyVisible { get; }
///
- /// Gets or sets a value indicating whether this scene graph node is visible.
+ /// Gets or sets a value indicating whether this control is visible.
///
bool IsVisible { get; set; }
///
- /// Gets or sets the opacity of the scene graph node.
+ /// Gets or sets the opacity of the control.
///
double Opacity { get; set; }
///
- /// Gets or sets the opacity mask of the scene graph node.
+ /// Gets or sets the opacity mask for the control.
///
IBrush OpacityMask { get; set; }
///
- /// Gets or sets the render transform of the scene graph node.
+ /// Gets or sets the render transform of the control.
///
Transform RenderTransform { get; set; }
///
- /// Gets or sets the render transform origin of the scene graph node.
+ /// Gets or sets the render transform origin of the control.
///
RelativePoint RenderTransformOrigin { get; set; }
///
- /// Gets the scene graph node's child nodes.
+ /// Gets the control's child visuals.
///
IAvaloniaReadOnlyList VisualChildren { get; }
///
- /// Gets the scene graph node's parent node.
+ /// Gets the control's parent visual.
///
IVisual VisualParent { get; }
@@ -107,7 +107,7 @@ namespace Avalonia.VisualTree
void InvalidateVisual();
///
- /// Renders the scene graph node to a .
+ /// Renders the control to a .
///
/// The context.
void Render(DrawingContext context);
diff --git a/src/Avalonia.Visuals/VisualTree/VisualExtensions.cs b/src/Avalonia.Visuals/VisualTree/VisualExtensions.cs
index e7876b762f..2af267aa97 100644
--- a/src/Avalonia.Visuals/VisualTree/VisualExtensions.cs
+++ b/src/Avalonia.Visuals/VisualTree/VisualExtensions.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using Avalonia.Rendering;
namespace Avalonia.VisualTree
{
@@ -20,6 +21,8 @@ namespace Avalonia.VisualTree
/// The common ancestor, or null if not found.
public static IVisual FindCommonVisualAncestor(this IVisual visual, IVisual target)
{
+ Contract.Requires(visual != null);
+
return visual.GetSelfAndVisualAncestors().Intersect(target.GetSelfAndVisualAncestors())
.FirstOrDefault();
}
@@ -49,6 +52,8 @@ namespace Avalonia.VisualTree
/// The visual and its ancestors.
public static IEnumerable GetSelfAndVisualAncestors(this IVisual visual)
{
+ Contract.Requires(visual != null);
+
yield return visual;
foreach (var ancestor in visual.GetVisualAncestors())
@@ -102,26 +107,9 @@ namespace Avalonia.VisualTree
{
Contract.Requires(visual != null);
- if (filter?.Invoke(visual) != false)
- {
- bool containsPoint = BoundsTracker.GetTransformedBounds((Visual)visual)?.Contains(p) == true;
-
- if ((containsPoint || !visual.ClipToBounds) && visual.VisualChildren.Any())
- {
- foreach (var child in visual.VisualChildren.SortByZIndex())
- {
- foreach (var result in child.GetVisualsAt(p, filter))
- {
- yield return result;
- }
- }
- }
-
- if (containsPoint)
- {
- yield return visual;
- }
- }
+ var root = visual.GetVisualRoot();
+ p = visual.TranslatePoint(p, root);
+ return root.Renderer.HitTest(p, filter);
}
///
@@ -197,11 +185,11 @@ namespace Avalonia.VisualTree
///
/// The root visual or null if the visual is not rooted.
///
- public static IVisual GetVisualRoot(this IVisual visual)
+ public static IRenderRoot GetVisualRoot(this IVisual visual)
{
Contract.Requires(visual != null);
- return visual.VisualRoot as IVisual;
+ return visual.VisualRoot as IRenderRoot ?? visual.VisualRoot;
}
///
diff --git a/src/Skia/Avalonia.Skia.iOS/Avalonia.Skia.iOS.csproj b/src/Skia/Avalonia.Skia.iOS/Avalonia.Skia.iOS.csproj
index 6b6604b88c..220fb37b81 100644
--- a/src/Skia/Avalonia.Skia.iOS/Avalonia.Skia.iOS.csproj
+++ b/src/Skia/Avalonia.Skia.iOS/Avalonia.Skia.iOS.csproj
@@ -37,10 +37,8 @@
true
-
-
diff --git a/src/Skia/Avalonia.Skia.iOS/RenderTarget.cs b/src/Skia/Avalonia.Skia.iOS/RenderTarget.cs
deleted file mode 100644
index d097f35ce1..0000000000
--- a/src/Skia/Avalonia.Skia.iOS/RenderTarget.cs
+++ /dev/null
@@ -1,133 +0,0 @@
-using System;
-using Avalonia.Media;
-using Avalonia.Platform;
-using SkiaSharp;
-using CoreGraphics;
-using UIKit;
-
-namespace Avalonia.Skia
-{
- internal partial class RenderTarget : IRenderTarget
- {
- public SKSurface Surface { get; protected set; }
-
- public virtual DrawingContext CreateDrawingContext()
- {
- return
- new DrawingContext(
- new DrawingContextImpl(Surface.Canvas));
- }
-
- public void Dispose()
- {
- // Nothing to do here.
- }
- }
-
- internal class WindowRenderTarget : RenderTarget
- {
-
- SKBitmap _bitmap;
- int Width { get; set; }
- int Height { get; set; }
-
- public WindowRenderTarget()
- {
- FixSize();
- }
-
- private CGRect GetApplicationFrame()
- {
- // if we are excluding Status Bar then we use ApplicationFrame
- // otherwise we use full screen bounds. Note that this must also match
- // the Skia/AvaloniaView!!!
- //
- bool excludeStatusArea = false; // TODO: make this configurable later
- if (excludeStatusArea)
- {
- return UIScreen.MainScreen.ApplicationFrame;
- }
- else
- {
- return UIScreen.MainScreen.Bounds;
- }
- }
-
- private void FixSize()
- {
- int width, height;
- GetPlatformWindowSize(out width, out height);
- if (Width == width && Height == height)
- return;
-
- Width = width;
- Height = height;
-
- if (Surface != null)
- {
- Surface.Dispose();
- }
-
- if (_bitmap != null)
- {
- _bitmap.Dispose();
- }
-
- _bitmap = new SKBitmap(width, height, SKImageInfo.PlatformColorType, SKAlphaType.Premul);
-
- IntPtr length;
- var pixels = _bitmap.GetPixels(out length);
-
- // Wrap the bitmap in a Surface and keep it cached
- Surface = SKSurface.Create(_bitmap.Info, pixels, _bitmap.RowBytes);
- }
-
- private void GetPlatformWindowSize(out int w, out int h)
- {
- var bounds = GetApplicationFrame();
- w = (int)bounds.Width;
- h = (int)bounds.Height;
- }
-
- public override DrawingContext CreateDrawingContext()
- {
- FixSize();
-
- var canvas = Surface.Canvas;
- canvas.RestoreToCount(0);
- canvas.Save();
-
- var screenScale = UIScreen.MainScreen.Scale;
- canvas.Scale((float)screenScale, (float)screenScale);
-
- canvas.Clear(SKColors.Red);
- canvas.ResetMatrix();
-
- return new DrawingContext(new WindowDrawingContextImpl(this));
- }
-
- public void Present()
- {
- _bitmap.LockPixels();
- IntPtr length;
- var pixels = _bitmap.GetPixels(out length);
-
- const int bitmapInfo = ((int)CGBitmapFlags.ByteOrder32Big) | ((int)CGImageAlphaInfo.PremultipliedLast);
- var bounds = GetApplicationFrame();
- var statusBarOffset = UIScreen.MainScreen.Bounds.Height - bounds.Height;
-
- using (var colorSpace = CGColorSpace.CreateDeviceRGB())
- using (var bContext = new CGBitmapContext(pixels, _bitmap.Width, _bitmap.Height, 8, _bitmap.Width * 4, colorSpace, (CGImageAlphaInfo)bitmapInfo))
- using (var image = bContext.ToImage())
- using (var context = UIGraphics.GetCurrentContext())
- {
- // flip the image for CGContext.DrawImage
- context.TranslateCTM(0, bounds.Height + statusBarOffset);
- context.ScaleCTM(1, -1);
- context.DrawImage(bounds, image);
- }
-
- _bitmap.UnlockPixels();
- }
- }
-}
diff --git a/src/Skia/Avalonia.Skia.iOS/WindowDrawingContextImpl.cs b/src/Skia/Avalonia.Skia.iOS/WindowDrawingContextImpl.cs
deleted file mode 100644
index db9d10a346..0000000000
--- a/src/Skia/Avalonia.Skia.iOS/WindowDrawingContextImpl.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-
-namespace Avalonia.Skia
-{
-#if !DESKTOP
- // not sure we need this yet
- internal class WindowDrawingContextImpl : DrawingContextImpl
- {
- WindowRenderTarget _target;
-
- public WindowDrawingContextImpl(WindowRenderTarget target)
- : base(target.Surface.Canvas)
- {
- _target = target;
- }
-
- public override void Dispose()
- {
- base.Dispose();
- _target.Present();
- }
- }
-#endif
-}
\ No newline at end of file
diff --git a/src/Skia/Avalonia.Skia/BitmapImpl.cs b/src/Skia/Avalonia.Skia/BitmapImpl.cs
index 9d46855a58..e9c241b848 100644
--- a/src/Skia/Avalonia.Skia/BitmapImpl.cs
+++ b/src/Skia/Avalonia.Skia/BitmapImpl.cs
@@ -30,6 +30,7 @@ namespace Avalonia.Skia
if (runtime?.IsDesktop == true && runtime?.OperatingSystem == OperatingSystemType.Linux)
colorType = SKColorType.Bgra8888;
Bitmap = new SKBitmap(width, height, colorType, SKAlphaType.Premul);
+ Bitmap.Erase(SKColor.Empty);
}
public void Dispose()
diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs
index e1fffd58d4..e817dd4812 100644
--- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs
+++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs
@@ -17,8 +17,7 @@ namespace Avalonia.Direct2D1.Media
public class WicBitmapImpl : BitmapImpl
{
private readonly ImagingFactory _factory;
-
- private SharpDX.Direct2D1.Bitmap _direct2D;
+
///
/// Initializes a new instance of the class.
@@ -101,7 +100,6 @@ namespace Avalonia.Direct2D1.Media
public override void Dispose()
{
WicImpl.Dispose();
- _direct2D?.Dispose();
}
///
diff --git a/tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs b/tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs
index d1f8d5b912..d6ffb32d1c 100644
--- a/tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs
@@ -198,7 +198,7 @@ namespace Avalonia.Controls.UnitTests
public bool IsClosed { get; private set; }
public TestWindowBase()
- : base(Mock.Of())
+ : base(Mock.Of(x => x.Scaling == 1))
{
}
diff --git a/tests/Avalonia.LeakTests/ControlTests.cs b/tests/Avalonia.LeakTests/ControlTests.cs
index 1aaba9cf6b..b9bdee09e6 100644
--- a/tests/Avalonia.LeakTests/ControlTests.cs
+++ b/tests/Avalonia.LeakTests/ControlTests.cs
@@ -4,18 +4,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using JetBrains.dotMemoryUnit;
-using Avalonia.Collections;
using Avalonia.Controls;
-using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Diagnostics;
using Avalonia.Layout;
using Avalonia.Platform;
using Avalonia.Rendering;
-using Avalonia.Styling;
using Avalonia.UnitTests;
using Avalonia.VisualTree;
+using JetBrains.dotMemoryUnit;
using Moq;
using Xunit;
using Xunit.Abstractions;
@@ -33,7 +30,7 @@ namespace Avalonia.LeakTests
[Fact]
public void Canvas_Is_Freed()
{
- using (UnitTestApplication.Start(TestServices.StyledWindow))
+ using (Start())
{
Func run = () =>
{
@@ -57,7 +54,6 @@ namespace Avalonia.LeakTests
};
var result = run();
- PurgeMoqReferences();
dotMemory.Check(memory =>
Assert.Equal(0, memory.GetObjects(where => where.Type.Is