Skip to content
This repository has been archived by the owner on May 1, 2024. It is now read-only.

Commit

Permalink
[All] Basic Right-To-Left Support (#1222)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
samhouts authored and PureWeen committed Oct 24, 2019
1 parent a331951 commit aed6da7
Show file tree
Hide file tree
Showing 41 changed files with 570 additions and 273 deletions.
231 changes: 231 additions & 0 deletions Xamarin.Forms.ControlGallery.WP8/App.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
using System;
using System.Diagnostics;
using System.Resources;
using System.Windows;
using System.Windows.Markup;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using Xamarin.Forms.ControlGallery.WP8.Resources;

using Application = Xamarin.Forms.Application;

namespace Xamarin.Forms.ControlGallery.WP8
{
public partial class App : System.Windows.Application
{
/// <summary>
/// Provides easy access to the root frame of the Phone Application.
/// </summary>
/// <returns>The root frame of the Phone Application.</returns>
public static PhoneApplicationFrame RootFrame { get; private set; }

/// <summary>
/// Constructor for the Application object.
/// </summary>
public App()
{
FormsMaps.Init (Controls.App.Config["WP8AppId"], Controls.App.Config["WP8AuthToken"]);

// Global handler for uncaught exceptions.
UnhandledException += Application_UnhandledException;

// Standard XAML initialization
InitializeComponent();

// Phone-specific initialization
InitializePhoneApplication();

// Language display initialization
InitializeLanguage();

// Show graphics profiling information while debugging.
if (Debugger.IsAttached)
{
// Display the current frame rate counters.
Current.Host.Settings.EnableFrameRateCounter = true;

// Show the areas of the app that are being redrawn in each frame.
//Application.Current.Host.Settings.EnableRedrawRegions = true;

// Enable non-production analysis visualization mode,
// which shows areas of a page that are handed off to GPU with a colored overlay.
//Application.Current.Host.Settings.EnableCacheVisualization = true;

// Prevent the screen from turning off while under the debugger by disabling
// the application's idle detection.
// Caution:- Use this under debug mode only. Application that disables user idle detection will continue to run
// and consume battery power when the user is not using the phone.
PhoneApplicationService.Current.UserIdleDetectionMode = IdleDetectionMode.Disabled;
}

}

// Code to execute when the application is launching (eg, from Start)
// This code will not execute when the application is reactivated
void Application_Launching(object sender, LaunchingEventArgs e)
{
Debug.WriteLine ("Launching");
}

// Code to execute when the application is activated (brought to foreground)
// This code will not execute when the application is first launched
void Application_Activated(object sender, ActivatedEventArgs e)
{
Debug.WriteLine ("Closing");
}

// Code to execute when the application is deactivated (sent to background)
// This code will not execute when the application is closing
void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
Debug.WriteLine ("Deactivated");
}

// Code to execute when the application is closing (eg, user hit Back)
// This code will not execute when the application is deactivated
void Application_Closing(object sender, ClosingEventArgs e)
{
Debug.WriteLine ("Closing");
}

// Code to execute if a navigation fails
void RootFrame_NavigationFailed(object sender, NavigationFailedEventArgs e)
{
if (Debugger.IsAttached)
{
// A navigation has failed; break into the debugger
Debugger.Break();
}
}

// Code to execute on Unhandled Exceptions
void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
{
if (Debugger.IsAttached)
{
// An unhandled exception has occurred; break into the debugger
Debugger.Break();
}
}

#region Phone application initialization

// Avoid double-initialization
bool _phoneApplicationInitialized = false;

// Do not add any additional code to this method
void InitializePhoneApplication()
{
if (_phoneApplicationInitialized)
return;

// Create the frame but don't set it as RootVisual yet; this allows the splash
// screen to remain active until the application is ready to render.
RootFrame = new PhoneApplicationFrame();
RootFrame.Navigated += CompleteInitializePhoneApplication;

// Handle navigation failures
RootFrame.NavigationFailed += RootFrame_NavigationFailed;

// Handle reset requests for clearing the backstack
RootFrame.Navigated += CheckForResetNavigation;

// Ensure we don't initialize again
_phoneApplicationInitialized = true;
}

// Do not add any additional code to this method
void CompleteInitializePhoneApplication(object sender, System.Windows.Navigation.NavigationEventArgs e)
{
// Set the root visual to allow the application to render
if (RootVisual != RootFrame)
RootVisual = RootFrame;

// Remove this handler since it is no longer needed
RootFrame.Navigated -= CompleteInitializePhoneApplication;
}

void CheckForResetNavigation(object sender, System.Windows.Navigation.NavigationEventArgs e)
{
// If the app has received a 'reset' navigation, then we need to check
// on the next navigation to see if the page stack should be reset
if (e.NavigationMode == NavigationMode.Reset)
RootFrame.Navigated += ClearBackStackAfterReset;
}

void ClearBackStackAfterReset(object sender, System.Windows.Navigation.NavigationEventArgs e)
{
// Unregister the event so it doesn't get called again
RootFrame.Navigated -= ClearBackStackAfterReset;

// Only clear the stack for 'new' (forward) and 'refresh' navigations
if (e.NavigationMode != NavigationMode.New && e.NavigationMode != NavigationMode.Refresh)
return;

// For UI consistency, clear the entire page stack
while (RootFrame.RemoveBackEntry() != null)
{
; // do nothing
}
}

#endregion

// Initialize the app's font and flow direction as defined in its localized resource strings.
//
// To ensure that the font of your application is aligned with its supported languages and that the
// FlowDirection for each of those languages follows its traditional direction, ResourceLanguage
// and ResourceFlowDirection should be initialized in each resx file to match these values with that
// file's culture. For example:
//
// AppResources.es-ES.resx
// ResourceLanguage's value should be "es-ES"
// ResourceFlowDirection's value should be "LeftToRight"
//
// AppResources.ar-SA.resx
// ResourceLanguage's value should be "ar-SA"
// ResourceFlowDirection's value should be "RightToLeft"
//
// For more info on localizing Windows Phone apps see http://go.microsoft.com/fwlink/?LinkId=262072.
//
void InitializeLanguage()
{
try
{
// Set the font to match the display language defined by the
// ResourceLanguage resource string for each supported language.
//
// Fall back to the font of the neutral language if the Display
// language of the phone is not supported.
//
// If a compiler error is hit then ResourceLanguage is missing from
// the resource file.
RootFrame.Language = XmlLanguage.GetLanguage(AppResources.ResourceLanguage);

// Set the FlowDirection of all elements under the root frame based
// on the ResourceFlowDirection resource string for each
// supported language.
//
// If a compiler error is hit then ResourceFlowDirection is missing from
// the resource file.
System.Windows.FlowDirection flow = (System.Windows.FlowDirection)Enum.Parse(typeof(System.Windows.FlowDirection), AppResources.ResourceFlowDirection);
RootFrame.FlowDirection = flow;
}
catch
{
// If an exception is caught here it is most likely due to either
// ResourceLangauge not being correctly set to a supported language
// code or ResourceFlowDirection is set to a value other than LeftToRight
// or RightToLeft.

if (Debugger.IsAttached)
{
Debugger.Break();
}

throw;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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" />
Expand Down
2 changes: 0 additions & 2 deletions Xamarin.Forms.Controls/CoreGallery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -244,8 +244,6 @@ public override string ToString()
}

List<GalleryPageFactory> _pages = new List<GalleryPageFactory> {
new GalleryPageFactory(() => new Issues.PerformanceGallery(), "Performance"),
new GalleryPageFactory(() => new VisualStateManagerGallery(), "VisualStateManager Gallery"),
new GalleryPageFactory(() => new FlowDirectionGalleryLandingPage(), "FlowDirection"),
new GalleryPageFactory(() => new AutomationPropertiesGallery(), "Accessibility"),
new GalleryPageFactory(() => new PlatformSpecificsGallery(), "Platform Specifics"),
Expand Down
1 change: 1 addition & 0 deletions Xamarin.Forms.Controls/Xamarin.Forms.Controls.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,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" />
Expand Down Expand Up @@ -222,4 +225,4 @@
<LogicalName>Images/crimson.jpg</LogicalName>
</EmbeddedResource>
</ItemGroup>
</Project>
</Project>
52 changes: 27 additions & 25 deletions Xamarin.Forms.Core/Device.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,29 @@

namespace Xamarin.Forms
{
public static class Device
{
public const string iOS = "iOS";
public const string Android = "Android";
public const string WinPhone = "WinPhone";
public const string UWP = "UWP";
public const string WinRT = "WinRT";
public const string macOS = "macOS";
public const string GTK = "GTK";
public const string Tizen = "Tizen";
public const string WPF = "WPF";

[EditorBrowsable(EditorBrowsableState.Never)]
public static DeviceInfo info;

static IPlatformServices s_platformServices;

[EditorBrowsable(EditorBrowsableState.Never)]
public static void SetIdiom(TargetIdiom value) => Idiom = value;
public static TargetIdiom Idiom { get; internal set; }

[EditorBrowsable(EditorBrowsableState.Never)]
public static void SetTargetIdiom(TargetIdiom value) => Idiom = value;

[Obsolete("TargetPlatform is obsolete as of version 2.3.4. Please use RuntimePlatform instead.")]
public static class Device
{
public const string iOS = "iOS";
public const string Android = "Android";
public const string WinPhone = "WinPhone";
public const string UWP = "UWP";
public const string WinRT = "WinRT";
public const string macOS = "macOS";

[EditorBrowsable(EditorBrowsableState.Never)]
public static DeviceInfo info;

static IPlatformServices s_platformServices;

[EditorBrowsable(EditorBrowsableState.Never)]
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;

[Obsolete("TargetPlatform is obsolete as of version 2.3.4. Please use RuntimePlatform instead.")]
#pragma warning disable 0618
public static TargetPlatform OS
{
Expand Down Expand Up @@ -68,6 +66,10 @@ public static DeviceInfo Info
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
{
Expand Down
23 changes: 0 additions & 23 deletions Xamarin.Forms.Core/FlowDirection.cs
Original file line number Diff line number Diff line change
@@ -1,32 +1,9 @@
using System;

namespace Xamarin.Forms
{
[TypeConverter(typeof(FlowDirectionConverter))]
public enum FlowDirection
{
MatchParent = 0,
LeftToRight = 1,
RightToLeft = 2,
}

[Xaml.TypeConversion(typeof(FlowDirection))]
public class FlowDirectionConverter : TypeConverter
{
public override object ConvertFromInvariantString(string value)
{
if (value != null) {
if (Enum.TryParse(value, out FlowDirection direction))
return direction;

if (value.Equals("ltr", StringComparison.OrdinalIgnoreCase))
return FlowDirection.LeftToRight;
if (value.Equals("rtl", StringComparison.OrdinalIgnoreCase))
return FlowDirection.RightToLeft;
if (value.Equals("inherit", StringComparison.OrdinalIgnoreCase))
return FlowDirection.MatchParent;
}
throw new InvalidOperationException(string.Format("Cannot convert \"{0}\" into {1}", value, typeof(FlowDirection)));
}
}
}
10 changes: 6 additions & 4 deletions Xamarin.Forms.Core/Layout.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,12 @@ public abstract class Layout : View, ILayout, ILayoutController, IPaddingElement
{
public static readonly BindableProperty IsClippedToBoundsProperty = BindableProperty.Create("IsClippedToBounds", typeof(bool), typeof(Layout), false);

public static readonly BindableProperty CascadeInputTransparentProperty = BindableProperty.Create(
nameof(CascadeInputTransparent), typeof(bool), typeof(Layout), true);

public static readonly BindableProperty PaddingProperty = PaddingElement.PaddingProperty;
public static readonly BindableProperty PaddingProperty = BindableProperty.Create("Padding", typeof(Thickness), typeof(Layout), default(Thickness),
propertyChanged: (bindable, old, newValue) =>
{
var layout = (Layout)bindable;
layout.UpdateChildrenLayout();
}, defaultValueCreator: (bindable) => ((Layout)bindable).CreateDefaultPadding());

static IList<KeyValuePair<Layout, int>> s_resolutionList = new List<KeyValuePair<Layout, int>>();
static bool s_relayoutInProgress;
Expand Down
Loading

0 comments on commit aed6da7

Please sign in to comment.