Browse Source

Merge branch 'master' into fix-win32-right-modifiers

pull/3320/head
Dariusz Komosiński 7 years ago
committed by GitHub
parent
commit
25aade0e1c
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 14
      native/Avalonia.Native/inc/avalonia-native.h
  2. 41
      native/Avalonia.Native/src/OSX/window.mm
  3. 20
      src/Avalonia.Controls/Utils/BorderRenderHelper.cs
  4. 2
      src/Avalonia.Input/IKeyboardDevice.cs
  5. 16
      src/Avalonia.Input/MouseDevice.cs
  6. 22
      src/Avalonia.Input/PointerPoint.cs
  7. 4
      src/Avalonia.Input/Raw/RawPointerEventArgs.cs
  8. 46
      src/Avalonia.Visuals/Media/DrawingContext.cs
  9. 26
      src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs
  10. 2
      src/Avalonia.Visuals/Rendering/DeferredRenderer.cs
  11. 2
      src/Avalonia.Visuals/Rendering/RendererBase.cs
  12. 21
      src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs
  13. 43
      src/Avalonia.Visuals/Rendering/SceneGraph/RectangleNode.cs
  14. 36
      src/Avalonia.X11/X11Window.cs
  15. 20
      src/Avalonia.X11/XI2Manager.cs
  16. 49
      src/Skia/Avalonia.Skia/DrawingContextImpl.cs
  17. 109
      src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs
  18. 52
      src/Windows/Avalonia.Win32/WindowImpl.cs
  19. 10
      tests/Avalonia.RenderTests/Media/BitmapTests.cs
  20. 8
      tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs
  21. 26
      tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/DeferredDrawingContextImplTests.cs

14
native/Avalonia.Native/inc/avalonia-native.h

@ -92,9 +92,17 @@ enum AvnRawMouseEventType
RightButtonUp, RightButtonUp,
MiddleButtonDown, MiddleButtonDown,
MiddleButtonUp, MiddleButtonUp,
XButton1Down,
XButton1Up,
XButton2Down,
XButton2Up,
Move, Move,
Wheel, Wheel,
NonClientLeftButtonDown NonClientLeftButtonDown,
TouchBegin,
TouchUpdate,
TouchEnd,
TouchCancel
}; };
enum AvnRawKeyEventType enum AvnRawKeyEventType
@ -112,7 +120,9 @@ enum AvnInputModifiers
Windows = 8, Windows = 8,
LeftMouseButton = 16, LeftMouseButton = 16,
RightMouseButton = 32, RightMouseButton = 32,
MiddleMouseButton = 64 MiddleMouseButton = 64,
XButton1MouseButton = 128,
XButton2MouseButton = 256
}; };
enum AvnWindowState enum AvnWindowState

41
native/Avalonia.Native/src/OSX/window.mm

@ -716,7 +716,7 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
AvnFramebuffer _swRenderedFrameBuffer; AvnFramebuffer _swRenderedFrameBuffer;
bool _queuedDisplayFromThread; bool _queuedDisplayFromThread;
NSTrackingArea* _area; NSTrackingArea* _area;
bool _isLeftPressed, _isMiddlePressed, _isRightPressed, _isMouseOver; bool _isLeftPressed, _isMiddlePressed, _isRightPressed, _isXButton1Pressed, _isXButton2Pressed, _isMouseOver;
NSEvent* _lastMouseDownEvent; NSEvent* _lastMouseDownEvent;
bool _lastKeyHandled; bool _lastKeyHandled;
} }
@ -942,9 +942,23 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
- (void)otherMouseDown:(NSEvent *)event - (void)otherMouseDown:(NSEvent *)event
{ {
_isMiddlePressed = true;
_lastMouseDownEvent = event; _lastMouseDownEvent = event;
[self mouseEvent:event withType:MiddleButtonDown];
switch(event.buttonNumber)
{
case 3:
_isMiddlePressed = true;
[self mouseEvent:event withType:MiddleButtonDown];
break;
case 4:
_isXButton1Pressed = true;
[self mouseEvent:event withType:XButton1Down];
break;
case 5:
_isXButton2Pressed = true;
[self mouseEvent:event withType:XButton2Down];
break;
}
} }
- (void)rightMouseDown:(NSEvent *)event - (void)rightMouseDown:(NSEvent *)event
@ -962,8 +976,21 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
- (void)otherMouseUp:(NSEvent *)event - (void)otherMouseUp:(NSEvent *)event
{ {
_isMiddlePressed = false; switch(event.buttonNumber)
[self mouseEvent:event withType:MiddleButtonUp]; {
case 3:
_isMiddlePressed = false;
[self mouseEvent:event withType:MiddleButtonUp];
break;
case 4:
_isXButton1Pressed = false;
[self mouseEvent:event withType:XButton1Up];
break;
case 5:
_isXButton2Pressed = false;
[self mouseEvent:event withType:XButton2Up];
break;
}
} }
- (void)rightMouseUp:(NSEvent *)event - (void)rightMouseUp:(NSEvent *)event
@ -1062,6 +1089,10 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
rv |= MiddleMouseButton; rv |= MiddleMouseButton;
if (_isRightPressed) if (_isRightPressed)
rv |= RightMouseButton; rv |= RightMouseButton;
if (_isXButton1Pressed)
rv |= XButton1MouseButton;
if (_isXButton2Pressed)
rv |= XButton2MouseButton;
return (AvnInputModifiers)rv; return (AvnInputModifiers)rv;
} }

20
src/Avalonia.Controls/Utils/BorderRenderHelper.cs

@ -90,23 +90,17 @@ namespace Avalonia.Controls.Utils
{ {
var borderThickness = borders.Top; var borderThickness = borders.Top;
var top = borderThickness * 0.5; var top = borderThickness * 0.5;
var cornerRadius = (float)Math.Max(0, radii.TopLeft - borderThickness - top);
if (background != null) IPen pen = null;
{
var topLeft = new Point(borders.Left, borders.Top);
var bottomRight = new Point(size.Width - borders.Right, size.Height - borders.Bottom);
var innerRect = new Rect(topLeft, bottomRight);
context.FillRectangle(background, innerRect, cornerRadius);
}
if (borderBrush != null && borderThickness > 0) if (borderThickness > 0)
{ {
var topLeft = new Point(top, top); pen = new Pen(borderBrush, borderThickness);
var bottomRight = new Point(size.Width - top, size.Height - top);
var outerRect = new Rect(topLeft, bottomRight);
context.DrawRectangle(new Pen(borderBrush, borderThickness), outerRect, (float)radii.TopLeft);
} }
var rect = new Rect(top, top, size.Width - borderThickness, size.Height - borderThickness);
context.DrawRectangle(background, pen, rect, radii.TopLeft, radii.TopLeft);
} }
} }

2
src/Avalonia.Input/IKeyboardDevice.cs

@ -48,6 +48,8 @@ namespace Avalonia.Input
LeftMouseButton = 16, LeftMouseButton = 16,
RightMouseButton = 32, RightMouseButton = 32,
MiddleMouseButton = 64, MiddleMouseButton = 64,
XButton1MouseButton = 128,
XButton2MouseButton = 256,
KeyboardMask = Alt | Control | Shift | Meta KeyboardMask = Alt | Control | Shift | Meta
} }

16
src/Avalonia.Input/MouseDevice.cs

@ -120,6 +120,10 @@ namespace Avalonia.Input
rv++; rv++;
if (props.IsRightButtonPressed) if (props.IsRightButtonPressed)
rv++; rv++;
if (props.IsXButton1Pressed)
rv++;
if (props.IsXButton2Pressed)
rv++;
return rv; return rv;
} }
@ -142,6 +146,8 @@ namespace Avalonia.Input
case RawPointerEventType.LeftButtonDown: case RawPointerEventType.LeftButtonDown:
case RawPointerEventType.RightButtonDown: case RawPointerEventType.RightButtonDown:
case RawPointerEventType.MiddleButtonDown: case RawPointerEventType.MiddleButtonDown:
case RawPointerEventType.XButton1Down:
case RawPointerEventType.XButton2Down:
if (ButtonCount(props) > 1) if (ButtonCount(props) > 1)
e.Handled = MouseMove(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers); e.Handled = MouseMove(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers);
else else
@ -151,6 +157,8 @@ namespace Avalonia.Input
case RawPointerEventType.LeftButtonUp: case RawPointerEventType.LeftButtonUp:
case RawPointerEventType.RightButtonUp: case RawPointerEventType.RightButtonUp:
case RawPointerEventType.MiddleButtonUp: case RawPointerEventType.MiddleButtonUp:
case RawPointerEventType.XButton1Up:
case RawPointerEventType.XButton2Up:
if (ButtonCount(props) != 0) if (ButtonCount(props) != 0)
e.Handled = MouseMove(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers); e.Handled = MouseMove(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers);
else else
@ -186,12 +194,20 @@ namespace Avalonia.Input
kind = PointerUpdateKind.MiddleButtonPressed; kind = PointerUpdateKind.MiddleButtonPressed;
if (args.Type == RawPointerEventType.RightButtonDown) if (args.Type == RawPointerEventType.RightButtonDown)
kind = PointerUpdateKind.RightButtonPressed; kind = PointerUpdateKind.RightButtonPressed;
if (args.Type == RawPointerEventType.XButton1Down)
kind = PointerUpdateKind.XButton1Pressed;
if (args.Type == RawPointerEventType.XButton2Down)
kind = PointerUpdateKind.XButton2Pressed;
if (args.Type == RawPointerEventType.LeftButtonUp) if (args.Type == RawPointerEventType.LeftButtonUp)
kind = PointerUpdateKind.LeftButtonReleased; kind = PointerUpdateKind.LeftButtonReleased;
if (args.Type == RawPointerEventType.MiddleButtonUp) if (args.Type == RawPointerEventType.MiddleButtonUp)
kind = PointerUpdateKind.MiddleButtonReleased; kind = PointerUpdateKind.MiddleButtonReleased;
if (args.Type == RawPointerEventType.RightButtonUp) if (args.Type == RawPointerEventType.RightButtonUp)
kind = PointerUpdateKind.RightButtonReleased; kind = PointerUpdateKind.RightButtonReleased;
if (args.Type == RawPointerEventType.XButton1Up)
kind = PointerUpdateKind.XButton1Released;
if (args.Type == RawPointerEventType.XButton2Up)
kind = PointerUpdateKind.XButton2Released;
return new PointerPointProperties(args.InputModifiers, kind); return new PointerPointProperties(args.InputModifiers, kind);
} }

22
src/Avalonia.Input/PointerPoint.cs

@ -21,18 +21,24 @@ namespace Avalonia.Input
public bool IsLeftButtonPressed { get; } public bool IsLeftButtonPressed { get; }
public bool IsMiddleButtonPressed { get; } public bool IsMiddleButtonPressed { get; }
public bool IsRightButtonPressed { get; } public bool IsRightButtonPressed { get; }
public bool IsXButton1Pressed { get; }
public bool IsXButton2Pressed { get; }
public PointerUpdateKind PointerUpdateKind { get; } public PointerUpdateKind PointerUpdateKind { get; }
private PointerPointProperties() private PointerPointProperties()
{ {
} }
public PointerPointProperties(RawInputModifiers modifiers, PointerUpdateKind kind) public PointerPointProperties(RawInputModifiers modifiers, PointerUpdateKind kind)
{ {
PointerUpdateKind = kind; PointerUpdateKind = kind;
IsLeftButtonPressed = modifiers.HasFlagCustom(RawInputModifiers.LeftMouseButton); IsLeftButtonPressed = modifiers.HasFlagCustom(RawInputModifiers.LeftMouseButton);
IsMiddleButtonPressed = modifiers.HasFlagCustom(RawInputModifiers.MiddleMouseButton); IsMiddleButtonPressed = modifiers.HasFlagCustom(RawInputModifiers.MiddleMouseButton);
IsRightButtonPressed = modifiers.HasFlagCustom(RawInputModifiers.RightMouseButton); IsRightButtonPressed = modifiers.HasFlagCustom(RawInputModifiers.RightMouseButton);
IsXButton1Pressed = modifiers.HasFlagCustom(RawInputModifiers.XButton1MouseButton);
IsXButton2Pressed = modifiers.HasFlagCustom(RawInputModifiers.XButton2MouseButton);
// The underlying input source might be reporting the previous state, // The underlying input source might be reporting the previous state,
// so make sure that we reflect the current state // so make sure that we reflect the current state
@ -49,6 +55,14 @@ namespace Avalonia.Input
IsRightButtonPressed = true; IsRightButtonPressed = true;
if (kind == PointerUpdateKind.RightButtonReleased) if (kind == PointerUpdateKind.RightButtonReleased)
IsRightButtonPressed = false; IsRightButtonPressed = false;
if (kind == PointerUpdateKind.XButton1Pressed)
IsXButton1Pressed = true;
if (kind == PointerUpdateKind.XButton1Released)
IsXButton1Pressed = false;
if (kind == PointerUpdateKind.XButton2Pressed)
IsXButton2Pressed = true;
if (kind == PointerUpdateKind.XButton2Released)
IsXButton2Pressed = false;
} }
public static PointerPointProperties None { get; } = new PointerPointProperties(); public static PointerPointProperties None { get; } = new PointerPointProperties();
@ -59,9 +73,13 @@ namespace Avalonia.Input
LeftButtonPressed, LeftButtonPressed,
MiddleButtonPressed, MiddleButtonPressed,
RightButtonPressed, RightButtonPressed,
XButton1Pressed,
XButton2Pressed,
LeftButtonReleased, LeftButtonReleased,
MiddleButtonReleased, MiddleButtonReleased,
RightButtonReleased, RightButtonReleased,
XButton1Released,
XButton2Released,
Other Other
} }

4
src/Avalonia.Input/Raw/RawPointerEventArgs.cs

@ -14,6 +14,10 @@ namespace Avalonia.Input.Raw
RightButtonUp, RightButtonUp,
MiddleButtonDown, MiddleButtonDown,
MiddleButtonUp, MiddleButtonUp,
XButton1Down,
XButton1Up,
XButton2Down,
XButton2Up,
Move, Move,
Wheel, Wheel,
NonClientLeftButtonDown, NonClientLeftButtonDown,

46
src/Avalonia.Visuals/Media/DrawingContext.cs

