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

Semantic OrderView #1011

Merged
merged 27 commits into from
Feb 28, 2023
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
f8d773b
Semantic OrderView
PureWeen Feb 19, 2023
8d5b9be
- address comments
PureWeen Feb 19, 2023
b89867c
- fix tizen and iOS check
PureWeen Feb 20, 2023
dfcd85b
Merge branch 'main' into semanticorderview
jfversluis Feb 20, 2023
ac39984
Merge branch 'main' into semanticorderview
jfversluis Feb 20, 2023
12212ba
Update SemanticOrderViewHandler.tizen.cs
PureWeen Feb 20, 2023
03da7e2
- Set View.Id on platform views
PureWeen Feb 20, 2023
d8e13c4
- add namespace
PureWeen Feb 20, 2023
b865511
- add basic unit test
PureWeen Feb 21, 2023
a1fac29
Merge branch 'main' into semanticorderview
PureWeen Feb 21, 2023
88028ec
- additional tests
PureWeen Feb 21, 2023
fc20b1a
Merge branch 'semanticorderview' of github.com:PureWeen/MauiCommunity…
PureWeen Feb 21, 2023
1135b86
Add Tizen implementation
JoonghyunCho Feb 27, 2023
163d895
Remove unnecessary cast
JoonghyunCho Feb 27, 2023
fad8555
Bring back if-endif
JoonghyunCho Feb 27, 2023
207740b
Merge branch 'main' into semanticorderview
jfversluis Feb 27, 2023
de5bc32
Merge branch 'main' into semanticorderview
brminnick Feb 27, 2023
de62008
Update Variable Names
brminnick Feb 27, 2023
0a7ca4b
Update formatting
brminnick Feb 27, 2023
dceb0e7
Merge branch 'main' into semanticorderview
brminnick Feb 28, 2023
185015d
Merge branch 'semanticorderview' of https://github.com/PureWeen/MauiC…
brminnick Feb 28, 2023
6b81401
Use `IEnumerable<IView>`
brminnick Feb 28, 2023
71261e4
Update Sample App
brminnick Feb 28, 2023
b5b224b
Merge branch 'main' into semanticorderview
brminnick Feb 28, 2023
4bc06d5
Update Sample App
brminnick Feb 28, 2023
716d9c0
Merge branch 'semanticorderview' of https://github.com/PureWeen/MauiC…
brminnick Feb 28, 2023
590d23f
Merge branch 'main' into semanticorderview
brminnick Feb 28, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions samples/CommunityToolkit.Maui.Sample/AppShell.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ public partial class AppShell : Shell
CreateViewModelMapping<PopupAnchorPage, PopupAnchorViewModel, ViewsGalleryPage, ViewsGalleryViewModel>(),
CreateViewModelMapping<PopupPositionPage, PopupPositionViewModel, ViewsGalleryPage, ViewsGalleryViewModel>(),
CreateViewModelMapping<ShowPopupInOnAppearingPage, ShowPopupInOnAppearingPageViewModel, ViewsGalleryPage, ViewsGalleryViewModel>(),
CreateViewModelMapping<SemanticOrderViewPage, SemanticOrderViewPageViewModel, ViewsGalleryPage, ViewsGalleryViewModel>(),
});

public AppShell() => InitializeComponent();
Expand Down
1 change: 1 addition & 0 deletions samples/CommunityToolkit.Maui.Sample/MauiProgram.cs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ static void RegisterViewsAndViewModels(in IServiceCollection services)
services.AddTransientWithShellRoute<PopupAnchorPage, PopupAnchorViewModel>();
services.AddTransientWithShellRoute<PopupPositionPage, PopupPositionViewModel>();
services.AddTransientWithShellRoute<ShowPopupInOnAppearingPage, ShowPopupInOnAppearingPageViewModel>();
services.AddTransientWithShellRoute<SemanticOrderViewPage, SemanticOrderViewPageViewModel>();

// Add Popups
services.AddTransient<CsharpBindingPopup, CsharpBindingPopupViewModel>();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8" ?>
brminnick marked this conversation as resolved.
Show resolved Hide resolved
<pages:BasePage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:mct="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
x:Class="CommunityToolkit.Maui.Sample.Pages.Views.SemanticOrderViewPage"
xmlns:pages="clr-namespace:CommunityToolkit.Maui.Sample.Pages"
Title="Semantic Order View"
xmlns:viewModels="clr-namespace:CommunityToolkit.Maui.Sample.ViewModels.Views"
x:TypeArguments="viewModels:SemanticOrderViewPageViewModel"
x:DataType="viewModels:SemanticOrderViewPageViewModel">
<ContentPage.Content>
<VerticalStackLayout Margin="20">
<Button Text="Element outside the Semantic View"></Button>
<mct:SemanticOrderView x:Name="acv">
<StackLayout>
<Label x:Name="second" Text="Second" Margin="0,20" />
<Button x:Name="third" Text="Third" Margin="0,20" />
<Label x:Name="fourth" Text="Fourth" Margin="0,20" />
<Button x:Name="fifth" Text="Fifth and last" Margin="0,20" />
<Button x:Name="first" Text="First" Margin="0,20" />
</StackLayout>
</mct:SemanticOrderView>
</VerticalStackLayout>
</ContentPage.Content>
</pages:BasePage>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System.Collections.Generic;

