Merge branch 'main' into feature/sl/remove-popup-constraint-from-popup-service
This commit is contained in:
Коммит
b701700b71
|
@ -117,17 +117,16 @@ public partial class AppShell : Shell
|
|||
CreateViewModelMapping<AvatarViewShadowsPage, AvatarViewShadowsViewModel, ViewsGalleryPage, ViewsGalleryViewModel>(),
|
||||
CreateViewModelMapping<AvatarViewShapesPage, AvatarViewShapesViewModel, ViewsGalleryPage, ViewsGalleryViewModel>(),
|
||||
CreateViewModelMapping<AvatarViewSizesPage, AvatarViewSizesViewModel, ViewsGalleryPage, ViewsGalleryViewModel>(),
|
||||
CreateViewModelMapping<BasicMapsPage, BasicMapsViewModel, ViewsGalleryPage, ViewsGalleryViewModel>(),
|
||||
CreateViewModelMapping<CameraViewPage, CameraViewViewModel, ViewsGalleryPage, ViewsGalleryViewModel>(),
|
||||
CreateViewModelMapping<CustomSizeAndPositionPopupPage, CustomSizeAndPositionPopupViewModel, ViewsGalleryPage, ViewsGalleryViewModel>(),
|
||||
CreateViewModelMapping<DrawingViewPage, DrawingViewViewModel, ViewsGalleryPage, ViewsGalleryViewModel>(),
|
||||
CreateViewModelMapping<ExpanderPage, ExpanderViewModel, ViewsGalleryPage, ViewsGalleryViewModel>(),
|
||||
|
||||
CreateViewModelMapping<BasicMapsPage, BasicMapsViewModel, ViewsGalleryPage, ViewsGalleryViewModel>(),
|
||||
CreateViewModelMapping<MapsPinsPage, MapsPinsViewModel, ViewsGalleryPage, ViewsGalleryViewModel>(),
|
||||
|
||||
CreateViewModelMapping<LazyViewPage, LazyViewViewModel, ViewsGalleryPage, ViewsGalleryViewModel>(),
|
||||
CreateViewModelMapping<MapsPinsPage, MapsPinsViewModel, ViewsGalleryPage, ViewsGalleryViewModel>(),
|
||||
CreateViewModelMapping<MediaElementPage, MediaElementViewModel, ViewsGalleryPage, ViewsGalleryViewModel>(),
|
||||
|
||||
CreateViewModelMapping<CustomSizeAndPositionPopupPage, CustomSizeAndPositionPopupViewModel, ViewsGalleryPage, ViewsGalleryViewModel>(),
|
||||
CreateViewModelMapping<MediaElementCarouselViewPage, MediaElementCarouselViewViewModel, ViewsGalleryPage, ViewsGalleryViewModel>(),
|
||||
CreateViewModelMapping<MediaElementCollectionViewPage, MediaElementCollectionViewViewModel, ViewsGalleryPage, ViewsGalleryViewModel>(),
|
||||
CreateViewModelMapping<MultiplePopupPage, MultiplePopupViewModel, ViewsGalleryPage, ViewsGalleryViewModel>(),
|
||||
CreateViewModelMapping<PopupAnchorPage, PopupAnchorViewModel, ViewsGalleryPage, ViewsGalleryViewModel>(),
|
||||
CreateViewModelMapping<PopupLayoutAlignmentPage, PopupLayoutAlignmentViewModel, ViewsGalleryPage, ViewsGalleryViewModel>(),
|
||||
|
@ -174,7 +173,7 @@ public partial class AppShell : Shell
|
|||
return uri.Uri.OriginalString[..^1];
|
||||
}
|
||||
|
||||
static string GetPageRoute(Type galleryPageType, Type contentPageType) => $"/{galleryPageType.Name}/{contentPageType.Name}";
|
||||
static string GetPageRoute(Type galleryPageType, Type contentPageType) => $"//{galleryPageType.Name}/{contentPageType.Name}";
|
||||
|
||||
static KeyValuePair<Type, (Type GalleryPageType, Type ContentPageType)> CreateViewModelMapping<TPage, TViewModel, TGalleryPage, TGalleryViewModel>() where TPage : BasePage<TViewModel>
|
||||
where TViewModel : BaseViewModel
|
||||
|
|
|
@ -229,17 +229,16 @@ public static class MauiProgram
|
|||
services.AddTransientWithShellRoute<NavigationBarPage, NavigationBarAndroidViewModel>();
|
||||
|
||||
// Add Views Pages + ViewModels
|
||||
services.AddTransientWithShellRoute<BasicMapsPage, BasicMapsViewModel>();
|
||||
services.AddTransientWithShellRoute<CameraViewPage, CameraViewViewModel>();
|
||||
services.AddTransientWithShellRoute<CustomSizeAndPositionPopupPage, CustomSizeAndPositionPopupViewModel>();
|
||||
services.AddTransientWithShellRoute<DrawingViewPage, DrawingViewViewModel>();
|
||||
services.AddTransientWithShellRoute<ExpanderPage, ExpanderViewModel>();
|
||||
|
||||
services.AddTransientWithShellRoute<BasicMapsPage, BasicMapsViewModel>();
|
||||
services.AddTransientWithShellRoute<MapsPinsPage, MapsPinsViewModel>();
|
||||
|
||||
services.AddTransientWithShellRoute<LazyViewPage, LazyViewViewModel>();
|
||||
services.AddTransientWithShellRoute<MapsPinsPage, MapsPinsViewModel>();
|
||||
services.AddTransientWithShellRoute<MediaElementPage, MediaElementViewModel>();
|
||||
|
||||
services.AddTransientWithShellRoute<CustomSizeAndPositionPopupPage, CustomSizeAndPositionPopupViewModel>();
|
||||
services.AddTransientWithShellRoute<MediaElementCarouselViewPage, MediaElementCarouselViewViewModel>();
|
||||
services.AddTransientWithShellRoute<MediaElementCollectionViewPage, MediaElementCollectionViewViewModel>();
|
||||
services.AddTransientWithShellRoute<MultiplePopupPage, MultiplePopupViewModel>();
|
||||
services.AddTransientWithShellRoute<PopupAnchorPage, PopupAnchorViewModel>();
|
||||
services.AddTransientWithShellRoute<PopupLayoutAlignmentPage, PopupLayoutAlignmentViewModel>();
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<pages:BasePage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
xmlns:pages="clr-namespace:CommunityToolkit.Maui.Sample.Pages"
|
||||
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
|
||||
xmlns:viewModels="clr-namespace:CommunityToolkit.Maui.Sample.ViewModels.Views"
|
||||
x:TypeArguments="viewModels:MediaElementCarouselViewViewModel"
|
||||
x:DataType="viewModels:MediaElementCarouselViewViewModel"
|
||||
x:Class="CommunityToolkit.Maui.Sample.Pages.Views.MediaElementCarouselViewPage"
|
||||
Padding="0, 20, 0, 0"
|
||||
Title="MediaElement in CarouselView">
|
||||
<VerticalStackLayout Spacing="12">
|
||||
<Label HorizontalTextAlignment="Center"
|
||||
VerticalTextAlignment="Center"
|
||||
HorizontalOptions="Center"
|
||||
VerticalOptions="Center"
|
||||
Text="This page demonstrates that the MediaElement can be used inside of a DataTemplate"
|
||||
Margin="12,0,12,0"/>
|
||||
<CarouselView HeightRequest="275" PeekAreaInsets="52" ItemsSource="{Binding ItemSource}">
|
||||
|
||||
<CarouselView.ItemsLayout>
|
||||
<LinearItemsLayout Orientation="Horizontal"
|
||||
ItemSpacing="24"
|
||||
SnapPointsAlignment="Center"
|
||||
SnapPointsType="MandatorySingle" />
|
||||
</CarouselView.ItemsLayout>
|
||||
|
||||
<CarouselView.ItemTemplate>
|
||||
<DataTemplate x:DataType="viewModels:MediaElementDataSource">
|
||||
<Border
|
||||
x:Name="CarouselViewBorder"
|
||||
BackgroundColor="Black"
|
||||
Padding="5">
|
||||
<Border.StrokeShape>
|
||||
<RoundRectangle CornerRadius="4" />
|
||||
</Border.StrokeShape>
|
||||
<VerticalStackLayout Spacing="6" HeightRequest="250">
|
||||
<toolkit:MediaElement
|
||||
HeightRequest="200"
|
||||
x:Name="MediaElement"
|
||||
ShouldAutoPlay="True"
|
||||
ShouldShowPlaybackControls="True"
|
||||
Source="{Binding Source, Mode=OneTime}" />
|
||||
<Label TextColor="White"
|
||||
HorizontalOptions="Center"
|
||||
VerticalOptions="Center"
|
||||
HorizontalTextAlignment="Center"
|
||||
VerticalTextAlignment="Center"
|
||||
FontAttributes="Bold"
|
||||
FontSize="18"
|
||||
Text="{Binding Name, Mode=OneTime}"/>
|
||||
</VerticalStackLayout>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</CarouselView.ItemTemplate>
|
||||
|
||||
</CarouselView>
|
||||
|
||||
<Label Text="Swipe Left or Right to see next video"
|
||||
HorizontalOptions="Center"
|
||||
VerticalOptions="Center"
|
||||
HorizontalTextAlignment="Center"
|
||||
VerticalTextAlignment="Center"
|
||||
FontAttributes="Italic"/>
|
||||
|
||||
</VerticalStackLayout>
|
||||
</pages:BasePage>
|
|
@ -0,0 +1,11 @@
|
|||
using CommunityToolkit.Maui.Sample.ViewModels.Views;
|
||||
|
||||
namespace CommunityToolkit.Maui.Sample.Pages.Views;
|
||||
|
||||
public partial class MediaElementCarouselViewPage : BasePage<MediaElementCarouselViewViewModel>
|
||||
{
|
||||
public MediaElementCarouselViewPage(MediaElementCarouselViewViewModel viewModel) : base(viewModel)
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<pages:BasePage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
xmlns:pages="clr-namespace:CommunityToolkit.Maui.Sample.Pages"
|
||||
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
|
||||
xmlns:viewModels="clr-namespace:CommunityToolkit.Maui.Sample.ViewModels.Views"
|
||||
x:TypeArguments="viewModels:MediaElementCollectionViewViewModel"
|
||||
x:DataType="viewModels:MediaElementCollectionViewViewModel"
|
||||
x:Class="CommunityToolkit.Maui.Sample.Pages.Views.MediaElementCollectionViewPage"
|
||||
Padding="0, 20, 0, 0"
|
||||
Title="MediaElement in CollectionView">
|
||||
<ScrollView>
|
||||
<VerticalStackLayout Spacing="12">
|
||||
<Label HorizontalTextAlignment="Center"
|
||||
VerticalTextAlignment="Center"
|
||||
HorizontalOptions="Center"
|
||||
VerticalOptions="Center"
|
||||
Text="This page demonstrates that the MediaElement can be used inside of a DataTemplate"
|
||||
Margin="12,0,12,0"/>
|
||||
<CollectionView HeightRequest="850" ItemsSource="{Binding ItemSource}">
|
||||
|
||||
<CollectionView.ItemsLayout>
|
||||
<GridItemsLayout Orientation="Vertical"
|
||||
Span="1"
|
||||
SnapPointsAlignment="Center"
|
||||
SnapPointsType="MandatorySingle" />
|
||||
</CollectionView.ItemsLayout>
|
||||
|
||||
<CollectionView.ItemTemplate>
|
||||
<DataTemplate x:DataType="viewModels:MediaElementDataSource">
|
||||
<Border
|
||||
x:Name="CollectionViewBorder"
|
||||
BackgroundColor="Black"
|
||||
Padding="5">
|
||||
<Border.StrokeShape>
|
||||
<RoundRectangle CornerRadius="4" />
|
||||
</Border.StrokeShape>
|
||||
<VerticalStackLayout Spacing="6" HeightRequest="250">
|
||||
<toolkit:MediaElement
|
||||
HeightRequest="200"
|
||||
x:Name="MediaElement"
|
||||
ShouldAutoPlay="True"
|
||||
ShouldShowPlaybackControls="True"
|
||||
Source="{Binding Source, Mode=OneTime}" />
|
||||
<Label TextColor="White"
|
||||
HorizontalOptions="Center"
|
||||
VerticalOptions="Center"
|
||||
HorizontalTextAlignment="Center"
|
||||
VerticalTextAlignment="Center"
|
||||
FontAttributes="Bold"
|
||||
FontSize="18"
|
||||
Text="{Binding Name, Mode=OneTime}"/>
|
||||
</VerticalStackLayout>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</CollectionView.ItemTemplate>
|
||||
|
||||
</CollectionView>
|
||||
|
||||
</VerticalStackLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
</pages:BasePage>
|
|
@ -0,0 +1,11 @@
|
|||
using CommunityToolkit.Maui.Sample.ViewModels.Views;
|
||||
|
||||
namespace CommunityToolkit.Maui.Sample.Pages.Views;
|
||||
|
||||
public partial class MediaElementCollectionViewPage : BasePage<MediaElementCollectionViewViewModel>
|
||||
{
|
||||
public MediaElementCollectionViewPage(MediaElementCollectionViewViewModel viewModel) : base(viewModel)
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
using System.ComponentModel;
|
||||
using CommunityToolkit.Maui.Core;
|
||||
using CommunityToolkit.Maui.Core.Primitives;
|
||||
using CommunityToolkit.Maui.Sample.ViewModels.Views;
|
||||
using CommunityToolkit.Maui.Views;
|
||||
|
@ -27,7 +26,7 @@ public partial class MediaElementPage : BasePage<MediaElementViewModel>
|
|||
|
||||
void MediaElement_PropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == MediaElement.DurationProperty.PropertyName)
|
||||
if (e.PropertyName == CommunityToolkit.Maui.Views.MediaElement.DurationProperty.PropertyName)
|
||||
{
|
||||
logger.LogInformation("Duration: {newDuration}", MediaElement.Duration);
|
||||
PositionSlider.Maximum = MediaElement.Duration.TotalSeconds;
|
||||
|
@ -238,7 +237,7 @@ public partial class MediaElementPage : BasePage<MediaElementViewModel>
|
|||
void DisplayPopup(object sender, EventArgs e)
|
||||
{
|
||||
MediaElement.Pause();
|
||||
MediaElement popupMediaElement = new MediaElement
|
||||
var popupMediaElement = new Maui.Views.MediaElement
|
||||
{
|
||||
Source = MediaSource.FromResource("AppleVideo.mp4"),
|
||||
HeightRequest = 600,
|
||||
|
@ -250,12 +249,12 @@ public partial class MediaElementPage : BasePage<MediaElementViewModel>
|
|||
{
|
||||
VerticalOptions = LayoutAlignment.Center,
|
||||
HorizontalOptions = LayoutAlignment.Center,
|
||||
};
|
||||
popup.Content = new StackLayout
|
||||
{
|
||||
Children =
|
||||
Content = new StackLayout
|
||||
{
|
||||
popupMediaElement,
|
||||
Children =
|
||||
{
|
||||
popupMediaElement,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
using System.Collections.ObjectModel;
|
||||
namespace CommunityToolkit.Maui.Sample.ViewModels.Views;
|
||||
|
||||
public partial class MediaElementCarouselViewViewModel : BaseViewModel
|
||||
{
|
||||
public ObservableCollection<MediaElementDataSource> ItemSource { get; } =
|
||||
[
|
||||
new(new Uri("https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"), "Buck Bunny", 720, 1280),
|
||||
new(new Uri("https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4"), "Elephants Dream", 720, 1280),
|
||||
new(new Uri("https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/Sintel.mp4"), "Sintel", 546, 1280)
|
||||
];
|
||||
}
|
||||
|
||||
public record MediaElementDataSource(Uri Source, string Name, int VideoHeight, int VideoWidth);
|
|
@ -0,0 +1,12 @@
|
|||
using System.Collections.ObjectModel;
|
||||
namespace CommunityToolkit.Maui.Sample.ViewModels.Views;
|
||||
|
||||
public partial class MediaElementCollectionViewViewModel : BaseViewModel
|
||||
{
|
||||
public ObservableCollection<MediaElementDataSource> ItemSource { get; } =
|
||||
[
|
||||
new(new Uri("https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"), "Buck Bunny", 720, 1280),
|
||||
new(new Uri("https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4"), "Elephants Dream", 720, 1280),
|
||||
new(new Uri("https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/Sintel.mp4"), "Sintel", 546, 1280)
|
||||
];
|
||||
}
|
|
@ -21,9 +21,11 @@ public sealed class ViewsGalleryViewModel() : BaseGalleryViewModel(
|
|||
SectionModel.Create<DrawingViewViewModel>("DrawingView", Colors.Red, "DrawingView provides a canvas for users to \"paint\" on the screen. The drawing can also be captured and displayed as an Image."),
|
||||
SectionModel.Create<ExpanderViewModel>("Expander Page", Colors.Red, "Expander allows collapse and expand content."),
|
||||
SectionModel.Create<BasicMapsViewModel>("Windows Maps Basic Page", Colors.Red, "A page demonstrating a basic example of .NET MAUI Maps for Windows."),
|
||||
SectionModel.Create<MapsPinsViewModel>("Windows Maps Pins Page", Colors.Red, "A page demonstrating .NET MAUI Maps for Windows with Pins."),
|
||||
SectionModel.Create<LazyViewViewModel>("LazyView", Colors.Red, "LazyView is a view that allows you to load its children in a delayed manner."),
|
||||
SectionModel.Create<MapsPinsViewModel>("Windows Maps Pins Page", Colors.Red, "A page demonstrating .NET MAUI Maps for Windows with Pins."),
|
||||
SectionModel.Create<MediaElementViewModel>("MediaElement", Colors.Red, "MediaElement is a view for playing video and audio"),
|
||||
SectionModel.Create<MediaElementCarouselViewViewModel>("MediaElement in CarouselView", Colors.Red, "MediaElement can be used inside a DataTemplate"),
|
||||
SectionModel.Create<MediaElementCollectionViewViewModel>("MediaElement in CollectionView", Colors.Red, "MediaElement can be used inside a DataTemplate"),
|
||||
SectionModel.Create<MultiplePopupViewModel>("Multiple Popups Page", Colors.Red, "A page demonstrating multiple different Popups"),
|
||||
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"),
|
||||
|
|
|
@ -10,10 +10,10 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentAssertions" Version="6.12.0" />
|
||||
<PackageReference Include="FluentAssertions" Version="6.12.1" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.XUnit" Version="1.1.2" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing.XUnit" Version="1.1.2" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
|
||||
<PackageReference Include="xunit" Version="2.9.0" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" PrivateAssets="All" />
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.2" PrivateAssets="All" />
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using CommunityToolkit.Maui.Core.Extensions;
|
||||
using Microsoft.Maui.ApplicationModel;
|
||||
using Microsoft.Maui.Handlers;
|
||||
|
||||
|
@ -56,6 +57,12 @@ public class MauiPopup : UIViewController
|
|||
}
|
||||
|
||||
SetElementSize(new Size(View.Bounds.Width, View.Bounds.Height));
|
||||
|
||||
if (VirtualView is not null)
|
||||
{
|
||||
PopupExtensions.SetSize(this, VirtualView);
|
||||
PopupExtensions.SetLayout(this, VirtualView);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
@ -177,6 +184,11 @@ public class MauiPopup : UIViewController
|
|||
view.Bounds = new CGRect(0, 0, PreferredContentSize.Width, PreferredContentSize.Height);
|
||||
AddChildViewController(control.ViewController);
|
||||
|
||||
view.SafeTopAnchor().ConstraintEqualTo(control.ViewController.View.SafeTopAnchor()).Active = true;
|
||||
view.SafeBottomAnchor().ConstraintEqualTo(control.ViewController.View.SafeBottomAnchor()).Active = true;
|
||||
view.SafeLeadingAnchor().ConstraintEqualTo(control.ViewController.View.SafeLeadingAnchor()).Active = true;
|
||||
view.SafeTrailingAnchor().ConstraintEqualTo(control.ViewController.View.SafeTrailingAnchor()).Active = true;
|
||||
|
||||
if (VirtualView is not null)
|
||||
{
|
||||
this.SetBackgroundColor(VirtualView);
|
||||
|
|
|
@ -1,12 +1,17 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Reflection;
|
||||
using AVKit;
|
||||
using CommunityToolkit.Maui.Core.Views;
|
||||
using CommunityToolkit.Maui.Views;
|
||||
using Microsoft.Maui.Controls.Handlers.Items;
|
||||
using Microsoft.Maui.Handlers;
|
||||
|
||||
namespace CommunityToolkit.Maui.Core.Handlers;
|
||||
|
||||
public partial class MediaElementHandler : ViewHandler<MediaElement, MauiMediaElement>, IDisposable
|
||||
{
|
||||
AVPlayerViewController? playerViewController;
|
||||
|
||||
/// <inheritdoc/>
|
||||
/// <exception cref="NullReferenceException">Thrown if <see cref="MauiContext"/> is <see langword="null"/>.</exception>
|
||||
protected override MauiMediaElement CreatePlatformView()
|
||||
|
@ -17,53 +22,79 @@ public partial class MediaElementHandler : ViewHandler<MediaElement, MauiMediaEl
|
|||
}
|
||||
|
||||
mediaManager ??= new(MauiContext,
|
||||
VirtualView,
|
||||
Dispatcher.GetForCurrentThread() ?? throw new InvalidOperationException($"{nameof(IDispatcher)} cannot be null"));
|
||||
VirtualView,
|
||||
Dispatcher.GetForCurrentThread() ?? throw new InvalidOperationException($"{nameof(IDispatcher)} cannot be null"));
|
||||
|
||||
var (_, playerViewController) = mediaManager.CreatePlatformView();
|
||||
(_, playerViewController) = mediaManager.CreatePlatformView();
|
||||
|
||||
if (VirtualView.TryFindParent<Page>(out var page))
|
||||
if (VirtualView.TryFindParent<Page>(out var page)
|
||||
&& page.Handler is PageHandler { ViewController: not null } pageHandler)
|
||||
{
|
||||
var parentViewController = (page.Handler as PageHandler)?.ViewController;
|
||||
return new(playerViewController, parentViewController);
|
||||
return new(playerViewController, pageHandler.ViewController);
|
||||
}
|
||||
|
||||
// The top-most parent is null when MediaElement is placed in a DataTemplate because DataTemplates defer loading until they are about to be displayed on the screen
|
||||
// Subscribe to ParentChanged and set the UIViewController once the DataTemplate's Parent has been set
|
||||
VirtualView.GetTopMostParent().ParentChanged += HandleMediaElementParentChanged;
|
||||
return new(playerViewController, null);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void ConnectHandler(MauiMediaElement platformView)
|
||||
{
|
||||
base.ConnectHandler(platformView);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void DisconnectHandler(MauiMediaElement platformView)
|
||||
{
|
||||
platformView.Dispose();
|
||||
Dispose();
|
||||
|
||||
base.DisconnectHandler(platformView);
|
||||
}
|
||||
|
||||
partial void PlatformDispose()
|
||||
{
|
||||
playerViewController?.Dispose();
|
||||
playerViewController = null;
|
||||
}
|
||||
|
||||
void HandleMediaElementParentChanged(object? sender, EventArgs e)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(sender);
|
||||
|
||||
if (playerViewController is null)
|
||||
{
|
||||
throw new InvalidOperationException($"{nameof(playerViewController)} must be set in the {nameof(CreatePlatformView)} method");
|
||||
}
|
||||
|
||||
if (VirtualView.TryFindParent<ItemsView>(out var itemsView) && itemsView.Handler is not null)
|
||||
{
|
||||
var parentViewController = itemsView.Handler switch
|
||||
{
|
||||
CarouselViewHandler carouselViewHandler => carouselViewHandler.ViewController ?? GetInternalController(carouselViewHandler),
|
||||
CollectionViewHandler collectionViewHandler => collectionViewHandler.ViewController ?? GetInternalController(collectionViewHandler),
|
||||
_ => throw new NotSupportedException($"{itemsView.Handler.GetType()} not yet supported")
|
||||
};
|
||||
|
||||
PlatformView.UpdateParentViewController(playerViewController, parentViewController);
|
||||
|
||||
VirtualView.ParentChanged -= HandleMediaElementParentChanged;
|
||||
}
|
||||
|
||||
// The Controller we need is a `protected internal` property in the ItemsViewContoller class: https://github.com/dotnet/maui/blob/cf002538cb73db4bf187a51e4786d7478a7025ee/src/Controls/src/Core/Handlers/Items/ItemsViewHandler.iOS.cs#L39
|
||||
// In this method, we must use reflection to get the value of its backing field
|
||||
static ItemsViewController<TItemsView> GetInternalController<TItemsView>(ItemsViewHandler<TItemsView> handler) where TItemsView : ItemsView
|
||||
{
|
||||
var nonPublicInstanceFields = typeof(ItemsViewHandler<TItemsView>).GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
|
||||
var controllerProperty = nonPublicInstanceFields.Single(x => x.FieldType == typeof(ItemsViewController<TItemsView>));
|
||||
return (ItemsViewController<TItemsView>)(controllerProperty.GetValue(handler) ?? throw new InvalidOperationException($"Unable to get the value for the Controller property on {handler.GetType()}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class ParentPage
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension method to find the Parent of <see cref="VisualElement"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="child"></param>
|
||||
/// <param name="parent"></param>
|
||||
/// <returns></returns>
|
||||
public static bool TryFindParent<T>(this VisualElement? child, [NotNullWhen(true)] out T? parent) where T : VisualElement
|
||||
{
|
||||
while (true)
|
||||
while (child is not null)
|
||||
{
|
||||
if (child is null)
|
||||
{
|
||||
parent = null;
|
||||
return false;
|
||||
}
|
||||
if (child.Parent is T element)
|
||||
{
|
||||
parent = element;
|
||||
|
@ -72,5 +103,18 @@ static class ParentPage
|
|||
|
||||
child = child.Parent as VisualElement;
|
||||
}
|
||||
|
||||
parent = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Element GetTopMostParent(this Element child)
|
||||
{
|
||||
while (child.Parent is not null)
|
||||
{
|
||||
child = child.Parent;
|
||||
}
|
||||
|
||||
return child;
|
||||
}
|
||||
}
|
|
@ -226,7 +226,10 @@ public partial class MediaElementHandler
|
|||
{
|
||||
mediaManager?.Dispose();
|
||||
mediaManager = null;
|
||||
PlatformDispose();
|
||||
}
|
||||
}
|
||||
|
||||
partial void PlatformDispose();
|
||||
#endif
|
||||
}
|
|
@ -40,4 +40,19 @@ public class MauiMediaElement : UIView
|
|||
#endif
|
||||
AddSubview(playerViewController.View);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds PlayerViewController to the Parent ViewController
|
||||
/// </summary>
|
||||
/// <param name="playerViewController"></param>
|
||||
/// <param name="parentViewController"></param>
|
||||
public void UpdateParentViewController(AVPlayerViewController playerViewController, UIViewController parentViewController)
|
||||
{
|
||||
parentViewController.AddChildViewController(playerViewController);
|
||||
|
||||
if (playerViewController.View is not null)
|
||||
{
|
||||
AddSubview(playerViewController.View);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,9 +9,9 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AutoFixture.Xunit2" Version="4.18.1" />
|
||||
<PackageReference Include="FluentAssertions" Version="6.12.0" />
|
||||
<PackageReference Include="FluentAssertions" Version="6.12.1" />
|
||||
<PackageReference Include="Nito.AsyncEx" Version="5.1.2" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
|
||||
<PackageReference Include="xunit" Version="2.9.0" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" PrivateAssets="All" />
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.2" PrivateAssets="All" />
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
using CommunityToolkit.Maui.Converters;
|
||||
using Xunit;
|
||||
|
||||
namespace CommunityToolkit.Maui.UnitTests.Converters;
|
||||
public class ColorToHexArgbStringConverterTests : BaseConverterTest<ColorToHexArgbStringConverter>
|
||||
{
|
||||
public static readonly IReadOnlyList<object[]> ValidInputData =
|
||||
[
|
||||
[int.MinValue, int.MinValue, int.MinValue, int.MinValue, "#00000000"],
|
||||
[int.MaxValue, int.MinValue, int.MinValue, int.MinValue, "#FF000000"],
|
||||
[int.MinValue, 0, 0, 0, "#00000000"],
|
||||
[-0.5, 0, 0, 0, "#00000000"],
|
||||
[0, 0, 0, 0, "#00000000"],
|
||||
[0.5, 0, 0, 0, "#7F000000"],
|
||||
[1, 0, 0, 0, "#FF000000"],
|
||||
[int.MaxValue, 0, 0, 0, "#FF000000"],
|
||||
[int.MinValue, int.MaxValue, int.MaxValue, int.MaxValue, "#00FFFFFF"],
|
||||
[int.MaxValue, int.MaxValue, int.MaxValue, int.MaxValue, "#FFFFFFFF"],
|
||||
[0, 1, 0, 0, "#00FF0000"],
|
||||
[1, 1, 0, 0, "#FFFF0000"],
|
||||
[0, 1, 1, 0, "#00FFFF00"],
|
||||
[1, 1, 1, 0, "#FFFFFF00"],
|
||||
[0, 0, 1, 0, "#0000FF00"],
|
||||
[1, 0, 1, 0, "#FF00FF00"],
|
||||
[0, 0, 1, 1, "#0000FFFF"],
|
||||
[1, 0, 1, 1, "#FF00FFFF"],
|
||||
[0, 1, 0, 1, "#00FF00FF"],
|
||||
[1, 1, 0, 1, "#FFFF00FF"],
|
||||
[0, 0, 0.5, 1, "#00007FFF"],
|
||||
[0, 0, 0.5, 0, "#00007F00"],
|
||||
[0.5, 0.5, 0.5, 1, "#7F7F7FFF"],
|
||||
[0.5, 0.5, 0.5, 0, "#7F7F7F00"],
|
||||
[0.25, 0.25, 0.25, 1, "#3F3F3FFF"],
|
||||
[0.25, 0.25, 0.25, 0, "#3F3F3F00"],
|
||||
[0.25, 1, 0.25, 1, "#3FFF3FFF"],
|
||||
[0.25, 1, 0.25, 0, "#3FFF3F00"],
|
||||
[0.75, 1, 0.25, 1, "#BFFF3FFF"],
|
||||
[0.75, 1, 0.25, 0, "#BFFF3F00"],
|
||||
[0.75, 0, 1, 1, "#BF00FFFF"],
|
||||
[0.75, 0, 1, 0, "#BF00FF00"]
|
||||
];
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(ValidInputData))]
|
||||
public void ColorToHexArgbStringConverterValidInputTest(float alpha, float red, float green, float blue, string expectedResult)
|
||||
{
|
||||
var converter = new ColorToHexArgbStringConverter();
|
||||
var color = new Color(red, green, blue, alpha);
|
||||
|
||||
var resultConvert = ((ICommunityToolkitValueConverter)converter).Convert(color, typeof(string), null, null);
|
||||
var resultConvertFrom = converter.ConvertFrom(color);
|
||||
|
||||
Assert.Equal(expectedResult, resultConvert);
|
||||
Assert.Equal(expectedResult, resultConvertFrom);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(ValidInputData))]
|
||||
public void ColorToHexArgbStringConverterConvertBackValidInputTest(float alpha, float red, float green, float blue, string expectedResult)
|
||||
{
|
||||
var converter = new ColorToHexArgbStringConverter();
|
||||
var color = new Color(red, green, blue, alpha);
|
||||
|
||||
var resultConvert = ((ICommunityToolkitValueConverter)converter).ConvertBack(expectedResult, typeof(Color), null, null);
|
||||
var resultConvertTo = converter.ConvertBackTo(expectedResult);
|
||||
|
||||
Assert.Equal(color, resultConvert);
|
||||
Assert.Equal(color, resultConvertTo);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ColorToHexArgbStringConverterNullInputTest()
|
||||
{
|
||||
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
|
||||
Assert.Throws<ArgumentNullException>(() => new ColorToHexArgbStringConverter().ConvertFrom(null));
|
||||
Assert.Throws<ArgumentNullException>(() => ((ICommunityToolkitValueConverter)new ColorToHexArgbStringConverter()).Convert(null, typeof(string), null, null));
|
||||
Assert.Throws<ArgumentNullException>(() => ((ICommunityToolkitValueConverter)new ColorToHexArgbStringConverter()).Convert(new Color(), null, null, null));
|
||||
#pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type.
|
||||
}
|
||||
}
|
|
@ -116,6 +116,32 @@ public class ColorToHexRgbaStringConverter : BaseConverter<Color, string>
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the incoming value from <see cref="Color"/> and returns the object of a type <see cref="string"/> and vice-versa.
|
||||
/// </summary>
|
||||
public class ColorToHexArgbStringConverter : BaseConverter<Color, string>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override string DefaultConvertReturnValue { get; set; } = string.Empty;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Color DefaultConvertBackReturnValue { get; set; } = Colors.Transparent;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ConvertFrom(Color value, CultureInfo? culture = null)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(value);
|
||||
return value.ToArgbHex(true);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Color ConvertBackTo(string value, CultureInfo? culture = null)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(value);
|
||||
return Color.FromArgb(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the incoming value from <see cref="Color"/> and returns the object of a type <see cref="string"/>.
|
||||
/// </summary>
|
||||
|
|
|
@ -35,4 +35,29 @@ public partial class Popup
|
|||
return (PageHandler)contentPage.ToHandler(mauiContext);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Action that's triggered when the Popup is Closed.
|
||||
/// </summary>
|
||||
/// <param name="handler">An instance of <see cref="PopupHandler"/>.</param>
|
||||
/// <param name="view">An instance of <see cref="IPopup"/>.</param>
|
||||
/// <param name="result">We don't need to provide the result parameter here.</param>
|
||||
public static void MapOnClosed(PopupHandler handler, IPopup view, object? result)
|
||||
{
|
||||
var parent = view.Parent as Element;
|
||||
if (parent is not null)
|
||||
{
|
||||
if (handler.VirtualView is Popup popup)
|
||||
{
|
||||
if (popup.Content is not null)
|
||||
{
|
||||
if (popup.Content.Parent is ContentPage contentPage)
|
||||
{
|
||||
parent.RemoveLogicalChild(contentPage);
|
||||
}
|
||||
}
|
||||
parent.RemoveLogicalChild(popup);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,7 +11,8 @@ public partial class Popup
|
|||
public static CommandMapper<IPopup, PopupHandler> ControlPopUpCommandMapper = new(PopupHandler.PopUpCommandMapper)
|
||||
{
|
||||
#if IOS || MACCATALYST
|
||||
[nameof(IPopup.OnOpened)] = MapOnOpened
|
||||
[nameof(IPopup.OnOpened)] = MapOnOpened,
|
||||
[nameof(IPopup.OnClosed)] = MapOnClosed
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче