Fix MasterBehavior to propagate correctly to FlyoutPage (#12727) fixes #12490

This commit is contained in:
Shane Neuville 2020-11-06 05:12:01 -06:00 коммит произвёл GitHub
Родитель 48775a1f78
Коммит 454cc53618
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
7 изменённых файлов: 444 добавлений и 23 удалений

Просмотреть файл

@ -0,0 +1,403 @@
using System;
using NUnit.Framework;
using Xamarin.Forms.Internals;
namespace Xamarin.Forms.Core.UnitTests
{
[TestFixture]
public class FlyoutPageUnitTests : BaseTestFixture
{
[SetUp]
public override void Setup()
{
base.Setup();
var mockDeviceInfo = new TestDeviceInfo();
Device.Info = mockDeviceInfo;
}
[Test]
public void TestConstructor()
{
FlyoutPage page = new FlyoutPage();
Assert.Null(page.Flyout);
Assert.Null(page.Detail);
}
[Test]
public void TestFlyoutSetter()
{
FlyoutPage page = new FlyoutPage();
var child = new ContentPage { Content = new Label(), Title = "Foo" };
page.Flyout = child;
Assert.AreEqual(child, page.Flyout);
}
[Test]
public void TestFlyoutSetNull()
{
FlyoutPage page = new FlyoutPage();
var child = new ContentPage { Content = new Label(), Title = "Foo" };
page.Flyout = child;
Assert.Throws<ArgumentNullException>(() => { page.Flyout = null; });
}
[Test]
public void TestFlyoutChanged()
{
FlyoutPage page = new FlyoutPage();
var child = new ContentPage { Content = new Label(), Title = "Foo" };
bool changed = false;
page.PropertyChanged += (sender, e) =>
{
if (e.PropertyName == "Flyout")
changed = true;
};
page.Flyout = child;
Assert.True(changed);
}
[Test]
public void TestDetailSetter()
{
FlyoutPage page = new FlyoutPage();
var child = new ContentPage { Content = new Label() };
page.Detail = child;
Assert.AreEqual(child, page.Detail);
}
[Test]
public void TestDetailSetNull()
{
FlyoutPage page = new FlyoutPage();
var child = new ContentPage { Content = new Label() };
page.Detail = child;
Assert.Throws<ArgumentNullException>(() => { page.Detail = null; });
}
[Test]
public void TestDetailChanged()
{
FlyoutPage page = new FlyoutPage();
var child = new ContentPage { Content = new Label() };
bool changed = false;
page.PropertyChanged += (sender, e) =>
{
if (e.PropertyName == "Detail")
changed = true;
};
page.Detail = child;
Assert.True(changed);
}
[Test]
public void ThrowsWhenFlyoutSetWithoutValidTitle([Values(null, "")] string title)
{
var page = new FlyoutPage();
Assert.Throws<InvalidOperationException>(() => page.Flyout = new ContentPage { Title = title });
}
[Test]
public void TestThrowsWhenPackedWithoutSetting()
{
FlyoutPage page = new FlyoutPage();
Assert.Throws<InvalidOperationException>(() => new TabbedPage { Children = { page } });
}
[Test]
public void TestDoesNotThrowWhenPackedWithSetting()
{
FlyoutPage page = new FlyoutPage
{
Flyout = new ContentPage { Content = new View(), Title = "Foo" },
Detail = new ContentPage { Content = new View() }
};
Assert.DoesNotThrow(() => new TabbedPage { Children = { page } });
}
[Test]
public void TestFlyoutVisible()
{
var page = new FlyoutPage();
Assert.AreEqual(false, page.IsPresented);
bool signaled = false;
page.PropertyChanged += (sender, args) =>
{
if (args.PropertyName == FlyoutPage.IsPresentedProperty.PropertyName)
signaled = true;
};
page.IsPresented = true;
Assert.AreEqual(true, page.IsPresented);
Assert.True(signaled);
}
[Test]
public void TestFlyoutVisibleDoubleSet()
{
var page = new FlyoutPage();
bool signaled = false;
page.PropertyChanged += (sender, args) =>
{
if (args.PropertyName == FlyoutPage.IsPresentedProperty.PropertyName)
signaled = true;
};
page.IsPresented = page.IsPresented;
Assert.False(signaled);
}
[Test]
public void TestSetFlyoutBounds()
{
var page = new FlyoutPage
{
Flyout = new ContentPage { Content = new View(), Title = "Foo" },
Detail = new ContentPage { Content = new View() }
};
((IFlyoutPageController)page).FlyoutBounds = new Rectangle(0, 0, 100, 100);
Assert.AreEqual(new Rectangle(0, 0, 100, 100), page.Flyout.Bounds);
Assert.AreEqual(new Rectangle(0, 0, 100, 100), ((IFlyoutPageController)page).FlyoutBounds);
}
[Test]
public void TestSetDetailBounds()
{
var page = new FlyoutPage
{
Flyout = new ContentPage { Content = new View(), Title = "Foo" },
Detail = new ContentPage { Content = new View() }
};
((IFlyoutPageController)page).DetailBounds = new Rectangle(0, 0, 100, 100);
Assert.AreEqual(new Rectangle(0, 0, 100, 100), page.Detail.Bounds);
Assert.AreEqual(new Rectangle(0, 0, 100, 100), ((IFlyoutPageController)page).DetailBounds);
}
[Test]
public void TestLayoutChildren()
{
var page = new FlyoutPage
{
Flyout = new ContentPage { Content = new View(), IsPlatformEnabled = true, Title = "Foo" },
Detail = new ContentPage { Content = new View(), IsPlatformEnabled = true },
IsPlatformEnabled = true,
};
((IFlyoutPageController)page).FlyoutBounds = new Rectangle(0, 0, 100, 200);
((IFlyoutPageController)page).DetailBounds = new Rectangle(0, 0, 100, 100);
page.Flyout.Layout(new Rectangle(0, 0, 1, 1));
page.Detail.Layout(new Rectangle(0, 0, 1, 1));
page.Layout(new Rectangle(0, 0, 200, 200));
Assert.AreEqual(new Rectangle(0, 0, 100, 200), page.Flyout.Bounds);
Assert.AreEqual(new Rectangle(0, 0, 100, 100), page.Detail.Bounds);
}
[Test]
public void ThorwsInLayoutChildrenWithNullDetail()
{
var page = new FlyoutPage
{
Flyout = new ContentPage { Content = new View(), IsPlatformEnabled = true, Title = "Foo" },
IsPlatformEnabled = true,
};
Assert.Throws<InvalidOperationException>(() => page.Layout(new Rectangle(0, 0, 200, 200)));
}
[Test]
public void ThorwsInLayoutChildrenWithNullFlyout()
{
var page = new FlyoutPage
{
Detail = new ContentPage { Content = new View(), IsPlatformEnabled = true },
IsPlatformEnabled = true,
};
Assert.Throws<InvalidOperationException>(() => page.Layout(new Rectangle(0, 0, 200, 200)));
}
[Test]
public void ThorwsInSetDetailBoundsWithNullDetail()
{
var page = new FlyoutPage
{
Flyout = new ContentPage { Content = new View(), Title = "Foo" },
IsPlatformEnabled = true,
};
Assert.Throws<InvalidOperationException>(() => ((IFlyoutPageController)page).DetailBounds = new Rectangle(0, 0, 200, 200));
}
[Test]
public void ThrowsInSetFlyoutBoundsWithNullFlyout()
{
var page = new FlyoutPage
{
Detail = new ContentPage { Content = new View() },
IsPlatformEnabled = true,
};
Assert.Throws<InvalidOperationException>(() => ((IFlyoutPageController)page).FlyoutBounds = new Rectangle(0, 0, 200, 200));
}
[Test]
public void ThrowsInSetIsPresentOnSplitModeOnTablet()
{
Device.Idiom = TargetIdiom.Tablet;
var page = new FlyoutPage
{
Flyout = new ContentPage { Content = new View(), IsPlatformEnabled = true, Title = "Foo" },
Detail = new ContentPage { Content = new View(), IsPlatformEnabled = true },
IsPlatformEnabled = true,
FlyoutLayoutBehavior = FlyoutLayoutBehavior.Split
};
Assert.Throws<InvalidOperationException>(() => page.IsPresented = false);
}
[Test]
public void ThorwsInSetIsPresentOnSplitPortraitModeOnTablet()
{
Device.Idiom = TargetIdiom.Tablet;
Device.Info.CurrentOrientation = DeviceOrientation.Portrait;
var page = new FlyoutPage
{
Flyout = new ContentPage { Content = new View(), IsPlatformEnabled = true, Title = "Foo" },
Detail = new ContentPage { Content = new View(), IsPlatformEnabled = true },
IsPlatformEnabled = true,
FlyoutLayoutBehavior = FlyoutLayoutBehavior.SplitOnPortrait
};
Assert.Throws<InvalidOperationException>(() => page.IsPresented = false);
}
[Test]
public void TestSetIsPresentedOnPopoverMode()
{
Device.Info.CurrentOrientation = DeviceOrientation.Landscape;
var page = new FlyoutPage
{
Flyout = new ContentPage { Content = new View(), IsPlatformEnabled = true, Title = "Foo" },
Detail = new ContentPage { Content = new View(), IsPlatformEnabled = true },
IsPlatformEnabled = true,
FlyoutLayoutBehavior = FlyoutLayoutBehavior.Popover
};
page.IsPresented = true;
Assert.AreEqual(true, page.IsPresented);
}
[Test]
public void SendsBackEventToPresentedFlyoutFirst()
{
var detail = new BackButtonPage() { Handle = true };
var Flyout = new BackButtonPage() { Title = "Flyout" };
var mdp = new FlyoutPage()
{
Detail = detail,
Flyout = Flyout,
IsPresented = true,
IsPlatformEnabled = true,
};
((IFlyoutPageController)mdp).BackButtonPressed += (sender, args) =>
{
args.Handled = mdp.IsPresented;
mdp.IsPresented = false;
};
var detailEmitted = false;
var FlyoutEmitted = false;
detail.BackPressed += (sender, args) => detailEmitted = true;
Flyout.BackPressed += (sender, args) => FlyoutEmitted = true;
var result = mdp.SendBackButtonPressed();
Assert.True(FlyoutEmitted);
Assert.False(detailEmitted);
Assert.True(result);
}
[Test]
public void EmitsCorrectlyWhenPresentedOnBackPressed()
{
var detail = new BackButtonPage();
var Flyout = new BackButtonPage { Title = "Flyout" };
var mdp = new FlyoutPage
{
Detail = detail,
Flyout = Flyout,
IsPresented = true,
IsPlatformEnabled = true,
};
((IFlyoutPageController)mdp).BackButtonPressed += (sender, args) =>
{
args.Handled = mdp.IsPresented;
mdp.IsPresented = false;
};
var detailEmitted = false;
var FlyoutEmitted = false;
detail.BackPressed += (sender, args) => detailEmitted = true;
Flyout.BackPressed += (sender, args) => FlyoutEmitted = true;
var result = mdp.SendBackButtonPressed();
Assert.True(FlyoutEmitted);
Assert.False(detailEmitted);
Assert.True(result);
}
[Test]
public void ThrowsExceptionWhenAddingAlreadyParentedDetail()
{
var detail = new ContentPage { };
// give detail a parent
var nav = new NavigationPage(detail);
var mdp = new FlyoutPage();
Assert.Throws<InvalidOperationException>(() => mdp.Detail = detail);
}
[Test]
public void ThrowsExceptionWhenAddingAlreadyParentedFlyout()
{
var Flyout = new ContentPage { Title = "Foo" };
// give Flyout a parent
var nav = new NavigationPage(Flyout);
var mdp = new FlyoutPage();
Assert.Throws<InvalidOperationException>(() => mdp.Flyout = Flyout);
}
}
}

Просмотреть файл

@ -420,6 +420,18 @@ namespace Xamarin.Forms.Core.UnitTests
var mdp = new MasterDetailPage();
Assert.Throws<InvalidOperationException>(() => mdp.Master = master);
}
[Test]
public void TestFlyoutLayoutBehaviorGetsSetViaBinding()
{
var master = new MasterDetailPage { Title = "Foo" };
master.SetValue(MasterDetailPage.MasterBehaviorProperty, MasterBehavior.Split);
Assert.AreEqual(MasterBehavior.Split, master.MasterBehavior);
Assert.AreEqual(FlyoutLayoutBehavior.Split, master.FlyoutLayoutBehavior);
}
}
}

