Implement Clipboard Essentials API#171
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds missing “Essentials” building blocks for the Avalonia backend by implementing cross-platform Clipboard support and a MainThread-compatible dispatcher API (with an MSBuild alias to keep existing MainThread.* call sites working on Avalonia TFMs).
Changes:
- Implement
AvaloniaClipboardwith desktop (TopLevel clipboard + window activation polling) and browser (JSImportnavigator.clipboard) backends. - Add
AvaloniaMainThreadbacked byDispatcher.UIThread, plus a conditionalMainThreadalias onnet11.0/net11.0-browser. - Add a ControlGallery sample page and basic unit tests/documentation updates.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/Avalonia.Controls.Maui.Essentials.Tests/AvaloniaClipboardTests.cs | Adds basic null-TopLevel and event-subscription tests for clipboard. |
| STATUS-ESSENTIALS.md | Marks Clipboard + AvaloniaMainThread APIs as implemented and updates MainThread note. |
| src/Avalonia.Controls.Maui.targets | Adds conditional MSBuild Using alias mapping MainThread → AvaloniaMainThread for Avalonia TFMs. |
| src/Avalonia.Controls.Maui.Essentials/README.md | Documents Clipboard and MainThread availability/usage guidance. |
| src/Avalonia.Controls.Maui.Essentials/MauiEssentialsBuilderExtensions.cs | Registers Clipboard.Default with the Avalonia implementation. |
| src/Avalonia.Controls.Maui.Essentials/MainThread/AvaloniaMainThread.cs | Introduces dispatcher-backed MainThread-compatible API surface. |
| src/Avalonia.Controls.Maui.Essentials/Clipboard/ClipboardInterop.Browser.cs | Adds JSImport bindings for navigator.clipboard APIs. |
| src/Avalonia.Controls.Maui.Essentials/Clipboard/AvaloniaClipboard.cs | Adds MAUI IClipboard implementation and shared state/event wiring. |
| src/Avalonia.Controls.Maui.Essentials/Clipboard/AvaloniaClipboard.Default.cs | Desktop clipboard implementation using TopLevel.Clipboard + activation checks. |
| src/Avalonia.Controls.Maui.Essentials/Clipboard/AvaloniaClipboard.Browser.cs | Browser clipboard implementation using JSImport interop. |
| samples/ControlGallery/ControlGallery/Pages/ClipboardPage.xaml | Adds UI for clipboard copy/paste/HasText/event demo. |
| samples/ControlGallery/ControlGallery/Pages/ClipboardPage.xaml.cs | Wires the sample page to Clipboard + MainThread APIs. |
| samples/ControlGallery/ControlGallery/MainPage.xaml.cs | Registers the new Clipboard sample in the gallery navigation. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
drasticactions
left a comment
There was a problem hiding this comment.
We shouldn't introduce a hack for MainThread, but Clipboard is fine. If you remove the MainThread code, then I'm 👍 .
| if (_platformProvider.GetTopLevel() is Window window) | ||
| { | ||
| window.Activated += OnWindowActivated; | ||
| _isSubscribed = true; | ||
| } | ||
| } |
There was a problem hiding this comment.
I'm not sure if this is correct. EnsureSubscribedToWindowActivation runs whenever GetClipboard is called, which occurs when a user explicitly calls a clipboard API. If they had an event handle for ClipboardContentChanged, it will only ever run if they happen to call one of these methods first.
There was a problem hiding this comment.
EnsureSubscribed now runs from the ClipboardContentChanged add accessor instead of GetClipboard.
|
|
||
| if (_platformProvider.GetTopLevel() is Window window) | ||
| { | ||
| window.Activated += OnWindowActivated; |
There was a problem hiding this comment.
window.Activated is attached to, but nothing disposes of it.
There was a problem hiding this comment.
Fixed. Applied changes and storing the subscribed Window reference and unsubscribing both Activated and Closed handlers in OnWindowClosed.
| UpdateState(currentText); | ||
|
|
||
| if (changed) | ||
| ClipboardContentChanged?.Invoke(this, EventArgs.Empty); |
There was a problem hiding this comment.
If you're in the app and you copy text from another text field, without activating the window, this event won't get invoked. It only activates when the window is activated.
| <!-- Avalonia.FreeDesktop still declares the vulnerable version; force the patched one. --> | ||
| <PackageReference Include="Tmds.DBus.Protocol" Version="0.92.0" /> | ||
| </ItemGroup> | ||
|
|
There was a problem hiding this comment.
This shouldn't be needed with the version bumps made elsewhere, and if something like this happens in the future, we should always fix the root versions rather than work around them.
| } | ||
|
|
||
| /// <inheritdoc/> | ||
| public bool HasText => _hasText; |
There was a problem hiding this comment.
Checking in the maui repo, these values are computed when called via OS values, (Ex, https://github.com/dotnet/maui/blob/388b82f166d937c69a4553556b0042f441eadcd6/src/Essentials/src/Clipboard/Clipboard.ios.cs#L20-L21)
Here, it's cached, so as far as I can tell, SetTextAsync, GetTextAsync, or Activated would need to be called to update the value. Since Activate should be called when you touch the window, that might be fine, but there might be a better way to handle this. @maxkatz6 @MrJul, what do you think about this?
There was a problem hiding this comment.
With async-only nature of current clipboard implementation, it would be hard to implement something like this.
On desktop, you we can use DispatcherFrame hack to (probably) safely run async code in-sync.
Otherwise, always returning true might be the safest bet (there is never a guarantee clipboard is not cleared between HasText and GeText calls anyway).
We need to have a new API on the Avalonia side, for clipboard changed events.
There was a problem hiding this comment.
We need to have a new API on the Avalonia side, for clipboard changed events.
Yeah, that sounds good. It may be best to hold off on this PR for now and implement it in Avalonia first, then here. I don't think this is so important that we need to hack around it right now for the sake of the previews. We have time to implement it properly in Avalonia and use that instead.
Changes:
IClipboard): Full implementation with platform-specific paths for desktop (Avalonia TopLevel.Clipboard + Window.Activated change detection) and browser (navigator.clipboard via [JSImport])Tested:
