Avalonia
Avalonia copied to clipboard
Spell-check for TextBox and derived controls
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.
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?
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
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
FireFox
Word
OneNote
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:
It's quite a hack though.
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.
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
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?
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
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?
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.
@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?
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?
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:
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.
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.
I do not see how to modify the rendering of portions of the text for TextBox
s 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