Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
96 commits
Select commit Hold shift + click to select a range
2067fff
chore: refactor InlinesCollection
ramezgerges Jun 20, 2025
a7eb496
chore: make clipboard-related text tests more stable
ramezgerges Jun 21, 2025
898e978
chore: refactor InlinesCollection 2
ramezgerges Jun 21, 2025
21517d4
chore: minor rearranging
ramezgerges Jun 23, 2025
2f0d387
chore: modify Draw() parameters
ramezgerges Jun 23, 2025
b22d914
chore: remove SelectionFound
ramezgerges Jun 23, 2025
0a079f6
chore: move logic from TextBlock to ParsedText
ramezgerges Jun 23, 2025
2927f6a
chore: remove TextBox chunking
ramezgerges Jun 23, 2025
1d1815a
chore: fix caret stem not showing
ramezgerges Jun 23, 2025
334cda4
chore: remove GetLineIntervals
ramezgerges Jun 23, 2025
d48e254
chore: IParsedText
ramezgerges Jun 23, 2025
4676038
chore: temp
ramezgerges Jun 27, 2025
513b5b7
chore: unicode text progress
ramezgerges Jul 15, 2025
f67565b
chore: cleanup and font fallback
ramezgerges Jul 15, 2025
1d38e5d
chore: adjust font fallback
ramezgerges Jul 15, 2025
62dd965
chore: transitional commit
ramezgerges Jul 16, 2025
46b33cc
chore: major refactoring to split bidi into two phases
ramezgerges Jul 17, 2025
2e988fb
chore: minor touch
ramezgerges Jul 17, 2025
538a4f8
chore: line breaking fix
ramezgerges Jul 17, 2025
5e22c53
chore: line layouting
ramezgerges Jul 17, 2025
fa8bf5f
chore: clustering
ramezgerges Jul 17, 2025
60d7898
chore: refactoring
ramezgerges Jul 18, 2025
842846a
chore: minor fixes
ramezgerges Jul 21, 2025
b2b2761
chore: GetIndexAt
ramezgerges Jul 23, 2025
066fcdf
chore: new line support
ramezgerges Jul 23, 2025
88ed964
chore: minor GetIndexAt fix
ramezgerges Jul 23, 2025
fd92b91
chore: minor GetIndexAt fix 2
ramezgerges Jul 23, 2025
f59e8cb
chore: more fixes to ApplyLineBreaking
ramezgerges Jul 23, 2025
8b4a2a2
chore: rebase
ramezgerges Aug 6, 2025
fd9189e
chore: multiple inlines
ramezgerges Aug 11, 2025
2fe55d9
chore: line stacking strategy
ramezgerges Aug 15, 2025
91adb95
chore: text alignment
ramezgerges Aug 15, 2025
8a3c98a
chore: small bug fixes
ramezgerges Aug 18, 2025
9a74e75
chore: UnicodeText.GetLineAt
ramezgerges Aug 18, 2025
7e7d80f
chore: UnicodeText.GetHyperlinkAt
ramezgerges Aug 18, 2025
225b29b
chore: comment
ramezgerges Aug 18, 2025
42923b8
chore: make FontDetails readonly
ramezgerges Aug 18, 2025
071c7b3
chore: UnicodeText.GetWordAt
ramezgerges Aug 18, 2025
18e44c7
chore: swap ParsedText out for UnicodeText
ramezgerges Aug 18, 2025
026004c
chore: use foreground color in UnicodeText.Draw
ramezgerges Aug 18, 2025
de7da52
chore: minor fix for GetRectForIndex
ramezgerges Aug 19, 2025
a89c3cb
chore: caret rendering and other adjustments
ramezgerges Aug 20, 2025
623fd30
chore: simple selection rendering
ramezgerges Aug 20, 2025
a894006
chore: fixes for caret rendering and more
ramezgerges Aug 20, 2025
0f2e465
chore: more adjustments to selection
ramezgerges Aug 20, 2025
2d623b9
chore: textbox alignment and flow direction auto detection
ramezgerges Sep 3, 2025
16be72a
chore: tiny refactoring
ramezgerges Sep 3, 2025
bedf72d
chore: adjust textbox directionality auto detection
ramezgerges Sep 4, 2025
441a74d
chore: adjust caret position on the boundary between two runs with di…
ramezgerges Sep 4, 2025
e1984c3
chore: clarifying comment
ramezgerges Sep 4, 2025
9ae40bb
chore: left/right arrows should respect base text direction
ramezgerges Sep 4, 2025
7c9f401
chore: MaxLines
ramezgerges Sep 4, 2025
1dc439f
chore: fix line breaking
ramezgerges Sep 4, 2025
e584bcc
chore: adjust word breaking
ramezgerges Sep 5, 2025
621dac2
chore: adjust line breaking
ramezgerges Sep 5, 2025
62a95f3
chore: fix When_Caret_Color_DarkMode
ramezgerges Sep 5, 2025
85ce3b1
chore: tab stops
ramezgerges Sep 8, 2025
0561043
chore: clean up tab stops handling
ramezgerges Sep 8, 2025
a981994
chore: initial attempt at trailing space hanging
ramezgerges Sep 8, 2025
4d93d15
chore: more on trailing space hanging
ramezgerges Sep 22, 2025
db74e92
chore: more on trailing space hanging by rewriting ApplyLineBreaking
ramezgerges Sep 23, 2025
0d9b0ee
chore: minor fix
ramezgerges Sep 23, 2025
b2c80eb
chore: fix IsLineBreak
ramezgerges Sep 23, 2025
84bc69f
chore: fix accidental double override on same run sequence
ramezgerges Sep 23, 2025
deeac24
chore: adjustment to GetIndexAndRunAt extended selection
ramezgerges Sep 23, 2025
e79c488
chore: ApplyLineBreaking last run bug
ramezgerges Sep 23, 2025
5e9f0b0
chore: fix tests
ramezgerges Sep 23, 2025
d0ca310
chore: fix pointer interaction with trailing line spaces
ramezgerges Sep 23, 2025
2e196d7
chore: wrapping partial words and various fixes
ramezgerges Sep 24, 2025
dac155d
chore: cleanup
ramezgerges Sep 24, 2025
7d0c42c
chore: a bunch of adjustments
ramezgerges Sep 25, 2025
bf7ea7c
chore: revert some test changes
ramezgerges Sep 25, 2025
20462fd
chore: revert Program.cs changes
ramezgerges Sep 25, 2025
b0ef7c1
chore: initial work on removing the ice.net dependency
ramezgerges Sep 25, 2025
418dfc7
chore: remove ice.net
ramezgerges Sep 25, 2025
388e471
chore: cleanup and linux support
ramezgerges Sep 25, 2025
14e9997
chore: rename file
ramezgerges Sep 25, 2025
859d55d
chore: only reference Microsoft.ICU.ICU4C.Runtime on win32 and wpf
ramezgerges Sep 29, 2025
537c203
chore: more work on ICU packaging
ramezgerges Sep 30, 2025
750025d
chore: CLDR data packaging
ramezgerges Sep 30, 2025
a379b98
chore: ICU loading for WASM
ramezgerges Sep 30, 2025
4a4abf7
chore: unoicu.a
ramezgerges Sep 30, 2025
277a774
chore: remove wasm Program.cs modifications
ramezgerges Sep 30, 2025
c091eab
chore: fix paths and naming
ramezgerges Sep 30, 2025
3c94a24
chore: adjust unoicu.a packaging
ramezgerges Oct 1, 2025
f9bdab4
chore: macos icu loading
ramezgerges Oct 6, 2025
67375a9
chore: reference Uno.icu-macos and adjust macos ICY loading
ramezgerges Oct 21, 2025
ca3a976
chore: add a reference to Uno.icu-wasm
ramezgerges Oct 21, 2025
b291f59
chore: refactor Uno.icu versioning
ramezgerges Oct 21, 2025
ed9e346
chore: adjust
ramezgerges Oct 21, 2025
7ae7861
chore: bump Uno.icu version
ramezgerges Oct 22, 2025
848fe23
chore: remove unnecessary Uno.icu files and references
ramezgerges Oct 22, 2025
006bca1
chore: selected text coloring
ramezgerges Oct 22, 2025
faa5c53
chore: cleanup
ramezgerges Oct 22, 2025
d112e3b
chore: fix TextBlock._hyperlinks
ramezgerges Oct 22, 2025
d5eaa26
chore: fix oob read
ramezgerges Oct 22, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/Directory.Build.targets
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
<MicrosoftTestingPlatformVersion>1.9.0</MicrosoftTestingPlatformVersion>
<SkiaSharpVersion>3.119.0</SkiaSharpVersion>
<HarfbuzzSharpVersion>8.3.1.1</HarfbuzzSharpVersion>
<UnoICUVersion>77.1.0-dev.44</UnoICUVersion>
</PropertyGroup>

