Skip to content

Commit

Permalink
feat(textblock): IsTextTrimmed & IsTextTrimmedChanged
Browse files Browse the repository at this point in the history
  • Loading branch information
Xiaoy312 committed Dec 7, 2023
1 parent 72dfb91 commit e1523f8
Show file tree
Hide file tree
Showing 9 changed files with 165 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
using static Private.Infrastructure.TestServices;
using System.Collections.Generic;

namespace Uno.UI.RuntimeTests.Tests.Windows_UI_Xaml_Controls
{
Expand Down Expand Up @@ -367,5 +368,63 @@ public async Task When_Empty_TextBlocks_Stacked()
previousOrigin = textBlockOrigin;
}
}

#if !__MACOS__
[TestMethod]
[RunsOnUIThread]
public async Task When_TextTrimming()
{
var sut = new TextBlock
{
Text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
TextTrimming = TextTrimming.Clip,
};
var container = new Border
{
BorderThickness = new Thickness(1),
BorderBrush = new SolidColorBrush(Colors.Red),
Width = 100,
Child = sut,
};

var states = new List<bool>();
sut.IsTextTrimmedChanged += (s, e) => states.Add(sut.IsTextTrimmed);

WindowHelper.WindowContent = container;
await WindowHelper.WaitForLoaded(container);
await WindowHelper.WaitForIdle(); // necessary on ios, since the container finished loading before the text is drawn

Assert.IsTrue(sut.IsTextTrimmed, "IsTextTrimmed should be trimmed.");
Assert.IsTrue(states.Count == 1 && states[0] == true, $"IsTextTrimmedChanged should only proc once for IsTextTrimmed=true. states: {(string.Join(", ", states) is string { Length: > 0 } tmp ? tmp : "(-empty-)")}");
}

[TestMethod]
[RunsOnUIThread]
public async Task When_TextTrimmingNone()
{
var sut = new TextBlock
{
Text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
TextTrimming = TextTrimming.None,
};
var container = new Border
{
BorderThickness = new Thickness(1),
BorderBrush = new SolidColorBrush(Colors.Red),
Width = 100,
Child = sut,
};

var states = new List<bool>();
sut.IsTextTrimmedChanged += (s, e) => states.Add(sut.IsTextTrimmed);

WindowHelper.WindowContent = container;
await WindowHelper.WaitForLoaded(container);
await WindowHelper.WaitForIdle(); // necessary on ios, since the container finished loading before the text is drawn

Assert.IsFalse(sut.IsTextTrimmed, "IsTextTrimmed should not be trimmed.");
Assert.IsTrue(states.Count == 0, $"IsTextTrimmedChanged should not proc at all. states: {(string.Join(", ", states) is string { Length: > 0 } tmp ? tmp : "(-empty-)")}");
}
#endif
}
}
37 changes: 3 additions & 34 deletions src/Uno.UI/Generated/3.0.0.0/Windows.UI.Xaml.Controls/TextBlock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -184,16 +184,7 @@ public bool IsTextScaleFactorEnabled
#endif
// Skipping already declared property TextDecorations
// Skipping already declared property HorizontalTextAlignment
#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
public bool IsTextTrimmed
{
get
{
return (bool)this.GetValue(IsTextTrimmedProperty);
}
}
#endif
// Skipping already declared property IsTextTrimmed
#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
public global::System.Collections.Generic.IList<global::Windows.UI.Xaml.Documents.TextHighlighter> TextHighlighters
Expand Down Expand Up @@ -299,14 +290,7 @@ public bool IsTextTrimmed
#endif
// Skipping already declared property TextDecorationsProperty
// Skipping already declared property HorizontalTextAlignmentProperty
#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
public static global::Windows.UI.Xaml.DependencyProperty IsTextTrimmedProperty { get; } =
Windows.UI.Xaml.DependencyProperty.Register(
nameof(IsTextTrimmed), typeof(bool),
typeof(global::Windows.UI.Xaml.Controls.TextBlock),
new Windows.UI.Xaml.FrameworkPropertyMetadata(default(bool)));
#endif
// Skipping already declared property IsTextTrimmed
#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
public static global::Windows.UI.Xaml.DependencyProperty SelectionFlyoutProperty { get; } =
Expand Down Expand Up @@ -470,21 +454,6 @@ public void CopySelectionToClipboard()
}
}
#endif
#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
public event global::Windows.Foundation.TypedEventHandler<global::Windows.UI.Xaml.Controls.TextBlock, global::Windows.UI.Xaml.Controls.IsTextTrimmedChangedEventArgs> IsTextTrimmedChanged
{
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
add
{
global::Windows.Foundation.Metadata.ApiInformation.TryRaiseNotImplemented("Windows.UI.Xaml.Controls.TextBlock", "event TypedEventHandler<TextBlock, IsTextTrimmedChangedEventArgs> TextBlock.IsTextTrimmedChanged");
}
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
remove
{
global::Windows.Foundation.Metadata.ApiInformation.TryRaiseNotImplemented("Windows.UI.Xaml.Controls.TextBlock", "event TypedEventHandler<TextBlock, IsTextTrimmedChangedEventArgs> TextBlock.IsTextTrimmedChanged");
}
}
#endif
// Skipping already declared event Microsoft.UI.Xaml.Controls.TextBlock.IsTextTrimmedChanged
}
}
10 changes: 10 additions & 0 deletions src/Uno.UI/UI/Xaml/Controls/TextBlock/TextBlock.Android.cs
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,8 @@ protected override Size ArrangeOverride(Size finalSize)
UpdateNativeTextBlockLayout();
}

