AvaloniaUI / Avalonia

Develop Desktop, Embedded, Mobile and WebAssembly apps with C# and XAML. The most popular .NET UI client technology
https://avaloniaui.net
MIT License
25.25k stars 2.19k forks source link

Spell-check for TextBox and derived controls #9148

Open timunie opened 1 year ago

timunie commented 1 year ago

Is your feature request related to a problem? Please describe. In WPF you can have a basic Spell-check, see : https://learn.microsoft.com/en-us/dotnet/api/system.windows.controls.spellcheck . In Avalonia this is missing as for now.

Describe the solution you'd like Implement the ability to add spell-check. One may use LibreOffice spelling dictionaries for example. See https://wiki.documentfoundation.org/Development/Dictionaries

Describe alternatives you've considered Add a highlight layer the developer can use to render their own highlight, something like https://github.com/dotnet/wpf/blob/main/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/Highlights.cs

Additional context from our telegram chat with @Gillibald :

We need a dedicated highlight layer to display regions of interest in within the text layout.

I guess this will be easier to implement with the TextContainer as a backing store of current edit state.

WPF has a TextStore that is used to communicate with Windows text services.

DJGosnell commented 8 months ago

Can this just use the adorer layer for the text underlining? I'm just not sure the best method to add a dynamic context menu. Should it be integrated into the current TextBox Copy/Cut/Paste context menu or be something completely separate?

maxkatz6 commented 8 months ago

Relevant API in UWP. This flyout can't be replaced, and populated by the framework there, but developers can edit it by adding custom MenuItems to the same flyout.

https://learn.microsoft.com/en-us/uwp/api/windows.ui.xaml.controls.textbox.proofingmenuflyout?view=winrt-22621 https://stackoverflow.com/questions/72147570/flyout-for-autocomplete-in-winui-3-textbox

DJGosnell commented 8 months ago

Is having a completely customizable Avalonia Flyout what we want to use here or have a ProofingMenuItems property collection where it can be populated dynamically by Avalonia or the end user? I guess the question is: "How closely do we want to follow UWP?"

There are multiple ways this is handled and personally I've found when the spell checks are buried under a sub-menu, it can be cumbersome unless it auto-opens. Applications for reference: UWP gFRWV FireFox image Word image OneNote image

maxkatz6 commented 8 months ago

There are multiple ways this is handled and personally I've found when the spell checks are buried under a sub-menu

I have seen UWP apps (specifically, Unigram) that reads this menu flyout and copies its items to the context menu: image

It's quite a hack though.

maxkatz6 commented 8 months ago

Either way, this discussion probably should start with platform level API, that can be used to retrive platform specific spell-check services. As I doubt, we can reliably implement it on our own. Possibly, allow providing custom spell-check service implementations in AppBuilder.

DJGosnell commented 8 months ago

Existing APIs

Win32 API (>=Windows 8) with sample https://github.com/microsoft/Windows-classic-samples/tree/main/Samples/SpellCheckerClient https://github.com/microsoft/Windows-classic-samples/tree/main/Samples/SpellCheckerProvider/cpp

DJGosnell commented 3 months ago

Digging into this again. Is the UWP method the one that should be perused?

Looking into WPF, it seems they have a very complex system for handling Text Editing and Spell Checking. Is this something that would be welcomed into Avalonia or is there a desire the keep the implementation slimmer like current?

DJGosnell commented 2 months ago

I have made some progress in this area with creating a custom TextBox control and drawing red underlines from the overridden Renderer, but I think one of the most performant ways to handle this is within the TextPresenter as the rendering and required hit testing can occur right at the source of the text

Samples of working system image image

Looking at implementation of this. Would the preferred method to get implement the spell checking OS integration back-end be like the Clipboard and StorageProvider where they are attached to the TopLevel control?

Gillibald commented 2 months ago

Y such service can be exposed on the TopLevel via IOptionalFeatureProvider

TextPresenter isn't responsible for analyzing text it just renders some state. TextBox itself or ideally some attached logic should be responsible for the analysis. The result is then used to define style overrides.

DJGosnell commented 2 months ago

@Gillibald

TextPresenter isn't responsible for analyzing text it just renders some state. TextBox itself or ideally some attached logic should be responsible for the analysis. The result is then used to define style overrides.

This makes the implementation a bit easier, but it requires re-querying the text for exact text placements even though those placements and calculations already were calculated and drawn by other text components such as ShapedTextRun.

I was just thinking about performance and presumed that TextLayout.HitTestTextRange was not a trivial calculation, but maybe I am incorrect in that assumption?

DJGosnell commented 2 months ago

Also, regarding modifying the DefaultTextBoxContextFlyout, I'm thinking about adding a Proofing SubMenu, as shown below, to allow for dedicated spell checking integration separate from the rest of the Flyout. Is that a good direction to proceed in for now? gFRWV

My code makes use of the https://www.nuget.org/packages/Microsoft.Windows.CsWin32 package and I saw that there seemed some desire to utilize this package for other interops. Currently my code requires the CLSCTX, CoCreateInstance, CoTaskMemFree, ISpellChecker, ISpellCheckerFactory, S_FALSE, S_OK SpellCheckerFactory`objects which generates the following classes: image

I would very much like to add the CsWin32 package to simplify the com interops, but I wanted to make sure that would be an okay addition to the Avalonia.Win32 project as it is purely a code gen package and does not add any dependencies.

Gillibald commented 2 months ago

Spell checking is always performed on the text buffer and only works with text positions. You should define text decorations for highlighted ranges. The text layout engine will then draw the decorations for you.

DJGosnell commented 2 months ago

I do not see how to modify the rendering of portions of the text for TextBoxs without changing the entire text style. I see textStyleOverrides inside the following method, but I must be overlooking something simple? https://github.com/AvaloniaUI/Avalonia/blob/3b235f9aa82dbcae024c0e75486e7d91df1be13d/src/Avalonia.Controls/Presenters/TextPresenter.cs#L526

GustavoHennig commented 7 hours ago

You may want to take a look at my implementation. I had to recreate the entire style, but it works perfectly with the Fluent theme. It's a work in progress for a personal project:
https://github.com/GustavoHennig/Avalonia.SpellChecker