@ -118,6 +118,42 @@ namespace Avalonia.Media
} }
} }
/// <summary>
/// Draws a rectangle with the specified Brush and Pen.
/// </summary>
/// <param name="brush">The brush used to fill the rectangle, or <c>null</c> for no fill.</param>
/// <param name="pen">The pen used to stroke the rectangle, or <c>null</c> for no stroke.</param>
/// <param name="rect">The rectangle bounds.</param>
/// <param name="radiusX">The radius in the X dimension of the rounded corners.
/// This value will be clamped to the range of 0 to Width/2
/// </param>
/// <param name="radiusY">The radius in the Y dimension of the rounded corners.
/// This value will be clamped to the range of 0 to Height/2
/// </param>
/// <remarks>
/// The brush and the pen can both be null. If the brush is null, then no fill is performed.
/// If the pen is null, then no stoke is performed. If both the pen and the brush are null, then the drawing is not visible.
/// </remarks>
public void DrawRectangle(IBrush brush, IPen pen, Rect rect, double radiusX = 0, double radiusY = 0)
{
if (brush == null && !PenIsVisible(pen))
{
return;
}
if (Math.Abs(radiusX) > double.Epsilon)
{
radiusX = Math.Min(radiusX, rect.Width / 2);
}
if (Math.Abs(radiusY) > double.Epsilon)
{
radiusY = Math.Min(radiusY, rect.Height / 2);
}
PlatformImpl.DrawRectangle(brush, pen, rect, radiusX, radiusY);
}
/// <summary> /// <summary>
/// Draws the outline of a rectangle. /// Draws the outline of a rectangle.
/// </summary> /// </summary>
@ -126,10 +162,7 @@ namespace Avalonia.Media
/// <param name="cornerRadius">The corner radius.</param> /// <param name="cornerRadius">The corner radius.</param>
public void DrawRectangle(IPen pen, Rect rect, float cornerRadius = 0.0f) public void DrawRectangle(IPen pen, Rect rect, float cornerRadius = 0.0f)
{ {
if (PenIsVisible(pen)) DrawRectangle(null, pen, rect, cornerRadius, cornerRadius);
{
PlatformImpl.DrawRectangle(pen, rect, cornerRadius);
}
} }
/// <summary> /// <summary>
@ -162,10 +195,7 @@ namespace Avalonia.Media
/// <param name="cornerRadius">The corner radius.</param> /// <param name="cornerRadius">The corner radius.</param>
public void FillRectangle(IBrush brush, Rect rect, float cornerRadius = 0.0f) public void FillRectangle(IBrush brush, Rect rect, float cornerRadius = 0.0f)
{ {
if (brush != null && rect != Rect.Empty) DrawRectangle(brush, null, rect, cornerRadius, cornerRadius);
{
PlatformImpl.FillRectangle(brush, rect, cornerRadius);
}
} }
public readonly struct PushedState : IDisposable public readonly struct PushedState : IDisposable

26
src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs

@ -61,12 +61,22 @@ namespace Avalonia.Platform
void DrawGeometry(IBrush brush, IPen pen, IGeometryImpl geometry); void DrawGeometry(IBrush brush, IPen pen, IGeometryImpl geometry);
/// <summary> /// <summary>
/// Draws the outline of a rectangle. /// Draws a rectangle with the specified Brush and Pen.
/// </summary> /// </summary>
/// <param name="pen">The pen.</param> /// <param name="brush">The brush used to fill the rectangle, or <c>null</c> for no fill.</param>
/// <param name="pen">The pen used to stroke the rectangle, or <c>null</c> for no stroke.</param>
/// <param name="rect">The rectangle bounds.</param> /// <param name="rect">The rectangle bounds.</param>
/// <param name="cornerRadius">The corner radius.</param> /// <param name="radiusX">The radius in the X dimension of the rounded corners.
void DrawRectangle(IPen pen, Rect rect, float cornerRadius = 0.0f); /// This value will be clamped to the range of 0 to Width/2
/// </param>
/// <param name="radiusY">The radius in the Y dimension of the rounded corners.
/// This value will be clamped to the range of 0 to Height/2
/// </param>
/// <remarks>
/// The brush and the pen can both be null. If the brush is null, then no fill is performed.
/// If the pen is null, then no stoke is performed. If both the pen and the brush are null, then the drawing is not visible.
/// </remarks>
void DrawRectangle(IBrush brush, IPen pen, Rect rect, double radiusX = 0, double radiusY = 0);
/// <summary> /// <summary>
/// Draws text. /// Draws text.
@ -76,14 +86,6 @@ namespace Avalonia.Platform
/// <param name="text">The text.</param> /// <param name="text">The text.</param>
void DrawText(IBrush foreground, Point origin, IFormattedTextImpl text); void DrawText(IBrush foreground, Point origin, IFormattedTextImpl text);
/// <summary>
/// Draws a filled rectangle.
/// </summary>
/// <param name="brush">The brush.</param>
/// <param name="rect">The rectangle bounds.</param>
/// <param name="cornerRadius">The corner radius.</param>
void FillRectangle(IBrush brush, Rect rect, float cornerRadius = 0.0f);
/// <summary> /// <summary>
/// Creates a new <see cref="IRenderTargetBitmapImpl"/> that can be used as a render layer /// Creates a new <see cref="IRenderTargetBitmapImpl"/> that can be used as a render layer
/// for the current render target. /// for the current render target.

2
src/Avalonia.Visuals/Rendering/DeferredRenderer.cs

@ -445,7 +445,7 @@ namespace Avalonia.Rendering
foreach (var r in _dirtyRectsDisplay) foreach (var r in _dirtyRectsDisplay)
{ {
var brush = new ImmutableSolidColorBrush(Colors.Magenta, r.Opacity); var brush = new ImmutableSolidColorBrush(Colors.Magenta, r.Opacity);
context.FillRectangle(brush, r.Rect); context.DrawRectangle(brush,null, r.Rect);
} }
} }

2
src/Avalonia.Visuals/Rendering/RendererBase.cs

@ -51,7 +51,7 @@ namespace Avalonia.Rendering
var rect = new Rect(clientRect.Right - size.Width, 0, size.Width, size.Height); var rect = new Rect(clientRect.Right - size.Width, 0, size.Width, size.Height);
context.Transform = Matrix.Identity; context.Transform = Matrix.Identity;
context.FillRectangle(Brushes.Black, rect); context.DrawRectangle(Brushes.Black,null, rect);
context.DrawText(Brushes.White, rect.TopLeft, _fpsText.PlatformImpl); context.DrawText(Brushes.White, rect.TopLeft, _fpsText.PlatformImpl);
} }
} }

21
src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs

@ -152,13 +152,13 @@ namespace Avalonia.Rendering.SceneGraph
} }
/// <inheritdoc/> /// <inheritdoc/>
public void DrawRectangle(IPen pen, Rect rect, float cornerRadius = 0) public void DrawRectangle(IBrush brush, IPen pen, Rect rect, double radiusX = 0, double radiusY = 0)
{ {
var next = NextDrawAs<RectangleNode>(); var next = NextDrawAs<RectangleNode>();
if (next == null || !next.Item.Equals(Transform, null, pen, rect, cornerRadius)) if (next == null || !next.Item.Equals(Transform, brush, pen, rect, radiusX, radiusY))
{ {
Add(new RectangleNode(Transform, null, pen, rect, cornerRadius, CreateChildScene(pen.Brush))); Add(new RectangleNode(Transform, brush, pen, rect, radiusX, radiusY, CreateChildScene(brush)));
} }
else else
{ {
@ -190,21 +190,6 @@ namespace Avalonia.Rendering.SceneGraph
} }
} }
/// <inheritdoc/>
public void FillRectangle(IBrush brush, Rect rect, float cornerRadius = 0)
{
var next = NextDrawAs<RectangleNode>();
if (next == null || !next.Item.Equals(Transform, brush, null, rect, cornerRadius))
{
Add(new RectangleNode(Transform, brush, null, rect, cornerRadius, CreateChildScene(brush)));
}
else
{
++_drawOperationindex;
}
}
public IRenderTargetBitmapImpl CreateLayer(Size size) public IRenderTargetBitmapImpl CreateLayer(Size size)
{ {
throw new NotSupportedException("Creating layers on a deferred drawing context not supported"); throw new NotSupportedException("Creating layers on a deferred drawing context not supported");

43
src/Avalonia.Visuals/Rendering/SceneGraph/RectangleNode.cs

@ -1,6 +1,7 @@
// Copyright (c) The Avalonia Project. All rights reserved. // 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. // Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Collections.Generic; using System.Collections.Generic;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Media.Immutable; using Avalonia.Media.Immutable;
@ -21,14 +22,16 @@ namespace Avalonia.Rendering.SceneGraph
/// <param name="brush">The fill brush.</param> /// <param name="brush">The fill brush.</param>
/// <param name="pen">The stroke pen.</param> /// <param name="pen">The stroke pen.</param>
/// <param name="rect">The rectangle to draw.</param> /// <param name="rect">The rectangle to draw.</param>
/// <param name="cornerRadius">The rectangle corner radius.</param> /// <param name="radiusY">The radius in the Y dimension of the rounded corners.</param>
/// <param name="radiusX">The radius in the X dimension of the rounded corners.</param>
/// <param name="childScenes">Child scenes for drawing visual brushes.</param> /// <param name="childScenes">Child scenes for drawing visual brushes.</param>
public RectangleNode( public RectangleNode(
Matrix transform, Matrix transform,
IBrush brush, IBrush brush,
IPen pen, IPen pen,
Rect rect, Rect rect,
float cornerRadius, double radiusX,
double radiusY,
IDictionary<IVisual, Scene> childScenes = null) IDictionary<IVisual, Scene> childScenes = null)
: base(rect, transform, pen) : base(rect, transform, pen)
{ {
@ -36,7 +39,8 @@ namespace Avalonia.Rendering.SceneGraph
Brush = brush?.ToImmutable(); Brush = brush?.ToImmutable();
Pen = pen?.ToImmutable(); Pen = pen?.ToImmutable();
Rect = rect; Rect = rect;
CornerRadius = cornerRadius; RadiusX = radiusX;
RadiusY = radiusY;
ChildScenes = childScenes; ChildScenes = childScenes;
} }
@ -61,9 +65,14 @@ namespace Avalonia.Rendering.SceneGraph
public Rect Rect { get; } public Rect Rect { get; }
/// <summary> /// <summary>
/// Gets the rectangle corner radius. /// The radius in the X dimension of the rounded corners.
/// </summary> /// </summary>
public float CornerRadius { get; } public double RadiusX { get; }
/// <summary>
/// The radius in the Y dimension of the rounded corners.
/// </summary>
public double RadiusY { get; }
/// <inheritdoc/> /// <inheritdoc/>
public override IDictionary<IVisual, Scene> ChildScenes { get; } public override IDictionary<IVisual, Scene> ChildScenes { get; }
@ -75,19 +84,21 @@ namespace Avalonia.Rendering.SceneGraph
/// <param name="brush">The fill of the other draw operation.</param> /// <param name="brush">The fill of the other draw operation.</param>
/// <param name="pen">The stroke of the other draw operation.</param> /// <param name="pen">The stroke of the other draw operation.</param>
/// <param name="rect">The rectangle of the other draw operation.</param> /// <param name="rect">The rectangle of the other draw operation.</param>
/// <param name="cornerRadius">The rectangle corner radius of the other draw operation.</param> /// <param name="radiusX"></param>
/// <param name="radiusY"></param>
/// <returns>True if the draw operations are the same, otherwise false.</returns> /// <returns>True if the draw operations are the same, otherwise false.</returns>
/// <remarks> /// <remarks>
/// The properties of the other draw operation are passed in as arguments to prevent /// The properties of the other draw operation are passed in as arguments to prevent
/// allocation of a not-yet-constructed draw operation object. /// allocation of a not-yet-constructed draw operation object.
/// </remarks> /// </remarks>
public bool Equals(Matrix transform, IBrush brush, IPen pen, Rect rect, float cornerRadius) public bool Equals(Matrix transform, IBrush brush, IPen pen, Rect rect, double radiusX, double radiusY)
{ {
return transform == Transform && return transform == Transform &&
Equals(brush, Brush) && Equals(brush, Brush) &&
Equals(Pen, pen) && Equals(Pen, pen) &&
rect == Rect && rect == Rect &&
cornerRadius == CornerRadius; Math.Abs(radiusX - RadiusX) < double.Epsilon &&
Math.Abs(radiusY - RadiusY) < double.Epsilon;
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -95,15 +106,7 @@ namespace Avalonia.Rendering.SceneGraph
{ {
context.Transform = Transform; context.Transform = Transform;
if (Brush != null) context.DrawRectangle(Brush, Pen, Rect, RadiusX, RadiusY);
{
context.FillRectangle(Brush, Rect, CornerRadius);
}
if (Pen != null)
{
context.DrawRectangle(Pen, Rect, CornerRadius);
}
} }
/// <inheritdoc/> /// <inheritdoc/>

36
src/Avalonia.X11/X11Window.cs

@ -351,10 +351,17 @@ namespace Avalonia.X11
{ {
if (ActivateTransientChildIfNeeded()) if (ActivateTransientChildIfNeeded())
return; return;
if (ev.ButtonEvent.button < 4) if (ev.ButtonEvent.button < 4 || ev.ButtonEvent.button == 8 || ev.ButtonEvent.button == 9)
MouseEvent(ev.ButtonEvent.button == 1 ? RawPointerEventType.LeftButtonDown MouseEvent(
: ev.ButtonEvent.button == 2 ? RawPointerEventType.MiddleButtonDown ev.ButtonEvent.button switch
: RawPointerEventType.RightButtonDown, ref ev, ev.ButtonEvent.state); {
1 => RawPointerEventType.LeftButtonDown,
2 => RawPointerEventType.MiddleButtonDown,
3 => RawPointerEventType.RightButtonDown,
8 => RawPointerEventType.XButton1Down,
9 => RawPointerEventType.XButton2Down
},
ref ev, ev.ButtonEvent.state);
else else
{ {
var delta = ev.ButtonEvent.button == 4 var delta = ev.ButtonEvent.button == 4
@ -372,10 +379,17 @@ namespace Avalonia.X11
} }
else if (ev.type == XEventName.ButtonRelease) else if (ev.type == XEventName.ButtonRelease)
{ {
if (ev.ButtonEvent.button < 4) if (ev.ButtonEvent.button < 4 || ev.ButtonEvent.button == 8 || ev.ButtonEvent.button == 9)
MouseEvent(ev.ButtonEvent.button == 1 ? RawPointerEventType.LeftButtonUp MouseEvent(
: ev.ButtonEvent.button == 2 ? RawPointerEventType.MiddleButtonUp ev.ButtonEvent.button switch
: RawPointerEventType.RightButtonUp, ref ev, ev.ButtonEvent.state); {
1 => RawPointerEventType.LeftButtonUp,
2 => RawPointerEventType.MiddleButtonUp,
3 => RawPointerEventType.RightButtonUp,
8 => RawPointerEventType.XButton1Up,
9 => RawPointerEventType.XButton2Up
},
ref ev, ev.ButtonEvent.state);
} }
else if (ev.type == XEventName.ConfigureNotify) else if (ev.type == XEventName.ConfigureNotify)
{ {
@ -586,8 +600,12 @@ namespace Avalonia.X11
rv |= RawInputModifiers.LeftMouseButton; rv |= RawInputModifiers.LeftMouseButton;
if (state.HasFlag(XModifierMask.Button2Mask)) if (state.HasFlag(XModifierMask.Button2Mask))
rv |= RawInputModifiers.RightMouseButton; rv |= RawInputModifiers.RightMouseButton;
if (state.HasFlag(XModifierMask.Button2Mask)) if (state.HasFlag(XModifierMask.Button3Mask))
rv |= RawInputModifiers.MiddleMouseButton; rv |= RawInputModifiers.MiddleMouseButton;
if (state.HasFlag(XModifierMask.Button4Mask))
rv |= RawInputModifiers.XButton1MouseButton;
if (state.HasFlag(XModifierMask.Button5Mask))
rv |= RawInputModifiers.XButton2MouseButton;
if (state.HasFlag(XModifierMask.ShiftMask)) if (state.HasFlag(XModifierMask.ShiftMask))
rv |= RawInputModifiers.Shift; rv |= RawInputModifiers.Shift;
if (state.HasFlag(XModifierMask.ControlMask)) if (state.HasFlag(XModifierMask.ControlMask))

20
src/Avalonia.X11/XI2Manager.cs

@ -240,11 +240,15 @@ namespace Avalonia.X11
if (ev.Type == XiEventType.XI_ButtonPress || ev.Type == XiEventType.XI_ButtonRelease) if (ev.Type == XiEventType.XI_ButtonPress || ev.Type == XiEventType.XI_ButtonRelease)
{ {
var down = ev.Type == XiEventType.XI_ButtonPress; var down = ev.Type == XiEventType.XI_ButtonPress;
var type = var type = ev.Button switch
ev.Button == 1 ? (down ? RawPointerEventType.LeftButtonDown : RawPointerEventType.LeftButtonUp) {
: ev.Button == 2 ? (down ? RawPointerEventType.MiddleButtonDown : RawPointerEventType.MiddleButtonUp) 1 => down ? RawPointerEventType.LeftButtonDown : RawPointerEventType.LeftButtonUp,
: ev.Button == 3 ? (down ? RawPointerEventType.RightButtonDown : RawPointerEventType.RightButtonUp) 2 => down ? RawPointerEventType.MiddleButtonDown : RawPointerEventType.MiddleButtonUp,
: (RawPointerEventType?)null; 3 => down ? RawPointerEventType.RightButtonDown : RawPointerEventType.RightButtonUp,
8 => down ? RawPointerEventType.XButton1Down : RawPointerEventType.XButton1Up,
9 => down ? RawPointerEventType.XButton2Down : RawPointerEventType.XButton2Up,
_ => (RawPointerEventType?)null
};
if (type.HasValue) if (type.HasValue)
client.ScheduleInput(new RawPointerEventArgs(client.MouseDevice, ev.Timestamp, client.InputRoot, client.ScheduleInput(new RawPointerEventArgs(client.MouseDevice, ev.Timestamp, client.InputRoot,
type.Value, ev.Position, ev.Modifiers)); type.Value, ev.Position, ev.Modifiers));
@ -283,12 +287,14 @@ namespace Avalonia.X11
var buttons = ev->buttons.Mask; var buttons = ev->buttons.Mask;
if (XIMaskIsSet(buttons, 1)) if (XIMaskIsSet(buttons, 1))
Modifiers |= RawInputModifiers.LeftMouseButton; Modifiers |= RawInputModifiers.LeftMouseButton;
if (XIMaskIsSet(buttons, 2)) if (XIMaskIsSet(buttons, 2))
Modifiers |= RawInputModifiers.MiddleMouseButton; Modifiers |= RawInputModifiers.MiddleMouseButton;
if (XIMaskIsSet(buttons, 3)) if (XIMaskIsSet(buttons, 3))
Modifiers |= RawInputModifiers.RightMouseButton; Modifiers |= RawInputModifiers.RightMouseButton;
if (XIMaskIsSet(buttons, 8))
Modifiers |= RawInputModifiers.XButton1MouseButton;
if (XIMaskIsSet(buttons, 9))
Modifiers |= RawInputModifiers.XButton2MouseButton;
} }
Valuators = new Dictionary<int, double>(); Valuators = new Dictionary<int, double>();

49
src/Skia/Avalonia.Skia/DrawingContextImpl.cs

@ -184,37 +184,40 @@ namespace Avalonia.Skia
} }
/// <inheritdoc /> /// <inheritdoc />
public void DrawRectangle(IPen pen, Rect rect, float cornerRadius = 0) public void DrawRectangle(IBrush brush, IPen pen, Rect rect, double radiusX, double radiusY)
{ {
using (var paint = CreatePaint(pen, rect.Size)) var rc = rect.ToSKRect();
{ var isRounded = Math.Abs(radiusX) > double.Epsilon || Math.Abs(radiusX) > double.Epsilon;
var rc = rect.ToSKRect();
if (Math.Abs(cornerRadius) < float.Epsilon) if (brush != null)
{ {
Canvas.DrawRect(rc, paint.Paint); using (var paint = CreatePaint(brush, rect.Size))
}
else
{ {
Canvas.DrawRoundRect(rc, cornerRadius, cornerRadius, paint.Paint); if (isRounded)
{
Canvas.DrawRoundRect(rc, (float)radiusX, (float)radiusY, paint.Paint);
}
else
{
Canvas.DrawRect(rc, paint.Paint);
}
} }
} }
}
/// <inheritdoc /> if (pen?.Brush != null)
public void FillRectangle(IBrush brush, Rect rect, float cornerRadius = 0)
{
using (var paint = CreatePaint(brush, rect.Size))
{ {
var rc = rect.ToSKRect(); using (var paint = CreatePaint(pen, rect.Size))
if (Math.Abs(cornerRadius) < float.Epsilon)
{
Canvas.DrawRect(rc, paint.Paint);
}
else
{ {
Canvas.DrawRoundRect(rc, cornerRadius, cornerRadius, paint.Paint); if (isRounded)
{
Canvas.DrawRoundRect(rc, (float)radiusX, (float)radiusY, paint.Paint);
}
else
{
Canvas.DrawRect(rc, paint.Paint);
}
} }
} }
} }

109
src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs

@ -230,34 +230,64 @@ namespace Avalonia.Direct2D1.Media
} }
} }
/// <summary> /// <inheritdoc />
/// Draws the outline of a rectangle. public void DrawRectangle(IBrush brush, IPen pen, Rect rect, double radiusX, double radiusY)
/// </summary>
/// <param name="pen">The pen.</param>
/// <param name="rect">The rectangle bounds.</param>
/// <param name="cornerRadius">The corner radius.</param>
public void DrawRectangle(IPen pen, Rect rect, float cornerRadius)
{ {
using (var brush = CreateBrush(pen.Brush, rect.Size)) var rc = rect.ToDirect2D();
using (var d2dStroke = pen.ToDirect2DStrokeStyle(_deviceContext)) var isRounded = Math.Abs(radiusX) > double.Epsilon || Math.Abs(radiusX) > double.Epsilon;
if (brush != null)
{ {
if (brush.PlatformBrush != null) using (var b = CreateBrush(brush, rect.Size))
{ {
if (cornerRadius == 0) if (b.PlatformBrush != null)
{ {
_deviceContext.DrawRectangle( if (isRounded)
rect.ToDirect2D(), {
brush.PlatformBrush, _deviceContext.FillRoundedRectangle(
(float)pen.Thickness, new RoundedRectangle
d2dStroke); {
Rect = new RawRectangleF(
(float)rect.X,
(float)rect.Y,
(float)rect.Right,
(float)rect.Bottom),
RadiusX = (float)radiusX,
RadiusY = (float)radiusY
},
b.PlatformBrush);
}
else
{
_deviceContext.FillRectangle(rc, b.PlatformBrush);
}
} }
else }
}
if (pen?.Brush != null)
{
using (var wrapper = CreateBrush(pen.Brush, rect.Size))
using (var d2dStroke = pen.ToDirect2DStrokeStyle(_deviceContext))
{
if (wrapper.PlatformBrush != null)
{ {
_deviceContext.DrawRoundedRectangle( if (isRounded)
new RoundedRectangle { Rect = rect.ToDirect2D(), RadiusX = cornerRadius, RadiusY = cornerRadius }, {
brush.PlatformBrush, _deviceContext.DrawRoundedRectangle(
(float)pen.Thickness, new RoundedRectangle { Rect = rc, RadiusX = (float)radiusX, RadiusY = (float)radiusY },
d2dStroke); wrapper.PlatformBrush,
(float)pen.Thickness,
d2dStroke);
}
else
{
_deviceContext.DrawRectangle(
rc,
wrapper.PlatformBrush,
(float)pen.Thickness,
d2dStroke);
}
} }
} }
} }
@ -286,41 +316,6 @@ namespace Avalonia.Direct2D1.Media
} }
} }
/// <summary>
/// Draws a filled rectangle.
/// </summary>
/// <param name="brush">The brush.</param>
/// <param name="rect">The rectangle bounds.</param>
/// <param name="cornerRadius">The corner radius.</param>
public void FillRectangle(IBrush brush, Rect rect, float cornerRadius)
{
using (var b = CreateBrush(brush, rect.Size))
{
if (b.PlatformBrush != null)
{
if (cornerRadius == 0)
{
_deviceContext.FillRectangle(rect.ToDirect2D(), b.PlatformBrush);
}
else
{
_deviceContext.FillRoundedRectangle(
new RoundedRectangle
{
Rect = new RawRectangleF(
(float)rect.X,
(float)rect.Y,
(float)rect.Right,
(float)rect.Bottom),
RadiusX = cornerRadius,
RadiusY = cornerRadius
},
b.PlatformBrush);
}
}
}
}
public IRenderTargetBitmapImpl CreateLayer(Size size) public IRenderTargetBitmapImpl CreateLayer(Size size)
{ {
if (_layerFactory != null) if (_layerFactory != null)

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

@ -545,34 +545,42 @@ namespace Avalonia.Win32
case UnmanagedMethods.WindowsMessage.WM_LBUTTONDOWN: case UnmanagedMethods.WindowsMessage.WM_LBUTTONDOWN:
case UnmanagedMethods.WindowsMessage.WM_RBUTTONDOWN: case UnmanagedMethods.WindowsMessage.WM_RBUTTONDOWN:
case UnmanagedMethods.WindowsMessage.WM_MBUTTONDOWN: case UnmanagedMethods.WindowsMessage.WM_MBUTTONDOWN:
if(ShouldIgnoreTouchEmulatedMessage()) case UnmanagedMethods.WindowsMessage.WM_XBUTTONDOWN:
if (ShouldIgnoreTouchEmulatedMessage())
break; break;
e = new RawPointerEventArgs( e = new RawPointerEventArgs(
_mouseDevice, _mouseDevice,
timestamp, timestamp,
_owner, _owner,
msg == (int)UnmanagedMethods.WindowsMessage.WM_LBUTTONDOWN (UnmanagedMethods.WindowsMessage)msg switch
? RawPointerEventType.LeftButtonDown {
: msg == (int)UnmanagedMethods.WindowsMessage.WM_RBUTTONDOWN UnmanagedMethods.WindowsMessage.WM_LBUTTONDOWN => RawPointerEventType.LeftButtonDown,
? RawPointerEventType.RightButtonDown UnmanagedMethods.WindowsMessage.WM_RBUTTONDOWN => RawPointerEventType.RightButtonDown,
: RawPointerEventType.MiddleButtonDown, UnmanagedMethods.WindowsMessage.WM_MBUTTONDOWN => RawPointerEventType.MiddleButtonDown,
UnmanagedMethods.WindowsMessage.WM_XBUTTONDOWN =>
HighWord(ToInt32(wParam)) == 1 ? RawPointerEventType.XButton1Down : RawPointerEventType.XButton2Down
},
DipFromLParam(lParam), GetMouseModifiers(wParam)); DipFromLParam(lParam), GetMouseModifiers(wParam));
break; break;
case UnmanagedMethods.WindowsMessage.WM_LBUTTONUP: case UnmanagedMethods.WindowsMessage.WM_LBUTTONUP:
case UnmanagedMethods.WindowsMessage.WM_RBUTTONUP: case UnmanagedMethods.WindowsMessage.WM_RBUTTONUP:
case UnmanagedMethods.WindowsMessage.WM_MBUTTONUP: case UnmanagedMethods.WindowsMessage.WM_MBUTTONUP:
if(ShouldIgnoreTouchEmulatedMessage()) case UnmanagedMethods.WindowsMessage.WM_XBUTTONUP:
if (ShouldIgnoreTouchEmulatedMessage())
break; break;
e = new RawPointerEventArgs( e = new RawPointerEventArgs(
_mouseDevice, _mouseDevice,
timestamp, timestamp,
_owner, _owner,
msg == (int)UnmanagedMethods.WindowsMessage.WM_LBUTTONUP (UnmanagedMethods.WindowsMessage)msg switch
? RawPointerEventType.LeftButtonUp {
: msg == (int)UnmanagedMethods.WindowsMessage.WM_RBUTTONUP UnmanagedMethods.WindowsMessage.WM_LBUTTONUP => RawPointerEventType.LeftButtonUp,
? RawPointerEventType.RightButtonUp UnmanagedMethods.WindowsMessage.WM_RBUTTONUP => RawPointerEventType.RightButtonUp,
: RawPointerEventType.MiddleButtonUp, UnmanagedMethods.WindowsMessage.WM_MBUTTONUP => RawPointerEventType.MiddleButtonUp,
UnmanagedMethods.WindowsMessage.WM_XBUTTONUP =>
HighWord(ToInt32(wParam)) == 1 ? RawPointerEventType.XButton1Up : RawPointerEventType.XButton2Up,
},
DipFromLParam(lParam), GetMouseModifiers(wParam)); DipFromLParam(lParam), GetMouseModifiers(wParam));
break; break;
@ -632,15 +640,19 @@ namespace Avalonia.Win32
case UnmanagedMethods.WindowsMessage.WM_NCLBUTTONDOWN: case UnmanagedMethods.WindowsMessage.WM_NCLBUTTONDOWN:
case UnmanagedMethods.WindowsMessage.WM_NCRBUTTONDOWN: case UnmanagedMethods.WindowsMessage.WM_NCRBUTTONDOWN:
case UnmanagedMethods.WindowsMessage.WM_NCMBUTTONDOWN: case UnmanagedMethods.WindowsMessage.WM_NCMBUTTONDOWN:
case UnmanagedMethods.WindowsMessage.WM_NCXBUTTONDOWN:
e = new RawPointerEventArgs( e = new RawPointerEventArgs(
_mouseDevice, _mouseDevice,
timestamp, timestamp,
_owner, _owner,
msg == (int)UnmanagedMethods.WindowsMessage.WM_NCLBUTTONDOWN (UnmanagedMethods.WindowsMessage)msg switch
? RawPointerEventType.NonClientLeftButtonDown {
: msg == (int)UnmanagedMethods.WindowsMessage.WM_NCRBUTTONDOWN UnmanagedMethods.WindowsMessage.WM_NCLBUTTONDOWN => RawPointerEventType.NonClientLeftButtonDown,
? RawPointerEventType.RightButtonDown UnmanagedMethods.WindowsMessage.WM_NCRBUTTONDOWN => RawPointerEventType.RightButtonDown,
: RawPointerEventType.MiddleButtonDown, UnmanagedMethods.WindowsMessage.WM_NCMBUTTONDOWN => RawPointerEventType.MiddleButtonDown,
UnmanagedMethods.WindowsMessage.WM_NCXBUTTONDOWN =>
HighWord(ToInt32(wParam)) == 1 ? RawPointerEventType.XButton1Down : RawPointerEventType.XButton2Down,
},
PointToClient(PointFromLParam(lParam)), GetMouseModifiers(wParam)); PointToClient(PointFromLParam(lParam)), GetMouseModifiers(wParam));
break; break;
case WindowsMessage.WM_TOUCH: case WindowsMessage.WM_TOUCH:
@ -783,6 +795,10 @@ namespace Avalonia.Win32
modifiers |= RawInputModifiers.RightMouseButton; modifiers |= RawInputModifiers.RightMouseButton;
if (keys.HasFlagCustom(UnmanagedMethods.ModifierKeys.MK_MBUTTON)) if (keys.HasFlagCustom(UnmanagedMethods.ModifierKeys.MK_MBUTTON))
modifiers |= RawInputModifiers.MiddleMouseButton; modifiers |= RawInputModifiers.MiddleMouseButton;
if (keys.HasFlag(UnmanagedMethods.ModifierKeys.MK_XBUTTON1))
modifiers |= RawInputModifiers.XButton1MouseButton;
if (keys.HasFlag(UnmanagedMethods.ModifierKeys.MK_XBUTTON2))
modifiers |= RawInputModifiers.XButton2MouseButton;
return modifiers; return modifiers;
} }
@ -1061,5 +1077,7 @@ namespace Avalonia.Win32
} }
} }
IntPtr EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo.Handle => Handle.Handle; IntPtr EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo.Handle => Handle.Handle;
private static int HighWord(int param) => param >> 16;
} }
} }