UpdateIsTextTrimmed();

return finalSize;
}
}
Expand Down Expand Up @@ -460,6 +462,14 @@ private Size UpdateLayout(ref LayoutBuilder layout, Size availableSize, bool exa
return layout.MeasuredSize;
}

partial void UpdateIsTextTrimmed()
{
IsTextTrimmed = IsTextTrimmable && (
_measureLayout.MeasuredSize.Width > _arrangeLayout.MeasuredSize.Width ||
_measureLayout.MeasuredSize.Height > _arrangeLayout.MeasuredSize.Height
);
}

/// <summary>
/// A layout builder, used to perform change tracking and avoid creating the same layout multiple times.
/// </summary>
Expand Down
43 changes: 43 additions & 0 deletions src/Uno.UI/UI/Xaml/Controls/TextBlock/TextBlock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Uno.Disposables;
using Uno.Extensions;
Expand Down Expand Up @@ -672,6 +673,40 @@ private void OnTextDecorationsChanged()

#endregion

#region DependencyProperty: IsTextTrimmed
#if false || false || IS_UNIT_TESTS || false || false || __NETSTD_REFERENCE__ || __MACOS__
[NotImplemented("IS_UNIT_TESTS", "__NETSTD_REFERENCE__", "__MACOS__")]
#endif
public event TypedEventHandler<TextBlock, IsTextTrimmedChangedEventArgs> IsTextTrimmedChanged;

#if false || false || IS_UNIT_TESTS || false || false || __NETSTD_REFERENCE__ || __MACOS__
[NotImplemented("IS_UNIT_TESTS", "__NETSTD_REFERENCE__", "__MACOS__")]
#endif
public static DependencyProperty IsTextTrimmedProperty { get; } = DependencyProperty.Register(
nameof(IsTextTrimmed),
typeof(bool),
typeof(TextBlock),
new FrameworkPropertyMetadata(false, propertyChangedCallback: (s, e) => ((TextBlock)s).OnIsTextTrimmedChanged()));

#if false || false || IS_UNIT_TESTS || false || false || __NETSTD_REFERENCE__ || __MACOS__
[NotImplemented("IS_UNIT_TESTS", "__NETSTD_REFERENCE__", "__MACOS__")]
#endif
public bool IsTextTrimmed
{
get => (bool)GetValue(IsTextTrimmedProperty);
private set => SetValue(IsTextTrimmedProperty, value);
}

private void OnIsTextTrimmedChanged()
{
OnIsTextTrimmedChangedPartial();
IsTextTrimmedChanged?.Invoke(this, new());
}

partial void OnIsTextTrimmedChangedPartial();

#endregion

/// <summary>
/// Gets whether the TextBlock is using the fast path in which Inlines
/// have not been initialized and don't need to be synchronized.
Expand Down Expand Up @@ -1080,5 +1115,13 @@ internal override void UpdateThemeBindings(Data.ResourceUpdateReason updateReaso
IsVisible() &&
/*IsEnabled() &&*/ (IsTextSelectionEnabled || IsTabStop) &&
AreAllAncestorsVisible();

[SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "Used only by some platforms")]
private bool IsTextTrimmable =>
TextTrimming != TextTrimming.None ||
MaxLines != 0;

[SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "Used only by some platforms")]
partial void UpdateIsTextTrimmed();
}
}
10 changes: 10 additions & 0 deletions src/Uno.UI/UI/Xaml/Controls/TextBlock/TextBlock.iOS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ public override void Draw(CGRect rect)
{
_attributedString?.DrawString(_drawRect, NSStringDrawingOptions.UsesLineFragmentOrigin, null);
}