Просмотреть файл

@ -155,6 +155,7 @@ namespace Xamarin.Forms.Core.UnitTests
new PropertyTestCase<TapGestureRecognizer, object> ("CommandParameter", t => t.CommandParameter, (t, o) => t.CommandParameter = o, () => null, "Test"),
new PropertyTestCase<TapGestureRecognizer, ICommand> ("Command", t => t.Command, (t, o) => t.Command = o, () => null, new Command(()=>{})),
new PropertyTestCase<MasterDetailPage, bool> ("IsGestureEnabled", md => md.IsGestureEnabled, (md, v) => md.IsGestureEnabled = v, () => true, false),
new PropertyTestCase<FlyoutPage, bool> ("IsGestureEnabled", fp => fp.IsGestureEnabled, (fp, v) => fp.IsGestureEnabled = v, () => true, false),
new PropertyTestCase<Entry, bool> ("IsReadOnly", v => v.IsReadOnly, (v, o) => v.IsReadOnly = o, () => false, true),
new PropertyTestCase<Editor, bool> ("IsReadOnly", v => v.IsReadOnly, (v, o) => v.IsReadOnly = o, () => false, true)
};

Просмотреть файл

@ -12,7 +12,7 @@ namespace Xamarin.Forms.Core.UnitTests
[Test]
public void VendorPlatformProperty()
{
var x = new MasterDetailPage();
var x = new FlyoutPage();
Assert.IsTrue(x.On<iOS>().GetVendorFoo());
@ -24,7 +24,7 @@ namespace Xamarin.Forms.Core.UnitTests
[Test]
public void ConsumeVendorSetting()
{
var x = new MasterDetailPage();
var x = new FlyoutPage();
x.On<iOS>().SetVendorFoo(false);
Assert.IsFalse(x.On<iOS>().GetVendorFoo());
@ -33,7 +33,7 @@ namespace Xamarin.Forms.Core.UnitTests
[Test]
public void Properties()
{
var x = new MasterDetailPage();
var x = new FlyoutPage();
x.On<Android>().SetSomeAndroidThing(42);
Assert.IsTrue(x.On<Android>().GetSomeAndroidThing() == 42);
@ -42,7 +42,7 @@ namespace Xamarin.Forms.Core.UnitTests
[Test]
public void ConvenienceConfiguration()
{
var x = new MasterDetailPage();
var x = new FlyoutPage();
x.On<Android>().UseTabletDefaults();
@ -71,13 +71,13 @@ namespace ImAVendor.Forms.PlatformConfiguration.iOS
{
using Xamarin.Forms;
using Xamarin.Forms.PlatformConfiguration;
using FormsElement = Xamarin.Forms.MasterDetailPage;
using FormsElement = Xamarin.Forms.FlyoutPage;
public static class MasterDetailPage
public static class FlyoutPage
{
public static readonly BindableProperty FooProperty =
BindableProperty.Create("VendorFoo", typeof(bool),
typeof(MasterDetailPage), true);
typeof(FlyoutPage), true);
public static void SetVendorFoo(BindableObject element, bool value)
{
@ -155,17 +155,17 @@ namespace ImAVendor.Forms.PlatformConfiguration.iOS
namespace Xamarin.Forms.PlatformConfiguration.AndroidSpecific
{
using FormsElement = Xamarin.Forms.MasterDetailPage;
using FormsElement = Xamarin.Forms.FlyoutPage;
public static class MasterDetailPage
public static class FlyoutPage
{
public static readonly BindableProperty SomeAndroidThingProperty =
BindableProperty.Create("SomeAndroidThing", typeof(int),
typeof(MasterDetailPage), 1);
typeof(FlyoutPage), 1);
public static readonly BindableProperty SomeOtherAndroidThingProperty =
BindableProperty.Create("SomeOtherAndroidThing", typeof(int),
typeof(MasterDetailPage), 1);
typeof(FlyoutPage), 1);
public static int GetSomeAndroidThing(BindableObject element)
{

Просмотреть файл

@ -209,7 +209,7 @@ namespace Xamarin.Forms.Core.UnitTests
var detailPage = new ContentPage { Content = stack2 };
var mdp = new MasterDetailPage { Master = masterPage, Detail = detailPage };
var fp = new FlyoutPage { Flyout = masterPage, Detail = detailPage };
var tabIndexes = stack.GetTabIndexesOnParentPage(out int _);
@ -246,7 +246,7 @@ namespace Xamarin.Forms.Core.UnitTests
var detailPage = new ContentPage { Content = stack };
var mdp = new MasterDetailPage { Master = masterPage, Detail = detailPage };
var fp = new FlyoutPage { Flyout = masterPage, Detail = detailPage };
var tabIndexes = stack.GetTabIndexesOnParentPage(out int _);

Просмотреть файл

@ -80,6 +80,7 @@
<Compile Include="DropGestureRecognizerTests.cs" />
<Compile Include="DragGestureRecognizerTests.cs" />
<Compile Include="ItemsLayoutTypeConverterTests.cs" />
<Compile Include="FlyoutPageUnitTests.cs" />
<Compile Include="MultiBindingTests.cs" />
<Compile Include="Markup\DefaultBindablePropertiesTests.cs" />
<Compile Include="Markup\BindableObjectExtensionsTests.cs" />
@ -306,4 +307,4 @@
<Copy SourceFiles="@(_NUnitTestAdapterFiles)" DestinationFolder="$(SolutionDir)packages\NUnitTestAdapter.AnyVersion\tools\%(RecursiveDir)" ContinueOnError="true" Retries="0" />
<Copy SourceFiles="@(_NUnitTestAdapterFiles)" DestinationFolder="$(SolutionDir)packages\NUnitTestAdapter.AnyVersion\build\%(RecursiveDir)" ContinueOnError="true" Retries="0" />
</Target>
</Project>
</Project>

Просмотреть файл

@ -8,14 +8,15 @@ using Xamarin.Forms.Platform;
namespace Xamarin.Forms
{
[RenderWith(typeof(_FlyoutPageRenderer))]
[ContentProperty("Detail")]
public class FlyoutPage : Page, IFlyoutPageController, IElementConfiguration<FlyoutPage>
{
public static readonly BindableProperty IsGestureEnabledProperty = BindableProperty.Create("IsGestureEnabled", typeof(bool), typeof(FlyoutPage), true);
public static readonly BindableProperty IsGestureEnabledProperty = BindableProperty.Create(nameof(IsGestureEnabled), typeof(bool), typeof(FlyoutPage), true);
public static readonly BindableProperty IsPresentedProperty = BindableProperty.Create("IsPresented", typeof(bool), typeof(FlyoutPage), default(bool),
public static readonly BindableProperty IsPresentedProperty = BindableProperty.Create(nameof(IsPresented), typeof(bool), typeof(FlyoutPage), default(bool),
propertyChanged: OnIsPresentedPropertyChanged, propertyChanging: OnIsPresentedPropertyChanging, defaultValueCreator: GetDefaultValue);
public static readonly BindableProperty FlyoutLayoutBehaviorProperty = BindableProperty.Create("FlyoutLayoutBehavior", typeof(FlyoutLayoutBehavior), typeof(FlyoutPage), default(FlyoutLayoutBehavior),
public static readonly BindableProperty FlyoutLayoutBehaviorProperty = BindableProperty.Create(nameof(FlyoutLayoutBehavior), typeof(FlyoutLayoutBehavior), typeof(FlyoutPage), default(FlyoutLayoutBehavior),
propertyChanged: OnFlyoutLayoutBehaviorPropertyChanged);
Page _detail;
@ -258,9 +259,14 @@ namespace Xamarin.Forms
[RenderWith(typeof(_MasterDetailPageRenderer))]
public class MasterDetailPage : FlyoutPage, IMasterDetailPageController
{
public static readonly BindableProperty MasterBehaviorProperty = BindableProperty.Create(nameof(MasterBehavior), typeof(MasterBehavior), typeof(MasterDetailPage), default(MasterBehavior),
propertyChanged: OnMasterBehaviorPropertyChanged);
public static readonly BindableProperty MasterBehaviorProperty = FlyoutPage.FlyoutLayoutBehaviorProperty;
static void OnMasterBehaviorPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
if((int)bindable.GetValue(FlyoutLayoutBehaviorProperty) != (int)newValue)
bindable.SetValue(FlyoutLayoutBehaviorProperty, (FlyoutLayoutBehavior)((int)newValue));
}
public Page Master
{
@ -268,7 +274,6 @@ namespace Xamarin.Forms
set => base.Flyout = value;
}
[EditorBrowsable(EditorBrowsableState.Never)]
public Rectangle MasterBounds
{
@ -278,8 +283,8 @@ namespace Xamarin.Forms
public MasterBehavior MasterBehavior
{
get => (MasterBehavior)((int)FlyoutLayoutBehavior);
set => FlyoutLayoutBehavior = (FlyoutLayoutBehavior)((int)value);
get => (MasterBehavior)GetValue(MasterBehaviorProperty);
set => SetValue(MasterBehaviorProperty, value);
}
protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
@ -288,7 +293,6 @@ namespace Xamarin.Forms
if (propertyName == nameof(Flyout))
OnPropertyChanged(nameof(Master));
if (propertyName == nameof(FlyoutLayoutBehavior))
OnPropertyChanged(nameof(Flyout));
}