Browse Source

Merge branch 'master' into issues/2000

pull/2001/head
Steven Kirk 8 years ago
committed by GitHub
parent
commit
2b195de4a0
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      azure-pipelines.yml
  2. 1
      build.cake
  3. 2
      build/SharedVersion.props
  4. 4
      native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/xcshareddata/xcschemes/Avalonia.Native.OSX.xcscheme
  5. 7
      packages/Avalonia/Avalonia.csproj
  6. 6
      packages/Avalonia/Avalonia.props
  7. 17
      parameters.cake
  8. 2
      samples/ControlCatalog/Pages/LayoutTransformControlPage.xaml
  9. 7
      src/Avalonia.Base/Collections/AvaloniaListExtensions.cs
  10. 8
      src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs
  11. 1
      src/Avalonia.Native/AvaloniaNativePlatform.cs
  12. 4
      src/Avalonia.Native/GlPlatformFeature.cs
  13. 5
      src/Avalonia.OpenGL/EglGlPlatformSurface.cs
  14. 5
      src/Avalonia.OpenGL/IGlPlatformSurfaceRenderingSession.cs
  15. 2
      src/Avalonia.Themes.Default/Accents/BaseLight.xaml
  16. 2
      src/Avalonia.Visuals/Platform/LockedFramebuffer.cs
  17. 16
      src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs
  18. 23
      src/Avalonia.Visuals/Visual.cs
  19. 4
      src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs
  20. 7
      src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs
  21. 2
      src/Skia/Avalonia.Skia/GlRenderTarget.cs
  22. 2
      src/Windows/Avalonia.Win32/WindowFramebuffer.cs
  23. 6
      src/Windows/Avalonia.Win32/WindowImpl.cs
  24. 29
      tests/Avalonia.Base.UnitTests/Collections/AvaloniaListExtenionsTests.cs
  25. 98
      tests/Avalonia.Visuals.UnitTests/Rendering/ImmediateRendererTests.cs
  26. 44
      tests/Avalonia.Visuals.UnitTests/VisualTests.cs

3
azure-pipelines.yml

@ -29,6 +29,7 @@ jobs:
inputs: inputs:
testResultsFormat: 'VSTest' testResultsFormat: 'VSTest'
testResultsFiles: '$(Build.SourcesDirectory)/artifacts/test-results/*.trx' testResultsFiles: '$(Build.SourcesDirectory)/artifacts/test-results/*.trx'
condition: not(canceled())
- job: macOS - job: macOS
pool: pool:
@ -77,6 +78,7 @@ jobs:
inputs: inputs:
testResultsFormat: 'VSTest' testResultsFormat: 'VSTest'
testResultsFiles: '$(Build.SourcesDirectory)/artifacts/test-results/*.trx' testResultsFiles: '$(Build.SourcesDirectory)/artifacts/test-results/*.trx'
condition: not(canceled())
- task: PublishBuildArtifacts@1 - task: PublishBuildArtifacts@1
inputs: inputs:
@ -111,6 +113,7 @@ jobs:
inputs: inputs:
testResultsFormat: 'VSTest' testResultsFormat: 'VSTest'
testResultsFiles: '$(Build.SourcesDirectory)/artifacts/test-results/*.trx' testResultsFiles: '$(Build.SourcesDirectory)/artifacts/test-results/*.trx'
condition: not(canceled())
- task: PublishBuildArtifacts@1 - task: PublishBuildArtifacts@1
inputs: inputs:

1
build.cake

