[All] Basic Right-To-Left Support (#1222)

* Restart RTL work

* Remove IsInherited flag as it never got used

* [Core] Unit tests

* [Core] FlowDirection

* Add FlowDirectionGallery

* Android gallery supports RTL

Need to set minSdkVersion to 17 to test

* iOS gallery supports RTL

* UWP gallery supports RTL

* [Android] Implement FlowDirection

* [iOS] Implement FlowDirection

* [macOS] Implement FlowDirection

* [UWP] Implement FlowDirection

* Update docs

* [Core] Simplify EffectiveFlowDirection enum & expose helper extensions

Also, TEST TEST TEST

* Update docs
This commit is contained in:
Samantha Houts 2017-11-09 06:31:15 -08:00 коммит произвёл Rui Marinho
Родитель 0a0f8f092e
Коммит d3d59ee4f0
87 изменённых файлов: 3236 добавлений и 89 удалений

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

@ -16,7 +16,7 @@
<uses-permission android:name="android.permission.READ_LOGS" />
<uses-permission android:name="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE" />
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
<application android:label="AndroidControlGallery">
<application android:label="AndroidControlGallery" android:supportsRtl="true">
<meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" />
</application>
</manifest>

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

@ -209,7 +209,7 @@ namespace Xamarin.Forms.ControlGallery.WP8
//
// If a compiler error is hit then ResourceFlowDirection is missing from
// the resource file.
FlowDirection flow = (FlowDirection)Enum.Parse(typeof(FlowDirection), AppResources.ResourceFlowDirection);
System.Windows.FlowDirection flow = (System.Windows.FlowDirection)Enum.Parse(typeof(System.Windows.FlowDirection), AppResources.ResourceFlowDirection);
RootFrame.FlowDirection = flow;
}
catch

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

@ -12,6 +12,8 @@
</Dependencies>
<Resources>
<Resource Language="x-generate" />
<Resource Language="en" />
<Resource Language="ar" />
</Resources>
<Applications>
<Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="Xamarin.Forms.ControlGallery.WindowsUniversal.App">

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

@ -96,6 +96,13 @@
<string>{320, 480}</string>
</dict>
</array>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleLocalizations</key>
<array>
<string>en</string>
<string>ar</string>
</array>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads </key>

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

@ -78,7 +78,7 @@
<MtouchUseLlvm>False</MtouchUseLlvm>
<MtouchUseThumb>False</MtouchUseThumb>
<MtouchOptimizePNGs>True</MtouchOptimizePNGs>
<MtouchI18n />
<MtouchI18n>mideast</MtouchI18n>
<BuildIpa>True</BuildIpa>
<IpaMetadata />
<IpaPackageName />
@ -132,6 +132,9 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'AppStore|iPhoneSimulator'">
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup Condition=" '$(RunConfiguration)' == 'Default' ">
<AppExtensionDebugBundleId />
</PropertyGroup>
<ItemGroup>
<Compile Include="BrokenImageSourceHandler.cs" />
<Compile Include="BrokenNativeControl.cs" />

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

@ -0,0 +1,429 @@
using System;
using System.Linq;
namespace Xamarin.Forms.Controls
{
public class FlowDirectionGalleryLandingPage : ContentPage
{
FlowDirection DeviceDirection => Device.FlowDirection;
public FlowDirectionGalleryLandingPage()
{
var np = new Button { Text = "Try NavigationPage", Command = new Command(() => { PushNavigationPage(DeviceDirection); }) };
var mdp = new Button { Text = "Try MasterDetailPage", Command = new Command(() => { PushMasterDetailPage(DeviceDirection); }) };
var crp = new Button { Text = "Try CarouselPage", Command = new Command(() => { PushCarouselPage(DeviceDirection); }) };
var tp = new Button { Text = "Try TabbedPage", Command = new Command(() => { PushTabbedPage(DeviceDirection); }) };
var cp = new Button { Text = "Try ContentPage", Command = new Command(() => { PushContentPage(DeviceDirection); }) };
Content = new StackLayout
{
Children = {
new Label { Text = "Click a button below to swap the MainPage of the app." },
np,
mdp,
crp,
tp,
cp
}
};
}
public static void PushNavigationPage(FlowDirection direction)
{
((App)Application.Current).SetMainPage(new FlowDirectionGalleryNP(direction));
}
public static void PushMasterDetailPage(FlowDirection direction)
{
((App)Application.Current).SetMainPage(new FlowDirectionGalleryMDP(direction));
}
public static void PushCarouselPage(FlowDirection direction)
{
((App)Application.Current).SetMainPage(new FlowDirectionGalleryCarP(direction));
}
public static void PushTabbedPage(FlowDirection direction)
{
((App)Application.Current).SetMainPage(new FlowDirectionGalleryTP(direction));
}
public static void PushContentPage(FlowDirection direction)
{
((App)Application.Current).SetMainPage(new FlowDirectionGalleryCP(direction)
{
FlowDirection = direction
});
}
}
public class FlowDirectionGalleryNP : NavigationPage
{
public FlowDirectionGalleryNP(FlowDirection direction)
{
FlowDirection = direction;
Navigation.PushAsync(new FlowDirectionGalleryCP(direction));
Navigation.PushAsync(new FlowDirectionGalleryCP(direction));
}
}
public class FlowDirectionGalleryMDP : MasterDetailPage
{
public FlowDirectionGalleryMDP(FlowDirection direction)
{
FlowDirection = direction;
Master = new FlowDirectionGalleryCP(direction) { Title = "Master", BackgroundColor = Color.Red };
Detail = new FlowDirectionGalleryCP(direction) { Title = "Detail" };
IsPresented = true;
}
}
public class FlowDirectionGalleryCarP : CarouselPage
{
public FlowDirectionGalleryCarP(FlowDirection direction)
{
FlowDirection = direction;
Children.Add(new FlowDirectionGalleryCP(direction) { Title = "1" });
Children.Add(new FlowDirectionGalleryCP(direction) { Title = "2" });
Children.Add(new FlowDirectionGalleryCP(direction) { Title = "3" });
}
}
public class FlowDirectionGalleryTP : TabbedPage
{
public FlowDirectionGalleryTP(FlowDirection direction)
{
FlowDirection = direction;
Children.Add(new FlowDirectionGalleryCP(direction) { Title = "1", BackgroundColor = Color.Red });
Children.Add(new FlowDirectionGalleryCP(direction) { Title = "2", BackgroundColor = Color.Orange });
Children.Add(new FlowDirectionGalleryCP(direction) { Title = "3", BackgroundColor = Color.Yellow });
}
}
public class FlowDirectionGalleryCP : ContentPage
{
FlowDirection DeviceDirection => Device.FlowDirection;
Page ParentPage => (Parent as Page) ?? this;
public FlowDirectionGalleryCP(FlowDirection direction)
{
var item = new ToolbarItem
{
Icon = "coffee.png",
Text = "Item 1",
};
var item2 = new ToolbarItem
{
Icon = "bank.png",
Text = "Item 2",
};
ToolbarItems.Add(item);
ToolbarItems.Add(item2);
Title = "Flow Direction Gallery";
NavigationPage.SetHasBackButton(this, true);
NavigationPage.SetBackButtonTitle(this, "Back");
SetContent(direction);
}
void SetContent(FlowDirection direction)
{
var hOptions = LayoutOptions.Start;
var imageCell = new DataTemplate(typeof(ImageCell));
imageCell.SetBinding(ImageCell.ImageSourceProperty, ".");
imageCell.SetBinding(ImageCell.TextProperty, ".");
var textCell = new DataTemplate(typeof(TextCell));
textCell.SetBinding(TextCell.DetailProperty, ".");
var entryCell = new DataTemplate(typeof(EntryCell));
entryCell.SetBinding(EntryCell.TextProperty, ".");
var switchCell = new DataTemplate(typeof(SwitchCell));
switchCell.SetBinding(SwitchCell.OnProperty, ".");
switchCell.SetValue(SwitchCell.TextProperty, "Switch Cell!");
var vc = new ViewCell
{
View = new StackLayout
{
Children = { new Label { HorizontalOptions = hOptions, Text = "View Cell! I have context actions." } }
}
};
var a1 = new MenuItem { Text = "First" };
vc.ContextActions.Add(a1);
var a2 = new MenuItem { Text = "Second" };
vc.ContextActions.Add(a2);
var viewCell = new DataTemplate(() => vc);
var relayout = new Switch
{
IsToggled = true,
HorizontalOptions = LayoutOptions.End,
VerticalOptions = LayoutOptions.Center
};
var flipButton = new Button
{
Text = direction == FlowDirection.RightToLeft ? "Switch to Left To Right" : "Switch to Right To Left",
HorizontalOptions = LayoutOptions.StartAndExpand,
VerticalOptions = LayoutOptions.Center
};
flipButton.Clicked += (s, e) =>
{
FlowDirection newDirection;
if (direction == FlowDirection.LeftToRight || direction == FlowDirection.MatchParent)
newDirection = FlowDirection.RightToLeft;
else
newDirection = FlowDirection.LeftToRight;
if (relayout.IsToggled)
{
ParentPage.FlowDirection = newDirection;
direction = newDirection;
flipButton.Text = direction == FlowDirection.RightToLeft ? "Switch to Left To Right" : "Switch to Right To Left";
return;
}
if (ParentPage == this)
{
FlowDirectionGalleryLandingPage.PushContentPage(newDirection);
return;
}
string parentType = ParentPage.GetType().ToString();
switch (parentType)
{
case "Xamarin.Forms.Controls.FlowDirectionGalleryMDP":
FlowDirectionGalleryLandingPage.PushMasterDetailPage(newDirection);
break;
case "Xamarin.Forms.Controls.FlowDirectionGalleryCarP":
FlowDirectionGalleryLandingPage.PushCarouselPage(newDirection);
break;
case "Xamarin.Forms.Controls.FlowDirectionGalleryNP":
FlowDirectionGalleryLandingPage.PushNavigationPage(newDirection);
break;
case "Xamarin.Forms.Controls.FlowDirectionGalleryTP":
FlowDirectionGalleryLandingPage.PushTabbedPage(newDirection);
break;
}
};
var horStack = new StackLayout
{
Orientation = StackOrientation.Horizontal,
Children = { flipButton, new Label { Text = "Relayout", HorizontalOptions = LayoutOptions.End, VerticalOptions = LayoutOptions.Center }, relayout }
};
var grid = new Grid
{
ColumnDefinitions = {
new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) },
new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) },
},
RowDefinitions = {
new RowDefinition { Height = new GridLength(100, GridUnitType.Absolute) },
new RowDefinition { Height = new GridLength(100, GridUnitType.Absolute) },
new RowDefinition { Height = new GridLength(100, GridUnitType.Absolute) },
new RowDefinition { Height = new GridLength(100, GridUnitType.Absolute) },
new RowDefinition { Height = new GridLength(100, GridUnitType.Absolute) },
new RowDefinition { Height = new GridLength(100, GridUnitType.Absolute) },
new RowDefinition { Height = new GridLength(100, GridUnitType.Absolute) },
}
};
int col = 0;
int row = 0;
var ai = AddView<ActivityIndicator>(grid, ref col, ref row);
ai.IsRunning = true;
var box = AddView<BoxView>(grid, ref col, ref row);
box.WidthRequest = box.HeightRequest = 20;
box.BackgroundColor = Color.Purple;
var btn = AddView<Button>(grid, ref col, ref row);
btn.Text = "Some text";
var date = AddView<DatePicker>(grid, ref col, ref row, 2);
var edit = AddView<Editor>(grid, ref col, ref row);
edit.WidthRequest = 100;
edit.HeightRequest = 100;
edit.Text = "Some longer text for wrapping";
var entry = AddView<Entry>(grid, ref col, ref row);
entry.WidthRequest = 100;
entry.Text = "Some text";
var image = AddView<Image>(grid, ref col, ref row);
image.Source = "oasis.jpg";
var lbl1 = AddView<Label>(grid, ref col, ref row);
lbl1.WidthRequest = 100;
lbl1.HorizontalTextAlignment = TextAlignment.Start;
lbl1.Text = "Start text";
var lblLong = AddView<Label>(grid, ref col, ref row);
lblLong.WidthRequest = 100;
lblLong.HorizontalTextAlignment = TextAlignment.Start;
lblLong.Text = "Start text that should wrap and wrap and wrap";
var lbl2 = AddView<Label>(grid, ref col, ref row);
lbl2.WidthRequest = 100;
lbl2.HorizontalTextAlignment = TextAlignment.End;
lbl2.Text = "End text";
var lbl3 = AddView<Label>(grid, ref col, ref row);
lbl3.WidthRequest = 100;
lbl3.HorizontalTextAlignment = TextAlignment.Center;
lbl3.Text = "Center text";
//var ogv = AddView<OpenGLView>(grid, ref col, ref row, hOptions, vOptions, margin);
var pkr = AddView<Picker>(grid, ref col, ref row);
pkr.ItemsSource = Enumerable.Range(0, 10).ToList();
var sld = AddView<Slider>(grid, ref col, ref row);
sld.WidthRequest = 100;
sld.Maximum = 10;
Device.StartTimer(TimeSpan.FromSeconds(1), () =>
{
sld.Value += 1;
if (sld.Value == 10d)
sld.Value = 0;
return true;
});
var stp = AddView<Stepper>(grid, ref col, ref row);
var swt = AddView<Switch>(grid, ref col, ref row);
var time = AddView<TimePicker>(grid, ref col, ref row, 2);
var prog = AddView<ProgressBar>(grid, ref col, ref row, 2);
prog.WidthRequest = 200;
prog.BackgroundColor = Color.DarkGray;
Device.StartTimer(TimeSpan.FromSeconds(1), () =>
{
prog.Progress += .1;
if (prog.Progress == 1d)
prog.Progress = 0;
return true;
});
var srch = AddView<SearchBar>(grid, ref col, ref row, 2);
srch.WidthRequest = 200;
srch.Text = "Some text";
TableView tbl = new TableView
{
Intent = TableIntent.Menu,
Root = new TableRoot
{
new TableSection("TableView")
{
new TextCell
{
Text = "A",
},
new TextCell
{
Text = "B",
},
new TextCell
{
Text = "C",
},
new TextCell
{
Text = "D",
},
}
}
};
var stack = new StackLayout
{
Children = { new Button { Text = "Go back to Gallery home", Command = new Command(()=> { ((App)Application.Current).SetMainPage(((App)Application.Current).CreateDefaultMainPage()); }) },
new Label { Text = $"Device Direction: {DeviceDirection}" },
horStack,
grid,
new Label { Text = "TableView", FontSize = 10, TextColor = Color.DarkGray },
tbl,
new Label { Text = "ListView w/ TextCell", FontSize = 10, TextColor = Color.DarkGray },
new ListView { HorizontalOptions = hOptions, ItemsSource = Enumerable.Range(0, 3).Select(c => "Text Cell!"), ItemTemplate = textCell },
new Label { Text = "ListView w/ SwitchCell", FontSize = 10, TextColor = Color.DarkGray },
new ListView { HorizontalOptions = hOptions, ItemsSource = Enumerable.Range(0, 3).Select(c => true), ItemTemplate = switchCell },
new Label { Text = "ListView w/ EntryCell", FontSize = 10, TextColor = Color.DarkGray },
new ListView { HorizontalOptions = hOptions, ItemsSource = Enumerable.Range(0, 3).Select(c => "Entry Cell!"), ItemTemplate = entryCell },
new Label { Text = "ListView w/ ImageCell", FontSize = 10, TextColor = Color.DarkGray },
new ListView { HorizontalOptions = hOptions, ItemsSource = Enumerable.Range(0, 3).Select(c => "coffee.png"), ItemTemplate = imageCell },
new Label { Text = "ListView w/ ViewCell", FontSize = 10, TextColor = Color.DarkGray },
new ListView { HorizontalOptions = hOptions, ItemsSource = Enumerable.Range(0, 3), ItemTemplate = viewCell },
},
HorizontalOptions = hOptions
};
Content = new ScrollView
{
Content = stack
};
}
T AddView<T>(Grid grid, ref int col, ref int row, int colSpan = 1) where T : View
{
var hOptions = LayoutOptions.Start;
var vOptions = LayoutOptions.End;
var margin = new Thickness(0, 10);
var bgColor = Color.LightGray;
T view = (T)Activator.CreateInstance(typeof(T));
view.VerticalOptions = vOptions;
view.HorizontalOptions = hOptions;
view.Margin = margin;
view.BackgroundColor = bgColor;
var label = new Label { Text = $"({col},{row}) {typeof(T).ToString()}", FontSize = 10, TextColor = Color.DarkGray };
if (colSpan > 1 && col > 0)
NextCell(ref col, ref row, colSpan);
grid.Children.Add(label, col, col + colSpan, row, row + 1);
grid.Children.Add(view, col, col + colSpan, row, row + 1);
NextCell(ref col, ref row, colSpan);
return (T)view;
}
void NextCell(ref int col, ref int row, int colspan)
{
if (col == 0 && colspan == 1)
{
col = 1;
}
else
{
col = 0;
row++;
}
}
}
}

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

