Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Core] Propagate IsEnabled property from Layouts to children #6892

Closed
wants to merge 4 commits into from

Conversation

jsuarezruiz
Copy link
Contributor

Description of Change

Propagate IsEnabled property from Layouts to childrens.

fix-layout-enabled-droid
fix-layout-enabled-windows

Issues Fixed

Fixes #4755
Fixes #5287

@jsuarezruiz jsuarezruiz added t/bug Something isn't working area-layout StackLayout, GridLayout, ContentView, AbsoluteLayout, FlexLayout, ContentPresenter labels May 6, 2022
Copy link
Member

@Redth Redth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jsuarezruiz There are some failing tests on input transparency as a result of this I think.

@mattleibow
Copy link
Member

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?

Copy link
Member

@mattleibow mattleibow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@jsuarezruiz jsuarezruiz added the do-not-merge Don't merge this PR label May 9, 2022
@jsuarezruiz
Copy link
Contributor Author

Added "do-not-merge" label, adding tests.

@Redth Redth added this to the 6.0.300 milestone May 9, 2022
@jsuarezruiz
Copy link
Contributor Author

Added new sample:
layout-isenabled-sample

{
platformView.IsHitTestVisible = view.IsEnabled && !view.InputTransparent;

(view as ILayout)?.InvalidateChildrenIsEnabled();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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...

Copy link
Member

@Redth Redth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@mattleibow mattleibow enabled auto-merge (squash) May 11, 2022 01:57
@mattleibow mattleibow disabled auto-merge May 11, 2022 01:57
@jsuarezruiz
Copy link
Contributor Author

Added Device tests.

src/Core/src/Core/IView.cs Outdated Show resolved Hide resolved
@rmarinho rmarinho removed the do-not-merge Don't merge this PR label May 11, 2022
Copy link
Member

@Redth Redth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one needs some more thought after some conversation with @mattleibow and failing tests...

@Redth Redth marked this pull request as draft May 11, 2022 20:52
@Redth Redth modified the milestones: 6.0.300, 6.0.300-servicing May 11, 2022
@jsuarezruiz
Copy link
Contributor Author

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 2 pipeline(s).

{
public static void InvalidateChildrenIsEnabled(this ILayout layout)
{
foreach (var children in layout.OrderByZIndex())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even if this were the correct place to do it, there's no reason to enable/disable the children in z-index order.

Copy link
Contributor

@hartez hartez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@hartez hartez changed the title [Core] Propagate IsEnabled property from Layouts to childrens [Core] Propagate IsEnabled property from Layouts to children Jul 19, 2022
@Redth Redth modified the milestones: 6.0-servicing, .NET 7 Planning Aug 30, 2022
@jsuarezruiz jsuarezruiz changed the base branch from net6.0 to main December 14, 2022 09:07
@jsuarezruiz jsuarezruiz changed the base branch from main to net6.0 December 14, 2022 09:08
@jsuarezruiz jsuarezruiz changed the base branch from net6.0 to main December 14, 2022 13:04
@jsuarezruiz jsuarezruiz marked this pull request as ready for review December 14, 2022 13:04
@jsuarezruiz
Copy link
Contributor Author

Changed target to main. Tested and passed the device tests in the different platforms.
image

Copy link
Contributor

@hartez hartez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

src/Core/src/Extensions/IViewExtensions.cs Outdated Show resolved Hide resolved
@rmarinho
Copy link
Member

rmarinho commented Jan 2, 2023

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.

@hartez
Copy link
Contributor

hartez commented Jan 3, 2023

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.

@hartez
Copy link
Contributor

hartez commented Jan 7, 2023

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.

Copy link
Contributor

@hartez hartez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

using System.Linq;

namespace Microsoft.Maui.Handlers
{
internal static class LayoutExtensions
{
public static IList<IView> GetChildren(this ILayout layout) => layout;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method isn't necessary.

if (platformView is UIControl uiControl)
uiControl.Enabled = view.GetIsEnabled();

if (platformView is LayoutView layout)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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,

@@ -76,6 +76,11 @@ public override UIView HitTest(CGPoint point, UIEvent? uievent)
return result;
}

internal bool InternalUserInteractionEnabled
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can probably change this to control.IsEnabled = view.GetIsEnabled() and avoid creating another extension method.

@@ -21,6 +21,9 @@ public static void UpdateFont(this Control platformControl, Font font, IFontMana
platformControl.IsTextScaleFactorEnabled = font.AutoScalingEnabled;
}

public static void UpdateIsEnabled(this Control platformControl, IView view) =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the only place using this method is ViewExtensions.cs below, we can probably just inline this and avoid adding more API.

@mattleibow
Copy link
Member

We are probably going to use v10 :) #12488

@mattleibow mattleibow closed this Jan 12, 2023
@mattleibow mattleibow deleted the fix-4755 branch January 12, 2023 17:46
@github-actions github-actions bot locked and limited conversation to collaborators Dec 22, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-layout StackLayout, GridLayout, ContentView, AbsoluteLayout, FlexLayout, ContentPresenter t/bug Something isn't working
Projects
None yet
Development

Successfully merging this pull request may close these issues.

StackLayout IsEnabled Property does not work IsEnabled not working properly for layout
5 participants