<ItemGroup>
Expand Down Expand Up @@ -161,6 +162,12 @@
<PackageReference Update="Xamarin.AndroidX.SwipeRefreshLayout" Version="1.1.0.28" />
</ItemGroup>

<ItemGroup>
<PackageReference Update="Uno.icu-macos" Version="$(UnoICUVersion)" />
<PackageReference Update="Uno.icu-wasm" Version="$(UnoICUVersion)" />
<PackageReference Update="Uno.icu-ios" Version="$(UnoICUVersion)" />
</ItemGroup>

<Target Name="ValidateSolutionPath" BeforeTargets="Build">
<Error Condition="$(MSBuildThisFileDirectory.Contains(' '))"
Text="The Uno.UI Solution cannot build with a space in the path. Consider changing to a path without spaces."/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
<PackageReference Include="SkiaSharp" />
<PackageReference Include="HarfBuzzSharp" />
<PackageReference Include="SkiaSharp.Views" />
<PackageReference Include="Uno.icu-ios" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
<PackageReference Include="HarfBuzzSharp.NativeAssets.macOS" />
<PackageReference Include="HarfBuzzSharp" />
<PackageReference Include="SkiaSharp" />
<PackageReference Include="Uno.icu-macos" />
</ItemGroup>

<PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
<PackageReference Include="SkiaSharp" />
<PackageReference Include="HarfBuzzSharp" />
<PackageReference Include="Microsoft.TypeScript.MSBuild" PrivateAssets="all" />
<PackageReference Include="Uno.icu-wasm" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.106" />
<PackageReference Include="HarfBuzzSharp" />
<PackageReference Include="Microsoft.ICU.ICU4C.Runtime" Version="72.1.0.3" />
</ItemGroup>

<ItemGroup>
Expand Down
1 change: 1 addition & 0 deletions src/Uno.UI.Runtime.Skia.Wpf/Uno.UI.Runtime.Skia.Wpf.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

<ItemGroup>
<PackageReference Include="Microsoft.Web.WebView2" Aliases="WpfWebView" PrivateAssets="all" />
<PackageReference Include="Microsoft.ICU.ICU4C.Runtime" Version="72.1.0.3" />

<!--
Required to ensure that this reference is latest and not
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
using Windows.System;
using Windows.UI;
using Windows.UI.Input.Preview.Injection;
using Uno.UI.Xaml.Media;
using static Private.Infrastructure.TestServices;
using Color = Windows.UI.Color;
using Point = Windows.Foundation.Point;
Expand Down Expand Up @@ -2057,6 +2058,7 @@ public async Task When_Paste_History_Remains_Intact()
var text = "copied content";
dp.SetText(text);
Clipboard.SetContent(dp);
await Task.Delay(500);

// This actually matches WinUI. text comes before "initial" and text2 comes after text

Expand All @@ -2068,6 +2070,7 @@ public async Task When_Paste_History_Remains_Intact()
var text2 = "copied content 2";
dp2.SetText(text2);
Clipboard.SetContent(dp2);
await Task.Delay(500);

SUT.PasteFromClipboard();
await WindowHelper.WaitForIdle();
Expand Down Expand Up @@ -2737,7 +2740,7 @@ public async Task When_Multiline_Pointer_Tap()
using var mouse = injector.GetMouse();

var bounds = SUT.GetAbsoluteBounds();
mouse.MoveTo(bounds.GetCenter());
mouse.MoveTo(bounds.Location.Offset(145, 55));
await WindowHelper.WaitForIdle();

