diff --git a/.editorconfig b/.editorconfig
index 25e0135725..cb589a5ce1 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -21,6 +21,7 @@ csharp_new_line_before_finally = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_between_query_expression_clauses = true
+trim_trailing_whitespace = true
# Indentation preferences
csharp_indent_block_contents = true
diff --git a/src/Avalonia.Base/Assets/BiDi.trie b/src/Avalonia.Base/Assets/BiDi.trie
deleted file mode 100644
index 1c6122e2f1..0000000000
Binary files a/src/Avalonia.Base/Assets/BiDi.trie and /dev/null differ
diff --git a/src/Avalonia.Base/Assets/GraphemeBreak.trie b/src/Avalonia.Base/Assets/GraphemeBreak.trie
deleted file mode 100644
index 482bf9b44d..0000000000
Binary files a/src/Avalonia.Base/Assets/GraphemeBreak.trie and /dev/null differ
diff --git a/src/Avalonia.Base/Assets/UnicodeData.trie b/src/Avalonia.Base/Assets/UnicodeData.trie
deleted file mode 100644
index 46175ea644..0000000000
Binary files a/src/Avalonia.Base/Assets/UnicodeData.trie and /dev/null differ
diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs
index 1f14ddede4..6633eabb5d 100644
--- a/src/Avalonia.Base/AvaloniaObject.cs
+++ b/src/Avalonia.Base/AvaloniaObject.cs
@@ -935,7 +935,8 @@ namespace Avalonia
public void Dispose()
{
- _subscription.Dispose();
+ // _subscription can be null, if Subscribe failed with an exception.
+ _subscription?.Dispose();
_owner._directBindings!.Remove(this);
}
diff --git a/src/Avalonia.Base/Input/IInputElement.cs b/src/Avalonia.Base/Input/IInputElement.cs
index 78001143d7..43ac87d008 100644
--- a/src/Avalonia.Base/Input/IInputElement.cs
+++ b/src/Avalonia.Base/Input/IInputElement.cs
@@ -42,12 +42,12 @@ namespace Avalonia.Input
///
/// Occurs when the pointer enters the control.
///
- event EventHandler? PointerEnter;
+ event EventHandler? PointerEntered;
///
/// Occurs when the pointer leaves the control.
///
- event EventHandler? PointerLeave;
+ event EventHandler? PointerExited;
///
/// Occurs when the pointer is pressed over the control.
diff --git a/src/Avalonia.Base/Input/InputElement.cs b/src/Avalonia.Base/Input/InputElement.cs
index f4e25ebada..d0130258c3 100644
--- a/src/Avalonia.Base/Input/InputElement.cs
+++ b/src/Avalonia.Base/Input/InputElement.cs
@@ -128,16 +128,20 @@ namespace Avalonia.Input
RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
///
- /// Defines the event.
+ /// Defines the event.
///
- public static readonly RoutedEvent PointerEnterEvent =
- RoutedEvent.Register(nameof(PointerEnter), RoutingStrategies.Direct);
+ public static readonly RoutedEvent PointerEnteredEvent =
+ RoutedEvent.Register(
+ nameof(PointerEntered),
+ RoutingStrategies.Direct);
///
- /// Defines the event.
+ /// Defines the event.
///
- public static readonly RoutedEvent PointerLeaveEvent =
- RoutedEvent.Register(nameof(PointerLeave), RoutingStrategies.Direct);
+ public static readonly RoutedEvent PointerExitedEvent =
+ RoutedEvent.Register(
+ nameof(PointerExited),
+ RoutingStrategies.Direct);
///
/// Defines the event.
@@ -208,8 +212,8 @@ namespace Avalonia.Input
KeyDownEvent.AddClassHandler((x, e) => x.OnKeyDown(e));
KeyUpEvent.AddClassHandler((x, e) => x.OnKeyUp(e));
TextInputEvent.AddClassHandler((x, e) => x.OnTextInput(e));
- PointerEnterEvent.AddClassHandler((x, e) => x.OnPointerEnterCore(e));
- PointerLeaveEvent.AddClassHandler((x, e) => x.OnPointerLeaveCore(e));
+ PointerEnteredEvent.AddClassHandler((x, e) => x.OnPointerEnteredCore(e));
+ PointerExitedEvent.AddClassHandler((x, e) => x.OnPointerExitedCore(e));
PointerMovedEvent.AddClassHandler((x, e) => x.OnPointerMoved(e));
PointerPressedEvent.AddClassHandler((x, e) => x.OnPointerPressed(e));
PointerReleasedEvent.AddClassHandler((x, e) => x.OnPointerReleased(e));
@@ -279,19 +283,19 @@ namespace Avalonia.Input
///
/// Occurs when the pointer enters the control.
///
- public event EventHandler? PointerEnter
+ public event EventHandler? PointerEntered
{
- add { AddHandler(PointerEnterEvent, value); }
- remove { RemoveHandler(PointerEnterEvent, value); }
+ add { AddHandler(PointerEnteredEvent, value); }
+ remove { RemoveHandler(PointerEnteredEvent, value); }
}
///
/// Occurs when the pointer leaves the control.
///
- public event EventHandler? PointerLeave
+ public event EventHandler? PointerExited
{
- add { AddHandler(PointerLeaveEvent, value); }
- remove { RemoveHandler(PointerLeaveEvent, value); }
+ add { AddHandler(PointerExitedEvent, value); }
+ remove { RemoveHandler(PointerExitedEvent, value); }
}
///
@@ -539,18 +543,18 @@ namespace Avalonia.Input
}
///
- /// Called before the event occurs.
+ /// Called before the event occurs.
///
/// The event args.
- protected virtual void OnPointerEnter(PointerEventArgs e)
+ protected virtual void OnPointerEntered(PointerEventArgs e)
{
}
///
- /// Called before the event occurs.
+ /// Called before the event occurs.
///
/// The event args.
- protected virtual void OnPointerLeave(PointerEventArgs e)
+ protected virtual void OnPointerExited(PointerEventArgs e)
{
}
@@ -561,7 +565,9 @@ namespace Avalonia.Input
protected virtual void OnPointerMoved(PointerEventArgs e)
{
if (_gestureRecognizers?.HandlePointerMoved(e) == true)
+ {
e.Handled = true;
+ }
}
///
@@ -571,7 +577,9 @@ namespace Avalonia.Input
protected virtual void OnPointerPressed(PointerPressedEventArgs e)
{
if (_gestureRecognizers?.HandlePointerPressed(e) == true)
+ {
e.Handled = true;
+ }
}
///
@@ -581,7 +589,9 @@ namespace Avalonia.Input
protected virtual void OnPointerReleased(PointerReleasedEventArgs e)
{
if (_gestureRecognizers?.HandlePointerReleased(e) == true)
+ {
e.Handled = true;
+ }
}
///
@@ -634,23 +644,23 @@ namespace Avalonia.Input
}
///
- /// Called before the event occurs.
+ /// Called before the event occurs.
///
/// The event args.
- private void OnPointerEnterCore(PointerEventArgs e)
+ private void OnPointerEnteredCore(PointerEventArgs e)
{
IsPointerOver = true;
- OnPointerEnter(e);
+ OnPointerEntered(e);
}
///
- /// Called before the event occurs.
+ /// Called before the event occurs.
///
/// The event args.
- private void OnPointerLeaveCore(PointerEventArgs e)
+ private void OnPointerExitedCore(PointerEventArgs e)
{
IsPointerOver = false;
- OnPointerLeave(e);
+ OnPointerExited(e);
}
///
diff --git a/src/Avalonia.Base/Input/PointerOverPreProcessor.cs b/src/Avalonia.Base/Input/PointerOverPreProcessor.cs
index d22252893d..67d1eea7e3 100644
--- a/src/Avalonia.Base/Input/PointerOverPreProcessor.cs
+++ b/src/Avalonia.Base/Input/PointerOverPreProcessor.cs
@@ -97,7 +97,7 @@ namespace Avalonia.Input
// Do not pass rootVisual, when we have unknown (negative) position,
// so GetPosition won't return invalid values.
var hasPosition = position.X >= 0 && position.Y >= 0;
- var e = new PointerEventArgs(InputElement.PointerLeaveEvent, element, pointer,
+ var e = new PointerEventArgs(InputElement.PointerExitedEvent, element, pointer,
hasPosition ? root : null, hasPosition ? position : default,
timestamp, properties, inputModifiers);
@@ -177,7 +177,7 @@ namespace Avalonia.Input
el = root.PointerOverElement;
- var e = new PointerEventArgs(InputElement.PointerLeaveEvent, el, pointer, root, position,
+ var e = new PointerEventArgs(InputElement.PointerExitedEvent, el, pointer, root, position,
timestamp, properties, inputModifiers);
if (el != null && branch != null && !el.IsAttachedToVisualTree)
{
@@ -195,7 +195,7 @@ namespace Avalonia.Input
el = root.PointerOverElement = element;
_lastPointer = (pointer, root.PointToScreen(position));
- e.RoutedEvent = InputElement.PointerEnterEvent;
+ e.RoutedEvent = InputElement.PointerEnteredEvent;
while (el != null && el != branch)
{
diff --git a/src/Avalonia.Base/Layout/LayoutHelper.cs b/src/Avalonia.Base/Layout/LayoutHelper.cs
index d24be57d2b..404d19906a 100644
--- a/src/Avalonia.Base/Layout/LayoutHelper.cs
+++ b/src/Avalonia.Base/Layout/LayoutHelper.cs
@@ -36,11 +36,28 @@ namespace Avalonia.Layout
public static Size MeasureChild(ILayoutable? control, Size availableSize, Thickness padding,
Thickness borderThickness)
{
- return MeasureChild(control, availableSize, padding + borderThickness);
+ if (IsParentLayoutRounded(control, out double scale))
+ {
+ padding = RoundLayoutThickness(padding, scale, scale);
+ borderThickness = RoundLayoutThickness(borderThickness, scale, scale);
+ }
+
+ if (control != null)
+ {
+ control.Measure(availableSize.Deflate(padding + borderThickness));
+ return control.DesiredSize.Inflate(padding + borderThickness);
+ }
+
+ return new Size().Inflate(padding + borderThickness);
}
public static Size MeasureChild(ILayoutable? control, Size availableSize, Thickness padding)
{
+ if (IsParentLayoutRounded(control, out double scale))
+ {
+ padding = RoundLayoutThickness(padding, scale, scale);
+ }
+
if (control != null)
{
control.Measure(availableSize.Deflate(padding));
@@ -137,7 +154,7 @@ namespace Avalonia.Layout
///
/// Rounds a size to integer values for layout purposes, compensating for high DPI screen
- /// coordinates.
+ /// coordinates by rounding the size up to the nearest pixel.
///
/// Input size.
/// DPI along x-dimension.
@@ -149,9 +166,9 @@ namespace Avalonia.Layout
/// associated with the UseLayoutRounding property and should not be used as a general rounding
/// utility.
///
- public static Size RoundLayoutSize(Size size, double dpiScaleX, double dpiScaleY)
+ public static Size RoundLayoutSizeUp(Size size, double dpiScaleX, double dpiScaleY)
{
- return new Size(RoundLayoutValue(size.Width, dpiScaleX), RoundLayoutValue(size.Height, dpiScaleY));
+ return new Size(RoundLayoutValueUp(size.Width, dpiScaleX), RoundLayoutValueUp(size.Height, dpiScaleY));
}
///
@@ -178,10 +195,9 @@ namespace Avalonia.Layout
);
}
-
-
///
- /// Calculates the value to be used for layout rounding at high DPI.
+ /// Calculates the value to be used for layout rounding at high DPI by rounding the value
+ /// up or down to the nearest pixel.
///
/// Input value to be rounded.
/// Ratio of screen's DPI to layout DPI
@@ -217,7 +233,46 @@ namespace Avalonia.Layout
return newValue;
}
-
+
+ ///
+ /// Calculates the value to be used for layout rounding at high DPI by rounding the value up
+ /// to the nearest pixel.
+ ///
+ /// Input value to be rounded.
+ /// Ratio of screen's DPI to layout DPI
+ /// Adjusted value that will produce layout rounding on screen at high dpi.
+ ///
+ /// This is a layout helper method. It takes DPI into account and also does not return
+ /// the rounded value if it is unacceptable for layout, e.g. Infinity or NaN. It's a helper
+ /// associated with the UseLayoutRounding property and should not be used as a general rounding
+ /// utility.
+ ///
+ public static double RoundLayoutValueUp(double value, double dpiScale)
+ {
+ double newValue;
+
+ // If DPI == 1, don't use DPI-aware rounding.
+ if (!MathUtilities.IsOne(dpiScale))
+ {
+ newValue = Math.Ceiling(value * dpiScale) / dpiScale;
+
+ // If rounding produces a value unacceptable to layout (NaN, Infinity or MaxValue),
+ // use the original value.
+ if (double.IsNaN(newValue) ||
+ double.IsInfinity(newValue) ||
+ MathUtilities.AreClose(newValue, double.MaxValue))
+ {
+ newValue = value;
+ }
+ }
+ else
+ {
+ newValue = Math.Ceiling(value);
+ }
+
+ return newValue;
+ }
+
///
/// Calculates the min and max height for a control. Ported from WPF.
///
diff --git a/src/Avalonia.Base/Layout/Layoutable.cs b/src/Avalonia.Base/Layout/Layoutable.cs
index f30925f489..101e867d56 100644
--- a/src/Avalonia.Base/Layout/Layoutable.cs
+++ b/src/Avalonia.Base/Layout/Layoutable.cs
@@ -548,6 +548,14 @@ namespace Avalonia.Layout
if (IsVisible)
{
var margin = Margin;
+ var useLayoutRounding = UseLayoutRounding;
+ var scale = 1.0;
+
+ if (useLayoutRounding)
+ {
+ scale = LayoutHelper.GetLayoutScale(this);
+ margin = LayoutHelper.RoundLayoutThickness(margin, scale, scale);
+ }
ApplyStyling();
ApplyTemplate();
@@ -584,16 +592,14 @@ namespace Avalonia.Layout
height = Math.Min(height, MaxHeight);
height = Math.Max(height, MinHeight);
- width = Math.Min(width, availableSize.Width);
- height = Math.Min(height, availableSize.Height);
-
- if (UseLayoutRounding)
+ if (useLayoutRounding)
{
- var scale = LayoutHelper.GetLayoutScale(this);
- width = LayoutHelper.RoundLayoutValue(width, scale);
- height = LayoutHelper.RoundLayoutValue(height, scale);
+ (width, height) = LayoutHelper.RoundLayoutSizeUp(new Size(width, height), scale, scale);
}
+ width = Math.Min(width, availableSize.Width);
+ height = Math.Min(height, availableSize.Height);
+
return NonNegative(new Size(width, height).Inflate(margin));
}
else
@@ -678,8 +684,8 @@ namespace Avalonia.Layout
if (useLayoutRounding)
{
- size = LayoutHelper.RoundLayoutSize(size, scale, scale);
- availableSizeMinusMargins = LayoutHelper.RoundLayoutSize(availableSizeMinusMargins, scale, scale);
+ size = LayoutHelper.RoundLayoutSizeUp(size, scale, scale);
+ availableSizeMinusMargins = LayoutHelper.RoundLayoutSizeUp(availableSizeMinusMargins, scale, scale);
}
size = ArrangeOverride(size).Constrain(size);
diff --git a/src/Avalonia.Base/Media/GlyphRun.cs b/src/Avalonia.Base/Media/GlyphRun.cs
index 6f1fa03990..ac87d521a5 100644
--- a/src/Avalonia.Base/Media/GlyphRun.cs
+++ b/src/Avalonia.Base/Media/GlyphRun.cs
@@ -734,10 +734,9 @@ namespace Avalonia.Media
private void Set(ref T field, T value)
{
- if (_glyphRunImpl != null)
- {
- throw new InvalidOperationException("GlyphRun can't be changed after it has been initialized.'");
- }
+ _glyphRunImpl?.Dispose();
+
+ _glyphRunImpl = null;
_glyphRunMetrics = null;
diff --git a/src/Avalonia.Base/Media/TextAlignment.cs b/src/Avalonia.Base/Media/TextAlignment.cs
index b1a394e157..94416ccde2 100644
--- a/src/Avalonia.Base/Media/TextAlignment.cs
+++ b/src/Avalonia.Base/Media/TextAlignment.cs
@@ -19,5 +19,28 @@ namespace Avalonia.Media
/// The text is right-aligned.
///
Right,
+
+ ///
+ /// The beginning of the text is aligned to the edge of the available space.
+ ///
+ Start,
+
+ ///
+ /// The end of the text is aligned to the edge of the available space.
+ ///
+ End,
+
+ ///
+ /// Text alignment is inferred from the text content.
+ ///
+ ///
+ /// When the TextAlignment property is set to DetectFromContent, alignment is inferred from the text content of the control. For example, English text is left aligned, and Arabic text is right aligned.
+ ///
+ DetectFromContent,
+
+ ///
+ /// Text is justified within the available space.
+ ///
+ Justify
}
}
diff --git a/src/Avalonia.Base/Media/TextFormatting/InterWordJustification.cs b/src/Avalonia.Base/Media/TextFormatting/InterWordJustification.cs
new file mode 100644
index 0000000000..df83ada34a
--- /dev/null
+++ b/src/Avalonia.Base/Media/TextFormatting/InterWordJustification.cs
@@ -0,0 +1,109 @@
+using System;
+using System.Collections.Generic;
+using Avalonia.Media.TextFormatting.Unicode;
+
+namespace Avalonia.Media.TextFormatting
+{
+ internal class InterWordJustification : JustificationProperties
+ {
+ public InterWordJustification(double width)
+ {
+ Width = width;
+ }
+
+ public override double Width { get; }
+
+ public override void Justify(TextLine textLine)
+ {
+ var paragraphWidth = Width;
+
+ if (double.IsInfinity(paragraphWidth))
+ {
+ return;
+ }
+
+ if (textLine.NewLineLength > 0)
+ {
+ return;
+ }
+
+ var textLineBreak = textLine.TextLineBreak;
+
+ if (textLineBreak is not null && textLineBreak.TextEndOfLine is not null)
+ {
+ if (textLineBreak.RemainingRuns is null || textLineBreak.RemainingRuns.Count == 0)
+ {
+ return;
+ }
+ }
+
+ var breakOportunities = new Queue();
+
+ foreach (var textRun in textLine.TextRuns)
+ {
+ var text = textRun.Text;
+
+ if (text.IsEmpty)
+ {
+ continue;
+ }
+
+ var start = text.Start;
+
+ var lineBreakEnumerator = new LineBreakEnumerator(text);
+
+ while (lineBreakEnumerator.MoveNext())
+ {
+ var currentBreak = lineBreakEnumerator.Current;
+
+ if (!currentBreak.Required && currentBreak.PositionWrap != text.Length)
+ {
+ breakOportunities.Enqueue(start + currentBreak.PositionMeasure);
+ }
+ }
+ }
+
+ if (breakOportunities.Count == 0)
+ {
+ return;
+ }
+
+ var remainingSpace = Math.Max(0, paragraphWidth - textLine.WidthIncludingTrailingWhitespace);
+ var spacing = remainingSpace / breakOportunities.Count;
+
+ foreach (var textRun in textLine.TextRuns)
+ {
+ var text = textRun.Text;
+
+ if (text.IsEmpty)
+ {
+ continue;
+ }
+
+ if (textRun is ShapedTextCharacters shapedText)
+ {
+ var glyphRun = shapedText.GlyphRun;
+ var shapedBuffer = shapedText.ShapedBuffer;
+ var currentPosition = text.Start;
+
+ while (breakOportunities.Count > 0)
+ {
+ var characterIndex = breakOportunities.Dequeue();
+
+ if (characterIndex < currentPosition)
+ {
+ continue;
+ }
+
+ var glyphIndex = glyphRun.FindGlyphIndex(characterIndex);
+ var glyphInfo = shapedBuffer.GlyphInfos[glyphIndex];
+
+ shapedBuffer.GlyphInfos[glyphIndex] = new GlyphInfo(glyphInfo.GlyphIndex, glyphInfo.GlyphCluster, glyphInfo.GlyphAdvance + spacing);
+ }
+
+ glyphRun.GlyphAdvances = shapedBuffer.GlyphAdvances;
+ }
+ }
+ }
+ }
+}
diff --git a/src/Avalonia.Base/Media/TextFormatting/JustificationProperties.cs b/src/Avalonia.Base/Media/TextFormatting/JustificationProperties.cs
new file mode 100644
index 0000000000..620ad17189
--- /dev/null
+++ b/src/Avalonia.Base/Media/TextFormatting/JustificationProperties.cs
@@ -0,0 +1,16 @@
+namespace Avalonia.Media.TextFormatting
+{
+ public abstract class JustificationProperties
+ {
+ ///
+ /// Gets the width in which the range is justified.
+ ///
+ public abstract double Width { get; }
+
+ ///
+ /// Justifies given text line.
+ ///
+ /// Text line to collapse.
+ public abstract void Justify(TextLine textLine);
+ }
+}
diff --git a/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs
index 4205268bc6..16caadb0dd 100644
--- a/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs
@@ -15,7 +15,7 @@ namespace Avalonia.Media.TextFormatting
TextParagraphProperties paragraphProperties, TextLineBreak? previousLineBreak = null)
{
var textWrapping = paragraphProperties.TextWrapping;
- FlowDirection flowDirection;
+ FlowDirection resolvedFlowDirection;
TextLineBreak? nextLineBreak = null;
List drawableTextRuns;
@@ -24,17 +24,17 @@ namespace Avalonia.Media.TextFormatting
if (previousLineBreak?.RemainingRuns != null)
{
- flowDirection = previousLineBreak.FlowDirection;
+ resolvedFlowDirection = previousLineBreak.FlowDirection;
drawableTextRuns = previousLineBreak.RemainingRuns.ToList();
nextLineBreak = previousLineBreak;
}
else
{
- drawableTextRuns = ShapeTextRuns(textRuns, paragraphProperties, out flowDirection);
+ drawableTextRuns = ShapeTextRuns(textRuns, paragraphProperties, out resolvedFlowDirection);
if (nextLineBreak == null && textEndOfLine != null)
{
- nextLineBreak = new TextLineBreak(textEndOfLine, flowDirection);
+ nextLineBreak = new TextLineBreak(textEndOfLine, resolvedFlowDirection);
}
}
@@ -45,7 +45,7 @@ namespace Avalonia.Media.TextFormatting
case TextWrapping.NoWrap:
{
textLine = new TextLineImpl(drawableTextRuns, firstTextSourceIndex, textSourceLength,
- paragraphWidth, paragraphProperties, flowDirection, nextLineBreak);
+ paragraphWidth, paragraphProperties, resolvedFlowDirection, nextLineBreak);
textLine.FinalizeLine();
@@ -55,7 +55,7 @@ namespace Avalonia.Media.TextFormatting
case TextWrapping.Wrap:
{
textLine = PerformTextWrapping(drawableTextRuns, firstTextSourceIndex, paragraphWidth, paragraphProperties,
- flowDirection, nextLineBreak);
+ resolvedFlowDirection, nextLineBreak);
break;
}
default:
@@ -404,9 +404,9 @@ namespace Avalonia.Media.TextFormatting
{
endOfLine = textEndOfLine;
- textRuns.Add(textRun);
+ textSourceLength += textEndOfLine.TextSourceLength;
- textSourceLength += textRun.TextSourceLength;
+ textRuns.Add(textRun);
break;
}
@@ -431,9 +431,9 @@ namespace Avalonia.Media.TextFormatting
break;
}
- case DrawableTextRun drawableTextRun:
+ default:
{
- textRuns.Add(drawableTextRun);
+ textRuns.Add(textRun);
break;
}
}
@@ -552,11 +552,11 @@ namespace Avalonia.Media.TextFormatting
/// The first text source index.
/// The paragraph width.
/// The text paragraph properties.
- ///
+ ///
/// The current line break if the line was explicitly broken.
/// The wrapped text line.
private static TextLineImpl PerformTextWrapping(List textRuns, int firstTextSourceIndex,
- double paragraphWidth, TextParagraphProperties paragraphProperties, FlowDirection flowDirection,
+ double paragraphWidth, TextParagraphProperties paragraphProperties, FlowDirection resolvedFlowDirection,
TextLineBreak? currentLineBreak)
{
if(textRuns.Count == 0)
@@ -684,16 +684,16 @@ namespace Avalonia.Media.TextFormatting
var remainingCharacters = splitResult.Second;
var lineBreak = remainingCharacters?.Count > 0 ?
- new TextLineBreak(currentLineBreak?.TextEndOfLine, flowDirection, remainingCharacters) :
+ new TextLineBreak(currentLineBreak?.TextEndOfLine, resolvedFlowDirection, remainingCharacters) :
null;
if (lineBreak is null && currentLineBreak?.TextEndOfLine != null)
{
- lineBreak = new TextLineBreak(currentLineBreak.TextEndOfLine, flowDirection);
+ lineBreak = new TextLineBreak(currentLineBreak.TextEndOfLine, resolvedFlowDirection);
}
var textLine = new TextLineImpl(splitResult.First, firstTextSourceIndex, measuredLength,
- paragraphWidth, paragraphProperties, flowDirection,
+ paragraphWidth, paragraphProperties, resolvedFlowDirection,
lineBreak);
return textLine.FinalizeLine();
diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs b/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs
index 4f7c43a6d1..f3af240c58 100644
--- a/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs
@@ -439,7 +439,7 @@ namespace Avalonia.Media.TextFormatting
var textLine = TextFormatter.Current.FormatLine(_textSource, _textSourceLength, MaxWidth,
_paragraphProperties, previousLine?.TextLineBreak);
- if(textLine == null || textLine.Length == 0)
+ if(textLine == null || textLine.Length == 0 || textLine.TextRuns.Count == 0 && textLine.TextLineBreak?.TextEndOfLine is TextEndOfParagraph)
{
if(previousLine != null && previousLine.NewLineLength > 0)
{
@@ -501,6 +501,35 @@ namespace Avalonia.Media.TextFormatting
Bounds = new Rect(left, 0, width, height);
+ if(_paragraphProperties.TextAlignment == TextAlignment.Justify)
+ {
+ var whitespaceWidth = 0d;
+
+ foreach (var line in textLines)
+ {
+ var lineWhitespaceWidth = line.Width - line.WidthIncludingTrailingWhitespace;
+
+ if(lineWhitespaceWidth > whitespaceWidth)
+ {
+ whitespaceWidth = lineWhitespaceWidth;
+ }
+ }
+
+ var justificationWidth = width - whitespaceWidth;
+
+ if(justificationWidth > 0)
+ {
+ var justificationProperties = new InterWordJustification(justificationWidth);
+
+ for (var i = 0; i < textLines.Count - 1; i++)
+ {
+ var line = textLines[i];
+
+ line.Justify(justificationProperties);
+ }
+ }
+ }
+
return textLines;
}
diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLine.cs b/src/Avalonia.Base/Media/TextFormatting/TextLine.cs
index 1f69c15acc..c8a23097db 100644
--- a/src/Avalonia.Base/Media/TextFormatting/TextLine.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/TextLine.cs
@@ -15,9 +15,15 @@ namespace Avalonia.Media.TextFormatting
/// The contained text runs.
///
public abstract IReadOnlyList TextRuns { get; }
-
+
+ ///
+ /// Gets the first TextSource position of the current line.
+ ///
public abstract int FirstTextSourceIndex { get; }
+ ///
+ /// Gets the total number of TextSource positions of the current line.
+ ///
public abstract int Length { get; }
///
@@ -56,7 +62,7 @@ namespace Avalonia.Media.TextFormatting
/// Gets a value that indicates whether content of the line overflows the specified paragraph width.
///
///
- /// true, it the line overflows the specified paragraph width; otherwise, false.
+ /// true, the line overflows the specified paragraph width; otherwise, false.
///
public abstract bool HasOverflowed { get; }
@@ -75,7 +81,7 @@ namespace Avalonia.Media.TextFormatting
/// The number of newline characters.
///
public abstract int NewLineLength { get; }
-
+
///
/// Gets the distance that black pixels extend beyond the bottom alignment edge of a line.
///
@@ -149,6 +155,15 @@ namespace Avalonia.Media.TextFormatting
///
public abstract TextLine Collapse(params TextCollapsingProperties[] collapsingPropertiesList);
+ ///
+ /// Create a justified line based on justification text properties.
+ ///
+ /// An object that represent the justification text properties.
+ ///
+ /// A value that represents a justified line that can be displayed.
+ ///
+ public abstract void Justify(JustificationProperties justificationProperties);
+
///
/// Gets the character hit corresponding to the specified distance from the beginning of the line.
///
@@ -192,50 +207,5 @@ namespace Avalonia.Media.TextFormatting
/// number of characters of the specified range
/// an array of bounding rectangles.
public abstract IReadOnlyList GetTextBounds(int firstTextSourceCharacterIndex, int textLength);
-
- ///
- /// Gets the text line offset x.
- ///
- /// The line width.
- /// The paragraph width including whitespace.
- /// The paragraph width.
- /// The text alignment.
- /// The flow direction of the line.
- /// The paragraph offset.
- internal static double GetParagraphOffsetX(double width, double widthIncludingTrailingWhitespace,
- double paragraphWidth, TextAlignment textAlignment, FlowDirection flowDirection)
- {
- if (double.IsPositiveInfinity(paragraphWidth))
- {
- return 0;
- }
-
- if (flowDirection == FlowDirection.LeftToRight)
- {
- switch (textAlignment)
- {
- case TextAlignment.Center:
- return Math.Max(0, (paragraphWidth - width) / 2);
-
- case TextAlignment.Right:
- return Math.Max(0, paragraphWidth - widthIncludingTrailingWhitespace);
-
- default:
- return 0;
- }
- }
-
- switch (textAlignment)
- {
- case TextAlignment.Center:
- return Math.Max(0, (paragraphWidth - width) / 2);
-
- case TextAlignment.Right:
- return 0;
-
- default:
- return Math.Max(0, paragraphWidth - widthIncludingTrailingWhitespace);
- }
- }
}
}
diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs
index 8b5e2cc2ce..7c686358e2 100644
--- a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs
@@ -10,10 +10,10 @@ namespace Avalonia.Media.TextFormatting
private readonly double _paragraphWidth;
private readonly TextParagraphProperties _paragraphProperties;
private TextLineMetrics _textLineMetrics;
- private readonly FlowDirection _flowDirection;
+ private readonly FlowDirection _resolvedFlowDirection;
public TextLineImpl(List textRuns, int firstTextSourceIndex, int length, double paragraphWidth,
- TextParagraphProperties paragraphProperties, FlowDirection flowDirection = FlowDirection.LeftToRight,
+ TextParagraphProperties paragraphProperties, FlowDirection resolvedFlowDirection = FlowDirection.LeftToRight,
TextLineBreak? lineBreak = null, bool hasCollapsed = false)
{
FirstTextSourceIndex = firstTextSourceIndex;
@@ -25,7 +25,7 @@ namespace Avalonia.Media.TextFormatting
_paragraphWidth = paragraphWidth;
_paragraphProperties = paragraphProperties;
- _flowDirection = flowDirection;
+ _resolvedFlowDirection = resolvedFlowDirection;
}
///
@@ -136,7 +136,7 @@ namespace Avalonia.Media.TextFormatting
}
var collapsedLine = new TextLineImpl(collapsedRuns, FirstTextSourceIndex, Length, _paragraphWidth, _paragraphProperties,
- _flowDirection, TextLineBreak, true);
+ _resolvedFlowDirection, TextLineBreak, true);
if (collapsedRuns.Count > 0)
{
@@ -144,7 +144,14 @@ namespace Avalonia.Media.TextFormatting
}
return collapsedLine;
+ }
+ ///
+ public override void Justify(JustificationProperties justificationProperties)
+ {
+ justificationProperties.Justify(this);
+
+ _textLineMetrics = CreateLineMetrics();
}
///
@@ -167,7 +174,7 @@ namespace Avalonia.Media.TextFormatting
return shapedTextCharacters.GlyphRun.GetCharacterHitFromDistance(distance, out _);
}
- return _flowDirection == FlowDirection.LeftToRight ?
+ return _resolvedFlowDirection == FlowDirection.LeftToRight ?
new CharacterHit(FirstTextSourceIndex) :
new CharacterHit(FirstTextSourceIndex + Length);
}
@@ -260,7 +267,7 @@ namespace Avalonia.Media.TextFormatting
//Look at the left and right edge of the current run
if (currentRun.IsLeftToRight)
{
- if (_flowDirection == FlowDirection.LeftToRight && (lastRun == null || lastRun.IsLeftToRight))
+ if (_resolvedFlowDirection == FlowDirection.LeftToRight && (lastRun == null || lastRun.IsLeftToRight))
{
if (characterIndex <= currentPosition)
{
@@ -735,7 +742,7 @@ namespace Avalonia.Media.TextFormatting
// Build up the collection of ordered runs.
var run = _textRuns[0];
- OrderedBidiRun orderedRun = new(run, GetRunBidiLevel(run, _flowDirection));
+ OrderedBidiRun orderedRun = new(run, GetRunBidiLevel(run, _resolvedFlowDirection));
var current = orderedRun;
@@ -743,7 +750,7 @@ namespace Avalonia.Media.TextFormatting
{
run = _textRuns[i];
- current.Next = new OrderedBidiRun(run, GetRunBidiLevel(run, _flowDirection));
+ current.Next = new OrderedBidiRun(run, GetRunBidiLevel(run, _resolvedFlowDirection));
current = current.Next;
}
@@ -762,7 +769,7 @@ namespace Avalonia.Media.TextFormatting
{
var currentRun = _textRuns[i];
- var level = GetRunBidiLevel(currentRun, _flowDirection);
+ var level = GetRunBidiLevel(currentRun, _resolvedFlowDirection);
if (level > max)
{
@@ -1242,8 +1249,7 @@ namespace Avalonia.Media.TextFormatting
}
}
- var start = GetParagraphOffsetX(width, widthIncludingWhitespace, _paragraphWidth,
- _paragraphProperties.TextAlignment, _paragraphProperties.FlowDirection);
+ var start = GetParagraphOffsetX(width, widthIncludingWhitespace);
if (!double.IsNaN(lineHeight) && !MathUtilities.IsZero(lineHeight))
{
@@ -1257,6 +1263,55 @@ namespace Avalonia.Media.TextFormatting
-ascent, trailingWhitespaceLength, width, widthIncludingWhitespace);
}
+ ///
+ /// Gets the text line offset x.
+ ///
+ /// The line width.
+ /// The paragraph width including whitespace.
+
+ /// The paragraph offset.
+ private double GetParagraphOffsetX(double width, double widthIncludingTrailingWhitespace)
+ {
+ if (double.IsPositiveInfinity(_paragraphWidth))
+ {
+ return 0;
+ }
+
+ var textAlignment = _paragraphProperties.TextAlignment;
+ var paragraphFlowDirection = _paragraphProperties.FlowDirection;
+
+ switch (textAlignment)
+ {
+ case TextAlignment.Start:
+ {
+ textAlignment = paragraphFlowDirection == FlowDirection.LeftToRight ? TextAlignment.Left : TextAlignment.Right;
+ break;
+ }
+ case TextAlignment.End:
+ {
+ textAlignment = paragraphFlowDirection == FlowDirection.RightToLeft ? TextAlignment.Left : TextAlignment.Right;
+ break;
+ }
+ case TextAlignment.DetectFromContent:
+ {
+ textAlignment = _resolvedFlowDirection == FlowDirection.LeftToRight ? TextAlignment.Left : TextAlignment.Right;
+ break;
+ }
+ }
+
+ switch (textAlignment)
+ {
+ case TextAlignment.Center:
+ return Math.Max(0, (_paragraphWidth - width) / 2);
+
+ case TextAlignment.Right:
+ return Math.Max(0, _paragraphWidth - widthIncludingTrailingWhitespace);
+
+ default:
+ return 0;
+ }
+ }
+
private sealed class OrderedBidiRun
{
public OrderedBidiRun(DrawableTextRun run, sbyte level)
diff --git a/src/Avalonia.Base/Media/TextFormatting/Unicode/BiDi.trie.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/BiDi.trie.cs
new file mode 100644
index 0000000000..7ce5453ec4
--- /dev/null
+++ b/src/Avalonia.Base/Media/TextFormatting/Unicode/BiDi.trie.cs
@@ -0,0 +1,42 @@
+using System;
+
+namespace Avalonia.Media.TextFormatting.Unicode
+{
+ internal static class BiDiTrie
+ {
+ public static ReadOnlySpan Data => new byte[]
+ {
+ 0,16,0,0,0,0,0,0,0,0,174,224,0,68,12,187,243,236,157,11,176,85,85,25,199,151,115,207,189,247,156,203,227,94,244,132,151,128,58,122,53,184,10,74,112,26,206,61,64,105,130,89,22,163,121,85,12,17,38,26,7,197,72,37,152,166,59,56,142,132,236,64,144,16,196,212,6,26,167,98,34,197,158,71,83,10,134,116,116,236,161,165,4,50,38,61,40,64,137,178,
+ 28,109,6,164,255,102,175,205,93,44,246,99,61,247,62,192,254,102,126,243,173,189,246,218,235,251,214,183,190,253,60,251,156,51,171,129,144,91,193,87,192,157,224,1,176,14,172,7,143,3,7,172,144,208,63,3,191,0,207,130,223,130,109,224,79,224,85,240,23,176,15,188,5,246,131,183,3,182,63,4,26,115,225,253,247,193,186,51,192,96,208,1,206,7,99,64,23,184,24,92,
+ 10,38,131,43,193,117,96,6,152,5,230,128,249,96,30,88,0,22,129,101,224,223,121,66,222,5,7,65,174,64,200,80,212,141,6,99,193,90,44,175,134,94,227,150,115,132,124,15,108,4,143,131,39,192,102,240,28,93,126,17,188,194,44,191,6,118,211,246,251,193,219,116,251,67,57,111,44,141,141,132,20,192,0,112,6,24,12,58,26,123,251,63,191,209,107,127,24,109,47,108,
+ 244,236,249,246,29,212,141,109,244,180,203,4,148,39,49,203,159,68,249,74,186,124,53,244,245,180,252,5,232,91,192,92,112,144,246,227,128,30,44,47,4,75,193,125,76,63,142,65,30,162,253,62,194,245,191,193,176,189,31,115,253,61,133,229,167,192,211,24,239,211,116,204,91,176,252,124,163,231,203,75,92,251,237,88,126,148,137,141,3,118,161,110,15,215,110,31,150,107,200,143,
+ 183,104,253,255,160,79,107,242,202,5,232,211,105,253,155,232,107,64,147,55,239,131,154,188,249,60,139,182,27,78,245,72,170,29,134,49,168,27,23,80,239,80,182,129,223,180,168,197,232,227,232,247,83,96,50,184,6,244,96,28,27,193,13,40,223,8,22,162,188,24,204,199,24,230,96,121,62,184,131,250,114,119,147,55,110,159,217,76,185,22,192,38,112,15,221,118,37,244,55,185,
+ 237,107,224,48,234,182,210,242,90,102,204,223,13,104,91,19,96,67,200,118,47,48,229,223,131,29,133,248,88,241,125,252,153,234,61,224,128,164,95,239,128,67,92,157,19,149,203,77,92,46,55,29,159,3,110,31,141,200,131,45,180,175,124,75,111,223,173,76,185,198,113,38,214,13,5,91,209,231,11,76,191,127,104,234,221,39,55,198,196,229,231,104,187,137,46,239,68,249,175,180,
+ 159,55,160,255,195,249,234,128,119,154,142,221,127,30,51,184,223,59,25,36,139,1,57,101,98,80,19,56,126,56,25,36,139,1,201,98,208,144,197,32,139,1,201,98,208,144,197,32,139,1,201,98,208,144,197,160,94,99,176,179,201,187,174,117,159,9,56,2,237,115,205,189,229,66,179,247,44,205,95,190,150,62,163,217,203,212,13,96,218,59,96,16,150,75,180,110,24,179,110,36,
+ 182,185,144,46,143,133,254,40,45,175,198,115,156,75,104,249,49,250,156,232,114,44,95,213,236,61,183,115,151,175,109,246,158,211,77,231,108,29,224,158,33,221,200,173,119,50,72,22,3,146,197,160,33,139,65,22,3,146,197,160,33,139,65,22,3,146,197,160,225,196,141,193,205,184,198,107,193,53,104,255,130,247,185,178,10,231,182,28,95,247,29,212,141,4,31,1,75,209,247,189,
+ 96,46,103,227,81,172,187,28,117,183,195,135,175,130,187,184,235,205,53,117,240,252,184,212,223,99,6,88,71,203,165,0,182,131,246,86,66,166,128,89,96,61,248,73,107,239,250,255,50,229,18,24,209,134,207,186,193,15,192,223,64,251,0,66,38,130,59,193,19,224,101,80,60,29,109,17,163,85,125,97,31,122,45,88,119,90,60,175,11,182,251,96,159,222,114,55,202,171,192,203,
+ 224,69,216,43,130,182,126,98,253,216,162,27,62,172,203,236,147,180,98,208,157,197,159,100,249,71,82,139,65,119,150,127,36,237,252,187,2,231,128,25,253,78,221,249,143,187,62,88,210,124,236,231,221,43,155,123,223,79,89,207,189,167,229,112,92,197,93,223,60,88,135,207,219,54,98,12,223,134,95,235,221,103,134,205,222,251,97,63,106,238,93,239,62,111,124,146,46,255,146,174,
+ 255,21,244,175,153,54,47,49,101,135,121,87,104,71,115,239,251,131,238,251,104,187,2,218,57,13,158,253,61,204,186,127,161,252,110,72,91,7,28,196,186,92,222,43,187,244,205,123,253,223,79,183,41,230,143,125,239,111,8,179,236,112,156,21,177,206,1,195,177,126,20,215,102,12,181,55,14,250,99,224,19,116,253,228,152,190,156,0,174,102,182,153,134,242,76,174,143,155,176,124,
+ 27,173,155,39,209,255,135,35,222,107,236,65,63,11,21,124,117,100,200,123,122,57,213,78,138,108,166,121,177,138,241,229,225,58,240,203,225,120,30,251,218,79,21,223,169,116,20,120,4,49,216,0,126,200,196,162,70,143,115,19,66,252,168,209,245,155,19,190,119,219,72,245,34,58,151,223,247,215,53,123,250,73,140,97,170,1,159,182,129,45,121,79,63,7,253,59,240,74,130,115,
+ 226,104,242,26,124,221,29,227,239,114,197,220,255,99,29,238,51,14,71,13,57,240,25,238,29,212,207,130,107,90,188,119,78,95,207,247,230,206,110,58,158,197,52,135,28,142,21,1,159,1,214,20,222,7,174,25,96,106,139,119,237,193,214,181,50,227,156,137,242,46,193,207,44,157,147,148,189,121,239,251,19,81,12,44,68,175,255,0,214,159,29,210,230,60,90,63,34,166,143,161,
+ 17,140,118,191,71,81,16,107,91,166,237,198,115,237,251,23,188,239,144,248,223,23,25,26,192,69,220,186,75,4,109,14,213,160,127,193,27,223,104,166,110,52,37,23,98,127,116,4,147,90,226,109,250,251,250,108,170,167,67,207,101,142,127,53,212,213,40,61,45,225,239,232,187,251,210,93,96,9,88,1,86,211,62,106,76,155,26,248,22,237,99,43,152,89,72,63,223,55,193,159,
+ 77,17,108,67,187,109,33,60,19,179,237,166,83,0,66,240,112,54,150,110,128,155,197,35,122,10,45,183,41,210,151,161,155,246,87,161,148,24,42,71,232,36,85,50,156,116,209,229,33,160,200,232,34,104,23,164,200,216,169,112,36,35,211,48,22,2,123,83,143,142,167,66,73,70,22,80,251,61,71,237,183,37,99,184,174,165,45,128,190,33,245,109,22,40,6,228,126,137,214,85,
+ 24,92,241,203,238,118,126,217,109,235,226,231,57,223,214,197,173,231,235,216,126,109,137,237,254,235,221,126,82,34,50,151,65,115,47,130,169,237,101,125,209,177,201,82,62,197,9,139,125,156,196,205,93,84,155,147,81,84,198,42,51,47,39,155,4,29,59,220,243,212,196,58,216,39,202,10,76,228,116,57,164,60,241,36,39,31,66,133,146,163,243,92,162,229,34,213,21,74,89,130,
+ 92,157,83,182,64,94,144,18,83,206,113,62,229,50,8,31,131,184,152,71,229,38,223,71,80,91,191,141,232,253,168,201,57,74,115,206,77,230,254,201,156,183,73,197,33,237,243,131,12,34,177,240,219,250,231,15,31,190,159,160,62,211,20,223,126,144,31,113,99,14,235,75,100,123,25,255,210,138,81,154,115,35,50,31,42,125,232,138,238,124,184,215,3,46,113,109,88,123,166,108,
+ 235,74,84,222,7,249,25,180,63,216,244,69,182,255,176,253,82,116,255,54,33,58,115,26,151,71,108,255,170,199,31,83,194,231,135,175,77,238,207,42,99,59,145,142,173,182,124,229,207,217,62,110,126,185,90,196,151,180,207,227,190,196,229,83,216,190,46,146,59,170,249,101,35,54,97,115,22,54,95,113,146,246,117,134,170,109,209,57,139,186,206,83,149,176,115,94,92,78,197,29,
+ 183,101,175,59,85,69,212,127,157,188,79,235,188,147,132,61,62,167,202,100,92,107,149,84,91,187,200,4,232,241,208,230,237,149,21,8,219,150,173,15,18,19,54,146,68,87,100,198,196,174,227,183,79,66,194,108,217,142,135,74,31,124,127,34,109,117,109,165,33,38,99,31,117,126,23,57,215,171,94,7,152,18,247,125,161,100,228,225,129,85,242,208,64,211,199,90,95,76,206,169,
+ 136,29,222,166,236,62,24,180,191,233,30,59,253,235,21,182,157,104,126,242,207,224,221,119,98,76,73,26,199,219,168,99,174,104,63,42,54,248,253,89,231,90,223,164,36,225,71,208,121,214,166,45,214,102,212,185,159,247,69,117,31,147,221,71,109,156,219,147,190,214,10,242,39,204,47,118,189,106,223,182,198,19,53,71,65,99,50,45,188,141,168,117,54,99,200,183,211,149,160,249,
+ 87,201,81,153,57,143,219,71,131,252,75,83,252,227,174,234,179,31,221,103,70,166,182,119,197,189,94,20,133,125,47,215,149,160,207,29,101,9,123,239,119,4,248,52,56,7,92,20,209,174,157,27,91,123,8,67,40,21,114,71,169,74,22,148,252,235,214,176,246,225,219,47,199,246,203,142,110,175,43,236,92,4,221,99,200,206,179,104,110,70,221,215,132,105,86,68,62,3,9,178,
+ 39,226,135,136,191,81,219,203,32,227,187,168,255,114,156,215,81,37,157,29,252,119,30,76,248,109,90,244,199,26,157,115,21,1,218,13,115,34,141,93,180,63,81,223,121,17,189,94,145,181,159,148,184,126,179,223,151,58,222,167,194,168,42,201,143,234,34,125,160,91,160,251,65,247,133,110,133,238,15,61,0,186,13,154,208,109,223,135,229,34,150,207,132,30,8,61,8,186,29,122,
+ 48,244,251,153,118,68,58,6,101,14,95,226,230,48,106,93,82,241,247,125,182,97,211,70,254,219,20,27,182,147,28,127,146,241,211,57,86,37,57,183,182,108,242,251,188,219,63,95,23,134,168,63,73,30,3,162,150,195,234,76,217,246,251,230,115,68,214,166,200,123,97,58,34,155,195,162,249,16,135,138,68,109,103,195,158,136,63,54,199,43,98,63,204,23,190,94,182,223,168,49,
+ 152,176,225,111,103,162,141,138,77,217,88,233,140,209,244,56,121,159,117,250,178,33,236,59,221,201,157,23,39,28,246,190,31,63,238,112,58,223,207,159,70,237,79,165,246,103,96,121,58,45,207,68,249,243,199,248,149,164,164,121,223,163,123,141,88,175,215,216,73,29,227,69,236,154,56,231,156,8,247,53,186,231,184,176,24,165,113,238,246,237,218,236,219,212,184,162,226,110,82,162,
+ 108,196,249,28,118,30,76,99,94,227,108,199,181,211,181,231,47,219,20,27,253,71,197,43,106,126,249,114,26,34,58,135,105,251,108,211,166,232,254,166,154,163,65,219,201,230,76,84,223,34,231,5,155,34,122,204,78,43,215,69,246,199,164,37,42,199,194,252,17,201,207,36,198,34,234,135,168,63,162,62,203,216,21,21,145,220,77,34,151,69,237,217,222,175,85,226,33,114,92,11,
+ 138,91,82,49,228,219,132,249,97,226,120,172,42,113,190,218,144,184,123,35,255,25,174,143,238,189,150,201,251,45,219,251,130,72,174,155,234,91,71,252,152,166,113,14,243,69,118,44,113,191,77,104,234,88,99,67,146,176,35,179,159,152,122,95,67,245,184,47,115,110,240,197,84,63,65,253,138,74,146,57,162,98,203,100,158,185,199,110,153,92,176,121,220,78,251,51,104,94,252,99,
+ 78,82,191,53,19,246,251,51,124,125,84,187,52,126,39,135,95,167,251,187,68,38,252,115,223,27,181,53,246,122,24,159,42,238,254,20,149,79,162,121,154,86,110,197,249,152,100,28,195,16,253,189,60,27,168,198,218,214,252,37,145,59,174,4,221,151,120,117,5,124,78,153,63,250,57,229,16,250,251,143,67,168,46,74,156,251,138,33,243,93,150,232,163,221,0,188,152,232,211,164,
+ 63,178,162,107,199,196,189,167,159,27,58,247,178,238,246,46,252,181,11,155,151,65,215,51,186,231,147,56,191,252,255,71,24,14,46,3,227,193,40,112,5,205,233,82,0,21,9,74,41,83,214,164,162,73,145,209,174,176,117,30,159,123,175,74,174,123,175,139,92,15,61,21,250,6,232,105,208,36,52,230,37,138,223,95,24,174,136,252,166,127,208,182,252,61,181,123,254,208,141,165,
+ 10,113,191,75,25,151,255,162,231,70,209,253,167,72,46,45,85,201,164,35,223,55,170,88,102,74,2,54,42,145,20,58,112,126,196,119,97,250,64,183,72,127,39,166,34,137,13,137,182,121,211,176,42,153,53,172,139,204,134,190,25,250,139,208,183,64,127,9,122,14,244,109,208,183,66,207,133,190,29,122,30,244,151,161,137,196,184,158,197,54,207,72,110,67,52,249,7,108,254,29,
+ 54,247,66,239,129,126,3,122,31,244,126,232,55,161,15,64,255,211,176,79,95,235,172,146,133,157,93,228,110,232,69,208,14,244,98,232,37,208,95,135,190,7,122,41,244,10,232,123,161,151,67,47,131,94,9,253,13,232,85,208,247,65,223,15,189,26,250,1,232,53,157,102,125,140,99,7,108,110,135,205,157,208,175,38,108,155,128,67,176,123,208,170,93,95,108,143,69,206,70,199,
+ 5,85,114,246,5,93,228,92,232,115,160,135,65,127,8,186,19,122,56,52,73,120,30,236,197,72,86,108,231,129,174,232,218,77,235,247,173,125,127,116,255,15,73,213,182,202,239,108,235,92,159,134,137,191,158,239,51,41,49,53,143,182,238,143,226,226,104,99,159,172,199,227,150,234,113,36,109,31,146,242,223,244,248,249,237,85,37,169,248,155,154,195,184,216,198,197,92,103,174,77,
+ 140,67,39,31,85,164,30,198,171,147,7,186,199,255,52,206,223,166,8,242,39,105,177,241,31,145,50,232,94,255,164,109,95,20,87,100,234,101,250,213,17,21,159,85,125,20,181,21,214,214,190,252,31,0,0,255,255,0,0,0,255,255,99,102,0,0};
+ }
+}
diff --git a/src/Avalonia.Base/Media/TextFormatting/Unicode/GraphemeBreak.trie.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/GraphemeBreak.trie.cs
new file mode 100644
index 0000000000..80158e9d7e
--- /dev/null
+++ b/src/Avalonia.Base/Media/TextFormatting/Unicode/GraphemeBreak.trie.cs
@@ -0,0 +1,31 @@
+using System;
+
+namespace Avalonia.Media.TextFormatting.Unicode
+{
+ internal static class GraphemeBreakTrie
+ {
+ public static ReadOnlySpan Data => new byte[]
+ {
+ 0,14,16,0,0,0,0,0,0,0,133,64,0,245,7,10,248,236,157,127,136,29,87,21,199,103,157,111,246,157,100,211,138,180,69,177,5,241,7,88,91,40,182,150,210,12,138,161,88,27,163,96,241,15,75,161,210,18,44,149,10,193,46,24,80,49,254,163,33,16,136,54,127,84,8,34,18,253,199,68,196,42,136,154,82,21,149,6,218,82,43,72,107,161,54,22,196,173,10,110,
+ 16,140,210,82,250,125,190,51,228,228,228,206,204,157,121,119,230,109,182,243,133,15,231,220,115,207,156,123,239,121,111,247,253,216,129,189,33,207,178,130,220,66,118,147,61,228,126,19,43,230,180,171,228,139,100,31,217,31,145,127,128,28,174,153,63,66,142,146,99,228,56,249,49,249,5,249,21,57,101,242,158,36,127,36,207,145,23,201,26,89,39,255,34,103,201,43,4,200,178,47,
+ 147,175,146,131,152,93,91,242,40,217,206,216,55,200,17,114,148,28,35,199,201,9,242,19,114,146,252,134,252,150,60,65,158,54,227,63,145,191,104,254,75,228,140,94,255,63,242,32,89,218,50,91,103,43,237,25,218,55,109,57,87,255,205,244,223,166,227,119,211,190,135,92,110,214,47,152,127,61,99,103,39,51,127,7,253,157,90,175,32,31,162,255,49,29,223,78,123,135,250,119,
+ 211,222,71,246,146,255,154,243,238,227,120,63,57,64,14,155,58,5,185,82,206,241,46,229,26,242,126,165,48,185,69,13,95,143,204,43,18,243,16,207,243,16,121,158,231,125,94,207,252,45,142,191,87,246,7,231,231,95,193,248,247,93,15,30,118,227,194,240,51,157,251,37,237,99,234,239,37,47,235,252,83,140,253,129,60,71,78,147,53,178,174,121,103,213,190,26,168,143,101,62,
+ 255,150,47,140,151,172,146,203,107,230,139,72,62,201,243,223,165,61,184,201,196,247,146,7,106,206,93,140,100,125,247,224,42,247,248,158,12,60,7,138,4,252,213,252,12,188,61,193,115,170,24,201,198,30,100,99,15,242,177,7,99,15,178,177,7,249,216,131,177,7,217,216,131,124,236,193,216,131,108,236,65,62,246,96,236,65,150,188,7,87,47,207,190,75,43,199,159,136,248,254,
+ 226,189,188,230,102,243,153,247,24,191,83,123,133,236,100,108,151,198,111,167,189,131,92,37,179,239,3,239,162,15,250,159,214,249,251,105,87,201,62,29,239,167,253,51,237,129,134,207,210,135,57,127,43,235,124,152,220,70,118,145,143,144,221,228,163,228,214,113,46,27,251,146,141,207,9,25,127,30,198,223,5,89,178,223,5,55,144,29,228,3,164,24,223,139,100,99,15,178,177,7,
+ 249,216,131,62,122,112,164,231,191,167,236,87,138,0,187,3,177,51,198,191,199,253,237,179,88,0,187,46,153,113,144,172,109,111,230,20,243,158,13,228,190,229,210,243,199,187,56,62,72,78,145,236,141,179,216,141,180,159,33,223,33,143,144,117,178,115,37,203,62,71,94,220,202,191,125,109,203,50,89,154,113,167,241,197,113,98,229,194,88,136,47,153,26,63,162,255,15,94,119,45,
+ 247,113,47,57,176,61,174,198,72,54,246,96,105,236,193,216,131,108,236,193,210,230,235,193,58,95,23,50,190,46,60,202,247,9,167,249,122,248,146,222,231,177,198,239,174,222,202,215,231,119,232,107,244,223,201,239,152,179,194,207,44,143,47,207,230,127,79,123,141,121,13,127,54,240,94,227,36,57,189,124,238,62,178,53,250,235,21,239,73,254,195,248,171,102,110,153,223,131,93,170,
+ 247,152,21,1,46,227,220,149,102,254,157,147,115,247,61,77,185,118,114,254,253,95,239,171,169,117,115,205,92,65,174,227,57,111,114,239,87,62,200,107,110,35,31,159,94,11,222,203,164,53,110,113,121,69,4,159,50,235,223,75,255,179,110,63,15,112,188,91,235,126,161,97,175,133,225,43,45,114,139,30,185,175,67,79,138,158,249,46,123,179,99,192,245,246,178,7,39,184,230,195,
+ 129,199,228,231,27,228,113,42,90,178,71,239,3,91,85,246,56,86,107,248,60,46,204,127,189,81,204,193,209,134,207,118,47,187,241,177,13,120,111,221,113,238,233,135,21,251,186,34,209,253,159,63,104,121,238,159,6,242,207,246,220,135,175,233,207,255,161,13,240,123,224,65,238,225,50,190,206,127,211,236,229,219,9,246,181,74,126,61,153,217,83,180,79,145,103,54,192,121,139,72,
+ 94,224,94,255,214,176,223,67,29,207,243,207,139,160,15,135,220,30,255,125,17,236,185,232,192,35,27,96,15,215,231,75,255,39,231,219,243,60,130,55,24,127,41,242,154,60,130,81,139,211,216,255,44,217,243,56,239,200,162,31,55,188,206,73,41,212,176,153,133,139,144,114,223,165,197,212,9,248,155,93,147,10,98,133,22,164,254,221,149,90,232,129,20,123,26,66,168,97,210,144,
+ 227,107,100,53,57,41,53,81,139,148,69,91,10,9,217,204,66,11,46,86,33,130,216,58,214,135,62,215,177,0,36,16,203,52,46,138,31,195,252,108,214,9,117,147,145,18,3,28,226,226,165,38,165,211,81,147,142,215,133,36,166,230,36,148,16,56,87,121,29,166,206,156,130,214,18,199,84,162,216,92,104,12,186,95,49,22,101,226,130,132,10,68,247,8,71,72,147,10,208,1,
+ 49,126,149,80,193,68,237,144,130,33,239,161,126,174,181,183,168,159,39,170,153,87,144,66,121,75,134,88,175,73,112,207,61,184,121,113,115,80,242,6,144,128,124,78,250,18,28,161,24,166,193,134,49,76,124,40,193,80,55,39,126,114,64,65,215,135,142,197,128,6,36,144,47,74,89,27,21,196,10,202,34,4,119,158,161,215,246,123,128,163,148,40,126,140,50,208,32,52,204,251,
+ 92,216,64,207,130,243,225,206,58,148,224,122,43,26,67,69,46,2,99,84,212,76,181,63,4,98,177,18,205,135,99,42,40,50,29,168,21,141,165,22,148,190,132,10,134,22,140,133,233,39,12,165,68,241,99,148,129,10,193,16,43,196,38,38,22,2,231,204,76,76,166,3,55,6,137,17,34,115,196,128,152,194,9,5,183,190,152,56,20,63,198,52,80,35,40,139,16,212,138,250,
+ 18,240,203,60,81,166,18,5,74,157,160,196,8,138,40,139,20,148,210,151,169,19,41,84,196,68,129,193,230,75,13,208,156,121,5,181,162,62,42,136,169,131,26,82,9,145,251,133,201,109,83,27,1,134,18,148,190,215,8,89,43,81,82,158,9,45,145,138,184,173,25,18,90,208,54,31,61,48,175,16,217,55,184,124,113,185,125,74,26,214,66,205,92,91,193,209,69,80,43,234,
+ 35,34,183,171,208,245,194,134,154,80,250,20,20,49,62,2,72,3,112,62,34,137,221,99,95,130,65,28,8,196,189,68,129,241,197,1,67,159,130,34,10,122,90,71,204,58,168,161,220,147,181,165,143,26,164,2,68,82,10,1,164,156,28,72,168,65,140,21,7,140,191,40,33,130,174,53,165,237,133,9,37,129,115,136,139,139,155,79,45,184,53,196,0,55,7,51,215,86,210,0,
+ 26,16,151,55,175,96,240,99,68,34,13,243,94,240,129,138,57,209,49,76,204,11,3,81,181,214,60,130,97,72,65,73,85,7,74,40,134,10,188,208,48,174,138,165,148,40,80,36,144,131,158,214,20,183,246,80,66,34,186,174,93,55,135,26,250,16,90,32,74,234,245,69,129,67,92,92,148,170,58,67,10,21,136,130,138,113,151,117,98,114,164,41,169,71,161,167,122,48,216,57,
+ 148,3,39,40,109,36,230,58,81,234,242,218,74,20,84,248,153,137,45,90,216,228,107,35,64,76,174,29,135,4,71,223,130,33,52,215,70,80,82,74,180,166,164,44,218,81,8,32,21,113,24,250,18,18,215,19,3,34,144,132,107,136,137,137,137,87,9,106,197,128,6,250,16,220,30,196,204,73,197,88,90,212,142,149,40,246,90,84,96,133,0,125,75,12,104,64,2,121,162,182,
+ 79,33,97,29,81,80,129,4,242,36,48,143,68,123,234,42,232,94,68,125,24,74,137,3,138,24,127,72,65,73,37,49,160,1,49,121,162,182,141,96,174,19,227,35,128,168,109,146,40,112,72,32,134,166,98,29,36,6,68,32,198,79,37,49,192,81,10,138,56,80,38,168,68,237,144,66,36,67,236,35,54,15,1,68,129,97,30,137,171,5,199,84,168,64,212,166,18,58,146,153,
+ 189,160,2,169,240,83,9,21,248,156,210,98,234,24,27,35,9,80,214,192,212,49,113,24,155,82,80,68,73,89,19,134,148,66,13,41,106,103,166,31,210,129,58,193,209,69,104,153,47,202,34,5,197,250,152,14,84,168,161,47,193,209,103,253,33,133,30,73,181,86,147,208,148,208,49,183,173,16,160,42,142,72,230,21,106,104,154,71,36,165,80,131,157,207,106,114,82,11,142,212,
+ 245,96,232,83,162,244,85,87,90,176,104,137,50,228,90,178,32,182,110,0,172,182,45,152,149,158,185,196,209,86,219,18,226,181,178,1,240,226,191,181,184,160,103,125,50,244,122,94,175,1,0,0,255,255,0,0,0,255,255,99,102,0,0};
+ }
+}
diff --git a/src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeData.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeData.cs
index 471cb52bea..ff39dc5011 100644
--- a/src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeData.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeData.cs
@@ -1,4 +1,5 @@
-using System.Runtime.CompilerServices;
+using System.IO;
+using System.Runtime.CompilerServices;
namespace Avalonia.Media.TextFormatting.Unicode
{
@@ -17,14 +18,14 @@ namespace Avalonia.Media.TextFormatting.Unicode
internal const int SCRIPT_SHIFT = CATEGORY_BITS;
internal const int LINEBREAK_SHIFT = CATEGORY_BITS + SCRIPT_BITS;
-
+
internal const int BIDIPAIREDBRACKEDTYPE_SHIFT = BIDIPAIREDBRACKED_BITS;
internal const int BIDICLASS_SHIFT = BIDIPAIREDBRACKED_BITS + BIDIPAIREDBRACKEDTYPE_BITS;
-
+
internal const int CATEGORY_MASK = (1 << CATEGORY_BITS) - 1;
internal const int SCRIPT_MASK = (1 << SCRIPT_BITS) - 1;
internal const int LINEBREAK_MASK = (1 << LINEBREAK_BITS) - 1;
-
+
internal const int BIDIPAIREDBRACKED_MASK = (1 << BIDIPAIREDBRACKED_BITS) - 1;
internal const int BIDIPAIREDBRACKEDTYPE_MASK = (1 << BIDIPAIREDBRACKEDTYPE_BITS) - 1;
internal const int BIDICLASS_MASK = (1 << BIDICLASS_BITS) - 1;
@@ -35,9 +36,29 @@ namespace Avalonia.Media.TextFormatting.Unicode
static UnicodeData()
{
- s_unicodeDataTrie = new UnicodeTrie(typeof(UnicodeData).Assembly.GetManifestResourceStream("Avalonia.Assets.UnicodeData.trie")!);
- s_graphemeBreakTrie = new UnicodeTrie(typeof(UnicodeData).Assembly.GetManifestResourceStream("Avalonia.Assets.GraphemeBreak.trie")!);
- s_biDiTrie = new UnicodeTrie(typeof(UnicodeData).Assembly.GetManifestResourceStream("Avalonia.Assets.BiDi.trie")!);
+ unsafe
+ {
+ var unicodeData = UnicodeDataTrie.Data;
+
+ fixed (byte* unicodeDataPtr = unicodeData)
+ {
+ s_unicodeDataTrie = new UnicodeTrie(new UnmanagedMemoryStream(unicodeDataPtr, unicodeData.Length));
+ }
+
+ var graphemeData = GraphemeBreakTrie.Data;
+
+ fixed (byte* graphemeDataPtr = graphemeData)
+ {
+ s_graphemeBreakTrie = new UnicodeTrie(new UnmanagedMemoryStream(graphemeDataPtr, graphemeData.Length));
+ }
+
+ var bidiData = BiDiTrie.Data;
+
+ fixed (byte* bidiDataPtr = bidiData)
+ {
+ s_biDiTrie = new UnicodeTrie(new UnmanagedMemoryStream(bidiDataPtr, bidiData.Length));
+ }
+ }
}
///
@@ -72,7 +93,7 @@ namespace Avalonia.Media.TextFormatting.Unicode
{
return (BidiClass)((s_biDiTrie.Get(codepoint) >> BIDICLASS_SHIFT) & BIDICLASS_MASK);
}
-
+
///
/// Gets the for a Unicode codepoint.
///
@@ -83,7 +104,7 @@ namespace Avalonia.Media.TextFormatting.Unicode
{
return (BidiPairedBracketType)((s_biDiTrie.Get(codepoint) >> BIDIPAIREDBRACKEDTYPE_SHIFT) & BIDIPAIREDBRACKEDTYPE_MASK);
}
-
+
///
/// Gets the paired bracket for a Unicode codepoint.
///
diff --git a/src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeData.trie.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeData.trie.cs
new file mode 100644
index 0000000000..dd55fda374
--- /dev/null
+++ b/src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeData.trie.cs
@@ -0,0 +1,122 @@
+using System;
+
+namespace Avalonia.Media.TextFormatting.Unicode
+{
+ internal static class UnicodeDataTrie
+ {
+ public static ReadOnlySpan Data => new byte[]
+ {
+ 0,16,0,0,0,0,0,0,0,1,154,144,0,161,43,94,212,236,157,15,148,28,85,157,239,107,230,215,147,204,84,146,78,103,152,252,115,128,16,8,241,224,234,238,193,197,221,163,139,231,117,2,132,158,162,51,52,67,155,63,244,152,233,66,130,202,81,247,225,209,221,167,7,125,13,139,74,202,48,84,134,89,16,199,5,70,81,244,249,111,117,209,93,93,229,5,68,157,41,218,
+ 177,9,35,168,3,72,16,124,60,149,125,111,87,217,231,62,193,247,190,53,117,59,115,231,166,254,220,234,170,234,10,58,57,231,147,223,253,255,251,221,63,117,235,214,173,59,213,151,100,20,101,47,208,193,91,193,181,224,0,56,4,110,3,147,224,51,224,139,224,31,193,127,7,223,5,223,7,143,130,199,193,51,224,57,240,60,248,13,80,186,20,165,27,228,192,6,112,26,56,19,
+ 188,10,188,6,188,30,108,7,26,24,2,151,129,125,224,74,240,14,240,87,224,253,224,131,224,58,96,128,155,192,173,224,239,192,39,193,231,193,87,192,55,193,183,193,247,192,15,192,143,193,83,224,25,240,28,120,30,188,8,104,153,162,172,0,39,129,126,112,6,248,35,240,185,94,69,249,83,200,191,0,231,129,29,96,16,148,193,59,215,41,202,217,39,33,30,252,5,184,7,105,
+ 135,17,126,57,184,202,142,95,166,40,239,5,53,112,3,48,193,71,192,237,224,83,224,11,224,171,224,27,224,1,80,7,179,224,49,240,52,248,5,248,21,248,15,208,177,92,81,122,192,26,176,30,108,2,47,7,127,2,206,131,238,63,135,252,79,224,66,48,8,118,129,125,224,74,112,21,120,231,114,199,246,247,66,94,11,14,48,255,69,200,251,94,212,227,16,252,183,128,219,193,
+ 39,193,231,89,252,165,136,223,11,116,240,86,112,53,184,100,13,218,22,241,23,160,190,239,129,255,61,140,107,25,31,2,7,24,135,24,223,92,238,200,111,67,78,131,135,152,255,81,38,31,135,124,6,60,7,246,162,252,189,224,121,184,111,67,220,71,99,226,14,240,34,202,188,27,242,110,208,213,173,40,171,192,90,112,10,56,19,124,17,225,95,6,175,130,251,53,224,245,224,235,
+ 240,127,3,117,189,0,238,251,224,222,217,237,148,119,9,228,94,160,131,183,130,119,128,105,132,255,21,228,53,96,207,6,69,185,30,242,33,132,29,132,28,7,31,3,119,129,207,117,59,237,251,35,196,61,102,143,157,110,232,0,79,195,253,75,240,107,240,0,252,47,64,214,33,103,193,3,176,225,49,200,39,193,179,224,127,131,127,197,245,180,19,225,117,240,6,123,252,245,34,109,
+ 175,115,93,253,166,219,185,62,121,244,53,139,253,74,15,108,71,88,6,114,37,184,2,121,251,32,79,6,91,192,107,215,43,202,54,48,0,94,9,255,57,160,128,122,157,11,121,62,184,26,121,223,3,174,5,22,198,81,17,97,159,66,218,207,130,47,129,175,129,251,192,52,120,8,252,8,60,9,158,5,207,129,171,193,123,64,21,101,62,15,249,34,200,192,221,3,214,108,112,194,
+ 47,69,153,21,240,12,202,255,25,120,11,194,222,2,158,227,220,111,7,87,131,247,128,141,96,51,56,11,92,11,14,128,67,224,108,48,6,62,10,62,14,254,27,248,50,211,241,85,91,255,58,69,249,237,58,199,127,45,236,184,1,220,11,247,189,62,60,203,234,242,44,195,100,242,86,112,187,16,247,172,11,85,148,241,90,144,7,111,178,251,162,199,9,107,114,0,237,122,53,194,
+ 222,13,14,193,125,27,199,251,16,246,54,244,215,7,32,51,125,138,242,97,200,155,193,4,227,58,212,229,173,208,241,24,210,124,162,199,25,111,159,133,252,14,202,253,142,93,247,158,5,183,200,215,17,119,24,60,141,188,191,4,83,112,175,132,142,6,100,31,228,201,224,215,8,255,33,252,143,129,45,240,111,1,71,225,126,37,228,140,221,54,61,24,163,128,96,67,15,120,100,131,
+ 227,94,3,158,128,251,41,240,115,198,47,55,56,121,68,206,65,89,231,44,161,44,181,129,178,212,6,125,242,109,208,156,63,206,9,145,231,156,37,148,165,54,80,150,218,160,111,169,13,150,218,64,89,106,131,190,165,54,88,106,3,101,169,13,250,254,48,219,224,55,120,118,123,1,207,120,231,194,125,238,9,130,162,226,185,19,235,218,229,144,25,236,141,156,143,176,243,125,88,137,
+ 52,171,85,103,239,112,18,207,204,159,1,69,132,23,193,122,132,247,33,254,75,108,79,228,171,144,247,130,7,192,38,196,157,14,202,72,247,10,200,97,200,87,67,94,1,249,102,240,58,184,255,18,242,175,193,251,193,118,248,53,240,65,184,13,48,4,247,101,96,12,238,203,33,175,2,39,219,251,80,125,216,23,0,239,132,255,189,224,58,96,128,119,161,157,15,65,94,3,121,13,
+ 248,20,210,220,6,255,23,32,191,8,254,30,124,9,124,25,252,3,184,7,124,97,41,78,89,106,23,101,105,76,244,45,93,15,75,115,129,18,219,92,48,137,121,247,211,224,179,224,106,220,23,174,78,17,29,123,165,122,138,236,93,66,89,106,3,101,169,13,214,187,183,65,152,245,244,63,168,11,238,127,132,251,94,240,29,48,3,30,81,157,247,212,54,223,64,252,19,156,255,219,
+ 1,60,128,244,218,50,199,253,83,150,239,127,66,62,7,158,183,215,151,235,240,94,13,178,107,197,226,124,43,224,63,9,212,177,222,189,30,107,206,45,88,159,158,5,250,17,118,6,248,163,21,233,206,61,54,15,176,245,120,6,246,157,223,235,216,105,191,139,125,193,126,46,57,9,235,102,112,15,120,18,156,134,118,168,130,113,48,11,186,215,226,93,37,248,47,224,159,192,111,64,
+ 55,218,226,92,240,46,112,15,248,53,56,7,122,222,2,198,193,44,88,137,231,139,34,56,104,239,161,171,138,114,39,120,18,156,134,246,168,130,15,131,7,153,124,142,201,15,115,188,106,37,222,215,195,230,14,240,118,184,63,191,114,113,188,205,115,8,91,181,106,193,191,19,238,27,193,247,192,170,44,252,224,70,112,103,71,188,124,143,149,217,181,26,239,179,87,59,238,18,147,119,
+ 50,62,8,255,167,192,207,184,240,173,57,39,172,157,108,203,29,223,110,75,40,75,109,176,98,169,13,150,218,64,249,131,104,131,107,83,158,3,21,156,151,82,82,226,190,92,186,207,94,39,2,127,138,62,120,29,216,190,194,57,191,83,199,186,166,206,208,16,54,4,222,0,142,96,29,243,3,240,70,184,247,131,17,236,221,190,157,229,217,193,241,238,21,206,218,232,125,144,31,0,
+ 143,193,125,35,228,223,130,219,192,36,120,26,97,159,97,121,103,177,134,249,49,56,104,159,3,234,195,115,50,194,191,6,190,9,190,5,126,221,231,172,133,94,128,60,226,162,47,131,53,87,38,128,31,35,223,83,224,231,46,249,255,13,97,207,131,23,193,74,164,237,178,215,77,43,157,184,181,144,167,128,51,153,127,7,232,91,235,172,191,94,3,54,98,45,247,122,200,243,65,17,
+ 156,140,184,45,107,157,116,175,132,44,35,236,28,200,97,200,43,192,219,192,187,192,53,92,121,231,34,254,92,112,61,11,123,202,110,139,149,88,43,162,61,198,33,207,71,220,199,184,244,59,124,184,11,233,138,72,255,57,46,125,25,254,123,224,255,103,46,236,126,184,167,193,67,96,24,241,251,192,143,224,126,18,92,9,247,85,224,89,184,255,5,188,19,238,127,135,252,29,232,196,
+ 218,241,189,240,191,31,124,16,168,240,247,130,245,171,22,202,222,4,247,203,193,40,226,255,4,242,207,193,235,193,5,171,156,53,168,104,243,45,72,119,27,120,3,226,222,8,38,225,254,36,216,239,146,118,7,248,60,226,222,142,184,119,179,248,175,192,255,53,240,62,248,63,0,238,91,235,172,115,255,150,197,255,29,228,39,61,202,218,1,166,145,254,243,66,252,207,209,7,31,67,
+ 251,127,5,225,223,4,223,2,15,114,105,30,134,251,81,240,19,240,16,242,255,0,252,15,184,31,135,252,41,248,95,112,255,2,242,255,248,232,221,193,248,127,72,147,193,154,121,37,56,9,108,0,167,102,23,226,183,194,253,10,240,106,240,58,144,7,5,46,126,135,15,37,143,116,187,17,190,25,99,119,4,242,87,176,243,87,109,228,205,130,77,143,99,78,121,156,241,151,62,246,
+ 252,181,100,157,119,252,30,243,91,180,195,111,99,224,69,38,187,214,121,167,177,159,99,127,235,18,254,95,79,128,126,200,193,182,92,11,172,21,252,31,106,177,46,69,220,139,138,17,185,9,186,111,5,119,112,54,124,2,238,79,131,83,96,219,102,240,247,112,255,19,56,156,80,155,255,27,230,185,187,236,179,183,217,197,225,103,65,247,89,160,129,240,175,67,62,194,197,63,1,247,
+ 217,8,59,251,15,148,159,181,161,254,175,109,145,127,97,253,244,239,66,127,238,136,153,109,208,181,221,135,223,49,253,203,176,199,178,10,104,8,211,36,89,179,58,126,123,227,102,8,118,14,49,54,194,222,83,193,214,54,218,93,229,206,239,87,5,254,120,181,35,255,140,201,49,216,248,81,112,231,58,199,127,46,194,95,128,124,1,156,239,98,115,145,229,171,130,50,220,195,46,105,
+ 254,175,189,247,216,139,53,237,106,103,223,244,109,144,239,2,215,128,223,217,123,189,171,177,134,5,227,128,16,191,162,119,33,207,139,108,175,117,37,248,45,220,157,189,78,158,9,164,237,131,251,100,112,6,120,5,88,15,54,129,173,224,85,224,19,72,115,63,234,145,217,24,141,79,163,140,207,174,115,228,151,109,247,106,236,157,175,78,127,92,201,242,207,176,245,91,62,246,94,134,
+ 58,61,136,248,135,67,214,233,114,228,155,123,9,180,195,85,176,243,42,23,126,202,108,127,13,198,202,235,193,47,218,80,151,95,65,199,89,88,75,255,134,211,165,96,95,161,43,231,157,103,21,226,214,130,83,192,153,224,85,224,53,44,253,185,62,249,118,48,102,236,107,55,183,240,55,44,5,184,75,160,12,246,130,179,237,119,12,27,157,191,91,234,101,82,207,57,239,131,222,2,
+ 249,159,193,187,192,53,224,122,22,126,47,199,140,240,55,50,47,67,25,167,131,87,128,18,23,254,106,248,95,11,182,129,129,141,11,225,151,192,189,7,236,3,186,173,127,163,19,254,14,200,119,51,247,12,184,6,238,26,243,223,0,121,16,182,140,231,156,185,167,201,199,56,255,93,112,127,14,220,195,194,190,206,228,253,144,223,229,234,97,162,172,143,176,114,191,207,218,233,78,248,
+ 31,97,238,42,87,246,19,224,103,224,151,224,95,89,25,85,97,78,253,15,166,167,210,227,188,115,225,219,234,222,54,211,179,38,253,115,123,73,177,134,181,237,57,39,128,45,231,36,204,198,53,254,237,144,180,254,115,78,96,54,167,92,255,179,82,190,198,239,61,1,56,39,166,107,249,222,151,32,61,109,28,127,103,191,132,219,233,222,152,218,250,181,224,18,172,89,46,97,236,16,
+ 214,59,27,216,119,28,54,184,176,109,205,241,233,95,106,236,245,56,251,179,247,15,128,129,53,233,239,233,164,77,41,211,113,140,109,249,46,9,50,202,182,90,143,178,237,112,183,67,30,110,169,124,93,129,236,175,169,74,165,214,161,84,14,99,189,171,144,178,47,223,9,63,128,187,130,176,97,60,222,236,169,57,113,85,59,78,233,84,118,43,153,121,89,57,220,161,12,29,238,148,
+ 162,98,167,87,58,149,170,93,142,226,48,175,87,33,156,23,107,15,118,93,42,168,195,30,212,103,4,122,119,129,17,48,133,184,169,54,48,12,253,85,244,229,158,60,36,244,110,67,251,255,161,255,219,230,54,46,15,199,55,190,131,216,175,216,99,16,207,174,24,243,251,48,54,246,49,116,244,79,165,214,173,140,0,29,110,235,80,183,114,217,97,167,223,182,163,15,117,54,118,116,
+ 228,171,34,125,25,233,202,243,233,51,74,129,229,173,176,252,101,96,33,255,94,228,47,51,127,217,142,87,236,179,146,201,141,247,42,116,212,125,226,167,18,166,10,253,83,1,105,234,191,231,114,42,166,52,245,19,88,214,125,234,53,229,147,166,46,184,235,46,121,234,33,242,76,5,164,159,10,136,155,146,212,85,119,73,51,5,44,23,123,44,129,58,120,144,203,255,160,135,123,42,
+ 130,156,138,177,127,167,124,108,171,199,168,167,126,130,200,41,15,234,62,253,94,119,105,147,122,68,125,83,47,17,172,54,235,155,150,96,0,247,223,129,0,70,4,6,108,106,221,32,195,100,247,98,191,194,165,113,201,63,210,164,230,220,243,143,161,176,176,121,153,113,181,119,196,142,83,22,179,41,227,208,212,53,32,196,143,180,136,137,53,143,185,132,50,223,6,74,135,35,243,33,
+ 176,243,40,30,132,41,39,239,80,92,65,74,1,20,153,28,176,251,105,197,130,159,176,70,36,48,192,252,5,70,5,207,82,69,46,158,24,35,43,156,126,182,227,42,76,22,25,132,248,34,39,139,172,172,98,12,16,87,102,209,135,66,130,20,5,119,209,69,103,81,82,214,151,97,190,1,245,144,178,224,161,175,234,210,222,5,183,246,95,254,210,166,144,50,197,19,68,234,160,116,
+ 97,215,60,38,187,214,75,112,95,204,40,158,32,118,22,19,144,197,22,243,22,78,16,251,11,17,36,97,30,92,217,73,109,129,216,156,223,3,119,191,7,221,39,0,253,157,157,202,250,237,153,99,246,110,70,216,102,112,234,246,206,121,127,110,103,215,75,142,245,59,51,243,178,127,21,29,11,235,231,221,55,118,204,75,18,238,207,36,160,238,164,182,65,1,186,251,97,127,255,170,
+ 227,211,249,113,93,7,29,199,118,196,220,9,121,39,99,226,218,206,121,110,7,21,172,91,38,58,58,149,73,132,79,2,115,27,230,70,73,42,216,47,190,14,146,160,119,226,90,236,219,193,111,160,12,35,69,6,20,249,180,205,251,128,41,195,54,57,198,238,235,148,98,194,238,131,251,24,130,77,70,202,109,104,156,64,76,96,92,25,1,227,114,187,226,63,118,15,32,238,0,23,
+ 63,233,146,214,96,250,198,208,31,99,18,24,156,141,147,12,195,182,119,115,107,216,215,208,117,144,6,48,117,216,195,220,70,155,49,161,219,140,9,98,115,146,193,235,232,120,105,163,109,57,49,41,93,129,245,172,36,26,210,83,136,123,10,113,28,249,97,167,20,214,6,236,69,165,76,99,79,87,32,211,72,55,13,142,130,185,121,58,149,185,143,116,204,135,17,171,115,3,233,158,
+ 216,211,57,79,227,141,200,199,49,125,10,244,248,248,27,12,98,101,205,33,126,174,69,8,249,181,62,244,119,194,148,202,24,39,12,98,118,87,16,78,226,181,188,217,31,59,61,165,140,145,192,28,64,33,202,165,16,182,154,146,107,12,147,191,111,17,230,236,243,186,148,81,6,217,182,17,116,251,64,205,182,33,119,127,88,40,32,47,177,120,226,251,133,220,109,178,235,98,192,63,
+ 202,213,201,20,32,150,118,148,171,51,113,126,147,149,65,1,237,61,202,229,37,151,54,49,56,251,76,65,247,216,183,176,14,144,192,96,249,111,255,27,172,183,193,56,220,227,34,127,131,245,183,157,230,60,39,253,4,211,71,205,249,231,34,204,33,96,6,16,252,22,246,165,44,23,72,168,139,37,132,91,30,249,172,0,40,32,47,9,186,44,15,55,177,186,16,228,12,171,207,12,
+ 171,91,131,133,19,71,67,8,111,120,164,109,184,228,37,151,118,176,4,187,200,239,62,247,32,238,99,18,52,152,61,22,87,126,3,254,185,128,242,137,191,238,47,194,216,2,163,128,236,113,183,2,99,38,0,18,210,145,100,62,67,162,44,195,39,158,60,210,18,87,23,3,254,81,86,159,81,86,55,83,128,92,234,61,202,165,167,230,245,184,34,218,125,196,206,111,0,83,40,119,
+ 12,125,55,38,193,4,242,222,126,145,179,47,226,167,195,244,169,103,238,178,46,37,203,32,248,213,151,225,249,222,7,98,229,170,30,126,53,36,20,144,151,4,93,170,143,77,57,212,65,133,63,11,153,99,117,202,9,16,75,155,229,234,76,156,63,199,133,145,11,57,174,108,18,226,84,15,59,115,66,185,189,63,238,148,98,51,203,223,231,1,73,142,179,198,229,152,3,78,103,115,
+ 11,164,229,2,241,115,210,233,139,195,101,210,18,115,147,16,70,46,233,200,67,15,185,132,91,18,144,80,223,25,212,119,6,52,152,156,1,228,18,63,195,194,103,56,127,131,75,107,185,148,77,92,25,36,217,254,71,158,194,124,44,193,44,244,205,50,142,122,240,196,229,157,243,146,220,230,234,55,97,142,226,48,129,113,6,230,24,31,200,158,35,206,240,246,135,133,34,230,55,88,
+ 25,212,156,191,206,192,252,197,234,98,10,245,27,5,196,234,109,50,72,240,155,44,140,60,48,185,52,6,116,25,12,114,155,75,207,112,226,248,60,100,207,215,63,197,124,44,1,249,216,49,241,137,140,50,142,178,199,61,152,4,86,14,247,239,18,198,41,99,206,204,204,135,89,62,144,61,142,115,222,254,176,80,136,252,228,145,150,154,243,82,9,243,18,252,51,144,13,48,227,2,
+ 177,116,51,156,127,134,185,27,12,242,105,215,25,46,31,121,96,9,182,54,132,114,143,52,112,125,74,64,92,25,36,57,55,16,71,110,23,238,19,32,203,80,215,97,254,15,128,144,79,245,241,171,33,200,50,253,57,14,66,121,89,206,38,18,252,57,102,231,102,166,155,56,84,161,236,62,200,62,15,84,142,28,167,155,64,239,35,184,31,74,208,231,83,126,31,99,243,45,157,190,
+ 109,217,24,193,120,97,144,221,159,155,208,159,49,64,252,120,219,20,79,153,124,217,86,136,116,36,97,7,113,246,54,88,91,16,127,93,141,44,180,147,29,223,96,16,151,126,134,75,51,227,2,121,221,47,127,130,235,73,2,18,108,153,19,236,38,9,140,45,106,170,152,130,219,244,128,4,187,247,225,28,185,225,81,230,129,128,178,76,48,129,125,214,177,167,113,95,146,96,226,138,
+ 204,60,228,214,126,107,160,19,16,115,19,23,102,112,144,71,184,17,1,10,208,105,184,96,10,110,51,0,131,149,79,62,245,58,192,164,233,81,6,53,215,9,15,161,61,37,32,15,125,22,250,204,242,129,236,107,122,139,127,154,168,144,208,255,13,236,217,55,90,96,6,227,169,225,18,62,199,133,91,46,250,27,33,116,80,4,251,26,49,65,176,225,40,234,116,212,102,11,214,240,
+ 46,52,144,238,168,71,28,217,249,183,56,238,185,187,50,14,87,48,137,48,29,41,116,142,185,45,29,243,144,203,181,90,239,193,217,51,23,136,197,145,71,30,98,238,41,184,167,34,96,72,64,246,184,239,57,62,204,45,220,16,210,24,156,52,66,230,55,132,114,140,22,243,27,39,120,253,140,16,118,25,109,172,159,17,51,196,116,154,3,120,86,99,76,32,124,98,0,247,50,91,
+ 246,184,51,30,51,196,223,43,123,162,213,105,82,18,242,88,235,20,113,230,180,152,0,196,202,47,216,103,90,151,29,79,51,126,255,222,140,162,109,196,123,188,54,48,188,81,81,246,236,21,214,42,120,79,105,72,82,201,103,142,113,51,252,55,51,140,0,200,103,173,169,98,190,86,67,66,46,249,114,184,95,228,24,20,102,173,187,18,54,198,128,89,196,245,196,168,176,54,34,9,
+ 253,106,23,236,143,129,220,5,168,59,160,144,107,125,11,239,125,173,144,144,144,143,236,53,133,142,123,59,160,144,250,179,57,213,149,156,224,206,73,210,95,202,48,58,148,158,166,63,135,115,121,144,167,150,240,156,203,210,17,211,223,139,61,138,94,9,200,195,254,62,148,221,39,1,121,228,159,192,51,246,4,168,216,127,203,93,235,80,38,118,57,99,231,214,91,48,39,219,113,183,
+ 32,204,102,29,198,24,158,255,77,198,117,235,156,245,204,24,158,241,199,36,32,175,241,191,14,227,55,97,200,239,250,91,23,156,223,174,175,1,73,110,249,187,144,38,1,72,114,252,106,189,152,91,83,130,160,191,52,132,115,31,140,65,129,146,75,56,9,246,15,114,105,7,61,40,113,144,144,95,103,118,16,163,50,134,113,12,134,142,116,74,161,157,174,198,2,53,251,227,116,239,
+ 56,25,12,164,55,66,64,17,243,27,146,229,18,99,12,239,68,198,36,24,23,202,152,132,127,18,88,25,204,217,9,209,56,31,247,0,48,195,104,0,98,250,231,16,63,7,52,60,179,107,49,51,8,74,156,44,121,64,246,245,178,102,113,250,160,60,37,46,221,160,64,73,18,98,109,80,186,164,75,25,194,94,198,144,4,228,209,255,67,17,243,87,96,79,197,131,1,159,56,98,
+ 249,205,48,103,176,243,11,220,196,185,41,228,26,129,98,100,116,59,222,145,1,147,201,81,96,116,98,206,247,128,196,235,239,126,92,95,18,76,108,199,253,219,166,19,247,240,166,123,187,195,36,194,38,37,48,153,157,166,15,147,18,229,16,127,191,34,92,131,46,148,112,126,105,16,148,24,131,28,37,33,174,196,197,81,64,123,87,80,118,133,67,197,253,66,229,200,226,158,146,245,
+ 33,23,64,86,72,71,156,238,254,33,172,253,4,122,113,207,233,149,128,184,114,84,206,222,177,31,161,127,37,48,240,188,101,184,80,224,254,86,171,224,1,249,180,103,29,207,178,245,54,65,46,58,251,177,222,239,247,129,2,198,131,201,205,3,21,132,152,156,223,148,100,48,32,94,67,185,26,135,233,18,166,185,196,13,10,101,80,128,253,102,130,144,135,174,2,206,46,21,124,40,
+ 6,80,240,128,88,253,138,30,249,136,197,23,2,244,19,43,131,60,100,33,33,251,11,18,118,145,132,253,23,5,80,240,40,187,224,97,255,69,96,132,197,141,112,20,66,230,31,113,201,87,112,169,87,209,165,223,70,124,242,23,36,218,127,132,131,92,218,145,130,236,255,112,230,88,222,237,53,231,239,127,120,200,14,207,35,92,130,178,226,124,243,193,206,67,160,92,235,158,15,43,
+ 11,84,57,134,145,110,79,94,81,166,15,177,180,118,158,26,135,100,126,130,156,150,252,134,198,52,7,241,115,72,30,247,150,8,152,104,3,179,133,181,215,77,28,166,139,223,12,1,69,88,127,61,140,246,120,152,81,103,223,36,121,88,160,140,62,209,145,90,119,153,131,171,136,171,10,232,46,84,89,223,233,62,80,27,214,155,122,128,13,122,194,246,232,33,244,235,33,108,212,99,
+ 46,87,119,209,163,75,164,91,169,182,142,173,163,27,178,59,37,200,94,91,45,91,252,253,11,155,163,30,212,133,180,141,29,120,198,102,212,89,24,185,140,129,185,15,226,253,231,14,188,27,101,204,34,221,28,11,155,194,154,110,202,5,66,190,41,143,247,38,83,92,184,101,159,193,59,163,117,220,202,39,198,52,226,231,222,36,183,143,238,70,195,62,235,217,19,237,253,146,204,251,
+ 52,242,193,8,200,111,36,28,191,155,189,139,24,134,187,194,189,191,169,184,161,176,119,60,77,153,119,190,211,104,83,1,212,202,252,115,16,249,219,0,37,164,139,78,144,250,82,92,247,131,26,202,11,1,185,148,161,173,234,81,180,27,241,204,20,36,111,76,152,48,122,87,5,196,1,98,245,51,217,58,103,36,223,49,207,192,78,7,59,45,185,180,135,122,61,246,6,218,4,217,
+ 250,14,194,29,3,196,141,135,178,128,30,97,124,144,223,124,184,186,39,18,155,97,247,230,148,32,123,236,31,196,120,105,51,100,183,115,205,65,189,59,60,182,221,196,216,12,255,230,152,32,137,254,86,79,130,13,17,24,123,6,251,105,18,24,103,226,158,119,166,156,77,97,176,176,127,106,185,240,176,36,13,236,209,54,192,28,220,115,216,251,157,19,32,73,59,234,18,223,200,164,
+ 152,235,78,130,110,139,123,166,157,98,126,75,64,195,30,160,198,24,196,121,128,65,80,98,12,2,29,225,58,71,73,56,51,80,198,255,101,23,116,198,190,154,243,109,105,62,15,241,243,75,63,198,65,140,76,76,96,207,126,158,142,121,40,160,173,6,247,161,158,28,37,6,5,228,171,236,195,122,11,12,61,129,247,55,18,144,215,245,118,10,174,27,15,114,111,196,30,185,36,89,
+ 6,133,24,39,253,182,158,226,178,182,67,156,13,217,139,187,148,254,213,216,139,118,227,98,188,3,104,178,122,113,156,157,119,0,255,247,126,31,251,255,18,144,88,247,213,78,57,198,122,188,191,21,48,193,1,151,112,195,133,177,71,49,159,73,96,184,228,37,187,255,151,161,15,2,200,225,121,49,231,66,150,145,243,240,147,204,124,191,108,177,30,25,91,178,92,217,189,223,65,251,
+ 74,64,205,118,95,134,118,199,115,108,63,67,59,3,239,92,129,6,40,161,249,144,24,26,211,51,0,42,12,117,61,234,37,73,118,55,218,149,145,101,244,239,70,61,128,138,248,30,70,150,165,33,73,187,140,128,231,85,35,98,60,181,160,219,144,124,94,157,138,225,27,199,133,21,139,253,246,253,106,4,165,143,184,220,55,10,24,63,133,8,168,66,127,102,133,254,204,9,125,155,
+ 21,250,178,23,215,114,175,4,228,117,189,173,196,28,200,65,33,199,176,122,16,249,98,130,196,178,235,8,111,51,36,94,163,7,163,173,185,211,88,227,83,138,76,249,92,87,20,114,108,117,99,205,219,45,64,110,99,112,167,243,221,71,217,239,48,158,182,74,46,157,219,247,27,85,159,112,226,226,201,195,173,6,96,68,252,86,209,29,12,138,225,254,100,196,244,237,36,138,80,22,
+ 37,124,15,54,66,218,115,251,181,206,183,51,197,189,30,179,69,42,74,39,246,73,237,189,85,251,55,158,216,239,52,225,28,111,5,12,179,247,134,149,195,222,237,96,199,15,11,178,130,235,176,2,134,5,127,133,177,139,163,210,140,207,59,229,85,176,95,85,177,169,57,236,230,202,25,246,208,83,1,85,176,155,201,42,131,236,242,106,220,111,90,177,48,10,104,127,138,105,236,25,
+ 17,199,220,118,188,107,166,249,58,116,28,171,231,62,174,46,21,48,204,181,69,213,14,203,99,207,156,201,10,228,144,36,21,214,238,85,86,78,117,190,252,142,216,246,234,212,22,160,69,247,121,242,77,163,198,24,79,156,222,125,236,119,161,170,104,143,17,182,103,184,15,254,125,246,251,108,197,121,199,215,124,87,91,101,232,12,66,60,73,176,157,59,31,160,43,206,251,223,102,156,
+ 133,51,91,86,8,168,133,60,86,74,101,19,87,30,69,44,155,90,108,47,43,166,252,86,4,157,196,81,193,59,171,10,131,132,184,50,254,47,39,12,113,250,116,110,44,235,94,172,144,163,204,208,57,72,86,135,178,24,59,31,53,109,92,209,158,115,16,94,232,130,109,122,27,48,185,51,44,26,246,152,181,54,67,92,253,173,46,140,229,136,80,132,246,55,217,153,170,114,155,32,
+ 65,255,108,150,22,65,18,54,91,72,103,53,81,163,243,176,100,26,114,177,69,195,30,177,150,0,165,10,246,141,5,200,77,255,153,72,159,2,100,207,181,251,177,223,182,21,126,14,18,237,219,186,56,94,164,114,101,70,185,20,242,82,1,146,28,191,198,203,176,230,75,1,98,250,199,126,140,253,88,9,200,195,254,34,202,42,198,8,9,229,23,16,86,104,35,36,232,87,177,175,
+ 167,250,64,1,253,171,98,61,175,70,128,66,204,133,253,49,60,175,26,88,151,24,9,65,41,234,167,152,234,79,17,219,151,130,198,203,114,244,187,11,196,197,147,79,58,53,38,136,211,65,130,125,196,185,45,140,57,43,1,8,229,207,109,195,89,63,184,103,125,208,54,164,135,109,35,249,80,70,154,114,0,20,113,188,88,43,209,94,49,65,92,121,228,162,107,22,225,179,2,218,
+ 201,201,80,246,128,56,123,42,195,25,197,192,122,212,104,3,228,210,30,19,118,220,218,100,17,117,142,35,108,156,139,31,143,8,9,229,142,135,196,194,59,227,6,222,89,55,24,132,178,26,156,155,4,26,92,218,6,176,144,223,226,32,123,12,174,246,246,39,5,185,216,71,46,182,207,34,237,108,0,228,115,189,206,225,221,253,156,31,171,253,243,27,125,232,119,1,179,140,189,92,
+ 238,183,19,168,217,167,125,232,35,129,137,50,206,130,184,49,43,55,23,105,120,7,162,37,0,241,215,245,118,236,127,120,96,97,14,176,18,128,154,115,220,201,232,67,31,44,60,219,88,49,65,110,115,108,63,244,248,160,194,6,53,6,200,167,143,251,17,223,207,65,33,239,73,125,200,211,231,1,69,189,223,109,68,219,5,64,17,117,144,15,7,241,204,126,48,38,168,5,253,55,
+ 32,223,13,49,65,62,122,198,17,63,238,130,134,51,82,26,71,9,103,172,74,28,20,96,255,208,227,56,255,37,1,121,217,213,1,59,82,130,236,241,183,21,99,76,2,66,218,198,149,184,143,128,71,177,71,64,205,241,187,117,33,190,21,202,167,97,29,228,129,6,40,32,191,138,52,106,12,228,170,56,7,34,73,31,210,247,113,244,187,64,49,93,159,6,206,180,24,17,25,247,
+ 129,98,158,79,52,236,33,104,9,65,18,250,75,231,227,186,245,160,114,62,238,185,54,25,236,115,9,144,56,46,51,24,131,17,24,122,0,215,189,4,20,161,173,75,168,83,255,37,56,159,6,40,68,190,78,238,236,136,129,177,106,68,132,124,116,141,61,137,253,54,9,200,107,60,117,161,239,61,40,93,128,126,21,24,116,9,43,9,144,61,111,127,27,237,47,65,229,2,140,23,
+ 14,13,122,7,153,30,13,110,10,104,107,11,207,84,86,76,52,46,197,185,117,200,185,155,177,110,244,120,134,35,23,102,55,97,173,21,35,20,102,62,200,161,175,98,128,90,44,107,176,132,190,98,148,56,6,185,176,65,46,188,82,66,63,219,228,48,47,112,238,18,251,214,190,134,115,133,154,7,196,226,201,37,29,5,228,213,2,160,16,249,43,56,215,72,94,247,147,77,184,102,
+ 35,48,58,210,117,12,51,0,114,155,15,126,130,235,93,2,242,176,63,119,17,238,193,32,203,32,132,169,120,103,170,250,64,44,175,234,225,87,67,66,1,121,73,208,165,122,164,49,243,78,93,108,127,150,213,39,199,213,45,203,213,145,64,214,195,159,229,194,84,159,247,199,89,46,29,113,168,46,246,101,133,114,115,204,182,156,11,228,210,63,57,143,120,10,96,116,15,198,22,48,
+ 57,70,153,52,176,191,104,112,76,236,193,115,62,207,6,82,198,126,136,241,35,193,4,203,67,208,57,129,124,166,80,62,133,176,153,98,36,183,31,237,14,114,12,21,239,246,84,208,207,36,5,228,239,125,6,231,147,37,32,175,249,122,19,230,144,22,25,196,53,63,200,40,9,16,43,127,144,75,51,200,197,15,114,238,202,29,152,115,71,24,119,224,28,151,205,38,204,107,77,70,
+ 184,248,144,104,200,175,113,148,4,251,74,187,160,223,254,230,231,46,128,111,92,106,30,223,185,36,15,134,30,193,186,65,2,242,200,63,97,127,103,244,150,112,120,149,69,45,160,158,142,113,38,73,238,114,140,85,144,99,50,203,220,57,23,178,76,170,62,191,87,69,246,248,125,10,227,83,2,74,232,250,211,58,212,182,65,205,49,215,161,30,99,144,81,18,24,116,9,43,113,101,
+ 80,115,252,221,135,241,37,65,25,121,203,160,130,119,82,21,134,14,191,218,141,190,13,65,182,128,190,101,228,36,200,114,238,126,228,39,193,254,35,152,155,142,72,48,139,185,112,54,0,10,217,247,22,242,88,246,59,192,229,238,28,75,183,124,177,59,40,143,229,226,182,90,100,230,194,46,87,8,101,207,112,110,2,13,184,27,44,172,1,44,150,223,98,178,1,230,46,196,30,61,
+ 131,36,218,232,200,119,209,254,18,144,87,27,175,71,61,124,32,201,116,86,4,102,118,163,77,24,13,1,106,182,221,110,199,207,167,157,97,97,22,202,152,187,21,207,96,172,44,74,104,46,34,23,42,47,199,253,239,205,184,94,121,238,6,8,47,189,57,216,22,11,207,235,13,236,97,53,92,152,97,52,56,44,164,183,56,230,238,196,88,169,58,4,233,162,4,80,237,119,6,253,
+ 201,226,167,223,34,180,67,0,36,153,206,138,200,204,121,232,35,31,200,30,199,231,185,199,53,243,90,40,103,238,60,244,167,0,73,246,199,145,111,225,122,151,96,22,122,102,91,132,56,125,115,227,176,111,188,67,177,240,238,207,138,1,106,94,239,101,180,71,11,144,61,239,150,253,211,240,241,51,66,94,10,192,192,57,71,195,3,226,226,41,32,173,17,1,83,195,243,151,11,196,
+ 217,105,50,191,201,197,153,30,249,76,96,176,114,41,160,254,99,22,158,215,36,32,143,252,26,244,104,46,16,23,71,62,233,180,152,24,68,93,7,5,8,122,75,144,37,230,30,100,225,37,78,106,30,231,92,137,49,132,186,15,73,64,73,205,199,125,152,51,99,34,135,107,34,7,178,140,126,132,245,3,138,211,222,147,162,149,247,196,199,157,223,182,141,10,181,160,123,238,114,156,
+ 15,194,187,54,163,13,144,135,13,55,35,238,230,22,33,228,159,192,187,195,9,1,10,209,6,134,164,157,148,16,22,158,89,172,22,33,228,191,191,187,195,7,69,185,191,16,80,127,252,173,173,225,1,181,163,254,157,168,75,194,144,159,254,117,72,147,18,4,253,71,176,119,115,68,2,18,175,221,93,88,183,0,138,216,254,170,253,205,118,138,78,179,188,28,214,128,57,129,254,16,
+ 235,191,105,140,187,105,142,185,157,25,229,104,136,177,120,228,123,104,47,9,8,105,103,81,238,172,7,100,143,141,85,232,167,152,33,183,49,184,42,217,242,195,208,135,51,139,125,9,209,255,6,188,91,182,89,11,55,32,183,249,232,100,204,61,146,144,144,215,28,198,58,16,225,163,144,163,45,66,62,109,99,34,222,228,56,0,93,7,66,210,243,166,14,165,103,175,243,183,196,3,
+ 192,204,5,127,115,138,103,176,136,181,28,160,86,175,247,79,224,122,77,16,10,208,111,224,221,167,145,0,20,99,253,41,226,53,68,62,104,73,127,51,243,70,111,252,236,34,89,251,87,57,223,210,228,191,167,73,33,48,132,239,61,82,80,127,221,134,62,105,35,36,218,219,141,241,37,9,185,213,183,91,62,191,95,57,20,178,60,98,233,39,225,54,177,55,110,130,137,66,230,184,
+ 239,237,83,130,99,157,92,208,241,191,158,0,20,131,126,74,216,70,61,164,173,212,74,251,174,112,254,118,188,132,247,228,37,134,14,63,181,169,127,203,248,191,28,35,116,130,140,47,93,210,150,114,204,245,47,135,108,147,34,254,47,182,64,193,7,66,185,133,128,52,5,70,81,162,188,66,140,20,153,125,69,206,77,204,79,66,155,144,71,27,145,68,187,21,60,218,132,2,218,134,
+ 66,180,93,161,197,190,43,250,216,89,244,169,35,73,142,25,10,72,67,33,235,88,136,216,223,69,159,250,20,125,236,39,201,235,132,98,168,15,69,188,30,139,45,244,119,53,160,221,10,156,93,67,135,177,143,27,3,147,167,210,34,204,125,93,199,220,19,251,176,31,199,115,106,180,251,144,93,182,41,64,30,225,166,36,20,241,126,151,195,158,122,206,3,10,136,207,73,66,33,117,
+ 145,71,90,74,224,126,175,102,177,110,14,9,241,237,55,8,219,60,232,65,218,30,15,136,229,239,157,193,249,29,9,200,197,238,205,45,254,141,17,241,229,108,69,157,66,144,187,18,117,227,232,253,25,236,147,128,92,116,159,122,101,167,162,97,31,69,19,160,230,90,100,45,214,11,1,148,222,128,53,162,7,36,81,255,41,252,63,197,209,192,51,69,195,131,105,110,254,33,198,17,
+ 204,33,71,36,32,33,223,28,131,98,160,140,255,203,45,162,215,112,246,137,243,239,19,252,101,151,58,83,130,246,148,99,66,143,152,159,88,93,140,14,60,143,114,144,75,152,145,32,196,233,35,230,38,206,54,10,97,15,249,212,135,132,178,73,192,112,137,51,60,242,27,45,216,79,18,229,145,75,126,35,68,223,144,100,30,138,169,109,141,22,251,155,92,218,217,8,208,67,9,234,
+ 167,16,220,137,50,238,140,80,142,206,190,193,168,75,66,2,122,204,80,72,244,24,116,233,17,203,208,219,0,73,183,71,119,11,80,236,223,141,214,149,229,237,225,198,214,199,142,211,94,212,86,168,5,253,36,97,63,197,92,127,106,19,122,30,125,24,166,13,243,225,210,7,234,175,69,235,195,200,249,149,120,9,221,254,74,2,248,212,151,34,182,31,197,140,30,178,110,20,115,251,
+ 81,76,246,81,139,249,169,77,227,131,132,254,166,86,251,171,150,32,108,110,161,8,101,80,179,156,60,67,244,231,37,137,88,23,242,169,7,121,196,147,11,186,132,14,242,65,143,1,106,177,124,106,1,61,106,125,149,24,175,25,123,125,174,132,207,19,133,33,201,125,92,242,42,163,22,255,28,45,133,226,232,79,227,247,24,181,132,127,31,134,66,180,131,22,241,247,99,180,151,248,
+ 111,215,104,105,219,169,56,191,63,224,121,125,40,201,98,255,77,191,25,1,138,168,63,147,239,78,5,98,250,235,61,148,42,197,101,233,34,254,6,104,61,38,57,21,64,157,251,173,181,130,15,197,0,10,17,243,235,236,62,88,180,101,205,249,109,208,249,61,14,133,133,177,240,2,243,23,25,5,70,145,15,171,113,249,240,251,36,58,123,103,90,116,65,231,169,177,125,21,46,174,
+ 200,108,43,50,127,221,110,179,67,221,139,242,23,184,242,234,107,28,105,135,105,2,5,78,87,65,200,87,117,161,200,165,43,112,249,171,92,57,83,107,28,121,220,222,124,173,219,65,241,136,19,220,43,213,116,177,219,109,202,229,55,113,167,2,198,120,221,227,55,116,167,2,174,129,105,70,189,201,50,132,131,122,74,178,184,28,253,11,138,33,165,6,74,23,118,41,23,115,84,88,
+ 88,73,130,10,210,14,180,160,183,24,81,14,48,74,204,142,17,140,193,17,197,233,11,190,239,234,33,231,185,122,76,114,58,196,220,89,23,198,227,148,132,28,96,245,29,113,169,179,21,115,253,234,30,215,73,221,231,250,169,7,200,10,214,76,245,67,237,193,254,109,175,10,244,237,201,43,243,191,61,181,11,20,113,222,181,152,34,133,132,57,24,227,247,85,15,182,64,253,204,116,
+ 89,137,51,11,43,83,164,136,235,178,216,6,10,9,81,124,137,219,95,144,168,79,181,77,186,171,41,183,115,245,247,164,94,213,4,251,168,126,130,48,149,22,61,237,163,2,125,211,98,248,26,119,92,159,7,151,47,172,191,227,98,0,229,14,128,105,143,240,1,142,41,15,91,167,124,24,88,238,31,159,36,211,46,20,2,158,183,47,10,160,16,49,255,84,27,199,219,148,27,107,
+ 218,135,189,70,159,134,156,230,40,224,153,173,144,34,35,88,3,79,97,109,60,213,6,134,177,254,174,178,245,119,181,230,248,163,174,111,45,156,17,183,34,112,67,140,223,255,191,161,5,166,176,70,157,74,145,110,172,81,187,83,196,58,37,25,26,111,196,25,98,142,105,151,176,6,48,48,31,27,192,196,222,129,105,239,99,228,157,223,138,29,251,110,167,20,19,200,123,128,149,97,
+ 180,194,186,5,14,112,110,163,93,108,76,150,3,30,76,236,205,204,51,45,204,135,211,109,96,128,187,135,79,167,160,127,154,179,227,178,195,138,178,23,12,99,46,220,131,121,113,216,67,86,216,184,172,52,169,117,204,239,253,84,154,126,69,112,11,236,62,156,153,167,194,151,161,144,178,127,222,173,28,251,125,115,29,12,20,59,20,13,242,82,188,35,27,14,176,107,152,73,157,229,
+ 29,14,72,183,59,223,113,204,189,135,229,187,20,122,46,13,192,20,222,83,101,139,14,246,111,179,15,136,228,187,142,217,115,41,87,198,128,93,175,188,243,219,239,205,120,227,38,14,251,111,204,111,114,145,110,172,14,136,115,137,175,64,247,128,210,163,28,184,184,99,30,3,97,234,221,241,211,243,230,14,249,244,39,37,75,143,7,253,151,102,230,49,112,15,50,66,112,0,76,236,
+ 199,220,241,201,142,121,105,132,204,111,8,140,224,26,24,73,136,1,9,140,245,106,36,14,248,196,77,114,24,96,20,152,76,26,140,61,236,218,31,62,54,207,40,243,227,212,224,199,241,106,111,236,241,108,120,93,7,55,181,143,129,230,247,81,46,116,238,227,54,163,192,136,114,111,94,222,58,77,253,38,179,97,148,249,71,57,251,76,15,70,5,76,206,109,216,101,231,133,247,246,
+ 92,94,35,192,46,21,247,124,181,77,228,118,117,205,163,194,157,133,204,2,107,11,41,71,239,202,28,99,206,102,11,57,242,46,219,221,177,224,190,162,233,119,1,225,71,231,243,117,204,203,163,140,198,21,88,215,129,163,92,216,81,14,35,226,245,102,196,200,40,119,77,154,28,163,46,97,166,75,220,168,224,55,192,216,163,88,151,74,48,177,27,115,168,205,122,204,167,28,134,164,
+ 173,134,128,41,196,141,114,24,46,97,163,30,105,12,151,58,27,45,96,186,216,60,234,129,201,217,55,38,217,126,163,66,254,73,134,90,92,150,46,7,83,166,158,46,6,246,116,140,20,185,245,2,172,73,240,187,76,198,9,196,36,152,136,171,188,83,210,69,205,225,26,75,145,28,35,203,185,115,46,100,25,70,26,207,244,235,22,99,226,190,107,2,67,8,55,146,224,116,212,57,
+ 69,180,53,233,98,110,71,91,115,140,2,163,19,237,210,38,76,166,211,116,97,148,147,163,140,156,238,144,5,234,102,92,95,41,144,101,54,228,56,178,140,28,231,207,113,168,44,111,239,209,78,41,84,15,221,26,165,139,218,155,46,214,25,233,178,232,155,131,171,122,218,254,141,195,221,246,179,238,106,143,125,31,81,222,148,48,237,210,115,211,2,42,246,197,212,52,185,62,93,194,
+ 252,189,128,150,0,106,2,251,127,106,8,140,136,251,103,70,68,172,54,124,207,219,242,65,179,239,1,155,113,126,20,247,20,13,82,99,148,4,191,230,17,166,69,196,232,79,151,193,125,93,243,104,120,23,170,165,192,32,211,63,40,137,41,177,127,102,74,96,120,236,143,77,112,24,182,252,16,158,231,184,61,189,161,239,119,74,161,173,70,253,18,164,116,49,206,59,251,80,177,223,
+ 235,92,156,81,84,251,153,233,148,214,201,161,172,28,35,11,84,232,86,83,34,199,108,200,114,54,229,56,219,178,46,126,21,103,28,212,20,209,206,192,115,65,138,148,56,89,226,252,154,139,95,115,137,83,177,39,168,166,73,29,125,184,18,123,59,237,162,222,94,125,113,124,67,200,0,119,180,136,145,18,123,216,59,39,203,126,6,233,77,17,236,105,89,105,146,77,23,109,107,186,
+ 68,57,187,101,197,128,138,61,91,53,77,58,210,197,192,53,96,164,136,245,178,228,56,202,152,245,193,178,207,160,173,77,15,213,30,131,167,182,151,62,70,191,237,223,208,126,250,56,140,62,127,38,3,226,141,136,104,246,51,96,103,122,88,56,11,103,165,136,134,53,190,150,34,150,125,15,216,154,30,218,105,237,167,204,49,120,62,158,215,192,32,208,50,136,111,51,37,166,191,228,
+ 65,238,18,135,44,80,113,102,81,109,51,89,166,59,203,236,200,113,100,133,240,126,164,239,7,157,182,188,4,231,203,64,233,2,212,131,161,97,189,165,181,153,220,8,108,3,89,160,110,66,157,82,36,203,236,200,50,155,114,1,100,61,236,238,31,65,219,218,108,114,220,98,190,126,132,103,153,187,247,39,120,255,34,129,106,151,117,7,246,43,88,153,253,12,3,126,35,37,76,216,
+ 111,216,247,168,13,233,50,186,7,239,234,24,102,0,42,246,84,213,20,201,238,199,152,97,228,60,200,114,50,43,164,215,208,238,90,154,172,75,151,65,188,167,31,100,148,2,24,228,210,13,50,169,158,142,126,72,147,238,116,209,216,239,208,151,36,208,144,86,75,144,146,135,222,65,166,187,36,132,87,238,206,28,247,123,247,86,74,52,170,56,71,25,146,25,206,61,87,197,217,77,
+ 134,133,242,230,238,132,27,168,62,191,123,175,182,1,3,107,62,35,69,44,140,81,43,85,148,5,10,28,98,186,130,24,214,76,231,145,62,105,155,11,238,122,189,126,139,216,104,59,138,98,236,4,17,203,177,82,166,177,19,215,175,7,115,59,113,13,55,89,133,107,26,28,21,48,66,252,30,171,145,0,106,194,191,87,170,6,96,36,244,123,165,134,44,55,225,222,211,230,51,52,
+ 154,15,106,155,127,143,83,21,48,236,57,162,59,61,90,249,189,35,53,78,182,166,139,248,91,67,90,155,49,183,225,185,8,108,87,22,220,237,228,200,211,157,82,204,110,193,126,180,4,71,175,200,28,251,251,150,6,39,31,219,162,40,63,188,66,57,38,103,16,54,3,74,56,179,80,74,17,83,252,91,165,188,28,205,252,149,26,206,232,49,118,49,134,37,255,38,117,24,82,71,
+ 191,235,2,131,40,127,80,208,167,187,196,15,10,108,15,192,12,64,103,229,155,18,105,244,152,49,19,210,99,70,248,45,61,51,102,38,241,62,99,146,195,140,169,156,73,151,114,39,3,210,76,50,70,35,252,182,252,104,12,12,197,244,187,141,67,45,242,240,161,238,112,172,161,197,28,106,49,31,227,210,21,233,83,22,208,125,184,25,247,204,155,83,164,220,252,222,107,45,29,100,
+ 127,179,168,156,4,10,250,0,223,98,208,109,89,235,96,208,162,249,110,56,196,189,103,15,39,203,73,181,151,178,216,190,32,202,168,79,89,2,61,33,202,129,253,79,210,54,150,19,178,187,28,129,113,172,57,199,83,100,22,123,176,179,49,115,52,128,39,62,222,121,140,178,61,198,148,20,105,105,206,33,142,238,72,236,199,185,219,253,94,40,29,139,252,219,149,46,229,58,172,197,
+ 174,59,188,252,216,26,114,55,194,119,35,221,110,91,230,157,111,182,236,70,185,21,27,101,241,247,98,108,247,101,130,191,194,165,173,216,28,230,64,121,111,58,220,173,92,1,182,187,172,95,247,67,111,165,214,233,143,178,96,195,188,158,60,242,216,40,88,155,51,42,28,85,165,147,251,182,68,199,162,244,21,31,170,76,238,106,134,229,133,111,220,52,81,22,194,230,219,52,31,188,
+ 78,79,146,76,74,191,127,144,97,116,229,213,84,169,216,227,226,240,194,184,60,54,86,14,47,192,135,93,214,116,231,133,126,117,233,231,221,243,82,153,247,239,182,251,93,225,198,34,220,21,137,251,223,176,248,221,34,197,35,109,208,189,181,198,16,194,171,12,221,135,225,32,221,74,235,52,245,87,57,134,153,190,170,71,124,53,1,134,37,215,69,213,20,116,14,135,148,213,20,219,
+ 173,218,70,221,85,143,177,162,115,99,75,119,113,235,30,227,80,143,145,170,168,191,230,92,159,77,169,39,68,53,44,181,238,5,89,99,110,62,174,198,133,241,110,69,200,139,223,27,169,218,40,46,233,196,176,26,43,183,137,226,82,94,83,214,92,224,211,40,46,122,196,180,138,79,157,130,168,121,212,57,40,157,18,162,124,62,111,205,167,109,252,252,74,68,226,44,191,38,217,102,
+ 178,241,74,140,117,140,171,44,37,6,14,71,47,35,169,121,68,247,160,154,130,78,157,211,93,141,25,253,4,169,131,222,42,53,129,208,249,187,23,214,142,53,230,118,67,9,81,14,243,87,221,242,213,66,166,19,210,234,110,241,53,9,251,100,237,175,133,40,79,34,93,53,0,157,215,87,19,244,207,187,201,61,141,226,204,103,209,234,76,137,141,231,106,8,244,128,178,244,192,122,
+ 116,75,247,223,62,172,81,246,121,81,243,137,11,162,22,16,198,220,186,120,189,214,146,99,164,182,252,56,54,227,204,197,230,20,209,35,142,55,61,42,53,151,177,210,78,148,54,216,35,234,80,18,108,15,175,114,20,89,253,2,243,97,221,199,151,173,120,216,125,92,56,45,158,51,107,112,47,10,167,197,191,87,173,180,1,94,183,34,198,177,58,132,237,55,37,110,27,61,218,179,
+ 221,237,83,147,161,217,183,62,125,220,12,175,137,233,197,48,209,205,245,147,239,245,74,62,215,5,121,216,76,194,248,235,246,73,79,238,99,166,198,16,127,127,93,17,210,37,65,13,182,242,254,195,216,19,20,152,204,224,28,65,138,232,7,83,166,217,63,181,116,216,12,27,54,167,132,30,131,253,147,56,15,58,153,18,122,202,250,39,153,13,186,95,255,222,157,46,126,103,65,244,
+ 54,48,41,121,142,105,50,33,116,123,14,172,181,0,63,135,214,98,96,209,188,76,194,125,166,59,2,116,60,121,110,189,20,55,10,35,201,121,81,241,209,225,21,158,231,238,173,110,237,144,119,137,143,147,188,139,110,89,106,49,234,143,131,90,192,88,18,211,230,37,235,210,140,207,71,168,115,179,255,21,137,241,224,151,87,137,56,54,221,168,69,196,175,108,37,65,187,101,168,37,
+ 132,221,79,121,143,49,22,87,249,53,73,157,113,160,180,185,29,91,209,147,247,185,214,69,148,136,227,198,139,195,220,115,8,206,235,232,77,20,159,122,136,246,214,90,28,15,110,117,175,181,216,159,34,121,143,251,143,23,249,22,239,13,126,113,97,202,23,245,200,232,142,10,95,190,232,22,210,138,255,254,63,0,0,0,255,255,0,0,0,255,255,99,102,0,0};
+ }
+}
diff --git a/src/Avalonia.Base/Point.cs b/src/Avalonia.Base/Point.cs
index fbdf0db800..0c789ff20f 100644
--- a/src/Avalonia.Base/Point.cs
+++ b/src/Avalonia.Base/Point.cs
@@ -188,7 +188,7 @@ namespace Avalonia
}
///
- /// Returns a boolean indicating whether the point is equal to the other given point.
+ /// Returns a boolean indicating whether the point is equal to the other given point (bitwise).
///
/// The other point to test equality against.
/// True if this point is equal to other; False otherwise.
@@ -200,6 +200,18 @@ namespace Avalonia
// ReSharper enable CompareOfFloatsByEqualityOperator
}
+ ///
+ /// Returns a boolean indicating whether the point is equal to the other given point
+ /// (numerically).
+ ///
+ /// The other point to test equality against.
+ /// True if this point is equal to other; False otherwise.
+ public bool NearlyEquals(Point other)
+ {
+ return MathUtilities.AreClose(_x, other._x) &&
+ MathUtilities.AreClose(_y, other._y);
+ }
+
///
/// Checks for equality between a point and an object.
///
diff --git a/src/Avalonia.Base/PropertyStore/PriorityValue.cs b/src/Avalonia.Base/PropertyStore/PriorityValue.cs
index 112cf6619f..182b2638c4 100644
--- a/src/Avalonia.Base/PropertyStore/PriorityValue.cs
+++ b/src/Avalonia.Base/PropertyStore/PriorityValue.cs
@@ -121,6 +121,7 @@ namespace Avalonia.PropertyStore
public void ClearLocalValue()
{
+ _localValue = default;
UpdateEffectiveValue(new AvaloniaPropertyChangedEventArgs(
_owner,
Property,
diff --git a/src/Avalonia.Base/Styling/PropertySetterInstance.cs b/src/Avalonia.Base/Styling/PropertySetterInstance.cs
index c4e8f47e67..9028224cc1 100644
--- a/src/Avalonia.Base/Styling/PropertySetterInstance.cs
+++ b/src/Avalonia.Base/Styling/PropertySetterInstance.cs
@@ -18,7 +18,7 @@ namespace Avalonia.Styling
private readonly DirectPropertyBase? _directProperty;
private readonly T _value;
private IDisposable? _subscription;
- private bool _isActive;
+ private State _state;
public PropertySetterInstance(
IStyleable target,
@@ -40,6 +40,8 @@ namespace Avalonia.Styling
_value = value;
}
+ private bool IsActive => _state == State.Active;
+
public void Start(bool hasActivator)
{
if (hasActivator)
@@ -70,31 +72,35 @@ namespace Avalonia.Styling
public void Activate()
{
- if (!_isActive)
+ if (!IsActive)
{
- _isActive = true;
+ _state = State.Active;
PublishNext();
}
}
public void Deactivate()
{
- if (_isActive)
+ if (IsActive)
{
- _isActive = false;
+ _state = State.Inactive;
PublishNext();
}
}
public override void Dispose()
{
+ if (_state == State.Disposed)
+ return;
+ _state = State.Disposed;
+
if (_subscription is object)
{
var sub = _subscription;
_subscription = null;
sub.Dispose();
}
- else if (_isActive)
+ else if (IsActive)
{
if (_styledProperty is object)
{
@@ -114,7 +120,14 @@ namespace Avalonia.Styling
private void PublishNext()
{
- PublishNext(_isActive ? new BindingValue(_value) : default);
+ PublishNext(IsActive ? new BindingValue(_value) : default);
+ }
+
+ private enum State
+ {
+ Inactive,
+ Active,
+ Disposed,
}
}
}
diff --git a/src/Avalonia.Base/Visual.cs b/src/Avalonia.Base/Visual.cs
index 4fd21f02f9..bdf8723b81 100644
--- a/src/Avalonia.Base/Visual.cs
+++ b/src/Avalonia.Base/Visual.cs
@@ -376,7 +376,9 @@ namespace Avalonia
if (e.OldValue is IAffectsRender oldValue)
{
if (sender._affectsRenderWeakSubscriber != null)
+ {
InvalidatedWeakEvent.Unsubscribe(oldValue, sender._affectsRenderWeakSubscriber);
+ }
}
if (e.NewValue is IAffectsRender newValue)
diff --git a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
index fa437de186..b39ceab1b6 100644
--- a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
+++ b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
@@ -49,9 +49,9 @@ namespace Avalonia.Build.Tasks
string projectDirectory,
string output, bool verifyIl, MessageImportance logImportance, string strongNameKey, bool patchCom, bool skipXamlCompilation, bool debuggerLaunch)
{
- var typeSystem = new CecilTypeSystem(references
- .Where(r => !r.ToLowerInvariant().EndsWith("avalonia.build.tasks.dll"))
- .Concat(new[] { input }), input);
+ var typeSystem = new CecilTypeSystem(
+ references.Where(r => !r.ToLowerInvariant().EndsWith("avalonia.build.tasks.dll")),
+ input);
var asm = typeSystem.TargetAssemblyDefinition;
diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs
index 245592207e..7e6b70a146 100644
--- a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs
+++ b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs
@@ -128,8 +128,8 @@ namespace Avalonia.Controls.Primitives
if (_inputTarget != null)
{
- _inputTarget.PointerEnter += InputTarget_PointerEnter;
- _inputTarget.PointerLeave += InputTarget_PointerLeave;
+ _inputTarget.PointerEntered += InputTarget_PointerEntered;
+ _inputTarget.PointerExited += InputTarget_PointerExited;
_inputTarget.PointerPressed += InputTarget_PointerPressed;
_inputTarget.PointerMoved += InputTarget_PointerMoved;
_inputTarget.PointerReleased += InputTarget_PointerReleased;
@@ -194,8 +194,8 @@ namespace Avalonia.Controls.Primitives
if (_inputTarget != null)
{
- _inputTarget.PointerEnter -= InputTarget_PointerEnter;
- _inputTarget.PointerLeave -= InputTarget_PointerLeave;
+ _inputTarget.PointerEntered -= InputTarget_PointerEntered;
+ _inputTarget.PointerExited -= InputTarget_PointerExited;
_inputTarget.PointerPressed -= InputTarget_PointerPressed;
_inputTarget.PointerMoved -= InputTarget_PointerMoved;
_inputTarget.PointerReleased -= InputTarget_PointerReleased;
@@ -362,7 +362,7 @@ namespace Avalonia.Controls.Primitives
}
///
- protected override void OnPointerLeave(PointerEventArgs e)
+ protected override void OnPointerExited(PointerEventArgs e)
{
// We only want to bother with the color name tool tip if we can provide color names.
if (_selectionEllipsePanel != null &&
@@ -373,7 +373,7 @@ namespace Avalonia.Controls.Primitives
UpdatePseudoClasses();
- base.OnPointerLeave(e);
+ base.OnPointerExited(e);
}
///
@@ -848,16 +848,16 @@ namespace Avalonia.Controls.Primitives
UpdatePseudoClasses();
}
- ///
- private void InputTarget_PointerEnter(object? sender, PointerEventArgs args)
+ ///
+ private void InputTarget_PointerEntered(object? sender, PointerEventArgs args)
{
_isPointerOver = true;
UpdatePseudoClasses();
args.Handled = true;
}
- ///
- private void InputTarget_PointerLeave(object? sender, PointerEventArgs args)
+ ///
+ private void InputTarget_PointerExited(object? sender, PointerEventArgs args)
{
_isPointerOver = false;
UpdatePseudoClasses();
diff --git a/src/Avalonia.Controls.DataGrid/DataGridCell.cs b/src/Avalonia.Controls.DataGrid/DataGridCell.cs
index 67183781d3..2a51f45896 100644
--- a/src/Avalonia.Controls.DataGrid/DataGridCell.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGridCell.cs
@@ -139,18 +139,18 @@ namespace Avalonia.Controls
}
}
- protected override void OnPointerEnter(PointerEventArgs e)
+ protected override void OnPointerEntered(PointerEventArgs e)
{
- base.OnPointerEnter(e);
+ base.OnPointerEntered(e);
if (OwningRow != null)
{
IsMouseOver = true;
}
}
- protected override void OnPointerLeave(PointerEventArgs e)
+ protected override void OnPointerExited(PointerEventArgs e)
{
- base.OnPointerLeave(e);
+ base.OnPointerExited(e);
if (OwningRow != null)
{
diff --git a/src/Avalonia.Controls.DataGrid/DataGridColumn.cs b/src/Avalonia.Controls.DataGrid/DataGridColumn.cs
index f3ea48ff80..c415f477d4 100644
--- a/src/Avalonia.Controls.DataGrid/DataGridColumn.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGridColumn.cs
@@ -855,7 +855,7 @@ namespace Avalonia.Controls
if (OwningGrid != null && OwningGrid.UseLayoutRounding)
{
var scale = LayoutHelper.GetLayoutScale(HeaderCell);
- var roundSize = LayoutHelper.RoundLayoutSize(new Size(leftEdge + ActualWidth, 1), scale, scale);
+ var roundSize = LayoutHelper.RoundLayoutSizeUp(new Size(leftEdge + ActualWidth, 1), scale, scale);
LayoutRoundedWidth = roundSize.Width - leftEdge;
}
else
diff --git a/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs b/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs
index 915b36687c..d3bd968d62 100644
--- a/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs
@@ -83,9 +83,9 @@ namespace Avalonia.Controls
{
PointerPressed += DataGridColumnHeader_PointerPressed;
PointerReleased += DataGridColumnHeader_PointerReleased;
- PointerMoved += DataGridColumnHeader_PointerMove;
- PointerEnter += DataGridColumnHeader_PointerEnter;
- PointerLeave += DataGridColumnHeader_PointerLeave;
+ PointerMoved += DataGridColumnHeader_PointerMoved;
+ PointerEntered += DataGridColumnHeader_PointerEntered;
+ PointerExited += DataGridColumnHeader_PointerExited;
}
private void OnAreSeparatorsVisibleChanged(AvaloniaPropertyChangedEventArgs e)
@@ -452,7 +452,7 @@ namespace Avalonia.Controls
SetDragCursor(mousePosition);
}
- private void DataGridColumnHeader_PointerEnter(object sender, PointerEventArgs e)
+ private void DataGridColumnHeader_PointerEntered(object sender, PointerEventArgs e)
{
if (!IsEnabled)
{
@@ -464,7 +464,7 @@ namespace Avalonia.Controls
UpdatePseudoClasses();
}
- private void DataGridColumnHeader_PointerLeave(object sender, PointerEventArgs e)
+ private void DataGridColumnHeader_PointerExited(object sender, PointerEventArgs e)
{
if (!IsEnabled)
{
@@ -506,7 +506,7 @@ namespace Avalonia.Controls
UpdatePseudoClasses();
}
- private void DataGridColumnHeader_PointerMove(object sender, PointerEventArgs e)
+ private void DataGridColumnHeader_PointerMoved(object sender, PointerEventArgs e)
{
if (OwningGrid == null || !IsEnabled)
{
diff --git a/src/Avalonia.Controls.DataGrid/DataGridRow.cs b/src/Avalonia.Controls.DataGrid/DataGridRow.cs
index b062a14e39..1559763a1b 100644
--- a/src/Avalonia.Controls.DataGrid/DataGridRow.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGridRow.cs
@@ -607,15 +607,15 @@ namespace Avalonia.Controls
}
}
- protected override void OnPointerEnter(PointerEventArgs e)
+ protected override void OnPointerEntered(PointerEventArgs e)
{
- base.OnPointerEnter(e);
+ base.OnPointerEntered(e);
IsMouseOver = true;
}
- protected override void OnPointerLeave(PointerEventArgs e)
+ protected override void OnPointerExited(PointerEventArgs e)
{
IsMouseOver = false;
- base.OnPointerLeave(e);
+ base.OnPointerExited(e);
}
internal void ApplyCellsState()
diff --git a/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs b/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs
index a3dfa44fc9..9f37e8a6aa 100644
--- a/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs
@@ -375,7 +375,7 @@ namespace Avalonia.Controls
ApplyHeaderStatus();
}
- protected override void OnPointerEnter(PointerEventArgs e)
+ protected override void OnPointerEntered(PointerEventArgs e)
{
if (IsEnabled)
{
@@ -383,10 +383,10 @@ namespace Avalonia.Controls
UpdatePseudoClasses();
}
- base.OnPointerEnter(e);
+ base.OnPointerEntered(e);
}
- protected override void OnPointerLeave(PointerEventArgs e)
+ protected override void OnPointerExited(PointerEventArgs e)
{
if (IsEnabled)
{
@@ -394,7 +394,7 @@ namespace Avalonia.Controls
UpdatePseudoClasses();
}
- base.OnPointerLeave(e);
+ base.OnPointerExited(e);
}
private void SetIsCheckedNoCallBack(bool value)
diff --git a/src/Avalonia.Controls.DataGrid/DataGridRowHeader.cs b/src/Avalonia.Controls.DataGrid/DataGridRowHeader.cs
index 03299bbf35..fe3ba0abf6 100644
--- a/src/Avalonia.Controls.DataGrid/DataGridRowHeader.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGridRowHeader.cs
@@ -158,23 +158,23 @@ namespace Avalonia.Controls.Primitives
}
}
- protected override void OnPointerEnter(PointerEventArgs e)
+ protected override void OnPointerEntered(PointerEventArgs e)
{
if (OwningRow != null)
{
OwningRow.IsMouseOver = true;
}
- base.OnPointerEnter(e);
+ base.OnPointerEntered(e);
}
- protected override void OnPointerLeave(PointerEventArgs e)
+ protected override void OnPointerExited(PointerEventArgs e)
{
if (OwningRow != null)
{
OwningRow.IsMouseOver = false;
}
- base.OnPointerLeave(e);
+ base.OnPointerExited(e);
}
//TODO TabStop
diff --git a/src/Avalonia.Controls/Calendar/CalendarItem.cs b/src/Avalonia.Controls/Calendar/CalendarItem.cs
index bcac6324ba..4c958c83b7 100644
--- a/src/Avalonia.Controls/Calendar/CalendarItem.cs
+++ b/src/Avalonia.Controls/Calendar/CalendarItem.cs
@@ -189,7 +189,7 @@ namespace Avalonia.Controls.Primitives
EventHandler cellMouseLeftButtonDown = Cell_MouseLeftButtonDown;
EventHandler cellMouseLeftButtonUp = Cell_MouseLeftButtonUp;
- EventHandler cellMouseEnter = Cell_MouseEnter;
+ EventHandler cellMouseEntered = Cell_MouseEntered;
EventHandler cellClick = Cell_Click;
for (int i = 1; i < Calendar.RowsPerMonth; i++)
@@ -206,7 +206,7 @@ namespace Avalonia.Controls.Primitives
cell.SetValue(Grid.ColumnProperty, j);
cell.CalendarDayButtonMouseDown += cellMouseLeftButtonDown;
cell.CalendarDayButtonMouseUp += cellMouseLeftButtonUp;
- cell.PointerEnter += cellMouseEnter;
+ cell.PointerEntered += cellMouseEntered;
cell.Click += cellClick;
children.Add(cell);
}
@@ -222,7 +222,7 @@ namespace Avalonia.Controls.Primitives
EventHandler monthCalendarButtonMouseDown = Month_CalendarButtonMouseDown;
EventHandler monthCalendarButtonMouseUp = Month_CalendarButtonMouseUp;
- EventHandler monthMouseEnter = Month_MouseEnter;
+ EventHandler monthMouseEntered = Month_MouseEntered;
for (int i = 0; i < Calendar.RowsPerYear; i++)
{
@@ -238,7 +238,7 @@ namespace Avalonia.Controls.Primitives
month.SetValue(Grid.ColumnProperty, j);
month.CalendarLeftMouseButtonDown += monthCalendarButtonMouseDown;
month.CalendarLeftMouseButtonUp += monthCalendarButtonMouseUp;
- month.PointerEnter += monthMouseEnter;
+ month.PointerEntered += monthMouseEntered;
children.Add(month);
}
}
@@ -876,7 +876,7 @@ namespace Avalonia.Controls.Primitives
}
}
- internal void Cell_MouseEnter(object? sender, PointerEventArgs e)
+ internal void Cell_MouseEntered(object? sender, PointerEventArgs e)
{
if (Owner != null)
{
@@ -1162,7 +1162,7 @@ namespace Avalonia.Controls.Primitives
}
}
- private void Month_MouseEnter(object? sender, PointerEventArgs e)
+ private void Month_MouseEntered(object? sender, PointerEventArgs e)
{
if (_isMouseLeftButtonDownYearView)
{
diff --git a/src/Avalonia.Controls/ComboBox.cs b/src/Avalonia.Controls/ComboBox.cs
index cbf9b35a05..05be5ad00d 100644
--- a/src/Avalonia.Controls/ComboBox.cs
+++ b/src/Avalonia.Controls/ComboBox.cs
@@ -181,26 +181,13 @@ namespace Avalonia.Controls
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
- this.UpdateSelectionBoxItem(SelectedItem);
+ UpdateSelectionBoxItem(SelectedItem);
}
- // Because the SelectedItem isn't connected to the visual tree
public override void InvalidateMirrorTransform()
{
base.InvalidateMirrorTransform();
-
- if (SelectedItem is Control selectedControl)
- {
- selectedControl.InvalidateMirrorTransform();
-
- foreach (var visual in selectedControl.GetVisualDescendants())
- {
- if (visual is Control childControl)
- {
- childControl.InvalidateMirrorTransform();
- }
- }
- }
+ UpdateFlowDirection();
}
///
@@ -365,6 +352,8 @@ namespace Avalonia.Controls
{
parent.GetObservable(IsVisibleProperty).Subscribe(IsVisibleChanged).DisposeWith(_subscriptionsOnOpen);
}
+
+ UpdateFlowDirection();
}
private void IsVisibleChanged(bool isVisible)
@@ -432,6 +421,8 @@ namespace Avalonia.Controls
}
};
}
+
+ UpdateFlowDirection();
}
else
{
@@ -439,6 +430,19 @@ namespace Avalonia.Controls
}
}
+ private void UpdateFlowDirection()
+ {
+ if (SelectionBoxItem is Rectangle rectangle)
+ {
+ if ((rectangle.Fill as VisualBrush)?.Visual is Control content)
+ {
+ var flowDirection = (((IVisual)content!).VisualParent as Control)?.FlowDirection ??
+ FlowDirection.LeftToRight;
+ rectangle.FlowDirection = flowDirection;
+ }
+ }
+ }
+
private void SelectFocusedItem()
{
foreach (ItemContainerInfo dropdownItem in ItemContainerGenerator.Containers)
diff --git a/src/Avalonia.Controls/Control.cs b/src/Avalonia.Controls/Control.cs
index d6a5fa0727..083182a370 100644
--- a/src/Avalonia.Controls/Control.cs
+++ b/src/Avalonia.Controls/Control.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.ComponentModel;
using Avalonia.Automation.Peers;
using Avalonia.Controls.Documents;
@@ -10,6 +11,7 @@ using Avalonia.Interactivity;
using Avalonia.Media;
using Avalonia.Rendering;
using Avalonia.Styling;
+using Avalonia.Threading;
using Avalonia.VisualTree;
namespace Avalonia.Controls
@@ -53,21 +55,57 @@ namespace Avalonia.Controls
/// Event raised when an element wishes to be scrolled into view.
///
public static readonly RoutedEvent RequestBringIntoViewEvent =
- RoutedEvent.Register("RequestBringIntoView", RoutingStrategies.Bubble);
+ RoutedEvent.Register(
+ "RequestBringIntoView",
+ RoutingStrategies.Bubble);
///
/// Provides event data for the event.
///
public static readonly RoutedEvent ContextRequestedEvent =
- RoutedEvent.Register(nameof(ContextRequested),
+ RoutedEvent.Register(
+ nameof(ContextRequested),
RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
+ ///
+ /// Defines the event.
+ ///
+ public static readonly RoutedEvent LoadedEvent =
+ RoutedEvent.Register(
+ nameof(Loaded),
+ RoutingStrategies.Direct);
+
+ ///
+ /// Defines the event.
+ ///
+ public static readonly RoutedEvent UnloadedEvent =
+ RoutedEvent.Register(
+ nameof(Unloaded),
+ RoutingStrategies.Direct);
+
///
/// Defines the property.
///
public static readonly AttachedProperty FlowDirectionProperty =
- AvaloniaProperty.RegisterAttached(nameof(FlowDirection), inherits: true);
-
+ AvaloniaProperty.RegisterAttached(
+ nameof(FlowDirection),
+ inherits: true);
+
+ // Note the following:
+ // _loadedQueue :
+ // Is the queue where any control will be added to indicate that its loaded
+ // event should be scheduled and called later.
+ // _loadedProcessingQueue :
+ // Contains a copied snapshot of the _loadedQueue at the time when processing
+ // starts and individual events are being fired. This was needed to avoid
+ // exceptions if new controls were added in the Loaded event itself.
+
+ private static bool _isLoadedProcessing = false;
+ private static readonly HashSet _loadedQueue = new HashSet();
+ private static readonly HashSet _loadedProcessingQueue = new HashSet();
+
+ private bool _isAttachedToVisualTree = false;
+ private bool _isLoaded = false;
private DataTemplates? _dataTemplates;
private IControl? _focusAdorner;
private AutomationPeer? _automationPeer;
@@ -108,6 +146,15 @@ namespace Avalonia.Controls
set => SetValue(ContextFlyoutProperty, value);
}
+ ///
+ /// Gets a value indicating whether the control is fully constructed in the visual tree
+ /// and both layout and render are complete.
+ ///
+ ///
+ /// This is set to true while raising the event.
+ ///
+ public bool IsLoaded => _isLoaded;
+
///
/// Gets or sets a user-defined object attached to the control.
///
@@ -135,6 +182,35 @@ namespace Avalonia.Controls
remove => RemoveHandler(ContextRequestedEvent, value);
}
+ ///
+ /// Occurs when the control has been fully constructed in the visual tree and both
+ /// layout and render are complete.
+ ///
+ ///
+ /// This event is guaranteed to occur after the control template is applied and references
+ /// to objects created after the template is applied are available. This makes it different
+ /// from OnAttachedToVisualTree which doesn't have these references. This event occurs at the
+ /// latest possible time in the control creation life-cycle.
+ ///
+ public event EventHandler? Loaded
+ {
+ add => AddHandler(LoadedEvent, value);
+ remove => RemoveHandler(LoadedEvent, value);
+ }
+
+ ///
+ /// Occurs when the control is removed from the visual tree.
+ ///
+ ///
+ /// This is API symmetrical with and exists for compatibility with other
+ /// XAML frameworks; however, it behaves the same as OnDetachedFromVisualTree.
+ ///
+ public event EventHandler? Unloaded
+ {
+ add => AddHandler(UnloadedEvent, value);
+ remove => RemoveHandler(UnloadedEvent, value);
+ }
+
public new IControl? Parent => (IControl?)base.Parent;
///
@@ -215,18 +291,124 @@ namespace Avalonia.Controls
/// The control that receives the focus adorner.
protected virtual IControl? GetTemplateFocusTarget() => this;
+ private static Action loadedProcessingAction = () =>
+ {
+ // Copy the loaded queue for processing
+ // There was a possibility of the "Collection was modified; enumeration operation may not execute."
+ // exception when only a single hash set was used. This could happen when new controls are added
+ // within the Loaded callback/event itself. To fix this, two hash sets are used and while one is
+ // being processed the other accepts adding new controls to process next.
+ _loadedProcessingQueue.Clear();
+ foreach (Control control in _loadedQueue)
+ {
+ _loadedProcessingQueue.Add(control);
+ }
+ _loadedQueue.Clear();
+
+ foreach (Control control in _loadedProcessingQueue)
+ {
+ control.OnLoadedCore();
+ }
+
+ _loadedProcessingQueue.Clear();
+ _isLoadedProcessing = false;
+
+ // Restart if any controls were added to the queue while processing
+ if (_loadedQueue.Count > 0)
+ {
+ _isLoadedProcessing = true;
+ Dispatcher.UIThread.Post(loadedProcessingAction!, DispatcherPriority.Loaded);
+ }
+ };
+
+ ///
+ /// Schedules to be called for this control.
+ /// For performance, it will be queued with other controls.
+ ///
+ internal void ScheduleOnLoadedCore()
+ {
+ if (_isLoaded == false)
+ {
+ bool isAdded = _loadedQueue.Add(this);
+
+ if (isAdded &&
+ _isLoadedProcessing == false)
+ {
+ _isLoadedProcessing = true;
+ Dispatcher.UIThread.Post(loadedProcessingAction!, DispatcherPriority.Loaded);
+ }
+ }
+ }
+
+ ///
+ /// Invoked as the first step of marking the control as loaded and raising the
+ /// event.
+ ///
+ internal void OnLoadedCore()
+ {
+ if (_isLoaded == false &&
+ _isAttachedToVisualTree)
+ {
+ _isLoaded = true;
+ OnLoaded();
+ }
+ }
+
+ ///
+ /// Invoked as the first step of marking the control as unloaded and raising the
+ /// event.
+ ///
+ internal void OnUnloadedCore()
+ {
+ if (_isLoaded)
+ {
+ // Remove from the loaded event queue here as a failsafe in case the control
+ // is detached before the dispatcher runs the Loaded jobs.
+ _loadedQueue.Remove(this);
+
+ _isLoaded = false;
+ OnUnloaded();
+ }
+ }
+
+ ///
+ /// Invoked just before the event.
+ ///
+ protected virtual void OnLoaded()
+ {
+ var eventArgs = new RoutedEventArgs(LoadedEvent);
+ eventArgs.Source = null;
+ RaiseEvent(eventArgs);
+ }
+
+ ///
+ /// Invoked just before the event.
+ ///
+ protected virtual void OnUnloaded()
+ {
+ var eventArgs = new RoutedEventArgs(UnloadedEvent);
+ eventArgs.Source = null;
+ RaiseEvent(eventArgs);
+ }
+
///
protected sealed override void OnAttachedToVisualTreeCore(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTreeCore(e);
+ _isAttachedToVisualTree = true;
InitializeIfNeeded();
+
+ ScheduleOnLoadedCore();
}
///
protected sealed override void OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e)
{
base.OnDetachedFromVisualTreeCore(e);
+ _isAttachedToVisualTree = false;
+
+ OnUnloadedCore();
}
///
@@ -324,7 +506,9 @@ namespace Avalonia.Controls
var keymap = AvaloniaLocator.Current.GetService()?.OpenContextMenu;
if (keymap is null)
+ {
return;
+ }
var matches = false;
@@ -378,17 +562,12 @@ namespace Avalonia.Controls
bool bypassFlowDirectionPolicies = BypassFlowDirectionPolicies;
bool parentBypassFlowDirectionPolicies = false;
- var parent = this.FindAncestorOfType();
+ var parent = ((IVisual)this).VisualParent as Control;
if (parent != null)
{
parentFlowDirection = parent.FlowDirection;
parentBypassFlowDirectionPolicies = parent.BypassFlowDirectionPolicies;
}
- else if (Parent is Control logicalParent)
- {
- parentFlowDirection = logicalParent.FlowDirection;
- parentBypassFlowDirectionPolicies = logicalParent.BypassFlowDirectionPolicies;
- }
bool thisShouldBeMirrored = flowDirection == FlowDirection.RightToLeft && !bypassFlowDirectionPolicies;
bool parentShouldBeMirrored = parentFlowDirection == FlowDirection.RightToLeft && !parentBypassFlowDirectionPolicies;
diff --git a/src/Avalonia.Controls/Converters/MenuScrollingVisibilityConverter.cs b/src/Avalonia.Controls/Converters/MenuScrollingVisibilityConverter.cs
index 6ab2f4c517..9d859a753a 100644
--- a/src/Avalonia.Controls/Converters/MenuScrollingVisibilityConverter.cs
+++ b/src/Avalonia.Controls/Converters/MenuScrollingVisibilityConverter.cs
@@ -26,7 +26,7 @@ namespace Avalonia.Controls.Converters
if (visibility == ScrollBarVisibility.Auto)
{
- if (extent == viewport)
+ if (MathUtilities.AreClose(extent, viewport))
{
return false;
}
diff --git a/src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs b/src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs
index 7f2abb7e98..46c5eaeaaa 100644
--- a/src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs
+++ b/src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs
@@ -194,7 +194,7 @@ namespace Avalonia.Controls
if (ClockIdentifier == "12HourClock")
{
- hr = per == 1 ? hr + 12 : per == 0 && hr == 12 ? 0 : hr;
+ hr = per == 1 ? (hr == 12) ? 12 : hr + 12 : per == 0 && hr == 12 ? 0 : hr;
}
Time = new TimeSpan(hr, min, 0);
diff --git a/src/Avalonia.Controls/GridSplitter.cs b/src/Avalonia.Controls/GridSplitter.cs
index 784d33ed58..85dad894fd 100644
--- a/src/Avalonia.Controls/GridSplitter.cs
+++ b/src/Avalonia.Controls/GridSplitter.cs
@@ -349,9 +349,9 @@ namespace Avalonia.Controls
}
}
- protected override void OnPointerEnter(PointerEventArgs e)
+ protected override void OnPointerEntered(PointerEventArgs e)
{
- base.OnPointerEnter(e);
+ base.OnPointerEntered(e);
GridResizeDirection direction = GetEffectiveResizeDirection();
diff --git a/src/Avalonia.Controls/MenuItem.cs b/src/Avalonia.Controls/MenuItem.cs
index 58229a1772..11c42f2ef3 100644
--- a/src/Avalonia.Controls/MenuItem.cs
+++ b/src/Avalonia.Controls/MenuItem.cs
@@ -79,25 +79,33 @@ namespace Avalonia.Controls
/// Defines the event.
///
public static readonly RoutedEvent ClickEvent =
- RoutedEvent.Register
/// The event sender.
/// The event args.
- private void ControlPointerLeave(object? sender, PointerEventArgs e)
+ private void ControlPointerExited(object? sender, PointerEventArgs e)
{
var control = (Control)sender!;
Close(control);
diff --git a/src/Avalonia.Controls/WindowBase.cs b/src/Avalonia.Controls/WindowBase.cs
index 12ba143c8a..5d3e51b394 100644
--- a/src/Avalonia.Controls/WindowBase.cs
+++ b/src/Avalonia.Controls/WindowBase.cs
@@ -169,7 +169,6 @@ namespace Avalonia.Controls
}
}
-
[Obsolete("No longer used. Has no effect.")]
protected IDisposable BeginAutoSizing() => Disposable.Empty;
@@ -186,6 +185,26 @@ namespace Avalonia.Controls
}
}
+ ///
+ protected override void OnClosed(EventArgs e)
+ {
+ // Window must manually raise Loaded/Unloaded events as it is a visual root and
+ // does not raise OnAttachedToVisualTreeCore/OnDetachedFromVisualTreeCore events
+ OnUnloadedCore();
+
+ base.OnClosed(e);
+ }
+
+ ///
+ protected override void OnOpened(EventArgs e)
+ {
+ // Window must manually raise Loaded/Unloaded events as it is a visual root and
+ // does not raise OnAttachedToVisualTreeCore/OnDetachedFromVisualTreeCore events
+ ScheduleOnLoadedCore();
+
+ base.OnOpened(e);
+ }
+
protected override void HandleClosed()
{
_ignoreVisibilityChange = true;
diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs b/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs
index 58807b489e..587898ac6e 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs
+++ b/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs
@@ -121,8 +121,8 @@ namespace Avalonia.Diagnostics.Views
if (header != null)
{
- header.PointerEnter += AddAdorner;
- header.PointerLeave += RemoveAdorner;
+ header.PointerEntered += AddAdorner;
+ header.PointerExited += RemoveAdorner;
}
item.TemplateApplied -= TreeViewItemTemplateApplied;
diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs
index 01e0fb1afc..a233dc9693 100644
--- a/src/Windows/Avalonia.Win32/WindowImpl.cs
+++ b/src/Windows/Avalonia.Win32/WindowImpl.cs
@@ -472,10 +472,14 @@ namespace Avalonia.Win32
{
GetWindowRect(_hwnd, out var rc);
- return new PixelPoint(rc.left, rc.top);
+ var border = HiddenBorderSize;
+ return new PixelPoint(rc.left + border.Width, rc.top + border.Height);
}
set
{
+ var border = HiddenBorderSize;
+ value = new PixelPoint(value.X - border.Width, value.Y - border.Height);
+
SetWindowPos(
Handle.Handle,
IntPtr.Zero,
@@ -489,6 +493,24 @@ namespace Avalonia.Win32
private bool HasFullDecorations => _windowProperties.Decorations == SystemDecorations.Full;
+ private PixelSize HiddenBorderSize
+ {
+ get
+ {
+ // Windows 10 and 11 add a 7 pixel invisible border on the left/right/bottom of windows for resizing
+ if (Win32Platform.WindowsVersion.Major < 10 || !HasFullDecorations)
+ {
+ return PixelSize.Empty;
+ }
+
+ DwmGetWindowAttribute(_hwnd, (int)DwmWindowAttribute.DWMWA_EXTENDED_FRAME_BOUNDS, out var clientRect, Marshal.SizeOf(typeof(RECT)));
+ GetWindowRect(_hwnd, out var frameRect);
+ var borderWidth = GetSystemMetrics(SystemMetric.SM_CXBORDER);
+
+ return new PixelSize(clientRect.left - frameRect.left - borderWidth, 0);
+ }
+ }
+
public void Move(PixelPoint point) => Position = point;
public void SetMinMaxSize(Size minSize, Size maxSize)
@@ -907,17 +929,20 @@ namespace Avalonia.Win32
borderCaptionThickness.top = 1;
}
+ //using a default margin of 0 when using WinUiComp removes artefacts when resizing. See issue #8316
+ var defaultMargin = _isUsingComposition ? 0 : 1;
+
MARGINS margins = new MARGINS();
- margins.cxLeftWidth = 1;
- margins.cxRightWidth = 1;
- margins.cyBottomHeight = 1;
+ margins.cxLeftWidth = defaultMargin;
+ margins.cxRightWidth = defaultMargin;
+ margins.cyBottomHeight = defaultMargin;
if (_extendTitleBarHint != -1)
{
borderCaptionThickness.top = (int)(_extendTitleBarHint * RenderScaling);
}
- margins.cyTopHeight = _extendChromeHints.HasAllFlags(ExtendClientAreaChromeHints.SystemChrome) && !_extendChromeHints.HasAllFlags(ExtendClientAreaChromeHints.PreferSystemChrome) ? borderCaptionThickness.top : 1;
+ margins.cyTopHeight = _extendChromeHints.HasAllFlags(ExtendClientAreaChromeHints.SystemChrome) && !_extendChromeHints.HasAllFlags(ExtendClientAreaChromeHints.PreferSystemChrome) ? borderCaptionThickness.top : defaultMargin;
if (WindowState == WindowState.Maximized)
{
diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_SetValue.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_SetValue.cs
index 954a609315..72162a4d8e 100644
--- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_SetValue.cs
+++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_SetValue.cs
@@ -17,6 +17,21 @@ namespace Avalonia.Base.UnitTests
Assert.Equal("foodefault", target.GetValue(Class1.FooProperty));
}
+ [Fact]
+ public void ClearValue_Resets_Value_To_Style_value()
+ {
+ Class1 target = new Class1();
+
+ target.SetValue(Class1.FooProperty, "style", BindingPriority.Style);
+ target.SetValue(Class1.FooProperty, "local");
+
+ Assert.Equal("local", target.GetValue(Class1.FooProperty));
+
+ target.ClearValue(Class1.FooProperty);
+
+ Assert.Equal("style", target.GetValue(Class1.FooProperty));
+ }
+
[Fact]
public void ClearValue_Raises_PropertyChanged()
{
diff --git a/tests/Avalonia.Base.UnitTests/Input/PointerOverTests.cs b/tests/Avalonia.Base.UnitTests/Input/PointerOverTests.cs
index 87df65a080..a5ca2aef4a 100644
--- a/tests/Avalonia.Base.UnitTests/Input/PointerOverTests.cs
+++ b/tests/Avalonia.Base.UnitTests/Input/PointerOverTests.cs
@@ -189,7 +189,7 @@ namespace Avalonia.Base.UnitTests.Input
// Ensure that e.Handled is reset between controls.
root.PointerMoved += (s, e) => e.Handled = true;
- decorator.PointerEnter += (s, e) => e.Handled = true;
+ decorator.PointerEntered += (s, e) => e.Handled = true;
SetHit(renderer, decorator);
impl.Object.Input!(CreateRawPointerMovedArgs(device, root));
@@ -231,7 +231,7 @@ namespace Avalonia.Base.UnitTests.Input
}
});
- AddEnterLeaveHandlers(HandleEvent, canvas, decorator);
+ AddEnteredExitedHandlers(HandleEvent, canvas, decorator);
// Enter decorator
SetHit(renderer, decorator);
@@ -246,17 +246,17 @@ namespace Avalonia.Base.UnitTests.Input
Assert.Equal(
new[]
{
- ((object?)decorator, "PointerEnter"),
- (decorator, "PointerMoved"),
- (decorator, "PointerLeave"),
- (canvas, "PointerEnter"),
- (canvas, "PointerMoved")
+ ((object?)decorator, nameof(InputElement.PointerEntered)),
+ (decorator, nameof(InputElement.PointerMoved)),
+ (decorator, nameof(InputElement.PointerExited)),
+ (canvas, nameof(InputElement.PointerEntered)),
+ (canvas, nameof(InputElement.PointerMoved))
},
result);
}
[Fact]
- public void PointerEnter_Leave_Should_Be_Raised_In_Correct_Order()
+ public void PointerEntered_Exited_Should_Be_Raised_In_Correct_Order()
{
using var app = UnitTestApplication.Start(new TestServices(inputManager: new InputManager()));
@@ -289,7 +289,7 @@ namespace Avalonia.Base.UnitTests.Input
SetHit(renderer, canvas);
impl.Object.Input!(CreateRawPointerMovedArgs(deviceMock.Object, root));
- AddEnterLeaveHandlers(HandleEvent, root, canvas, border, decorator);
+ AddEnteredExitedHandlers(HandleEvent, root, canvas, border, decorator);
SetHit(renderer, decorator);
impl.Object.Input!(CreateRawPointerMovedArgs(deviceMock.Object, root));
@@ -297,16 +297,16 @@ namespace Avalonia.Base.UnitTests.Input
Assert.Equal(
new[]
{
- ((object?)canvas, "PointerLeave"),
- (decorator, "PointerEnter"),
- (border, "PointerEnter"),
+ ((object?)canvas, nameof(InputElement.PointerExited)),
+ (decorator, nameof(InputElement.PointerEntered)),
+ (border, nameof(InputElement.PointerEntered)),
},
result);
}
// https://github.com/AvaloniaUI/Avalonia/issues/7896
[Fact]
- public void PointerEnter_Leave_Should_Set_Correct_Position()
+ public void PointerEntered_Exited_Should_Set_Correct_Position()
{
using var app = UnitTestApplication.Start(new TestServices(inputManager: new InputManager()));
@@ -331,7 +331,7 @@ namespace Avalonia.Base.UnitTests.Input
}
});
- AddEnterLeaveHandlers(HandleEvent, root, canvas);
+ AddEnteredExitedHandlers(HandleEvent, root, canvas);
SetHit(renderer, canvas);
impl.Object.Input!(CreateRawPointerMovedArgs(deviceMock.Object, root, expectedPosition));
@@ -342,10 +342,10 @@ namespace Avalonia.Base.UnitTests.Input
Assert.Equal(
new[]
{
- ((object?)canvas, "PointerEnter", expectedPosition),
- (root, "PointerEnter", expectedPosition),
- (canvas, "PointerLeave", expectedPosition),
- (root, "PointerLeave", expectedPosition)
+ ((object?)canvas, nameof(InputElement.PointerEntered), expectedPosition),
+ (root, nameof(InputElement.PointerEntered), expectedPosition),
+ (canvas, nameof(InputElement.PointerExited), expectedPosition),
+ (root, nameof(InputElement.PointerExited), expectedPosition)
},
result);
}
@@ -415,7 +415,7 @@ namespace Avalonia.Base.UnitTests.Input
}
});
- AddEnterLeaveHandlers(HandleEvent, root, canvas);
+ AddEnteredExitedHandlers(HandleEvent, root, canvas);
// Init pointer over.
SetHit(renderer, canvas);
@@ -429,22 +429,22 @@ namespace Avalonia.Base.UnitTests.Input
Assert.Equal(
new[]
{
- ((object?)canvas, "PointerEnter", lastClientPosition),
- (root, "PointerEnter", lastClientPosition),
- (canvas, "PointerLeave", lastClientPosition),
- (root, "PointerLeave", lastClientPosition),
+ ((object?)canvas, nameof(InputElement.PointerEntered), lastClientPosition),
+ (root, nameof(InputElement.PointerEntered), lastClientPosition),
+ (canvas, nameof(InputElement.PointerExited), lastClientPosition),
+ (root, nameof(InputElement.PointerExited), lastClientPosition),
},
result);
}
- private static void AddEnterLeaveHandlers(
+ private static void AddEnteredExitedHandlers(
EventHandler handler,
params IInputElement[] controls)
{
foreach (var c in controls)
{
- c.PointerEnter += handler;
- c.PointerLeave += handler;
+ c.PointerEntered += handler;
+ c.PointerExited += handler;
c.PointerMoved += handler;
}
}
diff --git a/tests/Avalonia.Base.UnitTests/Layout/LayoutableTests.cs b/tests/Avalonia.Base.UnitTests/Layout/LayoutableTests.cs
index 184aada6cd..c4b0795022 100644
--- a/tests/Avalonia.Base.UnitTests/Layout/LayoutableTests.cs
+++ b/tests/Avalonia.Base.UnitTests/Layout/LayoutableTests.cs
@@ -173,37 +173,6 @@ namespace Avalonia.Base.UnitTests.Layout
target.Verify(x => x.InvalidateMeasure(root), Times.Once());
}
- [Theory]
- [InlineData(16, 6, 5.333333333333333)]
- [InlineData(18, 10, 4)]
- public void UseLayoutRounding_Arranges_Center_Alignment_Correctly_With_Fractional_Scaling(
- double containerWidth,
- double childWidth,
- double expectedX)
- {
- Border target;
- var root = new TestRoot
- {
- LayoutScaling = 1.5,
- UseLayoutRounding = true,
- Child = new Decorator
- {
- Width = containerWidth,
- Height = 100,
- Child = target = new Border
- {
- Width = childWidth,
- HorizontalAlignment = HorizontalAlignment.Center,
- }
- }
- };
-
- root.Measure(new Size(100, 100));
- root.Arrange(new Rect(target.DesiredSize));
-
- Assert.Equal(new Rect(expectedX, 0, childWidth, 100), target.Bounds);
- }
-
[Fact]
public void LayoutUpdated_Is_Called_At_End_Of_Layout_Pass()
{
diff --git a/tests/Avalonia.Base.UnitTests/Layout/LayoutableTests_LayoutRounding.cs b/tests/Avalonia.Base.UnitTests/Layout/LayoutableTests_LayoutRounding.cs
new file mode 100644
index 0000000000..77f1a8882d
--- /dev/null
+++ b/tests/Avalonia.Base.UnitTests/Layout/LayoutableTests_LayoutRounding.cs
@@ -0,0 +1,140 @@
+using Avalonia.Controls;
+using Avalonia.Layout;
+using Avalonia.UnitTests;
+using Xunit;
+using Xunit.Sdk;
+
+namespace Avalonia.Base.UnitTests.Layout
+{
+ public class LayoutableTests_LayoutRounding
+ {
+ [Theory]
+ [InlineData(100, 100)]
+ [InlineData(101, 101.33333333333333)]
+ [InlineData(103, 103.33333333333333)]
+ public void Measure_Adjusts_DesiredSize_Upwards_When_Constraint_Allows(double desiredSize, double expectedSize)
+ {
+ var target = new TestLayoutable(new Size(desiredSize, desiredSize));
+ var root = CreateRoot(1.5, target);
+
+ root.LayoutManager.ExecuteInitialLayoutPass();
+
+ Assert.Equal(new Size(expectedSize, expectedSize), target.DesiredSize);
+ }
+
+ [Fact]
+ public void Measure_Constrains_Adjusted_DesiredSize_To_Constraint()
+ {
+ var target = new TestLayoutable(new Size(101, 101));
+ var root = CreateRoot(1.5, target, constraint: new Size(101, 101));
+
+ root.LayoutManager.ExecuteInitialLayoutPass();
+
+ // Desired width/height with layout rounding is 101.3333 but constraint is 101,101 so
+ // layout rounding should be ignored.
+ Assert.Equal(new Size(101, 101), target.DesiredSize);
+ }
+
+ [Fact]
+ public void Measure_Adjusts_DesiredSize_Upwards_When_Margin_Present()
+ {
+ var target = new TestLayoutable(new Size(101, 101), margin: 1);
+ var root = CreateRoot(1.5, target);
+
+ root.LayoutManager.ExecuteInitialLayoutPass();
+
+ // - 1 pixel margin is rounded up to 1.3333; for both sides it is 2.6666
+ // - Size of 101 gets rounded up to 101.3333
+ // - Final size = 101.3333 + 2.6666 = 104
+ AssertEqual(new Size(104, 104), target.DesiredSize);
+ }
+
+ [Fact]
+ public void Arrange_Adjusts_Bounds_Upwards_With_Margin()
+ {
+ var target = new TestLayoutable(new Size(101, 101), margin: 1);
+ var root = CreateRoot(1.5, target);
+
+ root.LayoutManager.ExecuteInitialLayoutPass();
+
+ // - 1 pixel margin is rounded up to 1.3333
+ // - Size of 101 gets rounded up to 101.3333
+ AssertEqual(new Point(1.3333333333333333, 1.3333333333333333), target.Bounds.Position);
+ AssertEqual(new Size(101.33333333333333, 101.33333333333333), target.Bounds.Size);
+ }
+
+ [Theory]
+ [InlineData(16, 6, 5.333333333333333)]
+ [InlineData(18, 10, 4)]
+ public void Arranges_Center_Alignment_Correctly_With_Fractional_Scaling(
+ double containerWidth,
+ double childWidth,
+ double expectedX)
+ {
+ Border target;
+ var root = new TestRoot
+ {
+ LayoutScaling = 1.5,
+ UseLayoutRounding = true,
+ Child = new Decorator
+ {
+ Width = containerWidth,
+ Height = 100,
+ Child = target = new Border
+ {
+ Width = childWidth,
+ HorizontalAlignment = HorizontalAlignment.Center,
+ }
+ }
+ };
+
+ root.Measure(new Size(100, 100));
+ root.Arrange(new Rect(target.DesiredSize));
+
+ Assert.Equal(new Rect(expectedX, 0, childWidth, 100), target.Bounds);
+ }
+
+ private static TestRoot CreateRoot(
+ double scaling,
+ Control child,
+ Size? constraint = null)
+ {
+ return new TestRoot
+ {
+ LayoutScaling = scaling,
+ UseLayoutRounding = true,
+ Child = child,
+ ClientSize = constraint ?? new Size(1000, 1000),
+ };
+ }
+
+ private static void AssertEqual(Point expected, Point actual)
+ {
+ if (!expected.NearlyEquals(actual))
+ {
+ throw new EqualException(expected, actual);
+ }
+ }
+
+ private static void AssertEqual(Size expected, Size actual)
+ {
+ if (!expected.NearlyEquals(actual))
+ {
+ throw new EqualException(expected, actual);
+ }
+ }
+
+ private class TestLayoutable : Control
+ {
+ private Size _desiredSize;
+
+ public TestLayoutable(Size desiredSize, double margin = 0)
+ {
+ _desiredSize = desiredSize;
+ Margin = new Thickness(margin);
+ }
+
+ protected override Size MeasureOverride(Size availableSize) => _desiredSize;
+ }
+ }
+}
diff --git a/tests/Avalonia.Base.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGenerator.cs b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGenerator.cs
index 7ad2e71d77..4d1941f7c5 100644
--- a/tests/Avalonia.Base.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGenerator.cs
+++ b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGenerator.cs
@@ -16,10 +16,12 @@ namespace Avalonia.Base.UnitTests.Media.TextFormatting
Directory.CreateDirectory("Generated");
}
+ var trie = GenerateBreakTypeTrie();
+
+ UnicodeDataGenerator.GenerateTrieClass("GraphemeBreak", trie);
+
using (var stream = File.Create("Generated\\GraphemeBreak.trie"))
{
- var trie = GenerateBreakTypeTrie();
-
trie.Save(stream);
}
}
diff --git a/tests/Avalonia.Base.UnitTests/Media/TextFormatting/UnicodeDataGenerator.cs b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/UnicodeDataGenerator.cs
index e2877abd83..ab6b06c200 100644
--- a/tests/Avalonia.Base.UnitTests/Media/TextFormatting/UnicodeDataGenerator.cs
+++ b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/UnicodeDataGenerator.cs
@@ -58,17 +58,69 @@ namespace Avalonia.Base.UnitTests.Media.TextFormatting
{
PairedBracketTypes = biDiPairedBracketTypeEntries, BiDiClasses = biDiClassEntries
};
+
+ var trie = biDiTrieBuilder.Freeze();
+
+ GenerateTrieClass("BiDi", trie);
using (var stream = File.Create("Generated\\BiDi.trie"))
{
- var trie = biDiTrieBuilder.Freeze();
-
trie.Save(stream);
return trie;
}
}
+ public static void GenerateTrieClass(string name, UnicodeTrie trie)
+ {
+ var stream = new MemoryStream();
+
+ trie.Save(stream);
+
+ using (var fileStream = File.Create($"Generated\\{name}.trie.cs"))
+ using (var writer = new StreamWriter(fileStream))
+ {
+ writer.WriteLine("using System;");
+ writer.WriteLine("namespace Avalonia.Media.TextFormatting.Unicode");
+ writer.WriteLine("{");
+ writer.WriteLine($" internal static class {name}Trie");
+ writer.WriteLine(" {");
+ writer.WriteLine(" public static ReadOnlySpan Data => new byte[]");
+ writer.WriteLine(" {");
+
+ stream.Position = 0;
+
+ writer.Write(" ");
+
+ while (true)
+ {
+ var b = stream.ReadByte();
+
+ if(b == -1)
+ {
+ break;
+ }
+
+ writer.Write(b.ToString());
+
+ writer.Write(',');
+
+ if (stream.Position % 100 == 0)
+ {
+ writer.Write(Environment.NewLine);
+
+ writer.Write(" ");
+
+ continue;
+ }
+ }
+
+ writer.WriteLine(" };");
+ writer.WriteLine(" }");
+ writer.WriteLine("}");
+ }
+ }
+
public static UnicodeTrie GenerateUnicodeDataTrie(out UnicodeDataEntries dataEntries, out Dictionary unicodeData)
{
var generalCategoryEntries =
@@ -105,10 +157,12 @@ namespace Avalonia.Base.UnitTests.Media.TextFormatting
LineBreakClasses = lineBreakClassEntries
};
+ var trie = unicodeDataTrieBuilder.Freeze();
+
+ GenerateTrieClass("UnicodeData", trie);
+
using (var stream = File.Create("Generated\\UnicodeData.trie"))
{
- var trie = unicodeDataTrieBuilder.Freeze();
-
trie.Save(stream);
return trie;
diff --git a/tests/Avalonia.Base.UnitTests/Media/TextFormatting/UnicodeEnumsGenerator.cs b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/UnicodeEnumsGenerator.cs
index 1323ddfbd1..10da018eeb 100644
--- a/tests/Avalonia.Base.UnitTests/Media/TextFormatting/UnicodeEnumsGenerator.cs
+++ b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/UnicodeEnumsGenerator.cs
@@ -102,7 +102,7 @@ namespace Avalonia.Base.UnitTests.Media.TextFormatting
using (var stream =
typeof(UnicodeEnumsGenerator).Assembly.GetManifestResourceStream(
- "Avalonia.Visuals.UnitTests.Media.TextFormatting.BreakPairTable.txt"))
+ "Avalonia.Base.UnitTests.Media.TextFormatting.BreakPairTable.txt"))
using (var reader = new StreamReader(stream))
{
while (!reader.EndOfStream)
diff --git a/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs b/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs
index 01afe85b8b..879de9fca5 100644
--- a/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs
+++ b/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs
@@ -349,6 +349,39 @@ namespace Avalonia.Base.UnitTests.Rendering.SceneGraph
}
}
+ [Fact]
+ public void MirrorTransform_For_Control_With_RenderTransform_Should_Be_Correct()
+ {
+ using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
+ {
+ Border border;
+ var tree = new TestRoot
+ {
+ Width = 400,
+ Height = 200,
+ Child = border = new Border
+ {
+ HorizontalAlignment = HorizontalAlignment.Left,
+ Background = Brushes.Red,
+ Width = 100,
+ RenderTransform = new ScaleTransform(0.5, 1),
+ FlowDirection = FlowDirection.RightToLeft
+ }
+ };
+
+ tree.Measure(Size.Infinity);
+ tree.Arrange(new Rect(tree.DesiredSize));
+
+ var scene = new Scene(tree);
+ var sceneBuilder = new SceneBuilder();
+ sceneBuilder.UpdateAll(scene);
+
+ var expectedTransform = new Matrix(-1, 0, 0, 1, 100, 0) * Matrix.CreateScale(0.5, 1) * Matrix.CreateTranslation(25, 0);
+ var borderNode = scene.FindNode(border);
+ Assert.Equal(expectedTransform, borderNode.Transform);
+ }
+ }
+
[Fact]
public void Should_Update_Border_Background_Node()
{
@@ -805,6 +838,7 @@ namespace Avalonia.Base.UnitTests.Rendering.SceneGraph
Canvas canvas;
var tree = new TestRoot
{
+ ClientSize = new Size(100, 100),
Child = decorator = new Decorator
{
Margin = new Thickness(0, 10, 0, 0),
diff --git a/tests/Avalonia.Base.UnitTests/Styling/SetterTests.cs b/tests/Avalonia.Base.UnitTests/Styling/SetterTests.cs
index ed4c78aa3e..c684466200 100644
--- a/tests/Avalonia.Base.UnitTests/Styling/SetterTests.cs
+++ b/tests/Avalonia.Base.UnitTests/Styling/SetterTests.cs
@@ -150,13 +150,43 @@ namespace Avalonia.Base.UnitTests.Styling
Assert.Equal(BindingPriority.StyleTrigger, control.GetDiagnostic(TextBlock.TagProperty).Priority);
}
- private IBinding CreateMockBinding(AvaloniaProperty property)
+ [Fact]
+ public void Disposing_Setter_Should_Preserve_LocalValue()
{
- var subject = new Subject