Skip to content

Commit

Permalink
2.0.31 - Fixes #218, #219, #220
Browse files Browse the repository at this point in the history
  • Loading branch information
adospace committed Mar 5, 2024
1 parent 884d03a commit 9191ddd
Show file tree
Hide file tree
Showing 6 changed files with 228 additions and 5 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
env:
Solution_Name: ./src/MauiReactor.Build.sln
TemplatePack_Name: ./src/MauiReactor.TemplatePack/MauiReactor.TemplatePack.csproj
Version: 2.0.29
Version: 2.0.31

steps:
- name: Checkout
Expand Down
46 changes: 46 additions & 0 deletions samples/MauiReactor.TestApp/Pages/TestBug218.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using MauiReactor.TestApp.Services;
using Microsoft.Maui;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MauiReactor.TestApp.Pages;

abstract partial class BaseComponent2<TState> : Component<TState> where TState : class, new()
{
[Inject] protected readonly IncrementService? _injectedService;
[Prop] protected MauiControls.Shell? shellRef;
}

abstract partial class BaseComponent2<TState, TProps> : Component<TState, TProps> where TState : class, new()
where TProps : class, new()
{
[Inject] protected readonly IncrementService? _injectedService;
[Prop] protected MauiControls.Shell? shellRef;
}

class TestBug218_1 : BaseComponent2<TestBug218_1.MyState>
{
public class MyState
{ }

public override VisualNode Render()
{
throw new NotImplementedException();
}
}

class TestBug218_2 : BaseComponent2<TestBug218_2.MyState, TestBug218_2.MyProps>
{
public class MyState
{ }
public class MyProps
{ }

public override VisualNode Render()
{
throw new NotImplementedException();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ void generateClassItem(FieldDeclarationSyntax fieldDeclaration, FieldAttributeTy
return;
}

string fullyQualifiedTypeName = classTypeSymbol.ToDisplayString(qualifiedFormat);
string fullyQualifiedTypeName = classTypeSymbol.ToDisplayString(symbolDisplayFormat);
string namespaceName = classTypeSymbol.ContainingNamespace.ToDisplayString();

if (!generatingClassItems.TryGetValue(fullyQualifiedTypeName, out var generatingClassItem))
Expand Down Expand Up @@ -196,7 +196,7 @@ void generateClassItem(FieldDeclarationSyntax fieldDeclaration, FieldAttributeTy

var source = textGenerator.TransformAndPrettify();

context.AddSource($"{generatingClassItem.Value.ClassName}.g.cs", source);
context.AddSource($"{generatingClassItem.Value.ClassName}_{Guid.NewGuid().ToString().Substring(0,4)}.g.cs", source);
}
}
}
Expand Down
86 changes: 86 additions & 0 deletions src/MauiReactor/Internals/IAutomationItemContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,5 +65,91 @@ public static class AutomationItemContainerExtensions
}
}
}

public static async Task<T?> FindOptional<T>(this IAutomationItemContainer automationItemContainer, ITemplateHost templateHost, string automationId, TimeSpan timeout, CancellationToken cancellationToken = default) where T : class
{
var itemFound = automationItemContainer.FindOptional<T>(automationId);
if (itemFound != null)
{
return itemFound;
}

using var waitSem = new SemaphoreSlim(1);

void handler(object? s, EventArgs e) => waitSem.Release();

try
{
templateHost.LayoutCycleExecuted += handler;

var waitingTimeout = timeout.TotalMilliseconds;

while (itemFound == null && waitingTimeout > 0)
{
DateTime now = DateTime.Now;
await waitSem.WaitAsync(TimeSpan.FromMilliseconds(waitingTimeout), cancellationToken);

itemFound = automationItemContainer.FindOptional<T>(automationId);
if (itemFound != null)
{
return itemFound;
}

waitingTimeout -= (DateTime.Now - now).TotalMilliseconds;
}

return null;
}
finally
{
templateHost.LayoutCycleExecuted -= handler;
}
}

public static Task<T?> Find<T>(this IAutomationItemContainer automationItemContainer, ITemplateHost templateHost, string automationId, TimeSpan timeout, CancellationToken cancellationToken = default) where T : class
=> automationItemContainer.FindOptional<T>(templateHost, automationId, timeout, cancellationToken) ?? throw new InvalidOperationException($"Element with automation id {automationId} not found");

public static async Task<T?> FindOptional<T>(this IAutomationItemContainer automationItemContainer, ITemplateHost templateHost, Func<T, bool> predicate, TimeSpan timeout, CancellationToken cancellationToken = default) where T : class
{
var itemFound = automationItemContainer.FindOptional(predicate);
if (itemFound != null)
{
return itemFound;
}

using var waitSem = new SemaphoreSlim(1);

void handler(object? s, EventArgs e) => waitSem.Release();

try
{
templateHost.LayoutCycleExecuted += handler;

var waitingTimeout = timeout.TotalMilliseconds;

while (itemFound == null && waitingTimeout > 0)
{
DateTime now = DateTime.Now;
await waitSem.WaitAsync(TimeSpan.FromMilliseconds(waitingTimeout), cancellationToken);

itemFound = automationItemContainer.FindOptional<T>(predicate);
if (itemFound != null)
{
return itemFound;
}

waitingTimeout -= (DateTime.Now - now).TotalMilliseconds;
}

return null;
}
finally
{
templateHost.LayoutCycleExecuted -= handler;
}
}