mouse.Press();
Expand Down Expand Up @@ -2955,8 +2958,8 @@ public async Task When_Multiline_Pointer_TripleTap()
mouse.Release();
await WindowHelper.WaitForIdle();

Assert.AreEqual(13, SUT.SelectionStart);
Assert.AreEqual(26, SUT.SelectionLength);
Assert.AreEqual(25, SUT.SelectionStart);
Assert.AreEqual(14, SUT.SelectionLength);
}

[TestMethod]
Expand Down Expand Up @@ -4213,10 +4216,10 @@ public async Task When_Caret_Color_DarkMode()
{
await Task.Delay(random.Next(75, 126));
var screenshot = await UITestHelper.ScreenShot(SUT);
// For some reason, the caret sometimes appears black, and sometimes as very dark grey (#FF030303), so we check for both
Color[] blacks = [Colors.Black, Colors.FromARGB(0xFF, 0x03, 0x03, 0x03)];
if (blacks.Any(b => HasColorInRectangle(screenshot, new Rectangle(0, 0, screenshot.Width / 2, screenshot.Height), b)) &&
blacks.All(b => !HasColorInRectangle(screenshot, new Rectangle(screenshot.Width / 2, 0, screenshot.Width / 2, screenshot.Height), Colors.Black)))
// this color is the result of alpha blending 0xE4000000 (the default caret color) on top of 0xFFFFFFFF
var black = Colors.White.AlphaBlend(((SolidColorBrush)DefaultBrushes.TextForegroundBrush).Color);
if (HasColorInRectangle(screenshot, new Rectangle(0, 0, screenshot.Width / 2, screenshot.Height), black) &&
!HasColorInRectangle(screenshot, new Rectangle(screenshot.Width / 2, 0, screenshot.Width / 2, screenshot.Height), black))
{
break;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ namespace Microsoft.UI.Xaml.Documents
public partial class Run : global::Microsoft.UI.Xaml.Documents.Inline
{
// Skipping already declared property Text
#if __ANDROID__ || __IOS__ || __TVOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "__TVOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__")]
#if __ANDROID__ || __IOS__ || __TVOS__ || IS_UNIT_TESTS || __WASM__ || __NETSTD_REFERENCE__
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "__TVOS__", "IS_UNIT_TESTS", "__WASM__", "__NETSTD_REFERENCE__")]
public global::Microsoft.UI.Xaml.FlowDirection FlowDirection
{
get
Expand All @@ -23,8 +23,8 @@ public partial class Run : global::Microsoft.UI.Xaml.Documents.Inline
}
}
#endif
#if __ANDROID__ || __IOS__ || __TVOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "__TVOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__")]
#if __ANDROID__ || __IOS__ || __TVOS__ || IS_UNIT_TESTS || __WASM__ || __NETSTD_REFERENCE__
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "__TVOS__", "IS_UNIT_TESTS", "__WASM__", "__NETSTD_REFERENCE__")]
public static global::Microsoft.UI.Xaml.DependencyProperty FlowDirectionProperty { get; } =
Microsoft.UI.Xaml.DependencyProperty.Register(
nameof(FlowDirection), typeof(global::Microsoft.UI.Xaml.FlowDirection),
Expand Down
54 changes: 16 additions & 38 deletions src/Uno.UI/UI/Xaml/Controls/TextBlock/TextBlock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public partial class TextBlock : DependencyObject, IThemeChangeAware

// end can be less than or equal to start when the selection starts ahead and then goes back
// see the selection in TextBox.skia.cs for more info
private Range Selection
internal Range Selection
{
get => _selection;
set
Expand Down Expand Up @@ -153,7 +153,7 @@ internal void InvalidateInlines(bool updateText)
}

UpdateHyperlinks();
Inlines.InvalidatePreorderTree();
Inlines.InvalidateTraversedTree();
}

OnInlinesChangedPartial();
Expand Down Expand Up @@ -1095,8 +1095,8 @@ private bool AbortHyperlinkCaptures(Pointer pointer)
var aborted = false;
foreach (var hyperlink in _hyperlinks.ToList()) // .ToList() : for a strange reason on WASM the collection gets modified
{
aborted |= hyperlink.hyperlink.AbortPointerPressed(pointer);
aborted |= hyperlink.hyperlink.ReleasePointerOver(pointer);
aborted |= hyperlink.AbortPointerPressed(pointer);
aborted |= hyperlink.ReleasePointerOver(pointer);
}