using CommunityToolkit.Maui.Sample.ViewModels.Views;
namespace CommunityToolkit.Maui.Sample.Pages.Views;

public partial class SemanticOrderViewPage : BasePage<SemanticOrderViewPageViewModel>
{
public SemanticOrderViewPage(SemanticOrderViewPageViewModel vm) : base(vm)
{
InitializeComponent();
acv.ViewOrder = new List<View> { first, second, third, fourth, fifth };
brminnick marked this conversation as resolved.
Show resolved Hide resolved
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
namespace CommunityToolkit.Maui.Sample.ViewModels.Views;

public class SemanticOrderViewPageViewModel : BaseViewModel
{
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public ViewsGalleryViewModel()
SectionModel.Create<PopupPositionViewModel>("Custom Positioning Popup", Colors.Red, "Displays a basic popup anywhere on the screen using VerticalOptions and HorizontalOptions"),
SectionModel.Create<PopupAnchorViewModel>("Anchor Popup", Colors.Red, "Popups can be anchored to other view's on the screen"),
SectionModel.Create<ShowPopupInOnAppearingPageViewModel>("Show Popup in OnAppearing", Colors.Red, "Proves that we now support showing a popup before the platform is even ready."),
SectionModel.Create<SemanticOrderViewPageViewModel>("Semantic Order View", Colors.Red, "Set accessiblity ordering on views."),
})
{
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using CommunityToolkit.Maui.Core.Views;
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Platform;
using AView = Android.Views.View;

namespace CommunityToolkit.Maui.Core.Handlers;

public partial class SemanticOrderViewHandler
{
/// <inheritdoc/>
protected override ContentViewGroup CreatePlatformView()
{
_ = MauiContext ?? throw new InvalidOperationException("MauiContext is null, please check your MauiApplication.");
_ = MauiContext.Context ?? throw new InvalidOperationException("Android Context is null, please check your MauiApplication.");

return new MauiSemanticOrderView(MauiContext.Context);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using CommunityToolkit.Maui.Core.Views;
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Platform;

namespace CommunityToolkit.Maui.Core.Handlers;

public partial class SemanticOrderViewHandler
{
/// <inheritdoc/>
protected override ContentView CreatePlatformView()
{
_ = MauiContext ?? throw new InvalidOperationException("MauiContext is null, please check your MauiApplication.");

return new MauiSemanticOrderView();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using Microsoft.Maui.Handlers;

namespace CommunityToolkit.Maui.Core.Handlers;

public partial class SemanticOrderViewHandler
{
/// <inheritdoc/>
protected override object CreatePlatformView() => throw new NotSupportedException();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using CommunityToolkit.Maui.Core.Views;
using Microsoft.Maui.Handlers;

namespace CommunityToolkit.Maui.Core.Handlers;

/// <summary>
/// Handler SemanticOrderView control
/// </summary>
public partial class SemanticOrderViewHandler : ContentViewHandler
{
/// <summary>
/// PropertyMapper for SemanticOrderView Control
/// </summary>
public static IPropertyMapper<ISemanticOrderView, SemanticOrderViewHandler> SemanticOrderViewMapper = new PropertyMapper<ISemanticOrderView, SemanticOrderViewHandler>(ContentViewHandler.Mapper)
{
[nameof(ISemanticOrderView.ViewOrder)] = MapViewOrder,
};
/// <summary>
/// <see cref ="CommandMapper"/> for SemanticOrderView Control.
/// </summary>
public static CommandMapper<ISemanticOrderView, SemanticOrderViewHandler> SemanticOrderViewCommandMapper = new(ContentViewHandler.CommandMapper)
{
};

/// <summary>
/// Constructor for <see cref="SemanticOrderViewHandler"/>.
/// </summary>
/// <param name="mapper">Custom instance of <see cref="PropertyMapper"/>, if it's null the <see cref="SemanticOrderViewMapper"/> will be used</param>
/// <param name="commandMapper">Custom instance of <see cref="CommandMapper"/>, if it's null the <see cref="SemanticOrderViewCommandMapper"/> will be used</param>
public SemanticOrderViewHandler(IPropertyMapper? mapper, CommandMapper? commandMapper)
: base(mapper ?? SemanticOrderViewMapper, commandMapper ?? SemanticOrderViewCommandMapper)
{
}

/// <summary>
/// Default Constructor for <see cref="SemanticOrderViewHandler"/>.
/// </summary>
public SemanticOrderViewHandler()
: base(SemanticOrderViewMapper, SemanticOrderViewCommandMapper)
{
}

static void MapViewOrder(SemanticOrderViewHandler handler, ISemanticOrderView view)
{
#if WINDOWS || IOS || MACCATALYST || ANDROID || TIZEN
if (handler.PlatformView is MauiSemanticOrderView mso)
{
mso.UpdateViewOrder();
}
#endif
}

/// <inheritdoc/>
public override void SetVirtualView(IView view)
{
base.SetVirtualView(view);
#if WINDOWS || IOS || MACCATALYST || ANDROID || TIZEN
if (PlatformView is MauiSemanticOrderView mso)
{
mso.VirtualView = (ISemanticOrderView)VirtualView;
}
#endif
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using CommunityToolkit.Maui.Core.Views;
using Microsoft.Maui.Platform;

namespace CommunityToolkit.Maui.Core.Handlers;

public partial class SemanticOrderViewHandler
{
/// <inheritdoc/>
protected override ContentViewGroup CreatePlatformView()
{
_ = MauiContext ?? throw new InvalidOperationException("MauiContext is null, please check your MauiApplication.");

return new MauiSemanticOrderView(VirtualView);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using CommunityToolkit.Maui.Core.Views;
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Platform;

namespace CommunityToolkit.Maui.Core.Handlers;

public partial class SemanticOrderViewHandler
{
/// <inheritdoc/>
protected override ContentPanel CreatePlatformView()
{
_ = MauiContext ?? throw new InvalidOperationException("MauiContext is null, please check your MauiApplication.");

return new MauiSemanticOrderView();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CommunityToolkit.Maui.Core;

/// <summary>
/// Allows users to specify the semantic order of child views.
/// </summary>
public interface ISemanticOrderView : IContentView
{
/// <summary>
/// The semantic order to traverse child views
/// </summary>
IEnumerable<IView> ViewOrder { get; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using System.Linq;
using Android.Content;
using Android.Views;
using Microsoft.Maui.Platform;

namespace CommunityToolkit.Maui.Core.Views;

/// <summary>
/// The native implementation of the <see href="SemanticOrderView"/> control.
/// </summary>
public class MauiSemanticOrderView : ContentViewGroup
{
ISemanticOrderView? virtualView;

public MauiSemanticOrderView(Context context)
: base(context)
{
}

internal ISemanticOrderView? VirtualView
{
get => virtualView;
set
{
virtualView = value;
UpdateViewOrder();
}
}

protected override void OnLayout(bool changed, int left, int top, int right, int bottom)
{
UpdateViewOrder();
base.OnLayout(changed, left, top, right, bottom);
}

internal void UpdateViewOrder()
{
if (VirtualView is null)
{
return;
}

var viewOrder = VirtualView.ViewOrder.ToList();

for (var i = 1; i < viewOrder.Count; i++)
{
var view1 = (viewOrder[i - 1]?.Handler as IPlatformViewHandler)?.PlatformView;
var view2 = (viewOrder[i]?.Handler as IPlatformViewHandler)?.PlatformView;

if (view1 is null || view2 is null)
{
return;
}

if (view1.Id <= 0)
{
view1.Id = View.GenerateViewId();
}

if (view2.Id <= 0)
{
view2.Id = View.GenerateViewId();
}

if (OperatingSystem.IsAndroidVersionAtLeast(22))
{
view2.AccessibilityTraversalAfter = view1.Id;
view1.AccessibilityTraversalBefore = view2.Id;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using System.Collections.Generic;
using Foundation;
using Microsoft.Maui.Platform;
using UIKit;

namespace CommunityToolkit.Maui.Core.Views;

/// <summary>
/// The native implementation of the <see href="SemanticOrderView"/> control.
/// </summary>
public class MauiSemanticOrderView : ContentView, IUIAccessibilityContainer
{
ISemanticOrderView? virtualView;
internal ISemanticOrderView? VirtualView
{
get => virtualView;
set
{
virtualView = value;
UpdateViewOrder();
}
}

internal void UpdateViewOrder()
{
this.SetAccessibilityElements(NSArray.FromNSObjects(GetAccessibilityElements().ToArray()));
}

IEnumerable<NSObject> GetAccessibilityElements()
{
if (VirtualView is null)
{
yield break;
}

var viewOrder = VirtualView.ViewOrder;

foreach (var view in viewOrder)
{
if (view.Handler is IPlatformViewHandler pvh &&
pvh.PlatformView is not null)
{
yield return pvh.PlatformView;
}
}
}
}
Loading