From 265518859a228584d92e8de79571c54cdd47bef9 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Mon, 3 May 2021 07:34:10 +0200 Subject: [PATCH 1/8] Fix text tokenization for inherited scripts --- .../Media/TextFormatting/TextCharacters.cs | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextCharacters.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextCharacters.cs index c6f524451b..86b96b9cfc 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextCharacters.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextCharacters.cs @@ -161,30 +161,27 @@ namespace Avalonia.Media.TextFormatting if (currentScript != script) { - if (currentScript != Script.Inherited && currentScript != Script.Common) + if (script == Script.Inherited || script == Script.Common) { - if (script == Script.Inherited || script == Script.Common) - { - script = currentScript; - } - else + script = currentScript; + } + else + { + if (currentScript != Script.Inherited && currentScript != Script.Common) { break; } } } - if (isFallback) + if (currentScript != Script.Common && currentScript != Script.Inherited) { - if (defaultFont.TryGetGlyph(grapheme.FirstCodepoint, out _)) + if (isFallback && defaultFont.TryGetGlyph(grapheme.FirstCodepoint, out _)) { break; } - } - if (!font.TryGetGlyph(grapheme.FirstCodepoint, out _)) - { - if (!grapheme.FirstCodepoint.IsWhiteSpace) + if (!font.TryGetGlyph(grapheme.FirstCodepoint, out _)) { break; } From d64a66d441ad9846065c426ddef61800af22950b Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Fri, 14 May 2021 11:17:45 +0200 Subject: [PATCH 2/8] Fix fallback selection --- .../TextFormatting/ShapedTextCharacters.cs | 6 +++-- .../Media/TextFormatting/TextCharacters.cs | 27 +++++++++++++------ 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/Avalonia.Visuals/Media/TextFormatting/ShapedTextCharacters.cs b/src/Avalonia.Visuals/Media/TextFormatting/ShapedTextCharacters.cs index 723d5e81ab..b304b19910 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/ShapedTextCharacters.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/ShapedTextCharacters.cs @@ -109,7 +109,8 @@ namespace Avalonia.Media.TextFormatting GlyphRun.GlyphAdvances.Take(glyphCount), GlyphRun.GlyphOffsets.Take(glyphCount), GlyphRun.Characters.Take(length), - GlyphRun.GlyphClusters.Take(glyphCount)); + GlyphRun.GlyphClusters.Take(glyphCount), + GlyphRun.BiDiLevel); var firstTextRun = new ShapedTextCharacters(firstGlyphRun, Properties); @@ -120,7 +121,8 @@ namespace Avalonia.Media.TextFormatting GlyphRun.GlyphAdvances.Skip(glyphCount), GlyphRun.GlyphOffsets.Skip(glyphCount), GlyphRun.Characters.Skip(length), - GlyphRun.GlyphClusters.Skip(glyphCount)); + GlyphRun.GlyphClusters.Skip(glyphCount), + GlyphRun.BiDiLevel); var secondTextRun = new ShapedTextCharacters(secondGlyphRun, Properties); diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextCharacters.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextCharacters.cs index 86b96b9cfc..0779716ec8 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextCharacters.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextCharacters.cs @@ -135,20 +135,20 @@ namespace Avalonia.Media.TextFormatting count = 0; var script = Script.Common; - //var direction = BiDiClass.LeftToRight; + var direction = BiDiClass.LeftToRight; var font = typeface.GlyphTypeface; var defaultFont = defaultTypeface.GlyphTypeface; - + var enumerator = new GraphemeEnumerator(text); while (enumerator.MoveNext()) { - var grapheme = enumerator.Current; + var currentGrapheme = enumerator.Current; - var currentScript = grapheme.FirstCodepoint.Script; + var currentScript = currentGrapheme.FirstCodepoint.Script; - //var currentDirection = grapheme.FirstCodepoint.BiDiClass; + var currentDirection = currentGrapheme.FirstCodepoint.BiDiClass; //// ToDo: Implement BiDi algorithm //if (currentScript.HorizontalDirection != direction) @@ -176,18 +176,29 @@ namespace Avalonia.Media.TextFormatting if (currentScript != Script.Common && currentScript != Script.Inherited) { - if (isFallback && defaultFont.TryGetGlyph(grapheme.FirstCodepoint, out _)) + if (isFallback && defaultFont.TryGetGlyph(currentGrapheme.FirstCodepoint, out _)) { break; } - if (!font.TryGetGlyph(grapheme.FirstCodepoint, out _)) + if (!font.TryGetGlyph(currentGrapheme.FirstCodepoint, out _)) { break; } } - count += grapheme.Text.Length; + if (!currentGrapheme.FirstCodepoint.IsWhiteSpace && !font.TryGetGlyph(currentGrapheme.FirstCodepoint, out _)) + { + break; + } + + if (direction == BiDiClass.RightToLeft && currentDirection == BiDiClass.CommonSeparator) + { + break; + } + + count += currentGrapheme.Text.Length; + direction = currentDirection; } return count > 0; From 43beabc9b49f92eafd75b00659f83ef75957532d Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Tue, 18 May 2021 16:50:22 +0200 Subject: [PATCH 3/8] Add a unit test --- .../TextFormatting/TextFormatterTests.cs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs index 97af874238..9c2a1953f1 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs @@ -125,6 +125,27 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting } } + [Fact] + public void Should_Produce_A_Single_Fallback_Run() + { + using (Start()) + { + var defaultProperties = new GenericTextRunProperties(Typeface.Default); + + const string text = "👍 👍 👍 👍"; + + var textSource = new SingleBufferTextSource(text, defaultProperties); + + var formatter = new TextFormatterImpl(); + + var textLine = + formatter.FormatLine(textSource, 0, double.PositiveInfinity, + new GenericTextParagraphProperties(defaultProperties)); + + Assert.Equal(1, textLine.TextRuns.Count); + } + } + [Fact] public void Should_Split_Run_On_Script() { From 9b48da09ca7f5b9bf0a6a1b4082b4aad4cb89302 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Tue, 18 May 2021 23:49:49 +0200 Subject: [PATCH 4/8] Add at least basic class level docs for several controls. --- .../DateTimePickers/TimePicker.cs | 2 +- src/Avalonia.Controls/Expander.cs | 21 +++++++++++++++++++ src/Avalonia.Controls/Grid.cs | 2 +- .../Primitives/ScrollBarVisibility.cs | 18 ++++++++++++++++ src/Avalonia.Controls/RadioButton.cs | 3 +++ src/Avalonia.Controls/RelativePanel.cs | 3 +++ src/Avalonia.Controls/RepeatButton.cs | 3 +++ src/Avalonia.Controls/TextBox.cs | 3 +++ 8 files changed, 53 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/DateTimePickers/TimePicker.cs b/src/Avalonia.Controls/DateTimePickers/TimePicker.cs index e4ff5e9e5b..d0cf772c01 100644 --- a/src/Avalonia.Controls/DateTimePickers/TimePicker.cs +++ b/src/Avalonia.Controls/DateTimePickers/TimePicker.cs @@ -8,7 +8,7 @@ using System.Globalization; namespace Avalonia.Controls { /// - /// A control to allow the user to select a time + /// A control to allow the user to select a time. /// [PseudoClasses(":hasnotime")] public class TimePicker : TemplatedControl diff --git a/src/Avalonia.Controls/Expander.cs b/src/Avalonia.Controls/Expander.cs index 9ff2e41fa9..052b42a233 100644 --- a/src/Avalonia.Controls/Expander.cs +++ b/src/Avalonia.Controls/Expander.cs @@ -4,14 +4,35 @@ using Avalonia.Controls.Primitives; namespace Avalonia.Controls { + /// + /// Direction in which an control opens. + /// public enum ExpandDirection { + /// + /// Opens down. + /// Down, + + /// + /// Opens up. + /// Up, + + /// + /// Opens left. + /// Left, + + /// + /// Opens right. + /// Right } + /// + /// A control with a header that has a collapsible content section. + /// [PseudoClasses(":expanded", ":up", ":down", ":left", ":right")] public class Expander : HeaderedContentControl { diff --git a/src/Avalonia.Controls/Grid.cs b/src/Avalonia.Controls/Grid.cs index 105a2744e5..c7d598006d 100644 --- a/src/Avalonia.Controls/Grid.cs +++ b/src/Avalonia.Controls/Grid.cs @@ -17,7 +17,7 @@ using Avalonia.VisualTree; namespace Avalonia.Controls { /// - /// Grid + /// Defines a flexible grid area that consists of columns and rows. /// public class Grid : Panel { diff --git a/src/Avalonia.Controls/Primitives/ScrollBarVisibility.cs b/src/Avalonia.Controls/Primitives/ScrollBarVisibility.cs index 90727a3505..e62e087140 100644 --- a/src/Avalonia.Controls/Primitives/ScrollBarVisibility.cs +++ b/src/Avalonia.Controls/Primitives/ScrollBarVisibility.cs @@ -1,10 +1,28 @@ namespace Avalonia.Controls.Primitives { + /// + /// Specifies the visibility of a for scrollable content. + /// public enum ScrollBarVisibility { + /// + /// No scrollbars and no scrolling in this dimension. + /// Disabled, + + /// + /// The scrollbar should be visible only if there is more content than fits in the viewport. + /// Auto, + + /// + /// The scrollbar should never be visible. No space should ever be reserved for the scrollbar. + /// Hidden, + + /// + /// The scrollbar should always be visible. Space should always be reserved for the scrollbar. + /// Visible, } } diff --git a/src/Avalonia.Controls/RadioButton.cs b/src/Avalonia.Controls/RadioButton.cs index 4bda241497..681278d247 100644 --- a/src/Avalonia.Controls/RadioButton.cs +++ b/src/Avalonia.Controls/RadioButton.cs @@ -8,6 +8,9 @@ using Avalonia.VisualTree; namespace Avalonia.Controls { + /// + /// Represents a button that allows a user to select a single option from a group of options. + /// public class RadioButton : ToggleButton { private class RadioButtonGroupManager diff --git a/src/Avalonia.Controls/RelativePanel.cs b/src/Avalonia.Controls/RelativePanel.cs index 2305d8fb9e..c5de004f09 100644 --- a/src/Avalonia.Controls/RelativePanel.cs +++ b/src/Avalonia.Controls/RelativePanel.cs @@ -8,6 +8,9 @@ using Avalonia.Layout; namespace Avalonia.Controls { + /// + /// Defines an area within which you can position and align child objects in relation to each other or the parent panel. + /// public partial class RelativePanel : Panel { private readonly Graph _childGraph; diff --git a/src/Avalonia.Controls/RepeatButton.cs b/src/Avalonia.Controls/RepeatButton.cs index fcd6607feb..ba770634d9 100644 --- a/src/Avalonia.Controls/RepeatButton.cs +++ b/src/Avalonia.Controls/RepeatButton.cs @@ -4,6 +4,9 @@ using Avalonia.Threading; namespace Avalonia.Controls { + /// + /// Represents a control that raises its event repeatedly when it is pressed and held. + /// public class RepeatButton : Button { /// diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 69da667011..42030c2860 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -17,6 +17,9 @@ using Avalonia.Controls.Metadata; namespace Avalonia.Controls { + /// + /// Represents a control that can be used to display or edit unformatted text. + /// [PseudoClasses(":empty")] public class TextBox : TemplatedControl, UndoRedoHelper.IUndoRedoHost { From 6f1b242f118f5c15b84c25232a6153802bfc99ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Komosi=C5=84ski?= Date: Wed, 19 May 2021 00:04:29 +0200 Subject: [PATCH 5/8] Update docs link in PR template. --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 34f931ff41..46e8665945 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -18,7 +18,7 @@ - [ ] Added unit tests (if possible)? - [ ] Added XML documentation to any related classes? -- [ ] Consider submitting a PR to https://github.com/AvaloniaUI/Avaloniaui.net with user documentation +- [ ] Consider submitting a PR to https://github.com/AvaloniaUI/Documentation with user documentation ## Breaking changes From b5efb293ba2b2fce4d7fdf58d7f823def49f953f Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 18 May 2021 22:38:00 +0100 Subject: [PATCH 6/8] trick to make OSX invalidate the shadow. --- native/Avalonia.Native/src/OSX/window.h | 1 + native/Avalonia.Native/src/OSX/window.mm | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/native/Avalonia.Native/src/OSX/window.h b/native/Avalonia.Native/src/OSX/window.h index b1f64bca88..6a4ca2b8d8 100644 --- a/native/Avalonia.Native/src/OSX/window.h +++ b/native/Avalonia.Native/src/OSX/window.h @@ -34,6 +34,7 @@ class WindowBaseImpl; -(double) getScaling; -(double) getExtendedTitleBarHeight; -(void) setIsExtended:(bool)value; +-(void) updateShadow; @end struct INSWindowHolder diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index d7afbdaa3a..ecce1370b6 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -124,7 +124,11 @@ public: [Window setTitle:_lastTitle]; _shown = true; - + + dispatch_async(dispatch_get_main_queue(), ^{ + [Window updateShadow]; + }); + return S_OK; } } @@ -1838,6 +1842,19 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent double _lastScaling; } +- (void)updateShadow +{ + // Because of [invalidateShadow] of NSWindow is not working, + // We should do the trick as following to force the NSWindow re-renders its shadow. + + NSRect frame = [self frame]; + NSRect updatedFrame = NSMakeRect(frame.origin.x, frame.origin.y, frame.size.width + 1.0, frame.size.height + 1.0); + [self setFrame:updatedFrame display:YES]; + [self setFrame:frame display:YES]; + + [self invalidateShadow]; +} + -(void) setIsExtended:(bool)value; { _isExtended = value; From 4cf49b659aed629a238ee31970a9cb56187f4cd9 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 19 May 2021 10:56:16 +0100 Subject: [PATCH 7/8] refactor broken english. --- native/Avalonia.Native/src/OSX/window.mm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index ecce1370b6..e7c144b2d3 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -1844,8 +1844,8 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent - (void)updateShadow { - // Because of [invalidateShadow] of NSWindow is not working, - // We should do the trick as following to force the NSWindow re-renders its shadow. + // Common problem in Cocoa where [invalidateShadow] does work, + // This hack forces Cocoa to invalidate the shadow. NSRect frame = [self frame]; NSRect updatedFrame = NSMakeRect(frame.origin.x, frame.origin.y, frame.size.width + 1.0, frame.size.height + 1.0); From 863961551c6e01c6fa254dc33d014dec02ae6846 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 19 May 2021 15:00:28 +0100 Subject: [PATCH 8/8] fix implementation of ScalingChanged property. there were 2x implementations, 1x implicit, 1x explicit... :face-palm: --- src/Avalonia.Native/WindowImplBase.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Avalonia.Native/WindowImplBase.cs b/src/Avalonia.Native/WindowImplBase.cs index 71359f733d..f716464d14 100644 --- a/src/Avalonia.Native/WindowImplBase.cs +++ b/src/Avalonia.Native/WindowImplBase.cs @@ -414,9 +414,7 @@ namespace Avalonia.Native public Action Input { get; set; } - Action ScalingChanged { get; set; } - - Action ITopLevelImpl.ScalingChanged { get; set; } + public Action ScalingChanged { get; set; } public Action TransparencyLevelChanged { get; set; }