aborted |= _hyperlinkOver?.ReleasePointerOver(pointer) ?? false;
Expand All @@ -1105,7 +1105,7 @@ private bool AbortHyperlinkCaptures(Pointer pointer)
return aborted;
}

private readonly ObservableCollection<(int start, int end, Hyperlink hyperlink)> _hyperlinks = new();
private readonly ObservableCollection<Hyperlink> _hyperlinks = new();

private void HyperlinksOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) => RecalculateSubscribeToPointerEvents();

Expand All @@ -1120,7 +1120,7 @@ private void RecalculateSubscribeToPointerEvents()

private void UpdateHyperlinks()
{
global::System.Diagnostics.Debug.Assert(_hyperlinkOver is null || _hyperlinks.Where(h => h.hyperlink == _hyperlinkOver).Count() == 1);
global::System.Diagnostics.Debug.Assert(_hyperlinkOver is null || _hyperlinks.Count(h => h == _hyperlinkOver) == 1);

if (UseInlinesFastPath) // i.e. no Inlines
{
Expand All @@ -1129,7 +1129,7 @@ private void UpdateHyperlinks()
// Make sure to clear the pressed state of removed hyperlinks
foreach (var hyperlink in _hyperlinks)
{
hyperlink.hyperlink.AbortAllPointerState();
hyperlink.AbortAllPointerState();
}

_hyperlinkOver = null;
Expand All @@ -1139,26 +1139,13 @@ private void UpdateHyperlinks()
return;
}

var previousHyperLinks = _hyperlinks.SelectToList(hyperlink => hyperlink.hyperlink);

_hyperlinkOver = null;
var previousHyperLinks = _hyperlinks.ToHashSet();
_hyperlinks.Clear();

var start = 0;
foreach (var inline in Inlines.PreorderTree)
foreach (var hyperlink in Inlines.TraversedTree.preorderTree.OfType<Hyperlink>())
{
switch (inline)
{
case Hyperlink hyperlink:
previousHyperLinks.Remove(hyperlink);
_hyperlinks.Add((start, start + hyperlink.GetText().Length, hyperlink));
break;
case Span span:
break;
default: // Leaf node
start += inline.GetText().Length;
break;
}
_hyperlinks.Add(hyperlink);
previousHyperLinks.Remove(hyperlink);
}

// Make sure to clear the pressed state of removed hyperlinks
Expand Down Expand Up @@ -1242,14 +1229,10 @@ private Hyperlink FindHyperlinkAt(PointerRoutedEventArgs e)
}

return null;
#elif __SKIA__
return ParsedText.GetHyperlinkAt(e.GetCurrentPoint(this).Position);
#else
var point = e.GetCurrentPoint(this).Position;
var characterIndex = GetCharacterIndexAtPoint(point);
var hyperlink = _hyperlinks
.FirstOrDefault(h => h.start <= characterIndex && h.end > characterIndex)
.hyperlink;

return hyperlink;
return null;
#endif
}
#endregion
Expand Down Expand Up @@ -1298,13 +1281,6 @@ internal override void UpdateThemeBindings(Data.ResourceUpdateReason updateReaso
/*IsEnabled() &&*/ (IsTextSelectionEnabled || IsTabStop || FocusProperties.GetCaretBrowsingModeEnable()) &&
AreAllAncestorsVisible();

private record struct Range(int start, int end)
{
public Range((int start, int end) tuple) : this(tuple.start, tuple.end)
{
}
}

[SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "Used only by some platforms")]
private bool IsTextTrimmable =>
TextTrimming != TextTrimming.None ||
Expand All @@ -1316,5 +1292,7 @@ public Range((int start, int end) tuple) : this(tuple.start, tuple.end)
// The way this works in WinUI is by the MarkInheritedPropertyDirty call in CFrameworkElement::NotifyThemeChangedForInheritedProperties
// There is a special handling for Foreground specifically there.
void IThemeChangeAware.OnThemeChanged() => OnForegroundChanged();

internal record struct Range(int start, int end);
}
}
Loading
Loading