@ -50,6 +50,7 @@ Setup<Parameters>(context =>
Information("IsPullRequest: " + parameters.IsPullRequest); Information("IsPullRequest: " + parameters.IsPullRequest);
Information("IsMainRepo: " + parameters.IsMainRepo); Information("IsMainRepo: " + parameters.IsMainRepo);
Information("IsMasterBranch: " + parameters.IsMasterBranch); Information("IsMasterBranch: " + parameters.IsMasterBranch);
Information("IsReleaseBranch: " + parameters.IsReleaseBranch);
Information("IsTagged: " + parameters.IsTagged); Information("IsTagged: " + parameters.IsTagged);
Information("IsReleasable: " + parameters.IsReleasable); Information("IsReleasable: " + parameters.IsReleasable);
Information("IsMyGetRelease: " + parameters.IsMyGetRelease); Information("IsMyGetRelease: " + parameters.IsMyGetRelease);

2
build/SharedVersion.props

@ -2,7 +2,7 @@
xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup> <PropertyGroup>
<Product>Avalonia</Product> <Product>Avalonia</Product>
<Version>0.7.0</Version> <Version>0.7.1</Version>
<Copyright>Copyright 2018 &#169; The AvaloniaUI Project</Copyright> <Copyright>Copyright 2018 &#169; The AvaloniaUI Project</Copyright>
<PackageLicenseUrl>https://github.com/AvaloniaUI/Avalonia/blob/master/licence.md</PackageLicenseUrl> <PackageLicenseUrl>https://github.com/AvaloniaUI/Avalonia/blob/master/licence.md</PackageLicenseUrl>
<PackageProjectUrl>https://github.com/AvaloniaUI/Avalonia/</PackageProjectUrl> <PackageProjectUrl>https://github.com/AvaloniaUI/Avalonia/</PackageProjectUrl>

4
native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/xcshareddata/xcschemes/Avalonia.Native.OSX.xcscheme

@ -38,7 +38,7 @@
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0" launchStyle = "0"
useCustomWorkingDirectory = "YES" useCustomWorkingDirectory = "YES"
customWorkingDirectory = "$PROJECT_DIR/../../../../samples/ControlCatalog" customWorkingDirectory = "$PROJECT_DIR/../../../../samples/ControlCatalog.NetCore"
ignoresPersistentStateOnLaunch = "NO" ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES" debugDocumentVersioning = "YES"
debugServiceExtension = "internal" debugServiceExtension = "internal"
@ -58,7 +58,7 @@
</MacroExpansion> </MacroExpansion>
<CommandLineArguments> <CommandLineArguments>
<CommandLineArgument <CommandLineArgument
argument = "bin/Debug/netcoreapp2.0/ControlCatalog.dll" argument = "bin/Debug/netcoreapp2.0/ControlCatalog.NetCore.dll"
isEnabled = "YES"> isEnabled = "YES">
</CommandLineArgument> </CommandLineArgument>
</CommandLineArguments> </CommandLineArguments>

7
packages/Avalonia/Avalonia.csproj

@ -1,4 +1,4 @@
<Project Sdk="AggregatePackage.NuGet.Sdk"> <Project Sdk="AggregatePackage.NuGet.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard2.0;net461;netcoreapp2.0</TargetFrameworks> <TargetFrameworks>netstandard2.0;net461;netcoreapp2.0</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
@ -27,6 +27,11 @@
<Visible>false</Visible> <Visible>false</Visible>
<BuildAction>None</BuildAction> <BuildAction>None</BuildAction>
</_PackageFiles> </_PackageFiles>
<_PackageFiles Include="Avalonia.props">
<PackagePath>build/Avalonia.props</PackagePath>
<Visible>false</Visible>
<BuildAction>None</BuildAction>
</_PackageFiles>
</ItemGroup> </ItemGroup>
</Target> </Target>

6
packages/Avalonia/Avalonia.props

@ -0,0 +1,6 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<AvaloniaPreviewerNetCoreToolPath>$(MSBuildThisFileDirectory)\..\tools\netcoreapp2.0\designer\Avalonia.Designer.HostApp.dll</AvaloniaPreviewerNetCoreToolPath>
<AvaloniaPreviewerNetFullToolPath>$(MSBuildThisFileDirectory)\..\tools\net461\designer\Avalonia.Designer.HostApp.exe</AvaloniaPreviewerNetFullToolPath>
</PropertyGroup>
</Project>

17
parameters.cake

@ -9,6 +9,7 @@ public class Parameters
public string MasterBranch { get; private set; } public string MasterBranch { get; private set; }
public string ReleasePlatform { get; private set; } public string ReleasePlatform { get; private set; }
public string ReleaseConfiguration { get; private set; } public string ReleaseConfiguration { get; private set; }
public string ReleaseBranchPrefix { get; private set; }
public string MSBuildSolution { get; private set; } public string MSBuildSolution { get; private set; }
public bool IsLocalBuild { get; private set; } public bool IsLocalBuild { get; private set; }
public bool IsRunningOnUnix { get; private set; } public bool IsRunningOnUnix { get; private set; }
@ -18,6 +19,7 @@ public class Parameters
public bool IsPullRequest { get; private set; } public bool IsPullRequest { get; private set; }
public bool IsMainRepo { get; private set; } public bool IsMainRepo { get; private set; }
public bool IsMasterBranch { get; private set; } public bool IsMasterBranch { get; private set; }
public bool IsReleaseBranch { get; private set; }
public bool IsTagged { get; private set; } public bool IsTagged { get; private set; }
public bool IsReleasable { get; private set; } public bool IsReleasable { get; private set; }
public bool IsMyGetRelease { get; private set; } public bool IsMyGetRelease { get; private set; }
@ -46,8 +48,9 @@ public class Parameters
SkipTests = context.HasArgument("skip-tests"); SkipTests = context.HasArgument("skip-tests");
// CONFIGURATION // CONFIGURATION
MainRepo = "AvaloniaUI/Avalonia"; MainRepo = "https://github.com/AvaloniaUI/Avalonia";
MasterBranch = "master"; MasterBranch = "master";
ReleaseBranchPrefix = "refs/heads/release/";
ReleaseConfiguration = "Release"; ReleaseConfiguration = "Release";
MSBuildSolution = "./dirs.proj"; MSBuildSolution = "./dirs.proj";
@ -59,12 +62,14 @@ public class Parameters
IsRunningOnAzure = buildSystem.IsRunningOnVSTS || buildSystem.IsRunningOnTFS || context.EnvironmentVariable("LOGNAME") == "vsts"; IsRunningOnAzure = buildSystem.IsRunningOnVSTS || buildSystem.IsRunningOnTFS || context.EnvironmentVariable("LOGNAME") == "vsts";
IsPullRequest = buildSystem.AppVeyor.Environment.PullRequest.IsPullRequest; IsPullRequest = buildSystem.AppVeyor.Environment.PullRequest.IsPullRequest;
IsMainRepo = StringComparer.OrdinalIgnoreCase.Equals(MainRepo, buildSystem.AppVeyor.Environment.Repository.Name); IsMainRepo = StringComparer.OrdinalIgnoreCase.Equals(MainRepo, context.EnvironmentVariable("BUILD_REPOSITORY_URI"));
IsMasterBranch = StringComparer.OrdinalIgnoreCase.Equals(MasterBranch, buildSystem.AppVeyor.Environment.Repository.Branch); IsMasterBranch = StringComparer.OrdinalIgnoreCase.Equals(MasterBranch, context.EnvironmentVariable("BUILD_SOURCEBRANCHNAME"));
IsReleaseBranch = context.EnvironmentVariable("BUILD_SOURCEBRANCH").ToLower().StartsWith(ReleaseBranchPrefix.ToLower());
IsTagged = buildSystem.AppVeyor.Environment.Repository.Tag.IsTag IsTagged = buildSystem.AppVeyor.Environment.Repository.Tag.IsTag
&& !string.IsNullOrWhiteSpace(buildSystem.AppVeyor.Environment.Repository.Tag.Name); && !string.IsNullOrWhiteSpace(buildSystem.AppVeyor.Environment.Repository.Tag.Name);
IsReleasable = StringComparer.OrdinalIgnoreCase.Equals(ReleaseConfiguration, Configuration); IsReleasable = StringComparer.OrdinalIgnoreCase.Equals(ReleaseConfiguration, Configuration);
IsMyGetRelease = !IsTagged && IsReleasable; IsMyGetRelease = !IsTagged && IsReleasable;
IsNuGetRelease = IsMainRepo && IsReleasable && IsReleaseBranch;
// VERSION // VERSION
Version = context.Argument("force-nuget-version", GetVersion()); Version = context.Argument("force-nuget-version", GetVersion());
@ -92,9 +97,13 @@ public class Parameters
} }
else if (IsRunningOnAzure) else if (IsRunningOnAzure)
{ {
if(!IsNuGetRelease)
{
// Use AssemblyVersion with Build as version // Use AssemblyVersion with Build as version
Version += "-build" + context.EnvironmentVariable("BUILD_BUILDID") + "-beta"; Version += "-build" + context.EnvironmentVariable("BUILD_BUILDID") + "-beta";
PublishTestResults = true; }
PublishTestResults = true;
} }
// DIRECTORIES // DIRECTORIES