@ -250,6 +250,7 @@ namespace Xamarin.Forms.Controls
}
List<GalleryPageFactory> _pages = new List<GalleryPageFactory> {
new GalleryPageFactory(() => new FlowDirectionGalleryLandingPage(), "FlowDirection"),
new GalleryPageFactory(() => new AutomationPropertiesGallery(), "Accessibility"),
new GalleryPageFactory(() => new PlatformSpecificsGallery(), "Platform Specifics"),
new GalleryPageFactory(() => new NativeBindingGalleryPage(), "Native Binding Controls Gallery"),

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

@ -75,6 +75,7 @@
<Compile Include="Bugzilla44596SplashPage.cs" />
<Compile Include="ControlGalleryPages\AutomationPropertiesGallery.cs" />
<Compile Include="ControlGalleryPages\CellForceUpdateSizeGalleryPage.cs" />
<Compile Include="ControlGalleryPages\FlowDirectionGallery.cs" />
<Compile Include="ControlGalleryPages\LayoutAddPerformance.xaml.cs">
<DependentUpon>LayoutAddPerformance.xaml</DependentUpon>
</Compile>

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

@ -0,0 +1,58 @@
using NUnit.Framework;
using System.Linq;
namespace Xamarin.Forms.Core.UnitTests
{
[TestFixture]
public class EffectiveFlowDirectionExtensions : BaseTestFixture
{
[Test]
public void LeftToRightImplicit()
{
var target = FlowDirection.LeftToRight.ToEffectiveFlowDirection();
Assert.IsTrue(target.IsLeftToRight());
Assert.IsTrue(target.IsImplicit());
Assert.IsFalse(target.IsRightToLeft());
Assert.IsFalse(target.IsExplicit());
}
[Test]
public void LeftToRightExplicit()
{
var target = FlowDirection.LeftToRight.ToEffectiveFlowDirection(isExplicit: true);
Assert.IsTrue(target.IsLeftToRight());
Assert.IsTrue(target.IsExplicit());
Assert.IsFalse(target.IsRightToLeft());
Assert.IsFalse(target.IsImplicit());
}
[Test]
public void RightToLeftImplicit()
{
var target = FlowDirection.RightToLeft.ToEffectiveFlowDirection();
Assert.IsTrue(target.IsRightToLeft());
Assert.IsTrue(target.IsImplicit());
Assert.IsFalse(target.IsLeftToRight());
Assert.IsFalse(target.IsExplicit());
}
[Test]
public void RightToLeftExplicit()
{
var target = FlowDirection.RightToLeft.ToEffectiveFlowDirection(isExplicit: true);
Assert.IsTrue(target.IsRightToLeft());
Assert.IsTrue(target.IsExplicit());
Assert.IsFalse(target.IsLeftToRight());
Assert.IsFalse(target.IsImplicit());
}
}
}

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

@ -0,0 +1,630 @@
using NUnit.Framework;
using System.Linq;
using System.Runtime.CompilerServices;
using Xamarin.Forms.Internals;
namespace Xamarin.Forms.Core.UnitTests
{
[TestFixture]
public class FlowDirectionTests : BaseTestFixture
{
[Test]
public void ListViewFlowDirectionIsInheritedByViewCells()
{
var lv = new ListView { FlowDirection = FlowDirection.RightToLeft, ItemTemplate = new DataTemplate(() => new ViewCell { View = new View() }) };
lv.ItemsSource = Enumerable.Range(0, 10);
ViewCell cell = lv.TemplatedItems[0] as ViewCell;
IViewController target = cell.View;
Assert.IsTrue(target.EffectiveFlowDirection.IsRightToLeft(), "ViewCell View is not RightToLeft");
}
[Test]
public void ListViewFlowDirectionIsInheritedByImageInViewCells()
{
var lv = new ListView { FlowDirection = FlowDirection.RightToLeft, ItemTemplate = new DataTemplate(() => new ViewCell { View = new Label() }) };
lv.ItemsSource = Enumerable.Range(0, 10);
ViewCell cell = lv.TemplatedItems[0] as ViewCell;
IViewController target = cell.View;
Assert.IsTrue(target.EffectiveFlowDirection.IsRightToLeft(), "ViewCell View is not RightToLeft");
}
[Test]
public void ScrollViewSetsFlowDirectionAndGrandchildMaintainsParentExplicitValue()
{
var layout = ImplicitLeftToRightScrollView();
var layout2 = ExplicitRightToLeftScrollView();
IViewController view = ImplicitLeftToRightView();
AddExplicitRTLToScrollView(layout, layout2);
AddImplicitToRTLScrollView(layout2, (View)view);
layout.FlowDirection = FlowDirection.LeftToRight;
var target = view.EffectiveFlowDirection;
Assert.IsTrue(target.IsImplicit(), "EffectiveFlowDirection should be Implicit");
Assert.IsTrue(!target.IsExplicit(), "EffectiveFlowDirection should be Implicit");
Assert.IsTrue(target.IsRightToLeft(), "EffectiveFlowDirection should be RightToLeft");
Assert.IsTrue(!target.IsLeftToRight(), "EffectiveFlowDirection should be RightToLeft");
Assert.AreEqual(FlowDirection.MatchParent, ((View)view).FlowDirection);
Assert.AreEqual(FlowDirection.RightToLeft, layout2.FlowDirection);
}
[Test]
public void GrandparentSetsFlowDirectionAndGrandchildMaintainsParentExplicitValue()
{
var layout = ImplicitLeftToRightLayout();
var layout2 = ExplicitLeftToRightLayout();
IViewController view = ImplicitLeftToRightView();
AddExplicitLTRToLayout(layout, layout2);
AddImplicitToLTR(layout2, (View)view);
layout.FlowDirection = FlowDirection.RightToLeft;
var target = view.EffectiveFlowDirection;
Assert.IsTrue(target.IsImplicit(), "EffectiveFlowDirection should be Implicit");
Assert.IsTrue(!target.IsExplicit(), "EffectiveFlowDirection should be Implicit");
Assert.IsTrue(!target.IsRightToLeft(), "EffectiveFlowDirection should be LeftToRight");
Assert.IsTrue(target.IsLeftToRight(), "EffectiveFlowDirection should be LeftToRight");
Assert.AreEqual(FlowDirection.MatchParent, ((View)view).FlowDirection);
Assert.AreEqual(FlowDirection.LeftToRight, layout2.FlowDirection);
}
[Test]
public void GrandparentSetsFlowDirectionAndImplicitDescendentsInheritValue()
{
var layout = ImplicitLeftToRightLayout();
var layout2 = ImplicitLeftToRightLayout();
IViewController view = ImplicitLeftToRightView();
AddImplicitToLTR(layout, layout2);
AddImplicitToLTR(layout2, (View)view);
layout.FlowDirection = FlowDirection.RightToLeft;
Assume.That(((IViewController)layout).EffectiveFlowDirection.IsExplicit());
Assume.That(((IViewController)layout).EffectiveFlowDirection.IsRightToLeft());
Assume.That(((IViewController)layout2).EffectiveFlowDirection.IsImplicit());
Assume.That(((IViewController)layout2).EffectiveFlowDirection.IsRightToLeft());
var target = view.EffectiveFlowDirection;
Assert.IsTrue(target.IsImplicit(), "EffectiveFlowDirection should be Implicit");
Assert.IsTrue(!target.IsExplicit(), "EffectiveFlowDirection should be Implicit");
Assert.IsTrue(target.IsRightToLeft(), "EffectiveFlowDirection should be RightToLeft");
Assert.IsTrue(!target.IsLeftToRight(), "EffectiveFlowDirection should be RightToLeft");
Assert.AreEqual(FlowDirection.MatchParent, ((View)view).FlowDirection);
Assert.AreEqual(FlowDirection.MatchParent, layout2.FlowDirection);
}
[Test]
public void GrandparentSetsOppositeFlowDirectionAndGrandchildInheritsParentExplicitValue()
{
var layout = ExplicitRightToLeftLayout();
var layout2 = ExplicitRightToLeftLayout();
IViewController view = ImplicitLeftToRightView();
AddExplicitRTLToLayout(layout, layout2);
AddImplicitToRTL(layout2, (View)view);
layout.FlowDirection = FlowDirection.LeftToRight;
Assume.That(((IViewController)layout).EffectiveFlowDirection.IsExplicit());
Assume.That(((IViewController)layout).EffectiveFlowDirection.IsLeftToRight());
Assume.That(((IViewController)layout2).EffectiveFlowDirection.IsExplicit());
Assume.That(((IViewController)layout2).EffectiveFlowDirection.IsRightToLeft());
Assume.That(view.EffectiveFlowDirection.IsImplicit());
Assume.That(view.EffectiveFlowDirection.IsRightToLeft());
var target = ((View)view).FlowDirection;
Assert.AreEqual(FlowDirection.MatchParent, target);
}
[Test]
public void NotifyFlowDirectionChangedDoesNotTriggerFlowDirectionPropertyChangedUnnecessarily()
{
var layout = ExplicitRightToLeftLayout();
var layout2 = ImplicitLeftToRightLayout();
IViewController view = new PropertyWatchingView();
AddImplicitToRTL(layout, layout2);
AddImplicitToRTL(layout2, (View)view);
layout2.FlowDirection = FlowDirection.RightToLeft;
Assume.That(((IViewController)layout2).EffectiveFlowDirection.IsExplicit(), "Explicit EffectiveFlowDirection not set on inner layout");
Assume.That(view.EffectiveFlowDirection.IsRightToLeft(), "Implicit FlowDirection not set on view");
layout.FlowDirection = FlowDirection.LeftToRight;
Assume.That(layout2.FlowDirection == FlowDirection.RightToLeft, "Explicit FlowDirection not respected on inner layout");
Assume.That(view.EffectiveFlowDirection.IsRightToLeft(), "Implicit FlowDirection not set on view");
var target = ((PropertyWatchingView)view).FlowDirectionPropertyChangedCount;
Assert.AreEqual(1, target);
}
[Test]
public void ReParentAndInheritNewParentValue()
{
var layout = ExplicitRightToLeftLayout();
IViewController view = ImplicitLeftToRightView();
var layout2 = ExplicitLeftToRightLayout();
AddImplicitToRTL(layout, (View)view);
((View)view).Parent = layout2;
Assume.That(((IViewController)layout2).EffectiveFlowDirection.IsExplicit());
Assume.That(((IViewController)layout2).EffectiveFlowDirection.IsLeftToRight());
var target = view.EffectiveFlowDirection;
Assert.IsTrue(target.IsImplicit(), "EffectiveFlowDirection should be Implicit");
Assert.IsTrue(!target.IsExplicit(), "EffectiveFlowDirection should be Implicit");
Assert.IsTrue(!target.IsRightToLeft(), "EffectiveFlowDirection should be LeftToRight");
Assert.IsTrue(target.IsLeftToRight(), "EffectiveFlowDirection should be LeftToRight");
Assert.AreEqual(FlowDirection.MatchParent, ((View)view).FlowDirection);
}
[Test]
public void ReParentParentAndInheritNewGrandParentValue()
{
var layout = ExplicitRightToLeftLayout();
var layout2 = ImplicitLeftToRightLayout();
IViewController view = ImplicitLeftToRightView();
var layout3 = ExplicitLeftToRightLayout();
AddImplicitToRTL(layout, layout2);
AddImplicitToRTL(layout2, (View)view);
layout2.Parent = layout3;
Assume.That(((IViewController)layout2).EffectiveFlowDirection.IsImplicit());
Assume.That(((IViewController)layout2).EffectiveFlowDirection.IsLeftToRight());
Assume.That(view.EffectiveFlowDirection.IsImplicit());
Assume.That(view.EffectiveFlowDirection.IsLeftToRight());
var target = ((View)view).FlowDirection;
Assert.AreEqual(FlowDirection.MatchParent, target);
}
[Test]
public void SetFlowDirectionToMatchParentAndInheritParentValue()
{
var layout = ImplicitLeftToRightLayout();
var layout2 = ExplicitRightToLeftLayout();
IViewController view = ExplicitLeftToRightView();
AddExplicitRTLToLayout(layout, layout2);
AddExplicitLTRToLayout(layout2, (View)view);
((View)view).FlowDirection = FlowDirection.MatchParent;
var target = view.EffectiveFlowDirection;
Assert.IsTrue(target.IsImplicit(), "EffectiveFlowDirection should be Implicit");
Assert.IsTrue(!target.IsExplicit(), "EffectiveFlowDirection should be Implicit");
Assert.IsTrue(target.IsRightToLeft(), "EffectiveFlowDirection should be RightToLeft");
Assert.IsTrue(!target.IsLeftToRight(), "EffectiveFlowDirection should be RightToLeft");
}
[Test]
public void SetGrandparentAndInheritExplicitParentValue()
{
var layout = ExplicitRightToLeftLayout();
var layout2 = ExplicitLeftToRightLayout();
IViewController view = ImplicitLeftToRightView();
AddExplicitLTRToLayout(layout, layout2);
AddImplicitToLTR(layout2, (View)view);
var target = view.EffectiveFlowDirection;
Assume.That(((IViewController)layout).EffectiveFlowDirection.IsExplicit());
Assume.That(((IViewController)layout).EffectiveFlowDirection.IsRightToLeft());
Assume.That(((IViewController)layout2).EffectiveFlowDirection.IsExplicit());
Assume.That(((IViewController)layout2).EffectiveFlowDirection.IsLeftToRight());
Assert.IsTrue(target.IsImplicit(), "EffectiveFlowDirection should be Implicit");
Assert.IsTrue(!target.IsExplicit(), "EffectiveFlowDirection should be Implicit");
Assert.IsTrue(!target.IsRightToLeft(), "EffectiveFlowDirection should be LeftToRight");
Assert.IsTrue(target.IsLeftToRight(), "EffectiveFlowDirection should be LeftToRight");
}
[Test]
public void SetGrandparentUsingAnonCtorAndMaintainExplicitParentValue()
{
var layout = new StackLayout
{
FlowDirection = FlowDirection.RightToLeft,
Children = {
new StackLayout {
FlowDirection = FlowDirection.LeftToRight,
Children = { ImplicitLeftToRightView() }
}
}
};
var layout2 = layout.Children[0] as StackLayout;
IViewController view = layout2.Children[0] as View;
var target = view.EffectiveFlowDirection;
Assume.That(((IViewController)layout).EffectiveFlowDirection.IsExplicit());
Assume.That(((IViewController)layout).EffectiveFlowDirection.IsRightToLeft());
Assume.That(((IViewController)layout2).EffectiveFlowDirection.IsExplicit());
Assume.That(((IViewController)layout2).EffectiveFlowDirection.IsLeftToRight());
Assert.IsTrue(target.IsImplicit(), "EffectiveFlowDirection should be Implicit");
Assert.IsTrue(!target.IsExplicit(), "EffectiveFlowDirection should be Implicit");
Assert.IsTrue(!target.IsRightToLeft(), "EffectiveFlowDirection should be LeftToRight");
Assert.IsTrue(target.IsLeftToRight(), "EffectiveFlowDirection should be LeftToRight");
}
[Test]
public void SetGrandparentUsingCtorAndMaintainExplicitParentValue()
{
IViewController view = ImplicitLeftToRightView();
var layout2 = new StackLayout { FlowDirection = FlowDirection.LeftToRight, Children = { (View)view } };
var layout = new StackLayout { FlowDirection = FlowDirection.RightToLeft, Children = { layout2 } };
var target = view.EffectiveFlowDirection;
Assume.That(((IViewController)layout).EffectiveFlowDirection.IsExplicit());
Assume.That(((IViewController)layout).EffectiveFlowDirection.IsRightToLeft());
Assume.That(((IViewController)layout2).EffectiveFlowDirection.IsExplicit());
Assume.That(((IViewController)layout2).EffectiveFlowDirection.IsLeftToRight());
Assert.IsTrue(target.IsImplicit(), "EffectiveFlowDirection should be Implicit");
Assert.IsTrue(!target.IsExplicit(), "EffectiveFlowDirection should be Implicit");
Assert.IsTrue(!target.IsRightToLeft(), "EffectiveFlowDirection should be LeftToRight");
Assert.IsTrue(target.IsLeftToRight(), "EffectiveFlowDirection should be LeftToRight");
}
[Test]
public void SetParentAndGrandchildrenInheritValue()
{
var layout = ExplicitRightToLeftLayout();
var layout2 = ImplicitLeftToRightLayout();
IViewController view = ImplicitLeftToRightView();
AddImplicitToRTL(layout, layout2);
AddImplicitToRTL(layout2, (View)view);
var target = view.EffectiveFlowDirection;
Assert.IsTrue(target.IsImplicit(), "EffectiveFlowDirection should be Implicit");
Assert.IsTrue(!target.IsExplicit(), "EffectiveFlowDirection should be Implicit");
Assert.IsTrue(target.IsRightToLeft(), "EffectiveFlowDirection should be RightToLeft");
Assert.IsTrue(!target.IsLeftToRight(), "EffectiveFlowDirection should be RightToLeft");
}
[Test]
public void SetParentAndContentAndGrandchildrenInheritValue()
{
var layout = ExplicitRightToLeftLayout();
var layout2 = ImplicitLeftToRightScrollView();
IViewController view = ImplicitLeftToRightView();
AddImplicitToRTL(layout, layout2);
AddImplicitToRTLScrollView(layout2, (View)view);
var target = view.EffectiveFlowDirection;
Assert.IsTrue(target.IsImplicit(), "EffectiveFlowDirection should be Implicit");
Assert.IsTrue(!target.IsExplicit(), "EffectiveFlowDirection should be Implicit");
Assert.IsTrue(target.IsRightToLeft(), "EffectiveFlowDirection should be RightToLeft");
Assert.IsTrue(!target.IsLeftToRight(), "EffectiveFlowDirection should be RightToLeft");
}
[Test]
public void SetParentAndInheritExplicitParentValue()
{
var layout = ExplicitRightToLeftLayout();
IViewController view = ImplicitLeftToRightView();
AddImplicitToRTL(layout, (View)view);
var target = view.EffectiveFlowDirection;
Assert.IsTrue(target.IsImplicit(), "EffectiveFlowDirection should be Implicit");
Assert.IsTrue(!target.IsExplicit(), "EffectiveFlowDirection should be Implicit");
Assert.IsTrue(target.IsRightToLeft(), "EffectiveFlowDirection should be RightToLeft");
Assert.IsTrue(!target.IsLeftToRight(), "EffectiveFlowDirection should be RightToLeft");
}
[Test]
public void SetParentAndMaintainExplicitValue()
{
var layout = ExplicitRightToLeftLayout();
IViewController view = ExplicitLeftToRightView();
AddExplicitLTRToLayout(layout, (View)view);
var target = view.EffectiveFlowDirection;
Assert.IsTrue(!target.IsImplicit(), "EffectiveFlowDirection should be Explicit");
Assert.IsTrue(target.IsExplicit(), "EffectiveFlowDirection should be Explicit");
Assert.IsTrue(!target.IsRightToLeft(), "EffectiveFlowDirection should be LeftToRight");
Assert.IsTrue(target.IsLeftToRight(), "EffectiveFlowDirection should be LeftToRight");
Assert.AreEqual(FlowDirection.LeftToRight, ((View)view).FlowDirection);
}
[Test]
public void SetParentUsingCtorAndInheritParentValue()
{
IViewController view = ImplicitLeftToRightView();
var layout = new StackLayout { FlowDirection = FlowDirection.RightToLeft, Children = { (View)view } };
Assume.That(((IViewController)layout).EffectiveFlowDirection.IsExplicit());
Assume.That(((IViewController)layout).EffectiveFlowDirection.IsRightToLeft());
Assume.That(((View)view).FlowDirection == FlowDirection.MatchParent);
var target = view.EffectiveFlowDirection;
Assert.IsTrue(target.IsImplicit(), "EffectiveFlowDirection should be Implicit");
Assert.IsTrue(!target.IsExplicit(), "EffectiveFlowDirection should be Implicit");
Assert.IsTrue(target.IsRightToLeft(), "EffectiveFlowDirection should be RightToLeft");
Assert.IsTrue(!target.IsLeftToRight(), "EffectiveFlowDirection should be RightToLeft");
Assert.AreEqual(FlowDirection.MatchParent, ((View)view).FlowDirection);
}
[SetUp]
public override void Setup()
{
base.Setup();
Device.PlatformServices = new MockPlatformServices();
}
[TearDown]
public override void TearDown()
{
base.TearDown();
Device.PlatformServices = null;
}
static void AddExplicitLTRToScrollView(ScrollView parent, View child)
{
parent.Content = child;
IViewController controller = child;
Assume.That(controller.EffectiveFlowDirection.IsExplicit(), "child view FlowDirection should be Explicit");
Assume.That(controller.EffectiveFlowDirection.IsLeftToRight(), "child view FlowDirection should be LeftToRight");
Assume.That(child.FlowDirection == FlowDirection.LeftToRight, "child view FlowDirection should be LeftToRight");
}
static void AddExplicitLTRToLayout(StackLayout parent, View child)
{
parent.Children.Add(child);
IViewController controller = child;
Assume.That(controller.EffectiveFlowDirection.IsExplicit(), "child view FlowDirection should be Explicit");
Assume.That(controller.EffectiveFlowDirection.IsLeftToRight(), "child view FlowDirection should be LeftToRight");
Assume.That(child.FlowDirection == FlowDirection.LeftToRight, "child view FlowDirection should be LeftToRight");
}
static void AddExplicitRTLToScrollView(ScrollView parent, View child)
{
parent.Content = child;
IViewController controller = child;
Assume.That(controller.EffectiveFlowDirection.IsExplicit(), "child view EffectiveFlowDirection should be Implicit");
Assume.That(controller.EffectiveFlowDirection.IsRightToLeft(), "child view EffectiveFlowDirection should be RightToLeft");
Assume.That(child.FlowDirection == FlowDirection.RightToLeft, "child view FlowDirection should be RightToLeft");
}
static void AddExplicitRTLToLayout(StackLayout parent, View child)
{
parent.Children.Add(child);
IViewController controller = child;
Assume.That(controller.EffectiveFlowDirection.IsExplicit(), "child view EffectiveFlowDirection should be Implicit");
Assume.That(controller.EffectiveFlowDirection.IsRightToLeft(), "child view EffectiveFlowDirection should be RightToLeft");
Assume.That(child.FlowDirection == FlowDirection.RightToLeft, "child view FlowDirection should be RightToLeft");
}
static void AddImplicitToLTR(StackLayout parent, View child)
{
parent.Children.Add(child);
IViewController controller = child;
Assume.That(controller.EffectiveFlowDirection.IsImplicit(), "child view EffectiveFlowDirection should be Implicit");
Assume.That(controller.EffectiveFlowDirection.IsLeftToRight(), "child view EffectiveFlowDirection should be LeftToRight");
Assume.That(child.FlowDirection == FlowDirection.MatchParent, "child view FlowDirection should be MatchParent");
}
static void AddImplicitToLTRScrollView(ScrollView parent, View child)
{
parent.Content = child;
IViewController controller = child;
Assume.That(controller.EffectiveFlowDirection.IsImplicit(), "child view EffectiveFlowDirection should be Implicit");
Assume.That(controller.EffectiveFlowDirection.IsLeftToRight(), "child view EffectiveFlowDirection should be LeftToRight");
Assume.That(child.FlowDirection == FlowDirection.MatchParent, "child view FlowDirection should be MatchParent");
}
static void AddImplicitToRTL(StackLayout parent, View child)
{
parent.Children.Add(child);
IViewController controller = child;
Assume.That(controller.EffectiveFlowDirection.IsImplicit(), "child view EffectiveFlowDirection should be Implicit");
Assume.That(controller.EffectiveFlowDirection.IsRightToLeft(), "child view EffectiveFlowDirection should be RightToLeft");
Assume.That(child.FlowDirection == FlowDirection.MatchParent, "child view FlowDirection should be MatchParent");
}
static void AddImplicitToRTLScrollView(ScrollView parent, View child)
{
parent.Content = child;
IViewController controller = child;
Assume.That(controller.EffectiveFlowDirection.IsImplicit(), "child view EffectiveFlowDirection should be Implicit");
Assume.That(controller.EffectiveFlowDirection.IsRightToLeft(), "child view EffectiveFlowDirection should be RightToLeft");
Assume.That(child.FlowDirection == FlowDirection.MatchParent, "child view FlowDirection should be MatchParent");
}
static ScrollView ExplicitLeftToRightScrollView()
{
var layout = new ScrollView { FlowDirection = FlowDirection.LeftToRight };
IViewController controller = layout;
Assume.That(controller.EffectiveFlowDirection.IsExplicit(), "Explicit LTR view EffectiveFlowDirection should be Explicit");
Assume.That(controller.EffectiveFlowDirection.IsLeftToRight(), "Explicit LTR view EffectiveFlowDirection should be LeftToRight");
Assume.That(layout.FlowDirection == FlowDirection.LeftToRight, "Explicit LTR view FlowDirection should be LeftToRight");
return layout;
}
static StackLayout ExplicitLeftToRightLayout()
{
var layout = new StackLayout { FlowDirection = FlowDirection.LeftToRight };
IViewController controller = layout;
Assume.That(controller.EffectiveFlowDirection.IsExplicit(), "Explicit LTR view EffectiveFlowDirection should be Explicit");
Assume.That(controller.EffectiveFlowDirection.IsLeftToRight(), "Explicit LTR view EffectiveFlowDirection should be LeftToRight");
Assume.That(layout.FlowDirection == FlowDirection.LeftToRight, "Explicit LTR view FlowDirection should be LeftToRight");
return layout;
}
static View ExplicitLeftToRightView()
{
var view = new View { FlowDirection = FlowDirection.LeftToRight };
IViewController controller = view;
Assume.That(controller.EffectiveFlowDirection.IsExplicit(), "Explicit LTR view EffectiveFlowDirection should be Explicit");
Assume.That(controller.EffectiveFlowDirection.IsLeftToRight(), "Explicit LTR view EffectiveFlowDirection should be LeftToRight");
Assume.That(((View)view).FlowDirection == FlowDirection.LeftToRight, "Explicit LTR view FlowDirection should be LeftToRight");
return view;
}
static ScrollView ExplicitRightToLeftScrollView()
{
var layout = new ScrollView { FlowDirection = FlowDirection.RightToLeft };
IViewController controller = layout;
Assume.That(controller.EffectiveFlowDirection.IsExplicit(), "Explicit RTL view EffectiveFlowDirection should be Explicit");
Assume.That(controller.EffectiveFlowDirection.IsRightToLeft(), "Explicit RTL view EffectiveFlowDirection should be RightToLeft");
Assume.That(layout.FlowDirection == FlowDirection.RightToLeft, "Explicit RTL view FlowDirection should be RightToLeft");
return layout;
}
static StackLayout ExplicitRightToLeftLayout()
{
var layout = new StackLayout { FlowDirection = FlowDirection.RightToLeft };
IViewController controller = layout;
Assume.That(controller.EffectiveFlowDirection.IsExplicit(), "Explicit RTL view EffectiveFlowDirection should be Explicit");
Assume.That(controller.EffectiveFlowDirection.IsRightToLeft(), "Explicit RTL view EffectiveFlowDirection should be RightToLeft");
Assume.That(layout.FlowDirection == FlowDirection.RightToLeft, "Explicit RTL view FlowDirection should be RightToLeft");
return layout;
}
static View ExplicitRightToLeftView()
{
var view = new View { FlowDirection = FlowDirection.RightToLeft };
IViewController controller = view;
Assume.That(controller.EffectiveFlowDirection.IsExplicit(), "Explicit RTL view EffectiveFlowDirection should be Explicit");
Assume.That(controller.EffectiveFlowDirection.IsRightToLeft(), "Explicit RTL view EffectiveFlowDirection should be RightToLeft");
Assume.That(((View)view).FlowDirection == FlowDirection.RightToLeft, "Explicit RTL view FlowDirection should be RightToLeft");
return view;
}
static ScrollView ImplicitLeftToRightScrollView()
{
var layout = new ScrollView();
IViewController controller = layout;
Assume.That(controller.EffectiveFlowDirection.IsImplicit(), "New view EffectiveFlowDirection should be Implicit");
Assume.That(controller.EffectiveFlowDirection.IsLeftToRight(), "New view EffectiveFlowDirection should be LeftToRight");
Assume.That(layout.FlowDirection == FlowDirection.MatchParent, "New view FlowDirection should be MatchParent");
return layout;
}
static StackLayout ImplicitLeftToRightLayout()
{
var layout = new StackLayout();
IViewController controller = layout;
Assume.That(controller.EffectiveFlowDirection.IsImplicit(), "New view EffectiveFlowDirection should be Implicit");
Assume.That(controller.EffectiveFlowDirection.IsLeftToRight(), "New view EffectiveFlowDirection should be LeftToRight");
Assume.That(layout.FlowDirection == FlowDirection.MatchParent, "New view FlowDirection should be MatchParent");
return layout;
}
static View ImplicitLeftToRightView()
{
var view = new View();
IViewController controller = view;
Assume.That(controller.EffectiveFlowDirection.IsImplicit(), "New view EffectiveFlowDirection should be Implicit");
Assume.That(controller.EffectiveFlowDirection.IsLeftToRight(), "New view EffectiveFlowDirection should be LeftToRight");
Assume.That(((View)view).FlowDirection == FlowDirection.MatchParent, "New view FlowDirection should be MatchParent");
return view;
}
class PropertyWatchingView : View
{
public int FlowDirectionPropertyChangedCount { get; private set; }
protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
base.OnPropertyChanged(propertyName);
if (propertyName == View.FlowDirectionProperty.PropertyName)
FlowDirectionPropertyChangedCount++;
}
}
}
}

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

