-
Notifications
You must be signed in to change notification settings - Fork 1.7k
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
Correctly handle ContainerView changes #13114
base: main
Are you sure you want to change the base?
Changes from 1 commit
ff49528
174ddf8
7e6cd4b
904553c
381ee30
1e389d4
4666446
b62ccb3
a96691e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
using System.Collections.Generic; | ||
using Microsoft.Maui.Graphics; | ||
#if __IOS__ || MACCATALYST | ||
using PlatformView = UIKit.UIView; | ||
|
@@ -13,7 +14,7 @@ | |
|
||
namespace Microsoft.Maui.Handlers | ||
{ | ||
public abstract partial class ViewHandler : ElementHandler, IViewHandler | ||
public abstract partial class ViewHandler : ElementHandler, IViewHandler, INeedsContainerViewHandler | ||
{ | ||
public static IPropertyMapper<IView, IViewHandler> ViewMapper = | ||
#if ANDROID | ||
|
@@ -48,6 +49,7 @@ public abstract partial class ViewHandler : ElementHandler, IViewHandler | |
[nameof(IView.RotationY)] = MapRotationY, | ||
[nameof(IView.AnchorX)] = MapAnchorX, | ||
[nameof(IView.AnchorY)] = MapAnchorY, | ||
[nameof(INeedsContainerViewHandler.NeedsContainer)] = MapNeedsContainer, | ||
[nameof(IViewHandler.ContainerView)] = MapContainerView, | ||
[nameof(IBorder.Border)] = MapBorderView, | ||
#if ANDROID || WINDOWS || TIZEN | ||
|
@@ -67,6 +69,7 @@ public abstract partial class ViewHandler : ElementHandler, IViewHandler | |
[nameof(IView.ZIndex)] = MapZIndex, | ||
[nameof(IView.Focus)] = MapFocus, | ||
[nameof(IView.Unfocus)] = MapUnfocus, | ||
[nameof(INeedsContainerViewHandler.NeedsContainer)] = MapNeedsContainer, | ||
}; | ||
|
||
bool _hasContainer; | ||
|
@@ -78,6 +81,8 @@ protected ViewHandler(IPropertyMapper mapper, CommandMapper? commandMapper = nul | |
{ | ||
} | ||
|
||
public ICollection<string> ChangesContainer { get; } = new HashSet<string>(); | ||
|
||
public bool HasContainer | ||
{ | ||
get => _hasContainer; | ||
|
@@ -92,12 +97,33 @@ public bool HasContainer | |
SetupContainer(); | ||
else | ||
RemoveContainer(); | ||
|
||
UpdateValue(nameof(IViewHandler.ContainerView)); | ||
Comment on lines
+103
to
+104
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The HasContainer property adds/removes the container and then calls the ContainerView property mapper. If the value is the same as before, it is terminated here and we skip the rest. |
||
} | ||
} | ||
|
||
public virtual bool NeedsContainer | ||
{ | ||
get => VirtualView.NeedsContainer(); | ||
get | ||
{ | ||
#if !TIZEN | ||
if (VirtualView?.Clip != null || VirtualView?.Shadow != null) | ||
return true; | ||
#endif | ||
#if ANDROID | ||
if (VirtualView?.InputTransparent == true) | ||
return true; | ||
#endif | ||
#if ANDROID || IOS | ||
if (VirtualView is IBorder border) | ||
return border.Border != null; | ||
mattleibow marked this conversation as resolved.
Show resolved
Hide resolved
|
||
#endif | ||
#if WINDOWS || TIZEN | ||
if (VirtualView is IBorderView border) | ||
return border?.Shape != null || border?.Stroke != null; | ||
mattleibow marked this conversation as resolved.
Show resolved
Hide resolved
|
||
#endif | ||
return false; | ||
} | ||
} | ||
|
||
protected abstract void SetupContainer(); | ||
|
@@ -201,10 +227,17 @@ public static void MapIsEnabled(IViewHandler handler, IView view) | |
|
||
public static void MapVisibility(IViewHandler handler, IView view) | ||
{ | ||
handler.Invoke(nameof(INeedsContainerViewHandler.NeedsContainer), nameof(IView.Visibility)); | ||
|
||
if (handler.HasContainer) | ||
{ | ||
((PlatformView?)handler.ContainerView)?.UpdateVisibility(view); | ||
((PlatformView?)handler.PlatformView)?.UpdateVisibility(Visibility.Visible); | ||
} | ||
else | ||
{ | ||
((PlatformView?)handler.PlatformView)?.UpdateVisibility(view); | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When a property changes that needs to respond to ContainerView changes, we call the "special" invoke and pass the property that requires a re-fire. This updates a collection, and ALSO triggers the path to ensure the container is correct. Then based on the HasContainer, the mapper needs to set the values of the container and/or the platform view. |
||
|
||
public static void MapBackground(IViewHandler handler, IView view) | ||
|
@@ -232,10 +265,8 @@ public static void MapFlowDirection(IViewHandler handler, IView view) | |
|
||
public static void MapOpacity(IViewHandler handler, IView view) | ||
{ | ||
if (handler.HasContainer) | ||
((PlatformView?)handler.ContainerView)?.UpdateOpacity(view); | ||
else | ||
((PlatformView?)handler.PlatformView)?.UpdateOpacity(view); | ||
handler.Invoke(nameof(INeedsContainerViewHandler.NeedsContainer), nameof(IView.Opacity)); | ||
handler.ToPlatform()?.UpdateOpacity(view); | ||
} | ||
|
||
public static void MapAutomationId(IViewHandler handler, IView view) | ||
|
@@ -245,14 +276,14 @@ public static void MapAutomationId(IViewHandler handler, IView view) | |
|
||
public static void MapClip(IViewHandler handler, IView view) | ||
{ | ||
handler.UpdateValue(nameof(IViewHandler.ContainerView)); | ||
handler.Invoke(nameof(INeedsContainerViewHandler.NeedsContainer), nameof(IView.Clip)); | ||
|
||
((PlatformView?)handler.ContainerView)?.UpdateClip(view); | ||
} | ||
|
||
public static void MapShadow(IViewHandler handler, IView view) | ||
{ | ||
handler.UpdateValue(nameof(IViewHandler.ContainerView)); | ||
handler.Invoke(nameof(INeedsContainerViewHandler.NeedsContainer), nameof(IView.Shadow)); | ||
|
||
((PlatformView?)handler.ContainerView)?.UpdateShadow(view); | ||
} | ||
|
@@ -270,27 +301,32 @@ public static void MapInvalidateMeasure(IViewHandler handler, IView view, object | |
(handler.PlatformView as PlatformView)?.InvalidateMeasure(view); | ||
} | ||
|
||
public static void MapContainerView(IViewHandler handler, IView view) | ||
public static void MapNeedsContainer(IViewHandler handler, IView view, object? args) | ||
{ | ||
if (handler is ViewHandler viewHandler) | ||
handler.HasContainer = viewHandler.NeedsContainer; | ||
else | ||
handler.HasContainer = view.NeedsContainer(); | ||
if (handler is ViewHandler viewHandler && args is string mapper) | ||
viewHandler.ChangesContainer.Add(mapper); | ||
|
||
UpdateInputTransparentOnContainerView(handler, view); | ||
handler.UpdateValue(nameof(INeedsContainerViewHandler.NeedsContainer)); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Then the NeedsContainer command adds the incoming property to the list to update later. After that, it calls the NeedsContainer PROPERTY mapper. |
||
|
||
static void UpdateInputTransparentOnContainerView(IViewHandler handler, IView view) | ||
public static void MapNeedsContainer(IViewHandler handler, IView view) | ||
{ | ||
#if ANDROID | ||
if (handler.ContainerView is WrapperView wrapper) | ||
wrapper.InputTransparent = view.InputTransparent; | ||
#endif | ||
if (handler is INeedsContainerViewHandler ncvh) | ||
handler.HasContainer = ncvh.NeedsContainer; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The NeedsContainer property mapper now updates the HasContainer property. |
||
|
||
public static void MapContainerView(IViewHandler handler, IView view) | ||
{ | ||
if (handler is INeedsContainerViewHandler ncvh) | ||
{ | ||
foreach (var map in ncvh.ChangesContainer) | ||
handler.UpdateValue(map); | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The ContainerView then re-fires all the things that were added before. |
||
|
||
public static void MapBorderView(IViewHandler handler, IView view) | ||
{ | ||
handler.UpdateValue(nameof(IViewHandler.ContainerView)); | ||
handler.Invoke(nameof(INeedsContainerViewHandler.NeedsContainer), nameof(IBorder.Border)); | ||
|
||
((PlatformView?)handler.ContainerView)?.UpdateBorder(view); | ||
} | ||
|
@@ -326,10 +362,10 @@ public static void MapFocus(IViewHandler handler, IView view, object? args) | |
public static void MapInputTransparent(IViewHandler handler, IView view) | ||
{ | ||
#if ANDROID | ||
handler.UpdateValue(nameof(IViewHandler.ContainerView)); | ||
UpdateInputTransparentOnContainerView(handler, view); | ||
handler.Invoke(nameof(INeedsContainerViewHandler.NeedsContainer), nameof(IView.InputTransparent)); | ||
((PlatformView?)handler.ContainerView)?.UpdateInputTransparent(view); | ||
#else | ||
((PlatformView?)handler.PlatformView)?.UpdateInputTransparent(handler, view); | ||
((PlatformView?)handler.PlatformView)?.UpdateInputTransparent(view); | ||
#endif | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ignore this.