diff --git a/Avalonia.sln b/Avalonia.sln
index 4999719676..35b6b2108a 100644
--- a/Avalonia.sln
+++ b/Avalonia.sln
@@ -13,7 +13,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Direct2D1", "src\W
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls", "src\Avalonia.Controls\Avalonia.Controls.csproj", "{D2221C82-4A25-4583-9B43-D791E3F6820C}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Themes.Default", "src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj", "{3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Themes.Simple", "src\Avalonia.Themes.Simple\Avalonia.Themes.Simple.csproj", "{3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Diagnostics", "src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj", "{7062AE20-5DCC-4442-9645-8195BDECE63E}"
EndProject
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index edf3c3d819..52fc8db53c 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -59,7 +59,7 @@ jobs:
variables:
SolutionDir: '$(Build.SourcesDirectory)'
pool:
- vmImage: 'macOS-10.15'
+ vmImage: 'macos-12'
steps:
- task: UseDotNet@2
displayName: 'Use .NET Core SDK 3.1.418'
@@ -91,10 +91,10 @@ jobs:
inputs:
actions: 'build'
scheme: ''
- sdk: 'macosx11.1'
+ sdk: 'macosx12.3'
configuration: 'Release'
xcWorkspacePath: '**/*.xcodeproj/project.xcworkspace'
- xcodeVersion: '12' # Options: 8, 9, default, specifyPath
+ xcodeVersion: '13' # Options: 8, 9, default, specifyPath
args: '-derivedDataPath ./'
- task: CmdLine@2
diff --git a/samples/BindingDemo/App.xaml b/samples/BindingDemo/App.xaml
index 3e312c8685..175e838616 100644
--- a/samples/BindingDemo/App.xaml
+++ b/samples/BindingDemo/App.xaml
@@ -4,6 +4,6 @@
x:Class="BindingDemo.App">
-
+
diff --git a/samples/ControlCatalog/App.xaml.cs b/samples/ControlCatalog/App.xaml.cs
index 7ebb87094a..750c1082a6 100644
--- a/samples/ControlCatalog/App.xaml.cs
+++ b/samples/ControlCatalog/App.xaml.cs
@@ -5,7 +5,7 @@ using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using Avalonia.Markup.Xaml.Styling;
using Avalonia.Styling;
-using Avalonia.Themes.Default;
+using Avalonia.Themes.Simple;
using Avalonia.Themes.Fluent;
using ControlCatalog.ViewModels;
@@ -23,9 +23,9 @@ namespace ControlCatalog
Source = new Uri("avares://Avalonia.Controls.ColorPicker/Themes/Fluent/Fluent.xaml")
};
- public static readonly StyleInclude ColorPickerDefault = new StyleInclude(new Uri("avares://ControlCatalog/Styles"))
+ public static readonly StyleInclude ColorPickerSimple = new StyleInclude(new Uri("avares://ControlCatalog/Styles"))
{
- Source = new Uri("avares://Avalonia.Controls.ColorPicker/Themes/Default/Default.xaml")
+ Source = new Uri("avares://Avalonia.Controls.ColorPicker/Themes/Simple/Simple.xaml")
};
public static readonly StyleInclude DataGridFluent = new StyleInclude(new Uri("avares://ControlCatalog/Styles"))
@@ -33,16 +33,16 @@ namespace ControlCatalog
Source = new Uri("avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml")
};
- public static readonly StyleInclude DataGridDefault = new StyleInclude(new Uri("avares://ControlCatalog/Styles"))
+ public static readonly StyleInclude DataGridSimple = new StyleInclude(new Uri("avares://ControlCatalog/Styles"))
{
- Source = new Uri("avares://Avalonia.Controls.DataGrid/Themes/Default.xaml")
+ Source = new Uri("avares://Avalonia.Controls.DataGrid/Themes/Simple.xaml")
};
public static FluentTheme Fluent = new FluentTheme(new Uri("avares://ControlCatalog/Styles"));
- public static SimpleTheme Default = new SimpleTheme(new Uri("avares://ControlCatalog/Styles"));
+ public static SimpleTheme Simple = new SimpleTheme(new Uri("avares://ControlCatalog/Styles"));
- public static Styles DefaultLight = new Styles
+ public static Styles SimpleLight = new Styles
{
new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
{
@@ -56,10 +56,10 @@ namespace ControlCatalog
{
Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/BaseLight.xaml")
},
- Default
+ Simple
};
- public static Styles DefaultDark = new Styles
+ public static Styles SimpleDark = new Styles
{
new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
{
@@ -73,7 +73,7 @@ namespace ControlCatalog
{
Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/BaseDark.xaml")
},
- Default
+ Simple
};
public override void Initialize()
diff --git a/samples/ControlCatalog/ControlCatalog.csproj b/samples/ControlCatalog/ControlCatalog.csproj
index 8358fb3cd4..2654574a3e 100644
--- a/samples/ControlCatalog/ControlCatalog.csproj
+++ b/samples/ControlCatalog/ControlCatalog.csproj
@@ -29,7 +29,7 @@
-
+
diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml
index 7461e78c33..7f5a191519 100644
--- a/samples/ControlCatalog/MainView.xaml
+++ b/samples/ControlCatalog/MainView.xaml
@@ -187,8 +187,8 @@
FluentLight
FluentDark
- DefaultLight
- DefaultDark
+ SimpleLight
+ SimpleDark
? items)
{
items ??= Array.Empty();
- var mappedResults = items.Select(FullPathOrName).ToList();
- bookmarkContainer.Text = items.FirstOrDefault(f => f.CanBookmark) is { } f ? await f.SaveBookmark() : "Can't bookmark";
+ bookmarkContainer.Text = items.FirstOrDefault(f => f.CanBookmark) is { } f ? await f.SaveBookmarkAsync() : "Can't bookmark";
+ var mappedResults = new List();
if (items.FirstOrDefault() is IStorageItem item)
{
@@ -267,9 +267,9 @@ Content:
if (file.CanOpenRead)
{
#if NET6_0_OR_GREATER
- await using var stream = await file.OpenRead();
+ await using var stream = await file.OpenReadAsync();
#else
- using var stream = await file.OpenRead();
+ using var stream = await file.OpenReadAsync();
#endif
using var reader = new System.IO.StreamReader(stream);
@@ -293,7 +293,19 @@ Content:
lastSelectedDirectory = await item.GetParentAsync();
if (lastSelectedDirectory is not null)
{
- mappedResults.Insert(0, "Parent: " + FullPathOrName(lastSelectedDirectory));
+ mappedResults.Add(FullPathOrName(lastSelectedDirectory));
+ }
+
+ foreach (var selectedItem in items)
+ {
+ mappedResults.Add("+> " + FullPathOrName(selectedItem));
+ if (selectedItem is IStorageFolder folder)
+ {
+ foreach (var innerItems in await folder.GetItemsAsync())
+ {
+ mappedResults.Add("++> " + FullPathOrName(innerItems));
+ }
+ }
}
}
diff --git a/samples/PlatformSanityChecks/App.xaml b/samples/PlatformSanityChecks/App.xaml
index 25bab6ae35..1b9d64fca6 100644
--- a/samples/PlatformSanityChecks/App.xaml
+++ b/samples/PlatformSanityChecks/App.xaml
@@ -1,6 +1,5 @@
-
-
+
diff --git a/samples/PlatformSanityChecks/PlatformSanityChecks.csproj b/samples/PlatformSanityChecks/PlatformSanityChecks.csproj
index 4f7f06b529..5c743aabdb 100644
--- a/samples/PlatformSanityChecks/PlatformSanityChecks.csproj
+++ b/samples/PlatformSanityChecks/PlatformSanityChecks.csproj
@@ -7,7 +7,7 @@
-
+
diff --git a/samples/Previewer/App.xaml b/samples/Previewer/App.xaml
index 6bae1955af..1b9d64fca6 100644
--- a/samples/Previewer/App.xaml
+++ b/samples/Previewer/App.xaml
@@ -1,6 +1,5 @@
-
-
+
-
\ No newline at end of file
+
diff --git a/samples/Previewer/Previewer.csproj b/samples/Previewer/Previewer.csproj
index 98560e9ab0..2cc84168dc 100644
--- a/samples/Previewer/Previewer.csproj
+++ b/samples/Previewer/Previewer.csproj
@@ -10,7 +10,7 @@
-
+
diff --git a/samples/VirtualizationDemo/App.xaml b/samples/VirtualizationDemo/App.xaml
index 3ad1dce794..eb5f0e4dca 100644
--- a/samples/VirtualizationDemo/App.xaml
+++ b/samples/VirtualizationDemo/App.xaml
@@ -1,9 +1,7 @@
-
-
-
-
-
-
\ No newline at end of file
+
+
+
+
+
diff --git a/samples/VirtualizationDemo/VirtualizationDemo.csproj b/samples/VirtualizationDemo/VirtualizationDemo.csproj
index bd6054327f..b27cfe77e8 100644
--- a/samples/VirtualizationDemo/VirtualizationDemo.csproj
+++ b/samples/VirtualizationDemo/VirtualizationDemo.csproj
@@ -5,7 +5,7 @@
-
+
diff --git a/samples/interop/Direct3DInteropSample/App.paml b/samples/interop/Direct3DInteropSample/App.paml
index d9630eef58..e6d77dfaf4 100644
--- a/samples/interop/Direct3DInteropSample/App.paml
+++ b/samples/interop/Direct3DInteropSample/App.paml
@@ -1,6 +1,5 @@
-
-
+
-
\ No newline at end of file
+
diff --git a/samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj b/samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj
index 81b94b4d09..f9ef4693d5 100644
--- a/samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj
+++ b/samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj
@@ -22,7 +22,7 @@
-
+
diff --git a/src/Android/Avalonia.Android/Platform/Storage/AndroidStorageItem.cs b/src/Android/Avalonia.Android/Platform/Storage/AndroidStorageItem.cs
index 50581d47b1..a9b2e16d43 100644
--- a/src/Android/Avalonia.Android/Platform/Storage/AndroidStorageItem.cs
+++ b/src/Android/Avalonia.Android/Platform/Storage/AndroidStorageItem.cs
@@ -1,6 +1,7 @@
#nullable enable
using System;
+using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
@@ -35,13 +36,13 @@ internal abstract class AndroidStorageItem : IStorageBookmarkItem
public bool CanBookmark => true;
- public Task SaveBookmark()
+ public Task SaveBookmarkAsync()
{
Context.ContentResolver?.TakePersistableUriPermission(Uri, ActivityFlags.GrantWriteUriPermission | ActivityFlags.GrantReadUriPermission);
return Task.FromResult(Uri.ToString());
}
- public Task ReleaseBookmark()
+ public Task ReleaseBookmarkAsync()
{
Context.ContentResolver?.ReleasePersistableUriPermission(Uri, ActivityFlags.GrantWriteUriPermission | ActivityFlags.GrantReadUriPermission);
return Task.CompletedTask;
@@ -106,6 +107,30 @@ internal sealed class AndroidStorageFolder : AndroidStorageItem, IStorageBookmar
{
return Task.FromResult(new StorageItemProperties());
}
+
+ public async Task> GetItemsAsync()
+ {
+ using var javaFile = new JavaFile(Uri.Path!);
+
+ // Java file represents files AND directories. Don't be confused.
+ var files = await javaFile.ListFilesAsync().ConfigureAwait(false);
+ if (files is null)
+ {
+ return Array.Empty();
+ }
+
+ return files
+ .Select(f => (file: f, uri: AndroidUri.FromFile(f)))
+ .Where(t => t.uri is not null)
+ .Select(t => t.file switch
+ {
+ { IsFile: true } => (IStorageItem)new AndroidStorageFile(Context, t.uri!),
+ { IsDirectory: true } => new AndroidStorageFolder(Context, t.uri!),
+ _ => null
+ })
+ .Where(i => i is not null)
+ .ToArray()!;
+ }
}
internal sealed class AndroidStorageFile : AndroidStorageItem, IStorageBookmarkFile
@@ -118,10 +143,10 @@ internal sealed class AndroidStorageFile : AndroidStorageItem, IStorageBookmarkF
public bool CanOpenWrite => true;
- public Task OpenRead() => Task.FromResult(OpenContentStream(Context, Uri, false)
+ public Task OpenReadAsync() => Task.FromResult(OpenContentStream(Context, Uri, false)
?? throw new InvalidOperationException("Failed to open content stream"));
- public Task OpenWrite() => Task.FromResult(OpenContentStream(Context, Uri, true)
+ public Task OpenWriteAsync() => Task.FromResult(OpenContentStream(Context, Uri, true)
?? throw new InvalidOperationException("Failed to open content stream"));
private Stream? OpenContentStream(Context context, AndroidUri uri, bool isOutput)
diff --git a/src/Avalonia.Base/Media/GlyphRun.cs b/src/Avalonia.Base/Media/GlyphRun.cs
index 2a7f3360ad..cae7a8fe75 100644
--- a/src/Avalonia.Base/Media/GlyphRun.cs
+++ b/src/Avalonia.Base/Media/GlyphRun.cs
@@ -265,7 +265,7 @@ namespace Avalonia.Media
//RightToLeft
var glyphIndex = FindGlyphIndex(characterIndex);
- if (GlyphClusters != null)
+ if (GlyphClusters != null && GlyphClusters.Count > 0)
{
if (characterIndex > GlyphClusters[0])
{
diff --git a/src/Avalonia.Base/Media/TextFormatting/FormattedTextSource.cs b/src/Avalonia.Base/Media/TextFormatting/FormattedTextSource.cs
index 97df87d3d9..7ab67ea34d 100644
--- a/src/Avalonia.Base/Media/TextFormatting/FormattedTextSource.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/FormattedTextSource.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using Avalonia.Media.TextFormatting.Unicode;
using Avalonia.Utilities;
namespace Avalonia.Media.TextFormatting
@@ -116,7 +117,30 @@ namespace Avalonia.Media.TextFormatting
length = text.Length;
}
+ length = CoerceLength(text, length);
+
return new ValueSpan(firstTextSourceIndex, length, currentProperties);
}
+
+ private static int CoerceLength(ReadOnlySlice text, int length)
+ {
+ var finalLength = 0;
+
+ var graphemeEnumerator = new GraphemeEnumerator(text);
+
+ while (graphemeEnumerator.MoveNext())
+ {
+ var grapheme = graphemeEnumerator.Current;
+
+ finalLength += grapheme.Text.Length;
+
+ if (finalLength >= length)
+ {
+ return finalLength;
+ }
+ }
+
+ return length;
+ }
}
}
diff --git a/src/Avalonia.Base/Media/TextFormatting/InterWordJustification.cs b/src/Avalonia.Base/Media/TextFormatting/InterWordJustification.cs
index df83ada34a..a49e4ef13b 100644
--- a/src/Avalonia.Base/Media/TextFormatting/InterWordJustification.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/InterWordJustification.cs
@@ -15,6 +15,13 @@ namespace Avalonia.Media.TextFormatting
public override void Justify(TextLine textLine)
{
+ var lineImpl = textLine as TextLineImpl;
+
+ if(lineImpl is null)
+ {
+ return;
+ }
+
var paragraphWidth = Width;
if (double.IsInfinity(paragraphWidth))
@@ -22,12 +29,12 @@ namespace Avalonia.Media.TextFormatting
return;
}
- if (textLine.NewLineLength > 0)
+ if (lineImpl.NewLineLength > 0)
{
return;
}
- var textLineBreak = textLine.TextLineBreak;
+ var textLineBreak = lineImpl.TextLineBreak;
if (textLineBreak is not null && textLineBreak.TextEndOfLine is not null)
{
@@ -39,7 +46,7 @@ namespace Avalonia.Media.TextFormatting
var breakOportunities = new Queue();
- foreach (var textRun in textLine.TextRuns)
+ foreach (var textRun in lineImpl.TextRuns)
{
var text = textRun.Text;
@@ -68,10 +75,10 @@ namespace Avalonia.Media.TextFormatting
return;
}
- var remainingSpace = Math.Max(0, paragraphWidth - textLine.WidthIncludingTrailingWhitespace);
+ var remainingSpace = Math.Max(0, paragraphWidth - lineImpl.WidthIncludingTrailingWhitespace);
var spacing = remainingSpace / breakOportunities.Count;
- foreach (var textRun in textLine.TextRuns)
+ foreach (var textRun in lineImpl.TextRuns)
{
var text = textRun.Text;
diff --git a/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs b/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs
index ab72601c3e..42a9e61c36 100644
--- a/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs
@@ -38,7 +38,7 @@ namespace Avalonia.Media.TextFormatting
/// Gets a list of .
///
/// The shapeable text characters.
- internal IReadOnlyList GetShapeableCharacters(ReadOnlySlice runText, sbyte biDiLevel,
+ internal IReadOnlyList GetShapeableCharacters(ReadOnlySlice runText, sbyte biDiLevel,
ref TextRunProperties? previousProperties)
{
var shapeableCharacters = new List(2);
@@ -65,7 +65,7 @@ namespace Avalonia.Media.TextFormatting
/// The bidi level of the run.
///
/// A list of shapeable text runs.
- private static ShapeableTextCharacters CreateShapeableRun(ReadOnlySlice text,
+ private static ShapeableTextCharacters CreateShapeableRun(ReadOnlySlice text,
TextRunProperties defaultProperties, sbyte biDiLevel, ref TextRunProperties? previousProperties)
{
var defaultTypeface = defaultProperties.Typeface;
@@ -76,7 +76,7 @@ namespace Avalonia.Media.TextFormatting
{
if (script == Script.Common && previousTypeface is not null)
{
- if(TryGetShapeableLength(text, previousTypeface.Value, defaultTypeface, out var fallbackCount, out _))
+ if (TryGetShapeableLength(text, previousTypeface.Value, defaultTypeface, out var fallbackCount, out _))
{
return new ShapeableTextCharacters(text.Take(fallbackCount),
defaultProperties.WithTypeface(previousTypeface.Value), biDiLevel);
@@ -86,10 +86,10 @@ namespace Avalonia.Media.TextFormatting
return new ShapeableTextCharacters(text.Take(count), defaultProperties.WithTypeface(currentTypeface),
biDiLevel);
}
-
+
if (previousTypeface is not null)
{
- if(TryGetShapeableLength(text, previousTypeface.Value, defaultTypeface, out count, out _))
+ if (TryGetShapeableLength(text, previousTypeface.Value, defaultTypeface, out count, out _))
{
return new ShapeableTextCharacters(text.Take(count),
defaultProperties.WithTypeface(previousTypeface.Value), biDiLevel);
@@ -106,12 +106,12 @@ namespace Avalonia.Media.TextFormatting
{
continue;
}
-
+
codepoint = codepointEnumerator.Current;
-
+
break;
}
-
+
//ToDo: Fix FontFamily fallback
var matchFound =
FontManager.Current.TryMatchCharacter(codepoint, defaultTypeface.Style, defaultTypeface.Weight,
@@ -157,14 +157,14 @@ namespace Avalonia.Media.TextFormatting
///
///
protected static bool TryGetShapeableLength(
- ReadOnlySlice text,
- Typeface typeface,
+ ReadOnlySlice text,
+ Typeface typeface,
Typeface? defaultTypeface,
out int length,
out Script script)
{
length = 0;
- script = Script.Unknown;
+ script = Script.Unknown;
if (text.Length == 0)
{
@@ -182,7 +182,7 @@ namespace Avalonia.Media.TextFormatting
var currentScript = currentGrapheme.FirstCodepoint.Script;
- if (currentScript != Script.Common && defaultFont != null && defaultFont.TryGetGlyph(currentGrapheme.FirstCodepoint, out _))
+ if (!currentGrapheme.FirstCodepoint.IsWhiteSpace && defaultFont != null && defaultFont.TryGetGlyph(currentGrapheme.FirstCodepoint, out _))
{
break;
}
@@ -192,7 +192,7 @@ namespace Avalonia.Media.TextFormatting
{
break;
}
-
+
if (currentScript != script)
{
if (script is Script.Unknown || currentScript != Script.Common &&
diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs b/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs
index f3e8b5969c..0828b6518a 100644
--- a/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs
@@ -537,8 +537,13 @@ namespace Avalonia.Media.TextFormatting
///
/// The collapsing width.
/// The .
- private TextCollapsingProperties GetCollapsingProperties(double width)
+ private TextCollapsingProperties? GetCollapsingProperties(double width)
{
+ if(_textTrimming == TextTrimming.None)
+ {
+ return null;
+ }
+
return _textTrimming.CreateCollapsingProperties(new TextCollapsingCreateInfo(width, _paragraphProperties.DefaultTextRunProperties));
}
}
diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLine.cs b/src/Avalonia.Base/Media/TextFormatting/TextLine.cs
index c8a23097db..61b24dc8c5 100644
--- a/src/Avalonia.Base/Media/TextFormatting/TextLine.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/TextLine.cs
@@ -153,7 +153,7 @@ namespace Avalonia.Media.TextFormatting
///
/// A value that represents a collapsed line that can be displayed.
///
- public abstract TextLine Collapse(params TextCollapsingProperties[] collapsingPropertiesList);
+ public abstract TextLine Collapse(params TextCollapsingProperties?[] collapsingPropertiesList);
///
/// Create a justified line based on justification text properties.
diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs
index f3c62f4994..fa1ab6fd29 100644
--- a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs
@@ -119,7 +119,7 @@ namespace Avalonia.Media.TextFormatting
}
///
- public override TextLine Collapse(params TextCollapsingProperties[] collapsingPropertiesList)
+ public override TextLine Collapse(params TextCollapsingProperties?[] collapsingPropertiesList)
{
if (collapsingPropertiesList.Length == 0)
{
@@ -128,6 +128,11 @@ namespace Avalonia.Media.TextFormatting
var collapsingProperties = collapsingPropertiesList[0];
+ if(collapsingProperties is null)
+ {
+ return this;
+ }
+
var collapsedRuns = collapsingProperties.Collapse(this);
if (collapsedRuns is null)
@@ -171,7 +176,7 @@ namespace Avalonia.Media.TextFormatting
return GetRunCharacterHit(firstRun, FirstTextSourceIndex, 0);
}
- if (distance > WidthIncludingTrailingWhitespace)
+ if (distance >= WidthIncludingTrailingWhitespace)
{
var lastRun = _textRuns[_textRuns.Count - 1];
@@ -183,8 +188,52 @@ namespace Avalonia.Media.TextFormatting
var currentPosition = FirstTextSourceIndex;
var currentDistance = 0.0;
- foreach (var currentRun in _textRuns)
+ for (var i = 0; i < _textRuns.Count; i++)
{
+ var currentRun = _textRuns[i];
+
+ if(currentRun is ShapedTextCharacters shapedRun && !shapedRun.ShapedBuffer.IsLeftToRight)
+ {
+ var rightToLeftIndex = i;
+ currentPosition += currentRun.TextSourceLength;
+
+ while (rightToLeftIndex + 1 <= _textRuns.Count - 1)
+ {
+ var nextShaped = _textRuns[rightToLeftIndex + 1] as ShapedTextCharacters;
+
+ if (nextShaped == null || nextShaped.ShapedBuffer.IsLeftToRight)
+ {
+ break;
+ }
+
+ currentPosition += nextShaped.TextSourceLength;
+
+ rightToLeftIndex++;
+ }
+
+ for (var j = i; i <= rightToLeftIndex; j++)
+ {
+ if(j > _textRuns.Count - 1)
+ {
+ break;
+ }
+
+ currentRun = _textRuns[j];
+
+ if(currentDistance + currentRun.Size.Width <= distance)
+ {
+ currentDistance += currentRun.Size.Width;
+ currentPosition -= currentRun.TextSourceLength;
+
+ continue;
+ }
+
+ characterHit = GetRunCharacterHit(currentRun, currentPosition, distance - currentDistance);
+
+ break;
+ }
+ }
+
if (currentDistance + currentRun.Size.Width < distance)
{
currentDistance += currentRun.Size.Width;
@@ -211,12 +260,16 @@ namespace Avalonia.Media.TextFormatting
{
characterHit = shapedRun.GlyphRun.GetCharacterHitFromDistance(distance, out _);
- var offset = Math.Max(0, currentPosition - shapedRun.Text.Start);
+ var offset = 0;
- if (!shapedRun.GlyphRun.IsLeftToRight)
+ if (shapedRun.GlyphRun.IsLeftToRight)
{
- offset = Math.Max(0, offset - shapedRun.Text.End);
+ offset = Math.Max(0, currentPosition - shapedRun.Text.Start);
}
+ //else
+ //{
+ // offset = Math.Max(0, currentPosition - shapedRun.Text.Start + shapedRun.Text.Length);
+ //}
characterHit = new CharacterHit(characterHit.FirstCharacterIndex + offset, characterHit.TrailingLength);
@@ -255,10 +308,56 @@ namespace Avalonia.Media.TextFormatting
{
var currentRun = _textRuns[index];
- if (TryGetDistanceFromCharacterHit(currentRun, characterHit, currentPosition, remainingLength,
- flowDirection, out var distance, out _))
+ if (currentRun is ShapedTextCharacters shapedRun && !shapedRun.ShapedBuffer.IsLeftToRight)
+ {
+ var i = index;
+
+ var rightToLeftWidth = currentRun.Size.Width;
+
+ while (i + 1 <= _textRuns.Count - 1)
+ {
+ var nextRun = _textRuns[i + 1];
+
+ if (nextRun is ShapedTextCharacters nextShapedRun && !nextShapedRun.ShapedBuffer.IsLeftToRight)
+ {
+ i++;
+
+ rightToLeftWidth += nextRun.Size.Width;
+
+ continue;
+ }
+
+ break;
+ }
+
+ if(i > index)
+ {
+ while (i >= index)
+ {
+ currentRun = _textRuns[i];
+
+ rightToLeftWidth -= currentRun.Size.Width;
+
+ if (currentPosition + currentRun.TextSourceLength >= characterIndex)
+ {
+ break;
+ }
+
+ currentPosition += currentRun.TextSourceLength;
+
+ remainingLength -= currentRun.TextSourceLength;
+
+ i--;
+ }
+
+ currentDistance += rightToLeftWidth;
+ }
+ }
+
+ if (currentPosition + currentRun.TextSourceLength >= characterIndex &&
+ TryGetDistanceFromCharacterHit(currentRun, characterHit, currentPosition, remainingLength, flowDirection, out var distance, out _))
{
- return currentDistance + distance;
+ return Math.Max(0, currentDistance + distance);
}
//No hit hit found so we add the full width
@@ -283,7 +382,7 @@ namespace Avalonia.Media.TextFormatting
distance = currentGlyphRun.Size.Width - distance;
}
- return currentDistance - distance;
+ return Math.Max(0, currentDistance - distance);
}
//No hit hit found so we add the full width
@@ -293,7 +392,7 @@ namespace Avalonia.Media.TextFormatting
}
}
- return currentDistance;
+ return Math.Max(0, currentDistance);
}
private static bool TryGetDistanceFromCharacterHit(
@@ -442,92 +541,139 @@ namespace Avalonia.Media.TextFormatting
continue;
}
- if (currentPosition + currentRun.TextSourceLength <= firstTextSourceIndex)
- {
- startX += currentRun.Size.Width;
-
- currentPosition += currentRun.TextSourceLength;
-
- continue;
- }
-
var characterLength = 0;
var endX = startX;
+ var runWidth = 0.0;
+ TextRunBounds? currentRunBounds = null;
- if (currentRun is ShapedTextCharacters currentShapedRun)
+ var currentShapedRun = currentRun as ShapedTextCharacters;
+
+ if (currentShapedRun != null && !currentShapedRun.ShapedBuffer.IsLeftToRight)
{
- var offset = Math.Max(0, firstTextSourceIndex - currentPosition);
+ var rightToLeftIndex = index;
+ startX += currentShapedRun.Size.Width;
- currentPosition += offset;
+ while (rightToLeftIndex + 1 <= _textRuns.Count - 1)
+ {
+ var nextShapedRun = _textRuns[rightToLeftIndex + 1] as ShapedTextCharacters;
- var startIndex = currentRun.Text.Start + offset;
+ if (nextShapedRun == null || nextShapedRun.ShapedBuffer.IsLeftToRight)
+ {
+ break;
+ }
- double startOffset;
- double endOffset;
+ startX += nextShapedRun.Size.Width;
- if (currentShapedRun.ShapedBuffer.IsLeftToRight)
+ rightToLeftIndex++;
+ }
+
+ if (TryGetTextRunBoundsRightToLeft(startX, firstTextSourceIndex, characterIndex, rightToLeftIndex, ref currentPosition, ref remainingLength, out currentRunBounds))
{
- startOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit(new CharacterHit(startIndex));
+ startX = currentRunBounds!.Rectangle.Left;
+ endX = currentRunBounds.Rectangle.Right;
- endOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit(new CharacterHit(startIndex + remainingLength));
+ runWidth = currentRunBounds.Rectangle.Width;
}
- else
+
+ currentDirection = FlowDirection.RightToLeft;
+ }
+ else
+ {
+ if (currentShapedRun != null)
{
- endOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit(new CharacterHit(startIndex));
+ if (currentPosition + currentRun.TextSourceLength <= firstTextSourceIndex)
+ {
+ startX += currentRun.Size.Width;
- if (currentPosition < startIndex)
+ currentPosition += currentRun.TextSourceLength;
+
+ continue;
+ }
+
+ var offset = Math.Max(0, firstTextSourceIndex - currentPosition);
+
+ currentPosition += offset;
+
+ var startIndex = currentRun.Text.Start + offset;
+
+ double startOffset;
+ double endOffset;
+
+ if (currentShapedRun.ShapedBuffer.IsLeftToRight)
{
- startOffset = endOffset;
+ startOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit(new CharacterHit(startIndex));
+
+ endOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit(new CharacterHit(startIndex + remainingLength));
}
else
{
- startOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit(new CharacterHit(startIndex + remainingLength));
+ endOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit(new CharacterHit(startIndex));
+
+ if (currentPosition < startIndex)
+ {
+ startOffset = endOffset;
+ }
+ else
+ {
+ startOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit(new CharacterHit(startIndex + remainingLength));
+ }
}
- }
- startX += startOffset;
+ startX += startOffset;
- endX += endOffset;
+ endX += endOffset;
- var endHit = currentShapedRun.GlyphRun.GetCharacterHitFromDistance(endOffset, out _);
- var startHit = currentShapedRun.GlyphRun.GetCharacterHitFromDistance(startOffset, out _);
+ var endHit = currentShapedRun.GlyphRun.GetCharacterHitFromDistance(endOffset, out _);
+ var startHit = currentShapedRun.GlyphRun.GetCharacterHitFromDistance(startOffset, out _);
- characterLength = Math.Abs(endHit.FirstCharacterIndex + endHit.TrailingLength - startHit.FirstCharacterIndex - startHit.TrailingLength);
+ characterLength = Math.Abs(endHit.FirstCharacterIndex + endHit.TrailingLength - startHit.FirstCharacterIndex - startHit.TrailingLength);
- currentDirection = currentShapedRun.ShapedBuffer.IsLeftToRight ?
- FlowDirection.LeftToRight :
- FlowDirection.RightToLeft;
- }
- else
- {
- if (currentPosition < firstTextSourceIndex)
+ currentDirection = FlowDirection.LeftToRight;
+ }
+ else
{
- startX += currentRun.Size.Width;
+ if (currentPosition + currentRun.TextSourceLength <= firstTextSourceIndex)
+ {
+ startX += currentRun.Size.Width;
+
+ currentPosition += currentRun.TextSourceLength;
+
+ continue;
+ }
+
+ if (currentPosition < firstTextSourceIndex)
+ {
+ startX += currentRun.Size.Width;
+ }
+
+ if (currentPosition + currentRun.TextSourceLength <= characterIndex)
+ {
+ endX += currentRun.Size.Width;
+
+ characterLength = currentRun.TextSourceLength;
+ }
}
- if (currentPosition + currentRun.TextSourceLength <= characterIndex)
+ if (endX < startX)
{
- endX += currentRun.Size.Width;
+ (endX, startX) = (startX, endX);
+ }
- characterLength = currentRun.TextSourceLength;
+ //Lines that only contain a linebreak need to be covered here
+ if (characterLength == 0)
+ {
+ characterLength = NewLineLength;
}
- }
- if (endX < startX)
- {
- (endX, startX) = (startX, endX);
- }
+ runWidth = endX - startX;
+ currentRunBounds = new TextRunBounds(new Rect(startX, 0, runWidth, Height), currentPosition, characterLength, currentRun);
- //Lines that only contain a linebreak need to be covered here
- if (characterLength == 0)
- {
- characterLength = NewLineLength;
- }
+ currentPosition += characterLength;
- var runWidth = endX - startX;
- var currentRunBounds = new TextRunBounds(new Rect(startX, 0, runWidth, Height), currentPosition, characterLength, currentRun);
+ remainingLength -= characterLength;
+ }
- if (!MathUtilities.IsZero(runWidth) || NewLineLength > 0)
+ if (currentRunBounds != null && !MathUtilities.IsZero(runWidth) || NewLineLength > 0)
{
if (lastDirection == currentDirection && result.Count > 0 && MathUtilities.AreClose(currentRect.Right, startX))
{
@@ -537,32 +683,26 @@ namespace Avalonia.Media.TextFormatting
textBounds.Rectangle = currentRect;
- textBounds.TextRunBounds.Add(currentRunBounds);
+ textBounds.TextRunBounds.Add(currentRunBounds!);
}
else
{
- currentRect = currentRunBounds.Rectangle;
+ currentRect = currentRunBounds!.Rectangle;
result.Add(new TextBounds(currentRect, currentDirection, new List { currentRunBounds }));
}
}
currentWidth += runWidth;
- currentPosition += characterLength;
+
- if (currentPosition > characterIndex)
+ if (remainingLength <= 0 || currentPosition >= characterIndex)
{
break;
}
startX = endX;
lastDirection = currentDirection;
- remainingLength -= characterLength;
-
- if (remainingLength <= 0)
- {
- break;
- }
}
return result;
@@ -674,7 +814,7 @@ namespace Avalonia.Media.TextFormatting
var currentRunBounds = new TextRunBounds(new Rect(Start + startX, 0, runWidth, Height), currentPosition, characterLength, currentRun);
- if(!MathUtilities.IsZero(runWidth) || NewLineLength > 0)
+ if (!MathUtilities.IsZero(runWidth) || NewLineLength > 0)
{
if (lastDirection == currentDirection && result.Count > 0 && MathUtilities.AreClose(currentRect.Right, Start + startX))
{
@@ -692,7 +832,7 @@ namespace Avalonia.Media.TextFormatting
result.Add(new TextBounds(currentRect, currentDirection, new List { currentRunBounds }));
}
- }
+ }
currentWidth += runWidth;
currentPosition += characterLength;
@@ -716,6 +856,107 @@ namespace Avalonia.Media.TextFormatting
return result;
}
+ private bool TryGetTextRunBoundsRightToLeft(double startX, int firstTextSourceIndex, int characterIndex, int runIndex, ref int currentPosition, ref int remainingLength, out TextRunBounds? textRunBounds)
+ {
+ textRunBounds = null;
+
+ for (var index = runIndex; index >= 0; index--)
+ {
+ if (TextRuns[index] is not DrawableTextRun currentRun)
+ {
+ continue;
+ }
+
+ if (currentPosition + currentRun.TextSourceLength <= firstTextSourceIndex)
+ {
+ startX -= currentRun.Size.Width;
+
+ currentPosition += currentRun.TextSourceLength;
+
+ continue;
+ }
+
+ var characterLength = 0;
+ var endX = startX;
+
+ if (currentRun is ShapedTextCharacters currentShapedRun)
+ {
+ var offset = Math.Max(0, firstTextSourceIndex - currentPosition);
+
+ currentPosition += offset;
+
+ var startIndex = currentRun.Text.Start + offset;
+ double startOffset;
+ double endOffset;
+
+ if (currentShapedRun.ShapedBuffer.IsLeftToRight)
+ {
+ if (currentPosition < startIndex)
+ {
+ startOffset = endOffset = 0;
+ }
+ else
+ {
+ endOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit(new CharacterHit(startIndex + remainingLength));
+
+ startOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit(new CharacterHit(startIndex));
+ }
+ }
+ else
+ {
+ endOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit(new CharacterHit(startIndex));
+
+ startOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit(new CharacterHit(startIndex + remainingLength));
+ }
+
+ startX -= currentRun.Size.Width - startOffset;
+ endX -= currentRun.Size.Width - endOffset;
+
+ var endHit = currentShapedRun.GlyphRun.GetCharacterHitFromDistance(endOffset, out _);
+ var startHit = currentShapedRun.GlyphRun.GetCharacterHitFromDistance(startOffset, out _);
+
+ characterLength = Math.Abs(startHit.FirstCharacterIndex + startHit.TrailingLength - endHit.FirstCharacterIndex - endHit.TrailingLength);
+ }
+ else
+ {
+ if (currentPosition + currentRun.TextSourceLength <= characterIndex)
+ {
+ endX -= currentRun.Size.Width;
+ }
+
+ if (currentPosition < firstTextSourceIndex)
+ {
+ startX -= currentRun.Size.Width;
+
+ characterLength = currentRun.TextSourceLength;
+ }
+ }
+
+ if (endX < startX)
+ {
+ (endX, startX) = (startX, endX);
+ }
+
+ //Lines that only contain a linebreak need to be covered here
+ if (characterLength == 0)
+ {
+ characterLength = NewLineLength;
+ }
+
+ var runWidth = endX - startX;
+
+ remainingLength -= characterLength;
+
+ currentPosition += characterLength;
+
+ textRunBounds = new TextRunBounds(new Rect(Start + startX, 0, runWidth, Height), currentPosition, characterLength, currentRun);
+
+ return true;
+ }
+
+ return false;
+ }
+
public override IReadOnlyList GetTextBounds(int firstTextSourceIndex, int textLength)
{
if (_paragraphProperties.FlowDirection == FlowDirection.LeftToRight)
@@ -1295,6 +1536,11 @@ namespace Avalonia.Media.TextFormatting
var textAlignment = _paragraphProperties.TextAlignment;
var paragraphFlowDirection = _paragraphProperties.FlowDirection;
+ if(textAlignment == TextAlignment.Justify)
+ {
+ textAlignment = TextAlignment.Start;
+ }
+
switch (textAlignment)
{
case TextAlignment.Start:
@@ -1319,12 +1565,12 @@ namespace Avalonia.Media.TextFormatting
case TextAlignment.Center:
var start = (_paragraphWidth - width) / 2;
- if(paragraphFlowDirection == FlowDirection.RightToLeft)
+ if (paragraphFlowDirection == FlowDirection.RightToLeft)
{
start -= (widthIncludingTrailingWhitespace - width);
}
- return Math.Max(0, start);
+ return Math.Max(0, start);
case TextAlignment.Right:
return Math.Max(0, _paragraphWidth - widthIncludingTrailingWhitespace);
diff --git a/src/Avalonia.Base/Media/TextFormatting/Unicode/Codepoint.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/Codepoint.cs
index 56a90f31ea..ab17263806 100644
--- a/src/Avalonia.Base/Media/TextFormatting/Unicode/Codepoint.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/Unicode/Codepoint.cs
@@ -224,7 +224,7 @@ namespace Avalonia.Media.TextFormatting.Unicode
}
///
- /// Returns if is between
+ /// Returns if is between
/// and , inclusive.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/Avalonia.Base/Platform/Storage/FileIO/BclStorageFile.cs b/src/Avalonia.Base/Platform/Storage/FileIO/BclStorageFile.cs
index 5af02219ce..cf21e9b8b5 100644
--- a/src/Avalonia.Base/Platform/Storage/FileIO/BclStorageFile.cs
+++ b/src/Avalonia.Base/Platform/Storage/FileIO/BclStorageFile.cs
@@ -47,22 +47,22 @@ public class BclStorageFile : IStorageBookmarkFile
return Task.FromResult(null);
}
- public Task OpenRead()
+ public Task OpenReadAsync()
{
return Task.FromResult(_fileInfo.OpenRead());
}
- public Task OpenWrite()
+ public Task OpenWriteAsync()
{
return Task.FromResult(_fileInfo.OpenWrite());
}
- public virtual Task SaveBookmark()
+ public virtual Task SaveBookmarkAsync()
{
return Task.FromResult(_fileInfo.FullName);
}
- public Task ReleaseBookmark()
+ public Task ReleaseBookmarkAsync()
{
// No-op
return Task.CompletedTask;
diff --git a/src/Avalonia.Base/Platform/Storage/FileIO/BclStorageFolder.cs b/src/Avalonia.Base/Platform/Storage/FileIO/BclStorageFolder.cs
index 7267017eaf..cd6c8be1ae 100644
--- a/src/Avalonia.Base/Platform/Storage/FileIO/BclStorageFolder.cs
+++ b/src/Avalonia.Base/Platform/Storage/FileIO/BclStorageFolder.cs
@@ -1,6 +1,8 @@
using System;
+using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
+using System.Linq;
using System.Security;
using System.Threading.Tasks;
using Avalonia.Metadata;
@@ -43,12 +45,22 @@ public class BclStorageFolder : IStorageBookmarkFolder
return Task.FromResult(null);
}
- public virtual Task SaveBookmark()
+ public Task> GetItemsAsync()
+ {
+ var items = _directoryInfo.GetDirectories()
+ .Select(d => (IStorageItem)new BclStorageFolder(d))
+ .Concat(_directoryInfo.GetFiles().Select(f => new BclStorageFile(f)))
+ .ToArray();
+
+ return Task.FromResult>(items);
+ }
+
+ public virtual Task SaveBookmarkAsync()
{
return Task.FromResult(_directoryInfo.FullName);
}
- public Task ReleaseBookmark()
+ public Task ReleaseBookmarkAsync()
{
// No-op
return Task.CompletedTask;
diff --git a/src/Avalonia.Base/Platform/Storage/IStorageBookmarkItem.cs b/src/Avalonia.Base/Platform/Storage/IStorageBookmarkItem.cs
index d21c950862..40f2720ee8 100644
--- a/src/Avalonia.Base/Platform/Storage/IStorageBookmarkItem.cs
+++ b/src/Avalonia.Base/Platform/Storage/IStorageBookmarkItem.cs
@@ -6,7 +6,7 @@ namespace Avalonia.Platform.Storage;
[NotClientImplementable]
public interface IStorageBookmarkItem : IStorageItem
{
- Task ReleaseBookmark();
+ Task ReleaseBookmarkAsync();
}
[NotClientImplementable]
diff --git a/src/Avalonia.Base/Platform/Storage/IStorageFile.cs b/src/Avalonia.Base/Platform/Storage/IStorageFile.cs
index 965caf8216..46aa6efa72 100644
--- a/src/Avalonia.Base/Platform/Storage/IStorageFile.cs
+++ b/src/Avalonia.Base/Platform/Storage/IStorageFile.cs
@@ -18,7 +18,7 @@ public interface IStorageFile : IStorageItem
///
/// Opens a stream for read access.
///
- Task OpenRead();
+ Task OpenReadAsync();
///
/// Returns true, if file is writeable.
@@ -28,5 +28,5 @@ public interface IStorageFile : IStorageItem
///
/// Opens stream for writing to the file.
///
- Task OpenWrite();
+ Task OpenWriteAsync();
}
diff --git a/src/Avalonia.Base/Platform/Storage/IStorageFolder.cs b/src/Avalonia.Base/Platform/Storage/IStorageFolder.cs
index 25b9f01a92..0ffb9f41c6 100644
--- a/src/Avalonia.Base/Platform/Storage/IStorageFolder.cs
+++ b/src/Avalonia.Base/Platform/Storage/IStorageFolder.cs
@@ -1,4 +1,6 @@
-using Avalonia.Metadata;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Avalonia.Metadata;
namespace Avalonia.Platform.Storage;
@@ -8,4 +10,11 @@ namespace Avalonia.Platform.Storage;
[NotClientImplementable]
public interface IStorageFolder : IStorageItem
{
+ ///
+ /// Gets the files and subfolders in the current folder.
+ ///
+ ///
+ /// When this method completes successfully, it returns a list of the files and folders in the current folder. Each item in the list is represented by an implementation object.
+ ///
+ Task> GetItemsAsync();
}
diff --git a/src/Avalonia.Base/Platform/Storage/IStorageItem.cs b/src/Avalonia.Base/Platform/Storage/IStorageItem.cs
index 8513ebc7d9..f5469d31c9 100644
--- a/src/Avalonia.Base/Platform/Storage/IStorageItem.cs
+++ b/src/Avalonia.Base/Platform/Storage/IStorageItem.cs
@@ -44,7 +44,7 @@ public interface IStorageItem : IDisposable
///
/// Returns identifier of a bookmark. Can be null if OS denied request.
///
- Task SaveBookmark();
+ Task SaveBookmarkAsync();
///
/// Gets the parent folder of the current storage item.
diff --git a/src/Avalonia.Base/Rendering/Composition/Animations/CompositionAnimation.cs b/src/Avalonia.Base/Rendering/Composition/Animations/CompositionAnimation.cs
index 19d316eb85..a6db4330a3 100644
--- a/src/Avalonia.Base/Rendering/Composition/Animations/CompositionAnimation.cs
+++ b/src/Avalonia.Base/Rendering/Composition/Animations/CompositionAnimation.cs
@@ -1,12 +1,9 @@
// ReSharper disable InconsistentNaming
// ReSharper disable CheckNamespace
-using System;
-using System.Collections.Generic;
using System.Numerics;
using Avalonia.Rendering.Composition.Expressions;
using Avalonia.Rendering.Composition.Server;
-using Avalonia.Rendering.Composition.Transport;
// Special license applies License.md
@@ -16,10 +13,10 @@ namespace Avalonia.Rendering.Composition.Animations
/// This is the base class for ExpressionAnimation and KeyFrameAnimation.
///
///
- /// Use the method to start the animation.
+ /// Use the method to start the animation.
/// Value parameters (as opposed to reference parameters which are set using )
/// are copied and "embedded" into an expression at the time CompositionObject.StartAnimation is called.
- /// Changing the value of the variable after is called will not affect
+ /// Changing the value of the variable after is called will not affect
/// the value of the ExpressionAnimation.
/// See the remarks section of ExpressionAnimation for additional information.
///
diff --git a/src/Avalonia.Base/Rendering/Composition/Animations/ExpressionAnimation.cs b/src/Avalonia.Base/Rendering/Composition/Animations/ExpressionAnimation.cs
index 577910d975..ec2972044e 100644
--- a/src/Avalonia.Base/Rendering/Composition/Animations/ExpressionAnimation.cs
+++ b/src/Avalonia.Base/Rendering/Composition/Animations/ExpressionAnimation.cs
@@ -16,7 +16,7 @@ namespace Avalonia.Rendering.Composition.Animations
/// This contrasts s, which use an interpolator to define how the animating
/// property changes over time. The mathematical equation can be defined using references to properties
/// of Composition objects, mathematical functions and operators and Input.
- /// Use the method to start the animation.
+ /// Use the method to start the animation.
///
public class ExpressionAnimation : CompositionAnimation
{
diff --git a/src/Avalonia.Base/Rendering/Composition/Animations/KeyFrameAnimation.cs b/src/Avalonia.Base/Rendering/Composition/Animations/KeyFrameAnimation.cs
index 4692fde5e3..d21a4d06e3 100644
--- a/src/Avalonia.Base/Rendering/Composition/Animations/KeyFrameAnimation.cs
+++ b/src/Avalonia.Base/Rendering/Composition/Animations/KeyFrameAnimation.cs
@@ -24,9 +24,9 @@ namespace Avalonia.Rendering.Composition.Animations
/// The delay behavior of the key frame animation.
///
public AnimationDelayBehavior DelayBehavior { get; set; }
-
+
///
- /// Delay before the animation starts after is called.
+ /// Delay before the animation starts after is called.
///
public System.TimeSpan DelayTime { get; set; }
diff --git a/src/Avalonia.Base/Rendering/SceneGraph/GeometryNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/GeometryNode.cs
index 4b43f93aee..cf53b86fa7 100644
--- a/src/Avalonia.Base/Rendering/SceneGraph/GeometryNode.cs
+++ b/src/Avalonia.Base/Rendering/SceneGraph/GeometryNode.cs
@@ -1,9 +1,7 @@
using System;
-using System.Collections.Generic;
using Avalonia.Media;
using Avalonia.Media.Immutable;
using Avalonia.Platform;
-using Avalonia.VisualTree;
namespace Avalonia.Rendering.SceneGraph
{
@@ -19,7 +17,7 @@ namespace Avalonia.Rendering.SceneGraph
/// The fill brush.
/// The stroke pen.
/// The geometry.
- /// Child scenes for drawing visual brushes.
+ /// Auxiliary data required to draw the brush.
public GeometryNode(Matrix transform,
IBrush? brush,
IPen? pen,
diff --git a/src/Avalonia.Base/Rendering/SceneGraph/GlyphRunNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/GlyphRunNode.cs
index 1f58111ecf..1d85e95835 100644
--- a/src/Avalonia.Base/Rendering/SceneGraph/GlyphRunNode.cs
+++ b/src/Avalonia.Base/Rendering/SceneGraph/GlyphRunNode.cs
@@ -1,10 +1,6 @@
using System;
-using System.Collections.Generic;
-
using Avalonia.Media;
-using Avalonia.Media.Immutable;
using Avalonia.Platform;
-using Avalonia.VisualTree;
namespace Avalonia.Rendering.SceneGraph
{
@@ -19,7 +15,7 @@ namespace Avalonia.Rendering.SceneGraph
/// The transform.
/// The foreground brush.
/// The glyph run to draw.
- /// Child scenes for drawing visual brushes.
+ /// Auxiliary data required to draw the brush.
public GlyphRunNode(
Matrix transform,
IBrush foreground,
diff --git a/src/Avalonia.Base/Rendering/SceneGraph/LineNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/LineNode.cs
index ee5ec0a5fc..0af8ba2752 100644
--- a/src/Avalonia.Base/Rendering/SceneGraph/LineNode.cs
+++ b/src/Avalonia.Base/Rendering/SceneGraph/LineNode.cs
@@ -1,9 +1,7 @@
using System;
-using System.Collections.Generic;
using Avalonia.Media;
using Avalonia.Media.Immutable;
using Avalonia.Platform;
-using Avalonia.VisualTree;
namespace Avalonia.Rendering.SceneGraph
{
@@ -19,7 +17,7 @@ namespace Avalonia.Rendering.SceneGraph
/// The stroke pen.
/// The start point of the line.
/// The end point of the line.
- /// Child scenes for drawing visual brushes.
+ /// Auxiliary data required to draw the brush.
public LineNode(
Matrix transform,
IPen pen,
diff --git a/src/Avalonia.Base/Rendering/SceneGraph/OpacityMaskNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/OpacityMaskNode.cs
index 549c1fd7de..5fd200ddff 100644
--- a/src/Avalonia.Base/Rendering/SceneGraph/OpacityMaskNode.cs
+++ b/src/Avalonia.Base/Rendering/SceneGraph/OpacityMaskNode.cs
@@ -17,7 +17,7 @@ namespace Avalonia.Rendering.SceneGraph
///
/// The opacity mask to push.
/// The bounds of the mask.
- /// Child scenes for drawing visual brushes.
+ /// Auxiliary data required to draw the brush.
public OpacityMaskNode(IBrush mask, Rect bounds, IDisposable? aux = null)
: base(Rect.Empty, Matrix.Identity, aux)
{
diff --git a/src/Avalonia.Base/Rendering/SceneGraph/RectangleNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/RectangleNode.cs
index a9d1bf96e5..f2ffd7411c 100644
--- a/src/Avalonia.Base/Rendering/SceneGraph/RectangleNode.cs
+++ b/src/Avalonia.Base/Rendering/SceneGraph/RectangleNode.cs
@@ -20,7 +20,7 @@ namespace Avalonia.Rendering.SceneGraph
/// The stroke pen.
/// The rectangle to draw.
/// The box shadow parameters
- /// Child scenes for drawing visual brushes.
+ /// Auxiliary data required to draw the brush.
public RectangleNode(
Matrix transform,
IBrush? brush,
diff --git a/src/Avalonia.Base/Utilities/MathUtilities.cs b/src/Avalonia.Base/Utilities/MathUtilities.cs
index 3d5be806e1..d381979c1e 100644
--- a/src/Avalonia.Base/Utilities/MathUtilities.cs
+++ b/src/Avalonia.Base/Utilities/MathUtilities.cs
@@ -255,7 +255,7 @@ namespace Avalonia.Utilities
///
/// Clamps a value between a minimum and maximum value.
///
- /// The value.
+ /// The value.
/// The minimum value.
/// The maximum value.
/// The clamped value.
diff --git a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
index 39a83c00c4..5915388822 100644
--- a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
+++ b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
@@ -326,8 +326,8 @@ namespace Avalonia.Build.Tasks
var op = i[c].Operand as MethodReference;
// TODO: Throw an error
- // This usually happens when same XAML resource was added twice for some weird reason
- // We currently support it for dual-named default theme resource
+ // This usually happens when the same XAML resource was added twice for some weird reason
+ // We currently support it for dual-named default theme resources
if (op != null
&& op.Name == TrampolineName)
{
diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Default/ColorSlider.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Default/ColorSlider.xaml
deleted file mode 100644
index 35cd7a9faa..0000000000
--- a/src/Avalonia.Controls.ColorPicker/Themes/Default/ColorSlider.xaml
+++ /dev/null
@@ -1,188 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Default/Default.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Default/Default.xaml
deleted file mode 100644
index db1fa3ee4e..0000000000
--- a/src/Avalonia.Controls.ColorPicker/Themes/Default/Default.xaml
+++ /dev/null
@@ -1,44 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorPicker.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorPicker.xaml
index 907b00dfff..74a1df4991 100644
--- a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorPicker.xaml
+++ b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorPicker.xaml
@@ -3,9 +3,6 @@
xmlns:controls="using:Avalonia.Controls"
x:CompileBindings="True">
-
- 5,5,0,0
-
@@ -25,7 +22,7 @@
Padding="0,0,10,0"
UseLayoutRounding="False">
-
@@ -45,7 +42,7 @@
-
+
+ SelectedIndex="{Binding SelectedIndex, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}">
+
+
+ 5,5,0,0
+
+
@@ -81,10 +83,4 @@
-
-
-
-
diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorView.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorView.xaml
index 993745b1e5..59cc48975f 100644
--- a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorView.xaml
+++ b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorView.xaml
@@ -77,6 +77,8 @@
17.7761 14 17.5 14H9.94999ZM7.5 16C6.67157 16 6 15.3284 6 14.5C6 13.6716 6.67157 13 7.5
13C8.32843 13 9 13.6716 9 14.5C9 15.3284 8.32843 16 7.5 16Z
+
+ 3
@@ -97,7 +99,7 @@
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
Background="{DynamicResource SystemControlBackgroundBaseLowBrush}"
- CornerRadius="{TemplateBinding CornerRadius}" />
+ CornerRadius="{DynamicResource ColorViewTabBackgroundCornerRadius}" />
+
-
-
- 80
- 40
-
+
+ 80
+ 40
-
+
-
+
diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorSlider.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorSlider.xaml
new file mode 100644
index 0000000000..9aa2dcd9f9
--- /dev/null
+++ b/src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorSlider.xaml
@@ -0,0 +1,190 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Default/ColorSpectrum.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorSpectrum.xaml
similarity index 69%
rename from src/Avalonia.Controls.ColorPicker/Themes/Default/ColorSpectrum.xaml
rename to src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorSpectrum.xaml
index 0e57f6b483..0e137c89c6 100644
--- a/src/Avalonia.Controls.ColorPicker/Themes/Default/ColorSpectrum.xaml
+++ b/src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorSpectrum.xaml
@@ -1,9 +1,10 @@
-
+
-
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
-
-
+
+
+
+
+
-
+
-
-
-
+
+
+
-
+
+
+
diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Simple/Simple.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Simple/Simple.xaml
new file mode 100644
index 0000000000..1e507a91fe
--- /dev/null
+++ b/src/Avalonia.Controls.ColorPicker/Themes/Simple/Simple.xaml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Avalonia.Controls.DataGrid/Themes/Default.xaml b/src/Avalonia.Controls.DataGrid/Themes/Simple.xaml
similarity index 99%
rename from src/Avalonia.Controls.DataGrid/Themes/Default.xaml
rename to src/Avalonia.Controls.DataGrid/Themes/Simple.xaml
index 83d9332613..6a748f399e 100644
--- a/src/Avalonia.Controls.DataGrid/Themes/Default.xaml
+++ b/src/Avalonia.Controls.DataGrid/Themes/Simple.xaml
@@ -223,7 +223,7 @@
-
@@ -270,7 +270,7 @@
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}"
Foreground="{TemplateBinding Foreground}"
- Theme="{StaticResource DefaultDataGridRowGroupExpanderButtonTheme}" />
+ Theme="{StaticResource SimpleDataGridRowGroupExpanderButtonTheme}" />
protected override void OnKeyDown(KeyEventArgs e)
{
- if (e.Key == Key.Enter)
+ switch (e.Key)
{
- OnClick();
- e.Handled = true;
- }
- else if (e.Key == Key.Space)
- {
- if (ClickMode == ClickMode.Press)
- {
+ case Key.Enter:
OnClick();
+ e.Handled = true;
+ break;
+
+ case Key.Space:
+ {
+ if (ClickMode == ClickMode.Press)
+ {
+ OnClick();
+ }
+
+ IsPressed = true;
+ e.Handled = true;
+ break;
}
- IsPressed = true;
- e.Handled = true;
- }
- else if (e.Key == Key.Escape && Flyout != null)
- {
- // If Flyout doesn't have focusable content, close the flyout here
- Flyout.Hide();
+
+ case Key.Escape when Flyout != null:
+ // If Flyout doesn't have focusable content, close the flyout here
+ CloseFlyout();
+ break;
}
base.OnKeyDown(e);
@@ -327,7 +333,14 @@ namespace Avalonia.Controls
{
if (IsEffectivelyEnabled)
{
- OpenFlyout();
+ if (_isFlyoutOpen)
+ {
+ CloseFlyout();
+ }
+ else
+ {
+ OpenFlyout();
+ }
var e = new RoutedEventArgs(ClickEvent);
RaiseEvent(e);
@@ -348,6 +361,14 @@ namespace Avalonia.Controls
Flyout?.ShowAt(this);
}
+ ///
+ /// Closes the button's flyout.
+ ///
+ protected virtual void CloseFlyout()
+ {
+ Flyout?.Hide();
+ }
+
///
/// Invoked when the button's flyout is opened.
///
@@ -494,8 +515,7 @@ namespace Avalonia.Controls
// If flyout is changed while one is already open, make sure we
// close the old one first
- if (oldFlyout != null &&
- oldFlyout.IsOpen)
+ if (oldFlyout != null && oldFlyout.IsOpen)
{
oldFlyout.Hide();
}
diff --git a/src/Avalonia.Controls/Flyouts/FlyoutBase.cs b/src/Avalonia.Controls/Flyouts/FlyoutBase.cs
index 1504d2b25f..00ebcab70e 100644
--- a/src/Avalonia.Controls/Flyouts/FlyoutBase.cs
+++ b/src/Avalonia.Controls/Flyouts/FlyoutBase.cs
@@ -12,17 +12,12 @@ namespace Avalonia.Controls.Primitives
{
public abstract class FlyoutBase : AvaloniaObject, IPopupHostProvider
{
- static FlyoutBase()
- {
- Control.ContextFlyoutProperty.Changed.Subscribe(OnContextFlyoutPropertyChanged);
- }
-
///
/// Defines the property
///
public static readonly DirectProperty IsOpenProperty =
- AvaloniaProperty.RegisterDirect(nameof(IsOpen),
- x => x.IsOpen);
+ AvaloniaProperty.RegisterDirect(nameof(IsOpen),
+ x => x.IsOpen);
///
/// Defines the property
@@ -43,6 +38,14 @@ namespace Avalonia.Controls.Primitives
AvaloniaProperty.RegisterDirect(nameof(ShowMode),
x => x.ShowMode, (x, v) => x.ShowMode = v);
+ ///
+ /// Defines the property
+ ///
+ public static readonly DirectProperty OverlayInputPassThroughElementProperty =
+ Popup.OverlayInputPassThroughElementProperty.AddOwner(
+ o => o._overlayInputPassThroughElement,
+ (o, v) => o._overlayInputPassThroughElement = v);
+
///
/// Defines the AttachedFlyout property
///
@@ -57,6 +60,12 @@ namespace Avalonia.Controls.Primitives
private PixelRect? _enlargePopupRectScreenPixelRect;
private IDisposable? _transientDisposable;
private Action? _popupHostChangedHandler;
+ private IInputElement? _overlayInputPassThroughElement;
+
+ static FlyoutBase()
+ {
+ Control.ContextFlyoutProperty.Changed.Subscribe(OnContextFlyoutPropertyChanged);
+ }
public FlyoutBase()
{
@@ -101,11 +110,21 @@ namespace Avalonia.Controls.Primitives
private set => SetAndRaise(TargetProperty, ref _target, value);
}
+ ///
+ /// Gets or sets an element that should receive pointer input events even when underneath
+ /// the flyout's overlay.
+ ///
+ public IInputElement? OverlayInputPassThroughElement
+ {
+ get => _overlayInputPassThroughElement;
+ set => SetAndRaise(OverlayInputPassThroughElementProperty, ref _overlayInputPassThroughElement, value);
+ }
+
IPopupHost? IPopupHostProvider.PopupHost => Popup?.Host;
- event Action? IPopupHostProvider.PopupHostChanged
- {
- add => _popupHostChangedHandler += value;
+ event Action? IPopupHostProvider.PopupHostChanged
+ {
+ add => _popupHostChangedHandler += value;
remove => _popupHostChangedHandler -= value;
}
@@ -175,8 +194,9 @@ namespace Avalonia.Controls.Primitives
IsOpen = false;
Popup.IsOpen = false;
+
((ISetLogicalParent)Popup).SetParent(null);
-
+
// Ensure this isn't active
_transientDisposable?.Dispose();
_transientDisposable = null;
@@ -231,6 +251,8 @@ namespace Avalonia.Controls.Primitives
Popup.Child = CreatePresenter();
}
+ Popup.OverlayInputPassThroughElement = OverlayInputPassThroughElement;
+
if (CancelOpening())
{
return false;
@@ -356,10 +378,13 @@ namespace Avalonia.Controls.Primitives
private Popup CreatePopup()
{
- var popup = new Popup();
- popup.WindowManagerAddShadowHint = false;
- popup.IsLightDismissEnabled = true;
- popup.OverlayDismissEventPassThrough = true;
+ var popup = new Popup
+ {
+ WindowManagerAddShadowHint = false,
+ IsLightDismissEnabled = true,
+ //Note: This is required to prevent Button.Flyout from opening the flyout again after dismiss.
+ OverlayDismissEventPassThrough = false
+ };
popup.Opened += OnPopupOpened;
popup.Closed += OnPopupClosed;
@@ -372,7 +397,7 @@ namespace Avalonia.Controls.Primitives
{
IsOpen = true;
- _popupHostChangedHandler?.Invoke(Popup!.Host);
+ _popupHostChangedHandler?.Invoke(Popup.Host);
}
private void OnPopupClosing(object? sender, CancelEventArgs e)
diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs
index e463bc5731..e540f58195 100644
--- a/src/Avalonia.Controls/Presenters/TextPresenter.cs
+++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs
@@ -9,6 +9,7 @@ using Avalonia.VisualTree;
using Avalonia.Layout;
using Avalonia.Media.Immutable;
using Avalonia.Controls.Documents;
+using Avalonia.Media.TextFormatting.Unicode;
namespace Avalonia.Controls.Presenters
{
@@ -496,14 +497,14 @@ namespace Avalonia.Controls.Presenters
var length = Math.Max(selectionStart, selectionEnd) - start;
IReadOnlyList>? textStyleOverrides = null;
-
- if (length > 0)
+
+ if (length > 0 && SelectionForegroundBrush != null)
{
textStyleOverrides = new[]
{
new ValueSpan(start, length,
new GenericTextRunProperties(typeface, FontSize,
- foregroundBrush: SelectionForegroundBrush ?? Brushes.White))
+ foregroundBrush: SelectionForegroundBrush))
};
}
diff --git a/src/Avalonia.Controls/Primitives/Popup.cs b/src/Avalonia.Controls/Primitives/Popup.cs
index 1501d97470..3573ad9aaa 100644
--- a/src/Avalonia.Controls/Primitives/Popup.cs
+++ b/src/Avalonia.Controls/Primitives/Popup.cs
@@ -501,7 +501,7 @@ namespace Avalonia.Controls.Primitives
if (dismissLayer != null)
{
dismissLayer.IsVisible = true;
- dismissLayer.InputPassThroughElement = _overlayInputPassThroughElement;
+ dismissLayer.InputPassThroughElement = OverlayInputPassThroughElement;
Disposable.Create(() =>
{
diff --git a/src/Avalonia.Controls/ProgressBar.cs b/src/Avalonia.Controls/ProgressBar.cs
index 56b9caf085..91114628ee 100644
--- a/src/Avalonia.Controls/ProgressBar.cs
+++ b/src/Avalonia.Controls/ProgressBar.cs
@@ -119,14 +119,12 @@ namespace Avalonia.Controls
nameof(Percentage),
o => o.Percentage);
- [Obsolete("To be removed when Avalonia.Themes.Default is discontinued.")]
public static readonly DirectProperty IndeterminateStartingOffsetProperty =
AvaloniaProperty.RegisterDirect(
nameof(IndeterminateStartingOffset),
p => p.IndeterminateStartingOffset,
(p, o) => p.IndeterminateStartingOffset = o);
- [Obsolete("To be removed when Avalonia.Themes.Default is discontinued.")]
public static readonly DirectProperty IndeterminateEndingOffsetProperty =
AvaloniaProperty.RegisterDirect(
nameof(IndeterminateEndingOffset),
@@ -139,14 +137,12 @@ namespace Avalonia.Controls
private set { SetAndRaise(PercentageProperty, ref _percentage, value); }
}
- [Obsolete("To be removed when Avalonia.Themes.Default is discontinued.")]
public double IndeterminateStartingOffset
{
get => _indeterminateStartingOffset;
set => SetAndRaise(IndeterminateStartingOffsetProperty, ref _indeterminateStartingOffset, value);
}
- [Obsolete("To be removed when Avalonia.Themes.Default is discontinued.")]
public double IndeterminateEndingOffset
{
get => _indeterminateEndingOffset;
diff --git a/src/Avalonia.Controls/RichTextBlock.cs b/src/Avalonia.Controls/RichTextBlock.cs
index 0c8b1d125d..1f8abbc30d 100644
--- a/src/Avalonia.Controls/RichTextBlock.cs
+++ b/src/Avalonia.Controls/RichTextBlock.cs
@@ -44,8 +44,8 @@ namespace Avalonia.Controls
///
/// Defines the property.
///
- public static readonly StyledProperty InlinesProperty =
- AvaloniaProperty.Register(
+ public static readonly StyledProperty InlinesProperty =
+ AvaloniaProperty.Register(
nameof(Inlines));
public static readonly DirectProperty CanCopyProperty =
@@ -138,7 +138,7 @@ namespace Avalonia.Controls
/// Gets or sets the inlines.
///
[Content]
- public InlineCollection Inlines
+ public InlineCollection? Inlines
{
get => GetValue(InlinesProperty);
set => SetValue(InlinesProperty, value);
@@ -159,7 +159,7 @@ namespace Avalonia.Controls
remove => RemoveHandler(CopyingToClipboardEvent, value);
}
- internal bool HasComplexContent => Inlines.Count > 0;
+ internal bool HasComplexContent => Inlines != null && Inlines.Count > 0;
///
/// Copies the current selection to the Clipboard.
@@ -260,23 +260,23 @@ namespace Avalonia.Controls
{
if (!string.IsNullOrEmpty(_text))
{
- Inlines.Add(_text);
+ Inlines?.Add(_text);
_text = null;
}
- Inlines.Add(text);
+ Inlines?.Add(text);
}
}
protected override string? GetText()
{
- return _text ?? Inlines.Text;
+ return _text ?? Inlines?.Text;
}
protected override void SetText(string? text)
{
- var oldValue = _text ?? Inlines?.Text;
+ var oldValue = GetText();
AddText(text);
@@ -301,10 +301,10 @@ namespace Avalonia.Controls
ITextSource textSource;
- var inlines = Inlines;
-
if (HasComplexContent)
{
+ var inlines = Inlines!;
+
var textRuns = new List();
foreach (var inline in inlines)
@@ -537,7 +537,7 @@ namespace Avalonia.Controls
switch (change.Property.Name)
{
- case nameof(InlinesProperty):
+ case nameof(Inlines):
{
OnInlinesChanged(change.OldValue as InlineCollection, change.NewValue as InlineCollection);
InvalidateTextLayout();
@@ -553,7 +553,7 @@ namespace Avalonia.Controls
return "";
}
- var text = Inlines.Text ?? Text;
+ var text = GetText();
if (string.IsNullOrEmpty(text))
{
diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs
index 1b268db2f7..4c9e9327d4 100644
--- a/src/Avalonia.Controls/TextBox.cs
+++ b/src/Avalonia.Controls/TextBox.cs
@@ -17,6 +17,7 @@ using Avalonia.Controls.Metadata;
using Avalonia.Media.TextFormatting;
using Avalonia.Media.TextFormatting.Unicode;
using Avalonia.Automation.Peers;
+using System.Diagnostics;
namespace Avalonia.Controls
{
@@ -1240,9 +1241,10 @@ namespace Avalonia.Controls
MathUtilities.Clamp(point.X, 0, Math.Max(_presenter.Bounds.Width - 1, 0)),
MathUtilities.Clamp(point.Y, 0, Math.Max(_presenter.Bounds.Height - 1, 0)));
- _presenter.MoveCaretToPoint(point);
+ _presenter.MoveCaretToPoint(point);
var caretIndex = _presenter.CaretIndex;
+
var text = Text;
if (text != null && _wordSelectionStart >= 0)
@@ -1266,7 +1268,7 @@ namespace Avalonia.Controls
}
else
{
- SelectionEnd = _presenter.CaretIndex;
+ SelectionEnd = caretIndex;
}
}
}
diff --git a/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj b/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj
index adddf3f57b..d719135a7f 100644
--- a/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj
+++ b/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj
@@ -19,7 +19,7 @@
-
+
diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml
index 004518598c..1270dbaa62 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml
+++ b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml
@@ -2,7 +2,6 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:views="clr-namespace:Avalonia.Diagnostics.Views"
xmlns:diag="clr-namespace:Avalonia.Diagnostics"
- xmlns:default="using:Avalonia.Themes.Default"
Title="Avalonia DevTools"
x:Class="Avalonia.Diagnostics.Views.MainWindow"
Theme="{StaticResource {x:Type Window}}">
@@ -11,8 +10,8 @@
-
-
+
+
diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs
index 96dc929434..c81997f2cb 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs
+++ b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs
@@ -11,7 +11,7 @@ using Avalonia.Input;
using Avalonia.Input.Raw;
using Avalonia.Markup.Xaml;
using Avalonia.Styling;
-using Avalonia.Themes.Default;
+using Avalonia.Themes.Simple;
using Avalonia.VisualTree;
namespace Avalonia.Diagnostics.Views
diff --git a/src/Avalonia.Themes.Default/DefaultTheme.xaml b/src/Avalonia.Themes.Default/DefaultTheme.xaml
deleted file mode 100644
index 8f5bea557c..0000000000
--- a/src/Avalonia.Themes.Default/DefaultTheme.xaml
+++ /dev/null
@@ -1,75 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/Avalonia.Themes.Default/DefaultTheme.xaml.cs b/src/Avalonia.Themes.Default/DefaultTheme.xaml.cs
deleted file mode 100644
index 598b418977..0000000000
--- a/src/Avalonia.Themes.Default/DefaultTheme.xaml.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using Avalonia.Styling;
-
-namespace Avalonia.Themes.Default
-{
- ///
- /// The default Avalonia theme.
- ///
- public class DefaultTheme : Styles
- {
- }
-}
diff --git a/src/Avalonia.Themes.Fluent/Controls/ButtonSpinner.xaml b/src/Avalonia.Themes.Fluent/Controls/ButtonSpinner.xaml
index 855dc5363e..aa55065f6d 100644
--- a/src/Avalonia.Themes.Fluent/Controls/ButtonSpinner.xaml
+++ b/src/Avalonia.Themes.Fluent/Controls/ButtonSpinner.xaml
@@ -41,13 +41,31 @@
M0,9 L10,0 20,9 19,10 10,2 1,10 z
M0,1 L10,10 20,1 19,0 10,8 1,0 z
-
-
+
-
+
+
+
+
+
+
+
+
+
@@ -83,7 +101,6 @@
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness, Converter={StaticResource ButtonSpinnerLeftThickness}}"
- CornerRadius="0"
VerticalAlignment="Stretch"
VerticalContentAlignment="Center"
Foreground="{TemplateBinding Foreground}"
@@ -99,7 +116,6 @@
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness, Converter={StaticResource ButtonSpinnerLeftThickness}}"
- CornerRadius="0"
VerticalAlignment="Stretch"
VerticalContentAlignment="Center"
Foreground="{TemplateBinding Foreground}"
diff --git a/src/Avalonia.Themes.Default/Accents/Base.xaml b/src/Avalonia.Themes.Simple/Accents/Base.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/Accents/Base.xaml
rename to src/Avalonia.Themes.Simple/Accents/Base.xaml
diff --git a/src/Avalonia.Themes.Default/Accents/BaseDark.xaml b/src/Avalonia.Themes.Simple/Accents/BaseDark.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/Accents/BaseDark.xaml
rename to src/Avalonia.Themes.Simple/Accents/BaseDark.xaml
diff --git a/src/Avalonia.Themes.Default/Accents/BaseLight.xaml b/src/Avalonia.Themes.Simple/Accents/BaseLight.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/Accents/BaseLight.xaml
rename to src/Avalonia.Themes.Simple/Accents/BaseLight.xaml
diff --git a/src/Avalonia.Themes.Default/ApiCompatBaseline.txt b/src/Avalonia.Themes.Simple/ApiCompatBaseline.txt
similarity index 100%
rename from src/Avalonia.Themes.Default/ApiCompatBaseline.txt
rename to src/Avalonia.Themes.Simple/ApiCompatBaseline.txt
diff --git a/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj b/src/Avalonia.Themes.Simple/Avalonia.Themes.Simple.csproj
similarity index 100%
rename from src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj
rename to src/Avalonia.Themes.Simple/Avalonia.Themes.Simple.csproj
diff --git a/src/Avalonia.Themes.Default/Controls/AutoCompleteBox.xaml b/src/Avalonia.Themes.Simple/Controls/AutoCompleteBox.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/Controls/AutoCompleteBox.xaml
rename to src/Avalonia.Themes.Simple/Controls/AutoCompleteBox.xaml
diff --git a/src/Avalonia.Themes.Default/Controls/Button.xaml b/src/Avalonia.Themes.Simple/Controls/Button.xaml
similarity index 100%
rename from src/Avalonia.Themes.Default/Controls/Button.xaml
rename to src/Avalonia.Themes.Simple/Controls/Button.xaml
diff --git a/src/Avalonia.Themes.Default/Controls/ButtonSpinner.xaml b/src/Avalonia.Themes.Simple/Controls/ButtonSpinner.xaml
similarity index 94%
rename from src/Avalonia.Themes.Default/Controls/ButtonSpinner.xaml
rename to src/Avalonia.Themes.Simple/Controls/ButtonSpinner.xaml
index 4585fc8e56..9798e5290b 100644
--- a/src/Avalonia.Themes.Default/Controls/ButtonSpinner.xaml
+++ b/src/Avalonia.Themes.Simple/Controls/ButtonSpinner.xaml
@@ -2,7 +2,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:CompileBindings="True">
-
@@ -38,7 +38,7 @@
IsVisible="{TemplateBinding ShowButtonSpinner}"
Rows="2">
+ Theme="{StaticResource SimpleButtonSpinnerRepeatButton}">
+ Theme="{StaticResource SimpleButtonSpinnerRepeatButton}">
-
@@ -44,7 +44,7 @@
TextElement.FontSize="10">
diff --git a/tests/Avalonia.Base.UnitTests/Utilities/UriExtensionsTests.cs b/tests/Avalonia.Base.UnitTests/Utilities/UriExtensionsTests.cs
index 5c3ac6adeb..4a879c8ced 100644
--- a/tests/Avalonia.Base.UnitTests/Utilities/UriExtensionsTests.cs
+++ b/tests/Avalonia.Base.UnitTests/Utilities/UriExtensionsTests.cs
@@ -10,9 +10,9 @@ public class UriExtensionsTests
public void Assembly_Name_From_Query_Parsed()
{
const string key = "assembly";
- const string value = "Avalonia.Themes.Default";
+ const string value = "Avalonia.Themes.Simple";
- var uri = new Uri($"resm:Avalonia.Themes.Default.Accents.BaseLight.xaml?{key}={value}");
+ var uri = new Uri($"resm:Avalonia.Themes.Simple.Accents.BaseLight.xaml?{key}={value}");
var name = uri.GetAssemblyNameFromQuery();
Assert.Equal(value, name);
@@ -21,7 +21,7 @@ public class UriExtensionsTests
[Fact]
public void Assembly_Name_From_Empty_Query_Not_Parsed()
{
- var uri = new Uri("resm:Avalonia.Themes.Default.Accents.BaseLight.xaml");
+ var uri = new Uri("resm:Avalonia.Themes.Simple.Accents.BaseLight.xaml");
var name = uri.GetAssemblyNameFromQuery();
Assert.Equal(string.Empty, name);
diff --git a/tests/Avalonia.Benchmarks/Avalonia.Benchmarks.csproj b/tests/Avalonia.Benchmarks/Avalonia.Benchmarks.csproj
index 5d17808e0c..3f4978f544 100644
--- a/tests/Avalonia.Benchmarks/Avalonia.Benchmarks.csproj
+++ b/tests/Avalonia.Benchmarks/Avalonia.Benchmarks.csproj
@@ -9,7 +9,7 @@
-
+
diff --git a/tests/Avalonia.Benchmarks/Themes/ThemeBenchmark.cs b/tests/Avalonia.Benchmarks/Themes/ThemeBenchmark.cs
index 9a5b49790d..86ba4c6005 100644
--- a/tests/Avalonia.Benchmarks/Themes/ThemeBenchmark.cs
+++ b/tests/Avalonia.Benchmarks/Themes/ThemeBenchmark.cs
@@ -4,6 +4,8 @@ using Avalonia.Controls;
using Avalonia.Markup.Xaml.Styling;
using Avalonia.Platform;
using Avalonia.Styling;
+using Avalonia.Themes.Fluent;
+using Avalonia.Themes.Simple;
using Avalonia.UnitTests;
using BenchmarkDotNet.Attributes;
@@ -25,32 +27,25 @@ namespace Avalonia.Benchmarks.Themes
}
[Benchmark]
- [Arguments("avares://Avalonia.Themes.Fluent/FluentDark.xaml")]
- [Arguments("avares://Avalonia.Themes.Fluent/FluentLight.xaml")]
- public bool InitFluentTheme(string themeUri)
+ [Arguments(FluentThemeMode.Dark)]
+ [Arguments(FluentThemeMode.Light)]
+ public bool InitFluentTheme(FluentThemeMode mode)
{
- UnitTestApplication.Current.Styles[0] = new StyleInclude(new Uri("resm:Styles?assembly=Avalonia.Benchmarks"))
+ UnitTestApplication.Current.Styles[0] = new FluentTheme(new Uri("resm:Styles?assembly=Avalonia.Benchmarks"))
{
- Source = new Uri(themeUri)
+ Mode = mode
};
return ((IResourceHost)UnitTestApplication.Current).TryGetResource("SystemAccentColor", out _);
}
[Benchmark]
- [Arguments("avares://Avalonia.Themes.Default/Accents/BaseLight.xaml")]
- [Arguments("avares://Avalonia.Themes.Default/Accents/BaseDark.xaml")]
- public bool InitDefaultTheme(string themeUri)
+ [Arguments(SimpleThemeMode.Dark)]
+ [Arguments(SimpleThemeMode.Light)]
+ public bool InitSimpleTheme(SimpleThemeMode mode)
{
- UnitTestApplication.Current.Styles[0] = new Styles
+ UnitTestApplication.Current.Styles[0] = new SimpleTheme(new Uri("resm:Styles?assembly=Avalonia.Benchmarks"))
{
- new StyleInclude(new Uri("resm:Styles?assembly=Avalonia.Benchmarks"))
- {
- Source = new Uri(themeUri)
- },
- new StyleInclude(new Uri("resm:Styles?assembly=Avalonia.Benchmarks"))
- {
- Source = new Uri("avares://Avalonia.Themes.Default/DefaultTheme.xaml")
- }
+ Mode = mode
};
return ((IResourceHost)UnitTestApplication.Current).TryGetResource("ThemeAccentColor", out _);
}
diff --git a/tests/Avalonia.Controls.UnitTests/RichTextBlockTests.cs b/tests/Avalonia.Controls.UnitTests/RichTextBlockTests.cs
index eb4b88956d..c74f13b808 100644
--- a/tests/Avalonia.Controls.UnitTests/RichTextBlockTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/RichTextBlockTests.cs
@@ -48,5 +48,49 @@ namespace Avalonia.Controls.UnitTests
Assert.False(target.IsMeasureValid);
}
}
+
+ [Fact]
+ public void Changing_Inlines_Should_Invalidate_Measure()
+ {
+ using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
+ {
+ var target = new RichTextBlock();
+
+ var inlines = new InlineCollection { new Run("Hello") };
+
+ target.Measure(Size.Infinity);
+
+ Assert.True(target.IsMeasureValid);
+
+ target.Inlines = inlines;
+
+ Assert.False(target.IsMeasureValid);
+ }
+ }
+
+ [Fact]
+ public void Changing_Inlines_Should_Reset_Inlines_Parent()
+ {
+ using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
+ {
+ var target = new RichTextBlock();
+
+ var run = new Run("Hello");
+
+ target.Inlines.Add(run);
+
+ target.Measure(Size.Infinity);
+
+ Assert.True(target.IsMeasureValid);
+
+ target.Inlines = null;
+
+ Assert.Null(run.Parent);
+
+ target.Inlines = new InlineCollection { run };
+
+ Assert.Equal(target, run.Parent);
+ }
+ }
}
}
diff --git a/tests/Avalonia.DesignerSupport.TestApp/App.xaml b/tests/Avalonia.DesignerSupport.TestApp/App.xaml
index 5a33ffff80..ad32431b37 100644
--- a/tests/Avalonia.DesignerSupport.TestApp/App.xaml
+++ b/tests/Avalonia.DesignerSupport.TestApp/App.xaml
@@ -1,10 +1,7 @@
-
-
-
-
-
-
+
+
+
+
diff --git a/tests/Avalonia.DesignerSupport.TestApp/Avalonia.DesignerSupport.TestApp.csproj b/tests/Avalonia.DesignerSupport.TestApp/Avalonia.DesignerSupport.TestApp.csproj
index f66b2b0457..278b0e087e 100644
--- a/tests/Avalonia.DesignerSupport.TestApp/Avalonia.DesignerSupport.TestApp.csproj
+++ b/tests/Avalonia.DesignerSupport.TestApp/Avalonia.DesignerSupport.TestApp.csproj
@@ -21,7 +21,7 @@
-
+
diff --git a/tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj b/tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj
index 52acc78db1..4a90da77e7 100644
--- a/tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj
+++ b/tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj
@@ -13,7 +13,7 @@
-
+
diff --git a/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj b/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj
index a00b24bdd7..4572f7ae7c 100644
--- a/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj
+++ b/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj
@@ -15,7 +15,7 @@
-
+
diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj b/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj
index a0efa7bdeb..f562529cb8 100644
--- a/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj
+++ b/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj
@@ -18,7 +18,7 @@
-
+
diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs
index 29148e6f2e..af2435a52f 100644
--- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs
+++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs
@@ -466,7 +466,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
var xaml = @"
-
+
";
var styles = AvaloniaRuntimeXamlLoader.Parse(xaml);
diff --git a/tests/Avalonia.RenderTests/Assets/NotoKufiArabic-Regular.ttf b/tests/Avalonia.RenderTests/Assets/NotoKufiArabic-Regular.ttf
deleted file mode 100644
index 6d2ad86f94..0000000000
Binary files a/tests/Avalonia.RenderTests/Assets/NotoKufiArabic-Regular.ttf and /dev/null differ
diff --git a/tests/Avalonia.RenderTests/Assets/NotoSansArabic-Regular.ttf b/tests/Avalonia.RenderTests/Assets/NotoSansArabic-Regular.ttf
new file mode 100644
index 0000000000..79359c460b
Binary files /dev/null and b/tests/Avalonia.RenderTests/Assets/NotoSansArabic-Regular.ttf differ
diff --git a/tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj b/tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj
index d3f2b44968..5e481f21c1 100644
--- a/tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj
+++ b/tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj
@@ -14,7 +14,7 @@
-
+
diff --git a/tests/Avalonia.Skia.UnitTests/Media/CustomFontManagerImpl.cs b/tests/Avalonia.Skia.UnitTests/Media/CustomFontManagerImpl.cs
index 13cc14b03e..7d0cf05b0b 100644
--- a/tests/Avalonia.Skia.UnitTests/Media/CustomFontManagerImpl.cs
+++ b/tests/Avalonia.Skia.UnitTests/Media/CustomFontManagerImpl.cs
@@ -16,7 +16,7 @@ namespace Avalonia.Skia.UnitTests.Media
private readonly Typeface _defaultTypeface =
new Typeface("resm:Avalonia.Skia.UnitTests.Assets?assembly=Avalonia.Skia.UnitTests#Noto Mono");
private readonly Typeface _arabicTypeface =
- new Typeface("resm:Avalonia.Skia.UnitTests.Assets?assembly=Avalonia.Skia.UnitTests#Noto Kufi Arabic");
+ new Typeface("resm:Avalonia.Skia.UnitTests.Assets?assembly=Avalonia.Skia.UnitTests#Noto Sans Arabic");
private readonly Typeface _italicTypeface =
new Typeface("resm:Avalonia.Skia.UnitTests.Assets?assembly=Avalonia.Skia.UnitTests#Noto Sans", FontStyle.Italic);
private readonly Typeface _emojiTypeface =
@@ -82,6 +82,12 @@ namespace Avalonia.Skia.UnitTests.Media
skTypeface = typefaceCollection.Get(typeface);
break;
}
+ case "Noto Sans Arabic":
+ {
+ var typefaceCollection = SKTypefaceCollectionCache.GetOrAddTypefaceCollection(_arabicTypeface.FontFamily);
+ skTypeface = typefaceCollection.Get(typeface);
+ break;
+ }
case FontFamily.DefaultFontFamilyName:
case "Noto Mono":
{
diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs
index 7d33f094fa..43948e9229 100644
--- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs
+++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Avalonia.Media;
@@ -914,14 +915,14 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
public void Should_Get_CharacterHit_From_Distance_RTL()
{
using (Start())
- {
+ {
var text = "أَبْجَدِيَّة عَرَبِيَّة";
var layout = new TextLayout(
- text,
- Typeface.Default,
- 12,
- Brushes.Black);
+ text,
+ Typeface.Default,
+ 12,
+ Brushes.Black);
var textLine = layout.TextLines[0];
@@ -952,6 +953,65 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
rect = layout.HitTestTextPosition(23);
Assert.Equal(0, rect.Left, 5);
+
+ }
+ }
+
+ [Fact]
+ public void Should_Get_CharacterHit_From_Distance_RTL_With_TextStyles()
+ {
+ using (Start())
+ {
+ var text = "أَبْجَدِيَّة عَرَبِيَّة";
+
+ var i = 0;
+
+ var graphemeEnumerator = new GraphemeEnumerator(text.AsMemory());
+
+ while (graphemeEnumerator.MoveNext())
+ {
+ var grapheme = graphemeEnumerator.Current;
+
+ var textStyleOverrides = new[] { new ValueSpan(i, grapheme.Text.Length, new GenericTextRunProperties(Typeface.Default, 12, foregroundBrush: Brushes.Red)) };
+
+ i += grapheme.Text.Length;
+
+ var layout = new TextLayout(
+ text,
+ Typeface.Default,
+ 12,
+ Brushes.Black,
+ textStyleOverrides: textStyleOverrides);
+
+ var textLine = layout.TextLines[0];
+
+ var shapedRuns = textLine.TextRuns.Cast().ToList();
+
+ var clusters = shapedRuns.SelectMany(x => x.ShapedBuffer.GlyphClusters).ToList();
+
+ var glyphAdvances = shapedRuns.SelectMany(x => x.ShapedBuffer.GlyphAdvances).ToList();
+
+ var currentX = 0.0;
+
+ var cluster = text.Length;
+
+ for (int j = 0; j < clusters.Count - 1; j++)
+ {
+ var glyphAdvance = glyphAdvances[j];
+
+ var characterHit = textLine.GetCharacterHitFromDistance(currentX);
+
+ Assert.Equal(cluster, characterHit.FirstCharacterIndex + characterHit.TrailingLength);
+
+ var distance = textLine.GetDistanceFromCharacterHit(new CharacterHit(cluster));
+
+ Assert.Equal(currentX, distance, 5);
+
+ currentX += glyphAdvance;
+
+ cluster = clusters[j];
+ }
+ }
}
}
diff --git a/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj b/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj
index 52ef23c966..cb6884cad8 100644
--- a/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj
+++ b/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj
@@ -18,7 +18,7 @@
-
+
diff --git a/tests/Avalonia.UnitTests/TestServices.cs b/tests/Avalonia.UnitTests/TestServices.cs
index c1be745aca..49da2794c1 100644
--- a/tests/Avalonia.UnitTests/TestServices.cs
+++ b/tests/Avalonia.UnitTests/TestServices.cs
@@ -6,7 +6,7 @@ using Avalonia.Markup.Xaml;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Styling;
-using Avalonia.Themes.Default;
+using Avalonia.Themes.Simple;
using Avalonia.Rendering;
using System.Reactive.Concurrency;
using System.Collections.Generic;
@@ -24,7 +24,7 @@ namespace Avalonia.UnitTests
renderInterface: new MockPlatformRenderInterface(),
standardCursorFactory: Mock.Of(),
styler: new Styler(),
- theme: () => CreateDefaultTheme(),
+ theme: () => CreateSimpleTheme(),
threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true),
fontManagerImpl: new MockFontManagerImpl(),
textShaperImpl: new MockTextShaperImpl(),
@@ -81,7 +81,7 @@ namespace Avalonia.UnitTests
IScheduler scheduler = null,
ICursorFactory standardCursorFactory = null,
IStyler styler = null,
- Func theme = null,
+ Func theme = null,
IPlatformThreadingInterface threadingInterface = null,
IFontManagerImpl fontManagerImpl = null,
ITextShaperImpl textShaperImpl = null,
@@ -122,7 +122,7 @@ namespace Avalonia.UnitTests
public IScheduler Scheduler { get; }
public ICursorFactory StandardCursorFactory { get; }
public IStyler Styler { get; }
- public Func Theme { get; }
+ public Func Theme { get; }
public IPlatformThreadingInterface ThreadingInterface { get; }
public IWindowImpl WindowImpl { get; }
public IWindowingPlatform WindowingPlatform { get; }
@@ -169,18 +169,9 @@ namespace Avalonia.UnitTests
windowImpl: windowImpl ?? WindowImpl);
}
- private static Styles CreateDefaultTheme()
+ private static IStyle CreateSimpleTheme()
{
- var result = new Styles
- {
- new DefaultTheme(),
- };
-
- var baseLight = (IStyle)AvaloniaXamlLoader.Load(
- new Uri("avares://Avalonia.Themes.Default/Accents/BaseLight.xaml"));
- result.Add(baseLight);
-
- return result;
+ return new SimpleTheme { Mode = SimpleThemeMode.Light };
}
private static IPlatformRenderInterface CreateRenderInterfaceMock()
diff --git a/tests/Avalonia.UnitTests/UnitTestApplication.cs b/tests/Avalonia.UnitTests/UnitTestApplication.cs
index 63c2832b92..260771c9ab 100644
--- a/tests/Avalonia.UnitTests/UnitTestApplication.cs
+++ b/tests/Avalonia.UnitTests/UnitTestApplication.cs
@@ -71,12 +71,16 @@ namespace Avalonia.UnitTests
.Bind().ToConstant(Services.Styler)
.Bind().ToConstant(Services.WindowingPlatform)
.Bind().ToSingleton();
- var styles = Services.Theme?.Invoke();
+ var theme = Services.Theme?.Invoke();
- if (styles != null)
+ if (theme is Styles styles)
{
Styles.AddRange(styles);
}
+ else if (theme is not null)
+ {
+ Styles.Add(theme);
+ }
}
}
}