@ -0,0 +1,844 @@
using NUnit.Framework;
using System;
namespace Xamarin.Forms.Core.UnitTests
{
[TestFixture]
public class LayoutChildIntoBoundingRegionTests : BaseTestFixture
{
const int Layout_Width = 100;
const int Margin_Large = 12;
const int Margin_None = 0;
const int Margin_Small = 2;
const int Region_Height = 50;
const int Region_Width = 150;
const int Region_X = 5;
const int Region_Y = 5;
const int View_Size = 20;
int Expected_Empty_Region_Height => Region_Height - View_Size;
int Expected_Empty_Region_Width => Region_Width - View_Size;
int Expected_Height_Start_End => View_Size;
int Expected_Region_Right => Region_X + Region_Width;
int Expected_Width_Start_End => View_Size;
int Expected_X_Center => Expected_Empty_Region_Width / 2 + Region_X;
int Expected_Y_Center => Expected_Empty_Region_Height / 2 + Region_Y;
[TestCase(Margin_None)]
[TestCase(Margin_Small)]
[TestCase(Margin_Large)]
public void Default(int margin)
{
var layout = new StackLayout();
layout.MockBounds(new Rectangle(0, 0, Layout_Width, double.PositiveInfinity));
var view = new MockView(margin);
layout.Children.Add(view);
var region = new Rectangle(Region_X, Region_Y, Region_Width, Region_Height);
Layout.LayoutChildIntoBoundingRegion(view, region);
var target = view.Bounds;
var thickness = margin * 2;
Assume.That(view.HorizontalOptions.Alignment == LayoutAlignment.Fill,
"view.HorizontalOptions.Alignment should be Fill");
Assume.That(view.VerticalOptions.Alignment == LayoutAlignment.Fill,
"view.VerticalOptions.Alignment should be Fill");
Assert.AreEqual(Expected_X_Fill_Plus_Margin(margin), target.X, "X");
Assert.AreEqual(Expected_Y_Fill_Plus_Margin(margin), target.Y, "Y");
Assert.AreEqual(Expected_Width_Fill_Less_Thickness(thickness), target.Width, "Width");
Assert.AreEqual(Expected_Height_Fill_Less_Thickness(thickness), target.Height, "Height");
}
[TestCase(Margin_None)]
[TestCase(Margin_Small)]
[TestCase(Margin_Large)]
public void Default_RTL(int margin)
{
var layout = new StackLayout { FlowDirection = FlowDirection.RightToLeft };
layout.MockBounds(new Rectangle(0, 0, Layout_Width, double.PositiveInfinity));
var view = new MockView(margin);
layout.Children.Add(view);
var region = new Rectangle(Region_X, Region_Y, Region_Width, Region_Height);
Layout.LayoutChildIntoBoundingRegion(view, region);
var target = view.Bounds;
var thickness = margin * 2;
Assume.That(view.HorizontalOptions.Alignment == LayoutAlignment.Fill,
"view.HorizontalOptions.Alignment should be Fill");
Assume.That(view.VerticalOptions.Alignment == LayoutAlignment.Fill,
"view.VerticalOptions.Alignment should be Fill");
Assert.AreEqual(Expected_Region_Right, region.Right, "region.Right");
Assert.AreEqual(Layout_Width, layout.Width, "layout.Width");
Assert.AreEqual(Expected_X_Fill_RTL_Plus_Margin(margin), target.X, "X");
Assert.AreEqual(Expected_Y_Fill_Plus_Margin(margin), target.Y, "Y");
Assert.AreEqual(Expected_Width_Fill_Less_Thickness(thickness), target.Width, "Width");
Assert.AreEqual(Expected_Height_Fill_Less_Thickness(thickness), target.Height, "Height");
}
[TestCase(Margin_None)]
[TestCase(Margin_Small)]
[TestCase(Margin_Large)]
public void HorizontalCenter(int margin)
{
var layout = new StackLayout();
layout.MockBounds(new Rectangle(0, 0, Layout_Width, double.PositiveInfinity));
var view = new MockView(margin) { HorizontalOptions = LayoutOptions.Center };
layout.Children.Add(view);
var region = new Rectangle(Region_X, Region_Y, Region_Width, Region_Height);
Layout.LayoutChildIntoBoundingRegion(view, region);
var target = view.Bounds;
var thickness = margin * 2;
Assume.That(view.HorizontalOptions.Alignment == LayoutAlignment.Center,
"view.HorizontalOptions.Alignment should be Center");
Assume.That(view.HorizontalOptions.Alignment.ToDouble() == 0.5,
"view.HorizontalOptions.Alignment.ToDouble() should be 0.5");
Assume.That(view.VerticalOptions.Alignment == LayoutAlignment.Fill,
"view.VerticalOptions.Alignment should be Fill");
Assert.AreEqual(Expected_X_Center, target.X, "X");
Assert.AreEqual(Expected_Y_Fill_Plus_Margin(margin), target.Y, "Y");
Assert.AreEqual(Expected_Width_Start_End, target.Width, "Width");
Assert.AreEqual(Expected_Height_Fill_Less_Thickness(thickness), target.Height, "Height");
}
[TestCase(Margin_None)]
[TestCase(Margin_Small)]
[TestCase(Margin_Large)]
public void HorizontalCenter_RTL(int margin)
{
var layout = new StackLayout { FlowDirection = FlowDirection.RightToLeft };
layout.MockBounds(new Rectangle(0, 0, Layout_Width, double.PositiveInfinity));
var view = new MockView(margin) { HorizontalOptions = LayoutOptions.Center };
layout.Children.Add(view);
var region = new Rectangle(Region_X, Region_Y, Region_Width, Region_Height);
Layout.LayoutChildIntoBoundingRegion(view, region);
var target = view.Bounds;
var thickness = margin * 2;
var center_margin = Math.Ceiling(margin / 2d);
Assume.That(view.HorizontalOptions.Alignment == LayoutAlignment.Center,
"view.HorizontalOptions.Alignment should be Center");
Assume.That(view.HorizontalOptions.Alignment.ToDouble() == 0.5,
"view.HorizontalOptions.Alignment.ToDouble() should be 0.5");
Assume.That(view.VerticalOptions.Alignment == LayoutAlignment.Fill,
"view.VerticalOptions.Alignment should be Fill");
Assert.AreEqual(Expected_X_Center_RTL_Plus_Margin(0), target.X, "X");
Assert.AreEqual(Expected_Y_Fill_Plus_Margin(margin), target.Y, "Y");
Assert.AreEqual(Expected_Width_Start_End, target.Width, "Width");
Assert.AreEqual(Expected_Height_Fill_Less_Thickness(thickness), target.Height, "Height");
}
[TestCase(Margin_None)]
[TestCase(Margin_Small)]
[TestCase(Margin_Large)]
public void HorizontalCenterAndExpand(int margin)
{
var layout = new StackLayout();
layout.MockBounds(new Rectangle(0, 0, Layout_Width, double.PositiveInfinity));
var view = new MockView(margin) { HorizontalOptions = LayoutOptions.CenterAndExpand };
layout.Children.Add(view);
var region = new Rectangle(Region_X, Region_Y, Region_Width, Region_Height);
Layout.LayoutChildIntoBoundingRegion(view, region);
var target = view.Bounds;
var thickness = margin * 2;
Assume.That(view.HorizontalOptions.Alignment == LayoutAlignment.Center,
"view.HorizontalOptions.Alignment should be Center");
Assume.That(view.HorizontalOptions.Alignment.ToDouble() == 0.5,
"view.HorizontalOptions.Alignment.ToDouble() should be 0.5");
Assume.That(view.VerticalOptions.Alignment == LayoutAlignment.Fill,
"view.VerticalOptions.Alignment should be Fill");
Assert.AreEqual(Expected_X_Center, target.X, "X");
Assert.AreEqual(Expected_Y_Fill_Plus_Margin(margin), target.Y, "Y");
Assert.AreEqual(Expected_Width_Start_End, target.Width, "Width");
Assert.AreEqual(Expected_Height_Fill_Less_Thickness(thickness), target.Height, "Height");
}
[TestCase(Margin_None)]
[TestCase(Margin_Small)]
[TestCase(Margin_Large)]
public void HorizontalCenterAndExpand_RTL(int margin)
{
var layout = new StackLayout { FlowDirection = FlowDirection.RightToLeft };
layout.MockBounds(new Rectangle(0, 0, Layout_Width, double.PositiveInfinity));
var view = new MockView(margin) { HorizontalOptions = LayoutOptions.CenterAndExpand };
layout.Children.Add(view);
var region = new Rectangle(Region_X, Region_Y, Region_Width, Region_Height);
Layout.LayoutChildIntoBoundingRegion(view, region);
var target = view.Bounds;
var thickness = margin * 2;
Assume.That(view.HorizontalOptions.Alignment == LayoutAlignment.Center,
"view.HorizontalOptions.Alignment should be Center");
Assume.That(view.HorizontalOptions.Alignment.ToDouble() == 0.5,
"view.HorizontalOptions.Alignment.ToDouble() should be 0.5");
Assume.That(view.VerticalOptions.Alignment == LayoutAlignment.Fill,
"view.VerticalOptions.Alignment should be Fill");
Assert.AreEqual(Expected_X_Center_RTL_Plus_Margin(0), target.X, "X");
Assert.AreEqual(Expected_Y_Fill_Plus_Margin(margin), target.Y, "Y");
Assert.AreEqual(Expected_Width_Start_End, target.Width, "Width");
Assert.AreEqual(Expected_Height_Fill_Less_Thickness(thickness), target.Height, "Height");
}
[TestCase(Margin_None)]
[TestCase(Margin_Small)]
[TestCase(Margin_Large)]
public void HorizontalEnd(int margin)
{
var layout = new StackLayout();
layout.MockBounds(new Rectangle(0, 0, Layout_Width, double.PositiveInfinity));
var view = new MockView(margin) { HorizontalOptions = LayoutOptions.End };
layout.Children.Add(view);
var region = new Rectangle(Region_X, Region_Y, Region_Width, Region_Height);
Layout.LayoutChildIntoBoundingRegion(view, region);
var target = view.Bounds;
var thickness = margin * 2;
Assume.That(view.HorizontalOptions.Alignment == LayoutAlignment.End,
"view.HorizontalOptions.Alignment should be End");
Assume.That(view.HorizontalOptions.Alignment.ToDouble() == 1,
"view.HorizontalOptions.Alignment.ToDouble() should be 1");
Assume.That(view.VerticalOptions.Alignment == LayoutAlignment.Fill,
"view.VerticalOptions.Alignment should be Fill");
Assert.AreEqual(Expected_X_End_Less_Margin(margin), target.X, "X");
Assert.AreEqual(Expected_Y_Fill_Plus_Margin(margin), target.Y, "Y");
Assert.AreEqual(Expected_Width_Start_End, target.Width, "Width");
Assert.AreEqual(Expected_Height_Fill_Less_Thickness(thickness), target.Height, "Height");
}
[TestCase(Margin_None)]
[TestCase(Margin_Small)]
[TestCase(Margin_Large)]
public void HorizontalEnd_RTL(int margin)
{
var layout = new StackLayout { FlowDirection = FlowDirection.RightToLeft };
layout.MockBounds(new Rectangle(0, 0, Layout_Width, double.PositiveInfinity));
var view = new MockView(margin) { HorizontalOptions = LayoutOptions.End };
layout.Children.Add(view);
var region = new Rectangle(Region_X, Region_Y, Region_Width, Region_Height);
Layout.LayoutChildIntoBoundingRegion(view, region);
var target = view.Bounds;
var thickness = margin * 2;
Assume.That(view.HorizontalOptions.Alignment == LayoutAlignment.End,
"view.HorizontalOptions.Alignment should be End");
Assume.That(view.HorizontalOptions.Alignment.ToDouble() == 1,
"view.HorizontalOptions.Alignment.ToDouble() should be 1");
Assume.That(view.VerticalOptions.Alignment == LayoutAlignment.Fill,
"view.VerticalOptions.Alignment should be Fill");
Assert.AreEqual(Expected_X_End_RTL_Plus_Margin(margin), target.X, "X");
Assert.AreEqual(Expected_Y_Fill_Plus_Margin(margin), target.Y, "Y");
Assert.AreEqual(Expected_Width_Start_End, target.Width, "Width");
Assert.AreEqual(Expected_Height_Fill_Less_Thickness(thickness), target.Height, "Height");
}
[TestCase(Margin_None)]
[TestCase(Margin_Small)]
[TestCase(Margin_Large)]
public void HorizontalEndAndExpand(int margin)
{
var layout = new StackLayout();
layout.MockBounds(new Rectangle(0, 0, Layout_Width, double.PositiveInfinity));
var view = new MockView(margin) { HorizontalOptions = LayoutOptions.EndAndExpand };
layout.Children.Add(view);
var region = new Rectangle(Region_X, Region_Y, Region_Width, Region_Height);
Layout.LayoutChildIntoBoundingRegion(view, region);
var target = view.Bounds;
var thickness = margin * 2;
Assume.That(view.HorizontalOptions.Alignment == LayoutAlignment.End,
"view.HorizontalOptions.Alignment should be End");
Assume.That(view.HorizontalOptions.Alignment.ToDouble() == 1,
"view.HorizontalOptions.Alignment.ToDouble() should be 1");
Assume.That(view.VerticalOptions.Alignment == LayoutAlignment.Fill,
"view.VerticalOptions.Alignment should be Fill");
Assert.AreEqual(Expected_X_End_Less_Margin(margin), target.X, "X");
Assert.AreEqual(Expected_Y_Fill_Plus_Margin(margin), target.Y, "Y");
Assert.AreEqual(Expected_Width_Start_End, target.Width, "Width");
Assert.AreEqual(Expected_Height_Fill_Less_Thickness(thickness), target.Height, "Height");
}
[TestCase(Margin_None)]
[TestCase(Margin_Small)]
[TestCase(Margin_Large)]
public void HorizontalEndAndExpand_RTL(int margin)
{
var layout = new StackLayout { FlowDirection = FlowDirection.RightToLeft };
layout.MockBounds(new Rectangle(0, 0, Layout_Width, double.PositiveInfinity));
var view = new MockView(margin) { HorizontalOptions = LayoutOptions.EndAndExpand };
layout.Children.Add(view);
var region = new Rectangle(Region_X, Region_Y, Region_Width, Region_Height);
Layout.LayoutChildIntoBoundingRegion(view, region);
var target = view.Bounds;
var thickness = margin * 2;
Assume.That(view.HorizontalOptions.Alignment == LayoutAlignment.End,
"view.HorizontalOptions.Alignment should be End");
Assume.That(view.HorizontalOptions.Alignment.ToDouble() == 1,
"view.HorizontalOptions.Alignment.ToDouble() should be 1");
Assume.That(view.VerticalOptions.Alignment == LayoutAlignment.Fill,
"view.VerticalOptions.Alignment should be Fill");
Assert.AreEqual(Expected_X_End_RTL_Plus_Margin(margin), target.X, "X");
Assert.AreEqual(Expected_Y_Fill_Plus_Margin(margin), target.Y, "Y");
Assert.AreEqual(Expected_Width_Start_End, target.Width, "Width");
Assert.AreEqual(Expected_Height_Fill_Less_Thickness(thickness), target.Height, "Height");
}
[TestCase(Margin_None)]
[TestCase(Margin_Small)]
[TestCase(Margin_Large)]
public void HorizontalStart(int margin)
{
var layout = new StackLayout();
layout.MockBounds(new Rectangle(0, 0, Layout_Width, double.PositiveInfinity));
var view = new MockView(margin) { HorizontalOptions = LayoutOptions.Start };
layout.Children.Add(view);
var region = new Rectangle(Region_X, Region_Y, Region_Width, Region_Height);
Layout.LayoutChildIntoBoundingRegion(view, region);
var target = view.Bounds;
var thickness = margin * 2;
Assume.That(view.HorizontalOptions.Alignment == LayoutAlignment.Start,
"view.HorizontalOptions.Alignment should be Start");
Assume.That(view.HorizontalOptions.Alignment.ToDouble() == 0,
"view.HorizontalOptions.Alignment.ToDouble() should be 0");
Assume.That(view.VerticalOptions.Alignment == LayoutAlignment.Fill,
"view.VerticalOptions.Alignment should be Fill");
Assert.AreEqual(Expected_X_Fill_Plus_Margin(margin), target.X, "X");
Assert.AreEqual(Expected_Y_Fill_Plus_Margin(margin), target.Y, "Y");
Assert.AreEqual(Expected_Width_Start_End, target.Width, "Width");
Assert.AreEqual(Expected_Height_Fill_Less_Thickness(thickness), target.Height, "Height");
}
[TestCase(Margin_None)]
[TestCase(Margin_Small)]
[TestCase(Margin_Large)]
public void HorizontalStart_RTL(int margin)
{
var layout = new StackLayout { FlowDirection = FlowDirection.RightToLeft };
layout.MockBounds(new Rectangle(0, 0, Layout_Width, double.PositiveInfinity));
var view = new MockView(margin) { HorizontalOptions = LayoutOptions.Start };
layout.Children.Add(view);
var region = new Rectangle(Region_X, Region_Y, Region_Width, Region_Height);
Layout.LayoutChildIntoBoundingRegion(view, region);
var target = view.Bounds;
var thickness = margin * 2;
Assume.That(view.HorizontalOptions.Alignment == LayoutAlignment.Start,
"view.HorizontalOptions.Alignment should be Start");
Assume.That(view.HorizontalOptions.Alignment.ToDouble() == 0,
"view.HorizontalOptions.Alignment.ToDouble() should be 0");
Assume.That(view.VerticalOptions.Alignment == LayoutAlignment.Fill,
"view.VerticalOptions.Alignment should be Fill");
Assert.AreEqual(Expected_X_Start_Less_Margin(margin), target.X, "X");
Assert.AreEqual(Expected_Y_Fill_Plus_Margin(margin), target.Y, "Y");
Assert.AreEqual(Expected_Width_Start_End, target.Width, "Width");
Assert.AreEqual(Expected_Height_Fill_Less_Thickness(thickness), target.Height, "Height");
}
[TestCase(Margin_None)]
[TestCase(Margin_Small)]
[TestCase(Margin_Large)]
public void HorizontalStartAndExpand(int margin)
{
var layout = new StackLayout();
layout.MockBounds(new Rectangle(0, 0, Layout_Width, double.PositiveInfinity));
var view = new MockView(margin) { HorizontalOptions = LayoutOptions.StartAndExpand };
layout.Children.Add(view);
var region = new Rectangle(Region_X, Region_Y, Region_Width, Region_Height);
Layout.LayoutChildIntoBoundingRegion(view, region);
var target = view.Bounds;
var thickness = margin * 2;
Assume.That(view.HorizontalOptions.Alignment == LayoutAlignment.Start,
"view.HorizontalOptions.Alignment should be Start");
Assume.That(view.HorizontalOptions.Alignment.ToDouble() == 0,
"view.HorizontalOptions.Alignment.ToDouble() should be 0");
Assume.That(view.VerticalOptions.Alignment == LayoutAlignment.Fill,
"view.VerticalOptions.Alignment should be Fill");
Assert.AreEqual(Expected_X_Fill_Plus_Margin(margin), target.X, "X");
Assert.AreEqual(Expected_Y_Fill_Plus_Margin(margin), target.Y, "Y");
Assert.AreEqual(Expected_Width_Start_End, target.Width, "Width");
Assert.AreEqual(Expected_Height_Fill_Less_Thickness(thickness), target.Height, "Height");
}
[TestCase(Margin_None)]
[TestCase(Margin_Small)]
[TestCase(Margin_Large)]
public void HorizontalStartAndExpand_RTL(int margin)
{
var layout = new StackLayout { FlowDirection = FlowDirection.RightToLeft };
layout.MockBounds(new Rectangle(0, 0, Layout_Width, double.PositiveInfinity));
var view = new MockView(margin) { HorizontalOptions = LayoutOptions.StartAndExpand };
layout.Children.Add(view);
var region = new Rectangle(Region_X, Region_Y, Region_Width, Region_Height);
Layout.LayoutChildIntoBoundingRegion(view, region);
var target = view.Bounds;
var thickness = margin * 2;
Assume.That(view.HorizontalOptions.Alignment == LayoutAlignment.Start,
"view.HorizontalOptions.Alignment should be Start");
Assume.That(view.HorizontalOptions.Alignment.ToDouble() == 0,
"view.HorizontalOptions.Alignment.ToDouble() should be 0");
Assume.That(view.VerticalOptions.Alignment == LayoutAlignment.Fill,
"view.VerticalOptions.Alignment should be Fill");
Assert.AreEqual(Expected_X_Start_Less_Margin(margin), target.X, "X");
Assert.AreEqual(Expected_Y_Fill_Plus_Margin(margin), target.Y, "Y");
Assert.AreEqual(Expected_Width_Start_End, target.Width, "Width");
Assert.AreEqual(Expected_Height_Fill_Less_Thickness(thickness), target.Height, "Height");
}
[TestCase(Margin_None)]
[TestCase(Margin_Small)]
[TestCase(Margin_Large)]
public void VerticalCenter(int margin)
{
var layout = new StackLayout();
layout.MockBounds(new Rectangle(0, 0, Layout_Width, double.PositiveInfinity));
var view = new MockView(margin) { VerticalOptions = LayoutOptions.Center };
layout.Children.Add(view);
var region = new Rectangle(Region_X, Region_Y, Region_Width, Region_Height);
Layout.LayoutChildIntoBoundingRegion(view, region);
var target = view.Bounds;
var thickness = margin * 2;
Assume.That(view.VerticalOptions.Alignment == LayoutAlignment.Center,
"view.VerticalOptions.Alignment should be Center");
Assume.That(view.VerticalOptions.Alignment.ToDouble() == 0.5,
"view.VerticalOptions.Alignment.ToDouble() should be 0.5");
Assume.That(view.HorizontalOptions.Alignment == LayoutAlignment.Fill,
"view.HorizontalOptions.Alignment should be Fill");
Assert.AreEqual(Expected_X_Fill_Plus_Margin(margin), target.X, "X");
Assert.AreEqual(Expected_Y_Center, target.Y, "Y");
Assert.AreEqual(Expected_Width_Fill_Less_Thickness(thickness), target.Width, "Width");
Assert.AreEqual(Expected_Height_Start_End, target.Height, "Height");
}
[TestCase(Margin_None)]
[TestCase(Margin_Small)]
[TestCase(Margin_Large)]
public void VerticalCenter_RTL(int margin)
{
var layout = new StackLayout { FlowDirection = FlowDirection.RightToLeft };
layout.MockBounds(new Rectangle(0, 0, Layout_Width, double.PositiveInfinity));
var view = new MockView(margin) { VerticalOptions = LayoutOptions.Center };
layout.Children.Add(view);
var region = new Rectangle(Region_X, Region_Y, Region_Width, Region_Height);
Layout.LayoutChildIntoBoundingRegion(view, region);
var target = view.Bounds;
var thickness = margin * 2;
Assume.That(view.VerticalOptions.Alignment == LayoutAlignment.Center,
"view.VerticalOptions.Alignment should be Center");
Assume.That(view.VerticalOptions.Alignment.ToDouble() == 0.5,
"view.VerticalOptions.Alignment.ToDouble() should be 0.5");
Assume.That(view.HorizontalOptions.Alignment == LayoutAlignment.Fill,
"view.HorizontalOptions.Alignment should be Fill");
Assert.AreEqual(Expected_X_Fill_RTL_Plus_Margin(margin), target.X, "X");
Assert.AreEqual(Expected_Y_Center, target.Y, "Y");
Assert.AreEqual(Expected_Width_Fill_Less_Thickness(thickness), target.Width, "Width");
Assert.AreEqual(Expected_Height_Start_End, target.Height, "Height");
}
[TestCase(Margin_None)]
[TestCase(Margin_Small)]
[TestCase(Margin_Large)]
public void VerticalCenterAndExpand(int margin)
{
var layout = new StackLayout();
layout.MockBounds(new Rectangle(0, 0, Layout_Width, double.PositiveInfinity));
var view = new MockView(margin) { VerticalOptions = LayoutOptions.CenterAndExpand };
layout.Children.Add(view);
var region = new Rectangle(Region_X, Region_Y, Region_Width, Region_Height);
Layout.LayoutChildIntoBoundingRegion(view, region);
var target = view.Bounds;
var thickness = margin * 2;
Assume.That(view.VerticalOptions.Alignment == LayoutAlignment.Center,
"view.VerticalOptions.Alignment should be Center");
Assume.That(view.VerticalOptions.Alignment.ToDouble() == 0.5,
"view.VerticalOptions.Alignment.ToDouble() should be 0.5");
Assume.That(view.HorizontalOptions.Alignment == LayoutAlignment.Fill,
"view.HorizontalOptions.Alignment should be Fill");
Assert.AreEqual(Expected_X_Fill_Plus_Margin(margin), target.X, "X");
Assert.AreEqual(Expected_Y_Center, target.Y, "Y");
Assert.AreEqual(Expected_Width_Fill_Less_Thickness(thickness), target.Width, "Width");
Assert.AreEqual(Expected_Height_Start_End, target.Height, "Height");
}
[TestCase(Margin_None)]
[TestCase(Margin_Small)]
[TestCase(Margin_Large)]
public void VerticalCenterAndExpand_RTL(int margin)
{
var layout = new StackLayout { FlowDirection = FlowDirection.RightToLeft };
layout.MockBounds(new Rectangle(0, 0, Layout_Width, double.PositiveInfinity));
var view = new MockView(margin) { VerticalOptions = LayoutOptions.CenterAndExpand };
layout.Children.Add(view);
var region = new Rectangle(Region_X, Region_Y, Region_Width, Region_Height);
Layout.LayoutChildIntoBoundingRegion(view, region);
var target = view.Bounds;
var thickness = margin * 2;
Assume.That(view.VerticalOptions.Alignment == LayoutAlignment.Center,
"view.VerticalOptions.Alignment should be Center");
Assume.That(view.VerticalOptions.Alignment.ToDouble() == 0.5,
"view.VerticalOptions.Alignment.ToDouble() should be 0.5");
Assume.That(view.HorizontalOptions.Alignment == LayoutAlignment.Fill,
"view.HorizontalOptions.Alignment should be Fill");
Assert.AreEqual(Expected_X_Fill_RTL_Plus_Margin(margin), target.X, "X");
Assert.AreEqual(Expected_Y_Center, target.Y, "Y");
Assert.AreEqual(Expected_Width_Fill_Less_Thickness(thickness), target.Width, "Width");
Assert.AreEqual(Expected_Height_Start_End, target.Height, "Height");
}
[TestCase(Margin_None)]
[TestCase(Margin_Small)]
[TestCase(Margin_Large)]
public void VerticalEnd(int margin)
{
var layout = new StackLayout();
layout.MockBounds(new Rectangle(0, 0, Layout_Width, double.PositiveInfinity));
var view = new MockView(margin) { VerticalOptions = LayoutOptions.End };
layout.Children.Add(view);
var region = new Rectangle(Region_X, Region_Y, Region_Width, Region_Height);
Layout.LayoutChildIntoBoundingRegion(view, region);
var target = view.Bounds;
var thickness = margin * 2;
Assume.That(view.VerticalOptions.Alignment == LayoutAlignment.End,
"view.VerticalOptions.Alignment should be End");
Assume.That(view.VerticalOptions.Alignment.ToDouble() == 1,
"view.VerticalOptions.Alignment.ToDouble() should be 1");
Assume.That(view.HorizontalOptions.Alignment == LayoutAlignment.Fill,
"view.HorizontalOptions.Alignment should be Fill");
Assert.AreEqual(Expected_X_Fill_Plus_Margin(margin), target.X, "X");
Assert.AreEqual(Expected_Y_End_Less_Margin(margin), target.Y, "Y");
Assert.AreEqual(Expected_Width_Fill_Less_Thickness(thickness), target.Width, "Width");
Assert.AreEqual(Expected_Height_Start_End, target.Height, "Height");
}
[TestCase(Margin_None)]
[TestCase(Margin_Small)]
[TestCase(Margin_Large)]
public void VerticalEnd_RTL(int margin)
{
var layout = new StackLayout { FlowDirection = FlowDirection.RightToLeft };
layout.MockBounds(new Rectangle(0, 0, Layout_Width, double.PositiveInfinity));
var view = new MockView(margin) { VerticalOptions = LayoutOptions.End };
layout.Children.Add(view);
var region = new Rectangle(Region_X, Region_Y, Region_Width, Region_Height);
Layout.LayoutChildIntoBoundingRegion(view, region);
var target = view.Bounds;
var thickness = margin * 2;
Assume.That(view.VerticalOptions.Alignment == LayoutAlignment.End,
"view.VerticalOptions.Alignment should be End");
Assume.That(view.VerticalOptions.Alignment.ToDouble() == 1,
"view.VerticalOptions.Alignment.ToDouble() should be 1");
Assume.That(view.HorizontalOptions.Alignment == LayoutAlignment.Fill,
"view.HorizontalOptions.Alignment should be Fill");
Assert.AreEqual(Expected_X_Fill_RTL_Plus_Margin(margin), target.X, "X");
Assert.AreEqual(Expected_Y_End_Less_Margin(margin), target.Y, "Y");
Assert.AreEqual(Expected_Width_Fill_Less_Thickness(thickness), target.Width, "Width");
Assert.AreEqual(Expected_Height_Start_End, target.Height, "Height");
}
[TestCase(Margin_None)]
[TestCase(Margin_Small)]
[TestCase(Margin_Large)]
public void VerticalEndAndExpand(int margin)
{
var layout = new StackLayout();
layout.MockBounds(new Rectangle(0, 0, Layout_Width, double.PositiveInfinity));
var view = new MockView(margin) { VerticalOptions = LayoutOptions.EndAndExpand };
layout.Children.Add(view);
var region = new Rectangle(Region_X, Region_Y, Region_Width, Region_Height);
Layout.LayoutChildIntoBoundingRegion(view, region);
var target = view.Bounds;
var thickness = margin * 2;
Assume.That(view.VerticalOptions.Alignment == LayoutAlignment.End,
"view.VerticalOptions.Alignment should be End");
Assume.That(view.VerticalOptions.Alignment.ToDouble() == 1,
"view.VerticalOptions.Alignment.ToDouble() should be 1");
Assume.That(view.HorizontalOptions.Alignment == LayoutAlignment.Fill,
"view.HorizontalOptions.Alignment should be Fill");
Assert.AreEqual(Expected_X_Fill_Plus_Margin(margin), target.X, "X");
Assert.AreEqual(Expected_Y_End_Less_Margin(margin), target.Y, "Y");
Assert.AreEqual(Expected_Width_Fill_Less_Thickness(thickness), target.Width, "Width");
Assert.AreEqual(Expected_Height_Start_End, target.Height, "Height");
}
[TestCase(Margin_None)]
[TestCase(Margin_Small)]
[TestCase(Margin_Large)]
public void VerticalEndAndExpand_RTL(int margin)
{
var layout = new StackLayout { FlowDirection = FlowDirection.RightToLeft };
layout.MockBounds(new Rectangle(0, 0, Layout_Width, double.PositiveInfinity));
var view = new MockView(margin) { VerticalOptions = LayoutOptions.EndAndExpand };
layout.Children.Add(view);
var region = new Rectangle(Region_X, Region_Y, Region_Width, Region_Height);
Layout.LayoutChildIntoBoundingRegion(view, region);
var target = view.Bounds;
var thickness = margin * 2;
Assume.That(view.VerticalOptions.Alignment == LayoutAlignment.End,
"view.VerticalOptions.Alignment should be End");
Assume.That(view.VerticalOptions.Alignment.ToDouble() == 1,
"view.VerticalOptions.Alignment.ToDouble() should be 1");
Assume.That(view.HorizontalOptions.Alignment == LayoutAlignment.Fill,
"view.HorizontalOptions.Alignment should be Fill");
Assert.AreEqual(Expected_X_Fill_RTL_Plus_Margin(margin), target.X, "X");
Assert.AreEqual(Expected_Y_End_Less_Margin(margin), target.Y, "Y");
Assert.AreEqual(Expected_Width_Fill_Less_Thickness(thickness), target.Width, "Width");
Assert.AreEqual(Expected_Height_Start_End, target.Height, "Height");
}
[TestCase(Margin_None)]
[TestCase(Margin_Small)]
[TestCase(Margin_Large)]
public void VerticalStart(int margin)
{
var layout = new StackLayout();
layout.MockBounds(new Rectangle(0, 0, Layout_Width, double.PositiveInfinity));
var view = new MockView(margin) { VerticalOptions = LayoutOptions.Start };
layout.Children.Add(view);
var region = new Rectangle(Region_X, Region_Y, Region_Width, Region_Height);
Layout.LayoutChildIntoBoundingRegion(view, region);
var target = view.Bounds;
var thickness = margin * 2;
Assume.That(view.VerticalOptions.Alignment == LayoutAlignment.Start,
"view.VerticalOptions.Alignment should be Start");
Assume.That(view.VerticalOptions.Alignment.ToDouble() == 0,
"view.VerticalOptions.Alignment.ToDouble() should be 0");
Assume.That(view.HorizontalOptions.Alignment == LayoutAlignment.Fill,
"view.HorizontalOptions.Alignment should be Fill");
Assert.AreEqual(Expected_X_Fill_Plus_Margin(margin), target.X, "X");
Assert.AreEqual(Expected_Y_Fill_Plus_Margin(margin), target.Y, "Y");
Assert.AreEqual(Expected_Width_Fill_Less_Thickness(thickness), target.Width, "Width");
Assert.AreEqual(Expected_Height_Start_End, target.Height, "Height");
}
[TestCase(Margin_None)]
[TestCase(Margin_Small)]
[TestCase(Margin_Large)]
public void VerticalStart_RTL(int margin)
{
var layout = new StackLayout { FlowDirection = FlowDirection.RightToLeft };
layout.MockBounds(new Rectangle(0, 0, Layout_Width, double.PositiveInfinity));
var view = new MockView(margin) { VerticalOptions = LayoutOptions.Start };
layout.Children.Add(view);
var region = new Rectangle(Region_X, Region_Y, Region_Width, Region_Height);
Layout.LayoutChildIntoBoundingRegion(view, region);
var target = view.Bounds;
var thickness = margin * 2;
Assume.That(view.VerticalOptions.Alignment == LayoutAlignment.Start,
"view.VerticalOptions.Alignment should be Start");
Assume.That(view.VerticalOptions.Alignment.ToDouble() == 0,
"view.VerticalOptions.Alignment.ToDouble() should be 0");
Assume.That(view.HorizontalOptions.Alignment == LayoutAlignment.Fill,
"view.HorizontalOptions.Alignment should be Fill");
Assert.AreEqual(Expected_X_Fill_RTL_Plus_Margin(margin), target.X, "X");
Assert.AreEqual(Expected_Y_Fill_Plus_Margin(margin), target.Y, "Y");
Assert.AreEqual(Expected_Width_Fill_Less_Thickness(thickness), target.Width, "Width");
Assert.AreEqual(Expected_Height_Start_End, target.Height, "Height");
}
[TestCase(Margin_None)]
[TestCase(Margin_Small)]
[TestCase(Margin_Large)]
public void VerticalStartAndExpand(int margin)
{
var layout = new StackLayout();
layout.MockBounds(new Rectangle(0, 0, Layout_Width, double.PositiveInfinity));
var view = new MockView(margin) { VerticalOptions = LayoutOptions.StartAndExpand };
layout.Children.Add(view);
var region = new Rectangle(Region_X, Region_Y, Region_Width, Region_Height);
Layout.LayoutChildIntoBoundingRegion(view, region);
var target = view.Bounds;
var thickness = margin * 2;
Assume.That(view.VerticalOptions.Alignment == LayoutAlignment.Start,
"view.VerticalOptions.Alignment should be Start");
Assume.That(view.VerticalOptions.Alignment.ToDouble() == 0,
"view.VerticalOptions.Alignment.ToDouble() should be 0");
Assume.That(view.HorizontalOptions.Alignment == LayoutAlignment.Fill,
"view.HorizontalOptions.Alignment should be Fill");
Assert.AreEqual(Expected_X_Fill_Plus_Margin(margin), target.X, "X");
Assert.AreEqual(Expected_Y_Fill_Plus_Margin(margin), target.Y, "Y");
Assert.AreEqual(Expected_Width_Fill_Less_Thickness(thickness), target.Width, "Width");
Assert.AreEqual(Expected_Height_Start_End, target.Height, "Height");
}
[TestCase(Margin_None)]
[TestCase(Margin_Small)]
[TestCase(Margin_Large)]
public void VerticalStartAndExpand_RTL(int margin)
{
var layout = new StackLayout { FlowDirection = FlowDirection.RightToLeft };
layout.MockBounds(new Rectangle(0, 0, Layout_Width, double.PositiveInfinity));
var view = new MockView(margin) { VerticalOptions = LayoutOptions.StartAndExpand };
layout.Children.Add(view);
var region = new Rectangle(Region_X, Region_Y, Region_Width, Region_Height);
Layout.LayoutChildIntoBoundingRegion(view, region);
var target = view.Bounds;
var thickness = margin * 2;
Assume.That(view.VerticalOptions.Alignment == LayoutAlignment.Start,
"view.VerticalOptions.Alignment should be Start");
Assume.That(view.VerticalOptions.Alignment.ToDouble() == 0,
"view.VerticalOptions.Alignment.ToDouble() should be 0");
Assume.That(view.HorizontalOptions.Alignment == LayoutAlignment.Fill,
"view.HorizontalOptions.Alignment should be Fill");
Assert.AreEqual(Expected_X_Fill_RTL_Plus_Margin(margin), target.X, "X");
Assert.AreEqual(Expected_Y_Fill_Plus_Margin(margin), target.Y, "Y");
Assert.AreEqual(Expected_Width_Fill_Less_Thickness(thickness), target.Width, "Width");
Assert.AreEqual(Expected_Height_Start_End, target.Height, "Height");
}
int Expected_Height_Fill_Less_Thickness(int thickness) => Region_Height - thickness;
int Expected_Width_Fill_Less_Thickness(int thickness) => Region_Width - thickness;
int Expected_X_Center_RTL_Plus_Margin(int margin) => Expected_Empty_Region_Width / 2 + Expected_X_Fill_RTL_Plus_Margin(margin);
int Expected_X_End_Less_Margin(int margin) => Expected_Empty_Region_Width + Region_X - margin;
int Expected_X_End_RTL_Plus_Margin(int margin) => Layout_Width - Region_Width - Region_X + margin;
int Expected_X_Fill_Plus_Margin(int margin) => Region_X + margin;
int Expected_X_Fill_RTL_Plus_Margin(int margin) => Layout_Width - Expected_Region_Right + margin;
int Expected_X_Start_Less_Margin(int margin) => Layout_Width - View_Size - Region_X - margin;
int Expected_Y_End_Less_Margin(int margin) => Expected_Empty_Region_Height + Region_Y - margin;
int Expected_Y_Fill_Plus_Margin(int margin) => Region_Y + margin;
class MockView : View
{
public MockView(int margin)
{
Margin = new Thickness(margin);
WidthRequest = View_Size;
HeightRequest = View_Size;
}
}
}
}

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