10
tests/Avalonia.RenderTests/Media/BitmapTests.cs

@ -78,9 +78,9 @@ namespace Avalonia.Direct2D1.RenderTests.Media
{ {
ctx.Clear(Colors.Transparent); ctx.Clear(Colors.Transparent);
ctx.PushOpacity(0.8); ctx.PushOpacity(0.8);
ctx.FillRectangle(Brushes.Chartreuse, new Rect(0, 0, 20, 100)); ctx.DrawRectangle(Brushes.Chartreuse, null, new Rect(0, 0, 20, 100));
ctx.FillRectangle(Brushes.Crimson, new Rect(20, 0, 20, 100)); ctx.DrawRectangle(Brushes.Crimson, null, new Rect(20, 0, 20, 100));
ctx.FillRectangle(Brushes.Gold, new Rect(40, 0, 20, 100)); ctx.DrawRectangle(Brushes.Gold,null, new Rect(40, 0, 20, 100));
ctx.PopOpacity(); ctx.PopOpacity();
} }
@ -90,8 +90,8 @@ namespace Avalonia.Direct2D1.RenderTests.Media
{ {
using (var ctx = rtb.CreateDrawingContext(null)) using (var ctx = rtb.CreateDrawingContext(null))
{ {
ctx.FillRectangle(Brushes.Blue, new Rect(0, 0, 100, 100)); ctx.DrawRectangle(Brushes.Blue, null, new Rect(0, 0, 100, 100));
ctx.FillRectangle(Brushes.Pink, new Rect(0, 20, 100, 10)); ctx.DrawRectangle(Brushes.Pink, null, new Rect(0, 20, 100, 10));
var rc = new Rect(0, 0, 60, 60); var rc = new Rect(0, 0, 60, 60);
ctx.DrawImage(bmp.PlatformImpl, 1, rc, rc); ctx.DrawImage(bmp.PlatformImpl, 1, rc, rc);

8
tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs

@ -466,7 +466,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering
var animation = new BehaviorSubject<double>(0.5); var animation = new BehaviorSubject<double>(0.5);
context.Verify(x => x.PushOpacity(0.5), Times.Once); context.Verify(x => x.PushOpacity(0.5), Times.Once);
context.Verify(x => x.FillRectangle(Brushes.Red, new Rect(0, 0, 100, 100), 0), Times.Once); context.Verify(x => x.DrawRectangle(Brushes.Red, null, new Rect(0, 0, 100, 100), 0, 0), Times.Once);
context.Verify(x => x.PopOpacity(), Times.Once); context.Verify(x => x.PopOpacity(), Times.Once);
} }
@ -496,7 +496,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering
var animation = new BehaviorSubject<double>(0.5); var animation = new BehaviorSubject<double>(0.5);
context.Verify(x => x.PushOpacity(0.5), Times.Never); context.Verify(x => x.PushOpacity(0.5), Times.Never);
context.Verify(x => x.FillRectangle(Brushes.Red, new Rect(0, 0, 100, 100), 0), Times.Never); context.Verify(x => x.DrawRectangle(Brushes.Red, null, new Rect(0, 0, 100, 100), 0, 0), Times.Never);
context.Verify(x => x.PopOpacity(), Times.Never); context.Verify(x => x.PopOpacity(), Times.Never);
} }
@ -522,7 +522,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering
var animation = new BehaviorSubject<double>(0.5); var animation = new BehaviorSubject<double>(0.5);
context.Verify(x => x.PushOpacityMask(Brushes.Green, new Rect(0, 0, 100, 100)), Times.Once); context.Verify(x => x.PushOpacityMask(Brushes.Green, new Rect(0, 0, 100, 100)), Times.Once);
context.Verify(x => x.FillRectangle(Brushes.Red, new Rect(0, 0, 100, 100), 0), Times.Once); context.Verify(x => x.DrawRectangle(Brushes.Red, null, new Rect(0, 0, 100, 100), 0, 0), Times.Once);
context.Verify(x => x.PopOpacityMask(), Times.Once); context.Verify(x => x.PopOpacityMask(), Times.Once);
} }
@ -641,7 +641,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering
var context = GetLayerContext(target, border); var context = GetLayerContext(target, border);
context.Verify(x => x.PushOpacity(0.5), Times.Never); context.Verify(x => x.PushOpacity(0.5), Times.Never);
context.Verify(x => x.FillRectangle(Brushes.Red, new Rect(0, 0, 100, 100), 0), Times.Once); context.Verify(x => x.DrawRectangle(Brushes.Red, null, new Rect(0, 0, 100, 100), 0, 0), Times.Once);
context.Verify(x => x.PopOpacity(), Times.Never); context.Verify(x => x.PopOpacity(), Times.Never);
} }