UpdateIsTextTrimmed();
}

/// <summary>
Expand Down Expand Up @@ -344,5 +346,13 @@ private int GetCharacterIndexAtPoint(Point point)

return characterIndex;
}

partial void UpdateIsTextTrimmed()
{
IsTextTrimmed = IsTextTrimmable && (
_attributedString.Size.Width > _drawRect.Size.Width ||
_attributedString.Size.Height > _drawRect.Size.Height
);
}
}
}
14 changes: 13 additions & 1 deletion src/Uno.UI/UI/Xaml/Controls/TextBlock/TextBlock.skia.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,11 @@ protected override Size ArrangeOverride(Size finalSize)
_textVisual.Size = new Vector2((float)arrangedSizeWithoutPadding.Width, (float)arrangedSizeWithoutPadding.Height);
_textVisual.Offset = new Vector3((float)padding.Left, (float)padding.Top, 0);
ApplyFlowDirection((float)finalSize.Width);
return base.ArrangeOverride(finalSize);

var result = base.ArrangeOverride(finalSize);
UpdateIsTextTrimmed();

return result;
}

/// <summary>
Expand Down Expand Up @@ -169,5 +173,13 @@ partial void OnLineStackingStrategyChangedPartial()
{
Inlines.InvalidateMeasure();
}

partial void UpdateIsTextTrimmed()
{
IsTextTrimmed = IsTextTrimmable && (
(_textVisual.Size.X + Padding.Left + Padding.Right) > ActualWidth ||
(_textVisual.Size.Y + Padding.Top + Padding.Bottom) > ActualHeight
);
}
}
}
15 changes: 15 additions & 0 deletions src/Uno.UI/UI/Xaml/Controls/TextBlock/TextBlock.wasm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Windows.UI.Text;
using Windows.UI.Xaml.Media;
using Uno.UI;
using Uno.UI.Xaml;

namespace Windows.UI.Xaml.Controls
{
Expand Down Expand Up @@ -150,6 +151,13 @@ protected override Size ArrangeOverride(Size finalSize)
return base.ArrangeOverride(arrangeSize);
}

internal override void OnLayoutUpdated()
{
base.OnLayoutUpdated();

UpdateIsTextTrimmed();
}

partial void OnFontStyleChangedPartial() => _fontStyleChanged = true;

partial void OnFontWeightChangedPartial() => _fontWeightChanged = true;
Expand Down Expand Up @@ -196,5 +204,12 @@ partial void OnTextChangedPartial()
partial void OnTextWrappingChangedPartial() => _textWrappingChanged = true;

partial void OnPaddingChangedPartial() => _paddingChangedChanged = true;

partial void UpdateIsTextTrimmed()
{
IsTextTrimmed =
IsTextTrimmable &&
WindowManagerInterop.GetIsOverflowing(HtmlId);
}
}
}
7 changes: 7 additions & 0 deletions src/Uno.UI/UI/Xaml/WindowManagerInterop.wasm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1056,6 +1056,9 @@ internal static void WindowActivate()
NativeMethods.WindowActivate();
}

internal static bool GetIsOverflowing(IntPtr htmlId)
=> NativeMethods.GetIsOverflowing(htmlId);

#region Pointers
[Flags]
internal enum HtmlPointerButtonsState
Expand All @@ -1082,6 +1085,7 @@ internal enum HtmlPointerButtonUpdate
Eraser = 5
}
#endregion

internal static partial class NativeMethods
{
[JSImport("globalThis.Uno.UI.WindowManager.current.arrangeElementNativeFast")]
Expand Down Expand Up @@ -1177,6 +1181,9 @@ internal static partial void ArrangeElement(

[JSImport("globalThis.Uno.UI.WindowManager.current.activate")]
internal static partial void WindowActivate();

[JSImport("globalThis.Uno.UI.WindowManager.current.getIsOverflowing")]
internal static partial bool GetIsOverflowing(IntPtr htmlId);
}
}
}
5 changes: 5 additions & 0 deletions src/Uno.UI/ts/WindowManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1780,6 +1780,11 @@ namespace Uno.UI {
(this.getView(elementId) as HTMLInputElement).setSelectionRange(start, start + length);
}

public getIsOverflowing(elementId: number): boolean {
const element = this.getView(elementId) as HTMLElement;

return element.clientWidth < element.scrollWidth || element.clientHeight < element.scrollHeight;
}
}

if (typeof define === "function") {
Expand Down

0 comments on commit e1523f8

Please sign in to comment.