2
samples/ControlCatalog/Pages/LayoutTransformControlPage.xaml

@ -19,7 +19,7 @@
<LayoutTransformControl.LayoutTransform> <LayoutTransformControl.LayoutTransform>
<RotateTransform Angle="{Binding #rotation.Value}"/> <RotateTransform Angle="{Binding #rotation.Value}"/>
</LayoutTransformControl.LayoutTransform> </LayoutTransformControl.LayoutTransform>
<TextBlock>Layout Transform</TextBlock> <Button Background="White">Layout Transform</Button>
</LayoutTransformControl> </LayoutTransformControl>
</Grid> </Grid>
</DockPanel> </DockPanel>

7
src/Avalonia.Base/Collections/AvaloniaListExtensions.cs

@ -104,7 +104,12 @@ namespace Avalonia.Collections
case NotifyCollectionChangedAction.Move: case NotifyCollectionChangedAction.Move:
case NotifyCollectionChangedAction.Replace: case NotifyCollectionChangedAction.Replace:
Remove(e.OldStartingIndex, e.OldItems); Remove(e.OldStartingIndex, e.OldItems);
Add(e.NewStartingIndex, e.NewItems); int newIndex = e.NewStartingIndex;
if(newIndex > e.OldStartingIndex)
{
newIndex -= e.OldItems.Count;
}
Add(newIndex, e.NewItems);
break; break;
case NotifyCollectionChangedAction.Remove: case NotifyCollectionChangedAction.Remove:

