Open stevemonaco opened 1 year ago
So far in my experience, when I need to show lots of text entry controls at once, the best approach is to use TextBlock, which then is replaced by TextBox on focus. The same approach is used in DataGrid in both WPF/Avalonia, and now it is in TreeDataGrid. It also can be applied to NumericUpDown.
But if you are reimplementing TextBox to be more lightweight, we need to understand what exactly is making TextBox "heavy". Is it data validation support, IME, automation, undo/redo...
Most layout-related performance issues could mostly be addressed through a new ControlTheme
for TextBox
(which I'd do on my end) to remove layout components that are unnecessary for said control. The TextBlock
swap trick is nice, but does add complexity. Especially if the number of controls is not extreme.
Data validation, IME, automation, undo/redo, cut/copy/paste, caret navigation, selection, etc are all expected parts of single line input controls and changes to those would require modifications to components deeper than TextBox
. So this issue is more about finding some means to reduce the wide-ranging API surface of TextBox
dependency properties to build custom controls on top of.
Many properties fit niche scenarios: MaxLines
, password-related / masking properties, etc, but don't necessarily impact layout. Others like TextWrapping
, Watermark
, UseFloatingWatermark
, InnerLeftContent
, and InnerRightContent
imply that any text input control should support those in order to be compliant and these require additional layout. I'm on the fence with ScrollViewer
being a required TemplatePart
for a lighter text entry control.
I would probably start by removing the above subset in the new control/primitive, but it may be too opinion-based for Avalonia to design and maintain. If that can't happen, then the next best thing would be making TextPresenter.GetCursorRectangle
(or equivalent) public. (Re)Implementing both TextInputMethodClient
and TextPresenter
seems a bit much before you can even touch the custom control which uses them.
Properties itself shouldn't really make performance noticeably worse, especially when kept in default value.
But styles using them could. Especially selectors like these: https://github.com/AvaloniaUI/Avalonia/blob/master/src/Avalonia.Themes.Fluent/Controls/TextBox.xaml#L226-L249
Unused template parts also add to overall complexity just by existing there. This is where x:Load
from UWP could help.
But before that, only a text presenter should be required from the whole template.
If that can't happen, then the next best thing would be making TextPresenter.GetCursorRectangle (or equivalent) public
I don't have an opinion on these, @Gillibald. TextInputMethodClient was designed to be implemented for each custom text entry control. Controls like AvaloniaEdit also implement their own. It should be possible to reimplement TextBox-like control if one needs.
To clarify, I'm not concerned about the existence of dependency properties themselves having a significant performance impact. It's the extra layout controls necessary to support the broader API surface. The FluentTheme TextBox
looks to have two ContentPresenter
s, a Grid
, two TextBlock
s, and probably a Panel
in excess that need to be available to support the properties I mentioned before that will never be used. Maybe the DockPanel
, too. Plus all of the TemplateBinding
s and also the conditional styling as you mentioned.
So there are two straightforward approaches to reduce layout controls as I see it. The TextBlock
swap is a possible third, but manages the problem at a higher level.
ControlTheme
. Run into the pain points I mentioned in the opening comment.ControlTheme
for TextBox
that chooses to not represent properties and remove the not-to-be-supported layout controls. This breaks expectations for a large amount of the TextBox
API surface.Further cons on approach 2. There doesn't seem to be a way to communicate unsupported properties within a ControlTemplate
for the TargetType
and the user must thoroughly examine the style instead of it being available at-a-glance. In my experience, this examination first happens in the middle of a debugging session because a selector hasn't worked as expected. Maybe some possibility for a hypothetical <Setter Property="InnerContentTemplate" Value="{x:NotSupported}" />
or {NotSupportedBinding}
that can log or exception at debug-time if it's ever set again? Custom Setter
s seemed difficult, but something explicit like <NotSupported Property="InnerContentTemplate" />
seems preferable if no other XAML dialect has solved this issue. Getting off-track here though.
off-topic: I think our highest bottleneck is that we don't have virtualized textboxes yet.
Virtualized textboxes would help with huge texts, but it's pretty much the opposite of being lightweight, as virtualization adds complexity on its own.
I don't understand the issue here. The TextBox itself just requires a control template with a ScrollViewer that contains a TextPresenter. That is it. Nothing more.
In this experimental branch I tried to reduce the allocation of TextPresenter.CarretBrush, but I am unable to measure the effectiveness. The benchmark is probably wrong.
@stevemonaco Can you test if it improves something in your use case?
I don't understand the issue here. The TextBox itself just requires a control template with a ScrollViewer that contains a TextPresenter. That is it. Nothing more.
I'm aware. However, if you are faithful to the vast number of dependency properties TextBox
has, you won't be faithfully theming the control and that's confusing to other devs. So I want to implement my own custom control which is a trimmed-down TextBox
primitive in terms of properties (or have Avalonia provide one). This is made difficult because of several pieces (TextBoxTextInputMethodClient
and TextPresenter.GetCursorRectangle
) are marked internal and can't be accessed in user apps. Particularly, TextPresenter.GetCursorRectangle
could be made public
as there's no other way to get those bounds, AFAIK.
@stevemonaco Can you test if it improves something in your use case?
Hi, this is unfortunately unrelated to my issue. It is nice to see improvements in other areas though.
WPF has more primitive "base class" text controls. Porting some of that over might help with the ask here.
TextBoxBase -> TextBox exists in WPF. I thought there was some sort of TextContainer-type control as well (https://github.com/AvaloniaUI/Avalonia/issues/7502#issuecomment-1075941003)?
Is your feature request related to a problem? Please describe.
TextBox
is a heavy control for building your own text entry controls on top of. There's a lot of flexibility in its dependency properties and that requires many layout controls. This is undesirable for simpler, single line text entry controls.Describe the solution you'd like
A lightweight
TextBoxBase
/TextEntryBase
that is more appropriate to derive from and build on top of, specifically for single line controls. I didn't see anything in the repo (Avalonia.Controls/Primitives) that was relevant.Describe alternatives you've considered
Theming
TextBox
to effectively ignore dependency properties and remove more expensive layout components. It feels a bit hackish like a particularly egregious Liskov Substitution Principle violation.Copying the implementation from
TextBox.cs
and its dependencies in preparation for trimming down the surface area. At the moment, you must copy the following classes from Avalonia to reproduceTextBox
roughly 1:1 in the project:Some of these, especially the latter, might go away once features get trimmed. But it's a painful starting point.
Additional context
I'm creating a custom numeric input control similar to
NumericUpDown
where I don't need most of theTextBox
feature set, but I do need tight control over mouse dragging. eg. Dragging up increases the number and dragging down decreases it.