26
tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/DeferredDrawingContextImplTests.cs

@ -100,20 +100,18 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
using (target.BeginUpdate(node)) using (target.BeginUpdate(node))
{ {
target.FillRectangle(Brushes.Red, new Rect(0, 0, 100, 100)); target.DrawRectangle(Brushes.Red, new Pen(Brushes.Green, 1), new Rect(0, 0, 100, 100));
target.DrawRectangle(new Pen(Brushes.Green, 1), new Rect(0, 0, 100, 100));
} }
Assert.Equal(2, node.DrawOperations.Count); Assert.Equal(1, node.DrawOperations.Count);
Assert.IsType<RectangleNode>(node.DrawOperations[0].Item); Assert.IsType<RectangleNode>(node.DrawOperations[0].Item);
Assert.IsType<RectangleNode>(node.DrawOperations[1].Item);
} }
[Fact] [Fact]
public void Should_Not_Replace_Identical_DrawOperation() public void Should_Not_Replace_Identical_DrawOperation()
{ {
var node = new VisualNode(new TestRoot(), null); var node = new VisualNode(new TestRoot(), null);
var operation = RefCountable.Create(new RectangleNode(Matrix.Identity, Brushes.Red, null, new Rect(0, 0, 100, 100), 0)); var operation = RefCountable.Create(new RectangleNode(Matrix.Identity, Brushes.Red, null, new Rect(0, 0, 100, 100), 0, 0));
var layers = new SceneLayers(node.Visual); var layers = new SceneLayers(node.Visual);
var target = new DeferredDrawingContextImpl(null, layers); var target = new DeferredDrawingContextImpl(null, layers);
@ -122,7 +120,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
using (target.BeginUpdate(node)) using (target.BeginUpdate(node))
{ {
target.FillRectangle(Brushes.Red, new Rect(0, 0, 100, 100)); target.DrawRectangle(Brushes.Red, null, new Rect(0, 0, 100, 100));
} }
Assert.Equal(1, node.DrawOperations.Count); Assert.Equal(1, node.DrawOperations.Count);
@ -135,7 +133,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
public void Should_Replace_Different_DrawOperation() public void Should_Replace_Different_DrawOperation()
{ {
var node = new VisualNode(new TestRoot(), null); var node = new VisualNode(new TestRoot(), null);
var operation = RefCountable.Create(new RectangleNode(Matrix.Identity, Brushes.Red, null, new Rect(0, 0, 100, 100), 0)); var operation = RefCountable.Create(new RectangleNode(Matrix.Identity, Brushes.Red, null, new Rect(0, 0, 100, 100), 0, 0));
var layers = new SceneLayers(node.Visual); var layers = new SceneLayers(node.Visual);
var target = new DeferredDrawingContextImpl(null, layers); var target = new DeferredDrawingContextImpl(null, layers);
@ -144,7 +142,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
using (target.BeginUpdate(node)) using (target.BeginUpdate(node))
{ {
target.FillRectangle(Brushes.Green, new Rect(0, 0, 100, 100)); target.DrawRectangle(Brushes.Green, null, new Rect(0, 0, 100, 100));
} }
Assert.Equal(1, node.DrawOperations.Count); Assert.Equal(1, node.DrawOperations.Count);
@ -157,7 +155,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
public void Should_Update_DirtyRects() public void Should_Update_DirtyRects()
{ {
var node = new VisualNode(new TestRoot(), null); var node = new VisualNode(new TestRoot(), null);
var operation = new RectangleNode(Matrix.Identity, Brushes.Red, null, new Rect(0, 0, 100, 100), 0); var operation = new RectangleNode(Matrix.Identity, Brushes.Red, null, new Rect(0, 0, 100, 100), 0, 0);
var layers = new SceneLayers(node.Visual); var layers = new SceneLayers(node.Visual);
var target = new DeferredDrawingContextImpl(null, layers); var target = new DeferredDrawingContextImpl(null, layers);
@ -165,7 +163,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
using (target.BeginUpdate(node)) using (target.BeginUpdate(node))
{ {
target.FillRectangle(Brushes.Green, new Rect(0, 0, 100, 100)); target.DrawRectangle(Brushes.Green, null, new Rect(0, 0, 100, 100));
} }
Assert.Equal(new Rect(0, 0, 100, 100), layers.Single().Dirty.Single()); Assert.Equal(new Rect(0, 0, 100, 100), layers.Single().Dirty.Single());
@ -192,8 +190,8 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
using (target.BeginUpdate(node)) using (target.BeginUpdate(node))
{ {
target.FillRectangle(Brushes.Green, new Rect(0, 0, 10, 100)); target.DrawRectangle(Brushes.Green, null, new Rect(0, 0, 10, 100));
target.FillRectangle(Brushes.Blue, new Rect(0, 0, 20, 100)); target.DrawRectangle(Brushes.Blue, null, new Rect(0, 0, 20, 100));
} }
Assert.Equal(2, node.DrawOperations.Count); Assert.Equal(2, node.DrawOperations.Count);
@ -208,7 +206,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
public void Trimmed_DrawOperations_Releases_Reference() public void Trimmed_DrawOperations_Releases_Reference()
{ {
var node = new VisualNode(new TestRoot(), null); var node = new VisualNode(new TestRoot(), null);
var operation = RefCountable.Create(new RectangleNode(Matrix.Identity, Brushes.Red, null, new Rect(0, 0, 100, 100), 0)); var operation = RefCountable.Create(new RectangleNode(Matrix.Identity, Brushes.Red, null, new Rect(0, 0, 100, 100), 0, 0));
var layers = new SceneLayers(node.Visual); var layers = new SceneLayers(node.Visual);
var target = new DeferredDrawingContextImpl(null, layers); var target = new DeferredDrawingContextImpl(null, layers);
@ -218,7 +216,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
using (target.BeginUpdate(node)) using (target.BeginUpdate(node))
{ {
target.FillRectangle(Brushes.Green, new Rect(0, 0, 100, 100)); target.DrawRectangle(Brushes.Green, null, new Rect(0, 0, 100, 100));
} }
Assert.Equal(1, node.DrawOperations.Count); Assert.Equal(1, node.DrawOperations.Count);

Loading…
Cancel
Save