Browse Source

Merge branch 'master' into win_mouse_IntermediatePoints

pull/9358/head
Nikita Tsukanov 4 years ago
committed by GitHub
parent
commit
37394f02ad
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 16
      src/Avalonia.Base/Controls/NameScopeEventArgs.cs
  2. 2
      src/Avalonia.Base/Input/DragEventArgs.cs
  3. 5
      src/Avalonia.Base/Input/GotFocusEventArgs.cs
  4. 5
      src/Avalonia.Base/Input/KeyEventArgs.cs
  5. 2
      src/Avalonia.Base/Input/PointerDeltaEventArgs.cs
  6. 12
      src/Avalonia.Base/Input/PointerEventArgs.cs
  7. 2
      src/Avalonia.Base/Input/PointerWheelEventArgs.cs
  8. 6
      src/Avalonia.Base/Input/ScrollGestureEventArgs.cs
  9. 2
      src/Avalonia.Base/Input/TappedEventArgs.cs
  10. 4
      src/Avalonia.Base/Input/TextInputEventArgs.cs
  11. 5
      src/Avalonia.Base/Input/VectorEventArgs.cs
  12. 2
      src/Avalonia.Base/Layout/EffectiveViewportChangedEventArgs.cs
  13. 18
      src/Avalonia.Base/Media/Imaging/Bitmap.cs
  14. 14
      src/Avalonia.Base/Media/Imaging/IBitmap.cs
  15. 14
      src/Avalonia.Base/Platform/IBitmapImpl.cs
  16. 2
      src/Avalonia.Base/Rendering/SceneInvalidatedEventArgs.cs
  17. 55
      src/Avalonia.Controls/Documents/InlineCollection.cs
  18. 19
      src/Avalonia.Controls/TextBlock.cs
  19. 4
      src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs
  20. 4
      src/Skia/Avalonia.Skia/Gpu/OpenGl/OpenGlBitmapImpl.cs
  21. 33
      src/Skia/Avalonia.Skia/Helpers/ImageSavingHelper.cs
  22. 4
      src/Skia/Avalonia.Skia/ImmutableBitmap.cs
  23. 8
      src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs
  24. 8
      src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs
  25. 6
      src/Windows/Avalonia.Direct2D1/Media/Imaging/BitmapImpl.cs
  26. 2
      src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DBitmapImpl.cs
  27. 2
      src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs
  28. 2
      src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs
  29. 6
      src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
  30. 3
      src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositorConnection.cs
  31. 11
      src/Windows/Avalonia.Win32/WinRT/winrt.idl
  32. 22
      tests/Avalonia.Controls.UnitTests/TextBlockTests.cs

16
src/Avalonia.Base/Controls/NameScopeEventArgs.cs

@ -1,16 +0,0 @@
using System;
namespace Avalonia.Controls
{
public class NameScopeEventArgs : EventArgs
{
public NameScopeEventArgs(string name, object element)
{
Name = name;
Element = element;
}
public string Name { get; }
public object Element { get; }
}
}

2
src/Avalonia.Base/Input/DragEventArgs.cs