@ -71,6 +71,9 @@
<Compile Include="ColorUnitTests.cs" />
<Compile Include="CommandSourceTests.cs" />
<Compile Include="CommandTests.cs" />
<Compile Include="EffectiveFlowDirectionExtensions.cs" />
<Compile Include="FlowDirectionTests.cs" />
<Compile Include="LayoutChildIntoBoundingRegionTests.cs" />
<Compile Include="MenuUnitTests.cs" />
<Compile Include="TemplatedViewUnitTests.cs" />
<Compile Include="TemplatedPageUnitTests.cs" />
@ -208,4 +211,4 @@
<LogicalName>Images/crimson.jpg</LogicalName>
</EmbeddedResource>
</ItemGroup>
</Project>
</Project>

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

@ -593,8 +593,7 @@ namespace Xamarin.Forms
OnPropertyChanged(property.PropertyName);
if (property.PropertyChanged != null)
property.PropertyChanged(this, original, value);
property.PropertyChanged?.Invoke(this, original, value);
}
}
@ -661,4 +660,4 @@ namespace Xamarin.Forms
RaiseOnEqual = 1 << 3
}
}
}
}

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

@ -4,10 +4,11 @@ using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Threading.Tasks;
using Xamarin.Forms.Internals;
namespace Xamarin.Forms
{
public abstract class Cell : Element, ICellController
public abstract class Cell : Element, ICellController, IFlowDirectionController
{
public const int DefaultCellHeight = 40;
public static readonly BindableProperty IsEnabledProperty = BindableProperty.Create("IsEnabled", typeof(bool), typeof(Cell), true, propertyChanged: OnIsEnabledPropertyChanged);
@ -18,6 +19,25 @@ namespace Xamarin.Forms
bool _nextCallToForceUpdateSizeQueued;
EffectiveFlowDirection _effectiveFlowDirection = default(EffectiveFlowDirection);
EffectiveFlowDirection IFlowDirectionController.EffectiveFlowDirection
{
get { return _effectiveFlowDirection; }
set
{
if (value == _effectiveFlowDirection)
return;
_effectiveFlowDirection = value;
var ve = (Parent as VisualElement);
ve?.InvalidateMeasureInternal(InvalidationTrigger.Undefined);
OnPropertyChanged(VisualElement.FlowDirectionProperty.PropertyName);
}
}
IFlowDirectionController FlowController => this;
public IList<MenuItem> ContextActions
{
get
@ -75,6 +95,8 @@ namespace Xamarin.Forms
}
}
double IFlowDirectionController.Width => (Parent as VisualElement)?.Width ?? 0;
public event EventHandler Appearing;
public event EventHandler Disappearing;
@ -136,6 +158,8 @@ namespace Xamarin.Forms
}
base.OnParentSet();
FlowController.NotifyFlowDirectionChanged();
}
protected override void OnPropertyChanging(string propertyName = null)
@ -147,6 +171,8 @@ namespace Xamarin.Forms
RealParent.PropertyChanged -= OnParentPropertyChanged;
RealParent.PropertyChanging -= OnParentPropertyChanging;
}
FlowController.NotifyFlowDirectionChanged();
}
base.OnPropertyChanging(propertyName);
@ -172,6 +198,19 @@ namespace Xamarin.Forms
container.SendCellDisappearing(this);
}
void IFlowDirectionController.NotifyFlowDirectionChanged()
{
SetFlowDirectionFromParent(this);
foreach (var element in LogicalChildren)
{
var view = element as IFlowDirectionController;
if (view == null)
continue;
view.NotifyFlowDirectionChanged();
}
}
void OnContextActionsChanged(object sender, NotifyCollectionChangedEventArgs e)
{
for (var i = 0; i < _contextActions.Count; i++)
@ -202,6 +241,8 @@ namespace Xamarin.Forms
// its uncommon enough that we don't want to take the penalty of N GetValue calls to verify.
if (e.PropertyName == "RowHeight")
OnPropertyChanged("RenderHeight");
else if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
FlowController.NotifyFlowDirectionChanged();
}
void OnParentPropertyChanging(object sender, PropertyChangingEventArgs e)

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

@ -27,6 +27,7 @@ namespace Xamarin.Forms
public static void SetIdiom(TargetIdiom value) => Idiom = value;
public static TargetIdiom Idiom { get; internal set; }
//TODO: Why are there two of these? This is never used...?
[EditorBrowsable(EditorBrowsableState.Never)]
public static void SetTargetIdiom(TargetIdiom value) => Idiom = value;
@ -65,6 +66,10 @@ namespace Xamarin.Forms
set { info = value; }
}
[EditorBrowsable(EditorBrowsableState.Never)]
public static void SetFlowDirection(FlowDirection value) => FlowDirection = value;
public static FlowDirection FlowDirection { get; internal set; }
[EditorBrowsable(EditorBrowsableState.Never)]
public static bool IsInvokeRequired
{

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

@ -0,0 +1,11 @@
using System;
namespace Xamarin.Forms
{
[Flags]
public enum EffectiveFlowDirection
{
RightToLeft = 1 << 0,
Explicit = 1 << 1,
}
}

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

@ -0,0 +1,72 @@
using System;
using System.ComponentModel;
namespace Xamarin.Forms
{
[EditorBrowsable(EditorBrowsableState.Never)]
public static class EffectiveFlowDirectionExtensions
{
internal static EffectiveFlowDirection ToEffectiveFlowDirection(this FlowDirection self, bool isExplicit = false)
{
switch (self)
{
case FlowDirection.MatchParent:
return default(EffectiveFlowDirection);
case FlowDirection.LeftToRight:
if (isExplicit)
{
return EffectiveFlowDirection.Explicit;
}
else
{
return default(EffectiveFlowDirection);
}
case FlowDirection.RightToLeft:
if (isExplicit)
{
return EffectiveFlowDirection.RightToLeft | EffectiveFlowDirection.Explicit;
}
else
{
return EffectiveFlowDirection.RightToLeft;
}
default:
throw new InvalidOperationException($"Cannot convert {self} to {nameof(EffectiveFlowDirection)}.");
}
}
internal static FlowDirection ToFlowDirection(this EffectiveFlowDirection self)
{
if (self.IsLeftToRight())
return FlowDirection.LeftToRight;
else
return FlowDirection.RightToLeft;
throw new InvalidOperationException($"Cannot convert {self} to {nameof(FlowDirection)}.");
}
public static bool IsRightToLeft(this EffectiveFlowDirection self)
{
return (self & EffectiveFlowDirection.RightToLeft) == EffectiveFlowDirection.RightToLeft;
}
public static bool IsLeftToRight(this EffectiveFlowDirection self)
{
return (self & EffectiveFlowDirection.RightToLeft) != EffectiveFlowDirection.RightToLeft;
}
public static bool IsImplicit(this EffectiveFlowDirection self)
{
return (self & EffectiveFlowDirection.Explicit) != EffectiveFlowDirection.Explicit;
}
public static bool IsExplicit(this EffectiveFlowDirection self)
{
return (self & EffectiveFlowDirection.Explicit) == EffectiveFlowDirection.Explicit;
}
}
}

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

@ -503,6 +503,27 @@ namespace Xamarin.Forms
internal event EventHandler ParentSet;
internal static void SetFlowDirectionFromParent(Element child)
{
IFlowDirectionController controller = child as IFlowDirectionController;
if (controller == null)
return;
if (controller.EffectiveFlowDirection.IsImplicit())
{
var parentView = child.Parent as IFlowDirectionController;
if (parentView == null)
return;
var flowDirection = parentView.EffectiveFlowDirection.ToFlowDirection();
if (flowDirection != controller.EffectiveFlowDirection.ToFlowDirection())
{
controller.EffectiveFlowDirection = flowDirection.ToEffectiveFlowDirection();
}
}
}
[EditorBrowsable(EditorBrowsableState.Never)]
public event EventHandler PlatformSet;

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

@ -0,0 +1,9 @@
namespace Xamarin.Forms
{
public enum FlowDirection
{
MatchParent = 0,
LeftToRight = 1,
RightToLeft = 2,
}
}

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

@ -0,0 +1,11 @@
namespace Xamarin.Forms
{
internal interface IFlowDirectionController
{
EffectiveFlowDirection EffectiveFlowDirection { get; set; }
double Width { get; }
void NotifyFlowDirectionChanged();
}
}

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

@ -10,6 +10,7 @@ namespace Xamarin.Forms
void InvalidateMeasure(InvalidationTrigger trigger);
bool Batched { get; }
bool DisableLayout { get; set; }
EffectiveFlowDirection EffectiveFlowDirection { get; }
bool IsInNativeLayout { get; set; }
bool IsNativeStateConsistent { get; set; }
bool IsPlatformEnabled { get; set; }

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

@ -55,7 +55,8 @@ namespace Xamarin.Forms
public static readonly BindableProperty IsClippedToBoundsProperty = BindableProperty.Create("IsClippedToBounds", typeof(bool), typeof(Layout), false);
public static readonly BindableProperty PaddingProperty = BindableProperty.Create("Padding", typeof(Thickness), typeof(Layout), default(Thickness),
propertyChanged: (bindable, old, newValue) => {
propertyChanged: (bindable, old, newValue) =>
{
var layout = (Layout)bindable;
layout.UpdateChildrenLayout();
}, defaultValueCreator: (bindable) => ((Layout)bindable).CreateDefaultPadding());
@ -125,6 +126,11 @@ namespace Xamarin.Forms
public static void LayoutChildIntoBoundingRegion(VisualElement child, Rectangle region)
{
var parent = child.Parent as IFlowDirectionController;
bool isRightToLeft = false;
if (parent != null && (isRightToLeft = parent.EffectiveFlowDirection.IsRightToLeft()))
region = new Rectangle(parent.Width - region.Right, region.Y, region.Width, region.Height);
var view = child as View;
if (view == null)
{
@ -137,7 +143,10 @@ namespace Xamarin.Forms
{
SizeRequest request = child.Measure(region.Width, region.Height, MeasureFlags.IncludeMargins);
double diff = Math.Max(0, region.Width - request.Request.Width);
region.X += (int)(diff * horizontalOptions.Alignment.ToDouble());
double horizontalAlign = horizontalOptions.Alignment.ToDouble();
if (isRightToLeft)
horizontalAlign = 1 - horizontalAlign;
region.X += (int)(diff * horizontalAlign);
region.Width -= diff;
}
@ -260,23 +269,33 @@ namespace Xamarin.Forms
internal static void LayoutChildIntoBoundingRegion(View child, Rectangle region, SizeRequest childSizeRequest)
{
var parent = child.Parent as IFlowDirectionController;
bool isRightToLeft = false;
if (parent != null && (isRightToLeft = parent.EffectiveFlowDirection.IsRightToLeft()))
region = new Rectangle(parent.Width - region.Right, region.Y, region.Width, region.Height);
if (region.Size != childSizeRequest.Request)
{
bool canUseAlreadyDoneRequest = region.Width >= childSizeRequest.Request.Width && region.Height >= childSizeRequest.Request.Height;
if (child.HorizontalOptions.Alignment != LayoutAlignment.Fill)
LayoutOptions horizontalOptions = child.HorizontalOptions;
if (horizontalOptions.Alignment != LayoutAlignment.Fill)
{
SizeRequest request = canUseAlreadyDoneRequest ? childSizeRequest : child.Measure(region.Width, region.Height, MeasureFlags.IncludeMargins);
double diff = Math.Max(0, region.Width - request.Request.Width);
region.X += (int)(diff * child.HorizontalOptions.Alignment.ToDouble());
double horizontalAlign = horizontalOptions.Alignment.ToDouble();
if (isRightToLeft)
horizontalAlign = 1 - horizontalAlign;
region.X += (int)(diff * horizontalAlign);
region.Width -= diff;
}
if (child.VerticalOptions.Alignment != LayoutAlignment.Fill)
LayoutOptions verticalOptions = child.VerticalOptions;
if (verticalOptions.Alignment != LayoutAlignment.Fill)
{
SizeRequest request = canUseAlreadyDoneRequest ? childSizeRequest : child.Measure(region.Width, region.Height, MeasureFlags.IncludeMargins);
double diff = Math.Max(0, region.Height - request.Request.Height);
region.Y += (int)(diff * child.VerticalOptions.Alignment.ToDouble());
region.Y += (int)(diff * verticalOptions.Alignment.ToDouble());
region.Height -= diff;
}
}

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

@ -5,7 +5,7 @@ using Xamarin.Forms.Internals;
namespace Xamarin.Forms
{
public partial class VisualElement : Element, IAnimatable, IVisualElementController, IResourcesProvider
public partial class VisualElement : Element, IAnimatable, IVisualElementController, IResourcesProvider, IFlowDirectionController
{
internal static readonly BindablePropertyKey NavigationPropertyKey = BindableProperty.CreateReadOnly("Navigation", typeof(INavigation), typeof(VisualElement), default(INavigation));
@ -93,6 +93,33 @@ namespace Xamarin.Forms
public static readonly BindableProperty IsFocusedProperty = IsFocusedPropertyKey.BindableProperty;
public static readonly BindableProperty FlowDirectionProperty = BindableProperty.Create(nameof(FlowDirection), typeof(FlowDirection), typeof(VisualElement), FlowDirection.MatchParent, propertyChanged: FlowDirectionChanged);
IFlowDirectionController FlowController => this;
public FlowDirection FlowDirection
{
get { return (FlowDirection)GetValue(FlowDirectionProperty); }
set { SetValue(FlowDirectionProperty, value); }
}
EffectiveFlowDirection _effectiveFlowDirection = default(EffectiveFlowDirection);
EffectiveFlowDirection IFlowDirectionController.EffectiveFlowDirection
{
get { return _effectiveFlowDirection; }
set
{
if (value == _effectiveFlowDirection)
return;
_effectiveFlowDirection = value;
InvalidateMeasureInternal(InvalidationTrigger.Undefined);
OnPropertyChanged(FlowDirectionProperty.PropertyName);
}
}
EffectiveFlowDirection IVisualElementController.EffectiveFlowDirection => FlowController.EffectiveFlowDirection;
readonly Dictionary<Size, SizeRequest> _measureCache = new Dictionary<Size, SizeRequest>();
readonly MergedStyle _mergedStyle;
@ -250,7 +277,7 @@ namespace Xamarin.Forms
set { SetValue(StyleProperty, value); }
}
[TypeConverter (typeof(ListStringTypeConverter))]
[TypeConverter(typeof(ListStringTypeConverter))]
public IList<string> StyleClass
{
get { return _mergedStyle.StyleClass; }
@ -537,9 +564,6 @@ namespace Xamarin.Forms
if (includeMargins)
{
if (!margin.IsDefault)
{
result.Minimum = new Size(result.Minimum.Width + margin.HorizontalThickness, result.Minimum.Height + margin.VerticalThickness);
@ -616,6 +640,8 @@ namespace Xamarin.Forms
NavigationProxy.Inner = null;
}
#pragma warning restore 0618
FlowController.NotifyFlowDirectionChanged();
}
protected virtual void OnSizeAllocated(double width, double height)
@ -738,6 +764,20 @@ namespace Xamarin.Forms
focus(this, new FocusEventArgs(this, true));
}
static void FlowDirectionChanged(BindableObject bindable, object oldValue, object newValue)
{
var self = bindable as IFlowDirectionController;
if (self.EffectiveFlowDirection.IsExplicit() && oldValue == newValue)
return;
var newFlowDirection = (FlowDirection)newValue;
self.EffectiveFlowDirection = newFlowDirection.ToEffectiveFlowDirection(isExplicit: true);
self.NotifyFlowDirectionChanged();
}
static void OnIsFocusedPropertyChanged(BindableObject bindable, object oldvalue, object newvalue)
{
var element = bindable as VisualElement;
@ -777,6 +817,19 @@ namespace Xamarin.Forms
unFocus(this, new FocusEventArgs(this, false));
}
void IFlowDirectionController.NotifyFlowDirectionChanged()
{
SetFlowDirectionFromParent(this);
foreach (var element in LogicalChildren)
{
var view = element as IFlowDirectionController;
if (view == null)
continue;
view.NotifyFlowDirectionChanged();
}
}
void SetSize(double width, double height)
{
if (Width == width && Height == height)

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

@ -77,7 +77,9 @@
<Compile Include="DataTemplateSelector.cs" />
<Compile Include="DateChangedEventArgs.cs" />
<Compile Include="DelegateLogListener.cs" />
<Compile Include="EffectiveFlowDirectionExtensions.cs" />
<Compile Include="IEditorController.cs" />
<Compile Include="IFlowDirectionController.cs" />
<Compile Include="IGridController.cs" />
<Compile Include="IWebViewController.cs" />
<Compile Include="INavigationMenuController.cs" />
@ -110,6 +112,8 @@
<Compile Include="IElementConfiguration.cs" />
<Compile Include="IPlatformElementConfiguration.cs" />
<Compile Include="PlatformConfigurationRegistry.cs" />
<Compile Include="EffectiveFlowDirection.cs" />
<Compile Include="FlowDirection.cs" />
<Compile Include="IFontElement.cs" />
<Compile Include="DependencyAttribute.cs" />
<Compile Include="DependencyFetchTarget.cs" />

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

@ -153,6 +153,8 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
UpdateMaster();
UpdateDetail();
UpdateFlowDirection();
((IMasterDetailPageController)newElement).BackButtonPressed += OnBackButtonPressed;
newElement.PropertyChanged += HandlePropertyChanged;
newElement.Appearing += MasterDetailPageAppearing;
@ -310,6 +312,8 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
UpdateBackgroundImage(Element);
else if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName)
UpdateBackgroundColor(Element);
else if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
UpdateFlowDirection();
}
void MasterDetailPageAppearing(object sender, EventArgs e)
@ -367,6 +371,11 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
_detailLayout.ChildView = Element.Detail;
}
void UpdateFlowDirection()
{
this.UpdateFlowDirection(Element);
}
void UpdateIsPresented()
{
if (_isPresentingFromCore)

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

@ -11,6 +11,7 @@ using AColor = Android.Graphics.Color;
using AColorDraw = Android.Graphics.Drawables.ColorDrawable;
using Xamarin.Forms.Internals;
using Android.Support.V4.Widget;
using Android.OS;
namespace Xamarin.Forms.Platform.Android
{
@ -80,6 +81,12 @@ namespace Xamarin.Forms.Platform.Android
SetMinimumHeight((int)context.ToPixels(DefaultMinHeight));
_androidDefaultTextColor = Color.FromUint((uint)_mainText.CurrentTextColor);
if ((int)Build.VERSION.SdkInt > 16)
{
_mainText.TextAlignment = global::Android.Views.TextAlignment.ViewStart;
_detailText.TextAlignment = global::Android.Views.TextAlignment.ViewStart;
}
}
public AView AccessoryView { get; private set; }

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

@ -29,6 +29,7 @@ namespace Xamarin.Forms.Platform.Android
UpdateText();
UpdateIsEnabled();
UpdateHeight();
UpdateFlowDirection();
_view.TextChanged = OnTextChanged;
_view.EditingCompleted = OnEditingCompleted;
@ -56,6 +57,11 @@ namespace Xamarin.Forms.Platform.Android
UpdateIsEnabled();
else if (e.PropertyName == "RenderHeight")
UpdateHeight();
else if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
{
UpdateFlowDirection();
UpdateHorizontalTextAlignment();
}
}
protected virtual NumberKeyListener GetDigitsKeyListener(InputTypes inputTypes)
@ -86,7 +92,7 @@ namespace Xamarin.Forms.Platform.Android
void UpdateHorizontalTextAlignment()
{
var entryCell = (EntryCell)Cell;
_view.EditText.Gravity = entryCell.HorizontalTextAlignment.ToHorizontalGravityFlags();
_view.EditText.UpdateHorizontalAlignment(entryCell.HorizontalTextAlignment);
}
void UpdateIsEnabled()
@ -117,6 +123,11 @@ namespace Xamarin.Forms.Platform.Android
_view.SetLabelTextColor(((EntryCell)Cell).LabelColor, global::Android.Resource.Color.PrimaryTextDark);
}
void UpdateFlowDirection()
{
_view.UpdateFlowDirection(ParentView);
}
void UpdatePlaceholder()
{
var entryCell = (EntryCell)Cell;

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

@ -11,6 +11,7 @@ namespace Xamarin.Forms.Platform.Android
var result = (BaseCellView)base.GetCellCore(item, convertView, parent, context);
UpdateImage();
UpdateFlowDirection();
return result;
}
@ -20,6 +21,8 @@ namespace Xamarin.Forms.Platform.Android
base.OnCellPropertyChanged(sender, args);
if (args.PropertyName == ImageCell.ImageSourceProperty.PropertyName)
UpdateImage();
else if (args.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
UpdateFlowDirection();
}
void UpdateImage()
@ -33,5 +36,10 @@ namespace Xamarin.Forms.Platform.Android
else
View.SetImageVisible(false);
}
void UpdateFlowDirection()
{
View.UpdateFlowDirection(ParentView);
}
}
}

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

@ -8,7 +8,6 @@ namespace Xamarin.Forms.Platform.Android
{
public class SwitchCellRenderer : CellRenderer
{
const double DefaultHeight = 30;
SwitchCellView _view;
protected override AView GetCellCore(Cell item, AView convertView, ViewGroup parent, Context context)
@ -24,6 +23,7 @@ namespace Xamarin.Forms.Platform.Android
UpdateChecked();
UpdateHeight();
UpdateIsEnabled(_view, cell);
UpdateFlowDirection();
return _view;
}
@ -38,6 +38,8 @@ namespace Xamarin.Forms.Platform.Android
UpdateHeight();
else if (args.PropertyName == Cell.IsEnabledProperty.PropertyName)
UpdateIsEnabled(_view, (SwitchCell)sender);
else if (args.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
UpdateFlowDirection();
}
void UpdateChecked()
@ -53,6 +55,11 @@ namespace Xamarin.Forms.Platform.Android
aSwitch.Enabled = switchCell.IsEnabled;
}
void UpdateFlowDirection()
{
_view.UpdateFlowDirection(ParentView);
}
void UpdateHeight()
{
_view.SetRenderHeight(Cell.RenderHeight);

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

@ -1,9 +1,8 @@
using System.ComponentModel;
using Android.Content;
using Android.Views;
using AView = Android.Views.View;
using AColor = Android.Graphics.Color;
using Xamarin.Forms.Internals;
using AView = Android.Views.View;
namespace Xamarin.Forms.Platform.Android
{
@ -20,6 +19,7 @@ namespace Xamarin.Forms.Platform.Android
UpdateDetailText();
UpdateHeight();
UpdateIsEnabled();
UpdateFlowDirection();
View.SetImageVisible(false);
return View;
@ -35,6 +35,8 @@ namespace Xamarin.Forms.Platform.Android
UpdateIsEnabled();
else if (args.PropertyName == "RenderHeight")
UpdateHeight();
else if (args.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
UpdateFlowDirection();
}
void UpdateDetailText()
@ -55,6 +57,11 @@ namespace Xamarin.Forms.Platform.Android
View.SetIsEnabled(cell.IsEnabled);
}
void UpdateFlowDirection()
{
View.UpdateFlowDirection(ParentView);
}
void UpdateMainText()
{
var cell = (TextCell)Cell;

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

@ -0,0 +1,44 @@
using Android.OS;
using Android.Widget;
using Xamarin.Forms.Internals;
using ALayoutDirection = Android.Views.LayoutDirection;
using AView = Android.Views.View;
namespace Xamarin.Forms.Platform.Android
{
internal static class FlowDirectionExtensions
{
internal static FlowDirection ToFlowDirection(this ALayoutDirection direction)
{
switch (direction)
{
case ALayoutDirection.Ltr:
return FlowDirection.LeftToRight;
case ALayoutDirection.Rtl:
return FlowDirection.RightToLeft;
default:
return FlowDirection.MatchParent;
}
}
internal static void UpdateFlowDirection(this AView view, IVisualElementController controller)
{
if (view == null || controller == null || (int)Build.VERSION.SdkInt < 17)
return;
if (controller.EffectiveFlowDirection.IsRightToLeft())
view.LayoutDirection = ALayoutDirection.Rtl;
else if (controller.EffectiveFlowDirection.IsLeftToRight())
view.LayoutDirection = ALayoutDirection.Ltr;
}
internal static void UpdateHorizontalAlignment(this EditText view, TextAlignment alignment)
{
if ((int)Build.VERSION.SdkInt < 17)
view.Gravity = alignment.ToHorizontalGravityFlags();
else
view.TextAlignment = alignment.ToTextAlignment();
}
}
}

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

@ -3,7 +3,6 @@ using System.ComponentModel;
using Android.Views;
using Xamarin.Forms.Internals;
using AView = Android.Views.View;
using Object = Java.Lang.Object;
namespace Xamarin.Forms.Platform.Android.FastRenderers
{
@ -14,7 +13,7 @@ namespace Xamarin.Forms.Platform.Android.FastRenderers
IVisualElementRenderer _renderer;
readonly GestureManager _gestureManager;
readonly AutomationPropertiesProvider _automatiomPropertiesProvider;
readonly AutomationPropertiesProvider _automationPropertiesProvider;
readonly EffectControlProvider _effectControlProvider;
public VisualElementRenderer(IVisualElementRenderer renderer)
@ -23,13 +22,13 @@ namespace Xamarin.Forms.Platform.Android.FastRenderers
_renderer.ElementPropertyChanged += OnElementPropertyChanged;
_renderer.ElementChanged += OnElementChanged;
_gestureManager = new GestureManager(_renderer);
_automatiomPropertiesProvider = new AutomationPropertiesProvider(_renderer);
_automationPropertiesProvider = new AutomationPropertiesProvider(_renderer);
_effectControlProvider = new EffectControlProvider(_renderer?.View);
}
VisualElement Element => _renderer?.Element;
AView Control => _renderer?.View;
void IEffectControlProvider.RegisterEffect(Effect effect)
@ -45,7 +44,15 @@ namespace Xamarin.Forms.Platform.Android.FastRenderers
Control.SetBackgroundColor((color ?? Element.BackgroundColor).ToAndroid());
}
public bool OnTouchEvent(MotionEvent e)
void UpdateFlowDirection()
{
if (_disposed)
return;
Control.UpdateFlowDirection(Element);
}
public bool OnTouchEvent(MotionEvent e)
{
return _gestureManager.OnTouchEvent(e);
}
@ -66,7 +73,7 @@ namespace Xamarin.Forms.Platform.Android.FastRenderers
if (disposing)
{
_gestureManager?.Dispose();
_automatiomPropertiesProvider?.Dispose();
_automationPropertiesProvider?.Dispose();
if (_renderer != null)
{
@ -88,6 +95,7 @@ namespace Xamarin.Forms.Platform.Android.FastRenderers
{
e.NewElement.PropertyChanged += OnElementPropertyChanged;
UpdateBackgroundColor();
UpdateFlowDirection();
}
EffectUtilities.RegisterEffectControlProvider(this, e.OldElement, e.NewElement);
@ -97,6 +105,8 @@ namespace Xamarin.Forms.Platform.Android.FastRenderers
{
if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName)
UpdateBackgroundColor();
else if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
UpdateFlowDirection();
}
}
}

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

@ -20,6 +20,7 @@ using Xamarin.Forms.Internals;
using Xamarin.Forms.Platform.Android;
using Resource = Android.Resource;
using Trace = System.Diagnostics.Trace;
using ALayoutDirection = Android.Views.LayoutDirection;
namespace Xamarin.Forms
{
@ -69,7 +70,7 @@ namespace Xamarin.Forms
+ "Please use SetTitleBarVisibility(Activity, AndroidTitleBarVisibility) instead.")]
public static void SetTitleBarVisibility(AndroidTitleBarVisibility visibility)
{
if((Activity)Context == null)
if ((Activity)Context == null)
throw new NullReferenceException("Must be called after Xamarin.Forms.Forms.Init() method");
if (visibility == AndroidTitleBarVisibility.Never)
@ -138,7 +139,7 @@ namespace Xamarin.Forms
// We want this to be updated when we have a new activity (e.g. on a configuration change)
// because AndroidPlatformServices needs a current activity to launch URIs from
Device.PlatformServices = new AndroidPlatformServices(activity);
// use field and not property to avoid exception in getter
if (Device.info != null)
{
@ -165,6 +166,7 @@ namespace Xamarin.Forms
// This could change as a result of a config change, so we need to check it every time
int minWidthDp = activity.Resources.Configuration.SmallestScreenWidthDp;
Device.SetIdiom(minWidthDp >= TabletCrossover ? TargetIdiom.Tablet : TargetIdiom.Phone);
Device.SetFlowDirection(activity.Resources.Configuration.LayoutDirection.ToFlowDirection());
if (ExpressionSearch.Default == null)
ExpressionSearch.Default = new AndroidExpressionSearch();
@ -198,11 +200,11 @@ namespace Xamarin.Forms
Color rc;
using (var value = new TypedValue())
{
if (context.Theme.ResolveAttribute(global::Android.Resource.Attribute.ColorAccent, value, true) && Forms.IsLollipopOrNewer) // Android 5.0+
if (context.Theme.ResolveAttribute(global::Android.Resource.Attribute.ColorAccent, value, true) && Forms.IsLollipopOrNewer) // Android 5.0+
{
rc = Color.FromUint((uint)value.Data);
}
else if(context.Theme.ResolveAttribute(context.Resources.GetIdentifier("colorAccent", "attr", context.PackageName), value, true)) // < Android 5.0
else if (context.Theme.ResolveAttribute(context.Resources.GetIdentifier("colorAccent", "attr", context.PackageName), value, true)) // < Android 5.0
{
rc = Color.FromUint((uint)value.Data);
}
@ -475,7 +477,7 @@ namespace Xamarin.Forms
{
global::Android.Net.Uri aUri = global::Android.Net.Uri.Parse(uri.ToString());
var intent = new Intent(Intent.ActionView, aUri);
// This seems to work fine even if the context has been destroyed (while another activity is in the
// foreground). If we run into a situation where that's not the case, we'll have to do some work to
// make sure this uses the active activity when launching the Intent

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

@ -1,9 +1,23 @@
using Android.Views;
using ATextAlignment = Android.Views.TextAlignment;
namespace Xamarin.Forms.Platform.Android
{
internal static class AlignmentExtensions
{
internal static ATextAlignment ToTextAlignment(this TextAlignment alignment)
{
switch (alignment)
{
case TextAlignment.Center:
return ATextAlignment.Center;
case TextAlignment.End:
return ATextAlignment.ViewEnd;
default:
return ATextAlignment.ViewStart;
}
}
internal static GravityFlags ToHorizontalGravityFlags(this TextAlignment alignment)
{
switch (alignment)
@ -11,9 +25,9 @@ namespace Xamarin.Forms.Platform.Android
case TextAlignment.Center:
return GravityFlags.CenterHorizontal;
case TextAlignment.End:
return GravityFlags.Right;
return GravityFlags.End;
default:
return GravityFlags.Left;
return GravityFlags.Start;
}
}

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

@ -2,6 +2,7 @@ using System;
using System.ComponentModel;
using Android.Content;
using Android.Content.Res;
using Android.OS;
using Android.Text;
using Android.Text.Method;
using Android.Util;
@ -28,7 +29,7 @@ namespace Xamarin.Forms.Platform.Android
AutoPackage = false;
}
IEditorController ElementController => Element;
IEditorController ElementController => Element;
void ITextWatcher.AfterTextChanged(IEditable s)
{
@ -70,6 +71,8 @@ namespace Xamarin.Forms.Platform.Android
edit.SetSingleLine(false);
edit.Gravity = GravityFlags.Top;
if ((int)Build.VERSION.SdkInt > 16)
edit.TextAlignment = global::Android.Views.TextAlignment.ViewStart;
edit.SetHorizontallyScrolling(false);
UpdateText();
@ -127,7 +130,7 @@ namespace Xamarin.Forms.Platform.Android
internal override void OnNativeFocusChanged(bool hasFocus)
{
if (Element.IsFocused && !hasFocus) // Editor has requested an unfocus, fire completed event
ElementController.SendCompleted();
ElementController.SendCompleted();
}
void UpdateFont()

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

@ -9,8 +9,6 @@ using Android.Views;
using Android.Views.InputMethods;
using Android.Widget;
using Java.Lang;
using Xamarin.Forms.Internals;
using Xamarin.Forms.PlatformConfiguration.AndroidSpecific;
namespace Xamarin.Forms.Platform.Android
{
@ -143,6 +141,8 @@ namespace Xamarin.Forms.Platform.Android
UpdateFont();
else if (e.PropertyName == Entry.PlaceholderColorProperty.PropertyName)
UpdatePlaceholderColor();
else if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
UpdateAlignment();
base.OnElementPropertyChanged(sender, e);
}
@ -157,7 +157,7 @@ namespace Xamarin.Forms.Platform.Android
void UpdateAlignment()
{
Control.Gravity = Element.HorizontalTextAlignment.ToHorizontalGravityFlags();
Control.UpdateHorizontalAlignment(Element.HorizontalTextAlignment);
}
void UpdateColor()

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

@ -115,6 +115,8 @@ namespace Xamarin.Forms.Platform.Android
UpdateTextColor();
else if (e.PropertyName == SearchBar.PlaceholderColorProperty.PropertyName)
UpdatePlaceholderColor();
else if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
UpdateAlignment();
}
internal override void OnNativeFocusChanged(bool hasFocus)
@ -130,7 +132,7 @@ namespace Xamarin.Forms.Platform.Android
if (_editText == null)
return;
_editText.Gravity = Element.HorizontalTextAlignment.ToHorizontalGravityFlags() | Xamarin.Forms.TextAlignment.Center.ToVerticalGravityFlags();
_editText.UpdateHorizontalAlignment(Element.HorizontalTextAlignment);
}
void UpdateCancelButtonColor()

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

@ -8,6 +8,7 @@ using Android.Text.Format;
using ADatePicker = Android.Widget.DatePicker;
using ATimePicker = Android.Widget.TimePicker;
using Object = Java.Lang.Object;
using Android.OS;
namespace Xamarin.Forms.Platform.Android
{
@ -60,12 +61,15 @@ namespace Xamarin.Forms.Platform.Android
textField.SetOnClickListener(TimePickerListener.Instance);
SetNativeControl(textField);
_textColorSwitcher = new TextColorSwitcher(textField.TextColors);
_is24HourFormat = DateFormat.Is24HourFormat(Context);
_is24HourFormat = DateFormat.Is24HourFormat(Context);
_timeFormat = _is24HourFormat ? "HH:mm" : Element.Format;
}
SetTime(e.NewElement.Time);
UpdateTextColor();
if ((int)Build.VERSION.SdkInt > 16)
Control.TextAlignment = global::Android.Views.TextAlignment.ViewStart;
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
@ -73,7 +77,7 @@ namespace Xamarin.Forms.Platform.Android
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == TimePicker.TimeProperty.PropertyName ||
e.PropertyName == TimePicker.FormatProperty.PropertyName)
e.PropertyName == TimePicker.FormatProperty.PropertyName)
SetTime(Element.Time);
if (e.PropertyName == TimePicker.TextColorProperty.PropertyName)
@ -103,7 +107,7 @@ namespace Xamarin.Forms.Platform.Android
{
TimePicker view = Element;
ElementController.SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, true);
_dialog = new TimePickerDialog(Context, this, view.Time.Hours, view.Time.Minutes, _is24HourFormat);
if (Forms.IsLollipopOrNewer)

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

@ -147,6 +147,8 @@ namespace Xamarin.Forms.Platform.Android
UpdateIsEnabled();
else if (e.PropertyName == AutomationProperties.LabeledByProperty.PropertyName)
SetLabeledBy();
else if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
UpdateFlowDirection();
}
protected override void OnLayout(bool changed, int l, int t, int r, int b)
@ -319,6 +321,7 @@ namespace Xamarin.Forms.Platform.Android
Control.OnFocusChangeListener = this;
UpdateIsEnabled();
UpdateFlowDirection();
SetLabeledBy();
}
@ -345,5 +348,10 @@ namespace Xamarin.Forms.Platform.Android
if (Control != null)
Control.Enabled = Element.IsEnabled;
}
void UpdateFlowDirection()
{
Control.UpdateFlowDirection(Element);
}
}
}

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