8
src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs

@ -211,10 +211,10 @@ namespace Avalonia.Controls.Remote.Server
} }
if(obj is KeyEventMessage key) if(obj is KeyEventMessage key)
{ {
Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1);
Dispatcher.UIThread.Post(() => Dispatcher.UIThread.Post(() =>
{ {
Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1);
Input?.Invoke(new RawKeyEventArgs( Input?.Invoke(new RawKeyEventArgs(
KeyboardDevice, KeyboardDevice,
0, 0,
@ -225,10 +225,10 @@ namespace Avalonia.Controls.Remote.Server
} }
if(obj is TextInputEventMessage text) if(obj is TextInputEventMessage text)
{ {
Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1);
Dispatcher.UIThread.Post(() => Dispatcher.UIThread.Post(() =>
{ {
Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1);
Input?.Invoke(new RawTextInputEventArgs( Input?.Invoke(new RawTextInputEventArgs(
KeyboardDevice, KeyboardDevice,
0, 0,

1
src/Avalonia.Native/AvaloniaNativePlatform.cs

@ -76,6 +76,7 @@ namespace Avalonia.Native
.Bind<IRenderTimer>().ToConstant(new DefaultRenderTimer(60)) .Bind<IRenderTimer>().ToConstant(new DefaultRenderTimer(60))
.Bind<ISystemDialogImpl>().ToConstant(new SystemDialogs(_factory.CreateSystemDialogs())) .Bind<ISystemDialogImpl>().ToConstant(new SystemDialogs(_factory.CreateSystemDialogs()))
.Bind<IWindowingPlatformGlFeature>().ToConstant(new GlPlatformFeature(_factory.ObtainGlFeature())) .Bind<IWindowingPlatformGlFeature>().ToConstant(new GlPlatformFeature(_factory.ObtainGlFeature()))
.Bind<PlatformHotkeyConfiguration>().ToConstant(new PlatformHotkeyConfiguration(InputModifiers.Windows))
.Bind<AvaloniaNativeOptions>().ToConstant(opts); .Bind<AvaloniaNativeOptions>().ToConstant(opts);
} }

4
src/Avalonia.Native/GlPlatformFeature.cs

@ -100,12 +100,12 @@ namespace Avalonia.Native
public IGlDisplay Display { get; } public IGlDisplay Display { get; }
public System.Drawing.Size PixelSize public PixelSize Size
{ {
get get
{ {
var s = _session.GetPixelSize(); var s = _session.GetPixelSize();
return new System.Drawing.Size(s.Width, s.Height); return new PixelSize(s.Width, s.Height);
} }
} }

5
src/Avalonia.OpenGL/EglGlPlatformSurface.cs

@ -7,8 +7,7 @@ namespace Avalonia.OpenGL
public interface IEglWindowGlPlatformSurfaceInfo public interface IEglWindowGlPlatformSurfaceInfo
{ {
IntPtr Handle { get; } IntPtr Handle { get; }
// TODO: Change to PixelSize struct once https://github.com/AvaloniaUI/Avalonia/pull/1889 is merged PixelSize Size { get; }
System.Drawing.Size PixelSize { get; }
double Scaling { get; } double Scaling { get; }
} }
@ -71,7 +70,7 @@ namespace Avalonia.OpenGL
} }
public IGlDisplay Display => _context.Display; public IGlDisplay Display => _context.Display;
public System.Drawing.Size PixelSize => _info.PixelSize; public PixelSize Size => _info.Size;
public double Scaling => _info.Scaling; public double Scaling => _info.Scaling;
} }
} }

5
src/Avalonia.OpenGL/IGlPlatformSurfaceRenderingSession.cs

@ -5,8 +5,7 @@ namespace Avalonia.OpenGL
public interface IGlPlatformSurfaceRenderingSession : IDisposable public interface IGlPlatformSurfaceRenderingSession : IDisposable
{ {
IGlDisplay Display { get; } IGlDisplay Display { get; }
// TODO: Change to PixelSize struct once https://github.com/AvaloniaUI/Avalonia/pull/1889 is merged PixelSize Size { get; }
System.Drawing.Size PixelSize { get; }
double Scaling { get; } double Scaling { get; }
} }
} }

2
src/Avalonia.Themes.Default/Accents/BaseLight.xaml

@ -46,7 +46,7 @@
<SolidColorBrush x:Key="ErrorBrush" Color="{DynamicResource ErrorColor}"></SolidColorBrush> <SolidColorBrush x:Key="ErrorBrush" Color="{DynamicResource ErrorColor}"></SolidColorBrush>
<SolidColorBrush x:Key="ErrorLowBrush" Color="{DynamicResource ErrorLowColor}"></SolidColorBrush> <SolidColorBrush x:Key="ErrorLowBrush" Color="{DynamicResource ErrorLowColor}"></SolidColorBrush>
<Thickness x:Key="ThemeBorderThickness">2</Thickness> <Thickness x:Key="ThemeBorderThickness">1</Thickness>
<sys:Double x:Key="ThemeDisabledOpacity">0.5</sys:Double> <sys:Double x:Key="ThemeDisabledOpacity">0.5</sys:Double>
<sys:Double x:Key="FontSizeSmall">10</sys:Double> <sys:Double x:Key="FontSizeSmall">10</sys:Double>

2
src/Avalonia.Visuals/Platform/LockedFramebuffer.cs

@ -11,7 +11,7 @@ namespace Avalonia.Platform
{ {
_onDispose = onDispose; _onDispose = onDispose;
Address = address; Address = address;
Size = Size; Size = size;
RowBytes = rowBytes; RowBytes = rowBytes;
Dpi = dpi; Dpi = dpi;
Format = format; Format = format;

16
src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs

@ -125,6 +125,20 @@ namespace Avalonia.Rendering
if (m.HasValue) if (m.HasValue)
{ {
var bounds = new Rect(visual.Bounds.Size).TransformToAABB(m.Value); var bounds = new Rect(visual.Bounds.Size).TransformToAABB(m.Value);
//use transformedbounds as previous render state of the visual bounds
//so we can invalidate old and new bounds of a control in case it moved/shrinked
if (visual.TransformedBounds.HasValue)
{
var trb = visual.TransformedBounds.Value;
var trBounds = trb.Bounds.TransformToAABB(trb.Transform);
if (trBounds != bounds)
{
_renderRoot?.Invalidate(trBounds);
}
}
_renderRoot?.Invalidate(bounds); _renderRoot?.Invalidate(bounds);
} }
} }
@ -191,7 +205,7 @@ namespace Avalonia.Rendering
} }
} }
static IEnumerable<IVisual> HitTest( private static IEnumerable<IVisual> HitTest(
IVisual visual, IVisual visual,
Point p, Point p,
Func<IVisual, bool> filter) Func<IVisual, bool> filter)