@ -32,7 +32,7 @@ namespace Avalonia.Input
return point; return point;
} }
public DragEventArgs(RoutedEvent<DragEventArgs> routedEvent, IDataObject data, Interactive target, Point targetLocation, KeyModifiers keyModifiers) internal DragEventArgs(RoutedEvent<DragEventArgs> routedEvent, IDataObject data, Interactive target, Point targetLocation, KeyModifiers keyModifiers)
: base(routedEvent) : base(routedEvent)
{ {
Data = data; Data = data;

5
src/Avalonia.Base/Input/GotFocusEventArgs.cs

@ -7,6 +7,11 @@ namespace Avalonia.Input
/// </summary> /// </summary>
public class GotFocusEventArgs : RoutedEventArgs public class GotFocusEventArgs : RoutedEventArgs
{ {
internal GotFocusEventArgs()
{
}
/// <summary> /// <summary>
/// Gets or sets a value indicating how the change in focus occurred. /// Gets or sets a value indicating how the change in focus occurred.
/// </summary> /// </summary>

5
src/Avalonia.Base/Input/KeyEventArgs.cs

@ -5,6 +5,11 @@ namespace Avalonia.Input
{ {
public class KeyEventArgs : RoutedEventArgs public class KeyEventArgs : RoutedEventArgs
{ {
internal KeyEventArgs()
{
}
public IKeyboardDevice? Device { get; set; } public IKeyboardDevice? Device { get; set; }
public Key Key { get; set; } public Key Key { get; set; }

2
src/Avalonia.Base/Input/PointerDeltaEventArgs.cs

@ -7,7 +7,7 @@ namespace Avalonia.Input
{ {
public Vector Delta { get; set; } public Vector Delta { get; set; }
public PointerDeltaEventArgs(RoutedEvent routedEvent, IInteractive? source, internal PointerDeltaEventArgs(RoutedEvent routedEvent, IInteractive? source,
IPointer pointer, IVisual rootVisual, Point rootVisualPosition, ulong timestamp, IPointer pointer, IVisual rootVisual, Point rootVisualPosition, ulong timestamp,
PointerPointProperties properties, KeyModifiers modifiers, Vector delta) PointerPointProperties properties, KeyModifiers modifiers, Vector delta)
: base(routedEvent, source, pointer, rootVisual, rootVisualPosition, : base(routedEvent, source, pointer, rootVisual, rootVisualPosition,

12
src/Avalonia.Base/Input/PointerEventArgs.cs

@ -13,7 +13,7 @@ namespace Avalonia.Input
private readonly PointerPointProperties _properties; private readonly PointerPointProperties _properties;
private readonly Lazy<IReadOnlyList<RawPointerPoint>?>? _previousPoints; private readonly Lazy<IReadOnlyList<RawPointerPoint>?>? _previousPoints;
public PointerEventArgs(RoutedEvent routedEvent, internal PointerEventArgs(RoutedEvent routedEvent,
IInteractive? source, IInteractive? source,
IPointer pointer, IPointer pointer,
IVisual? rootVisual, Point rootVisualPosition, IVisual? rootVisual, Point rootVisualPosition,
@ -30,8 +30,8 @@ namespace Avalonia.Input
Timestamp = timestamp; Timestamp = timestamp;
KeyModifiers = modifiers; KeyModifiers = modifiers;
} }
public PointerEventArgs(RoutedEvent routedEvent, internal PointerEventArgs(RoutedEvent routedEvent,
IInteractive? source, IInteractive? source,
IPointer pointer, IPointer pointer,
IVisual? rootVisual, Point rootVisualPosition, IVisual? rootVisual, Point rootVisualPosition,
@ -124,7 +124,7 @@ namespace Avalonia.Input
public class PointerPressedEventArgs : PointerEventArgs public class PointerPressedEventArgs : PointerEventArgs
{ {
public PointerPressedEventArgs( internal PointerPressedEventArgs(
IInteractive source, IInteractive source,
IPointer pointer, IPointer pointer,
IVisual rootVisual, Point rootVisualPosition, IVisual rootVisual, Point rootVisualPosition,
@ -143,7 +143,7 @@ namespace Avalonia.Input
public class PointerReleasedEventArgs : PointerEventArgs public class PointerReleasedEventArgs : PointerEventArgs
{ {
public PointerReleasedEventArgs( internal PointerReleasedEventArgs(
IInteractive source, IPointer pointer, IInteractive source, IPointer pointer,
IVisual rootVisual, Point rootVisualPosition, ulong timestamp, IVisual rootVisual, Point rootVisualPosition, ulong timestamp,
PointerPointProperties properties, KeyModifiers modifiers, PointerPointProperties properties, KeyModifiers modifiers,
@ -164,7 +164,7 @@ namespace Avalonia.Input
{ {
public IPointer Pointer { get; } public IPointer Pointer { get; }
public PointerCaptureLostEventArgs(IInteractive source, IPointer pointer) : base(InputElement.PointerCaptureLostEvent) internal PointerCaptureLostEventArgs(IInteractive source, IPointer pointer) : base(InputElement.PointerCaptureLostEvent)
{ {
Pointer = pointer; Pointer = pointer;
Source = source; Source = source;

2
src/Avalonia.Base/Input/PointerWheelEventArgs.cs

@ -7,7 +7,7 @@ namespace Avalonia.Input
{ {
public Vector Delta { get; set; } public Vector Delta { get; set; }
public PointerWheelEventArgs(IInteractive source, IPointer pointer, IVisual rootVisual, internal PointerWheelEventArgs(IInteractive source, IPointer pointer, IVisual rootVisual,
Point rootVisualPosition, ulong timestamp, Point rootVisualPosition, ulong timestamp,
PointerPointProperties properties, KeyModifiers modifiers, Vector delta) PointerPointProperties properties, KeyModifiers modifiers, Vector delta)
: base(InputElement.PointerWheelChangedEvent, source, pointer, rootVisual, rootVisualPosition, : base(InputElement.PointerWheelChangedEvent, source, pointer, rootVisual, rootVisualPosition,

6
src/Avalonia.Base/Input/ScrollGestureEventArgs.cs

@ -9,8 +9,8 @@ namespace Avalonia.Input
private static int _nextId = 1; private static int _nextId = 1;
public static int GetNextFreeId() => _nextId++; public static int GetNextFreeId() => _nextId++;
public ScrollGestureEventArgs(int id, Vector delta) : base(Gestures.ScrollGestureEvent) internal ScrollGestureEventArgs(int id, Vector delta) : base(Gestures.ScrollGestureEvent)
{ {
Id = id; Id = id;
Delta = delta; Delta = delta;
@ -21,7 +21,7 @@ namespace Avalonia.Input
{ {
public int Id { get; } public int Id { get; }
public ScrollGestureEndedEventArgs(int id) : base(Gestures.ScrollGestureEndedEvent) internal ScrollGestureEndedEventArgs(int id) : base(Gestures.ScrollGestureEndedEvent)
{ {
Id = id; Id = id;
} }

2
src/Avalonia.Base/Input/TappedEventArgs.cs

@ -7,7 +7,7 @@ namespace Avalonia.Input
{ {
private readonly PointerEventArgs lastPointerEventArgs; private readonly PointerEventArgs lastPointerEventArgs;
public TappedEventArgs(RoutedEvent routedEvent, PointerEventArgs lastPointerEventArgs) internal TappedEventArgs(RoutedEvent routedEvent, PointerEventArgs lastPointerEventArgs)
: base(routedEvent) : base(routedEvent)
{ {
this.lastPointerEventArgs = lastPointerEventArgs; this.lastPointerEventArgs = lastPointerEventArgs;

4
src/Avalonia.Base/Input/TextInputEventArgs.cs

@ -4,6 +4,10 @@ namespace Avalonia.Input
{ {
public class TextInputEventArgs : RoutedEventArgs public class TextInputEventArgs : RoutedEventArgs
{ {
internal TextInputEventArgs()
{
}
public IKeyboardDevice? Device { get; set; } public IKeyboardDevice? Device { get; set; }
public string? Text { get; set; } public string? Text { get; set; }

5
src/Avalonia.Base/Input/VectorEventArgs.cs

@ -5,6 +5,11 @@ namespace Avalonia.Input
{ {
public class VectorEventArgs : RoutedEventArgs public class VectorEventArgs : RoutedEventArgs
{ {
internal VectorEventArgs()
{
}
public Vector Vector { get; set; } public Vector Vector { get; set; }
} }
} }

2
src/Avalonia.Base/Layout/EffectiveViewportChangedEventArgs.cs

@ -7,7 +7,7 @@ namespace Avalonia.Layout
/// </summary> /// </summary>
public class EffectiveViewportChangedEventArgs : EventArgs public class EffectiveViewportChangedEventArgs : EventArgs
{ {
public EffectiveViewportChangedEventArgs(Rect effectiveViewport) internal EffectiveViewportChangedEventArgs(Rect effectiveViewport)
{ {
EffectiveViewport = effectiveViewport; EffectiveViewport = effectiveViewport;
} }

18
src/Avalonia.Base/Media/Imaging/Bitmap.cs

@ -121,18 +121,28 @@ namespace Avalonia.Media.Imaging
/// Saves the bitmap to a file. /// Saves the bitmap to a file.
/// </summary> /// </summary>
/// <param name="fileName">The filename.</param> /// <param name="fileName">The filename.</param>
public void Save(string fileName) /// <param name="quality">
/// The optional quality for compression.
/// The quality value is interpreted from 0 - 100. If quality is null the default quality
/// setting is applied.
/// </param>
public void Save(string fileName, int? quality = null)
{ {
PlatformImpl.Item.Save(fileName); PlatformImpl.Item.Save(fileName, quality);
} }
/// <summary> /// <summary>
/// Saves the bitmap to a stream. /// Saves the bitmap to a stream.
/// </summary> /// </summary>
/// <param name="stream">The stream.</param> /// <param name="stream">The stream.</param>
public void Save(Stream stream) /// <param name="quality">
/// The optional quality for compression.
/// The quality value is interpreted from 0 - 100. If quality is null the default quality
/// setting is applied.
/// </param>
public void Save(Stream stream, int? quality = null)
{ {
PlatformImpl.Item.Save(stream); PlatformImpl.Item.Save(stream, quality);
} }
/// <inheritdoc/> /// <inheritdoc/>

14
src/Avalonia.Base/Media/Imaging/IBitmap.cs

@ -35,12 +35,22 @@ namespace Avalonia.Media.Imaging
/// Saves the bitmap to a file. /// Saves the bitmap to a file.
/// </summary> /// </summary>
/// <param name="fileName">The filename.</param> /// <param name="fileName">The filename.</param>
void Save(string fileName); /// <param name="quality">
/// The optional quality for compression if supported by the specific backend.
/// The quality value is interpreted from 0 - 100. If quality is null the default quality
/// setting of the backend is applied.
/// </param>
void Save(string fileName, int? quality = null);
/// <summary> /// <summary>
/// Saves the bitmap to a stream in png format. /// Saves the bitmap to a stream in png format.
/// </summary> /// </summary>
/// <param name="stream">The stream.</param> /// <param name="stream">The stream.</param>
void Save(Stream stream); /// <param name="quality">
/// The optional quality for compression if supported by the specific backend.
/// The quality value is interpreted from 0 - 100. If quality is null the default quality
/// setting of the backend is applied.
/// </param>
void Save(Stream stream, int? quality = null);
} }
} }

14
src/Avalonia.Base/Platform/IBitmapImpl.cs

@ -29,12 +29,22 @@ namespace Avalonia.Platform
/// Saves the bitmap to a file. /// Saves the bitmap to a file.
/// </summary> /// </summary>
/// <param name="fileName">The filename.</param> /// <param name="fileName">The filename.</param>
void Save(string fileName); /// <param name="quality">
/// The optional quality for compression if supported by the specific backend.
/// The quality value is interpreted from 0 - 100. If quality is null the default quality
/// setting of the backend is applied.
/// </param>
void Save(string fileName, int? quality = null);
/// <summary> /// <summary>
/// Saves the bitmap to a stream in png format. /// Saves the bitmap to a stream in png format.
/// </summary> /// </summary>
/// <param name="stream">The stream.</param> /// <param name="stream">The stream.</param>
void Save(Stream stream); /// <param name="quality">
/// The optional quality for compression if supported by the specific backend.
/// The quality value is interpreted from 0 - 100. If quality is null the default quality
/// setting of the backend is applied.
/// </param>
void Save(Stream stream, int? quality = null);
} }
} }

2
src/Avalonia.Base/Rendering/SceneInvalidatedEventArgs.cs

@ -12,7 +12,7 @@ namespace Avalonia.Rendering
/// </summary> /// </summary>
/// <param name="root">The render root that has been updated.</param> /// <param name="root">The render root that has been updated.</param>
/// <param name="dirtyRect">The updated area.</param> /// <param name="dirtyRect">The updated area.</param>
public SceneInvalidatedEventArgs( internal SceneInvalidatedEventArgs(
IRenderRoot root, IRenderRoot root,
Rect dirtyRect) Rect dirtyRect)
{ {

55
src/Avalonia.Controls/Documents/InlineCollection.cs

@ -89,56 +89,45 @@ namespace Avalonia.Controls.Documents
} }
public override void Add(Inline inline)
{
if (InlineHost is TextBlock textBlock && !string.IsNullOrEmpty(textBlock._text))
{
base.Add(new Run(textBlock._text));
textBlock._text = null;
}
base.Add(inline);
}
/// <summary> /// <summary>
/// Add a text segment to the collection. /// Adds a text segment to the collection.
/// <remarks> /// <remarks>
/// For non complex content this appends the text to the end of currently held text. /// For non complex content this appends the text to the end of currently held text.
/// For complex content this adds a <see cref="Run"/> to the collection. /// For complex content this adds a <see cref="Run"/> to the collection.
/// </remarks> /// </remarks>
/// </summary> /// </summary>
/// <param name="text"></param> /// <param name="text">The to be added text.</param>
public void Add(string text) public void Add(string text)
{ {
AddText(text); if (InlineHost is TextBlock textBlock && !textBlock.HasComplexContent)
}
public override void Add(Inline inline)
{
OnAdd();
base.Add(inline);
}
public void Add(IControl child)
{
OnAdd();
base.Add(new InlineUIContainer(child));
}
private void AddText(string text)
{
if (LogicalChildren is TextBlock textBlock && !textBlock.HasComplexContent)
{ {
textBlock._text += text; textBlock._text += text;
} }
else else
{ {
base.Add(new Run(text)); Add(new Run(text));
} }
} }
private void OnAdd() /// <summary>
/// Adds a control wrapped inside a <see cref="InlineUIContainer"/> to the collection.
/// </summary>
/// <param name="control">The to be added control.</param>
public void Add(IControl control)
{ {
if (LogicalChildren is TextBlock textBlock) Add(new InlineUIContainer(control));
{
if (!textBlock.HasComplexContent && !string.IsNullOrEmpty(textBlock._text))
{
base.Add(new Run(textBlock._text));
textBlock._text = null;
}
}
} }
/// <summary> /// <summary>

19
src/Avalonia.Controls/TextBlock.cs

@ -597,23 +597,12 @@ namespace Avalonia.Controls
protected virtual void SetText(string? text) protected virtual void SetText(string? text)
{ {
if (Inlines != null && Inlines.Count > 0) if (HasComplexContent)
{
var oldValue = Inlines.Text;
if (!string.IsNullOrEmpty(text))
{
Inlines.Add(text);
}
text = Inlines.Text;
RaisePropertyChanged(TextProperty, oldValue, text);
}
else
{ {
SetAndRaise(TextProperty, ref _text, text); Inlines?.Clear();
} }
SetAndRaise(TextProperty, ref _text, text);
} }
/// <summary> /// <summary>

4
src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs

@ -323,12 +323,12 @@ namespace Avalonia.Headless
public Vector Dpi { get; } public Vector Dpi { get; }
public PixelSize PixelSize { get; } public PixelSize PixelSize { get; }
public int Version { get; set; } public int Version { get; set; }
public void Save(string fileName) public void Save(string fileName, int? quality = null)
{ {
} }
public void Save(Stream stream) public void Save(Stream stream, int? quality = null)
{ {
} }

4
src/Skia/Avalonia.Skia/Gpu/OpenGl/OpenGlBitmapImpl.cs

@ -25,9 +25,9 @@ namespace Avalonia.Skia
public Vector Dpi { get; } public Vector Dpi { get; }
public PixelSize PixelSize { get; } public PixelSize PixelSize { get; }
public int Version { get; private set; } public int Version { get; private set; }
public void Save(string fileName) => throw new NotSupportedException(); public void Save(string fileName, int? quality = null) => throw new NotSupportedException();
public void Save(Stream stream) => throw new NotSupportedException(); public void Save(Stream stream, int? quality = null) => throw new NotSupportedException();
public void Draw(DrawingContextImpl context, SKRect sourceRect, SKRect destRect, SKPaint paint) public void Draw(DrawingContextImpl context, SKRect sourceRect, SKRect destRect, SKPaint paint)
{ {

33
src/Skia/Avalonia.Skia/Helpers/ImageSavingHelper.cs

@ -14,14 +14,19 @@ namespace Avalonia.Skia.Helpers
/// </summary> /// </summary>
/// <param name="image">Image to save</param> /// <param name="image">Image to save</param>
/// <param name="fileName">Target file.</param> /// <param name="fileName">Target file.</param>
public static void SaveImage(SKImage image, string fileName) /// <param name="quality">
/// The optional quality for PNG compression.
/// The quality value is interpreted from 0 - 100. If quality is null
/// the encoder applies the default quality value.
/// </param>
public static void SaveImage(SKImage image, string fileName, int? quality = null)
{ {
if (image == null) throw new ArgumentNullException(nameof(image)); if (image == null) throw new ArgumentNullException(nameof(image));
if (fileName == null) throw new ArgumentNullException(nameof(fileName)); if (fileName == null) throw new ArgumentNullException(nameof(fileName));
using (var stream = File.Create(fileName)) using (var stream = File.Create(fileName))
{ {
SaveImage(image, stream); SaveImage(image, stream, quality);
} }
} }
@ -29,16 +34,30 @@ namespace Avalonia.Skia.Helpers
/// Save Skia image to a stream. /// Save Skia image to a stream.
/// </summary> /// </summary>
/// <param name="image">Image to save</param> /// <param name="image">Image to save</param>
/// <param name="stream">Target stream.</param> /// <param name="quality">
public static void SaveImage(SKImage image, Stream stream) /// The optional quality for PNG compression.
/// The quality value is interpreted from 0 - 100. If quality is null
/// the encoder applies the default quality value.
/// </param>
public static void SaveImage(SKImage image, Stream stream, int? quality = null)
{ {
if (image == null) throw new ArgumentNullException(nameof(image)); if (image == null) throw new ArgumentNullException(nameof(image));
if (stream == null) throw new ArgumentNullException(nameof(stream)); if (stream == null) throw new ArgumentNullException(nameof(stream));
using (var data = image.Encode()) if (quality == null)
{ {
data.SaveTo(stream); using (var data = image.Encode())
{
data.SaveTo(stream);
}
}
else
{
using (var data = image.Encode(SKEncodedImageFormat.Png, (int)quality))
{
data.SaveTo(stream);
}
} }
} }
} }
} }

4
src/Skia/Avalonia.Skia/ImmutableBitmap.cs

@ -139,13 +139,13 @@ namespace Avalonia.Skia
} }
/// <inheritdoc /> /// <inheritdoc />
public void Save(string fileName) public void Save(string fileName, int? quality = null)
{ {
ImageSavingHelper.SaveImage(_image, fileName); ImageSavingHelper.SaveImage(_image, fileName);
} }
/// <inheritdoc /> /// <inheritdoc />
public void Save(Stream stream) public void Save(Stream stream, int? quality = null)
{ {
ImageSavingHelper.SaveImage(_image, stream); ImageSavingHelper.SaveImage(_image, stream);
} }

8
src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs

@ -116,20 +116,20 @@ namespace Avalonia.Skia
public int Version { get; private set; } = 1; public int Version { get; private set; } = 1;
/// <inheritdoc /> /// <inheritdoc />
public void Save(string fileName) public void Save(string fileName, int? quality = null)
{ {
using (var image = SnapshotImage()) using (var image = SnapshotImage())
{ {
ImageSavingHelper.SaveImage(image, fileName); ImageSavingHelper.SaveImage(image, fileName, quality);
} }
} }
/// <inheritdoc /> /// <inheritdoc />
public void Save(Stream stream) public void Save(Stream stream, int? quality = null)
{ {
using (var image = SnapshotImage()) using (var image = SnapshotImage())
{ {
ImageSavingHelper.SaveImage(image, stream); ImageSavingHelper.SaveImage(image, stream, quality);
} }
} }

8
src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs

@ -137,20 +137,20 @@ namespace Avalonia.Skia
} }
/// <inheritdoc /> /// <inheritdoc />
public void Save(Stream stream) public void Save(Stream stream, int? quality = null)
{ {
using (var image = GetSnapshot()) using (var image = GetSnapshot())
{ {
ImageSavingHelper.SaveImage(image, stream); ImageSavingHelper.SaveImage(image, stream, quality);
} }
} }
/// <inheritdoc /> /// <inheritdoc />
public void Save(string fileName) public void Save(string fileName, int? quality = null)
{ {
using (var image = GetSnapshot()) using (var image = GetSnapshot())
{ {
ImageSavingHelper.SaveImage(image, fileName); ImageSavingHelper.SaveImage(image, fileName, quality);
} }
} }

6
src/Windows/Avalonia.Direct2D1/Media/Imaging/BitmapImpl.cs

@ -15,7 +15,7 @@ namespace Avalonia.Direct2D1.Media
public abstract OptionalDispose<D2DBitmap> GetDirect2DBitmap(SharpDX.Direct2D1.RenderTarget target); public abstract OptionalDispose<D2DBitmap> GetDirect2DBitmap(SharpDX.Direct2D1.RenderTarget target);
public void Save(string fileName) public void Save(string fileName, int? quality = null)
{ {
if (Path.GetExtension(fileName) != ".png") if (Path.GetExtension(fileName) != ".png")
{ {
@ -25,11 +25,11 @@ namespace Avalonia.Direct2D1.Media
using (FileStream s = new FileStream(fileName, FileMode.Create)) using (FileStream s = new FileStream(fileName, FileMode.Create))
{ {
Save(s); Save(s, quality);
} }
} }
public abstract void Save(Stream stream); public abstract void Save(Stream stream, int? quality = null);
public virtual void Dispose() public virtual void Dispose()
{ {

2
src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DBitmapImpl.cs

@ -43,7 +43,7 @@ namespace Avalonia.Direct2D1.Media
return new OptionalDispose<Bitmap>(_direct2DBitmap, false); return new OptionalDispose<Bitmap>(_direct2DBitmap, false);
} }
public override void Save(Stream stream) public override void Save(Stream stream, int? quality = null)
{ {
using (var encoder = new PngBitmapEncoder(Direct2D1Platform.ImagingFactory, stream)) using (var encoder = new PngBitmapEncoder(Direct2D1Platform.ImagingFactory, stream))
using (var frame = new BitmapFrameEncode(encoder)) using (var frame = new BitmapFrameEncode(encoder))

2
src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs

@ -56,7 +56,7 @@ namespace Avalonia.Direct2D1.Media.Imaging
return new OptionalDispose<D2DBitmap>(_renderTarget.Bitmap, false); return new OptionalDispose<D2DBitmap>(_renderTarget.Bitmap, false);
} }
public override void Save(Stream stream) public override void Save(Stream stream, int? quality = null)
{ {
using (var wic = new WicRenderTargetBitmapImpl(PixelSize, Dpi)) using (var wic = new WicRenderTargetBitmapImpl(PixelSize, Dpi))
{ {

2
src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs

@ -186,7 +186,7 @@ namespace Avalonia.Direct2D1.Media
return new OptionalDispose<D2DBitmap>(D2DBitmap.FromWicBitmap(renderTarget, converter), true); return new OptionalDispose<D2DBitmap>(D2DBitmap.FromWicBitmap(renderTarget, converter), true);
} }
public override void Save(Stream stream) public override void Save(Stream stream, int? quality = null)
{ {
using (var encoder = new PngBitmapEncoder(Direct2D1Platform.ImagingFactory, stream)) using (var encoder = new PngBitmapEncoder(Direct2D1Platform.ImagingFactory, stream))
using (var frame = new BitmapFrameEncode(encoder)) using (var frame = new BitmapFrameEncode(encoder))

6
src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs

@ -2118,6 +2118,12 @@ namespace Avalonia.Win32.Interop
public int Y; public int Y;
} }
public struct SIZE_F
{
public float X;
public float Y;
}
public struct RECT public struct RECT
{ {
public int left; public int left;

3
src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositorConnection.cs

@ -174,8 +174,9 @@ namespace Avalonia.Win32.WinRT.Composition
using var sc = _syncContext.EnsureLocked(); using var sc = _syncContext.EnsureLocked();
using var desktopTarget = _compositorDesktopInterop.CreateDesktopWindowTarget(hWnd, 0); using var desktopTarget = _compositorDesktopInterop.CreateDesktopWindowTarget(hWnd, 0);
using var target = desktopTarget.QueryInterface<ICompositionTarget>(); using var target = desktopTarget.QueryInterface<ICompositionTarget>();
using var device2 = _device.QueryInterface<ICompositionGraphicsDevice2>();
using var drawingSurface = _device.CreateDrawingSurface(new UnmanagedMethods.SIZE(), DirectXPixelFormat.B8G8R8A8UIntNormalized, using var drawingSurface = device2.CreateDrawingSurface2(new UnmanagedMethods.SIZE(), DirectXPixelFormat.B8G8R8A8UIntNormalized,
DirectXAlphaMode.Premultiplied); DirectXAlphaMode.Premultiplied);
using var surface = drawingSurface.QueryInterface<ICompositionSurface>(); using var surface = drawingSurface.QueryInterface<ICompositionSurface>();
using var surfaceInterop = drawingSurface.QueryInterface<ICompositionDrawingSurfaceInterop>(); using var surfaceInterop = drawingSurface.QueryInterface<ICompositionDrawingSurfaceInterop>();

11
src/Windows/Avalonia.Win32/WinRT/winrt.idl

@ -8,6 +8,7 @@
@clr-map Matrix4x4 System.Numerics.Matrix4x4 @clr-map Matrix4x4 System.Numerics.Matrix4x4
@clr-map RECT Avalonia.Win32.Interop.UnmanagedMethods.RECT @clr-map RECT Avalonia.Win32.Interop.UnmanagedMethods.RECT
@clr-map SIZE Avalonia.Win32.Interop.UnmanagedMethods.SIZE @clr-map SIZE Avalonia.Win32.Interop.UnmanagedMethods.SIZE
@clr-map SIZE_F Avalonia.Win32.Interop.UnmanagedMethods.SIZE_F
@clr-map POINT Avalonia.Win32.Interop.UnmanagedMethods.POINT @clr-map POINT Avalonia.Win32.Interop.UnmanagedMethods.POINT
@clr-map HWND IntPtr @clr-map HWND IntPtr
@clr-map BOOL int @clr-map BOOL int
@ -442,12 +443,18 @@ interface IDesktopWindowContentBridgeInterop : IUnknown
[uuid(FB22C6E1-80A2-4667-9936-DBEAF6EEFE95)] [uuid(FB22C6E1-80A2-4667-9936-DBEAF6EEFE95)]
interface ICompositionGraphicsDevice : IInspectable interface ICompositionGraphicsDevice : IInspectable
{ {
HRESULT CreateDrawingSurface([in] SIZE sizePixels, [in] DirectXPixelFormat pixelFormat, HRESULT CreateDrawingSurface([in] SIZE_F sizePixels, [in] DirectXPixelFormat pixelFormat,
[in] DirectXAlphaMode alphaMode, [out] [retval] ICompositionDrawingSurface** result); [in] DirectXAlphaMode alphaMode, [out] [retval] ICompositionDrawingSurface** result);
HRESULT AddRenderingDeviceReplaced(void* handler, void* token); HRESULT AddRenderingDeviceReplaced(void* handler, void* token);
HRESULT RemoveRenderingDeviceReplaced([in] int token); HRESULT RemoveRenderingDeviceReplaced([in] int token);
} }
[uuid(0FB8BDF6-C0F0-4BCC-9FB8-084982490D7D)]
interface ICompositionGraphicsDevice2 : IInspectable
{
HRESULT CreateDrawingSurface2([in] SIZE sizePixels, [in] DirectXPixelFormat pixelFormat, [in] DirectXAlphaMode alphaMode, [out] [retval] ICompositionDrawingSurface** result);
}
[uuid(1527540D-42C7-47A6-A408-668F79A90DFB)] [uuid(1527540D-42C7-47A6-A408-668F79A90DFB)]
interface ICompositionSurface : IInspectable interface ICompositionSurface : IInspectable
{ {
@ -465,7 +472,7 @@ interface ICompositionDrawingSurface : IInspectable
{ {
[propget] HRESULT GetAlphaMode([out] [retval] DirectXAlphaMode* value); [propget] HRESULT GetAlphaMode([out] [retval] DirectXAlphaMode* value);
[propget] HRESULT GetPixelFormat([out] [retval] DirectXPixelFormat* value); [propget] HRESULT GetPixelFormat([out] [retval] DirectXPixelFormat* value);
[propget] HRESULT GetSize([out] [retval] POINT* value); [propget] HRESULT GetSize([out] [retval] SIZE_F* value);
} }
enum CompositionBitmapInterpolationMode enum CompositionBitmapInterpolationMode

22
tests/Avalonia.Controls.UnitTests/TextBlockTests.cs

@ -3,6 +3,7 @@ using Avalonia.Controls.Documents;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Metadata;
using Avalonia.Rendering; using Avalonia.Rendering;
using Avalonia.UnitTests; using Avalonia.UnitTests;
using Moq; using Moq;
@ -181,5 +182,26 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(60, button.Bounds.Left); Assert.Equal(60, button.Bounds.Left);
} }
} }
[Fact]
public void Setting_Text_Should_Reset_Inlines()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var target = new TextBlock();
target.Inlines.Add(new Run("Hello World"));
Assert.Equal("Hello World", target.Text);
Assert.Equal(1, target.Inlines.Count);
target.Text = "1234";
Assert.Equal("1234", target.Text);
Assert.Equal(0, target.Inlines.Count);
}
}
} }
} }

Loading…
Cancel
Save