@ -128,6 +128,7 @@
<Compile Include="ExportCellAttribute.cs" />
<Compile Include="ExportImageSourceHandlerAttribute.cs" />
<Compile Include="ExportRendererAttribute.cs" />
<Compile Include="Extensions\FlowDirectionExtensions.cs" />
<Compile Include="GestureManager.cs" />
<Compile Include="FastRenderers\LabelRenderer.cs" />
<Compile Include="FastRenderers\VisualElementRenderer.cs" />
@ -300,4 +301,4 @@
<Import Project="..\packages\Xamarin.Android.Support.Animated.Vector.Drawable.25.4.0.2\build\MonoAndroid70\Xamarin.Android.Support.Animated.Vector.Drawable.targets" Condition="Exists('..\packages\Xamarin.Android.Support.Animated.Vector.Drawable.25.4.0.2\build\MonoAndroid70\Xamarin.Android.Support.Animated.Vector.Drawable.targets')" />
<Import Project="..\packages\Xamarin.Android.Support.v7.AppCompat.25.4.0.2\build\MonoAndroid70\Xamarin.Android.Support.v7.AppCompat.targets" Condition="Exists('..\packages\Xamarin.Android.Support.v7.AppCompat.25.4.0.2\build\MonoAndroid70\Xamarin.Android.Support.v7.AppCompat.targets')" />
<Import Project="..\packages\Xamarin.Android.Support.Design.25.4.0.2\build\MonoAndroid70\Xamarin.Android.Support.Design.targets" Condition="Exists('..\packages\Xamarin.Android.Support.Design.25.4.0.2\build\MonoAndroid70\Xamarin.Android.Support.Design.targets')" />
</Project>
</Project>

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

@ -80,6 +80,8 @@ namespace Xamarin.Forms.Platform.MacOS
UpdateHorizontalTextAlignment(realCell, entryCell);
else if (e.PropertyName == Cell.IsEnabledProperty.PropertyName)
UpdateIsEnabled(realCell, entryCell);
else if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
UpdateHorizontalTextAlignment(realCell, entryCell);
}
static void OnTextFieldTextChanged(object sender, EventArgs eventArgs)
@ -101,9 +103,11 @@ namespace Xamarin.Forms.Platform.MacOS
static void UpdateHorizontalTextAlignment(CellNSView cell, EntryCell entryCell)
{
IVisualElementController viewController = entryCell.Parent as VisualElement;
var nsTextField = cell.AccessoryView.Subviews[0] as NSTextField;
if (nsTextField != null)
nsTextField.Alignment = entryCell.HorizontalTextAlignment.ToNativeTextAlignment();
nsTextField.Alignment = entryCell.HorizontalTextAlignment.ToNativeTextAlignment(viewController?.EffectiveFlowDirection ?? default(EffectiveFlowDirection));
}
static void UpdateIsEnabled(CellNSView cell, EntryCell entryCell)

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

@ -1,20 +1,28 @@
using System;
using AppKit;
using Xamarin.Forms.Internals;
namespace Xamarin.Forms.Platform.MacOS
{
internal static class AlignmentExtensions
{
internal static NSTextAlignment ToNativeTextAlignment(this TextAlignment alignment)
internal static NSTextAlignment ToNativeTextAlignment(this TextAlignment alignment, EffectiveFlowDirection flowDirection)
{
var isLtr = flowDirection.IsLeftToRight();
switch (alignment)
{
case TextAlignment.Center:
return NSTextAlignment.Center;
case TextAlignment.End:
return NSTextAlignment.Right;
if (isLtr)
return NSTextAlignment.Right;
else
return NSTextAlignment.Left;
default:
return NSTextAlignment.Left;
if (isLtr)
return NSTextAlignment.Left;
else
return NSTextAlignment.Natural;
}
}
}

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

@ -0,0 +1,47 @@
using AppKit;
using Xamarin.Forms.Internals;
namespace Xamarin.Forms.Platform.MacOS
{
internal static class FlowDirectionExtensions
{
internal static FlowDirection ToFlowDirection(this NSApplicationLayoutDirection direction)
{
switch (direction)
{
case NSApplicationLayoutDirection.LeftToRight:
return FlowDirection.LeftToRight;
case NSApplicationLayoutDirection.RightToLeft:
return FlowDirection.RightToLeft;
default:
return FlowDirection.MatchParent;
}
}
internal static void UpdateFlowDirection(this NSView view, IVisualElementController controller)
{
if (controller == null || view == null)
return;
if (controller.EffectiveFlowDirection.IsRightToLeft())
view.UserInterfaceLayoutDirection = NSUserInterfaceLayoutDirection.RightToLeft;
else if (controller.EffectiveFlowDirection.IsLeftToRight())
view.UserInterfaceLayoutDirection = NSUserInterfaceLayoutDirection.LeftToRight;
}
internal static void UpdateFlowDirection(this NSTextField control, IVisualElementController controller)
{
if (controller == null || control == null)
return;
if (controller.EffectiveFlowDirection.IsRightToLeft())
{
control.Alignment = NSTextAlignment.Right;
}
else if (controller.EffectiveFlowDirection.IsLeftToRight())
{
control.Alignment = NSTextAlignment.Left;
}
}
}
}

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

@ -98,6 +98,8 @@ namespace Xamarin.Forms.Platform.MacOS
UpdateColor();
UpdatePlaceholder();
}
else if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
UpdateAlignment();
base.OnElementPropertyChanged(sender, e);
}
@ -152,7 +154,7 @@ namespace Xamarin.Forms.Platform.MacOS
void UpdateAlignment()
{
Control.Alignment = Element.HorizontalTextAlignment.ToNativeTextAlignment();
Control.Alignment = Element.HorizontalTextAlignment.ToNativeTextAlignment(((IVisualElementController)Element).EffectiveFlowDirection);
}
void UpdateColor()

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

@ -83,6 +83,8 @@ namespace Xamarin.Forms.Platform.MacOS
UpdateFont();
else if (e.PropertyName == SearchBar.HorizontalTextAlignmentProperty.PropertyName)
UpdateAlignment();
else if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
UpdateAlignment();
}
protected override void SetBackgroundColor(Color color)
@ -125,7 +127,7 @@ namespace Xamarin.Forms.Platform.MacOS
void UpdateAlignment()
{
Control.Alignment = Element.HorizontalTextAlignment.ToNativeTextAlignment();
Control.Alignment = Element.HorizontalTextAlignment.ToNativeTextAlignment(((IVisualElementController)Element).EffectiveFlowDirection);
}
void UpdateCancelButton()

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

@ -70,6 +70,7 @@
<Compile Include="..\Xamarin.Forms.Platform.iOS\Flags.cs">
<Link>Flags.cs</Link>
</Compile>
<Compile Include="Extensions\FlowDirectionExtensions.cs" />
<Compile Include="FormsApplicationDelegate.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="PlatformRenderer.cs" />

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

@ -2,7 +2,6 @@
using System.ComponentModel;
using System.Threading.Tasks;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Automation;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Media;
@ -18,7 +17,7 @@ namespace Xamarin.Forms.Platform.UWP
bool _showTitle;
VisualElementTracker<Page, FrameworkElement> _tracker;
public MasterDetailControl Control { get; private set; }
public MasterDetailPage Element { get; private set; }
@ -169,6 +168,8 @@ namespace Xamarin.Forms.Platform.UWP
UpdateMode();
else if(e.PropertyName == PlatformConfiguration.WindowsSpecific.Page.ToolbarPlacementProperty.PropertyName)
UpdateToolbarPlacement();
else if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
UpdateFlowDirection();
}
void ClearDetail()
@ -208,6 +209,7 @@ namespace Xamarin.Forms.Platform.UWP
Element.SendAppearing();
UpdateBounds();
UpdateFlowDirection();
}
void OnControlUnloaded(object sender, RoutedEventArgs routedEventArgs)
@ -277,6 +279,11 @@ namespace Xamarin.Forms.Platform.UWP
(this as ITitleProvider).ShowTitle = !string.IsNullOrEmpty(Control.DetailTitle);
}
void UpdateFlowDirection()
{
Control.UpdateFlowDirection(Element);
}
void UpdateIsPresented()
{
// Ignore the IsPresented value being set to false for Split mode on desktop and allow the master

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

@ -408,7 +408,7 @@
</DataTemplate>
<DataTemplate x:Key="SwitchCell">
<Grid HorizontalAlignment="Stretch">
<Grid HorizontalAlignment="Stretch" x:Name="ParentGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
@ -416,7 +416,7 @@
<TextBlock Grid.Column="0" Text="{Binding Text}" VerticalAlignment="Center" Style="{ThemeResource BaseTextBlockStyle}" />
<ToggleSwitch Grid.Column="1" IsOn="{Binding On, Mode=TwoWay}" OnContent="" OffContent="" VerticalAlignment="Center" />
<ToggleSwitch Grid.Column="1" IsOn="{Binding On, Mode=TwoWay}" OnContent="" OffContent="" VerticalAlignment="Center" FlowDirection="{Binding FlowDirection, ElementName=ParentGrid }" />
</Grid>
</DataTemplate>

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

@ -63,6 +63,8 @@ namespace Xamarin.Forms.Platform.UWP
UpdateTextColor();
else if (e.PropertyName == SearchBar.PlaceholderColorProperty.PropertyName)
UpdatePlaceholderColor();
else if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
UpdateAlignment();
}
void OnControlLoaded(object sender, RoutedEventArgs routedEventArgs)
@ -92,7 +94,7 @@ namespace Xamarin.Forms.Platform.UWP
if (_queryTextBox == null)
return;
_queryTextBox.TextAlignment = Element.HorizontalTextAlignment.ToNativeTextAlignment();
_queryTextBox.TextAlignment = Element.HorizontalTextAlignment.ToNativeTextAlignment(((IVisualElementController)Element).EffectiveFlowDirection);
}
void UpdateCancelButtonColor()

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

@ -134,6 +134,9 @@
<Compile Include="..\Xamarin.Forms.Platform.WinRT\BrushHelpers.cs">
<Link>BrushHelpers.cs</Link>
</Compile>
<Compile Include="..\Xamarin.Forms.Platform.WinRT\FlowDirectionExtensions.cs">
<Link>FlowDirectionExtensions.cs</Link>
</Compile>
<Compile Include="..\Xamarin.Forms.Platform.WinRT\FormsProgressBar.cs">
<Link>FormsProgressBar.cs</Link>
</Compile>

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

@ -4,6 +4,7 @@ using System.Diagnostics;
using System.Linq;
using System.Reflection;
using Windows.ApplicationModel.Activation;
using Windows.ApplicationModel.Resources.Core;
using Windows.Foundation.Metadata;
using Windows.UI;
using Windows.UI.Core;
@ -48,6 +49,7 @@ namespace Xamarin.Forms
Windows.UI.Xaml.Application.Current.Resources.MergedDictionaries.Add(GetTabletResources());
Device.SetIdiom(TargetIdiom.Tablet);
Device.SetFlowDirection(GetFlowDirection());
Device.PlatformServices = new WindowsPlatformServices(Window.Current.Dispatcher);
#if WINDOWS_UWP
Device.SetFlags(s_flags);
@ -96,6 +98,17 @@ namespace Xamarin.Forms
return Windows.Foundation.Metadata.Platform.Windows;
}
static FlowDirection GetFlowDirection()
{
string resourceFlowDirection = ResourceContext.GetForCurrentView().QualifierValues["LayoutDirection"];
if (resourceFlowDirection == "LTR")
return FlowDirection.LeftToRight;
else if (resourceFlowDirection == "RTL")
return FlowDirection.RightToLeft;
return FlowDirection.MatchParent;
}
static Windows.UI.Xaml.ResourceDictionary GetTabletResources()
{
return new Windows.UI.Xaml.ResourceDictionary {

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

@ -1,4 +1,5 @@
using Windows.UI.Xaml;
using Xamarin.Forms.Internals;
#if WINDOWS_UWP
@ -10,16 +11,23 @@ namespace Xamarin.Forms.Platform.WinRT
{
internal static class AlignmentExtensions
{
internal static Windows.UI.Xaml.TextAlignment ToNativeTextAlignment(this TextAlignment alignment)
internal static Windows.UI.Xaml.TextAlignment ToNativeTextAlignment(this TextAlignment alignment, EffectiveFlowDirection flowDirection = default(EffectiveFlowDirection))
{
var isLtr = flowDirection.IsLeftToRight();
switch (alignment)
{
case TextAlignment.Center:
return Windows.UI.Xaml.TextAlignment.Center;
case TextAlignment.End:
return Windows.UI.Xaml.TextAlignment.Right;
if (isLtr)
return Windows.UI.Xaml.TextAlignment.Right;
else
return Windows.UI.Xaml.TextAlignment.Left;
default:
return Windows.UI.Xaml.TextAlignment.Left;
if (isLtr)
return Windows.UI.Xaml.TextAlignment.Left;
else
return Windows.UI.Xaml.TextAlignment.Right;
}
}

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

@ -142,6 +142,8 @@ namespace Xamarin.Forms.Platform.WinRT
{
SetupContextMenu();
}
else if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
UpdateFlowDirection(Cell);
}
void OnClick(object sender, PointerRoutedEventArgs e)
@ -263,6 +265,7 @@ namespace Xamarin.Forms.Platform.WinRT
newCell.SendAppearing();
UpdateContent(newCell);
UpdateFlowDirection(newCell);
SetupContextMenu();
newCell.PropertyChanged += _propertyChangedHandler;
@ -316,5 +319,13 @@ namespace Xamarin.Forms.Platform.WinRT
((FrameworkElement)Content).DataContext = newCell;
}
void UpdateFlowDirection(Cell newCell)
{
if (newCell is ViewCell)
return;
this.UpdateFlowDirection(newCell.Parent as VisualElement);
}
}
}

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

@ -43,6 +43,7 @@ namespace Xamarin.Forms.Platform.WinRT
UpdateMinimumDate();
UpdateMaximumDate();
UpdateDate(e.NewElement.Date);
UpdateFlowDirection();
}
base.OnElementChanged(e);
@ -68,6 +69,8 @@ namespace Xamarin.Forms.Platform.WinRT
UpdateMinimumDate();
else if (e.PropertyName == DatePicker.TextColorProperty.PropertyName)
UpdateTextColor();
else if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
UpdateFlowDirection();
}
protected override bool PreventGestureBubbling { get; set; } = true;
@ -87,6 +90,11 @@ namespace Xamarin.Forms.Platform.WinRT
Control.Date = date;
}
void UpdateFlowDirection()
{
Control.UpdateFlowDirection(Element);
}
void UpdateMaximumDate()
{
DateTime maxdate = Element.MaximumDate;

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

@ -44,6 +44,8 @@ namespace Xamarin.Forms.Platform.WinRT
UpdateInputScope();
UpdateTextColor();
UpdateFont();
UpdateTextAlignment();
UpdateFlowDirection();
}
base.OnElementChanged(e);
@ -84,6 +86,11 @@ namespace Xamarin.Forms.Platform.WinRT
{
UpdateText();
}
else if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
{
UpdateTextAlignment();
UpdateFlowDirection();
}
}
void OnLostFocus(object sender, RoutedEventArgs e)
@ -174,6 +181,11 @@ namespace Xamarin.Forms.Platform.WinRT
Control.SelectionStart = Control.Text.Length;
}
void UpdateTextAlignment()
{
Control.UpdateTextAlignment(Element);
}
void UpdateTextColor()
{
Color textColor = Element.TextColor;
@ -184,5 +196,10 @@ namespace Xamarin.Forms.Platform.WinRT
BrushHelpers.UpdateColor(textColor, ref _defaultTextColorFocusBrush,
() => Control.ForegroundFocusBrush, brush => Control.ForegroundFocusBrush = brush);
}
void UpdateFlowDirection()
{
Control.UpdateFlowDirection(Element);
}
}
}

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

@ -87,6 +87,8 @@ namespace Xamarin.Forms.Platform.WinRT
UpdateAlignment();
else if (e.PropertyName == Entry.PlaceholderColorProperty.PropertyName)
UpdatePlaceholderColor();
else if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
UpdateAlignment();
}
protected override void UpdateBackgroundColor()
@ -126,7 +128,7 @@ namespace Xamarin.Forms.Platform.WinRT
void UpdateAlignment()
{
Control.TextAlignment = Element.HorizontalTextAlignment.ToNativeTextAlignment();
Control.TextAlignment = Element.HorizontalTextAlignment.ToNativeTextAlignment(((IVisualElementController)Element).EffectiveFlowDirection);
}
void UpdateFont()

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

@ -0,0 +1,37 @@
using Windows.UI.Xaml;
using WFlowDirection = Windows.UI.Xaml.FlowDirection;
using WTextAlignment = Windows.UI.Xaml.TextAlignment;
using Xamarin.Forms.Internals;
using Windows.UI.Xaml.Controls;
#if WINDOWS_UWP
namespace Xamarin.Forms.Platform.UWP
#else
namespace Xamarin.Forms.Platform.WinRT
#endif
{
internal static class FlowDirectionExtensions
{
internal static void UpdateFlowDirection(this FrameworkElement control, IVisualElementController controller)
{
if (controller == null || control == null)
return;
if (controller.EffectiveFlowDirection.IsRightToLeft())
control.FlowDirection = WFlowDirection.RightToLeft;
else if (controller.EffectiveFlowDirection.IsLeftToRight())
control.FlowDirection = WFlowDirection.LeftToRight;
}
internal static void UpdateTextAlignment(this TextBox control, IVisualElementController controller)
{
if (controller == null || control == null)
return;
if (controller.EffectiveFlowDirection.IsRightToLeft())
control.TextAlignment = WTextAlignment.Right;
else if (controller.EffectiveFlowDirection.IsLeftToRight())
control.TextAlignment = WTextAlignment.Left;
}
}
}

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

@ -147,6 +147,8 @@ namespace Xamarin.Forms.Platform.WinRT
UpdateFont(Control);
else if (e.PropertyName == Label.LineBreakModeProperty.PropertyName)
UpdateLineBreakMode(Control);
else if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
UpdateAlign(Control);
base.OnElementPropertyChanged(sender, e);
}
@ -162,7 +164,7 @@ namespace Xamarin.Forms.Platform.WinRT
if (label == null)
return;
textBlock.TextAlignment = label.HorizontalTextAlignment.ToNativeTextAlignment();
textBlock.TextAlignment = label.HorizontalTextAlignment.ToNativeTextAlignment(((IVisualElementController)Element).EffectiveFlowDirection);
textBlock.VerticalAlignment = label.VerticalTextAlignment.ToNativeVerticalAlignment();
}

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

@ -41,6 +41,7 @@ namespace Xamarin.Forms.Platform.WinRT
}
Control.Value = e.NewElement.Progress;
UpdateFlowDirection();
}
}
@ -50,11 +51,18 @@ namespace Xamarin.Forms.Platform.WinRT
if (e.PropertyName == ProgressBar.ProgressProperty.PropertyName)
Control.Value = Element.Progress;
else if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
UpdateFlowDirection();
}
void ProgressBarOnValueChanged(object sender, RangeBaseValueChangedEventArgs rangeBaseValueChangedEventArgs)
{
((IVisualElementController)Element)?.InvalidateMeasure(InvalidationTrigger.MeasureChanged);
}
void UpdateFlowDirection()
{
Control.UpdateFlowDirection(Element);
}
}
}

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