23
src/Avalonia.Visuals/Visual.cs

@ -304,7 +304,7 @@ namespace Avalonia
{ {
var thisOffset = GetOffsetFrom(common, this); var thisOffset = GetOffsetFrom(common, this);
var thatOffset = GetOffsetFrom(common, visual); var thatOffset = GetOffsetFrom(common, visual);
return Matrix.CreateTranslation(-thatOffset) * Matrix.CreateTranslation(thisOffset); return -thatOffset * thisOffset;
} }
return null; return null;
@ -454,13 +454,28 @@ namespace Avalonia
/// <param name="ancestor">The ancestor visual.</param> /// <param name="ancestor">The ancestor visual.</param>
/// <param name="visual">The visual.</param> /// <param name="visual">The visual.</param>
/// <returns>The visual offset.</returns> /// <returns>The visual offset.</returns>
private static Vector GetOffsetFrom(IVisual ancestor, IVisual visual) private static Matrix GetOffsetFrom(IVisual ancestor, IVisual visual)
{ {
var result = new Vector(); var result = Matrix.Identity;
while (visual != ancestor) while (visual != ancestor)
{ {
result = new Vector(result.X + visual.Bounds.X, result.Y + visual.Bounds.Y); if (visual.RenderTransform?.Value != null)
{
var origin = visual.RenderTransformOrigin.ToPixels(visual.Bounds.Size);
var offset = Matrix.CreateTranslation(origin);
var renderTransform = (-offset) * visual.RenderTransform.Value * (offset);
result *= renderTransform;
}
var topLeft = visual.Bounds.TopLeft;
if (topLeft != default)
{
result *= Matrix.CreateTranslation(topLeft);
}
visual = visual.VisualParent; visual = visual.VisualParent;
if (visual == null) if (visual == null)

4
src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs

@ -510,12 +510,12 @@ namespace Avalonia.Gtk3
: new ImmediateRenderer(root); : new ImmediateRenderer(root);
} }
System.Drawing.Size EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo.PixelSize PixelSize EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo.Size
{ {
get get
{ {
var cs = ClientSize; var cs = ClientSize;
return new System.Drawing.Size((int)Math.Max(1, LastKnownScaleFactor * cs.Width), return new PixelSize((int)Math.Max(1, LastKnownScaleFactor * cs.Width),
(int)Math.Max(1, LastKnownScaleFactor * ClientSize.Height)); (int)Math.Max(1, LastKnownScaleFactor * ClientSize.Height));
} }
} }

7
src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs

@ -143,23 +143,26 @@ namespace Avalonia.Skia
// Create bitmap using default platform settings // Create bitmap using default platform settings
_bitmap = new SKBitmap(destinationInfo.Width, destinationInfo.Height); _bitmap = new SKBitmap(destinationInfo.Width, destinationInfo.Height);
SKColorType bitmapColorType;
if (!_bitmap.CanCopyTo(destinationInfo.ColorType)) if (!_bitmap.CanCopyTo(destinationInfo.ColorType))
{ {
bitmapColorType = _bitmap.ColorType;
_bitmap.Dispose(); _bitmap.Dispose();
throw new Exception( throw new Exception(
$"Unable to create pixel format shim for conversion from {_bitmap.ColorType} to {destinationInfo.ColorType}"); $"Unable to create pixel format shim for conversion from {bitmapColorType} to {destinationInfo.ColorType}");
} }
Surface = SKSurface.Create(_bitmap.Info, _bitmap.GetPixels(), _bitmap.RowBytes); Surface = SKSurface.Create(_bitmap.Info, _bitmap.GetPixels(), _bitmap.RowBytes);
if (Surface == null) if (Surface == null)
{ {
bitmapColorType = _bitmap.ColorType;
_bitmap.Dispose(); _bitmap.Dispose();
throw new Exception( throw new Exception(
$"Unable to create pixel format shim surface for conversion from {_bitmap.ColorType} to {destinationInfo.ColorType}"); $"Unable to create pixel format shim surface for conversion from {bitmapColorType} to {destinationInfo.ColorType}");
} }
SurfaceCopyHandler = Disposable.Create(CopySurface); SurfaceCopyHandler = Disposable.Create(CopySurface);

2
src/Skia/Avalonia.Skia/GlRenderTarget.cs

@ -28,7 +28,7 @@ namespace Avalonia.Skia
var gl = disp.GlInterface; var gl = disp.GlInterface;
gl.GetIntegerv(GL_FRAMEBUFFER_BINDING, out var fb); gl.GetIntegerv(GL_FRAMEBUFFER_BINDING, out var fb);
var size = session.PixelSize; var size = session.Size;
var scaling = session.Scaling; var scaling = session.Scaling;
GRBackendRenderTargetDesc desc = new GRBackendRenderTargetDesc GRBackendRenderTargetDesc desc = new GRBackendRenderTargetDesc

2
src/Windows/Avalonia.Win32/WindowFramebuffer.cs

@ -61,7 +61,7 @@ namespace Avalonia.Win32
} }
} }
public PixelSize Size => new PixelSize(_bmpInfo.biWidth, _bmpInfo.biHeight); public PixelSize Size => new PixelSize(_bmpInfo.biWidth, -_bmpInfo.biHeight);
public void DrawToDevice(IntPtr hDC, int destX = 0, int destY = 0, int srcX = 0, int srcY = 0, int width = -1, public void DrawToDevice(IntPtr hDC, int destX = 0, int destY = 0, int srcX = 0, int srcY = 0, int width = -1,
int height = -1) int height = -1)

6
src/Windows/Avalonia.Win32/WindowImpl.cs

@ -933,14 +933,14 @@ namespace Avalonia.Win32
_topmost = value; _topmost = value;
} }
System.Drawing.Size EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo.PixelSize PixelSize EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo.Size
{ {
get get
{ {
RECT rect; RECT rect;
GetClientRect(_hwnd, out rect); GetClientRect(_hwnd, out rect);
return new System.Drawing.Size( return new PixelSize(
Math.Max(1, rect.right - rect.left), Math.Max(1, rect.right - rect.left),
Math.Max(1, rect.bottom - rect.top)); Math.Max(1, rect.bottom - rect.top));
} }

29
tests/Avalonia.Base.UnitTests/Collections/AvaloniaListExtenionsTests.cs

@ -1,5 +1,4 @@
using System; using System.Linq;
using System.Linq;
using Avalonia.Collections; using Avalonia.Collections;
using Xunit; using Xunit;
@ -82,13 +81,31 @@ namespace Avalonia.Base.UnitTests.Collections
Assert.Equal(source, result); Assert.Equal(source, result);
} }
[Fact] [Theory]
public void CreateDerivedList_Handles_MoveRange() [InlineData(0, 2, 3)]
[InlineData(0, 2, 4)]
[InlineData(0, 2, 5)]
[InlineData(0, 4, 4)]
[InlineData(1, 2, 0)]
[InlineData(1, 2, 4)]
[InlineData(1, 2, 5)]
[InlineData(1, 4, 0)]
[InlineData(2, 2, 0)]
[InlineData(2, 2, 1)]
[InlineData(2, 2, 3)]
[InlineData(2, 2, 4)]
[InlineData(2, 2, 5)]
[InlineData(4, 2, 0)]
[InlineData(4, 2, 1)]
[InlineData(4, 2, 3)]
[InlineData(5, 1, 0)]
[InlineData(5, 1, 3)]
public void CreateDerivedList_Handles_MoveRange(int oldIndex, int count, int newIndex)
{ {
var source = new AvaloniaList<int>(new[] { 0, 1, 2, 3 }); var source = new AvaloniaList<int>(new[] { 0, 1, 2, 3, 4, 5 });
var target = source.CreateDerivedList(x => new Wrapper(x)); var target = source.CreateDerivedList(x => new Wrapper(x));
source.MoveRange(1, 2, 0); source.MoveRange(oldIndex, count, newIndex);
var result = target.Select(x => x.Value).ToList(); var result = target.Select(x => x.Value).ToList();

98
tests/Avalonia.Visuals.UnitTests/Rendering/ImmediateRendererTests.cs

@ -0,0 +1,98 @@
using System.Collections.Generic;
using Avalonia.Collections;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.VisualTree;
using Moq;
using Xunit;
namespace Avalonia.Visuals.UnitTests.Rendering
{
public class ImmediateRendererTests
{
[Fact]
public void AddDirty_Call_RenderRoot_Invalidate()
{
var visual = new Mock<Visual>();
var child = new Mock<Visual>() { CallBase = true };
var renderRoot = visual.As<IRenderRoot>();
visual.As<IVisual>().Setup(v => v.Bounds).Returns(new Rect(0, 0, 400, 400));
child.As<IVisual>().Setup(v => v.Bounds).Returns(new Rect(10, 10, 100, 100));
child.As<IVisual>().Setup(v => v.VisualParent).Returns(visual.Object);
var target = new ImmediateRenderer(visual.Object);
target.AddDirty(child.Object);
renderRoot.Verify(v => v.Invalidate(new Rect(10, 10, 100, 100)));
}
[Fact]
public void AddDirty_With_RenderTransform_Call_RenderRoot_Invalidate()
{
var visual = new Mock<Visual>();
var child = new Mock<Visual>() { CallBase = true };
var renderRoot = visual.As<IRenderRoot>();
visual.As<IVisual>().Setup(v => v.Bounds).Returns(new Rect(0, 0, 400, 400));
child.As<IVisual>().Setup(v => v.Bounds).Returns(new Rect(100, 100, 100, 100));
child.As<IVisual>().Setup(v => v.VisualParent).Returns(visual.Object);
child.Object.RenderTransform = new ScaleTransform() { ScaleX = 2, ScaleY = 2 };
var target = new ImmediateRenderer(visual.Object);
target.AddDirty(child.Object);
renderRoot.Verify(v => v.Invalidate(new Rect(50, 50, 200, 200)));
}
[Fact]
public void AddDirty_For_Child_Moved_Should_Invalidate_Previous_Bounds()
{
var visual = new Mock<Visual>() { CallBase = true };
var child = new Mock<Visual>() { CallBase = true };
var renderRoot = visual.As<IRenderRoot>();
var renderTarget = visual.As<IRenderTarget>();
renderRoot.Setup(r => r.CreateRenderTarget()).Returns(renderTarget.Object);
renderTarget.Setup(r => r.CreateDrawingContext(It.IsAny<IVisualBrushRenderer>())).Returns(Mock.Of<IDrawingContextImpl>());
visual.As<IVisual>().Setup(v => v.Bounds).Returns(new Rect(0, 0, 400, 400));
visual.As<IVisual>().Setup(v => v.VisualChildren).Returns(new AvaloniaList<IVisual>() { child.As<IVisual>().Object });
Rect childBounds = new Rect(0, 0, 100, 100);
child.As<IVisual>().Setup(v => v.Bounds).Returns(() => childBounds);
child.As<IVisual>().Setup(v => v.VisualParent).Returns(visual.Object);
child.As<IVisual>().Setup(v => v.VisualChildren).Returns(new AvaloniaList<IVisual>());
var invalidationCalls = new List<Rect>();
renderRoot.Setup(v => v.Invalidate(It.IsAny<Rect>())).Callback<Rect>(v => invalidationCalls.Add(v));
var target = new ImmediateRenderer(visual.Object);
target.AddDirty(child.Object);
Assert.Equal(new Rect(0, 0, 100, 100), invalidationCalls[0]);
target.Paint(new Rect(0, 0, 100, 100));
//move child 100 pixels bottom/right
childBounds = new Rect(100, 100, 100, 100);
//renderer should invalidate old child bounds with new one
//as on old area there can be artifacts
target.AddDirty(child.Object);
//invalidate first old position
Assert.Equal(new Rect(0, 0, 100, 100), invalidationCalls[1]);
//then new position
Assert.Equal(new Rect(100, 100, 100, 100), invalidationCalls[2]);
}
}
}

44
tests/Avalonia.Visuals.UnitTests/VisualTests.cs

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Media;
using Avalonia.Rendering; using Avalonia.Rendering;
using Avalonia.UnitTests; using Avalonia.UnitTests;
using Avalonia.VisualTree; using Avalonia.VisualTree;
@ -192,5 +193,48 @@ namespace Avalonia.Visuals.UnitTests
Assert.Throws<InvalidOperationException>(() => root2.Child = child); Assert.Throws<InvalidOperationException>(() => root2.Child = child);
Assert.Empty(root2.GetVisualChildren()); Assert.Empty(root2.GetVisualChildren());
} }
[Fact]
public void TransformToVisual_Should_Work()
{
var child = new Decorator { Width = 100, Height = 100 };
var root = new TestRoot() { Child = child, Width = 400, Height = 400 };
root.Measure(Size.Infinity);
root.Arrange(new Rect(new Point(), root.DesiredSize));
var tr = child.TransformToVisual(root);
Assert.NotNull(tr);
var point = root.Bounds.TopLeft * tr;
//child is centered (400 - 100)/2
Assert.Equal(new Point(150, 150), point);
}
[Fact]
public void TransformToVisual_With_RenderTransform_Should_Work()
{
var child = new Decorator
{
Width = 100,
Height = 100,
RenderTransform = new ScaleTransform() { ScaleX = 2, ScaleY = 2 }
};
var root = new TestRoot() { Child = child, Width = 400, Height = 400 };
root.Measure(Size.Infinity);
root.Arrange(new Rect(new Point(), root.DesiredSize));
var tr = child.TransformToVisual(root);
Assert.NotNull(tr);
var point = root.Bounds.TopLeft * tr;
//child is centered (400 - 100*2 scale)/2
Assert.Equal(new Point(100, 100), point);
}
} }
} }

Loading…
Cancel
Save