Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: WASM TextBlock SelectAll and CopySelectionToClipboard #16538

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System.Linq;
using System;
using System.Linq;
using System.Threading.Tasks;
using Uno.Helpers;
using Uno.UI.RuntimeTests.Helpers;
using Windows.Foundation.Metadata;
using Windows.UI;
Expand All @@ -9,7 +9,6 @@
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Documents;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Media.Imaging;
using FluentAssertions;
using static Private.Infrastructure.TestServices;
using System.Collections.Generic;
Expand All @@ -18,10 +17,9 @@
using Uno.Extensions;
using Point = Windows.Foundation.Point;
using Size = Windows.Foundation.Size;
using Windows.ApplicationModel.DataTransfer;

#if __SKIA__
using System;
using Windows.ApplicationModel.DataTransfer;
using Windows.System;
using Windows.UI.Input.Preview.Injection;
using SkiaSharp;
Expand Down Expand Up @@ -243,6 +241,20 @@ public async Task When_Multiline_Wrapping_Text_Ends_In_Too_Many_Spaces()
Assert.AreEqual(height, SUT.ActualHeight);
}

[TestMethod]
public async Task Copy_And_Paste_All_Text()
{
var SUT = new TextBlock { Text = "Some text", IsTextSelectionEnabled = true };
WindowHelper.WindowContent = SUT;
await WindowHelper.WaitForLoaded(SUT);
SUT.SelectAll();
SUT.CopySelectionToClipboard();
await WindowHelper.WaitForIdle();
var dataPackage = Clipboard.GetContent();
var text = await dataPackage.GetTextAsync();
Assert.AreEqual("Some text", text);
}

[TestMethod]
[RunsOnUIThread]
#if !__SKIA__
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -366,8 +366,8 @@ public double BaselineOffset
// Forced skipping of method Microsoft.UI.Xaml.Controls.TextBlock.ContextMenuOpening.remove
// Forced skipping of method Microsoft.UI.Xaml.Controls.TextBlock.IsTextTrimmedChanged.add
// Forced skipping of method Microsoft.UI.Xaml.Controls.TextBlock.IsTextTrimmedChanged.remove
#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || false || __NETSTD_REFERENCE__ || __MACOS__
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__NETSTD_REFERENCE__", "__MACOS__")]
#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || false || false || false || __MACOS__
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__MACOS__")]
public void SelectAll()
{
global::Windows.Foundation.Metadata.ApiInformation.TryRaiseNotImplemented("Microsoft.UI.Xaml.Controls.TextBlock", "void TextBlock.SelectAll()");
Expand All @@ -387,8 +387,8 @@ public void Select(global::Microsoft.UI.Xaml.Documents.TextPointer start, global
throw new global::System.NotImplementedException("The member CompositionBrush TextBlock.GetAlphaMask() is not implemented. For more information, visit https://aka.platform.uno/notimplemented#m=CompositionBrush%20TextBlock.GetAlphaMask%28%29");
}
#endif
#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || false || __NETSTD_REFERENCE__ || __MACOS__
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__NETSTD_REFERENCE__", "__MACOS__")]
#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || false || false || false || __MACOS__
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__MACOS__")]
public void CopySelectionToClipboard()
{
global::Windows.Foundation.Metadata.ApiInformation.TryRaiseNotImplemented("Microsoft.UI.Xaml.Controls.TextBlock", "void TextBlock.CopySelectionToClipboard()");
Expand Down Expand Up @@ -438,16 +438,16 @@ public void CopySelectionToClipboard()
}
#endif
// Skipping already declared event Microsoft.UI.Xaml.Controls.TextBlock.IsTextTrimmedChanged
#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __MACOS__
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__MACOS__")]
public event global::Microsoft.UI.Xaml.RoutedEventHandler SelectionChanged
{
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__MACOS__")]
add
{
global::Windows.Foundation.Metadata.ApiInformation.TryRaiseNotImplemented("Microsoft.UI.Xaml.Controls.TextBlock", "event RoutedEventHandler TextBlock.SelectionChanged");
}
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__MACOS__")]
remove
{
global::Windows.Foundation.Metadata.ApiInformation.TryRaiseNotImplemented("Microsoft.UI.Xaml.Controls.TextBlock", "event RoutedEventHandler TextBlock.SelectionChanged");
Expand Down
20 changes: 20 additions & 0 deletions src/Uno.UI/UI/Xaml/Controls/TextBlock/TextBlock.Interop.wasm.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.InteropServices.JavaScript;
using System.Text;
using System.Threading.Tasks;

namespace __Uno.UI.Xaml.Controls;
internal partial class TextBlock
{
internal static partial class NativeMethods
{
[JSImport("globalThis.Microsoft.UI.Xaml.Controls.TextBlock.select")]
internal static partial bool Select(IntPtr htmlId, int start, int length);

[JSImport("globalThis.Microsoft.UI.Xaml.Controls.TextBlock.getSelectedText")]
internal static partial string GetSelectedText(IntPtr htmlId);
}
}
22 changes: 15 additions & 7 deletions src/Uno.UI/UI/Xaml/Controls/TextBlock/TextBlock.crossruntime.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
using System;
using System.Collections.Generic;
using Uno.UI;
using Windows.ApplicationModel.DataTransfer;

namespace Microsoft.UI.Xaml.Controls
namespace Microsoft.UI.Xaml.Controls;

partial class TextBlock
{
partial class TextBlock
{
internal override bool IsViewHit() => Text != null || base.IsViewHit();
}
/// <summary>
/// Occurs when the text selection has changed.
/// </summary>
public event RoutedEventHandler SelectionChanged;

/// <summary>
/// Selects the entire contents in the TextBlock.
/// </summary>
public void SelectAll() => Selection = new Range(0, Text.Length);

internal override bool IsViewHit() => Text != null || base.IsViewHit();
}
2 changes: 2 additions & 0 deletions src/Uno.UI/UI/Xaml/Controls/TextBlock/TextBlock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ private Range Selection
_selection = value;

OnSelectionChanged();

SelectionChanged?.Invoke(this, new(this));
}
}

Expand Down
7 changes: 4 additions & 3 deletions src/Uno.UI/UI/Xaml/Controls/TextBlock/TextBlock.skia.cs
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,9 @@ private void OnRightTapped(RightTappedRoutedEventArgs e)
}
}

/// <summary>
/// Copies the selected content to the Windows clipboard.
/// </summary>
public void CopySelectionToClipboard()
{
if (Selection.start != Selection.end)
Expand All @@ -345,9 +348,7 @@ public void CopySelectionToClipboard()
}
}

public void SelectAll() => Selection = new Range(0, Text.Length);

// TODO: move to TextBlock.cs when we implement SelectionHighlightColor for the other platforms
// TODO: move to TextBlock.cs when we implement SelectionHighlightColor for the other platform
public SolidColorBrush SelectionHighlightColor
{
get => (SolidColorBrush)GetValue(SelectionHighlightColorProperty);
Expand Down
34 changes: 25 additions & 9 deletions src/Uno.UI/UI/Xaml/Controls/TextBlock/TextBlock.wasm.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,10 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using Windows.Foundation;
using Microsoft.UI.Xaml.Documents;
using Uno.Extensions;
using Uno.Foundation;
using System.Linq;

using Windows.UI.Text;
using Microsoft.UI.Xaml.Media;
using Uno.UI;
using Uno.UI.Xaml;
using Windows.ApplicationModel.DataTransfer;
using Windows.Foundation;
using static __Uno.UI.Xaml.Controls.TextBlock;

#if !HAS_UNO_WINUI
using Microsoft/* UWP don't rename */.UI.Xaml.Media;
Expand Down Expand Up @@ -72,6 +66,20 @@ private void ConditionalUpdate(ref bool condition, Action action)
}
}

/// <summary>
/// Copies the selected content to the Windows clipboard.
/// </summary>
public void CopySelectionToClipboard()
{
if (Selection.start != Selection.end)
{
var text = NativeMethods.GetSelectedText(HtmlId);
var dataPackage = new DataPackage();
dataPackage.SetText(text);
Clipboard.SetContent(dataPackage);
}
}

private void SynchronizeHtmlParagraphAttributes()
{
ConditionalUpdate(ref _fontStyleChanged, () => this.SetFontStyle(FontStyle));
Expand Down Expand Up @@ -226,5 +234,13 @@ partial void UpdateIsTextTrimmed()
IsTextTrimmable &&
WindowManagerInterop.GetIsOverflowing(HtmlId);
}

partial void OnSelectionChanged()
{
if (IsTextSelectionEnabled && Selection.start != Selection.end)
{
NativeMethods.Select(HtmlId, Selection.start, Selection.end - Selection.start);
}
}
}
}
28 changes: 28 additions & 0 deletions src/Uno.UI/ts/Windows/UI/Xaml/Controls/TextBlock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
namespace Microsoft.UI.Xaml.Controls {
export class TextBlock {
public static getSelectedText(htmlId: number): string {
var element = document.getElementById(htmlId.toString());

Check warning on line 4 in src/Uno.UI/ts/Windows/UI/Xaml/Controls/TextBlock.ts

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/Uno.UI/ts/Windows/UI/Xaml/Controls/TextBlock.ts#L4

Identifier 'element' is never reassigned; use 'const' instead of 'var'.
if (element) {
var selection = window.getSelection();

Check warning on line 6 in src/Uno.UI/ts/Windows/UI/Xaml/Controls/TextBlock.ts

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/Uno.UI/ts/Windows/UI/Xaml/Controls/TextBlock.ts#L6

Identifier 'selection' is never reassigned; use 'const' instead of 'var'.
if (selection && selection.rangeCount > 0) {
var range = selection.getRangeAt(0);

Check warning on line 8 in src/Uno.UI/ts/Windows/UI/Xaml/Controls/TextBlock.ts

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/Uno.UI/ts/Windows/UI/Xaml/Controls/TextBlock.ts#L8

Identifier 'range' is never reassigned; use 'const' instead of 'var'.
if (element.contains(range.startContainer) && element.contains(range.endContainer)) {
return range.toString();
}
}
}
}

public static select(htmlId : number, from: number, length: number): void {
var element = document.getElementById(htmlId.toString());
if (element) {
var selection = window.getSelection();
var range = document.createRange();

Check warning on line 20 in src/Uno.UI/ts/Windows/UI/Xaml/Controls/TextBlock.ts

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/Uno.UI/ts/Windows/UI/Xaml/Controls/TextBlock.ts#L20

Identifier 'range' is never reassigned; use 'const' instead of 'var'.
range.setStart(element.childNodes[0], from);
range.setEnd(element.childNodes[0], from + length);
selection.removeAllRanges();
selection.addRange(range);
}
}
}
}
Loading