@ -53,6 +53,7 @@ namespace Xamarin.Forms.Platform.WinRT
double stepping = Math.Min((e.NewElement.Maximum - e.NewElement.Minimum) / 10, 1);
Control.StepFrequency = stepping;
Control.SmallChange = stepping;
UpdateFlowDirection();
}
}
@ -69,6 +70,8 @@ namespace Xamarin.Forms.Platform.WinRT
if (Control.Value != Element.Value)
Control.Value = Element.Value;
}
else if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
UpdateFlowDirection();
}
protected override void UpdateBackgroundColor()
@ -87,6 +90,11 @@ namespace Xamarin.Forms.Platform.WinRT
}
}
void UpdateFlowDirection()
{
Control.UpdateFlowDirection(Element);
}
protected override bool PreventGestureBubbling { get; set; } = true;
void OnNativeValueChanged(object sender, RangeBaseValueChangedEventArgs e)

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

@ -26,7 +26,8 @@ namespace Xamarin.Forms.Platform.WinRT
UpdateMaximum();
UpdateMinimum();
UpdateValue();
UpdateIncrement();
UpdateIncrement();
UpdateFlowDirection();
}
}
@ -44,6 +45,8 @@ namespace Xamarin.Forms.Platform.WinRT
UpdateIncrement();
else if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName)
UpdateBackgroundColor();
else if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
UpdateFlowDirection();
}
protected override void UpdateBackgroundColor()
@ -59,6 +62,11 @@ namespace Xamarin.Forms.Platform.WinRT
Element.SetValueCore(Stepper.ValueProperty, Control.Value);
}
void UpdateFlowDirection()
{
Control.UpdateFlowDirection(Element);
}
void UpdateIncrement()
{
Control.Increment = Element.Increment;

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

@ -29,6 +29,8 @@ namespace Xamarin.Forms.Platform.WinRT
}
Control.IsOn = Element.IsToggled;
UpdateFlowDirection();
}
}
@ -40,6 +42,10 @@ namespace Xamarin.Forms.Platform.WinRT
{
Control.IsOn = Element.IsToggled;
}
else if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
{
UpdateFlowDirection();
}
}
protected override bool PreventGestureBubbling { get; set; } = true;
@ -48,5 +54,10 @@ namespace Xamarin.Forms.Platform.WinRT
{
((IElementController)Element).SetValueFromRenderer(Switch.IsToggledProperty, Control.IsOn);
}
void UpdateFlowDirection()
{
Control.UpdateFlowDirection(Element);
}
}
}

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

@ -43,6 +43,7 @@ namespace Xamarin.Forms.Platform.WinRT
}
UpdateTime();
UpdateFlowDirection();
}
}
@ -63,6 +64,9 @@ namespace Xamarin.Forms.Platform.WinRT
if (e.PropertyName == TimePicker.TextColorProperty.PropertyName)
UpdateTextColor();
if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
UpdateFlowDirection();
}
protected override bool PreventGestureBubbling { get; set; } = true;
@ -73,6 +77,11 @@ namespace Xamarin.Forms.Platform.WinRT
((IVisualElementController)Element)?.InvalidateMeasure(InvalidationTrigger.SizeRequestChanged);
}
void UpdateFlowDirection()
{
Control.UpdateFlowDirection(Element);
}
void UpdateTime()
{
Control.Time = Element.Time;

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

@ -1,5 +1,4 @@
using Windows.UI.Xaml;
using Windows.UI.Xaml.Automation;
using Windows.UI.Xaml.Automation.Peers;
#if WINDOWS_UWP
@ -24,6 +23,7 @@ namespace Xamarin.Forms.Platform.WinRT
if (e.NewElement != null)
{
UpdateBackgroundColor();
UpdateFlowDirection();
}
}
@ -131,5 +131,10 @@ namespace Xamarin.Forms.Platform.WinRT
else
Control.SetValue(Windows.UI.Xaml.Automation.AutomationProperties.LabeledByProperty, _defaultAutomationPropertiesLabeledBy);
}
void UpdateFlowDirection()
{
Control.UpdateFlowDirection(Element);
}
}
}

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

@ -66,10 +66,10 @@
<Compile Include="..\Xamarin.Forms.Core\Properties\GlobalAssemblyInfo.cs">
<Link>Properties\GlobalAssemblyInfo.cs</Link>
</Compile>
<Compile Include="FlowDirectionExtensions.cs" />
</ItemGroup>
<ItemGroup Condition=" '$(OS)' != 'Unix' ">
<Compile Include="PageControl.cs" />
<Compile Include="PageControl.cs" />
<Compile Include="NavigationPageRendererWinRT.cs" />
<Compile Include="PlatformWinRT.cs" />
<Compile Include="BrushHelpers.cs" />

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

@ -63,6 +63,8 @@ namespace Xamarin.Forms.Platform.iOS
UpdateHorizontalTextAlignment(realCell, entryCell);
else if (e.PropertyName == Cell.IsEnabledProperty.PropertyName)
UpdateIsEnabled(realCell, entryCell);
else if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
UpdateHorizontalTextAlignment(realCell, entryCell);
}
static void OnKeyBoardDoneButtonPressed(object sender, EventArgs e)
@ -83,7 +85,8 @@ namespace Xamarin.Forms.Platform.iOS
static void UpdateHorizontalTextAlignment(EntryCellTableViewCell cell, EntryCell entryCell)
{
cell.TextField.TextAlignment = entryCell.HorizontalTextAlignment.ToNativeTextAlignment();
IViewController viewController = entryCell.Parent as View;
cell.TextField.TextAlignment = entryCell.HorizontalTextAlignment.ToNativeTextAlignment(viewController?.EffectiveFlowDirection ?? default(EffectiveFlowDirection));
}
static void UpdateIsEnabled(EntryCellTableViewCell cell, EntryCell entryCell)

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

@ -43,6 +43,7 @@ namespace Xamarin.Forms.Platform.iOS
UpdateBackground(tvc, item);
UpdateIsEnabled(tvc, boolCell);
UpdateFlowDirection(tvc, boolCell);
return tvc;
}
@ -58,6 +59,8 @@ namespace Xamarin.Forms.Platform.iOS
realCell.TextLabel.Text = boolCell.Text;
else if (e.PropertyName == Cell.IsEnabledProperty.PropertyName)
UpdateIsEnabled(realCell, boolCell);
else if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
UpdateFlowDirection(realCell, boolCell);
}
void OnSwitchValueChanged(object sender, EventArgs eventArgs)
@ -76,6 +79,15 @@ namespace Xamarin.Forms.Platform.iOS
((SwitchCell)realCell.Cell).On = sw.On;
}
void UpdateFlowDirection(CellTableViewCell cell, SwitchCell switchCell)
{
IVisualElementController controller = switchCell.Parent as View;
var uiSwitch = cell.AccessoryView as UISwitch;
uiSwitch.UpdateFlowDirection(controller);
}
void UpdateIsEnabled(CellTableViewCell cell, SwitchCell switchCell)
{
cell.UserInteractionEnabled = switchCell.IsEnabled;

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

@ -0,0 +1,61 @@
using UIKit;
using Xamarin.Forms.Internals;
namespace Xamarin.Forms.Platform.iOS
{
internal static class FlowDirectionExtensions
{
internal static FlowDirection ToFlowDirection(this UIUserInterfaceLayoutDirection direction)
{
switch (direction)
{
case UIUserInterfaceLayoutDirection.LeftToRight:
return FlowDirection.LeftToRight;
case UIUserInterfaceLayoutDirection.RightToLeft:
return FlowDirection.RightToLeft;
default:
return FlowDirection.MatchParent;
}
}
internal static void UpdateFlowDirection(this UIView view, IVisualElementController controller)
{
if (controller == null || view == null)
return;
if (controller.EffectiveFlowDirection.IsRightToLeft())
view.SemanticContentAttribute = UISemanticContentAttribute.ForceRightToLeft;
else if (controller.EffectiveFlowDirection.IsLeftToRight())
view.SemanticContentAttribute = UISemanticContentAttribute.ForceLeftToRight;
}
internal static void UpdateTextAlignment(this UITextField control, IVisualElementController controller)
{
if (controller == null || control == null)
return;
if (controller.EffectiveFlowDirection.IsRightToLeft())
{
control.HorizontalAlignment = UIControlContentHorizontalAlignment.Right;
control.TextAlignment = UITextAlignment.Right;
}
else if (controller.EffectiveFlowDirection.IsLeftToRight())
{
control.HorizontalAlignment = UIControlContentHorizontalAlignment.Left;
control.TextAlignment = UITextAlignment.Left;
}
}
internal static void UpdateTextAlignment(this UITextView control, IVisualElementController controller)
{
if (controller == null || control == null)
return;
if (controller.EffectiveFlowDirection.IsRightToLeft())
control.TextAlignment = UITextAlignment.Right;
else if (controller.EffectiveFlowDirection.IsLeftToRight())
control.TextAlignment = UITextAlignment.Left;
}
}
}

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

@ -101,8 +101,10 @@ namespace Xamarin.Forms
#if __MOBILE__
Device.SetIdiom(UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Pad ? TargetIdiom.Tablet : TargetIdiom.Phone);
Device.SetFlowDirection(UIApplication.SharedApplication.UserInterfaceLayoutDirection.ToFlowDirection());
#else
Device.SetIdiom(TargetIdiom.Desktop);
Device.SetFlowDirection(NSApplication.SharedApplication.UserInterfaceLayoutDirection.ToFlowDirection());
#endif
Device.SetFlags(s_flags);
Device.PlatformServices = new IOSPlatformServices();

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

@ -1,19 +1,27 @@
using UIKit;
using Xamarin.Forms.Internals;
namespace Xamarin.Forms.Platform.iOS
{
internal static class AlignmentExtensions
{
internal static UITextAlignment ToNativeTextAlignment(this TextAlignment alignment)
internal static UITextAlignment ToNativeTextAlignment(this TextAlignment alignment, EffectiveFlowDirection flowDirection)
{
var isLtr = flowDirection.IsLeftToRight();
switch (alignment)
{
case TextAlignment.Center:
return UITextAlignment.Center;
case TextAlignment.End:
return UITextAlignment.Right;
if (isLtr)
return UITextAlignment.Right;
else
return UITextAlignment.Left;
default:
return UITextAlignment.Left;
if (isLtr)
return UITextAlignment.Left;
else
return UITextAlignment.Natural;
}
}
}

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

@ -63,6 +63,7 @@ namespace Xamarin.Forms.Platform.iOS
UpdateMaximumDate();
UpdateMinimumDate();
UpdateTextColor();
UpdateFlowDirection();
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
@ -77,6 +78,8 @@ namespace Xamarin.Forms.Platform.iOS
UpdateMaximumDate();
else if (e.PropertyName == DatePicker.TextColorProperty.PropertyName || e.PropertyName == VisualElement.IsEnabledProperty.PropertyName)
UpdateTextColor();
else if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
UpdateFlowDirection();
}
void HandleValueChanged(object sender, EventArgs e)
@ -102,6 +105,11 @@ namespace Xamarin.Forms.Platform.iOS
Control.Text = Element.Date.ToString(Element.Format);
}
void UpdateFlowDirection()
{
(Control as UITextField).UpdateTextAlignment(Element);
}
void UpdateMaximumDate()
{
_picker.MaximumDate = Element.MaximumDate.ToNSDate();

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

@ -8,9 +8,9 @@ namespace Xamarin.Forms.Platform.iOS
public class EditorRenderer : ViewRenderer<Editor, UITextView>
{
bool _disposed;
IEditorController ElementController => Element;
IEditorController ElementController => Element;
protected override void Dispose(bool disposing)
protected override void Dispose(bool disposing)
{
if (_disposed)
return;
@ -51,7 +51,7 @@ namespace Xamarin.Forms.Platform.iOS
var doneButton = new UIBarButtonItem(UIBarButtonSystemItem.Done, (o, a) =>
{
Control.ResignFirstResponder();
ElementController.SendCompleted();
ElementController.SendCompleted();
});
accessoryView.SetItems(new[] { spacer, doneButton }, false);
Control.InputAccessoryView = accessoryView;
@ -67,6 +67,7 @@ namespace Xamarin.Forms.Platform.iOS
UpdateTextColor();
UpdateKeyboard();
UpdateEditable();
UpdateTextAlignment();
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
@ -87,6 +88,8 @@ namespace Xamarin.Forms.Platform.iOS
UpdateFont();
else if (e.PropertyName == Editor.FontSizeProperty.PropertyName)
UpdateFont();
else if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
UpdateTextAlignment();
}
void HandleChanged(object sender, EventArgs e)
@ -135,6 +138,11 @@ namespace Xamarin.Forms.Platform.iOS
Control.Text = Element.Text;
}
void UpdateTextAlignment()
{
Control.UpdateTextAlignment(Element);
}
void UpdateTextColor()
{
var textColor = Element.TextColor;

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

@ -114,6 +114,8 @@ namespace Xamarin.Forms.Platform.iOS
}
else if (e.PropertyName == PlatformConfiguration.iOSSpecific.Entry.AdjustsFontSizeToFitWidthProperty.PropertyName)
UpdateAdjustsFontSizeToFitWidth();
else if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
UpdateAlignment();
base.OnElementPropertyChanged(sender, e);
}
@ -148,7 +150,7 @@ namespace Xamarin.Forms.Platform.iOS
void UpdateAlignment()
{
Control.TextAlignment = Element.HorizontalTextAlignment.ToNativeTextAlignment();
Control.TextAlignment = Element.HorizontalTextAlignment.ToNativeTextAlignment(((IVisualElementController)Element).EffectiveFlowDirection);
}
void UpdateColor()

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

@ -152,6 +152,8 @@ namespace Xamarin.Forms.Platform.MacOS
UpdateText();
else if (e.PropertyName == Label.LineBreakModeProperty.PropertyName)
UpdateLineBreakMode();
else if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
UpdateAlignment();
}
#if __MOBILE__
@ -190,9 +192,9 @@ namespace Xamarin.Forms.Platform.MacOS
void UpdateAlignment()
{
#if __MOBILE__
Control.TextAlignment = Element.HorizontalTextAlignment.ToNativeTextAlignment();
Control.TextAlignment = Element.HorizontalTextAlignment.ToNativeTextAlignment(((IVisualElementController)Element).EffectiveFlowDirection);
#else
Control.Alignment = Element.HorizontalTextAlignment.ToNativeTextAlignment();
Control.Alignment = Element.HorizontalTextAlignment.ToNativeTextAlignment(((IVisualElementController)Element).EffectiveFlowDirection);
#endif
}

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

@ -2,6 +2,7 @@ using System;
using System.ComponentModel;
using System.Linq;
using UIKit;
using Xamarin.Forms.Internals;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
using PointF = CoreGraphics.CGPoint;
@ -25,6 +26,7 @@ namespace Xamarin.Forms.Platform.iOS
VisualElementTracker _tracker;
Page Page => Element as Page;
public PhoneMasterDetailRenderer()
{
@ -241,12 +243,23 @@ namespace Xamarin.Forms.Platform.iOS
var masterFrame = frame;
masterFrame.Width = (int)(Math.Min(masterFrame.Width, masterFrame.Height) * 0.8);
var isRTL = (Element as IVisualElementController)?.EffectiveFlowDirection.IsRightToLeft() == true;
if (isRTL)
{
masterFrame.X = (int)(masterFrame.Width * .25);
}
_masterController.View.Frame = masterFrame;
var target = frame;
if (Presented)
target.X += masterFrame.Width;
if (isRTL)
{
target.X = target.X * -1;
}
if (animated)
{
UIView.BeginAnimations("Flyout");
@ -259,7 +272,7 @@ namespace Xamarin.Forms.Platform.iOS
else
_detailController.View.Frame = target;
MasterDetailPage.MasterBounds = new Rectangle(0, 0, masterFrame.Width, masterFrame.Height);
MasterDetailPage.MasterBounds = new Rectangle(masterFrame.X, 0, masterFrame.Width, masterFrame.Height);
MasterDetailPage.DetailBounds = new Rectangle(0, 0, frame.Width, frame.Height);
if (Presented)
@ -341,7 +354,7 @@ namespace Xamarin.Forms.Platform.iOS
else
return base.ChildViewControllerForStatusBarHidden();
}
void UpdatePanGesture()
{
var model = (MasterDetailPage)Element;
@ -358,10 +371,14 @@ namespace Xamarin.Forms.Platform.iOS
return;
}
UITouchEventArgs shouldRecieve = (g, t) => !(t.View is UISlider);
UITouchEventArgs shouldReceive = (g, t) => !(t.View is UISlider);
var center = new PointF();
_panGesture = new UIPanGestureRecognizer(g =>
{
var isRTL = (Element as IVisualElementController)?.EffectiveFlowDirection.IsRightToLeft() == true;
int directionModifier = isRTL ? -1 : 1;
switch (g.State)
{
case UIGestureRecognizerState.Began:
@ -370,12 +387,18 @@ namespace Xamarin.Forms.Platform.iOS
case UIGestureRecognizerState.Changed:
var currentPosition = g.LocationInView(g.View);
var motion = currentPosition.X - center.X;
motion = motion * directionModifier;
var detailView = _detailController.View;
var targetFrame = detailView.Frame;
if (Presented)
targetFrame.X = (nfloat)Math.Max(0, _masterController.View.Frame.Width + Math.Min(0, motion));
else
targetFrame.X = (nfloat)Math.Min(_masterController.View.Frame.Width, Math.Max(0, motion));
targetFrame.X = targetFrame.X * directionModifier;
detailView.Frame = targetFrame;
break;
case UIGestureRecognizerState.Ended:
@ -383,14 +406,14 @@ namespace Xamarin.Forms.Platform.iOS
var masterFrame = _masterController.View.Frame;
if (Presented)
{
if (detailFrame.X < masterFrame.Width * .75)
if (detailFrame.X * directionModifier < masterFrame.Width * .75)
Presented = false;
else
LayoutChildren(true);
}
else
{
if (detailFrame.X > masterFrame.Width * .25)
if (detailFrame.X * directionModifier > masterFrame.Width * .25)
Presented = true;
else
LayoutChildren(true);
@ -398,7 +421,7 @@ namespace Xamarin.Forms.Platform.iOS
break;
}
});
_panGesture.ShouldReceiveTouch = shouldRecieve;
_panGesture.ShouldReceiveTouch = shouldReceive;
_panGesture.MaximumNumberOfTouches = 2;
View.AddGestureRecognizer(_panGesture);
}

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

@ -96,6 +96,8 @@ namespace Xamarin.Forms.Platform.iOS
UpdateFont();
else if (e.PropertyName == SearchBar.HorizontalTextAlignmentProperty.PropertyName)
UpdateAlignment();
else if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
UpdateAlignment();
}
protected override void SetBackgroundColor(Color color)
@ -161,7 +163,7 @@ namespace Xamarin.Forms.Platform.iOS
if (_textField == null)
return;
_textField.TextAlignment = Element.HorizontalTextAlignment.ToNativeTextAlignment();
_textField.TextAlignment = Element.HorizontalTextAlignment.ToNativeTextAlignment(((IVisualElementController)Element).EffectiveFlowDirection);
}
void UpdateCancelButton()

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