public static Task<T?> Find<T>(this IAutomationItemContainer automationItemContainer, ITemplateHost templateHost, Func<T, bool> predicate, TimeSpan timeout, CancellationToken cancellationToken = default) where T : class
=> automationItemContainer.FindOptional<T>(templateHost, predicate, timeout, cancellationToken) ?? throw new InvalidOperationException($"Unable to find the element");
}

86 changes: 86 additions & 0 deletions src/MauiReactor/NativeElementExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,90 @@ public static class NativeElementExtensions
}
}
}

public static async Task<T?> FindOptional<T>(this IElementController elementController, ITemplateHost templateHost, string automationId, TimeSpan timeout, CancellationToken cancellationToken = default) where T : class
{
var itemFound = elementController.FindOptional<T>(automationId);
if (itemFound != null)
{
return itemFound;
}

using var waitSem = new SemaphoreSlim(1);

void handler(object? s, EventArgs e) => waitSem.Release();

try
{
templateHost.LayoutCycleExecuted += handler;

var waitingTimeout = timeout.TotalMilliseconds;

while (itemFound == null && waitingTimeout > 0)
{
DateTime now = DateTime.Now;
await waitSem.WaitAsync(TimeSpan.FromMilliseconds(waitingTimeout), cancellationToken);

itemFound = elementController.FindOptional<T>(automationId);
if (itemFound != null)
{
return itemFound;
}

waitingTimeout -= (DateTime.Now - now).TotalMilliseconds;
}

return null;
}
finally
{
templateHost.LayoutCycleExecuted -= handler;
}
}

public static Task<T?> Find<T>(this IElementController elementController, ITemplateHost templateHost, string automationId, TimeSpan timeout, CancellationToken cancellationToken = default) where T : class
=> elementController.FindOptional<T>(templateHost, automationId, timeout, cancellationToken) ?? throw new InvalidOperationException($"Element with automation id {automationId} not found");

public static async Task<T?> FindOptional<T>(this IElementController elementController, ITemplateHost templateHost, Func<T, bool> predicate, TimeSpan timeout, CancellationToken cancellationToken = default) where T : class
{
var itemFound = elementController.FindOptional(predicate);
if (itemFound != null)
{
return itemFound;
}

using var waitSem = new SemaphoreSlim(1);

void handler(object? s, EventArgs e) => waitSem.Release();

try
{
templateHost.LayoutCycleExecuted += handler;

var waitingTimeout = timeout.TotalMilliseconds;

while (itemFound == null && waitingTimeout > 0)
{
DateTime now = DateTime.Now;
await waitSem.WaitAsync(TimeSpan.FromMilliseconds(waitingTimeout), cancellationToken);

itemFound = elementController.FindOptional<T>(predicate);
if (itemFound != null)
{
return itemFound;
}

waitingTimeout -= (DateTime.Now - now).TotalMilliseconds;
}

return null;
}
finally
{
templateHost.LayoutCycleExecuted -= handler;
}
}

public static Task<T?> Find<T>(this IElementController elementController, ITemplateHost templateHost, Func<T, bool> predicate, TimeSpan timeout, CancellationToken cancellationToken = default) where T : class
=> elementController.FindOptional<T>(templateHost, predicate, timeout, cancellationToken) ?? throw new InvalidOperationException($"Unable to find the element");
}
9 changes: 7 additions & 2 deletions src/MauiReactor/VisualNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -690,7 +690,7 @@ IEnumerable<T> IAutomationItemContainer.Descendants<T>()

public interface IVisualNodeWithNativeControl : IVisualNode
{
TResult GetNativeControl<TResult>() where TResult : BindableObject;
TResult? GetNativeControl<TResult>() where TResult : BindableObject;

void Attach(BindableObject nativeControl);
}
Expand Down Expand Up @@ -888,8 +888,13 @@ private void NativeControl_PropertyChanging(object? sender, Microsoft.Maui.Contr
PropertyChangingAction?.Invoke(sender, new System.ComponentModel.PropertyChangingEventArgs(e.PropertyName));
}

TResult IVisualNodeWithNativeControl.GetNativeControl<TResult>()
TResult? IVisualNodeWithNativeControl.GetNativeControl<TResult>() where TResult : class
{
if (_nativeControl == null)
{
return default;
}

return (_nativeControl as TResult) ??
throw new InvalidOperationException($"Unable to convert from type {typeof(T)} to type {typeof(TResult)} when getting the native control");
}
Expand Down

0 comments on commit 9191ddd

Please sign in to comment.