[Core] Propagate IsEnabled property from Layouts to children#6892
[Core] Propagate IsEnabled property from Layouts to children#6892jsuarezruiz wants to merge 4 commits into
Conversation
Redth
left a comment
There was a problem hiding this comment.
@jsuarezruiz There are some failing tests on input transparency as a result of this I think.
|
Just had a nasty thought... What happens if we have a layout that we disable, and then manually enable the child in it? Will it undo the enabled? Or, if I have a nested grid and disable the outer one and then enable the inner one? |
mattleibow
left a comment
There was a problem hiding this comment.
Since this PR makes changes to child/parent views, we also need to make sure we test these cases:
- nested layouts
- disabling a parent and enabling the child
- disabling the child and enabling the parent
- disabling a layout and then adding a child
- any other properties that also enable/disable controls that could potentially conflict with this property
- since this affects the children, we need tests on moving a child from one layout to another
|
Added "do-not-merge" label, adding tests. |
| { | ||
| platformView.IsHitTestVisible = view.IsEnabled && !view.InputTransparent; | ||
|
|
||
| (view as ILayout)?.InvalidateChildrenIsEnabled(); |
There was a problem hiding this comment.
Should this be inside the else block? What happens if Control is also ILayout? If not, then it is a no-op, but if it is...
Redth
left a comment
There was a problem hiding this comment.
I fixed the initialization case in android, and fixed the sample to have the "all children disabled" layout actually specify IsEnabled="False"
I tested this on Windows, Android, and MacCatalyst and am now comfortable this works as expected.
|
Added Device tests. |
Redth
left a comment
There was a problem hiding this comment.
This one needs some more thought after some conversation with @mattleibow and failing tests...
|
/azp run |
|
Azure Pipelines successfully started running 2 pipeline(s). |
| { | ||
| public static void InvalidateChildrenIsEnabled(this ILayout layout) | ||
| { | ||
| foreach (var children in layout.OrderByZIndex()) |
There was a problem hiding this comment.
Even if this were the correct place to do it, there's no reason to enable/disable the children in z-index order.
hartez
left a comment
There was a problem hiding this comment.
What happens if we have a layout that we disable, and then manually enable the child in it?
Core is the wrong place to implement these rules. Core should handle mapping IsEnabled -> platformView.IsEnabled (or whatever the platform equivalent is) and nothing else. Any sort of propagation is the business of the implementing SDK.
At the Controls level, we should implement whatever propagation we had in Forms (for consistency). IIRC, in Forms a Layout with IsEnabled == false meant that all child controls are also effectively disabled. Any attempt to "enable" a child of disabled Layout does nothing. We should verify that, and we would need to implement that at the Controls level.
Other SDKs may want more permissive enable/disable rules (like bulk disabling a Layout and then enabling a single child) - and they'd be free to handle it that way.
a7d5c1f to
2209f34
Compare
hartez
left a comment
There was a problem hiding this comment.
None of the propagation should be happening in Core; that should be left to the implementing SDK. And Controls should be implementing this in a way that matches Forms, for backward compatibility.
|
Not sure if i agree, that the propagation should not be on the Core. What's the reason for that? that's not a sdk rule, that should be a layout rule, if a layout is disable the children are disable, it's a general rule for all sdks. not a form specific. |
Because reasonable people can disagree on what the rules should be. For example, look at the questions in Matt's comment - there's no obviously right answer to these. Choosing a set of answers and making Core opionated about how enabling/disabling containers should work adds complexity to Core and impacts performance. And it forces every other SDK implementation to either use those rules or spend a lot of effort working around them. Core should be providing simple, basic infrastructure that's not especially opinionated. And the simplest possible mapping of IsEnabled to the containers is to do nothing. This gives us the flexibility to answer all of those questions in Controls in whatever way we choose, whether it's by replicating the weirdness of Forms for backward compatibility, or doing something simpler and less confusing. |
|
Okay, after thinking about this some more, I think it's fine for us to put this in Core with some modifications. If we don't force any disabling in the backing layouts themselves, this is easy enough for 3rd party implementations to change if they want to. |
hartez
left a comment
There was a problem hiding this comment.
On further reflection, I think this will be fine. We just need to make some adjustments.
| { | ||
| internal static void InvalidateChildrenIsEnabled(this ILayout layout) | ||
| { | ||
| foreach (var children in layout.GetChildren()) |
There was a problem hiding this comment.
layout is already an IList<IView>, so we don't need the GetChildren() method here. And this should be a for loop rather than a foreach, for performance reasons.
| { | ||
| internal static class LayoutExtensions | ||
| { | ||
| public static IList<IView> GetChildren(this ILayout layout) => layout; |
There was a problem hiding this comment.
This method isn't necessary.
| if (platformView is UIControl uiControl) | ||
| uiControl.Enabled = view.GetIsEnabled(); | ||
|
|
||
| if (platformView is LayoutView layout) |
There was a problem hiding this comment.
We don't need to enable/disable the layout backing controls; the child controls will all be disabled anyway. We can just drop lines 26 & 27,
| return result; | ||
| } | ||
|
|
||
| internal bool InternalUserInteractionEnabled |
There was a problem hiding this comment.
We don't need to disable user interaction, so this property can just go away.
| if (platformView is Control control) | ||
| control.UpdateIsEnabled(view); | ||
| else | ||
| platformView.IsHitTestVisible = view.GetIsEnabled() && !view.InputTransparent; |
There was a problem hiding this comment.
We don't need to block input to the layout backing control here; lines 34 & 35 can go.
| public static void UpdateIsEnabled(this FrameworkElement platformView, IView view) | ||
| { | ||
| if (platformView is Control control) | ||
| control.UpdateIsEnabled(view); |
There was a problem hiding this comment.
We can probably change this to control.IsEnabled = view.GetIsEnabled() and avoid creating another extension method.
| platformControl.IsTextScaleFactorEnabled = font.AutoScalingEnabled; | ||
| } | ||
|
|
||
| public static void UpdateIsEnabled(this Control platformControl, IView view) => |
There was a problem hiding this comment.
I think the only place using this method is ViewExtensions.cs below, we can probably just inline this and avoid adding more API.
|
We are probably going to use v10 :) #12488 |


Description of Change
Propagate IsEnabled property from Layouts to childrens.
Issues Fixed
Fixes #4755
Fixes #5287