@ -1,7 +1,6 @@
using System;
using System.ComponentModel;
using UIKit;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
namespace Xamarin.Forms.Platform.iOS
{
@ -46,7 +45,7 @@ namespace Xamarin.Forms.Platform.iOS
bool _disposed;
EventTracker _events;
InnerDelegate _innerDelegate;
nfloat _masterWidth = 0;
EventedViewController _masterController;
MasterDetailPage _masterDetailPage;
@ -184,8 +183,10 @@ namespace Xamarin.Forms.Platform.iOS
var detailsBounds = _detailController.View.Frame;
var masterBounds = _masterController.View.Frame;
_masterWidth = (nfloat)Math.Max(_masterWidth, masterBounds.Width);
if (!masterBounds.IsEmpty)
MasterDetailPage.MasterBounds = new Rectangle(0, 0, masterBounds.Width, masterBounds.Height);
MasterDetailPage.MasterBounds = new Rectangle(_masterWidth, 0, _masterWidth, masterBounds.Height);
if (!detailsBounds.IsEmpty)
MasterDetailPage.DetailBounds = new Rectangle(0, 0, detailsBounds.Width, detailsBounds.Height);
@ -195,6 +196,7 @@ namespace Xamarin.Forms.Platform.iOS
{
base.ViewDidLoad();
UpdateBackground();
UpdateFlowDirection();
_tracker = new VisualElementTracker(this);
_events = new EventTracker(this);
_events.LoadEvents(NativeView);
@ -249,6 +251,8 @@ namespace Xamarin.Forms.Platform.iOS
var changed = ElementChanged;
if (changed != null)
changed(this, e);
_masterWidth = 0;
}
void ClearControllers()
@ -283,6 +287,8 @@ namespace Xamarin.Forms.Platform.iOS
ToggleMaster();
else if (e.PropertyName == Xamarin.Forms.MasterDetailPage.IsGestureEnabledProperty.PropertyName)
base.PresentsWithGesture = this.MasterDetailPage.IsGestureEnabled;
else if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
UpdateFlowDirection();
MessagingCenter.Send<IVisualElementRenderer>(this, NavigationRenderer.UpdateToolbarButtons);
}
@ -346,6 +352,11 @@ namespace Xamarin.Forms.Platform.iOS
_detailController.AddChildViewController(detail);
}
void UpdateFlowDirection()
{
NativeView.UpdateFlowDirection(Element);
}
class InnerDelegate : UISplitViewControllerDelegate
{
readonly MasterBehavior _masterPresentedDefaultState;

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

@ -75,6 +75,7 @@ namespace Xamarin.Forms.Platform.iOS
UpdateTime();
UpdateTextColor();
UpdateFlowDirection();
}
base.OnElementChanged(e);
@ -89,6 +90,9 @@ namespace Xamarin.Forms.Platform.iOS
if (e.PropertyName == TimePicker.TextColorProperty.PropertyName || e.PropertyName == VisualElement.IsEnabledProperty.PropertyName)
UpdateTextColor();
if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
UpdateFlowDirection();
}
void OnEnded(object sender, EventArgs eventArgs)
@ -106,6 +110,11 @@ namespace Xamarin.Forms.Platform.iOS
ElementController.SetValueFromRenderer(TimePicker.TimeProperty, _picker.Date.ToDateTime() - new DateTime(1, 1, 1));
}
void UpdateFlowDirection()
{
(Control as UITextField).UpdateTextAlignment(Element);
}
void UpdateTextColor()
{
var textColor = Element.TextColor;

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

@ -5,10 +5,9 @@ using RectangleF = CoreGraphics.CGRect;
using SizeF = CoreGraphics.CGSize;
#if __MOBILE__
using UIKit;
using NativeView = UIKit.UIView;
using NativeColor = UIKit.UIColor;
using NativeControl = UIKit.UIControl;
using NativeView = UIKit.UIView;
namespace Xamarin.Forms.Platform.iOS
#else
@ -96,6 +95,7 @@ namespace Xamarin.Forms.Platform.MacOS
}
UpdateIsEnabled();
UpdateFlowDirection();
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
@ -106,6 +106,8 @@ namespace Xamarin.Forms.Platform.MacOS
UpdateIsEnabled();
else if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName)
SetBackgroundColor(Element.BackgroundColor);
else if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
UpdateFlowDirection();
}
base.OnElementPropertyChanged(sender, e);
@ -209,6 +211,8 @@ namespace Xamarin.Forms.Platform.MacOS
UpdateIsEnabled();
UpdateFlowDirection();
AddSubview(uiview);
}
@ -230,6 +234,11 @@ namespace Xamarin.Forms.Platform.MacOS
uiControl.Enabled = Element.IsEnabled;
}
void UpdateFlowDirection()
{
Control.UpdateFlowDirection(Element);
}
void ViewOnFocusChangeRequested(object sender, VisualElement.FocusRequestArgs focusRequestArgs)
{
if (Control == null)

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

@ -120,6 +120,7 @@
<Compile Include="ExportImageSourceHandlerAttribute.cs" />
<Compile Include="ExportRendererAttribute.cs" />
<Compile Include="Extensions\ArrayExtensions.cs" />
<Compile Include="Extensions\FlowDirectionExtensions.cs" />
<Compile Include="Extensions\PlatformConfigurationExtensions.cs" />
<Compile Include="Flags.cs" />
<Compile Include="NativeViewWrapper.cs" />
@ -195,4 +196,4 @@
<Link>Properties\GlobalAssemblyInfo.cs</Link>
</Compile>
</ItemGroup>
</Project>
</Project>

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

@ -92,6 +92,22 @@ Device.BeginInvokeOnMainThread (() => {
<remarks>To be added.</remarks>
</Docs>
</Member>
<Member MemberName="FlowDirection">
<MemberSignature Language="C#" Value="public static Xamarin.Forms.FlowDirection FlowDirection { get; }" />
<MemberSignature Language="ILAsm" Value=".property valuetype Xamarin.Forms.FlowDirection FlowDirection" />
<MemberType>Property</MemberType>
<AssemblyInfo>
<AssemblyVersion>2.0.0.0</AssemblyVersion>
</AssemblyInfo>
<ReturnValue>
<ReturnType>Xamarin.Forms.FlowDirection</ReturnType>
</ReturnValue>
<Docs>
<summary>To be added.</summary>
<value>To be added.</value>
<remarks>To be added.</remarks>
</Docs>
</Member>
<Member MemberName="GetAssemblies">
<MemberSignature Language="C#" Value="public static System.Reflection.Assembly[] GetAssemblies ();" />
<MemberSignature Language="ILAsm" Value=".method public static hidebysig class System.Reflection.Assembly[] GetAssemblies() cil managed" />
@ -513,6 +529,30 @@ button.HeightRequest = Device.OnPlatform (20,30,30);
<remarks>To be added.</remarks>
</Docs>
</Member>
<Member MemberName="SetFlowDirection">
<MemberSignature Language="C#" Value="public static void SetFlowDirection (Xamarin.Forms.FlowDirection value);" />
<MemberSignature Language="ILAsm" Value=".method public static hidebysig void SetFlowDirection(valuetype Xamarin.Forms.FlowDirection value) cil managed" />
<MemberType>Method</MemberType>
<AssemblyInfo>
<AssemblyVersion>2.0.0.0</AssemblyVersion>
</AssemblyInfo>
<Attributes>
<Attribute>
<AttributeName>System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)</AttributeName>
</Attribute>
</Attributes>
<ReturnValue>
<ReturnType>System.Void</ReturnType>
</ReturnValue>
<Parameters>
<Parameter Name="value" Type="Xamarin.Forms.FlowDirection" />
</Parameters>
<Docs>
<param name="value">To be added.</param>
<summary>To be added.</summary>
<remarks>To be added.</remarks>
</Docs>
</Member>
<Member MemberName="SetIdiom">
<MemberSignature Language="C#" Value="public static void SetIdiom (Xamarin.Forms.TargetIdiom value);" />
<MemberSignature Language="ILAsm" Value=".method public static hidebysig void SetIdiom(valuetype Xamarin.Forms.TargetIdiom value) cil managed" />

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

@ -0,0 +1,50 @@
<Type Name="EffectiveFlowDirection" FullName="Xamarin.Forms.EffectiveFlowDirection">
<TypeSignature Language="C#" Value="public enum EffectiveFlowDirection" />
<TypeSignature Language="ILAsm" Value=".class public auto ansi sealed EffectiveFlowDirection extends System.Enum" />
<AssemblyInfo>
<AssemblyName>Xamarin.Forms.Core</AssemblyName>
<AssemblyVersion>2.0.0.0</AssemblyVersion>
</AssemblyInfo>
<Base>
<BaseTypeName>System.Enum</BaseTypeName>
</Base>
<Attributes>
<Attribute>
<AttributeName>System.Flags</AttributeName>
</Attribute>
</Attributes>
<Docs>
<summary>To be added.</summary>
<remarks>To be added.</remarks>
</Docs>
<Members>
<Member MemberName="Explicit">
<MemberSignature Language="C#" Value="Explicit" />
<MemberSignature Language="ILAsm" Value=".field public static literal valuetype Xamarin.Forms.EffectiveFlowDirection Explicit = int32(2)" />
<MemberType>Field</MemberType>
<AssemblyInfo>
<AssemblyVersion>2.0.0.0</AssemblyVersion>
</AssemblyInfo>
<ReturnValue>
<ReturnType>Xamarin.Forms.EffectiveFlowDirection</ReturnType>
</ReturnValue>
<Docs>
<summary>To be added.</summary>
</Docs>
</Member>
<Member MemberName="RightToLeft">
<MemberSignature Language="C#" Value="RightToLeft" />
<MemberSignature Language="ILAsm" Value=".field public static literal valuetype Xamarin.Forms.EffectiveFlowDirection RightToLeft = int32(1)" />
<MemberType>Field</MemberType>
<AssemblyInfo>
<AssemblyVersion>2.0.0.0</AssemblyVersion>
</AssemblyInfo>
<ReturnValue>
<ReturnType>Xamarin.Forms.EffectiveFlowDirection</ReturnType>
</ReturnValue>
<Docs>
<summary>To be added.</summary>
</Docs>
</Member>
</Members>
</Type>

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

@ -0,0 +1,103 @@
<Type Name="EffectiveFlowDirectionExtensions" FullName="Xamarin.Forms.EffectiveFlowDirectionExtensions">
<TypeSignature Language="C#" Value="public static class EffectiveFlowDirectionExtensions" />
<TypeSignature Language="ILAsm" Value=".class public auto ansi abstract sealed beforefieldinit EffectiveFlowDirectionExtensions extends System.Object" />
<AssemblyInfo>
<AssemblyName>Xamarin.Forms.Core</AssemblyName>
<AssemblyVersion>2.0.0.0</AssemblyVersion>
</AssemblyInfo>
<Base>
<BaseTypeName>System.Object</BaseTypeName>
</Base>
<Interfaces />
<Attributes>
<Attribute>
<AttributeName>System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)</AttributeName>
</Attribute>
</Attributes>
<Docs>
<summary>To be added.</summary>
<remarks>To be added.</remarks>
</Docs>
<Members>
<Member MemberName="IsExplicit">
<MemberSignature Language="C#" Value="public static bool IsExplicit (this Xamarin.Forms.EffectiveFlowDirection self);" />
<MemberSignature Language="ILAsm" Value=".method public static hidebysig bool IsExplicit(valuetype Xamarin.Forms.EffectiveFlowDirection self) cil managed" />
<MemberType>Method</MemberType>
<AssemblyInfo>
<AssemblyVersion>2.0.0.0</AssemblyVersion>
</AssemblyInfo>
<ReturnValue>
<ReturnType>System.Boolean</ReturnType>
</ReturnValue>
<Parameters>
<Parameter Name="self" Type="Xamarin.Forms.EffectiveFlowDirection" RefType="this" />
</Parameters>
<Docs>
<param name="self">To be added.</param>
<summary>To be added.</summary>
<returns>To be added.</returns>
<remarks>To be added.</remarks>
</Docs>
</Member>
<Member MemberName="IsImplicit">
<MemberSignature Language="C#" Value="public static bool IsImplicit (this Xamarin.Forms.EffectiveFlowDirection self);" />
<MemberSignature Language="ILAsm" Value=".method public static hidebysig bool IsImplicit(valuetype Xamarin.Forms.EffectiveFlowDirection self) cil managed" />
<MemberType>Method</MemberType>
<AssemblyInfo>
<AssemblyVersion>2.0.0.0</AssemblyVersion>
</AssemblyInfo>
<ReturnValue>
<ReturnType>System.Boolean</ReturnType>
</ReturnValue>
<Parameters>
<Parameter Name="self" Type="Xamarin.Forms.EffectiveFlowDirection" RefType="this" />
</Parameters>
<Docs>
<param name="self">To be added.</param>
<summary>To be added.</summary>
<returns>To be added.</returns>
<remarks>To be added.</remarks>
</Docs>
</Member>
<Member MemberName="IsLeftToRight">
<MemberSignature Language="C#" Value="public static bool IsLeftToRight (this Xamarin.Forms.EffectiveFlowDirection self);" />
<MemberSignature Language="ILAsm" Value=".method public static hidebysig bool IsLeftToRight(valuetype Xamarin.Forms.EffectiveFlowDirection self) cil managed" />
<MemberType>Method</MemberType>
<AssemblyInfo>
<AssemblyVersion>2.0.0.0</AssemblyVersion>
</AssemblyInfo>
<ReturnValue>
<ReturnType>System.Boolean</ReturnType>
</ReturnValue>
<Parameters>
<Parameter Name="self" Type="Xamarin.Forms.EffectiveFlowDirection" RefType="this" />
</Parameters>
<Docs>
<param name="self">To be added.</param>
<summary>To be added.</summary>
<returns>To be added.</returns>
<remarks>To be added.</remarks>
</Docs>
</Member>
<Member MemberName="IsRightToLeft">
<MemberSignature Language="C#" Value="public static bool IsRightToLeft (this Xamarin.Forms.EffectiveFlowDirection self);" />
<MemberSignature Language="ILAsm" Value=".method public static hidebysig bool IsRightToLeft(valuetype Xamarin.Forms.EffectiveFlowDirection self) cil managed" />
<MemberType>Method</MemberType>
<AssemblyInfo>
<AssemblyVersion>2.0.0.0</AssemblyVersion>
</AssemblyInfo>
<ReturnValue>
<ReturnType>System.Boolean</ReturnType>
</ReturnValue>
<Parameters>
<Parameter Name="self" Type="Xamarin.Forms.EffectiveFlowDirection" RefType="this" />
</Parameters>
<Docs>
<param name="self">To be added.</param>
<summary>To be added.</summary>
<returns>To be added.</returns>
<remarks>To be added.</remarks>
</Docs>
</Member>
</Members>
</Type>

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

@ -0,0 +1,59 @@
<Type Name="FlowDirection" FullName="Xamarin.Forms.FlowDirection">
<TypeSignature Language="C#" Value="public enum FlowDirection" />
<TypeSignature Language="ILAsm" Value=".class public auto ansi sealed FlowDirection extends System.Enum" />
<AssemblyInfo>
<AssemblyName>Xamarin.Forms.Core</AssemblyName>
<AssemblyVersion>2.0.0.0</AssemblyVersion>
</AssemblyInfo>
<Base>
<BaseTypeName>System.Enum</BaseTypeName>
</Base>
<Docs>
<summary>To be added.</summary>
<remarks>To be added.</remarks>
</Docs>
<Members>
<Member MemberName="LeftToRight">
<MemberSignature Language="C#" Value="LeftToRight" />
<MemberSignature Language="ILAsm" Value=".field public static literal valuetype Xamarin.Forms.FlowDirection LeftToRight = int32(1)" />
<MemberType>Field</MemberType>
<AssemblyInfo>
<AssemblyVersion>2.0.0.0</AssemblyVersion>
</AssemblyInfo>
<ReturnValue>
<ReturnType>Xamarin.Forms.FlowDirection</ReturnType>
</ReturnValue>
<Docs>
<summary>To be added.</summary>
</Docs>
</Member>
<Member MemberName="MatchParent">
<MemberSignature Language="C#" Value="MatchParent" />
<MemberSignature Language="ILAsm" Value=".field public static literal valuetype Xamarin.Forms.FlowDirection MatchParent = int32(0)" />
<MemberType>Field</MemberType>
<AssemblyInfo>
<AssemblyVersion>2.0.0.0</AssemblyVersion>
</AssemblyInfo>
<ReturnValue>
<ReturnType>Xamarin.Forms.FlowDirection</ReturnType>
</ReturnValue>
<Docs>
<summary>To be added.</summary>
</Docs>
</Member>
<Member MemberName="RightToLeft">
<MemberSignature Language="C#" Value="RightToLeft" />
<MemberSignature Language="ILAsm" Value=".field public static literal valuetype Xamarin.Forms.FlowDirection RightToLeft = int32(2)" />
<MemberType>Field</MemberType>
<AssemblyInfo>
<AssemblyVersion>2.0.0.0</AssemblyVersion>
</AssemblyInfo>
<ReturnValue>
<ReturnType>Xamarin.Forms.FlowDirection</ReturnType>
</ReturnValue>
<Docs>
<summary>To be added.</summary>
</Docs>
</Member>
</Members>
</Type>

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

@ -65,6 +65,22 @@
<remarks>To be added.</remarks>
</Docs>
</Member>
<Member MemberName="EffectiveFlowDirection">
<MemberSignature Language="C#" Value="public Xamarin.Forms.EffectiveFlowDirection EffectiveFlowDirection { get; }" />
<MemberSignature Language="ILAsm" Value=".property instance valuetype Xamarin.Forms.EffectiveFlowDirection EffectiveFlowDirection" />
<MemberType>Property</MemberType>
<AssemblyInfo>
<AssemblyVersion>2.0.0.0</AssemblyVersion>
</AssemblyInfo>
<ReturnValue>
<ReturnType>Xamarin.Forms.EffectiveFlowDirection</ReturnType>
</ReturnValue>
<Docs>
<summary>To be added.</summary>
<value>To be added.</value>
<remarks>To be added.</remarks>
</Docs>
</Member>
<Member MemberName="FocusChangeRequested">
<MemberSignature Language="C#" Value="public event EventHandler&lt;Xamarin.Forms.VisualElement.FocusRequestArgs&gt; FocusChangeRequested;" />
<MemberSignature Language="ILAsm" Value=".event class System.EventHandler`1&lt;class Xamarin.Forms.VisualElement/FocusRequestArgs&gt; FocusChangeRequested" />

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

@ -352,6 +352,37 @@
<remarks>To be added.</remarks>
</Docs>
</Member>
<Member MemberName="FlowDirection">
<MemberSignature Language="C#" Value="public Xamarin.Forms.FlowDirection FlowDirection { get; set; }" />
<MemberSignature Language="ILAsm" Value=".property instance valuetype Xamarin.Forms.FlowDirection FlowDirection" />
<MemberType>Property</MemberType>
<AssemblyInfo>
<AssemblyVersion>2.0.0.0</AssemblyVersion>
</AssemblyInfo>
<ReturnValue>
<ReturnType>Xamarin.Forms.FlowDirection</ReturnType>
</ReturnValue>
<Docs>
<summary>To be added.</summary>
<value>To be added.</value>
<remarks>To be added.</remarks>
</Docs>
</Member>
<Member MemberName="FlowDirectionProperty">
<MemberSignature Language="C#" Value="public static readonly Xamarin.Forms.BindableProperty FlowDirectionProperty;" />
<MemberSignature Language="ILAsm" Value=".field public static initonly class Xamarin.Forms.BindableProperty FlowDirectionProperty" />
<MemberType>Field</MemberType>
<AssemblyInfo>
<AssemblyVersion>2.0.0.0</AssemblyVersion>
</AssemblyInfo>
<ReturnValue>
<ReturnType>Xamarin.Forms.BindableProperty</ReturnType>
</ReturnValue>
<Docs>
<summary>To be added.</summary>
<remarks>To be added.</remarks>
</Docs>
</Member>
<Member MemberName="Focus">
<MemberSignature Language="C#" Value="public bool Focus ();" />
<MemberSignature Language="ILAsm" Value=".method public hidebysig instance bool Focus() cil managed" />
@ -2062,6 +2093,22 @@
<remarks>The x value of an element is set during the Layout phase.</remarks>
</Docs>
</Member>
<Member MemberName="Xamarin.Forms.IVisualElementController.EffectiveFlowDirection">
<MemberSignature Language="C#" Value="Xamarin.Forms.EffectiveFlowDirection Xamarin.Forms.IVisualElementController.EffectiveFlowDirection { get; }" />
<MemberSignature Language="ILAsm" Value=".property instance valuetype Xamarin.Forms.EffectiveFlowDirection Xamarin.Forms.IVisualElementController.EffectiveFlowDirection" />
<MemberType>Property</MemberType>
<AssemblyInfo>
<AssemblyVersion>2.0.0.0</AssemblyVersion>
</AssemblyInfo>
<ReturnValue>
<ReturnType>Xamarin.Forms.EffectiveFlowDirection</ReturnType>
</ReturnValue>
<Docs>
<summary>To be added.</summary>
<value>To be added.</value>
<remarks>To be added.</remarks>
</Docs>
</Member>
<Member MemberName="Xamarin.Forms.IVisualElementController.InvalidateMeasure">
<MemberSignature Language="C#" Value="void IVisualElementController.InvalidateMeasure (Xamarin.Forms.Internals.InvalidationTrigger trigger);" />
<MemberSignature Language="ILAsm" Value=".method hidebysig newslot virtual instance void Xamarin.Forms.IVisualElementController.InvalidateMeasure(valuetype Xamarin.Forms.Internals.InvalidationTrigger trigger) cil managed" />

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

@ -213,6 +213,8 @@
<Type Name="Easing" Kind="Class" />
<Type Name="Editor" Kind="Class" />
<Type Name="Effect" Kind="Class" />
<Type Name="EffectiveFlowDirection" Kind="Enumeration" />
<Type Name="EffectiveFlowDirectionExtensions" Kind="Class" />
<Type Name="Element" Kind="Class" />
<Type Name="ElementEventArgs" Kind="Class" />
<Type Name="ElementTemplate" Kind="Class" />
@ -222,6 +224,7 @@
<Type Name="ExportEffectAttribute" Kind="Class" />
<Type Name="FileImageSource" Kind="Class" />
<Type Name="FileImageSourceConverter" Kind="Class" />
<Type Name="FlowDirection" Kind="Enumeration" />
<Type Name="FocusEventArgs" Kind="Class" />
<Type Name="Font" Kind="Structure" />
<Type Name="FontAttributes" Kind="Enumeration" />
@ -857,6 +860,90 @@
<Link Type="Xamarin.Forms.BindableObjectExtensions" Member="M:Xamarin.Forms.BindableObjectExtensions.SetBinding``1(Xamarin.Forms.BindableObject,Xamarin.Forms.BindableProperty,System.Linq.Expressions.Expression{System.Func{``0,System.Object}},Xamarin.Forms.BindingMode,Xamarin.Forms.IValueConverter,System.String)" />
</Member>
</ExtensionMethod>
<ExtensionMethod>
<Targets>
<Target Type="T:Xamarin.Forms.EffectiveFlowDirection" />
</Targets>
<Member MemberName="IsExplicit">
<MemberSignature Language="C#" Value="public static bool IsExplicit (this Xamarin.Forms.EffectiveFlowDirection self);" />
<MemberSignature Language="ILAsm" Value=".method public static hidebysig bool IsExplicit(valuetype Xamarin.Forms.EffectiveFlowDirection self) cil managed" />
<MemberType>ExtensionMethod</MemberType>
<ReturnValue>
<ReturnType>System.Boolean</ReturnType>
</ReturnValue>
<Parameters>
<Parameter Name="self" Type="Xamarin.Forms.EffectiveFlowDirection" RefType="this" />
</Parameters>
<Docs>
<param name="self">To be added.</param>
<summary>To be added.</summary>
</Docs>
<Link Type="Xamarin.Forms.EffectiveFlowDirectionExtensions" Member="M:Xamarin.Forms.EffectiveFlowDirectionExtensions.IsExplicit(Xamarin.Forms.EffectiveFlowDirection)" />
</Member>
</ExtensionMethod>
<ExtensionMethod>
<Targets>
<Target Type="T:Xamarin.Forms.EffectiveFlowDirection" />
</Targets>
<Member MemberName="IsImplicit">
<MemberSignature Language="C#" Value="public static bool IsImplicit (this Xamarin.Forms.EffectiveFlowDirection self);" />
<MemberSignature Language="ILAsm" Value=".method public static hidebysig bool IsImplicit(valuetype Xamarin.Forms.EffectiveFlowDirection self) cil managed" />
<MemberType>ExtensionMethod</MemberType>
<ReturnValue>
<ReturnType>System.Boolean</ReturnType>
</ReturnValue>
<Parameters>
<Parameter Name="self" Type="Xamarin.Forms.EffectiveFlowDirection" RefType="this" />
</Parameters>
<Docs>
<param name="self">To be added.</param>
<summary>To be added.</summary>
</Docs>
<Link Type="Xamarin.Forms.EffectiveFlowDirectionExtensions" Member="M:Xamarin.Forms.EffectiveFlowDirectionExtensions.IsImplicit(Xamarin.Forms.EffectiveFlowDirection)" />
</Member>
</ExtensionMethod>
<ExtensionMethod>
<Targets>
<Target Type="T:Xamarin.Forms.EffectiveFlowDirection" />
</Targets>
<Member MemberName="IsLeftToRight">
<MemberSignature Language="C#" Value="public static bool IsLeftToRight (this Xamarin.Forms.EffectiveFlowDirection self);" />
<MemberSignature Language="ILAsm" Value=".method public static hidebysig bool IsLeftToRight(valuetype Xamarin.Forms.EffectiveFlowDirection self) cil managed" />
<MemberType>ExtensionMethod</MemberType>
<ReturnValue>
<ReturnType>System.Boolean</ReturnType>
</ReturnValue>
<Parameters>
<Parameter Name="self" Type="Xamarin.Forms.EffectiveFlowDirection" RefType="this" />
</Parameters>
<Docs>
<param name="self">To be added.</param>
<summary>To be added.</summary>
</Docs>
<Link Type="Xamarin.Forms.EffectiveFlowDirectionExtensions" Member="M:Xamarin.Forms.EffectiveFlowDirectionExtensions.IsLeftToRight(Xamarin.Forms.EffectiveFlowDirection)" />
</Member>
</ExtensionMethod>
<ExtensionMethod>
<Targets>
<Target Type="T:Xamarin.Forms.EffectiveFlowDirection" />
</Targets>
<Member MemberName="IsRightToLeft">
<MemberSignature Language="C#" Value="public static bool IsRightToLeft (this Xamarin.Forms.EffectiveFlowDirection self);" />
<MemberSignature Language="ILAsm" Value=".method public static hidebysig bool IsRightToLeft(valuetype Xamarin.Forms.EffectiveFlowDirection self) cil managed" />
<MemberType>ExtensionMethod</MemberType>
<ReturnValue>
<ReturnType>System.Boolean</ReturnType>
</ReturnValue>
<Parameters>
<Parameter Name="self" Type="Xamarin.Forms.EffectiveFlowDirection" RefType="this" />
</Parameters>
<Docs>
<param name="self">To be added.</param>
<summary>To be added.</summary>
</Docs>
<Link Type="Xamarin.Forms.EffectiveFlowDirectionExtensions" Member="M:Xamarin.Forms.EffectiveFlowDirectionExtensions.IsRightToLeft(Xamarin.Forms.EffectiveFlowDirection)" />
</Member>
</ExtensionMethod>
<ExtensionMethod>
<Targets>
<Target Type="T:Xamarin.Forms.BindableObject" />