summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKangho Hur <kangho.hur@samsung.com>2016-12-16 02:00:07 (GMT)
committerKangho Hur <kangho.hur@samsung.com>2016-12-16 02:02:21 (GMT)
commit347db139a9b1b93cd07739e281631bf13031496b (patch)
tree1ab166cf542e4d0a217fa75db6b12607e2dfadc5
parent8f1bb7b4b2ae8fd182c3874f208a12d549d230dd (diff)
downloadxamarin-forms-347db139a9b1b93cd07739e281631bf13031496b.zip
xamarin-forms-347db139a9b1b93cd07739e281631bf13031496b.tar.gz
xamarin-forms-347db139a9b1b93cd07739e281631bf13031496b.tar.bz2
- Xamarin.Forms.Platform.Tizen has been added - Xamarin.Forms.Maps.Tizen has been added - RPM build spec has been added Change-Id: I0021e0f040d97345affc87512ee0f6ce437f4e6d
-rw-r--r--.nuspec/Xamarin.Forms.Maps.Tizen.nuspec16
-rw-r--r--.nuspec/Xamarin.Forms.Platform.Tizen.nuspec21
-rw-r--r--Xamarin.Forms.Core/Properties/AssemblyInfo.cs4
-rwxr-xr-xXamarin.Forms.Maps.Tizen/FormsMaps.cs19
-rw-r--r--Xamarin.Forms.Maps.Tizen/GeocoderBackend.cs25
-rwxr-xr-xXamarin.Forms.Maps.Tizen/MapControl.cs14
-rwxr-xr-xXamarin.Forms.Maps.Tizen/MapRenderer.cs47
-rw-r--r--Xamarin.Forms.Maps.Tizen/Properties/AssemblyInfo.cs32
-rw-r--r--Xamarin.Forms.Maps.Tizen/Xamarin.Forms.Maps.Tizen.csproj82
-rwxr-xr-xXamarin.Forms.Maps.Tizen/Xamarin.Forms.Maps.Tizen.project.json12
-rw-r--r--Xamarin.Forms.Maps.Tizen/packages.config4
-rw-r--r--Xamarin.Forms.Platform.Tizen/Cells/CellRenderer.cs142
-rw-r--r--Xamarin.Forms.Platform.Tizen/Cells/EntryCellRenderer.cs163
-rw-r--r--Xamarin.Forms.Platform.Tizen/Cells/ImageCellRenderer.cs60
-rw-r--r--Xamarin.Forms.Platform.Tizen/Cells/SwitchCellRenderer.cs74
-rw-r--r--Xamarin.Forms.Platform.Tizen/Cells/TextCellRenderer.cs72
-rw-r--r--Xamarin.Forms.Platform.Tizen/Cells/ViewCellRenderer.cs43
-rw-r--r--Xamarin.Forms.Platform.Tizen/Deserializer.cs106
-rw-r--r--Xamarin.Forms.Platform.Tizen/ElementChangedEventArgs.cs38
-rw-r--r--Xamarin.Forms.Platform.Tizen/EvasObjectWrapper.cs24
-rw-r--r--Xamarin.Forms.Platform.Tizen/ExportCellAttribute.cs12
-rw-r--r--Xamarin.Forms.Platform.Tizen/ExportImageSourceHandlerAttribute.cs12
-rw-r--r--Xamarin.Forms.Platform.Tizen/ExportRendererAttribute.cs12
-rw-r--r--Xamarin.Forms.Platform.Tizen/Extensions/ColorExtensions.cs39
-rw-r--r--Xamarin.Forms.Platform.Tizen/Extensions/KeyboardExtensions.cs42
-rw-r--r--Xamarin.Forms.Platform.Tizen/Extensions/LayoutExtensions.cs38
-rw-r--r--Xamarin.Forms.Platform.Tizen/Extensions/ScrollToPositionExtensions.cs28
-rw-r--r--Xamarin.Forms.Platform.Tizen/Extensions/TextAlignmentExtensions.cs26
-rw-r--r--Xamarin.Forms.Platform.Tizen/Forms.cs173
-rw-r--r--Xamarin.Forms.Platform.Tizen/FormsApplication.cs344
-rw-r--r--Xamarin.Forms.Platform.Tizen/GestureHandler.cs299
-rw-r--r--Xamarin.Forms.Platform.Tizen/Log/ConsoleLogger.cs68
-rw-r--r--Xamarin.Forms.Platform.Tizen/Log/DlogLogger.cs41
-rw-r--r--Xamarin.Forms.Platform.Tizen/Log/ILogger.cs71
-rw-r--r--Xamarin.Forms.Platform.Tizen/Log/Log.cs989
-rw-r--r--Xamarin.Forms.Platform.Tizen/Log/XamarinLogListener.cs20
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/Box.cs67
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/Button.cs303
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/Canvas.cs109
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/ContentPage.cs76
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/DateChangedEventArgs.cs31
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/DatePicker.cs130
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/DateTimePickerDialog.cs100
-rwxr-xr-xXamarin.Forms.Platform.Tizen/Native/Dialog.cs276
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/DisplayOrientations.cs36
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/Entry.cs396
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/FormattedString.cs128
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/IContainable.cs16
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/IMeasurable.cs20
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/ITextable.cs68
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/Image.cs124
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/Keyboard.cs84
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/Label.cs347
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/LayoutEventArgs.cs55
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/LineBreakMode.cs43
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/ListView.cs601
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/MasterDetailPage.cs364
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/ObservableCollection.cs30
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/SearchBar.cs423
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/Span.cs294
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/TableView.cs67
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/TextAlignment.cs25
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/TextHelper.cs54
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/TimePicker.cs185
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/Window.cs138
-rw-r--r--Xamarin.Forms.Platform.Tizen/Platform.cs345
-rw-r--r--Xamarin.Forms.Platform.Tizen/PlatformEffect.cs11
-rw-r--r--Xamarin.Forms.Platform.Tizen/Properties/AssemblyInfo.cs59
-rw-r--r--Xamarin.Forms.Platform.Tizen/Renderers/ActivityIndicatorRenderer.cs57
-rw-r--r--Xamarin.Forms.Platform.Tizen/Renderers/BoxViewRenderer.cs61
-rw-r--r--Xamarin.Forms.Platform.Tizen/Renderers/ButtonRenderer.cs95
-rw-r--r--Xamarin.Forms.Platform.Tizen/Renderers/CarouselPageRenderer.cs259
-rw-r--r--Xamarin.Forms.Platform.Tizen/Renderers/ContentPageRenderer.cs65
-rw-r--r--Xamarin.Forms.Platform.Tizen/Renderers/DatePickerRenderer.cs77
-rw-r--r--Xamarin.Forms.Platform.Tizen/Renderers/EditorRenderer.cs88
-rw-r--r--Xamarin.Forms.Platform.Tizen/Renderers/EntryRenderer.cs127
-rw-r--r--Xamarin.Forms.Platform.Tizen/Renderers/EvasObjectWrapperRenderer.cs32
-rw-r--r--Xamarin.Forms.Platform.Tizen/Renderers/FrameRenderer.cs124
-rw-r--r--Xamarin.Forms.Platform.Tizen/Renderers/IVisualElementRenderer.cs39
-rw-r--r--Xamarin.Forms.Platform.Tizen/Renderers/ImageRenderer.cs105
-rw-r--r--Xamarin.Forms.Platform.Tizen/Renderers/LabelRenderer.cs103
-rw-r--r--Xamarin.Forms.Platform.Tizen/Renderers/LayoutRenderer.cs57
-rw-r--r--Xamarin.Forms.Platform.Tizen/Renderers/ListViewRenderer.cs437
-rw-r--r--Xamarin.Forms.Platform.Tizen/Renderers/MasterDetailPageRenderer.cs96
-rw-r--r--Xamarin.Forms.Platform.Tizen/Renderers/NavigationPageRenderer.cs390
-rw-r--r--Xamarin.Forms.Platform.Tizen/Renderers/PickerRenderer.cs119
-rw-r--r--Xamarin.Forms.Platform.Tizen/Renderers/ProgressBarRenderer.cs60
-rwxr-xr-xXamarin.Forms.Platform.Tizen/Renderers/ScrollViewRenderer.cs130
-rw-r--r--Xamarin.Forms.Platform.Tizen/Renderers/SearchBarRenderer.cs167
-rw-r--r--Xamarin.Forms.Platform.Tizen/Renderers/SliderRenderer.cs63
-rw-r--r--Xamarin.Forms.Platform.Tizen/Renderers/StepperRenderer.cs86
-rw-r--r--Xamarin.Forms.Platform.Tizen/Renderers/SwitchRenderer.cs50
-rw-r--r--Xamarin.Forms.Platform.Tizen/Renderers/TabbedPageRenderer.cs190
-rw-r--r--Xamarin.Forms.Platform.Tizen/Renderers/TableViewRenderer.cs79
-rw-r--r--Xamarin.Forms.Platform.Tizen/Renderers/TimePickerRenderer.cs101
-rw-r--r--Xamarin.Forms.Platform.Tizen/Renderers/ViewRenderer.cs46
-rw-r--r--Xamarin.Forms.Platform.Tizen/Renderers/VisualElementRenderer.cs946
-rw-r--r--Xamarin.Forms.Platform.Tizen/ResourcePath.cs44
-rw-r--r--Xamarin.Forms.Platform.Tizen/ResourcesProvider.cs72
-rw-r--r--Xamarin.Forms.Platform.Tizen/TizenIsolatedStorageFile.cs125
-rw-r--r--Xamarin.Forms.Platform.Tizen/TizenPlatformServices.cs232
-rw-r--r--Xamarin.Forms.Platform.Tizen/TizenTitleBarVisibility.cs8
-rw-r--r--Xamarin.Forms.Platform.Tizen/ViewInitializedEventArgs.cs20
-rw-r--r--Xamarin.Forms.Platform.Tizen/VisualElementChangedEventArgs.cs16
-rw-r--r--Xamarin.Forms.Platform.Tizen/VisualElementRendererFlags.cs13
-rw-r--r--Xamarin.Forms.Platform.Tizen/Xamarin.Forms.Platform.Tizen.csproj163
-rw-r--r--Xamarin.Forms.Platform.Tizen/Xamarin.Forms.Platform.Tizen.project.json13
-rw-r--r--Xamarin.Forms.Tizen.sln65
-rwxr-xr-xpackaging/custom-find-requires12
-rwxr-xr-xpackaging/xamarin-forms-tizen.manifest5
-rw-r--r--packaging/xamarin-forms-tizen.spec94
111 files changed, 13347 insertions, 1 deletions
diff --git a/.nuspec/Xamarin.Forms.Maps.Tizen.nuspec b/.nuspec/Xamarin.Forms.Maps.Tizen.nuspec
new file mode 100644
index 0000000..b951d4a
--- /dev/null
+++ b/.nuspec/Xamarin.Forms.Maps.Tizen.nuspec
@@ -0,0 +1,16 @@
+<?xml version="1.0"?>
+<package>
+ <metadata>
+ <id>Xamarin.Forms.Maps.Tizen</id>
+ <version>$version$</version>
+ <authors>Tizen Developers</authors>
+ <description>Xamarin.Forms.Maps Renderer for Tizen.Net</description>
+ <dependencies>
+ <dependency id="Xamarin.Forms.Maps" version="2.3.3.0" />
+ </dependencies>
+ </metadata>
+ <files>
+ <file src="../Xamarin.Forms.Maps.Tizen/bin/$Configuration$/Xamarin.Forms.Maps.Tizen.dll" target="lib/netcoreapp1.0"/>
+ <file src="../Xamarin.Forms.Maps.Tizen/bin/$Configuration$/Xamarin.Forms.Maps.Tizen.*pdb" target="lib/netcoreapp1.0"/>
+ </files>
+</package>
diff --git a/.nuspec/Xamarin.Forms.Platform.Tizen.nuspec b/.nuspec/Xamarin.Forms.Platform.Tizen.nuspec
new file mode 100644
index 0000000..3bcf80b
--- /dev/null
+++ b/.nuspec/Xamarin.Forms.Platform.Tizen.nuspec
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+<package>
+ <metadata>
+ <id>Xamarin.Forms.Platform.Tizen</id>
+ <version>$version$</version>
+ <authors>tizen</authors>
+ <tags>xamarin forms xamarin.forms tizen</tags>
+ <projectUrl>https://www.tizen.org/</projectUrl>
+ <iconUrl>https://developer.tizen.org/sites/default/files/images/tizen-pinwheel-on-light-rgb_64_64.png</iconUrl>
+ <requireLicenseAcceptance>false</requireLicenseAcceptance>
+ <description>Xamarin Forms Renderer to build native UIs for Tizen.Net</description>
+ <copyright>Copyright 2016</copyright>
+ <dependencies>
+ <dependency id="Xamarin.Forms" version="2.3.3.175" />
+ </dependencies>
+ </metadata>
+ <files>
+ <file src="../Xamarin.Forms.Platform.Tizen/bin/$Configuration$/Xamarin.Forms.Platform.Tizen.dll" target="lib/netcoreapp1.0"/>
+ <file src="../Xamarin.Forms.Platform.Tizen/bin/$Configuration$/Xamarin.Forms.Platform.Tizen.*pdb" target="lib/netcoreapp1.0"/>
+ </files>
+</package>
diff --git a/Xamarin.Forms.Core/Properties/AssemblyInfo.cs b/Xamarin.Forms.Core/Properties/AssemblyInfo.cs
index 6bbdb60..dfd46e0 100644
--- a/Xamarin.Forms.Core/Properties/AssemblyInfo.cs
+++ b/Xamarin.Forms.Core/Properties/AssemblyInfo.cs
@@ -28,6 +28,7 @@ using Xamarin.Forms.Internals;
[assembly: InternalsVisibleTo("Xamarin.Forms.Platform.WinRT.Tablet")]
[assembly: InternalsVisibleTo("Xamarin.Forms.Platform.WinRT.Phone")]
[assembly: InternalsVisibleTo("Xamarin.Forms.Platform.WP8")]
+[assembly: InternalsVisibleTo("Xamarin.Forms.Platform.Tizen")]
[assembly: InternalsVisibleTo("Xamarin.Forms.Platform.MacOS")]
[assembly: InternalsVisibleTo("iOSUnitTests")]
[assembly: InternalsVisibleTo("Xamarin.Forms.Controls")]
@@ -40,6 +41,7 @@ using Xamarin.Forms.Internals;
[assembly: InternalsVisibleTo("Xamarin.Forms.Maps.iOS")]
[assembly: InternalsVisibleTo("Xamarin.Forms.Maps.iOS.Classic")]
[assembly: InternalsVisibleTo("Xamarin.Forms.Maps.Android")]
+[assembly: InternalsVisibleTo("Xamarin.Forms.Maps.Tizen")]
[assembly: InternalsVisibleTo("Xamarin.Forms.Xaml.UnitTests")]
[assembly: InternalsVisibleTo("Xamarin.Forms.UITests")]
//[assembly:InternalsVisibleTo("Xamarin.Forms.Core.UITests")]
@@ -58,4 +60,4 @@ using Xamarin.Forms.Internals;
[assembly: InternalsVisibleTo("Xamarin.Forms.CarouselView")]
[assembly: Preserve]
-[assembly: XmlnsDefinition("http://xamarin.com/schemas/2014/forms", "Xamarin.Forms")] \ No newline at end of file
+[assembly: XmlnsDefinition("http://xamarin.com/schemas/2014/forms", "Xamarin.Forms")]
diff --git a/Xamarin.Forms.Maps.Tizen/FormsMaps.cs b/Xamarin.Forms.Maps.Tizen/FormsMaps.cs
new file mode 100755
index 0000000..b41b50d
--- /dev/null
+++ b/Xamarin.Forms.Maps.Tizen/FormsMaps.cs
@@ -0,0 +1,19 @@
+using Xamarin.Forms.Maps.Tizen;
+
+namespace Xamarin
+{
+ public static class FormsMaps
+ {
+ public static bool IsInitialized { get; private set; }
+
+ public static void Init()
+ {
+ if (IsInitialized)
+ return;
+
+ IsInitialized = true;
+
+ GeocoderBackend.Register();
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Maps.Tizen/GeocoderBackend.cs b/Xamarin.Forms.Maps.Tizen/GeocoderBackend.cs
new file mode 100644
index 0000000..a18990d
--- /dev/null
+++ b/Xamarin.Forms.Maps.Tizen/GeocoderBackend.cs
@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using System;
+
+namespace Xamarin.Forms.Maps.Tizen
+{
+ public class Position {};
+
+ internal class GeocoderBackend
+ {
+ public static void Register()
+ {
+ }
+
+ public static async Task<IEnumerable<Position>> GetPositionsForAddressAsync(string address)
+ {
+ return new Position[]{};
+ }
+
+ public static async Task<IEnumerable<string>> GetAddressesForPositionAsync(Position position)
+ {
+ return new String[]{"Not supported"};
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Maps.Tizen/MapControl.cs b/Xamarin.Forms.Maps.Tizen/MapControl.cs
new file mode 100755
index 0000000..a10bb80
--- /dev/null
+++ b/Xamarin.Forms.Maps.Tizen/MapControl.cs
@@ -0,0 +1,14 @@
+using TLabel = Xamarin.Forms.Platform.Tizen.Native.Label;
+
+namespace Xamarin.Forms.Maps.Tizen
+{
+ public class MapControl : TLabel
+ {
+ public MapControl(ElmSharp.EvasObject parent) : base(parent)
+ {
+ Text = "Can not supported Maps";
+ TextColor = ElmSharp.Color.Red;
+ }
+ }
+}
+
diff --git a/Xamarin.Forms.Maps.Tizen/MapRenderer.cs b/Xamarin.Forms.Maps.Tizen/MapRenderer.cs
new file mode 100755
index 0000000..b7278dc
--- /dev/null
+++ b/Xamarin.Forms.Maps.Tizen/MapRenderer.cs
@@ -0,0 +1,47 @@
+using Xamarin.Forms.Platform.Tizen;
+using TForms = Xamarin.Forms.Platform.Tizen.Forms;
+
+namespace Xamarin.Forms.Maps.Tizen
+{
+ public class MapRenderer : ViewRenderer<Map, MapControl>
+ {
+ public MapRenderer()
+ {
+ RegisterPropertyHandler(Map.MapTypeProperty, UpdateMapType);
+ RegisterPropertyHandler(Map.IsShowingUserProperty, UpdateIsShowingUser);
+ RegisterPropertyHandler(Map.HasScrollEnabledProperty, UpdateHasScrollEnabled);
+ RegisterPropertyHandler(Map.HasZoomEnabledProperty, UpdateHasZoomEnabled);
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<Map> e)
+ {
+ base.OnElementChanged(e);
+
+ if (Control == null)
+ {
+ var mapControl = new MapControl(TForms.Context.MainWindow);
+ SetNativeControl(mapControl);
+ }
+ }
+
+ void UpdateMapType()
+ {
+ // TODO
+ }
+
+ void UpdateIsShowingUser()
+ {
+ // TODO
+ }
+
+ void UpdateHasScrollEnabled()
+ {
+ // TODO
+ }
+
+ void UpdateHasZoomEnabled()
+ {
+ // TODO
+ }
+ }
+}
diff --git a/Xamarin.Forms.Maps.Tizen/Properties/AssemblyInfo.cs b/Xamarin.Forms.Maps.Tizen/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..ec69a71
--- /dev/null
+++ b/Xamarin.Forms.Maps.Tizen/Properties/AssemblyInfo.cs
@@ -0,0 +1,32 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+// Information about this assembly is defined by the following attributes.
+// Change them to the values specific to your project.
+using Xamarin.Forms;
+using Xamarin.Forms.Maps;
+using Xamarin.Forms.Maps.Tizen;
+using Xamarin.Forms.Platform.Tizen;
+
+[assembly: AssemblyTitle("Xamarin.Forms.Maps.Tizen")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("")]
+[assembly: AssemblyCopyright("k.lipner")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
+// The form "{Major}.{Minor}.*" will automatically update the build and revision,
+// and "{Major}.{Minor}.{Build}.*" will update just the revision.
+
+[assembly: AssemblyVersion("1.0.0")]
+
+// The following attributes are used to specify the signing key for the assembly,
+// if desired. See the Mono documentation for more information about signing.
+
+[assembly: ExportRenderer(typeof (Map), typeof (MapRenderer))]
+//[assembly: AssemblyDelaySign(false)]
+//[assembly: AssemblyKeyFile("")]
+
diff --git a/Xamarin.Forms.Maps.Tizen/Xamarin.Forms.Maps.Tizen.csproj b/Xamarin.Forms.Maps.Tizen/Xamarin.Forms.Maps.Tizen.csproj
new file mode 100644
index 0000000..9b16376
--- /dev/null
+++ b/Xamarin.Forms.Maps.Tizen/Xamarin.Forms.Maps.Tizen.csproj
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProjectGuid>{D29D5750-9A39-4E92-A19E-62567D660B7D}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>Xamarin.Forms.Maps.Tizen</RootNamespace>
+ <AssemblyName>Xamarin.Forms.Maps.Tizen</AssemblyName>
+ <FileAlignment>512</FileAlignment>
+ </PropertyGroup>
+ <PropertyGroup>
+ <TargetFrameworkIdentifier>.NETStandard</TargetFrameworkIdentifier>
+ <TargetFrameworkVersion>v1.6</TargetFrameworkVersion>
+ <NuGetTargetMoniker>.NETStandard,Version=v1.6</NuGetTargetMoniker>
+ <AddAdditionalExplicitAssemblyReferences>false</AddAdditionalExplicitAssemblyReferences>
+ <NoStdLib>true</NoStdLib>
+ <NoWarn>$(NoWarn);1701;1702</NoWarn>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="FormsMaps.cs" />
+ <Compile Include="GeocoderBackend.cs" />
+ <Compile Include="MapRenderer.cs" />
+ <Compile Include="MapControl.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="Xamarin.Forms.Maps.Tizen.project.json" />
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+ <ItemGroup>
+ <ProjectReference Include="..\Xamarin.Forms.Maps\Xamarin.Forms.Maps.csproj">
+ <Project>{7D13BAC2-C6A4-416A-B07E-C169B199E52B}</Project>
+ <Name>Xamarin.Forms.Maps</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\Xamarin.Forms.Core\Xamarin.Forms.Core.csproj">
+ <Project>{57B8B73D-C3B5-4C42-869E-7B2F17D354AC}</Project>
+ <Name>Xamarin.Forms.Core</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\Xamarin.Forms.Platform.Tizen\Xamarin.Forms.Platform.Tizen.csproj">
+ <Project>{227D2CC5-26A1-4CE7-AE25-1B18AF625B9C}</Project>
+ <Name>Xamarin.Forms.Platform.Tizen</Name>
+ </ProjectReference>
+ </ItemGroup>
+ <PropertyGroup>
+ <!-- https://github.com/dotnet/corefxlab/tree/master/samples/NetCoreSample and
+ https://docs.microsoft.com/en-us/dotnet/articles/core/tutorials/target-dotnetcore-with-msbuild
+ -->
+ <!-- We don't use any of MSBuild's resolution logic for resolving the framework, so just set these two
+ properties to any folder that exists to skip the GetReferenceAssemblyPaths task (not target) and
+ to prevent it from outputting a warning (MSB3644).
+ -->
+ <_TargetFrameworkDirectories>$(MSBuildThisFileDirectory)</_TargetFrameworkDirectories>
+ <_FullFrameworkReferenceAssemblyPaths>$(MSBuildThisFileDirectory)</_FullFrameworkReferenceAssemblyPaths>
+ <AutoUnifyAssemblyReferences>true</AutoUnifyAssemblyReferences>
+ </PropertyGroup>
+</Project> \ No newline at end of file
diff --git a/Xamarin.Forms.Maps.Tizen/Xamarin.Forms.Maps.Tizen.project.json b/Xamarin.Forms.Maps.Tizen/Xamarin.Forms.Maps.Tizen.project.json
new file mode 100755
index 0000000..f4511fe
--- /dev/null
+++ b/Xamarin.Forms.Maps.Tizen/Xamarin.Forms.Maps.Tizen.project.json
@@ -0,0 +1,12 @@
+{
+ "dependencies": {
+ "ElmSharp": "1.1.0-*",
+ "NETStandard.Library": "1.6.0",
+ "Tizen.Applications": "1.0.2"
+ },
+ "frameworks": {
+ "netstandard1.6": {
+ "imports": "portable-net45+win8+wpa81+wp8"
+ }
+ }
+}
diff --git a/Xamarin.Forms.Maps.Tizen/packages.config b/Xamarin.Forms.Maps.Tizen/packages.config
new file mode 100644
index 0000000..433f1b9
--- /dev/null
+++ b/Xamarin.Forms.Maps.Tizen/packages.config
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+ <package id="ElmSharp" version="1.0.3" targetFramework="net45" />
+</packages> \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.Tizen/Cells/CellRenderer.cs b/Xamarin.Forms.Platform.Tizen/Cells/CellRenderer.cs
new file mode 100644
index 0000000..1cda74c
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Cells/CellRenderer.cs
@@ -0,0 +1,142 @@
+using ElmSharp;
+using System.Collections.Generic;
+using EColor = ElmSharp.Color;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ public abstract class CellRenderer : IRegisterable
+ {
+ const string _heightProperty = "Height";
+ protected static readonly EColor s_defaultTextColor = EColor.Black;
+ protected static readonly EColor s_defaultBackgroundColor = EColor.White;
+ readonly Dictionary<Cell, Dictionary<string, EvasObject>> _realizedNativeViews = new Dictionary<Cell, Dictionary<string, EvasObject>>();
+
+ protected CellRenderer(string style)
+ {
+ Class = new GenItemClass(style)
+ {
+ GetTextHandler = GetText,
+ GetContentHandler = GetContent,
+ DeleteHandler = ItemDeleted,
+ };
+ }
+
+ public GenItemClass Class
+ {
+ get;
+ private set;
+ }
+
+ protected virtual bool OnCellPropertyChanged(Cell cell, string property, Dictionary<string, EvasObject> realizedView)
+ {
+ if (property == _heightProperty)
+ {
+ return true;
+ }
+ return false;
+ }
+
+ protected virtual Span OnGetText(Cell cell, string part)
+ {
+ return null;
+ }
+ protected virtual EvasObject OnGetContent(Cell cell, string part)
+ {
+ return null;
+ }
+ protected virtual void OnDeleted(Cell cell)
+ {
+ }
+
+ protected virtual void OnUnrealizedCell(Cell cell)
+ {
+ }
+
+ protected int FindCellContentHeight(Cell cell)
+ {
+ ViewCell viewCell = cell as ViewCell;
+ if (viewCell != null)
+ {
+ var parentWidth = (cell.Parent as VisualElement).Width;
+ var view = viewCell.View;
+ return (int)view.Measure(parentWidth, double.PositiveInfinity).Request.Height;
+ }
+ else
+ return -1;
+ }
+
+ static Native.Span ToNative(Span span)
+ {
+ var nativeSpan = new Native.Span();
+ nativeSpan.Text = span.Text;
+ nativeSpan.ForegroundColor = span.ForegroundColor.IsDefault ? s_defaultTextColor : span.ForegroundColor.ToNative();
+ nativeSpan.FontAttributes = span.FontAttributes;
+ nativeSpan.BackgroundColor = span.BackgroundColor.IsDefault ? s_defaultBackgroundColor : span.BackgroundColor.ToNative();
+ nativeSpan.FontSize = span.FontSize;
+ nativeSpan.FontFamily = span.FontFamily;
+ return nativeSpan;
+ }
+
+ internal void SendCellPropertyChanged(Cell cell, GenListItem item, string property)
+ {
+ Dictionary<string, EvasObject> realizedView = null;
+ _realizedNativeViews.TryGetValue(cell, out realizedView);
+
+ // just to prevent null reference exception in OnCellPropertyChanged
+ realizedView = realizedView ?? new Dictionary<string, EvasObject>();
+
+ if (property == Cell.IsEnabledProperty.PropertyName)
+ {
+ item.IsEnabled = cell.IsEnabled;
+ }
+ // if true was returned, item was updated
+ // if it's possible to update the cell property without Update(), return false
+ else if (OnCellPropertyChanged(cell, property, realizedView))
+ {
+ item.Update();
+ }
+ }
+
+ internal void SendUnrealizedCell(Cell cell)
+ {
+ _realizedNativeViews.Remove(cell);
+ OnUnrealizedCell(cell);
+ }
+
+ string GetText(object data, string part)
+ {
+ var span = OnGetText((data as Native.ListView.ItemContext).Cell, part);
+ return span != null ? ToNative(span).GetDecoratedText() : null;
+ }
+
+ EvasObject GetContent(object data, string part)
+ {
+ var cell = (data as Native.ListView.ItemContext).Cell;
+ EvasObject nativeView = OnGetContent(cell, part);
+ if (part != null && nativeView != null)
+ {
+ Dictionary<string, EvasObject> realizedView = null;
+ _realizedNativeViews.TryGetValue(cell, out realizedView);
+ if (realizedView == null)
+ {
+ realizedView = new Dictionary<string, EvasObject>();
+ _realizedNativeViews[cell] = realizedView;
+ }
+ realizedView[part] = nativeView;
+
+ nativeView.Deleted += (sender, e) =>
+ {
+ realizedView.Remove(part);
+ };
+ }
+ return nativeView;
+ }
+
+ void ItemDeleted(object data)
+ {
+ var cell = (data as Native.ListView.ItemContext).Cell;
+ _realizedNativeViews.Remove(cell);
+ OnDeleted(cell);
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Cells/EntryCellRenderer.cs b/Xamarin.Forms.Platform.Tizen/Cells/EntryCellRenderer.cs
new file mode 100644
index 0000000..c07985f
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Cells/EntryCellRenderer.cs
@@ -0,0 +1,163 @@
+using ElmSharp;
+using EColor = ElmSharp.Color;
+using EBox = ElmSharp.Box;
+using System.Collections.Generic;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ public class EntryCellRenderer : ViewCellRenderer
+ {
+ const int _defaultHeight = 120;
+ readonly Dictionary<EvasObject, NativeEntryComponent> _realizedComponent = new Dictionary<EvasObject, NativeEntryComponent>();
+
+ public EntryCellRenderer()
+ {
+ }
+
+ protected override EvasObject OnGetContent(Cell cell, string part)
+ {
+ if (part == MainContentPart)
+ {
+ var entryCell = cell as EntryCell;
+ int height = (int)entryCell.RenderHeight;
+ height = height <= 0 ? FindCellContentHeight(entryCell) : height;
+ height = height <= 0 ? _defaultHeight : height;
+
+ // TODO
+ // Need to use Forms.Core element instead of using Elmsharp.
+ // if we use Forms element, easily binding data to view elements
+ var box = new EBox(Forms.Context.MainWindow)
+ {
+ IsHorizontal = true,
+ MinimumHeight = height,
+ };
+ box.SetAlignment(-1.0, -1.0); // fill
+ box.SetWeight(1.0, 1.0); // expand
+
+ var label = new Native.Label(box)
+ {
+ Text = entryCell.Label,
+ TextColor = GetLabelColor(entryCell),
+ };
+ label.SetAlignment(0.0, 0.5);
+ label.SetWeight(0.0, 1.0);
+
+ var entry = new Native.Entry(box)
+ {
+ IsSingleLine = true,
+ Text = entryCell.Text,
+ TextColor = s_defaultTextColor,
+ Placeholder = entryCell.Placeholder,
+ PlaceholderColor = s_defaultTextColor,
+ Keyboard = entryCell.Keyboard.ToNative(),
+ HorizontalTextAlignment = entryCell.HorizontalTextAlignment.ToNative(),
+ };
+ entry.SetAlignment(-1.0, 0.5);
+ entry.SetWeight(1.0, 1.0);
+
+ box.PackEnd(label);
+ box.PackEnd(entry);
+
+ label.Show();
+ entry.Show();
+
+ entry.TextChanged += (sender, e) =>
+ {
+ entryCell.Text = e.NewTextValue;
+ };
+ entry.Activated += (sender, e) =>
+ {
+ (cell as IEntryCellController).SendCompleted();
+ };
+ NativeEntryComponent component = new NativeEntryComponent()
+ {
+ Entry = entry,
+ Label = label,
+ };
+ _realizedComponent[box] = component;
+ box.Deleted += (sender, e) =>
+ {
+ _realizedComponent.Remove(box);
+ };
+ return box;
+ }
+ return null;
+ }
+
+ protected override bool OnCellPropertyChanged(Cell cell, string property, Dictionary<string, EvasObject> realizedView)
+ {
+ if (!realizedView.ContainsKey(MainContentPart) || !_realizedComponent.ContainsKey(realizedView[MainContentPart]))
+ {
+ return base.OnCellPropertyChanged(cell, property, realizedView);
+ }
+
+ NativeEntryComponent realizedCompoenet = _realizedComponent[realizedView[MainContentPart]];
+ EntryCell entryCell = (EntryCell)cell;
+
+ if (property == EntryCell.HorizontalTextAlignmentProperty.PropertyName)
+ {
+ var entry = realizedCompoenet.Entry;
+ if (entry != null)
+ {
+ entry.HorizontalTextAlignment = entryCell.HorizontalTextAlignment.ToNative();
+ }
+ }
+ else if (property == EntryCell.KeyboardProperty.PropertyName)
+ {
+ var entry = realizedCompoenet.Entry;
+ if (entry != null)
+ {
+ entry.Keyboard = entryCell.Keyboard.ToNative();
+ }
+ }
+ else if (property == EntryCell.PlaceholderProperty.PropertyName)
+ {
+ var entry = realizedCompoenet.Entry;
+ if (entry != null)
+ {
+ entry.Placeholder = entryCell.Placeholder;
+ }
+ }
+ else if (property == EntryCell.TextProperty.PropertyName)
+ {
+ var entry = realizedCompoenet.Entry;
+ if (entry != null)
+ {
+ entry.Text = entryCell.Text;
+ }
+ }
+ else if (property == EntryCell.LabelProperty.PropertyName)
+ {
+ var label = realizedCompoenet.Label;
+ if (label != null)
+ {
+ label.Text = entryCell.Label;
+ }
+ }
+ else if (property == EntryCell.LabelColorProperty.PropertyName)
+ {
+ var label = realizedCompoenet.Label;
+ if (label != null)
+ {
+ label.TextColor = GetLabelColor(entryCell);
+ }
+ }
+ else
+ {
+ return base.OnCellPropertyChanged(cell, property, realizedView);
+ }
+ return false;
+ }
+
+ EColor GetLabelColor(EntryCell cell)
+ {
+ return cell.LabelColor.IsDefault ? s_defaultTextColor : cell.LabelColor.ToNative();
+ }
+
+ class NativeEntryComponent
+ {
+ public Native.Entry Entry { get; set; }
+ public Native.Label Label { get; set; }
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Cells/ImageCellRenderer.cs b/Xamarin.Forms.Platform.Tizen/Cells/ImageCellRenderer.cs
new file mode 100644
index 0000000..9492e7c
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Cells/ImageCellRenderer.cs
@@ -0,0 +1,60 @@
+using ElmSharp;
+using System.Collections.Generic;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ public class ImageCellRenderer : TextCellRenderer
+ {
+ const int _defaultHeight = 100;
+ Dictionary<EvasObject, Native.Image> _realizedViews = new Dictionary<EvasObject, Native.Image>();
+
+ public ImageCellRenderer() : this("type1")
+ {
+ ImagePart = "elm.swallow.icon";
+ }
+ protected ImageCellRenderer(string style) : base(style) { }
+
+ protected string ImagePart { get; set; }
+
+ protected override EvasObject OnGetContent(Cell cell, string part)
+ {
+ if (part == ImagePart)
+ {
+ var imgCell = cell as ImageCell;
+ var size = imgCell.RenderHeight;
+ if (size <= 0)
+ {
+ size = _defaultHeight;
+ }
+
+ var image = new Native.Image(Forms.Context.MainWindow)
+ {
+ MinimumWidth = (int)size,
+ MinimumHeight = (int)size
+ };
+ image.SetAlignment(-1.0, -1.0); // fill
+ image.SetWeight(1.0, 1.0); // expand
+
+ image.LoadFromImageSourceAsync(imgCell.ImageSource);
+ return image;
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ protected override bool OnCellPropertyChanged(Cell cell, string property, Dictionary<string, EvasObject> realizedView)
+ {
+ if (property == ImageCell.ImageSourceProperty.PropertyName)
+ {
+ EvasObject image;
+ realizedView.TryGetValue(ImagePart, out image);
+ (image as Native.Image)?.LoadFromImageSourceAsync((cell as ImageCell)?.ImageSource);
+ return false;
+ }
+ return base.OnCellPropertyChanged(cell, property, realizedView);
+ }
+
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Cells/SwitchCellRenderer.cs b/Xamarin.Forms.Platform.Tizen/Cells/SwitchCellRenderer.cs
new file mode 100644
index 0000000..6c5a038
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Cells/SwitchCellRenderer.cs
@@ -0,0 +1,74 @@
+using System.Collections.Generic;
+using ElmSharp;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ public class SwitchCellRenderer : CellRenderer
+ {
+ protected SwitchCellRenderer(string style) : base(style)
+ {
+ }
+ public SwitchCellRenderer() : this("end_icon")
+ {
+ MainPart = "elm.text";
+ SwitchPart = "elm.swallow.end";
+ }
+
+ protected string MainPart { get; set; }
+ protected string SwitchPart { get; set; }
+
+ protected override Span OnGetText(Cell cell, string part)
+ {
+ if (part == MainPart)
+ {
+ return new Span()
+ {
+ Text = (cell as SwitchCell).Text
+ };
+ }
+ return null;
+ }
+
+ protected override EvasObject OnGetContent(Cell cell, string part)
+ {
+ if (part == SwitchPart)
+ {
+ var switchCell = cell as SwitchCell;
+ var checkbox = new Check(Forms.Context.MainWindow)
+ {
+ Style = "on&off",
+ IsChecked = switchCell.On,
+ };
+
+ checkbox.StateChanged += (sender, e) =>
+ {
+ switchCell.On = e.NewState;
+ };
+
+ checkbox.SetAlignment(-1.0, -1.0); // fill
+ checkbox.SetWeight(1.0, 1.0); // expand
+ return checkbox;
+ }
+ return null;
+ }
+
+ protected override bool OnCellPropertyChanged(Cell cell, string property, Dictionary<string, EvasObject> realizedView)
+ {
+ if (property == SwitchCell.OnProperty.PropertyName && realizedView.ContainsKey(SwitchPart))
+ {
+ var checkbox = realizedView[SwitchPart] as Check;
+ if (checkbox != null)
+ {
+ checkbox.IsChecked = (cell as SwitchCell).On;
+ }
+ return false;
+ }
+ else if (property == SwitchCell.TextProperty.PropertyName)
+ {
+ return true;
+ }
+
+ return base.OnCellPropertyChanged(cell, property, realizedView);
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Cells/TextCellRenderer.cs b/Xamarin.Forms.Platform.Tizen/Cells/TextCellRenderer.cs
new file mode 100644
index 0000000..2bea384
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Cells/TextCellRenderer.cs
@@ -0,0 +1,72 @@
+using ElmSharp;
+using System.Collections.Generic;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ public class TextCellRenderer : CellRenderer
+ {
+ public TextCellRenderer() : this("double_label") { }
+ protected TextCellRenderer(string style) : base(style)
+ {
+ MainPart = "elm.text";
+ DetailPart = "elm.text.sub";
+ }
+
+ protected string MainPart { get; set; }
+ protected string DetailPart { get; set; }
+
+ protected override Span OnGetText(Cell cell, string part)
+ {
+ var textCell = (TextCell)cell;
+ if (part == MainPart)
+ {
+ return OnMainText(textCell);
+ }
+ if (part == DetailPart)
+ {
+ return OnDetailText(textCell);
+ }
+ return null;
+ }
+
+ protected virtual Span OnMainText(TextCell cell)
+ {
+ return new Span()
+ {
+ Text = cell.Text,
+ ForegroundColor = cell.TextColor
+ };
+ }
+
+ protected virtual Span OnDetailText(TextCell cell)
+ {
+ return new Span()
+ {
+ Text = cell.Detail,
+ ForegroundColor = cell.DetailColor
+ };
+ }
+
+ protected override bool OnCellPropertyChanged(Cell cell, string property, Dictionary<string, EvasObject> realizedView)
+ {
+ if (property == TextCell.TextProperty.PropertyName ||
+ property == TextCell.TextColorProperty.PropertyName ||
+ property == TextCell.DetailProperty.PropertyName ||
+ property == TextCell.DetailColorProperty.PropertyName)
+ {
+ return true;
+ }
+ return base.OnCellPropertyChanged(cell, property, realizedView);
+ }
+ }
+
+ internal class GroupCellTextRenderer : TextCellRenderer
+ {
+
+ public GroupCellTextRenderer() : this("group_index")
+ {
+ DetailPart = "elm.text.end";
+ }
+ protected GroupCellTextRenderer(string style) : base(style) { }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Cells/ViewCellRenderer.cs b/Xamarin.Forms.Platform.Tizen/Cells/ViewCellRenderer.cs
new file mode 100644
index 0000000..69065c8
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Cells/ViewCellRenderer.cs
@@ -0,0 +1,43 @@
+using System.Collections.Generic;
+using ElmSharp;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ public class ViewCellRenderer : CellRenderer
+ {
+ public ViewCellRenderer() : base("full")
+ {
+ MainContentPart = "elm.swallow.content";
+ }
+
+ protected string MainContentPart { get; set; }
+
+ protected override EvasObject OnGetContent(Cell cell, string part)
+ {
+ if (part == MainContentPart)
+ {
+ var viewCell = cell as ViewCell;
+ if (viewCell != null)
+ {
+ var renderer = Platform.GetOrCreateRenderer(viewCell.View);
+ int height = (int)viewCell.RenderHeight;
+ height = height <= 0 ? FindCellContentHeight(viewCell) : height;
+
+ renderer.NativeView.MinimumHeight = height;
+ return renderer.NativeView;
+ }
+ return null;
+ }
+ return null;
+ }
+
+ protected override bool OnCellPropertyChanged(Cell cell, string property, Dictionary<string, EvasObject> realizedView)
+ {
+ if (property == "View")
+ {
+ return true;
+ }
+ return base.OnCellPropertyChanged(cell, property, realizedView);
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Deserializer.cs b/Xamarin.Forms.Platform.Tizen/Deserializer.cs
new file mode 100644
index 0000000..8335840
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Deserializer.cs
@@ -0,0 +1,106 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using System.Runtime.Serialization;
+using System.Xml;
+using System.Diagnostics;
+using System.IO;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ internal class Deserializer : IDeserializer
+ {
+ const string PropertyStoreFile = "PropertyStore.forms";
+
+ public Task<IDictionary<string, object>> DeserializePropertiesAsync()
+ {
+ // Deserialize property dictionary to local storage
+ // Make sure to use Internal
+ return Task.Run(() =>
+ {
+ var store = new TizenIsolatedStorageFile();
+ Stream stream = null;
+ try
+ {
+ stream = store.OpenFile(PropertyStoreFile, System.IO.FileMode.OpenOrCreate);
+ if (stream.Length == 0)
+ {
+ return null;
+ }
+ using (XmlDictionaryReader reader = XmlDictionaryReader.CreateBinaryReader(stream, XmlDictionaryReaderQuotas.Max))
+ {
+ stream = null;
+ var dcs = new DataContractSerializer(typeof(Dictionary<string, object>));
+ return (IDictionary<string, object>)dcs.ReadObject(reader);
+ }
+ }
+ catch (Exception e)
+ {
+ Debug.WriteLine("Could not deserialize properties: " + e.Message);
+ Xamarin.Forms.Log.Warning("Xamarin.Forms PropertyStore", $"Exception while reading Application properties: {e}");
+ }
+ finally
+ {
+ if (stream != null)
+ {
+ stream.Dispose();
+ }
+ }
+
+ return null;
+ });
+ }
+
+ public Task SerializePropertiesAsync(IDictionary<string, object> properties)
+ {
+ properties = new Dictionary<string, object>(properties);
+ // Serialize property dictionary to local storage
+ // Make sure to use Internal
+ return Task.Run(() =>
+ {
+ var success = false;
+ var store = new TizenIsolatedStorageFile();
+ Stream stream = null;
+ try
+ {
+ stream = store.OpenFile(PropertyStoreFile + ".tmp", System.IO.FileMode.OpenOrCreate);
+ using (XmlDictionaryWriter writer = XmlDictionaryWriter.CreateBinaryWriter(stream))
+ {
+ stream = null;
+ var dcs = new DataContractSerializer(typeof(Dictionary<string, object>));
+ dcs.WriteObject(writer, properties);
+ writer.Flush();
+ success = true;
+ }
+ }
+ catch (Exception e)
+ {
+ Debug.WriteLine("Could not serialize properties: " + e.Message);
+ Xamarin.Forms.Log.Warning("Xamarin.Forms PropertyStore", $"Exception while writing Application properties: {e}");
+ }
+ finally
+ {
+ if (stream != null)
+ {
+ stream.Dispose();
+ }
+ }
+
+ if (!success)
+ return;
+
+ try
+ {
+ if (store.FileExists(PropertyStoreFile))
+ store.DeleteFile(PropertyStoreFile);
+ store.MoveFile(PropertyStoreFile + ".tmp", PropertyStoreFile);
+ }
+ catch (Exception e)
+ {
+ Debug.WriteLine("Could not move new serialized property file over old: " + e.Message);
+ Xamarin.Forms.Log.Warning("Xamarin.Forms PropertyStore", $"Exception while writing Application properties: {e}");
+ }
+ });
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/ElementChangedEventArgs.cs b/Xamarin.Forms.Platform.Tizen/ElementChangedEventArgs.cs
new file mode 100644
index 0000000..844752e
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/ElementChangedEventArgs.cs
@@ -0,0 +1,38 @@
+using System;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ public class ElementChangedEventArgs<TElement> : EventArgs where TElement : Element
+ {
+ /// <summary>
+ /// Holds the old element which is about to be replaced by a new element
+ /// </summary>
+ /// <value>An TElement instance representing the old element just being replaced</value>
+ public TElement OldElement
+ {
+ get;
+ private set;
+ }
+
+ /// <summary>
+ /// Holds the new element which will replace the old element
+ /// </summary>
+ /// <value>An TElement instance representing the new element to be used from now on</value>
+ public TElement NewElement
+ {
+ get;
+ private set;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Xamarin.Forms.Platform.Tizen.ElementChangedEventArgs`1"/> class.
+ /// </summary>
+ /// <param name="oldElement">The old element which will be replaced by a newElement momentarily.</param>
+ /// <param name="newElement">The new element, taking place of an old element.</param>
+ public ElementChangedEventArgs(TElement oldElement, TElement newElement)
+ {
+ this.OldElement = oldElement;
+ this.NewElement = newElement;
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/EvasObjectWrapper.cs b/Xamarin.Forms.Platform.Tizen/EvasObjectWrapper.cs
new file mode 100644
index 0000000..97cf77f
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/EvasObjectWrapper.cs
@@ -0,0 +1,24 @@
+using ElmSharp;
+using ESize = ElmSharp.Size;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ public delegate ESize? MeasureDelegate(EvasObjectWrapperRenderer renderer, int availableWidth, int availableHeight);
+
+ public class EvasObjectWrapper : View
+ {
+ public EvasObjectWrapper(EvasObject obj, MeasureDelegate measureDelegate = null)
+ {
+ EvasObject = obj;
+ MeasureDelegate = measureDelegate;
+ }
+
+ public EvasObject EvasObject
+ {
+ get;
+ private set;
+ }
+
+ public MeasureDelegate MeasureDelegate { get; }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/ExportCellAttribute.cs b/Xamarin.Forms.Platform.Tizen/ExportCellAttribute.cs
new file mode 100644
index 0000000..e0435ad
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/ExportCellAttribute.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
+ public sealed class ExportCellAttribute : HandlerAttribute
+ {
+ public ExportCellAttribute(Type handler, Type target) : base(handler, target)
+ {
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/ExportImageSourceHandlerAttribute.cs b/Xamarin.Forms.Platform.Tizen/ExportImageSourceHandlerAttribute.cs
new file mode 100644
index 0000000..4463db8
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/ExportImageSourceHandlerAttribute.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
+ public sealed class ExportImageSourceHandlerAttribute : HandlerAttribute
+ {
+ public ExportImageSourceHandlerAttribute(Type handler, Type target) : base(handler, target)
+ {
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/ExportRendererAttribute.cs b/Xamarin.Forms.Platform.Tizen/ExportRendererAttribute.cs
new file mode 100644
index 0000000..9af8019
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/ExportRendererAttribute.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
+ public sealed class ExportRendererAttribute : HandlerAttribute
+ {
+ public ExportRendererAttribute(Type handler, Type target) : base(handler, target)
+ {
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Extensions/ColorExtensions.cs b/Xamarin.Forms.Platform.Tizen/Extensions/ColorExtensions.cs
new file mode 100644
index 0000000..553c2cb
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Extensions/ColorExtensions.cs
@@ -0,0 +1,39 @@
+using EColor = ElmSharp.Color;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ public static class ColorExtensions
+ {
+ /// <summary>
+ /// Creates an instance of ElmSharp.Color class based on provided Xamarin.Forms.Color instance
+ /// </summary>
+ /// <returns>ElmSharp.Color instance representing a color which corresponds to the provided Xamarin.Forms.Color</returns>
+ /// <param name="c">The Xamarin.Forms.Color instance which will be converted to a ElmSharp.Color</param>
+ public static EColor ToNative(this Color c)
+ {
+ if (c.IsDefault)
+ {
+ Log.Warn("Trying to convert the default color, this may result in black color.");
+ return EColor.Default;
+ }
+ else
+ {
+ return new EColor((int)(255.0 * c.R), (int)(255.0 * c.G), (int)(255.0 * c.B), (int)(255.0 * c.A));
+ }
+ }
+
+ /// <summary>
+ /// Returns a string representing the provided ElmSharp.Color instance in a hexagonal notation
+ /// </summary>
+ /// <returns>string value containing the encoded color</returns>
+ /// <param name="c">The ElmSharp.Color class instance which will be serialized</param>
+ internal static string ToHex(this EColor c)
+ {
+ if (c.IsDefault)
+ {
+ Log.Warn("Trying to convert the default color to hexagonal notation, it does not works as expected.");
+ }
+ return string.Format("#{0:X2}{1:X2}{2:X2}{3:X2}", c.R, c.G, c.B, c.A);
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Extensions/KeyboardExtensions.cs b/Xamarin.Forms.Platform.Tizen/Extensions/KeyboardExtensions.cs
new file mode 100644
index 0000000..000b6bb
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Extensions/KeyboardExtensions.cs
@@ -0,0 +1,42 @@
+namespace Xamarin.Forms.Platform.Tizen
+{
+ public static class KeyboardExtensions
+ {
+ /// <summary>
+ /// Creates an instance of ElmSharp.Keyboard reflecting the provided Xamarin.Forms.Keyboard instance
+ /// </summary>
+ /// <returns>Keyboard type corresponding to the provided Xamarin.Forms.Keyboard</returns>
+ /// <param name="keyboard">The Xamarin.Forms.Keyboard class instance to be converted to ElmSharp.Keyboard.</param>
+ public static Native.Keyboard ToNative(this Keyboard keyboard)
+ {
+ if (keyboard is TextKeyboard)
+ {
+ return Native.Keyboard.Normal;
+ }
+ else if (keyboard is NumericKeyboard)
+ {
+ return Native.Keyboard.Number;
+ }
+ else if (keyboard is TelephoneKeyboard)
+ {
+ return Native.Keyboard.PhoneNumber;
+ }
+ else if (keyboard is EmailKeyboard)
+ {
+ return Native.Keyboard.Email;
+ }
+ else if (keyboard is UrlKeyboard)
+ {
+ return Native.Keyboard.Url;
+ }
+ else if (keyboard is ChatKeyboard)
+ {
+ return Native.Keyboard.Emoticon;
+ }
+ else
+ {
+ return Native.Keyboard.Normal;
+ }
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Extensions/LayoutExtensions.cs b/Xamarin.Forms.Platform.Tizen/Extensions/LayoutExtensions.cs
new file mode 100644
index 0000000..4880beb
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Extensions/LayoutExtensions.cs
@@ -0,0 +1,38 @@
+using System.Collections.Generic;
+using ElmSharp;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ /// <summary>
+ /// Extension class, provides native embedding functionalities:
+ /// https://developer.xamarin.com/guides/xamarin-forms/user-interface/layouts/add-platform-controls/
+ /// </summary>
+ /// <remarks>
+ /// This code is not used in the Xamarin.Forms.Platform.Tizen implementation, however it should not
+ /// be removed as it allows developers to use native controls directly.
+ /// </remarks>
+ public static class LayoutExtensions
+ {
+ /// <summary>
+ /// Add the specified evas object to the list of children views.
+ /// </summary>
+ /// <param name="children">The extended class.</param>
+ /// <param name="obj">Object to be added.</param>
+ /// <param name="measureDelegate">Optional delegate which provides measurements for the added object.</param>
+ public static void Add(this IList<View> children, EvasObject obj, MeasureDelegate measureDelegate = null)
+ {
+ children.Add(obj.ToView(measureDelegate));
+ }
+
+ /// <summary>
+ /// Wraps the evas object into a view which can be used by Xamarin.
+ /// </summary>
+ /// <returns>The Xamarin view which wraps the evas object.</returns>
+ /// <param name="obj">The extended class.</param>
+ /// <param name="measureDelegate">Optional delegate which provides measurements for the evas object.</param>
+ public static View ToView(this EvasObject obj, MeasureDelegate measureDelegate = null)
+ {
+ return new EvasObjectWrapper(obj, measureDelegate);
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Extensions/ScrollToPositionExtensions.cs b/Xamarin.Forms.Platform.Tizen/Extensions/ScrollToPositionExtensions.cs
new file mode 100644
index 0000000..9d1dc25
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Extensions/ScrollToPositionExtensions.cs
@@ -0,0 +1,28 @@
+using EScrollToPosition = ElmSharp.ScrollToPosition;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ public static class ScrollToPositionExtensions
+ {
+ public static EScrollToPosition ToNative(this ScrollToPosition position)
+ {
+ switch (position)
+ {
+ case ScrollToPosition.Center:
+ return EScrollToPosition.Middle;
+
+ case ScrollToPosition.End:
+ return EScrollToPosition.Bottom;
+
+ case ScrollToPosition.MakeVisible:
+ return EScrollToPosition.In;
+
+ case ScrollToPosition.Start:
+ return EScrollToPosition.Top;
+
+ default:
+ return EScrollToPosition.Top;
+ }
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Extensions/TextAlignmentExtensions.cs b/Xamarin.Forms.Platform.Tizen/Extensions/TextAlignmentExtensions.cs
new file mode 100644
index 0000000..cf662c4
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Extensions/TextAlignmentExtensions.cs
@@ -0,0 +1,26 @@
+namespace Xamarin.Forms.Platform.Tizen
+{
+ public static class TextAlignmentExtensions
+ {
+ public static Native.TextAlignment ToNative(this TextAlignment alignment)
+ {
+ switch (alignment)
+ {
+ case TextAlignment.Center:
+ return Native.TextAlignment.Center;
+
+ case TextAlignment.Start:
+ return Native.TextAlignment.Start;
+
+ case TextAlignment.End:
+ return Native.TextAlignment.End;
+
+ default:
+ Log.Warn("Warning: unrecognized HorizontalTextAlignment value {0}. " +
+ "Expected: {Start|Center|End}.", alignment);
+ Log.Debug("Falling back to platform's default settings.");
+ return Native.TextAlignment.Auto;
+ }
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Forms.cs b/Xamarin.Forms.Platform.Tizen/Forms.cs
new file mode 100644
index 0000000..6aa0e62
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Forms.cs
@@ -0,0 +1,173 @@
+using System;
+using System.Reflection;
+using Tizen.Applications;
+using ElmSharp;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ public static class Forms
+ {
+ class TizenDeviceInfo : DeviceInfo
+ {
+ readonly Size pixelScreenSize;
+
+ readonly Size scaledScreenSize;
+
+ readonly double scalingFactor;
+
+ public override Size PixelScreenSize
+ {
+ get
+ {
+ return this.pixelScreenSize;
+ }
+ }
+
+ public override Size ScaledScreenSize
+ {
+ get
+ {
+ return this.scaledScreenSize;
+ }
+ }
+
+ public override double ScalingFactor
+ {
+ get
+ {
+ return this.scalingFactor;
+ }
+ }
+
+ public TizenDeviceInfo(FormsApplication formsApplication)
+ {
+ // TODO: obtain screen data from device
+ pixelScreenSize = new Size();
+ scaledScreenSize = new Size();
+ scalingFactor = 0.0;
+ }
+ }
+
+ public static event EventHandler<ViewInitializedEventArgs> ViewInitialized;
+
+ public static FormsApplication Context
+ {
+ get;
+ internal set;
+ }
+
+ public static bool IsInitialized
+ {
+ get;
+ private set;
+ }
+
+ internal static TizenTitleBarVisibility TitleBarVisibility
+ {
+ get;
+ private set;
+ }
+
+ internal static void SendViewInitialized(this VisualElement self, EvasObject nativeView)
+ {
+ EventHandler<ViewInitializedEventArgs> viewInitialized = Forms.ViewInitialized;
+ if (viewInitialized != null)
+ {
+ viewInitialized.Invoke(self, new ViewInitializedEventArgs
+ {
+ View = self,
+ NativeView = nativeView
+ });
+ }
+ }
+
+ public static void SetTitleBarVisibility(TizenTitleBarVisibility visibility)
+ {
+ TitleBarVisibility = visibility;
+ }
+
+ public static void Init(FormsApplication application)
+ {
+ SetupInit(application);
+ }
+
+ static void SetupInit(FormsApplication application)
+ {
+ Color.Accent = GetAccentColor();
+
+ Context = application;
+
+ if (!IsInitialized)
+ {
+ Xamarin.Forms.Log.Listeners.Add(new XamarinLogListener());
+ if (System.Threading.SynchronizationContext.Current == null)
+ {
+ TizenSynchronizationContext.Initialize();
+ }
+ Elementary.Initialize();
+ Elementary.ThemeOverlay();
+ }
+
+ //TO-DO: Need to change to Tizen.
+ Device.OS = TargetPlatform.Other;
+
+#if !NET45
+ // In .NETCore, AppDomain feature is not supported.
+ // The list of assemblies returned by AppDomain.GetAssemblies() method should be registered manually.
+ // The assembly of the executing application and referenced assemblies of it are added into the list here.
+ Assembly asm = application.GetType().GetTypeInfo().Assembly;
+ TizenPlatformServices.AppDomain.CurrentDomain.RegisterAssembly(asm);
+ foreach (var refName in asm.GetReferencedAssemblies())
+ {
+ if (!refName.Name.StartsWith("System.") && !refName.Name.StartsWith("Microsoft."))
+ {
+ try
+ {
+ Assembly refAsm = Assembly.Load(refName);
+ TizenPlatformServices.AppDomain.CurrentDomain.RegisterAssembly(refAsm);
+ }
+ catch
+ {
+ Log.Warn("Reference Assembly can not be loaded. {0}", refName.FullName);
+ }
+ }
+ }
+#endif
+
+ Device.PlatformServices = new TizenPlatformServices(); ;
+ if (Device.info != null)
+ {
+ ((TizenDeviceInfo)Device.info).Dispose();
+ Device.info = null;
+ }
+
+ Device.Info = new Forms.TizenDeviceInfo(application);
+
+ if (!Forms.IsInitialized)
+ {
+ Registrar.RegisterAll(new Type[]
+ {
+ typeof(ExportRendererAttribute),
+ typeof(ExportImageSourceHandlerAttribute),
+ typeof(ExportCellAttribute),
+ });
+ }
+
+ // FIXME: We should consider TV and Common (Desktop) profiles also.
+ Device.Idiom = TargetIdiom.Phone;
+
+ IsInitialized = true;
+ }
+
+ static Color GetAccentColor()
+ {
+ // On Windows Phone, this is the complementary color chosen by the user.
+ // Good Windows Phone applications use this as part of their styling to provide a native look and feel.
+ // On iOS and Android this instance is set to a contrasting color that is visible on the default
+ // background but is not the same as the default text color.
+
+ // TODO: implement me
+ return Color.Black;
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/FormsApplication.cs b/Xamarin.Forms.Platform.Tizen/FormsApplication.cs
new file mode 100644
index 0000000..32ef3c3
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/FormsApplication.cs
@@ -0,0 +1,344 @@
+using System;
+using System.ComponentModel;
+using System.Diagnostics;
+using Tizen.Applications;
+using ElmSharp;
+using EProgressBar = ElmSharp.ProgressBar;
+using EColor = ElmSharp.Color;
+using ELabel = ElmSharp.Label;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ public class FormsApplication : CoreUIApplication
+ {
+ Platform _platform;
+ Application _application;
+ bool _isInitialStart;
+ int _pageBusyCount;
+ Native.Dialog _pageBusyDialog;
+ Native.Window _window;
+
+ protected FormsApplication()
+ {
+ _isInitialStart = true;
+ _pageBusyCount = 0;
+ }
+
+ /// <summary>
+ /// Gets the main window or <c>null</c> if it's not set.
+ /// </summary>
+ /// <value>The main window or <c>null</c>.</value>
+ public Native.Window MainWindow
+ {
+ get
+ {
+ return _window;
+ }
+
+ private set
+ {
+ _window = value;
+ }
+ }
+
+ protected override void OnCreate()
+ {
+ base.OnCreate();
+ Application.ClearCurrent();
+ CreateWindow();
+ }
+
+ protected override void OnTerminate()
+ {
+ base.OnTerminate();
+ MessagingCenter.Unsubscribe<Page, AlertArguments>(this, "Xamarin.SendAlert");
+ MessagingCenter.Unsubscribe<Page, bool>(this, "Xamarin.BusySet");
+ MessagingCenter.Unsubscribe<Page, ActionSheetArguments>(this, "Xamarin.ShowActionSheet");
+ if (_platform != null)
+ {
+ _platform.Dispose();
+ }
+ }
+
+ protected override void OnAppControlReceived(AppControlReceivedEventArgs e)
+ {
+ base.OnAppControlReceived(e);
+
+ if (!_isInitialStart && _application != null)
+ {
+ _application.SendResume();
+ }
+ _isInitialStart = false;
+ }
+
+ protected override void OnPause()
+ {
+ base.OnPause();
+ if (_application != null)
+ {
+ _application.SendSleepAsync();
+ }
+ }
+
+ protected override void OnResume()
+ {
+ base.OnResume();
+ if (_application != null)
+ {
+ _application.SendResume();
+ }
+ }
+
+ public void LoadApplication(Application application)
+ {
+ if (null == MainWindow)
+ {
+ throw new NullReferenceException("Call base OnCreate() method before loading the application.");
+ }
+ if (null == application)
+ {
+ throw new ArgumentNullException("application");
+ }
+ _application = application;
+ Application.Current = application;
+ application.SendStart();
+ application.PropertyChanged += new PropertyChangedEventHandler(this.AppOnPropertyChanged);
+ SetPage(_application.MainPage);
+ }
+
+ void AppOnPropertyChanged(object sender, PropertyChangedEventArgs args)
+ {
+ if ("MainPage" == args.PropertyName)
+ {
+ SetPage(_application.MainPage);
+ }
+ }
+
+ void ShowActivityIndicatorDialog(bool enabled)
+ {
+ if (null == _pageBusyDialog)
+ {
+ _pageBusyDialog = new Native.Dialog(Forms.Context.MainWindow)
+ {
+ Orientation = PopupOrientation.Top,
+ };
+
+ var activity = new EProgressBar(_pageBusyDialog)
+ {
+ Style = "process_large",
+ IsPulseMode = true,
+ };
+ activity.PlayPulse();
+ activity.Show();
+
+ _pageBusyDialog.Content = activity;
+
+ }
+ _pageBusyCount = Math.Max(0, enabled ? _pageBusyCount + 1 : _pageBusyCount - 1);
+ if (_pageBusyCount > 0)
+ {
+ _pageBusyDialog.Show();
+ }
+ else
+ {
+ _pageBusyDialog.Dismiss();
+ _pageBusyDialog = null;
+ }
+ }
+
+ void SetPage(Page page)
+ {
+ if (!Forms.IsInitialized)
+ {
+ throw new InvalidOperationException("Call Forms.Init (UIApplication) before this");
+ }
+ if (_platform != null)
+ {
+ _platform.SetPage(page);
+ return;
+ }
+ MessagingCenter.Subscribe<Page, bool>(this, Page.BusySetSignalName, delegate (Page sender, bool enabled)
+ {
+ ShowActivityIndicatorDialog(enabled);
+ }, null);
+
+ MessagingCenter.Subscribe<Page, AlertArguments>(this, Page.AlertSignalName, delegate (Page sender, AlertArguments arguments)
+ {
+ Native.Dialog alert = new Native.Dialog(Forms.Context.MainWindow);
+
+ alert.Title = arguments.Title;
+ var label = new ELabel(alert)
+ {
+ Text = "<span font_size=30 >" + arguments.Message + "<\\span>",
+ };
+ label.Show();
+
+ var box = new Box(alert);
+ box.PackEnd(label);
+ box.Show();
+ alert.Content = box;
+
+ Native.Button cancel = new Native.Button(alert) { Text = arguments.Cancel };
+ alert.NegativeButton = cancel;
+ cancel.Clicked += (s, evt) =>
+ {
+ arguments.SetResult(false);
+ alert.Dismiss();
+ };
+
+ if (arguments.Accept != null)
+ {
+ Native.Button ok = new Native.Button(alert) { Text = arguments.Accept };
+ alert.PositiveButton = ok;
+ ok.Clicked += (s, evt) =>
+ {
+ arguments.SetResult(true);
+ alert.Dismiss();
+ };
+ }
+
+ alert.BackButtonPressed += (s, evt) =>
+ {
+ arguments.SetResult(false);
+ alert.Dismiss();
+ };
+
+ alert.Show();
+ }, null);
+
+ MessagingCenter.Subscribe<Page, ActionSheetArguments>(this, Page.ActionSheetSignalName, delegate (Page sender, ActionSheetArguments arguments)
+ {
+ Native.Dialog alert = new Native.Dialog(Forms.Context.MainWindow);
+
+ alert.Title = arguments.Title;
+ Box box = new Box(alert);
+
+ if (null != arguments.Destruction)
+ {
+ Native.Button destruction = new Native.Button(alert)
+ {
+ Text = arguments.Destruction,
+ TextColor = EColor.Red,
+ AlignmentX = -1
+ };
+ destruction.Clicked += (s, evt) =>
+ {
+ arguments.SetResult(arguments.Destruction);
+ alert.Dismiss();
+ };
+ destruction.Show();
+ box.PackEnd(destruction);
+ }
+
+ foreach (string buttonName in arguments.Buttons)
+ {
+ Native.Button button = new Native.Button(alert)
+ {
+ Text = buttonName,
+ AlignmentX = -1
+ };
+ button.Clicked += (s, evt) =>
+ {
+ arguments.SetResult(buttonName);
+ alert.Dismiss();
+ };
+ button.Show();
+ box.PackEnd(button);
+ }
+
+ box.Show();
+ alert.Content = box;
+
+ if (null != arguments.Cancel)
+ {
+ Native.Button cancel = new Native.Button(Forms.Context.MainWindow) { Text = arguments.Cancel };
+ alert.NegativeButton = cancel;
+ cancel.Clicked += (s, evt) =>
+ {
+ alert.Dismiss();
+ };
+ }
+
+ alert.BackButtonPressed += (s, evt) =>
+ {
+ alert.Dismiss();
+ };
+
+ alert.Show();
+ }, null);
+
+ _platform = new Platform(this);
+ if (_application != null)
+ {
+ _application.Platform = _platform;
+ }
+ _platform.SetPage(page);
+ }
+
+ void CreateWindow()
+ {
+ Debug.Assert(null == MainWindow);
+
+ var window = new Native.Window();
+ window.Closed += (s, e) =>
+ {
+ Exit();
+ };
+ window.RotationChanged += (sender, e) =>
+ {
+ switch (_window.CurrentOrientation)
+ {
+ case Native.DisplayOrientations.None:
+ Device.Info.CurrentOrientation = DeviceOrientation.Other;
+ break;
+
+ case Native.DisplayOrientations.Portrait:
+ Device.Info.CurrentOrientation = DeviceOrientation.PortraitUp;
+ break;
+
+ case Native.DisplayOrientations.Landscape:
+ Device.Info.CurrentOrientation = DeviceOrientation.LandscapeLeft;
+ break;
+
+ case Native.DisplayOrientations.PortraitFlipped:
+ Device.Info.CurrentOrientation = DeviceOrientation.PortraitDown;
+ break;
+
+ case Native.DisplayOrientations.LandscapeFlipped:
+ Device.Info.CurrentOrientation = DeviceOrientation.LandscapeRight;
+ break;
+ }
+ };
+
+ MainWindow = window;
+ }
+ public void Run()
+ {
+ Run(System.Environment.GetCommandLineArgs());
+ }
+
+ /// <summary>
+ /// Exits the application's main loop, which initiates the process of its termination
+ /// </summary>
+ public override void Exit()
+ {
+ if (_platform == null)
+ {
+ Log.Warn("Exit was already called or FormsApplication is not initialized yet.");
+ return;
+ }
+ // before everything is closed, inform the MainPage that it is disappearing
+ try
+ {
+ (_platform?.Page as IPageController)?.SendDisappearing();
+ _platform = null;
+ }
+ catch (Exception e)
+ {
+ Log.Error("Exception thrown from SendDisappearing: {0}", e.Message);
+ }
+
+ base.Exit();
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/GestureHandler.cs b/Xamarin.Forms.Platform.Tizen/GestureHandler.cs
new file mode 100644
index 0000000..209da3e
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/GestureHandler.cs
@@ -0,0 +1,299 @@
+using System.Linq;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.Collections.Generic;
+using ElmSharp;
+using EColor = ElmSharp.Color;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ internal class GestureHandler
+ {
+ internal readonly IVisualElementRenderer _renderer;
+ internal GestureLayer _gestureLayer;
+ View _view => _renderer.Element as View;
+ IPanGestureController _currentPanGestureController;
+ int _currentPanGestureId;
+ IPinchGestureController _currentPinchGestureController;
+ Point _currentScalePoint;
+ int _previousPinchRadius;
+ double _originalPinchScale;
+ Polygon _hitBox;
+
+ public GestureHandler(IVisualElementRenderer renderer)
+ {
+ _renderer = renderer;
+ // Whenever a GestureRecognizer is added to the View, it will be connected to GestureLayer
+ (_view.GestureRecognizers as ObservableCollection<IGestureRecognizer>).CollectionChanged += OnGestureRecognizersChanged;
+ // handle GestureRecognizers which were already set by the time we got here
+ if (_view.GestureRecognizers.Count > 0)
+ {
+ CreateGestureLayer();
+ foreach (var item in _view.GestureRecognizers)
+ ToggleRecognizer(item, true);
+ }
+ }
+
+ public void Clear()
+ {
+ // this will clear all callbacks in ElmSharp GestureLayer
+ _gestureLayer.Unrealize();
+ (_view.GestureRecognizers as ObservableCollection<IGestureRecognizer>).CollectionChanged -= OnGestureRecognizersChanged;
+ if (_hitBox != null)
+ {
+ _hitBox.Unrealize();
+ _hitBox = null;
+ }
+ }
+
+ public void UpdateHitBox()
+ {
+ if (_hitBox == null)
+ return;
+ // _hitBox has to be used because gestures do not work well with transformations (EvasMap)
+ // so we create additional object which has the same shape as tranformed target, but does not have EvasMap on it
+ EvasObject target = _renderer.NativeView;
+ _hitBox.ClearPoints();
+ if (target.IsMapEnabled)
+ {
+ var map = target.EvasMap;
+ Point3D point;
+ for (var i = 0; i < 4; i++)
+ {
+ point = map.GetPointCoordinate(i);
+ _hitBox.AddPoint(point.X, point.Y);
+ }
+ }
+ else
+ {
+ var geometry = target.Geometry;
+ if (geometry.Width == 0 || geometry.Height == 0)
+ return;
+ _hitBox.AddPoint(geometry.Left, geometry.Top);
+ _hitBox.AddPoint(geometry.Right, geometry.Top);
+ _hitBox.AddPoint(geometry.Right, geometry.Bottom);
+ _hitBox.AddPoint(geometry.Left, geometry.Bottom);
+ }
+ }
+
+ protected void ToggleRecognizer(IGestureRecognizer recognizer, bool enable)
+ {
+ TapGestureRecognizer tapRecognizer;
+ PanGestureRecognizer panRecognizer;
+ PinchGestureRecognizer pinchRecognizer;
+
+ if ((tapRecognizer = recognizer as TapGestureRecognizer) != null)
+ {
+ ToggleTapRecognizer(tapRecognizer, enable);
+ }
+ else if ((panRecognizer = recognizer as PanGestureRecognizer) != null)
+ {
+ if (enable)
+ AddPanRecognizer(panRecognizer);
+ else
+ RemovePanRecognizer(panRecognizer);
+ }
+ else if ((pinchRecognizer = recognizer as PinchGestureRecognizer) != null)
+ {
+ if (enable)
+ AddPinchRecognizer(pinchRecognizer);
+ else
+ RemovePinchRecognizer(pinchRecognizer);
+ }
+ else
+ {
+ Log.Error("Unknown GestureRecognizer will be ignored: {0}", recognizer);
+ }
+ }
+
+ void ToggleTapRecognizer(TapGestureRecognizer recognizer, bool enable)
+ {
+ GestureLayer.GestureType type;
+ switch (recognizer.NumberOfTapsRequired)
+ {
+ case 1:
+ type = GestureLayer.GestureType.Tap;
+ break;
+ case 2:
+ type = GestureLayer.GestureType.DoubleTap;
+ break;
+ default:
+ type = GestureLayer.GestureType.TripleTap;
+ break;
+ }
+ if (enable)
+ _gestureLayer.SetTapCallback(type, GestureLayer.GestureState.End, (data) => recognizer.SendTapped(_view));
+ else
+ _gestureLayer.SetTapCallback(type, GestureLayer.GestureState.End, null);
+ }
+
+ void AddPanRecognizer(PanGestureRecognizer recognizer)
+ {
+ if (_currentPanGestureController != null)
+ Log.Warn("More than one PanGestureRecognizer on {0}. Only the last one will work.", _view);
+ EnsureHitBoxExists();
+ _currentPanGestureController = recognizer;
+ _gestureLayer.SetLineCallback(GestureLayer.GestureState.Start, OnPanStarted);
+ _gestureLayer.SetLineCallback(GestureLayer.GestureState.Move, OnPanMoved);
+ _gestureLayer.SetLineCallback(GestureLayer.GestureState.End, OnPanCompleted);
+ _gestureLayer.SetLineCallback(GestureLayer.GestureState.Abort, OnPanCancelled);
+ }
+
+ void RemovePanRecognizer(PanGestureRecognizer recognizer)
+ {
+ _gestureLayer.SetLineCallback(GestureLayer.GestureState.Start, null);
+ _gestureLayer.SetLineCallback(GestureLayer.GestureState.Move, null);
+ _gestureLayer.SetLineCallback(GestureLayer.GestureState.End, null);
+ _gestureLayer.SetLineCallback(GestureLayer.GestureState.Abort, null);
+ _currentPanGestureController = null;
+ }
+
+ void AddPinchRecognizer(PinchGestureRecognizer recognizer)
+ {
+ if (_currentPinchGestureController != null)
+ Log.Warn("More than one PinchGestureRecognizer on {0}. Only the last one will work.", _view);
+ EnsureHitBoxExists();
+ _currentPinchGestureController = recognizer;
+ _gestureLayer.SetZoomCallback(GestureLayer.GestureState.Start, OnPinchStarted);
+ _gestureLayer.SetZoomCallback(GestureLayer.GestureState.Move, OnPinchMoved);
+ _gestureLayer.SetZoomCallback(GestureLayer.GestureState.End, OnPinchCompleted);
+ _gestureLayer.SetZoomCallback(GestureLayer.GestureState.Abort, OnPinchCancelled);
+ }
+
+ void RemovePinchRecognizer(PinchGestureRecognizer recognizer)
+ {
+ _gestureLayer.SetZoomCallback(GestureLayer.GestureState.Start, null);
+ _gestureLayer.SetZoomCallback(GestureLayer.GestureState.Move, null);
+ _gestureLayer.SetZoomCallback(GestureLayer.GestureState.End, null);
+ _gestureLayer.SetZoomCallback(GestureLayer.GestureState.Abort, null);
+ _currentPinchGestureController = null;
+ }
+
+ void CreateGestureLayer()
+ {
+ _gestureLayer = new GestureLayer(_renderer.NativeView);
+ _gestureLayer.Attach(_renderer.NativeView);
+ }
+
+ void EnsureHitBoxExists()
+ {
+ if (_hitBox == null)
+ {
+ Box parent = (Platform.GetRenderer(_renderer.Element.RealParent) as LayoutRenderer).Control;
+ _hitBox = new Polygon(parent)
+ {
+ Color = EColor.Transparent
+ };
+ _hitBox.Show();
+ UpdateHitBox();
+ parent.PackAfter(_hitBox, _renderer.NativeView);
+ _gestureLayer.Attach(_hitBox);
+ }
+ }
+
+ void AddAndRemoveRecognizers(IEnumerable<IGestureRecognizer> removed, IEnumerable<IGestureRecognizer> added)
+ {
+ if (_hitBox == null &&
+ added != null &&
+ added.Any(item => (item is IPanGestureController || item is IPinchGestureController)))
+ {
+ // at least one of the added recognizers requires _hitBot, which is not ready
+ _gestureLayer.ClearCallbacks();
+ EnsureHitBoxExists();
+ // as _gestureLayer was reattached, register all callbacks, not only new ones
+ removed = null;
+ added = _view.GestureRecognizers;
+ }
+
+ if (removed != null)
+ {
+ foreach (var item in removed)
+ ToggleRecognizer(item, false);
+ }
+ if (added != null)
+ {
+ foreach (var item in added)
+ ToggleRecognizer(item, true);
+ }
+ }
+
+ void OnGestureRecognizersChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
+ {
+ // Gestures will be registered/unregistered according to changes in the GestureRecognizers list
+ switch (e.Action)
+ {
+ case NotifyCollectionChangedAction.Add:
+ if (_gestureLayer == null)
+ CreateGestureLayer();
+ AddAndRemoveRecognizers(null, e.NewItems.OfType<IGestureRecognizer>());
+ break;
+
+ case NotifyCollectionChangedAction.Replace:
+ AddAndRemoveRecognizers(e.OldItems.OfType<IGestureRecognizer>(), e.NewItems.OfType<IGestureRecognizer>());
+ break;
+
+ case NotifyCollectionChangedAction.Remove:
+ AddAndRemoveRecognizers(e.OldItems.OfType<IGestureRecognizer>(), null);
+ break;
+
+ case NotifyCollectionChangedAction.Reset:
+ AddAndRemoveRecognizers(_view.GestureRecognizers, null);
+ break;
+ }
+ }
+
+ void OnPanStarted(GestureLayer.LineData data)
+ {
+ _currentPanGestureId++;
+ _currentPanGestureController.SendPanStarted(_view, _currentPanGestureId);
+ }
+
+ void OnPanMoved(GestureLayer.LineData data)
+ {
+ _currentPanGestureController.SendPan(_view, data.X2 - data.X1, data.Y2 - data.Y1, _currentPanGestureId);
+ }
+
+ void OnPanCompleted(GestureLayer.LineData data)
+ {
+ _currentPanGestureController.SendPanCompleted(_view, _currentPanGestureId);
+ }
+
+ void OnPanCancelled(GestureLayer.LineData data)
+ {
+ // don't trust ElmSharp that the gesture has been aborted, report that it is completed
+ _currentPanGestureController.SendPanCompleted(_view, _currentPanGestureId);
+ }
+
+ void OnPinchStarted(GestureLayer.ZoomData data)
+ {
+ var geometry = _renderer.NativeView.Geometry;
+ _currentScalePoint = new Point((data.X - geometry.X) / (double)geometry.Width, (data.Y - geometry.Y) / (double)geometry.Height);
+ _originalPinchScale = _view.Scale;
+ _previousPinchRadius = data.Radius;
+ _currentPinchGestureController.SendPinchStarted(_view, _currentScalePoint);
+ }
+
+ void OnPinchMoved(GestureLayer.ZoomData data)
+ {
+ if (_previousPinchRadius <= 0)
+ _previousPinchRadius = 1;
+ // functionality limitation: _currentScalePoint is not updated
+ _currentPinchGestureController.SendPinch(_view,
+ 1 + _originalPinchScale * (data.Radius - _previousPinchRadius) / _previousPinchRadius,
+ _currentScalePoint
+ );
+ _previousPinchRadius = data.Radius;
+ }
+
+ void OnPinchCompleted(GestureLayer.ZoomData data)
+ {
+ _currentPinchGestureController.SendPinchEnded(_view);
+ }
+
+ void OnPinchCancelled(GestureLayer.ZoomData data)
+ {
+ // ElmSharp says the gesture has been aborted really too often, report completion instead
+ _currentPinchGestureController.SendPinchEnded(_view);
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Log/ConsoleLogger.cs b/Xamarin.Forms.Platform.Tizen/Log/ConsoleLogger.cs
new file mode 100644
index 0000000..19e46e9
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Log/ConsoleLogger.cs
@@ -0,0 +1,68 @@
+using System;
+using System.IO;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ /// <summary>
+ /// Logs a message to the console.
+ /// </summary>
+ internal class ConsoleLogger : ILogger
+ {
+ public void Debug(string tag, string message, string file, string func, int line)
+ {
+ Print("D", tag, message, file, func, line);
+ }
+
+ public void Verbose(string tag, string message, string file, string func, int line)
+ {
+ Print("V", tag, message, file, func, line);
+ }
+
+ public void Info(string tag, string message, string file, string func, int line)
+ {
+ Print("I", tag, message, file, func, line);
+ }
+
+ public void Warn(string tag, string message, string file, string func, int line)
+ {
+ Print("W", tag, message, file, func, line);
+ }
+
+ public void Error(string tag, string message, string file, string func, int line)
+ {
+ Print("E", tag, message, file, func, line);
+ }
+
+ public void Fatal(string tag, string message, string file, string func, int line)
+ {
+ Print("F", tag, message, file, func, line);
+ }
+
+ /// <summary>
+ /// Formats and prints the log information.
+ /// </summary>
+ /// <param name="level">Log level</param>
+ /// <param name="tag">Log tag</param>
+ /// <param name="message">Log message</param>
+ /// <param name="file">Full path to the file</param>
+ /// <param name="func">Function name</param>
+ /// <param name="line">Line number</param>
+ void Print(string level, string tag, string message, string file, string func, int line)
+ {
+ Uri f = new Uri(file);
+ Console.WriteLine(
+ String.Format(
+ "\n[{6:yyyy-MM-dd HH:mm:ss.ffff} {0}/{1}]\n{2}: {3}({4}) > {5}",
+ level, // 0
+ tag, // 1
+ Path.GetFileName(f.AbsolutePath), // 2
+ func, // 3
+ line, // 4
+ message, // 5
+ DateTime.Now // 6
+ )
+ );
+ }
+ }
+}
+
diff --git a/Xamarin.Forms.Platform.Tizen/Log/DlogLogger.cs b/Xamarin.Forms.Platform.Tizen/Log/DlogLogger.cs
new file mode 100644
index 0000000..987a000
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Log/DlogLogger.cs
@@ -0,0 +1,41 @@
+using T = Tizen;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ /// <summary>
+ /// Logs a message to the dlog.
+ /// </summary>
+ internal class DlogLogger : ILogger
+ {
+ public void Debug(string tag, string message, string file, string func, int line)
+ {
+ T.Log.Debug(tag, message, file, func, line);
+ }
+
+ public void Verbose(string tag, string message, string file, string func, int line)
+ {
+ T.Log.Verbose(tag, message, file, func, line);
+ }
+
+ public void Info(string tag, string message, string file, string func, int line)
+ {
+ T.Log.Info(tag, message, file, func, line);
+ }
+
+ public void Warn(string tag, string message, string file, string func, int line)
+ {
+ T.Log.Warn(tag, message, file, func, line);
+ }
+
+ public void Error(string tag, string message, string file, string func, int line)
+ {
+ T.Log.Error(tag, message, file, func, line);
+ }
+
+ public void Fatal(string tag, string message, string file, string func, int line)
+ {
+ T.Log.Fatal(tag, message, file, func, line);
+ }
+ }
+}
+
diff --git a/Xamarin.Forms.Platform.Tizen/Log/ILogger.cs b/Xamarin.Forms.Platform.Tizen/Log/ILogger.cs
new file mode 100644
index 0000000..6874641
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Log/ILogger.cs
@@ -0,0 +1,71 @@
+using System;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ /// <summary>
+ /// Reports log messages with various log levels.
+ /// </summary>
+ internal interface ILogger
+ {
+ /// <summary>
+ /// Reports a debug log message.
+ /// </summary>
+ /// <param name="tag">Log tag</param>
+ /// <param name="message">Log message</param>
+ /// <param name="file">Full path to the file</param>
+ /// <param name="func">Function name</param>
+ /// <param name="line">Line number</param>
+ void Debug(string tag, string message, string file, string func, int line);
+
+ /// <summary>
+ /// Reports a verbose log message.
+ /// </summary>
+ /// <param name="tag">Log tag</param>
+ /// <param name="message">Log message</param>
+ /// <param name="file">Full path to the file</param>
+ /// <param name="func">Function name</param>
+ /// <param name="line">Line number</param>
+ void Verbose(string tag, string message, string file, string func, int line);
+
+ /// <summary>
+ /// Reports an information log message.
+ /// </summary>
+ /// <param name="tag">Log tag</param>
+ /// <param name="message">Log message</param>
+ /// <param name="file">Full path to the file</param>
+ /// <param name="func">Function name</param>
+ /// <param name="line">Line number</param>
+ void Info(string tag, string message, string file, string func, int line);
+
+ /// <summary>
+ /// Reports a warning log message.
+ /// </summary>
+ /// <param name="tag">Log tag</param>
+ /// <param name="message">Log message</param>
+ /// <param name="file">Full path to the file</param>
+ /// <param name="func">Function name</param>
+ /// <param name="line">Line number</param>
+ void Warn(string tag, string message, string file, string func, int line);
+
+ /// <summary>
+ /// Reports an error log message.
+ /// </summary>
+ /// <param name="tag">Log tag</param>
+ /// <param name="message">Log message</param>
+ /// <param name="file">Full path to the file</param>
+ /// <param name="func">Function name</param>
+ /// <param name="line">Line number</param>
+ void Error(string tag, string message, string file, string func, int line);
+
+ /// <summary>
+ /// Reports a fatal error log message.
+ /// </summary>
+ /// <param name="tag">Log tag</param>
+ /// <param name="message">Log message</param>
+ /// <param name="file">Full path to the file</param>
+ /// <param name="func">Function name</param>
+ /// <param name="line">Line number</param>
+ void Fatal(string tag, string message, string file, string func, int line);
+ }
+}
+
diff --git a/Xamarin.Forms.Platform.Tizen/Log/Log.cs b/Xamarin.Forms.Platform.Tizen/Log/Log.cs
new file mode 100644
index 0000000..3ebeb11
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Log/Log.cs
@@ -0,0 +1,989 @@
+using System;
+using System.Runtime.CompilerServices;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ /// <summary>
+ /// Provides logging functionality.
+ /// </summary>
+ internal static class Log
+ {
+ static String _tag = "Xamarin";
+
+ static ILogger _logger = IsTizen() ? (ILogger)new DlogLogger() : (ILogger)new ConsoleLogger();
+
+ /// <summary>
+ /// Gets or sets the log tag.
+ /// </summary>
+ public static String Tag
+ {
+ get
+ {
+ return _tag;
+ }
+ set
+ {
+ _tag = value;
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the logger used to report messages.
+ /// It's DlogLogger on a Tizen platform, ConsoleLogger on any other.
+ /// </summary>
+ public static ILogger Logger
+ {
+ get
+ {
+ return _logger;
+ }
+ set
+ {
+ _logger = value;
+ }
+ }
+
+ public static void Debug(string message,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ _logger.Debug(_tag, message, file, func, line);
+ }
+
+ public static void Debug<T0>(string message,
+ T0 arg0,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Debug(String.Format(message, arg0), _, file, func, line);
+ }
+
+ public static void Debug<T0, T1>(string message,
+ T0 arg0,
+ T1 arg1,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Debug(String.Format(message, arg0, arg1), _, file, func, line);
+ }
+
+ public static void Debug<T0, T1, T2>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Debug(String.Format(message, arg0, arg1, arg2), _, file, func, line);
+ }
+
+ public static void Debug<T0, T1, T2, T3>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ T3 arg3,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Debug(String.Format(message, arg0, arg1, arg2, arg3), _, file, func, line);
+ }
+
+ public static void Debug<T0, T1, T2, T3, T4>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ T3 arg3,
+ T4 arg4,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Debug(String.Format(message, arg0, arg1, arg2, arg3, arg4), _, file, func, line);
+ }
+
+ public static void Debug<T0, T1, T2, T3, T4, T5>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ T3 arg3,
+ T4 arg4,
+ T5 arg5,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Debug(String.Format(message, arg0, arg1, arg2, arg3, arg4, arg5), _, file, func, line);
+ }
+
+ public static void Debug<T0, T1, T2, T3, T4, T5, T6>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ T3 arg3,
+ T4 arg4,
+ T5 arg5,
+ T6 arg6,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Debug(String.Format(message, arg0, arg1, arg2, arg3, arg4, arg5, arg6), _, file, func, line);
+ }
+
+ public static void Debug<T0, T1, T2, T3, T4, T5, T6, T7>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ T3 arg3,
+ T4 arg4,
+ T5 arg5,
+ T6 arg6,
+ T7 arg7,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Debug(String.Format(message, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7), _, file, func, line);
+ }
+
+ public static void Debug<T0, T1, T2, T3, T4, T5, T6, T7, T8>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ T3 arg3,
+ T4 arg4,
+ T5 arg5,
+ T6 arg6,
+ T7 arg7,
+ T8 arg8,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Debug(String.Format(message, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8), _, file, func, line);
+ }
+
+ public static void Debug<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ T3 arg3,
+ T4 arg4,
+ T5 arg5,
+ T6 arg6,
+ T7 arg7,
+ T8 arg8,
+ T9 arg9,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Debug(String.Format(message, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9), _, file, func, line);
+ }
+
+ public static void Verbose(string message,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ _logger.Verbose(_tag, message, file, func, line);
+ }
+
+ public static void Verbose<T0>(string message,
+ T0 arg0,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Verbose(String.Format(message, arg0), _, file, func, line);
+ }
+
+ public static void Verbose<T0, T1>(string message,
+ T0 arg0,
+ T1 arg1,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Verbose(String.Format(message, arg0, arg1), _, file, func, line);
+ }
+
+ public static void Verbose<T0, T1, T2>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Verbose(String.Format(message, arg0, arg1, arg2), _, file, func, line);
+ }
+
+ public static void Verbose<T0, T1, T2, T3>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ T3 arg3,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Verbose(String.Format(message, arg0, arg1, arg2, arg3), _, file, func, line);
+ }
+
+ public static void Verbose<T0, T1, T2, T3, T4>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ T3 arg3,
+ T4 arg4,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Verbose(String.Format(message, arg0, arg1, arg2, arg3, arg4), _, file, func, line);
+ }
+
+ public static void Verbose<T0, T1, T2, T3, T4, T5>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ T3 arg3,
+ T4 arg4,
+ T5 arg5,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Verbose(String.Format(message, arg0, arg1, arg2, arg3, arg4, arg5), _, file, func, line);
+ }
+
+ public static void Verbose<T0, T1, T2, T3, T4, T5, T6>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ T3 arg3,
+ T4 arg4,
+ T5 arg5,
+ T6 arg6,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Verbose(String.Format(message, arg0, arg1, arg2, arg3, arg4, arg5, arg6), _, file, func, line);
+ }
+
+ public static void Verbose<T0, T1, T2, T3, T4, T5, T6, T7>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ T3 arg3,
+ T4 arg4,
+ T5 arg5,
+ T6 arg6,
+ T7 arg7,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Verbose(String.Format(message, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7), _, file, func, line);
+ }
+
+ public static void Verbose<T0, T1, T2, T3, T4, T5, T6, T7, T8>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ T3 arg3,
+ T4 arg4,
+ T5 arg5,
+ T6 arg6,
+ T7 arg7,
+ T8 arg8,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Verbose(String.Format(message, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8), _, file, func, line);
+ }
+
+ public static void Verbose<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ T3 arg3,
+ T4 arg4,
+ T5 arg5,
+ T6 arg6,
+ T7 arg7,
+ T8 arg8,
+ T9 arg9,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Verbose(String.Format(message, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9), _, file, func, line);
+ }
+
+ public static void Info(string message,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ _logger.Info(_tag, message, file, func, line);
+ }
+
+ public static void Info<T0>(string message,
+ T0 arg0,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Info(String.Format(message, arg0), _, file, func, line);
+ }
+
+ public static void Info<T0, T1>(string message,
+ T0 arg0,
+ T1 arg1,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Info(String.Format(message, arg0, arg1), _, file, func, line);
+ }
+
+ public static void Info<T0, T1, T2>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Info(String.Format(message, arg0, arg1, arg2), _, file, func, line);
+ }
+
+ public static void Info<T0, T1, T2, T3>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ T3 arg3,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Info(String.Format(message, arg0, arg1, arg2, arg3), _, file, func, line);
+ }
+
+ public static void Info<T0, T1, T2, T3, T4>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ T3 arg3,
+ T4 arg4,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Info(String.Format(message, arg0, arg1, arg2, arg3, arg4), _, file, func, line);
+ }
+
+ public static void Info<T0, T1, T2, T3, T4, T5>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ T3 arg3,
+ T4 arg4,
+ T5 arg5,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Info(String.Format(message, arg0, arg1, arg2, arg3, arg4, arg5), _, file, func, line);
+ }
+
+ public static void Info<T0, T1, T2, T3, T4, T5, T6>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ T3 arg3,
+ T4 arg4,
+ T5 arg5,
+ T6 arg6,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Info(String.Format(message, arg0, arg1, arg2, arg3, arg4, arg5, arg6), _, file, func, line);
+ }
+
+ public static void Info<T0, T1, T2, T3, T4, T5, T6, T7>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ T3 arg3,
+ T4 arg4,
+ T5 arg5,
+ T6 arg6,
+ T7 arg7,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Info(String.Format(message, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7), _, file, func, line);
+ }
+
+ public static void Info<T0, T1, T2, T3, T4, T5, T6, T7, T8>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ T3 arg3,
+ T4 arg4,
+ T5 arg5,
+ T6 arg6,
+ T7 arg7,
+ T8 arg8,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Info(String.Format(message, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8), _, file, func, line);
+ }
+
+ public static void Info<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ T3 arg3,
+ T4 arg4,
+ T5 arg5,
+ T6 arg6,
+ T7 arg7,
+ T8 arg8,
+ T9 arg9,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Info(String.Format(message, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9), _, file, func, line);
+ }
+
+ public static void Warn(string message,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ _logger.Warn(_tag, message, file, func, line);
+ }
+
+ public static void Warn<T0>(string message,
+ T0 arg0,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Warn(String.Format(message, arg0), _, file, func, line);
+ }
+
+ public static void Warn<T0, T1>(string message,
+ T0 arg0,
+ T1 arg1,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Warn(String.Format(message, arg0, arg1), _, file, func, line);
+ }
+
+ public static void Warn<T0, T1, T2>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Warn(String.Format(message, arg0, arg1, arg2), _, file, func, line);
+ }
+
+ public static void Warn<T0, T1, T2, T3>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ T3 arg3,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Warn(String.Format(message, arg0, arg1, arg2, arg3), _, file, func, line);
+ }
+
+ public static void Warn<T0, T1, T2, T3, T4>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ T3 arg3,
+ T4 arg4,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Warn(String.Format(message, arg0, arg1, arg2, arg3, arg4), _, file, func, line);
+ }
+
+ public static void Warn<T0, T1, T2, T3, T4, T5>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ T3 arg3,
+ T4 arg4,
+ T5 arg5,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Warn(String.Format(message, arg0, arg1, arg2, arg3, arg4, arg5), _, file, func, line);
+ }
+
+ public static void Warn<T0, T1, T2, T3, T4, T5, T6>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ T3 arg3,
+ T4 arg4,
+ T5 arg5,
+ T6 arg6,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Warn(String.Format(message, arg0, arg1, arg2, arg3, arg4, arg5, arg6), _, file, func, line);
+ }
+
+ public static void Warn<T0, T1, T2, T3, T4, T5, T6, T7>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ T3 arg3,
+ T4 arg4,
+ T5 arg5,
+ T6 arg6,
+ T7 arg7,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Warn(String.Format(message, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7), _, file, func, line);
+ }
+
+ public static void Warn<T0, T1, T2, T3, T4, T5, T6, T7, T8>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ T3 arg3,
+ T4 arg4,
+ T5 arg5,
+ T6 arg6,
+ T7 arg7,
+ T8 arg8,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Warn(String.Format(message, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8), _, file, func, line);
+ }
+
+ public static void Warn<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ T3 arg3,
+ T4 arg4,
+ T5 arg5,
+ T6 arg6,
+ T7 arg7,
+ T8 arg8,
+ T9 arg9,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Warn(String.Format(message, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9), _, file, func, line);
+ }
+
+ public static void Error(string message,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ _logger.Error(_tag, message, file, func, line);
+ }
+
+ public static void Error<T0>(string message,
+ T0 arg0,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Error(String.Format(message, arg0), _, file, func, line);
+ }
+
+ public static void Error<T0, T1>(string message,
+ T0 arg0,
+ T1 arg1,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Error(String.Format(message, arg0, arg1), _, file, func, line);
+ }
+
+ public static void Error<T0, T1, T2>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Error(String.Format(message, arg0, arg1, arg2), _, file, func, line);
+ }
+
+ public static void Error<T0, T1, T2, T3>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ T3 arg3,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Error(String.Format(message, arg0, arg1, arg2, arg3), _, file, func, line);
+ }
+
+ public static void Error<T0, T1, T2, T3, T4>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ T3 arg3,
+ T4 arg4,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Error(String.Format(message, arg0, arg1, arg2, arg3, arg4), _, file, func, line);
+ }
+
+ public static void Error<T0, T1, T2, T3, T4, T5>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ T3 arg3,
+ T4 arg4,
+ T5 arg5,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Error(String.Format(message, arg0, arg1, arg2, arg3, arg4, arg5), _, file, func, line);
+ }
+
+ public static void Error<T0, T1, T2, T3, T4, T5, T6>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ T3 arg3,
+ T4 arg4,
+ T5 arg5,
+ T6 arg6,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Error(String.Format(message, arg0, arg1, arg2, arg3, arg4, arg5, arg6), _, file, func, line);
+ }
+
+ public static void Error<T0, T1, T2, T3, T4, T5, T6, T7>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ T3 arg3,
+ T4 arg4,
+ T5 arg5,
+ T6 arg6,
+ T7 arg7,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Error(String.Format(message, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7), _, file, func, line);
+ }
+
+ public static void Error<T0, T1, T2, T3, T4, T5, T6, T7, T8>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ T3 arg3,
+ T4 arg4,
+ T5 arg5,
+ T6 arg6,
+ T7 arg7,
+ T8 arg8,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Error(String.Format(message, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8), _, file, func, line);
+ }
+
+ public static void Error<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ T3 arg3,
+ T4 arg4,
+ T5 arg5,
+ T6 arg6,
+ T7 arg7,
+ T8 arg8,
+ T9 arg9,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Error(String.Format(message, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9), _, file, func, line);
+ }
+
+ public static void Fatal(string message,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ _logger.Fatal(_tag, message, file, func, line);
+ }
+
+ public static void Fatal<T0>(string message,
+ T0 arg0,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Fatal(String.Format(message, arg0), _, file, func, line);
+ }
+
+ public static void Fatal<T0, T1>(string message,
+ T0 arg0,
+ T1 arg1,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Fatal(String.Format(message, arg0, arg1), _, file, func, line);
+ }
+
+ public static void Fatal<T0, T1, T2>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Fatal(String.Format(message, arg0, arg1, arg2), _, file, func, line);
+ }
+
+ public static void Fatal<T0, T1, T2, T3>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ T3 arg3,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Fatal(String.Format(message, arg0, arg1, arg2, arg3), _, file, func, line);
+ }
+
+ public static void Fatal<T0, T1, T2, T3, T4>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ T3 arg3,
+ T4 arg4,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Fatal(String.Format(message, arg0, arg1, arg2, arg3, arg4), _, file, func, line);
+ }
+
+ public static void Fatal<T0, T1, T2, T3, T4, T5>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ T3 arg3,
+ T4 arg4,
+ T5 arg5,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Fatal(String.Format(message, arg0, arg1, arg2, arg3, arg4, arg5), _, file, func, line);
+ }
+
+ public static void Fatal<T0, T1, T2, T3, T4, T5, T6>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ T3 arg3,
+ T4 arg4,
+ T5 arg5,
+ T6 arg6,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Fatal(String.Format(message, arg0, arg1, arg2, arg3, arg4, arg5, arg6), _, file, func, line);
+ }
+
+ public static void Fatal<T0, T1, T2, T3, T4, T5, T6, T7>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ T3 arg3,
+ T4 arg4,
+ T5 arg5,
+ T6 arg6,
+ T7 arg7,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Fatal(String.Format(message, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7), _, file, func, line);
+ }
+
+ public static void Fatal<T0, T1, T2, T3, T4, T5, T6, T7, T8>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ T3 arg3,
+ T4 arg4,
+ T5 arg5,
+ T6 arg6,
+ T7 arg7,
+ T8 arg8,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Fatal(String.Format(message, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8), _, file, func, line);
+ }
+
+ public static void Fatal<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9>(string message,
+ T0 arg0,
+ T1 arg1,
+ T2 arg2,
+ T3 arg3,
+ T4 arg4,
+ T5 arg5,
+ T6 arg6,
+ T7 arg7,
+ T8 arg8,
+ T9 arg9,
+ Guardian _ = default(Guardian),
+ [CallerFilePath] string file = "",
+ [CallerMemberName] string func = "",
+ [CallerLineNumber] int line = 0)
+ {
+ Fatal(String.Format(message, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9), _, file, func, line);
+ }
+
+ /// <summary>
+ /// Determines if Xamarin is running in a Tizen environment.
+ /// </summary>
+ /// <returns><c>true</c> if application is running on Tizen; otherwise, <c>false</c>.</returns>
+ static bool IsTizen()
+ {
+ return System.IO.File.Exists("/etc/tizen-release");
+ }
+
+ /// <summary>
+ /// A helper class, it allows to separate optional parameters from non-optional ones.
+ /// In case of any compilation errors, please make sure you're not using
+ /// explicit <c>null</c> value as one of the parameters.
+ /// </summary>
+ internal struct Guardian
+ {
+ }
+ }
+}
+
diff --git a/Xamarin.Forms.Platform.Tizen/Log/XamarinLogListener.cs b/Xamarin.Forms.Platform.Tizen/Log/XamarinLogListener.cs
new file mode 100644
index 0000000..02bdb0f
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Log/XamarinLogListener.cs
@@ -0,0 +1,20 @@
+using System;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ internal class XamarinLogListener : LogListener
+ {
+ public XamarinLogListener()
+ {
+ }
+
+ #region implemented abstract members of LogListener
+
+ public override void Warning(string category, string message)
+ {
+ Log.Warn("[{0}] {1}", category, message);
+ }
+
+ #endregion
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/Box.cs b/Xamarin.Forms.Platform.Tizen/Native/Box.cs
new file mode 100644
index 0000000..f22d27b
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/Box.cs
@@ -0,0 +1,67 @@
+using System;
+using ElmSharp;
+using EBox = ElmSharp.Box;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// Extends the ElmSharp.Box class with functionality useful to Xamarin.Forms renderer.
+ /// </summary>
+ /// <remarks>
+ /// This class overrides the layout mechanism. Instead of using the native layout,
+ /// <c>LayoutUpdated</c> event is sent.
+ /// </remarks>
+ public class Box : EBox
+ {
+ /// <summary>
+ /// The last processed geometry of the Box which was reported from the native layer.
+ /// </summary>
+ Rect _previousGeometry;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Xamarin.Forms.Platform.Tizen.Native.Box"/> class.
+ /// </summary>
+ /// <param name="parent">The parent EvasObject.</param>
+ public Box(EvasObject parent) : base(parent)
+ {
+ Resized += (sender, e) => { NotifyOnLayout(); };
+ SetLayoutCallback(() => { NotifyOnLayout(); });
+ }
+
+ /// <summary>
+ /// Notifies that the layout has been updated.
+ /// </summary>
+ public event EventHandler<LayoutEventArgs> LayoutUpdated;
+
+ /// <summary>
+ /// Triggers the <c>LayoutUpdated</c> event.
+ /// </summary>
+ /// <remarks>
+ /// This method is called whenever there is a possibility that the size and/or position has been changed.
+ /// </remarks>
+ void NotifyOnLayout()
+ {
+ var g = Geometry;
+
+ if (0 == g.Width || 0 == g.Height)
+ {
+ // ignore irrelevant dimensions
+ return;
+ }
+ if (null != LayoutUpdated)
+ {
+ LayoutUpdated(this, new LayoutEventArgs()
+ {
+ HasChanged = g != _previousGeometry,
+ X = g.X,
+ Y = g.Y,
+ Width = g.Width,
+ Height = g.Height,
+ }
+ );
+ }
+
+ _previousGeometry = g;
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/Button.cs b/Xamarin.Forms.Platform.Tizen/Native/Button.cs
new file mode 100644
index 0000000..8f85da6
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/Button.cs
@@ -0,0 +1,303 @@
+using System;
+using ElmSharp;
+using EButton = ElmSharp.Button;
+using ESize = ElmSharp.Size;
+using EColor = ElmSharp.Color;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// Extends the EButton control, providing basic formatting features,
+ /// i.e. font color, size, additional image.
+ /// </summary>
+ public class Button : EButton, IMeasurable
+ {
+ /// <summary>
+ /// Holds the formatted text of the button.
+ /// </summary>
+ readonly Span _span = new Span();
+
+ /// <summary>
+ /// The internal padding of the button, helps to determine the size.
+ /// </summary>
+ readonly ESize _internalPadding;
+
+ /// <summary>
+ /// Optional image, if set will be drawn on the button.
+ /// </summary>
+ Image _image;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Xamarin.Forms.Platform.Tizen.Native.Button"/> class.
+ /// </summary>
+ /// <param name="parent">Parent evas object.</param>
+ public Button(EvasObject parent) : base(parent)
+ {
+ _internalPadding = GetInternalPadding();
+ }
+
+ /// <summary>
+ /// Gets or sets the button's text.
+ /// </summary>
+ /// <value>The text.</value>
+ public override string Text
+ {
+ get
+ {
+ return _span.Text;
+ }
+
+ set
+ {
+ if (value != _span.Text)
+ {
+ _span.Text = value;
+ ApplyTextAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the color of the text.
+ /// </summary>
+ /// <value>The color of the text.</value>
+ public EColor TextColor
+ {
+ get
+ {
+ return _span.ForegroundColor;
+ }
+
+ set
+ {
+ if (!_span.ForegroundColor.Equals(value))
+ {
+ _span.ForegroundColor = value;
+ ApplyTextAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the color of the text background.
+ /// </summary>
+ /// <value>The color of the text background.</value>
+ public EColor TextBackgroundColor
+ {
+ get
+ {
+ return _span.BackgroundColor;
+ }
+
+ set
+ {
+ if (!_span.BackgroundColor.Equals(value))
+ {
+ _span.BackgroundColor = value;
+ ApplyTextAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the font family.
+ /// </summary>
+ /// <value>The font family.</value>
+ public string FontFamily
+ {
+ get
+ {
+ return _span.FontFamily;
+ }
+
+ set
+ {
+ if (value != _span.FontFamily)
+ {
+ _span.FontFamily = value;
+ ApplyTextAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the font attributes.
+ /// </summary>
+ /// <value>The font attributes.</value>
+ public FontAttributes FontAttributes
+ {
+ get
+ {
+ return _span.FontAttributes;
+ }
+
+ set
+ {
+ if (value != _span.FontAttributes)
+ {
+ _span.FontAttributes = value;
+ ApplyTextAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the size of the font.
+ /// </summary>
+ /// <value>The size of the font.</value>
+ public double FontSize
+ {
+ get
+ {
+ return _span.FontSize;
+ }
+
+ set
+ {
+ if (value != _span.FontSize)
+ {
+ _span.FontSize = value;
+ ApplyTextAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the image to be displayed next to the button's text.
+ /// </summary>
+ /// <value>The image displayed on the button.</value>
+ public Image Image
+ {
+ get
+ {
+ return _image;
+ }
+
+ set
+ {
+ if (value != _image)
+ {
+ ApplyImage(value);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Implementation of the IMeasurable.Measure() method.
+ /// </summary>
+ public ESize Measure(int availableWidth, int availableHeight)
+ {
+ var size = Geometry;
+
+ // resize the control using the whole available width
+ Resize(availableWidth, size.Height);
+
+ // measure the button's text, use it as a hint for the size
+ var rawSize = Native.TextHelper.GetRawTextBlockSize(this);
+ var formattedSize = Native.TextHelper.GetFormattedTextBlockSize(this);
+
+ // restore the original size
+ Resize(size.Width, size.Height);
+
+ var padding = _internalPadding;
+
+ if (rawSize.Width > availableWidth)
+ {
+ // if the raw text width is larger than the available width, use
+ // either formatted size or internal padding, whichever is bigger
+ return new ESize()
+ {
+ Width = Math.Max(padding.Width, formattedSize.Width),
+ Height = Math.Max(padding.Height, Math.Min(formattedSize.Height, Math.Max(rawSize.Height, availableHeight))),
+ };
+ }
+ else
+ {
+ // otherwise use the formatted size along with padding
+ return new ESize()
+ {
+ Width = padding.Width + formattedSize.Width,
+ Height = Math.Max(padding.Height, formattedSize.Height),
+ };
+ }
+ }
+
+ /// <summary>
+ /// Applies the button's text and its style.
+ /// </summary>
+ void ApplyTextAndStyle()
+ {
+ SetInternalTextAndStyle(_span.GetDecoratedText(), _span.GetStyle());
+ }
+
+ /// <summary>
+ /// Sets the button's internal text and its style.
+ /// </summary>
+ /// <param name="formattedText">Formatted text, supports HTML tags.</param>
+ /// <param name="textStyle">Style applied to the formattedText.</param>
+ void SetInternalTextAndStyle(string formattedText, string textStyle)
+ {
+ string emission = "elm,state,text,visible";
+
+ if (string.IsNullOrEmpty(formattedText))
+ {
+ formattedText = null;
+ textStyle = null;
+ emission = "elm,state,text,hidden";
+ }
+
+ base.Text = formattedText;
+
+ var textblock = EdjeObject["elm.text"];
+
+ if (textblock != null)
+ {
+ textblock.TextStyle = textStyle;
+ }
+
+ EdjeObject.EmitSignal(emission, "elm");
+ }
+
+ /// <summary>
+ /// Gets the internal padding of the button.
+ /// </summary>
+ /// <returns>The internal padding.</returns>
+ ESize GetInternalPadding()
+ {
+ var edje = EdjeObject;
+
+ return new ESize
+ {
+ Width = (edje["padding_top_left"]?.Geometry.Width ?? 64) + (edje["padding_bottom_right"]?.Geometry.Width ?? 64),
+ Height = edje["bg"]?.Geometry.Height ?? 64
+ };
+ }
+
+ /// <summary>
+ /// Applies the image to be displayed on the button. If value is <c>null</c>,
+ /// image will be removed.
+ /// </summary>
+ /// <param name="image">Image to be displayed or null.</param>
+ void ApplyImage(Image image)
+ {
+ _image = image;
+
+ SetInternalImage();
+ }
+
+ /// <summary>
+ /// Sets the internal image. If value is <c>null</c>, image will be removed.
+ /// </summary>
+ void SetInternalImage()
+ {
+ if (_image == null)
+ {
+ SetPartContent("icon", null);
+ }
+ else
+ {
+ SetPartContent("icon", _image);
+ }
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/Canvas.cs b/Xamarin.Forms.Platform.Tizen/Native/Canvas.cs
new file mode 100644
index 0000000..c345abf
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/Canvas.cs
@@ -0,0 +1,109 @@
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using ElmSharp;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// A Canvas provides a class which can be a container for other controls.
+ /// </summary>
+ /// <remarks>
+ /// This class is used as a container view for Layouts from Xamarin.Forms.Platform.Tizen framework.
+ /// It is used for implementing xamarin pages and layouts.
+ /// </remarks>
+ public class Canvas : Box, IContainable<EvasObject>
+ {
+ /// <summary>
+ /// The list of Views.
+ /// </summary>
+ readonly ObservableCollection<EvasObject> _children = new ObservableCollection<EvasObject>();
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Xamarin.Forms.Platform.Tizen.Native.Canvas"/> class.
+ /// </summary>
+ /// <remarks>Canvas doesn't support replacing its children, this will be ignored.</remarks>
+ /// <param name="parent">Parent of this instance.</param>
+ public Canvas(EvasObject parent) : base(parent)
+ {
+ _children.CollectionChanged += (o, e) =>
+ {
+ if (e.Action == NotifyCollectionChangedAction.Add)
+ {
+ foreach (var v in e.NewItems)
+ {
+ var view = v as EvasObject;
+ if (null != view)
+ {
+ OnAdd(view);
+ }
+ }
+ }
+ else if (e.Action == NotifyCollectionChangedAction.Remove)
+ {
+ foreach (var v in e.OldItems)
+ {
+ var view = v as EvasObject;
+ if (null != view)
+ {
+ OnRemove(view);
+ }
+ }
+ }
+ else if (e.Action == NotifyCollectionChangedAction.Reset)
+ {
+ OnRemoveAll();
+ }
+ };
+ }
+
+ /// <summary>
+ /// Gets list of native elements that are placed in the canvas.
+ /// </summary>
+ public IList<EvasObject> Children
+ {
+ get
+ {
+ return _children;
+ }
+ }
+
+ /// <summary>
+ /// Provides destruction for native element and contained elements.
+ /// </summary>
+ protected override void OnUnrealize()
+ {
+ foreach (var child in _children)
+ {
+ child.Unrealize();
+ }
+
+ base.OnUnrealize();
+ }
+
+ /// <summary>
+ /// Adds a new child to a container.
+ /// </summary>
+ /// <param name="view">Native element which will be added</param>
+ void OnAdd(EvasObject view)
+ {
+ PackEnd(view);
+ }
+
+ /// <summary>
+ /// Removes a child from a container.
+ /// </summary>
+ /// <param name="view">Child element to be removed from canvas</param>
+ void OnRemove(EvasObject view)
+ {
+ UnPack(view);
+ }
+
+ /// <summary>
+ /// Removes all children from a canvas.
+ /// </summary>
+ void OnRemoveAll()
+ {
+ UnPackAll();
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/ContentPage.cs b/Xamarin.Forms.Platform.Tizen/Native/ContentPage.cs
new file mode 100644
index 0000000..32f24a0
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/ContentPage.cs
@@ -0,0 +1,76 @@
+using System;
+using System.Collections.Generic;
+using ElmSharp;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// A basic page which can hold a single view.
+ /// </summary>
+ public class ContentPage : Background, IContainable<EvasObject>
+ {
+ /// <summary>
+ /// The name of the part to be used when setting content.
+ /// </summary>
+ public const string ContentPartName = "overlay";
+
+ /// <summary>
+ /// Exposes the Children property, mapping it to the _canvas' Children property.
+ /// </summary>
+ public IList<EvasObject> Children => _canvas.Children;
+
+ /// <summary>
+ /// The canvas, used as a container for other objects.
+ /// </summary>
+ /// <remarks>
+ /// The canvas holds all the Views that the ContentPage is composed of.
+ /// </remarks>
+ internal Canvas _canvas;
+
+ /// <summary>
+ /// Initializes a new instance of the ContentPage class.
+ /// </summary>
+ public ContentPage(EvasObject parent) : base(parent)
+ {
+ _canvas = new Canvas(this);
+ SetPartContent(ContentPartName, _canvas);
+ }
+
+ /// <summary>
+ /// Gets or sets the title.
+ /// </summary>
+ /// <value>The current title.p</value>
+ public string Title
+ {
+ get;
+ set;
+ }
+
+ /// <summary>
+ /// Allows custom handling of events emitted when the layout has been updated.
+ /// </summary>
+ public event EventHandler<LayoutEventArgs> LayoutUpdated
+ {
+ add
+ {
+ _canvas.LayoutUpdated += value;
+ }
+ remove
+ {
+ _canvas.LayoutUpdated -= value;
+ }
+ }
+
+ /// <summary>
+ /// Handles the disposing of a ContentPage
+ /// </summary>
+ /// <remarks>
+ /// Takes the proper care of discarding the canvas, then calls the base method.
+ /// </remarks>
+ protected override void OnUnrealize()
+ {
+ _canvas.Unrealize();
+ base.OnUnrealize();
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/DateChangedEventArgs.cs b/Xamarin.Forms.Platform.Tizen/Native/DateChangedEventArgs.cs
new file mode 100644
index 0000000..c238e9e
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/DateChangedEventArgs.cs
@@ -0,0 +1,31 @@
+using System;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// Event arguments for <see cref="DatePicker.DateChanged"/> event.
+ /// </summary>
+ public class DateChangedEventArgs : EventArgs
+ {
+ /// <summary>
+ /// The date that was on the element at the time that the user selected it.
+ /// </summary>
+ public DateTime OldDate { get; private set; }
+
+ /// <summary>
+ /// The date that the user entered.
+ /// </summary>
+ public DateTime NewDate { get; private set; }
+
+ /// <summary>
+ /// Creates a new <see cref="DateChangedEventArgs"/> object that represents a change from <paramref name="oldDate"/> to <paramref name="newDate"/>.
+ /// </summary>
+ /// <param name="oldDate">Old date of <see cref="DatePicker"/>.</param>
+ /// <param name="newDate">Current date of <see cref="DatePicker"/>.</param>
+ public DateChangedEventArgs(DateTime oldDate, DateTime newDate)
+ {
+ this.OldDate = oldDate;
+ this.NewDate = newDate;
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/DatePicker.cs b/Xamarin.Forms.Platform.Tizen/Native/DatePicker.cs
new file mode 100644
index 0000000..fabd526
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/DatePicker.cs
@@ -0,0 +1,130 @@
+using System;
+using ElmSharp;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// Extends the ElmSharp.DateTimeSelector class with functionality useful to renderer.
+ /// </summary>
+ public class DatePicker : DateTimeSelector
+ {
+ const string DateLayoutStyle = "date_layout";
+ const string DefaultEFLFormat = "%d/%b/%Y";
+ static readonly DateTime s_defaultMaximumDate = new DateTime(2037, 12, 31);
+ static readonly DateTime s_defaultMinimumDate = new DateTime(1970, 1, 1);
+ DateTime _date;
+ DateTime _maxDate;
+ DateTime _minDate;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DatePicker"/> class.
+ /// </summary>
+ /// <param name="parent">The parent EvasObject.</param>
+ public DatePicker(EvasObject parent) : base(parent)
+ {
+ SetFieldVisible(DateTimeFieldType.Hour, false);
+ Style = DateLayoutStyle;
+ ApplyDate(Date);
+ ApplyMinDate(s_defaultMinimumDate);
+ ApplyMaxDate(s_defaultMaximumDate);
+ //TODO use date format currently set on the platform
+ Format = DefaultEFLFormat;
+
+ DateTimeChanged += (sender, e) =>
+ {
+ Date = e.NewDate;
+ };
+ }
+
+ /// <summary>
+ /// Gets or sets the displayed date.
+ /// </summary>
+ public DateTime Date
+ {
+ get
+ {
+ return _date;
+ }
+ set
+ {
+ if (_date != value)
+ {
+ ApplyDate(value);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets of sets the highest date selectable for this <see cref="DatePicker"/>.
+ /// </summary>
+ /// <remarks>
+ /// Default value is 31st Dec, 2037.
+ /// </remarks>
+ public DateTime MaximumDate
+ {
+ get
+ {
+ return _maxDate;
+ }
+ set
+ {
+ if (_maxDate != value)
+ {
+ ApplyMaxDate(value);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets of sets the lowest date selectable for this <see cref="DatePicker"/>.
+ /// </summary>
+ /// <remarks>
+ /// Default value is 1st Jan, 1970.
+ /// </remarks>
+ public DateTime MinimumDate
+ {
+ get
+ {
+ return _minDate;
+ }
+ set
+ {
+ if (_minDate != value)
+ {
+ ApplyMinDate(value);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Sets the <c>DateTime</c> property according to the given <paramref name="date"/>.
+ /// </summary>
+ /// <param name="date">The date value to be applied to the date picker.</param>
+ void ApplyDate(DateTime date)
+ {
+ _date = date;
+ DateTime = date;
+ }
+
+ /// <summary>
+ /// Sets the <c>MaximumDateTime</c> property according to the given <paramref name="maxDate"/>.
+ /// </summary>
+ /// <param name="maxDate">The maximum date value to be applied to the date picker.</param>
+ void ApplyMaxDate(DateTime maxDate)
+ {
+ _maxDate = maxDate;
+ MaximumDateTime = maxDate;
+ }
+
+ /// <summary>
+ /// Sets the <c>MinimumDateTime</c> property according to the given <paramref name="minDate"/>.
+ /// </summary>
+ /// <param name="minDate">The minimum date value to be applied to the date picker.</param>
+ void ApplyMinDate(DateTime minDate)
+ {
+ _minDate = minDate;
+ MinimumDateTime = minDate;
+ }
+ }
+}
+
diff --git a/Xamarin.Forms.Platform.Tizen/Native/DateTimePickerDialog.cs b/Xamarin.Forms.Platform.Tizen/Native/DateTimePickerDialog.cs
new file mode 100644
index 0000000..64b1b3b
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/DateTimePickerDialog.cs
@@ -0,0 +1,100 @@
+using System;
+using ElmSharp;
+using EButton = ElmSharp.Button;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ public class DateTimePickerDialog : Dialog
+ {
+ DateTimeSelector _dateTimePicker;
+ EvasObject _parent;
+
+ /// <summary>
+ /// Creates a dialog window.
+ /// </summary>
+ public DateTimePickerDialog(EvasObject parent) : base(parent)
+ {
+ _parent = parent;
+ Initialize();
+ }
+
+ /// <summary>
+ /// Occurs when the date of this dialog has changed.
+ /// </summary>
+ public event EventHandler<DateChangedEventArgs> DateTimeChanged;
+
+ /// <summary>
+ /// Gets the <see cref="DateTimePicker"/> contained in this dialog.
+ /// </summary>
+ public DateTimeSelector DateTimePicker
+ {
+ get
+ {
+ return _dateTimePicker;
+ }
+ private set
+ {
+ if (_dateTimePicker != value)
+ {
+ ApplyDateTimePicker(value);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Creates date picker in dialog window.
+ /// </summary>
+ public void InitializeDatePicker(DateTime date, DateTime minimumDate, DateTime maximumDate)
+ {
+ var datePicker = new DatePicker(this)
+ {
+ Date = date,
+ MinimumDate = minimumDate,
+ MaximumDate = maximumDate
+ };
+ Content = DateTimePicker = datePicker;
+ }
+
+ /// <summary>
+ /// Creates time picker in dialog window.
+ /// </summary>
+ public void InitializeTimePicker(TimeSpan time, string format)
+ {
+ var timePicker = new TimePicker(this)
+ {
+ Time = time,
+ DateTimeFormat = format
+ };
+ Content = DateTimePicker = timePicker;
+ }
+
+ void ApplyDateTimePicker(DateTimeSelector dateTimePicker)
+ {
+ _dateTimePicker = dateTimePicker;
+ Content = _dateTimePicker;
+ }
+
+ void Initialize()
+ {
+ //TODO need to add internationalization support
+ PositiveButton = new EButton(_parent) { Text = "Set" };
+ PositiveButton.Clicked += (s, e) =>
+ {
+ DateTime oldDate = DateTimePicker.DateTime;
+ DateTimeChanged?.Invoke(this, new DateChangedEventArgs(oldDate, DateTimePicker.DateTime));
+ Hide();
+ };
+
+ //TODO need to add internationalization support
+ NegativeButton = new EButton(_parent) { Text = "Cancel" };
+ NegativeButton.Clicked += (s, e) =>
+ {
+ Hide();
+ };
+ BackButtonPressed += (object s, EventArgs e) =>
+ {
+ Hide();
+ };
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/Dialog.cs b/Xamarin.Forms.Platform.Tizen/Native/Dialog.cs
new file mode 100755
index 0000000..e616ff5
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/Dialog.cs
@@ -0,0 +1,276 @@
+using System;
+using ElmSharp;
+using EButton = ElmSharp.Button;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// Base class for Dialogs.
+ /// A dialog is a small window that prompts the user to make a decision or enter additional information.
+ /// </summary>
+ public class Dialog : Popup
+ {
+ EButton _positiveButton;
+ EButton _neutralButton;
+ EButton _negativeButton;
+ EvasObject _content;
+ string _title;
+
+ /// <summary>
+ /// Creates a dialog window that uses the default dialog theme.
+ /// </summary>
+ public Dialog(EvasObject parent) : base(parent)
+ {
+ Initialize();
+ }
+
+ /// <summary>
+ /// Occurs when the hardware Back button is pressed.
+ /// </summary>
+ public event EventHandler BackButtonPressed;
+
+ /// <summary>
+ /// Occurs whenever the dialog is first displayed.
+ /// </summary>
+ public event EventHandler Shown;
+
+ /// <summary>
+ /// Enumerates the three valid positions of a dialog button.
+ /// </summary>
+ enum ButtonPosition
+ {
+ Positive,
+ Neutral,
+ Negative
+ }
+
+ /// <summary>
+ /// Gets or sets the title of the dialog
+ /// </summary>
+ public string Title
+ {
+ get
+ {
+ return _title;
+ }
+ set
+ {
+ if (_title != value)
+ {
+ ApplyTitle(value);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the content to display in that dialog.
+ /// </summary>
+ public EvasObject Content
+ {
+ get
+ {
+ return _content;
+ }
+ set
+ {
+ if (_content != value)
+ {
+ ApplyContent(value);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the positive button used in the dialog
+ /// </summary>
+ public EButton PositiveButton
+ {
+ get
+ {
+ return _positiveButton;
+ }
+ set
+ {
+ if (_positiveButton != value)
+ {
+ ApplyButton(ButtonPosition.Positive, value);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the neutral button used in the dialog
+ /// </summary>
+ public EButton NeutralButton
+ {
+ get
+ {
+ return _neutralButton;
+ }
+ set
+ {
+ if (_neutralButton != value)
+ {
+ ApplyButton(ButtonPosition.Neutral, value);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the negative button used in the dialog
+ /// </summary>
+ public EButton NegativeButton
+ {
+ get
+ {
+ return _negativeButton;
+ }
+ set
+ {
+ if (_negativeButton != value)
+ {
+ ApplyButton(ButtonPosition.Negative, value);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Starts the dialog and displays it on screen.
+ /// </summary>
+ public new void Show()
+ {
+ base.Show();
+ Shown?.Invoke(this, EventArgs.Empty);
+ }
+
+ /// <summary>
+ /// Handles the disposing of a dialog widget.
+ /// </summary>
+ protected override void OnUnrealize()
+ {
+ _content?.Unrealize();
+
+ ApplyButton(ButtonPosition.Positive, null);
+ ApplyButton(ButtonPosition.Neutral, null);
+ ApplyButton(ButtonPosition.Negative, null);
+ ApplyContent(null);
+
+ UngrabBackKey();
+
+ base.OnUnrealize();
+ }
+
+ /// <summary>
+ /// Called when the dialog is shown.
+ /// </summary>
+ /// <remarks>When shown, the dialog will register itself for the back key press event handling.</remarks>
+ protected virtual void OnShown()
+ {
+ GrabBackKey();
+ }
+
+ /// <summary>
+ /// Called when the dialog is dismissed.
+ /// </summary>
+ /// <remarks>When dismissed, the dialog will unregister itself from the back key press event handling.</remarks>
+ protected virtual void OnDismissed()
+ {
+ UngrabBackKey();
+ }
+
+ /// <summary>
+ /// Handles the initialization process.
+ /// </summary>
+ /// <remarks>Creates handlers for vital events</remarks>
+ void Initialize()
+ {
+ // Adds a handler for the Dismissed event.
+ // In effect, unregisters this instance from being affected by the hardware back key presses.
+ Dismissed += (s, e) =>
+ {
+ OnDismissed();
+ };
+
+ // Adds a handler for the Shown event.
+ // In effect, registers this instance to be affected by the hardware back key presses.
+ Shown += (s, e) =>
+ {
+ OnShown();
+ };
+
+ // Adds a handler for the KeyUp event.
+ // The handler checks whether the key just pressed is a back key
+ // and if that is the case, invokes the back button press handler of this instance.
+ KeyUp += (s, e) =>
+ {
+ if (e.KeyName == EvasKeyEventArgs.PlatformBackButtonName)
+ BackButtonPressed?.Invoke(this, EventArgs.Empty);
+ };
+ }
+
+ /// <summary>
+ /// Changes the dialog title.
+ /// </summary>
+ /// <param name="title">New dialog title.</param>
+ void ApplyTitle(string title)
+ {
+ _title = title;
+
+ SetPartText("title,text", _title);
+ }
+
+ /// <summary>
+ /// Puts the button in one of the three available slots.
+ /// </summary>
+ /// <param name="position">The slot to be occupied by the button expressed as a <see cref="ButtonPosition"/></param>
+ /// <param name="button">The new button.</param>
+ void ApplyButton(ButtonPosition position, EButton button)
+ {
+ switch (position)
+ {
+ case ButtonPosition.Positive:
+ _positiveButton = button;
+ SetPartContent("button3", _positiveButton, true);
+ break;
+
+ case ButtonPosition.Neutral:
+ _neutralButton = button;
+ SetPartContent("button2", _neutralButton, true);
+ break;
+
+ case ButtonPosition.Negative:
+ _negativeButton = button;
+ SetPartContent("button1", _negativeButton, true);
+ break;
+ }
+ }
+
+ /// <summary>
+ /// Updates the content of the dialog.
+ /// </summary>
+ /// <param name="content">New dialog content.</param>
+ void ApplyContent(EvasObject content)
+ {
+ _content = content;
+
+ SetPartContent("default", _content, true);
+ }
+
+ /// <summary>
+ /// Registers this instance to be affected by pressing the hardware back key.
+ /// </summary>
+ void GrabBackKey()
+ {
+ KeyGrab(EvasKeyEventArgs.PlatformBackButtonName, true);
+ }
+
+ /// <summary>
+ /// Unregisters this instance from being affected by pressing the hardware back key.
+ /// </summary>
+ void UngrabBackKey()
+ {
+ KeyUngrab(EvasKeyEventArgs.PlatformBackButtonName);
+ }
+
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/DisplayOrientations.cs b/Xamarin.Forms.Platform.Tizen/Native/DisplayOrientations.cs
new file mode 100644
index 0000000..efb0952
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/DisplayOrientations.cs
@@ -0,0 +1,36 @@
+using System;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// Enumeration for the orientation of a rectangular screen.
+ /// </summary>
+ [Flags]
+ public enum DisplayOrientations
+ {
+ /// <summary>
+ /// No display orientation is specified.
+ /// </summary>
+ None = 0,
+
+ /// <summary>
+ /// The display is oriented in a natural position.
+ /// </summary>
+ Portrait = 1,
+
+ /// <summary>
+ /// The display's left side is at the top.
+ /// </summary>
+ Landscape = 2,
+
+ /// <summary>
+ /// The display is upside down.
+ /// </summary>
+ PortraitFlipped = 4,
+
+ /// <summary>
+ /// The display's right side is at the top.
+ /// </summary>
+ LandscapeFlipped = 8
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/Entry.cs b/Xamarin.Forms.Platform.Tizen/Native/Entry.cs
new file mode 100644
index 0000000..808155c
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/Entry.cs
@@ -0,0 +1,396 @@
+using System;
+using ElmSharp;
+using EEntry = ElmSharp.Entry;
+using EColor = ElmSharp.Color;
+using ESize = ElmSharp.Size;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// Extends the Entry control, providing basic formatting features,
+ /// i.e. font color, size, placeholder.
+ /// </summary>
+ public class Entry : EEntry, IMeasurable
+ {
+ /// <summary>
+ /// Holds the formatted text of the entry.
+ /// </summary>
+ readonly Span _span = new Span();
+
+ /// <summary>
+ /// Holds the formatted text of the placeholder.
+ /// </summary>
+ readonly Span _placeholderSpan = new Span();
+
+ /// <summary>
+ /// Helps to detect whether the text change was initiated by the user
+ /// or via the Text property.
+ /// </summary>
+ int _changedByUserCallbackDepth;
+
+ /// <summary>
+ /// The type of the keyboard used by the entry.
+ /// </summary>
+ Keyboard _keyboard;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Xamarin.Forms.Platform.Tizen.Native.Entry"/> class.
+ /// </summary>
+ /// <param name="parent">Parent evas object.</param>
+ public Entry(EvasObject parent) : base(parent)
+ {
+ Scrollable = true;
+
+ ChangedByUser += (s, e) =>
+ {
+ _changedByUserCallbackDepth++;
+
+ Text = GetInternalText();
+
+ _changedByUserCallbackDepth--;
+ };
+
+ ApplyKeyboard(Keyboard.Normal);
+ }
+
+ /// <summary>
+ /// Occurs when the text has changed.
+ /// </summary>
+ public event EventHandler<TextChangedEventArgs> TextChanged;
+
+ /// <summary>
+ /// Gets or sets the text.
+ /// </summary>
+ /// <value>The text.</value>
+ public override string Text
+ {
+ get
+ {
+ return _span.Text;
+ }
+
+ set
+ {
+ if (value != _span.Text)
+ {
+ var old = _span.Text;
+ _span.Text = value;
+ ApplyTextAndStyle();
+ Device.StartTimer(TimeSpan.FromTicks(1), () =>
+ {
+ TextChanged?.Invoke(this, new TextChangedEventArgs(old, value));
+ return false;
+ });
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the color of the text.
+ /// </summary>
+ /// <value>The color of the text.</value>
+ public EColor TextColor
+ {
+ get
+ {
+ return _span.ForegroundColor;
+ }
+
+ set
+ {
+ if (!_span.ForegroundColor.Equals(value))
+ {
+ _span.ForegroundColor = value;
+ ApplyTextAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the font family of the text and the placeholder.
+ /// </summary>
+ /// <value>The font family of the text and the placeholder.</value>
+ public string FontFamily
+ {
+ get
+ {
+ return _span.FontFamily;
+ }
+
+ set
+ {
+ if (value != _span.FontFamily)
+ {
+ _span.FontFamily = value;
+ ApplyTextAndStyle();
+
+ _placeholderSpan.FontFamily = value;
+ ApplyPlaceholderAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the font attributes of the text and the placeholder.
+ /// </summary>
+ /// <value>The font attributes of the text and the placeholder.</value>
+ public FontAttributes FontAttributes
+ {
+ get
+ {
+ return _span.FontAttributes;
+ }
+
+ set
+ {
+ if (value != _span.FontAttributes)
+ {
+ _span.FontAttributes = value;
+ ApplyTextAndStyle();
+
+ _placeholderSpan.FontAttributes = value;
+ ApplyPlaceholderAndStyle();
+ }
+ }
+ }
+
+
+ /// <summary>
+ /// Gets or sets the size of the font of both text and placeholder.
+ /// </summary>
+ /// <value>The size of the font of both text and placeholder.</value>
+ public double FontSize
+ {
+ get
+ {
+ return _span.FontSize;
+ }
+
+ set
+ {
+ if (value != _span.FontSize)
+ {
+ _span.FontSize = value;
+ ApplyTextAndStyle();
+
+ _placeholderSpan.FontSize = value;
+ ApplyPlaceholderAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the horizontal text alignment of both text and placeholder.
+ /// </summary>
+ /// <value>The horizontal text alignment of both text and placeholder.</value>
+ public TextAlignment HorizontalTextAlignment
+ {
+ get
+ {
+ return _span.HorizontalTextAlignment;
+ }
+
+ set
+ {
+ if (value != _span.HorizontalTextAlignment)
+ {
+ _span.HorizontalTextAlignment = value;
+ ApplyTextAndStyle();
+
+ _placeholderSpan.HorizontalTextAlignment = value;
+ ApplyPlaceholderAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the keyboard type used by the entry.
+ /// </summary>
+ /// <value>The keyboard type.</value>
+ public Keyboard Keyboard
+ {
+ get
+ {
+ return _keyboard;
+ }
+
+ set
+ {
+ if (value != _keyboard)
+ {
+ ApplyKeyboard(value);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the placeholder's text.
+ /// </summary>
+ /// <value>The placeholder's text.</value>
+ public string Placeholder
+ {
+ get
+ {
+ return _placeholderSpan.Text;
+ }
+
+ set
+ {
+ if (value != _placeholderSpan.Text)
+ {
+ _placeholderSpan.Text = value;
+ ApplyPlaceholderAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the color of the placeholder's text.
+ /// </summary>
+ /// <value>The color of the placeholder's text.</value>
+ public EColor PlaceholderColor
+ {
+ get
+ {
+ return _placeholderSpan.ForegroundColor;
+ }
+
+ set
+ {
+ if (!_placeholderSpan.ForegroundColor.Equals(value))
+ {
+ _placeholderSpan.ForegroundColor = value;
+ ApplyPlaceholderAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Implementation of the IMeasurable.Measure() method.
+ /// </summary>
+ public ESize Measure(int availableWidth, int availableHeight)
+ {
+ var originalSize = Geometry;
+ // resize the control using the whole available width
+ Resize(availableWidth, originalSize.Height);
+
+ ESize rawSize;
+ ESize formattedSize;
+ var edjeTextBlock = EdjeObject["elm.guide"];
+
+ // if there's no text, but there's a placeholder, use it for measurements
+ if (string.IsNullOrEmpty(Text) && !string.IsNullOrEmpty(Placeholder) && edjeTextBlock != null)
+ {
+ rawSize = edjeTextBlock.TextBlockNativeSize;
+ formattedSize = edjeTextBlock.TextBlockFormattedSize;
+ }
+ else
+ {
+ // there's text in the entry, use it instead
+ rawSize = Native.TextHelper.GetRawTextBlockSize(this);
+ formattedSize = Native.TextHelper.GetFormattedTextBlockSize(this);
+ }
+
+ // restore the original size
+ Resize(originalSize.Width, originalSize.Height);
+
+ // Set bottom padding for lower case letters that have segments below the bottom line of text (g, j, p, q, y).
+ var verticalPadding = (int)Math.Ceiling(0.05 * FontSize);
+ var horizontalPadding = (int)Math.Ceiling(0.2 * FontSize);
+ rawSize.Height += verticalPadding;
+ formattedSize.Height += verticalPadding;
+ formattedSize.Width += horizontalPadding;
+
+ // if the raw text width is larger than available width, we use the available width,
+ // while height is set to the smallest height value
+ if (rawSize.Width > availableWidth)
+ {
+ return new ESize
+ {
+ Width = availableWidth,
+ Height = Math.Min(formattedSize.Height, Math.Max(rawSize.Height, availableHeight)),
+ };
+ }
+ else
+ {
+ // width is fine, return the formatted text size
+ return formattedSize;
+ }
+ }
+
+ /// <summary>
+ /// Applies entry's text and its style.
+ /// </summary>
+ void ApplyTextAndStyle()
+ {
+ SetInternalTextAndStyle(_span.GetDecoratedText(), _span.GetStyle());
+ }
+
+ /// <summary>
+ /// Sets entry's internal text and its style.
+ /// </summary>
+ /// <param name="formattedText">Formatted text, supports HTML tags.</param>
+ /// <param name="textStyle">Style applied to the formattedText.</param>
+ void SetInternalTextAndStyle(string formattedText, string textStyle)
+ {
+ if (_changedByUserCallbackDepth == 0)
+ {
+ base.Text = formattedText;
+ base.TextStyle = textStyle;
+ }
+ }
+
+ /// <summary>
+ /// Gets the internal text representation of the entry.
+ /// </summary>
+ /// <returns>The internal text representation.</returns>
+ string GetInternalText()
+ {
+ return Entry.ConvertMarkupToUtf8(base.Text);
+ }
+
+ /// <summary>
+ /// Applies the keyboard type to be used by the entry.
+ /// </summary>
+ /// <param name="keyboard">Keyboard type to be used.</param>
+ void ApplyKeyboard(Keyboard keyboard)
+ {
+ SetInternalKeyboard(_keyboard = keyboard);
+ }
+
+ /// <summary>
+ /// Configures the ElmSharp.Entry with specified keyboard type and displays
+ /// the keyboard automatically unless the provided type is Keyboard.None.
+ /// </summary>
+ /// <param name="keyboard">Keyboard type to be used.</param>
+ void SetInternalKeyboard(Keyboard keyboard)
+ {
+ if (keyboard == Keyboard.None)
+ {
+ SetInputPanelEnabled(false);
+ }
+ else
+ {
+ SetInputPanelEnabled(true);
+ SetInputPanelLayout((InputPanelLayout)keyboard);
+ }
+ }
+
+ /// <summary>
+ /// Applies placeholders's text and its style.
+ /// </summary>
+ void ApplyPlaceholderAndStyle()
+ {
+ SetInternalPlaceholderAndStyle(_placeholderSpan.GetMarkupText());
+ }
+
+ /// <summary>
+ /// Sets placeholder's internal text and style.
+ /// </summary>
+ /// <param name="markupText">Markup text to be used as a placeholder.</param>
+ void SetInternalPlaceholderAndStyle(string markupText)
+ {
+ SetPartText("guide", markupText ?? "");
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/FormattedString.cs b/Xamarin.Forms.Platform.Tizen/Native/FormattedString.cs
new file mode 100644
index 0000000..f782efd
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/FormattedString.cs
@@ -0,0 +1,128 @@
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// Represents a text with attributes applied to some parts.
+ /// </summary>
+ /// <remarks>
+ /// Formatted string consists of spans that represent text segments with various attributes applied.
+ /// </remarks>
+ public class FormattedString
+ {
+ /// <summary>
+ /// A flag indicating whether the instance contains just a plain string without any formatting.
+ /// </summary>
+ /// <remarks>
+ /// <c>true</c> if the instance contains an unformatted string.
+ /// </remarks>
+ readonly bool _just_string;
+
+ /// <summary>
+ /// Holds the unformatted string.
+ /// </summary>
+ /// <remarks>
+ /// The contents of this field are accurate if and only if the _just_string flag is set.
+ /// </remarks>
+ readonly string _string;
+
+ /// <summary>
+ /// Holds the collection of span elements.
+ /// </summary>
+ /// <remarks>
+ /// Span elements are basically chunks of text with uniform formatting.
+ /// </remarks>
+ readonly ObservableCollection<Span> _spans;
+
+ /// <summary>
+ /// Returns the collection of span elements.
+ /// </summary>
+ public IList<Span> Spans { get { return _spans; } }
+
+ /// <summary>
+ /// Creates a new FormattedString instance with an empty string.
+ /// </summary>
+ public FormattedString()
+ {
+ _just_string = false;
+ _spans = new ObservableCollection<Span>();
+ }
+
+ /// <summary>
+ /// Creates a new FormattedString instance based on given <c>str</c>.
+ /// </summary>
+ /// <param name="str">
+ /// A string used to make a new FormattedString instance.
+ /// </param>
+ public FormattedString(string str)
+ {
+ _just_string = true;
+ _string = str;
+ }
+
+ /// <summary>
+ /// Returns the plain text of the FormattedString as an unformatted string.
+ /// </summary>
+ /// <returns>
+ /// The text content of the FormattedString without any format applied.
+ /// </returns>
+ public override string ToString()
+ {
+ if (_just_string)
+ {
+ return _string;
+ }
+ else
+ {
+ return string.Concat(from span in this.Spans select span.Text);
+ }
+ }
+
+ /// <summary>
+ /// Returns the markup text representation of the FormattedString instance.
+ /// </summary>
+ /// <returns>The string containing a markup text.</returns>
+ internal string ToMarkupString()
+ {
+ if (_just_string)
+ {
+ return _string;
+ }
+ else
+ {
+ return string.Concat(from span in this.Spans select span.GetMarkupText());
+ }
+ }
+
+ /// <summary>
+ /// Casts the FormattedString to a string.
+ /// </summary>
+ /// <param name="formatted">The FormattedString instance which will be used for the conversion.</param>
+ public static explicit operator string (FormattedString formatted)
+ {
+ return formatted.ToString();
+ }
+
+ /// <summary>
+ /// Casts the string to a FormattedString.
+ /// </summary>
+ /// <param name="text">The text which will be put in a new FormattedString instance.</param>
+ public static implicit operator FormattedString(string text)
+ {
+ return new FormattedString(text);
+ }
+
+ /// <summary>
+ /// Casts the Span to a FormattedString.
+ /// </summary>
+ /// <param name="span">The span which will be used for the conversion.</param>
+ public static implicit operator FormattedString(Span span)
+ {
+ return new FormattedString()
+ {
+ Spans = { span }
+ };
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/IContainable.cs b/Xamarin.Forms.Platform.Tizen/Native/IContainable.cs
new file mode 100644
index 0000000..dfee0cb
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/IContainable.cs
@@ -0,0 +1,16 @@
+using System.Collections.Generic;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// Interface defining methods for managing elements of the container.
+ /// </summary>
+ /// <typeparam name="T">The type of element that can be added to the container.</typeparam>
+ public interface IContainable<T>
+ {
+ /// <summary>
+ /// The children collection of an element.
+ /// </summary>
+ IList<T> Children { get; }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/IMeasurable.cs b/Xamarin.Forms.Platform.Tizen/Native/IMeasurable.cs
new file mode 100644
index 0000000..13ee1c1
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/IMeasurable.cs
@@ -0,0 +1,20 @@
+using ESize = ElmSharp.Size;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// Interface of the controls which can measure their size taking into
+ /// account the available area.
+ /// </summary>
+ public interface IMeasurable
+ {
+ /// <summary>
+ /// Measures the size of the control in order to fit it into the
+ /// available area.
+ /// </summary>
+ /// <param name="availableWidth">Available width.</param>
+ /// <param name="availableHeight">Available height.</param>
+ /// <returns>Size of the control that fits the available area.</returns>
+ ESize Measure(int availableWidth, int availableHeight);
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/ITextable.cs b/Xamarin.Forms.Platform.Tizen/Native/ITextable.cs
new file mode 100644
index 0000000..876646e
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/ITextable.cs
@@ -0,0 +1,68 @@
+using EColor = ElmSharp.Color;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// Interface defining properties of formattable text.
+ /// </summary>
+ public interface ITextable
+ {
+ /// <summary>
+ /// Get or sets the formatted text.
+ /// </summary>
+ FormattedString FormattedText { get; set; }
+
+ /// <summary>
+ /// Gets or sets the text.
+ /// </summary>
+ string Text { get; set; }
+
+ /// <summary>
+ /// Gets or sets the color for the text.
+ /// </summary>
+ EColor TextColor { get; set; }
+
+ /// <summary>
+ /// Gets or sets the background color for the text.
+ /// </summary>
+ EColor TextBackgroundColor { get; set; }
+
+ /// <summary>
+ /// Gets or sets the font family for the text.
+ /// </summary>
+ string FontFamily { get; set; }
+
+ /// <summary>
+ /// Gets or sets the font attributes for the text.
+ /// See <see cref="FontAttributes"/> for information about FontAttributes.
+ /// </summary>
+ FontAttributes FontAttributes { get; set; }
+
+ /// <summary>
+ /// Gets or sets the font size for the text.
+ /// </summary>
+ double FontSize { get; set; }
+
+ /// <summary>
+ /// Gets or sets the horizontal alignment mode for the text.
+ /// See <see cref="TextAlignment"/> for information about TextAlignment.
+ /// </summary>
+ TextAlignment HorizontalTextAlignment { get; set; }
+
+ /// <summary>
+ /// Gets or sets the vertical alignment mode for the text.
+ /// See <see cref="TextAlignment"/> for information about TextAlignment.
+ /// </summary>
+ TextAlignment VerticalTextAlignment { get; set; }
+
+ /// <summary>
+ /// Gets or sets the value that indicates whether the text has underline.
+ /// </summary>
+ bool Underline { get; set; }
+
+ /// <summary>
+ /// Gets or sets the value that indicates whether the text has strike line though it.
+ /// </summary>
+ bool Strikethrough { get; set; }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/Image.cs b/Xamarin.Forms.Platform.Tizen/Native/Image.cs
new file mode 100644
index 0000000..7321c5b
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/Image.cs
@@ -0,0 +1,124 @@
+using System.Threading.Tasks;
+using ElmSharp;
+using EImage = ElmSharp.Image;
+using ESize = ElmSharp.Size;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// Extends the ElmSharp.Image class with functionality useful to renderer.
+ /// </summary>
+ public class Image : EImage, IMeasurable
+ {
+ Aspect _aspect;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Xamarin.Forms.Platform.Tizen.Native.Image"/> class.
+ /// </summary>
+ /// <param name="parent">The parent EvasObject.</param>
+ public Image(EvasObject parent) : base(parent)
+ {
+ IsScaling = true;
+ CanScaleUp = true;
+ CanScaleDown = true;
+
+ ApplyAspect(Aspect.AspectFit);
+ }
+
+ /// <summary>
+ /// Gets or sets the image aspect ratio preserving option.
+ /// </summary>
+ /// <value>The aspect option.</value>
+ public Aspect Aspect
+ {
+ get
+ {
+ return _aspect;
+ }
+
+ set
+ {
+ if (_aspect != value)
+ {
+ ApplyAspect(value);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Loads image data from the given <see cref="Xamarin.Forms.ImageSource"/> asynchronously.
+ /// </summary>
+ /// <returns>A task which will be completed when image data is loaded.</returns>
+ /// <param name="source">Image source specifying from where the image data has to be loaded.</param>
+ public Task<bool> LoadFromImageSourceAsync(ImageSource source)
+ {
+ IImageSourceHandler handler;
+ if (source != null && (handler = Registrar.Registered.GetHandler<IImageSourceHandler>(source.GetType())) != null)
+ {
+ return handler.LoadImageAsync(this, source);
+ }
+ return Task.FromResult<bool>(false);
+ }
+
+ /// <summary>
+ /// Implements the <see cref="Xamarin.Forms.Platform.Tizen.Native.IMeasurable"/> interface.
+ /// </summary>
+ /// <param name="availableWidth">Available width.</param>
+ /// <param name="availableHeight">Available height.</param>
+ public ESize Measure(int availableWidth, int availableHeight)
+ {
+ var imageSize = ObjectSize;
+
+ var size = new ESize()
+ {
+ Width = imageSize.Width,
+ Height = imageSize.Height,
+ };
+
+ if (0 != availableWidth && 0 != availableHeight
+ && (imageSize.Width > availableWidth || imageSize.Height > availableHeight))
+ {
+ // when available size is limited and insufficient for the image ...
+ double imageRatio = (double)imageSize.Width / (double)imageSize.Height;
+ double availableRatio = (double)availableWidth / (double)availableHeight;
+ // depending on the relation between availableRatio and imageRatio, copy the availableWidth or availableHeight
+ // and calculate the size which preserves the image ratio, but does not exceed the available size
+ size.Width = availableRatio > imageRatio ? imageSize.Width * availableHeight / imageSize.Height : availableWidth;
+ size.Height = availableRatio > imageRatio ? availableHeight : imageSize.Height * availableWidth / imageSize.Width;
+ }
+
+ return size;
+ }
+
+ /// <summary>
+ /// Sets the <c>IsFixedAspect</c> and <c>CanFillOutside</c> properties according to the given <paramref name="aspect"/>.
+ /// </summary>
+ /// <param name="aspect">The aspect setting to be applied to the image.</param>
+ void ApplyAspect(Aspect aspect)
+ {
+ _aspect = aspect;
+
+ switch (_aspect)
+ {
+ case Aspect.AspectFit:
+ IsFixedAspect = true;
+ CanFillOutside = false;
+ break;
+
+ case Aspect.AspectFill:
+ IsFixedAspect = true;
+ CanFillOutside = true;
+ break;
+
+ case Aspect.Fill:
+ IsFixedAspect = false;
+ CanFillOutside = false;
+ break;
+
+ default:
+ Log.Warn("Invalid Aspect value: {0}", _aspect);
+ break;
+ }
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/Keyboard.cs b/Xamarin.Forms.Platform.Tizen/Native/Keyboard.cs
new file mode 100644
index 0000000..4223ca0
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/Keyboard.cs
@@ -0,0 +1,84 @@
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// Keyboard layout type on entry control.
+ /// </summary>
+ public enum Keyboard
+ {
+ /// <summary>
+ /// Disable Keyboard
+ /// </summary>
+ None = -1,
+
+ /// <summary>
+ /// Keyboard layout type default.
+ /// </summary>
+ Normal,
+
+ /// <summary>
+ /// Keyboard layout type number.
+ /// </summary>
+ Number,
+
+ /// <summary>
+ /// Keyboard layout type email.
+ /// </summary>
+ Email,
+
+ /// <summary>
+ /// Keyboard layout type url.
+ /// </summary>
+ Url,
+
+ /// <summary>
+ /// Keyboard layout type phone.
+ /// </summary>
+ PhoneNumber,
+
+ /// <summary>
+ /// Keyboard layout type ip.
+ /// </summary>
+ Ip,
+
+ /// <summary>
+ /// Keyboard layout type month.
+ /// </summary>
+ Month,
+
+ /// <summary>
+ /// Keyboard layout type number.
+ /// </summary>
+ NumberOnly,
+
+ /// <summary>
+ /// Keyboard layout type error type. Do not use it directly!
+ /// </summary>
+ Invalid,
+
+ /// <summary>
+ /// Keyboard layout type hexadecimal.
+ /// </summary>
+ Hex,
+
+ /// <summary>
+ /// Keyboard layout type terminal type, esc, alt, ctrl, etc.
+ /// </summary>
+ Terminal,
+
+ /// <summary>
+ /// Keyboard layout type password.
+ /// </summary>
+ Password,
+
+ /// <summary>
+ /// Keyboard layout type date and time.
+ /// </summary>
+ DateTime,
+
+ /// <summary>
+ /// Keyboard layout type emoticons.
+ /// </summary>
+ Emoticon
+ }
+}
+
diff --git a/Xamarin.Forms.Platform.Tizen/Native/Label.cs b/Xamarin.Forms.Platform.Tizen/Native/Label.cs
new file mode 100644
index 0000000..cba2ba2
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/Label.cs
@@ -0,0 +1,347 @@
+using System;
+using ElmSharp;
+using ELabel = ElmSharp.Label;
+using EColor = ElmSharp.Color;
+using ESize = ElmSharp.Size;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// The Label class extends <c>ElmSharp.Label</c> to be better suited for Xamarin renderers.
+ /// Mainly the formatted text support.
+ /// </summary>
+ public class Label : ELabel, ITextable, IMeasurable
+ {
+ /// <summary>
+ /// The _span holds the content of the label.
+ /// </summary>
+ readonly Span _span = new Span();
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Xamarin.Forms.Platform.Tizen.Native.Label"/> class.
+ /// </summary>
+ /// <param name="parent">Parent evas object.</param>
+ public Label(EvasObject parent) : base(parent)
+ {
+ }
+
+ /// <summary>
+ /// Get or sets the formatted text.
+ /// </summary>
+ /// <remarks>Setting <c>FormattedText</c> changes the value of the <c>Text</c> property.</remarks>
+ /// <value>The formatted text.</value>
+ public FormattedString FormattedText
+ {
+ get
+ {
+ return _span.FormattedText;
+ }
+
+ set
+ {
+ if (value != _span.FormattedText)
+ {
+ _span.FormattedText = value;
+ ApplyTextAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the text.
+ /// </summary>
+ /// <remarks>Setting <c>Text</c> overwrites the value of the <c>FormattedText</c> property too.</remarks>
+ /// <value>The content of the label.</value>
+ public override string Text
+ {
+ get
+ {
+ return _span.Text;
+ }
+
+ set
+ {
+ if (value != _span.Text)
+ {
+ _span.Text = value;
+ ApplyTextAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the color of the formatted text.
+ /// </summary>
+ /// <value>The color of the text.</value>
+ public EColor TextColor
+ {
+ get
+ {
+ return _span.ForegroundColor;
+ }
+
+ set
+ {
+ if (!_span.ForegroundColor.Equals(value))
+ {
+ _span.ForegroundColor = value;
+ ApplyTextAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the background color for the text.
+ /// </summary>
+ /// <value>The color of the label's background.</value>
+ public EColor TextBackgroundColor
+ {
+ get
+ {
+ return _span.BackgroundColor;
+ }
+
+ set
+ {
+ if (!_span.BackgroundColor.Equals(value))
+ {
+ _span.BackgroundColor = value;
+ ApplyTextAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the font family for the text.
+ /// </summary>
+ /// <value>The font family.</value>
+ public string FontFamily
+ {
+ get
+ {
+ return _span.FontFamily;
+ }
+
+ set
+ {
+ if (value != _span.FontFamily)
+ {
+ _span.FontFamily = value;
+ ApplyTextAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the font attributes.
+ /// </summary>
+ /// <value>The font attributes.</value>
+ public FontAttributes FontAttributes
+ {
+ get
+ {
+ return _span.FontAttributes;
+ }
+
+ set
+ {
+ if (value != _span.FontAttributes)
+ {
+ _span.FontAttributes = value;
+ ApplyTextAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the font size for the text.
+ /// </summary>
+ /// <value>The size of the font.</value>
+ public double FontSize
+ {
+ get
+ {
+ return _span.FontSize;
+ }
+
+ set
+ {
+ if (value != _span.FontSize)
+ {
+ _span.FontSize = value;
+ ApplyTextAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the line wrap option.
+ /// </summary>
+ /// <value>The line break mode.</value>
+ public LineBreakMode LineBreakMode
+ {
+ get
+ {
+ return _span.LineBreakMode;
+ }
+
+ set
+ {
+ if (value != _span.LineBreakMode)
+ {
+ _span.LineBreakMode = value;
+ ApplyTextAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the horizontal text alignment.
+ /// </summary>
+ /// <value>The horizontal text alignment.</value>
+ public TextAlignment HorizontalTextAlignment
+ {
+ get
+ {
+ return _span.HorizontalTextAlignment;
+ }
+
+ set
+ {
+ if (value != _span.HorizontalTextAlignment)
+ {
+ _span.HorizontalTextAlignment = value;
+ ApplyTextAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the vertical text alignment.
+ /// </summary>
+ /// <value>The vertical text alignment.</value>
+ public TextAlignment VerticalTextAlignment
+ {
+ get
+ {
+ return _span.VerticalTextAlignment;
+ }
+
+ set
+ {
+ if (value != _span.VerticalTextAlignment)
+ {
+ _span.VerticalTextAlignment = value;
+ ApplyTextAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the value that indicates whether the text is underlined.
+ /// </summary>
+ /// <value><c>true</c> if the text is underlined.</value>
+ public bool Underline
+ {
+ get
+ {
+ return _span.Underline;
+ }
+
+ set
+ {
+ if (value != _span.Underline)
+ {
+ _span.Underline = value;
+ ApplyTextAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the value that indicates whether the text is striked out.
+ /// </summary>
+ /// <value><c>true</c> if the text is striked out.</value>
+ public bool Strikethrough
+ {
+ get
+ {
+ return _span.Strikethrough;
+ }
+
+ set
+ {
+ if (value != _span.Strikethrough)
+ {
+ _span.Strikethrough = value;
+ ApplyTextAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Implements <see cref="Xamarin.Forms.Platform.Tizen.Native.IMeasurable"/> to provide a desired size of the label.
+ /// </summary>
+ /// <param name="availableWidth">Available width.</param>
+ /// <param name="availableHeight">Available height.</param>
+ /// <returns>Size of the control that fits the available area.</returns>
+ public ESize Measure(int availableWidth, int availableHeight)
+ {
+ var size = Geometry;
+
+ Resize(availableWidth, size.Height);
+
+ var rawSize = Native.TextHelper.GetRawTextBlockSize(this);
+ var formattedSize = Native.TextHelper.GetFormattedTextBlockSize(this);
+ Resize(size.Width, size.Height);
+
+ // Set bottom padding for lower case letters that have segments below the bottom line of text (g, j, p, q, y).
+ var verticalPadding = (int)Math.Ceiling(0.05 * FontSize);
+ var horizontalPadding = (int)Math.Ceiling(0.2 * FontSize);
+ rawSize.Height += verticalPadding;
+ formattedSize.Height += verticalPadding;
+ formattedSize.Width += horizontalPadding;
+
+ if (rawSize.Width > availableWidth)
+ {
+ return new ESize()
+ {
+ Width = formattedSize.Width,
+ Height = Math.Min(formattedSize.Height, Math.Max(rawSize.Height, availableHeight)),
+ };
+ }
+ else
+ {
+ return formattedSize;
+ }
+ }
+
+ void ApplyTextAndStyle()
+ {
+ SetInternalTextAndStyle(_span.GetDecoratedText(), _span.GetStyle());
+ }
+
+ void SetInternalTextAndStyle(string formattedText, string textStyle)
+ {
+ string emission = "elm,state,text,visible";
+
+ if (string.IsNullOrEmpty(formattedText))
+ {
+ formattedText = null;
+ textStyle = null;
+ emission = "elm,state,text,hidden";
+ }
+
+ base.Text = formattedText;
+
+ var textblock = EdjeObject["elm.text"];
+
+ if (textblock != null)
+ {
+ textblock.TextStyle = textStyle;
+ }
+
+ EdjeObject.EmitSignal(emission, "elm");
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/LayoutEventArgs.cs b/Xamarin.Forms.Platform.Tizen/Native/LayoutEventArgs.cs
new file mode 100644
index 0000000..6668f8d
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/LayoutEventArgs.cs
@@ -0,0 +1,55 @@
+using System;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// Holds information about size of the area which can be used for layout.
+ /// </summary>
+ public class LayoutEventArgs : EventArgs
+ {
+ /// <summary>
+ /// Whether or not the dimensions have changed.
+ /// </summary>
+ public bool HasChanged
+ {
+ get;
+ internal set;
+ }
+
+ /// <summary>
+ /// X coordinate of the layout area, relative to the main window.
+ /// </summary>
+ public int X
+ {
+ get;
+ internal set;
+ }
+
+ /// <summary>
+ /// Y coordinate of the layout area, relative to the main window.
+ /// </summary>
+ public int Y
+ {
+ get;
+ internal set;
+ }
+
+ /// <summary>
+ /// Width of the layout area.
+ /// </summary>
+ public int Width
+ {
+ get;
+ internal set;
+ }
+
+ /// <summary>
+ /// Height of the layout area.
+ /// </summary>
+ public int Height
+ {
+ get;
+ internal set;
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/LineBreakMode.cs b/Xamarin.Forms.Platform.Tizen/Native/LineBreakMode.cs
new file mode 100644
index 0000000..27253ad
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/LineBreakMode.cs
@@ -0,0 +1,43 @@
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// Enumerates values that describe options for line braking.
+ /// </summary>
+ public enum LineBreakMode
+ {
+ /// <summary>
+ /// Do not wrap text.
+ /// </summary>
+ NoWrap,
+
+ /// <summary>
+ /// Wrap at character boundaries.
+ /// </summary>
+ CharacterWrap,
+
+ /// <summary>
+ /// Wrap at word boundaries.
+ /// </summary>
+ WordWrap,
+
+ /// <summary>
+ /// Tries to wrap at word boundaries, and then wrap at a character boundary if the word is too long.
+ /// </summary>
+ MixedWrap,
+
+ /// <summary>
+ /// Truncate the head of text.
+ /// </summary>
+ HeadTruncation,
+
+ /// <summary>
+ /// Truncate the middle of text. This may be done, for example, by replacing it with an ellipsis.
+ /// </summary>
+ MiddleTruncation,
+
+ /// <summary>
+ /// Truncate the tail of text.
+ /// </summary>
+ TailTruncation
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/ListView.cs b/Xamarin.Forms.Platform.Tizen/Native/ListView.cs
new file mode 100644
index 0000000..d5531dc
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/ListView.cs
@@ -0,0 +1,601 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.ComponentModel;
+using ElmSharp;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// Type alias which identifies list of cells whose data model was transformed by Xamarin.
+ /// </summary>
+ using GroupList = TemplatedItemsList<ItemsView<Cell>, Cell>;
+
+ /// <summary>
+ /// Native ListView implementation for Xamarin renderer
+ /// </summary>
+ /// <remarks>
+ /// This internally uses GenList class.
+ /// One should note that it is optimized for displaying many elements which may be
+ /// unavailable at first. This means that only currently visible elements will be constructed.
+ /// Whenever element disappears from visible space its content is destroyed for time being.
+ /// This is carried out by so called Cell Handlers.
+ /// </remarks>
+ public class ListView : GenList
+ {
+ /// <summary>
+ /// ItemContext helper class. This represents the association between Xamarin.Forms.Cell and
+ /// native elements. It also stores useful context for them.
+ /// </summary>
+ public class ItemContext
+ {
+ public ItemContext()
+ {
+ Item = null;
+ Cell = null;
+ Renderer = null;
+ ListOfSubItems = null;
+ }
+
+ public GenListItem Item;
+ public Cell Cell;
+ public bool IsGroupItem;
+ public CellRenderer Renderer;
+ internal TemplatedItemsList<ItemsView<Cell>, Cell> ListOfSubItems;
+ }
+
+ /// <summary>
+ /// The item context list for each added element.
+ /// </summary>
+ readonly List<ItemContext> _itemContextList = new List<ItemContext>();
+
+ /// <summary>
+ /// Registered cell handlers.
+ /// </summary>
+ protected readonly IDictionary<Type, CellRenderer> _cellRendererCache = new Dictionary<Type, CellRenderer>();
+
+ /// <summary>
+ /// Registered group handlers.
+ /// </summary>
+ protected readonly IDictionary<Type, CellRenderer> _groupCellRendererCache = new Dictionary<Type, CellRenderer>();
+
+ /// <summary>
+ /// The header context.
+ /// </summary>
+ ItemContext _headerContext;
+
+ /// <summary>
+ /// The header element.
+ /// </summary>
+ VisualElement _headerElement;
+
+ /// <summary>
+ /// The footer context.
+ /// </summary>
+ ItemContext _footerContext;
+
+ /// <summary>
+ /// The footer element.
+ /// </summary>
+ VisualElement _footerElement;
+
+ /// <summary>
+ /// The item class for header and footer.
+ /// </summary>
+ GenItemClass _headerFooterItemClass = null;
+
+ /// <summary>
+ /// Gets or sets a value indicating whether this instance has grouping enabled.
+ /// </summary>
+ /// <value><c>true</c> if this instance has grouping enabled.</value>
+ public bool IsGroupingEnabled { get; set; }
+
+ /// <summary>
+ /// Constructor of ListView native control.
+ /// </summary>
+ /// <param name="parent">ElmSharp object which is parent of particular list view</param>
+ public ListView(EvasObject parent)
+ : base(parent)
+ {
+ ItemRealized += OnItemAppear;
+ ItemUnrealized += OnItemDisappear;
+ }
+
+ /// <summary>
+ /// Gets the item context based on Cell item.
+ /// </summary>
+ /// <returns>The item context.</returns>
+ /// <param name="cell">Cell for which context should be found.</param>
+ internal ItemContext GetItemContext(Cell cell)
+ {
+ if (cell == null)
+ {
+ return null;
+ }
+ else
+ {
+ return _itemContextList.Find(X => X.Cell == cell);
+ }
+ }
+
+ /// <summary>
+ /// Sets the HasUnevenRows property.
+ /// </summary>
+ /// <param name="hasUnevenRows">If <c>true</c>, the list will allow uneven sizes for its rows.</param>
+ public void SetHasUnevenRows(bool hasUnevenRows)
+ {
+ Homogeneous = !hasUnevenRows;
+ UpdateRealizedItems();
+ }
+
+ /// <summary>
+ /// Adds elements to the list and defines its presentation based on Cell type.
+ /// </summary>
+ /// <param name="_source">IEnumerable on Cell collection.</param>
+ /// <param name="beforeCell">Cell before which new items will be placed.
+ /// Null value may also be passed as this parameter, which results in appending new items to the end.
+ /// </param>
+ public void AddSource(IEnumerable _source, Cell beforeCell = null)
+ {
+ foreach (var data in _source)
+ {
+ GroupList groupList = data as GroupList;
+ if (groupList != null)
+ {
+ AddGroupItem(groupList, beforeCell);
+ foreach (var item in groupList)
+ {
+ AddItem(item as Cell, groupList.HeaderContent);
+ }
+ }
+ else
+ {
+ AddItem(data as Cell, null, beforeCell);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Deletes all items from a given group.
+ /// </summary>
+ /// <param name="group">Group of items to be deleted.</param>
+ internal void ResetGroup(GroupList group)
+ {
+ var items = _itemContextList.FindAll(x => x.ListOfSubItems == group && x.Cell != group.HeaderContent);
+ foreach (var item in items)
+ {
+ item.Item?.Delete();
+ }
+ }
+
+ /// <summary>
+ /// Adds items to the group.
+ /// </summary>
+ /// <param name="itemGroup">Group to which elements will be added.</param>
+ /// <param name="newItems">New list items to be added.</param>
+ /// <param name="cellBefore">A reference to the Cell already existing in a ListView.
+ /// Newly added cells will be put just before this cell.</param>
+ public void AddItemsToGroup(IEnumerable itemGroup, IEnumerable newItems, Cell cellBefore = null)
+ {
+ ItemContext groupCtx = GetItemContext((itemGroup as GroupList)?.HeaderContent);
+ if (groupCtx != null)
+ {
+ foreach (var item in newItems)
+ {
+ AddItem(item as Cell, groupCtx.Cell, cellBefore);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Removes the specified cells.
+ /// </summary>
+ /// <param name="cells">Cells to be removed.</param>
+ public void Remove(IEnumerable cells)
+ {
+ foreach (var data in cells)
+ {
+ var group = data as GroupList;
+ if (group != null)
+ {
+ ItemContext groupCtx = GetItemContext(group.HeaderContent);
+ Remove(groupCtx.ListOfSubItems);
+ groupCtx.Item.Delete();
+ }
+ else
+ {
+ ItemContext itemCtx = GetItemContext(data as Cell);
+ itemCtx?.Item?.Delete();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Scrolls the list to a specified cell.
+ /// </summary>
+ /// <remarks>
+ /// Different scrolling behaviors are also possible. The element may be positioned in the center,
+ /// top or bottom of the visible part of the list depending on the value of the <c>position</c> parameter.
+ /// </remarks>
+ /// <param name="cell">Cell which will be displayed after scrolling .</param>
+ /// <param name="position">This will defines scroll to behavior based on ScrollToPosition values.</param>
+ /// <param name="animated">If <c>true</c>, scrolling will be animated. Otherwise the cell will be moved instantaneously.</param>
+ public void ApplyScrollTo(Cell cell, ScrollToPosition position, bool animated)
+ {
+ GenListItem item = GetItemContext(cell)?.Item;
+ if (item != null)
+ this.ScrollTo(item, position.ToNative(), animated);
+ }
+
+ /// <summary>
+ /// Selects the specified cell.
+ /// </summary>
+ /// <param name="cell">Cell to be selected.</param>
+ public void ApplySelectedItem(Cell cell)
+ {
+ GenListItem item = GetItemContext(cell)?.Item;
+ if (item != null)
+ item.IsSelected = true;
+ }
+
+ /// <summary>
+ /// Sets the header.
+ /// </summary>
+ /// <param name="header">Header of the list.</param>
+ public void SetHeader(VisualElement header)
+ {
+ if (header == null)
+ {
+ if (HasHeader())
+ {
+ RemoveHeader();
+ }
+
+ return;
+ }
+
+ GenItemClass headerTemplate = GetHeaderFooterItemClass();
+
+ _headerElement = header;
+ if (HasHeader())
+ {
+ FirstItem.UpdateItemClass(headerTemplate, header);
+ }
+ else
+ {
+ _headerContext = new ItemContext();
+ _headerContext.Item = _itemContextList.Count > 0 ? InsertBefore(headerTemplate, header, FirstItem) : Append(headerTemplate, header);
+ _headerContext.Item.SelectionMode = GenListSelectionMode.None;
+ _headerContext.Item.Deleted += HeaderDeletedHandler;
+ _itemContextList.Insert(0, _headerContext);
+ }
+ }
+
+ /// <summary>
+ /// Sets the footer.
+ /// </summary>
+ /// <param name="footer">Footer of the list.</param>
+ public void SetFooter(VisualElement footer)
+ {
+ if (footer == null)
+ {
+ if (HasFooter())
+ {
+ RemoveFooter();
+ }
+ return;
+ }
+
+ GenItemClass footerTemplate = GetHeaderFooterItemClass();
+
+ _footerElement = footer;
+ if (HasFooter())
+ {
+ _footerContext.Item.UpdateItemClass(footerTemplate, footer);
+ }
+ else
+ {
+ _footerContext = new ItemContext();
+ _footerContext.Item = Append(footerTemplate, footer);
+ _footerContext.Item.SelectionMode = GenListSelectionMode.None;
+ _footerContext.Item.Deleted += FooterDeletedHandler;
+ _itemContextList.Add(_footerContext);
+ }
+ }
+
+ /// <summary>
+ /// Removes the header.
+ /// </summary>
+ public void RemoveHeader()
+ {
+ _itemContextList.Remove(_headerContext);
+ _headerContext?.Item?.Delete();
+ _headerContext = null;
+ _headerElement = null;
+ }
+
+ /// <summary>
+ /// Removes the footer.
+ /// </summary>
+ public void RemoveFooter()
+ {
+ _itemContextList.Remove(_footerContext);
+ _footerContext?.Item?.Delete();
+ _footerContext = null;
+ _footerElement = null;
+ }
+
+ /// <summary>
+ /// Determines whether this instance has a header.
+ /// </summary>
+ /// <returns><c>true</c> if the header is present.</returns>
+ public bool HasHeader()
+ {
+ return _headerContext != null;
+ }
+
+ /// <summary>
+ /// Determines whether this instance has a footer.
+ /// </summary>
+ /// <returns><c>true</c> if the footer is present.</returns>
+ public bool HasFooter()
+ {
+ return _footerContext != null;
+ }
+
+ /// <summary>
+ /// Gets the header.
+ /// </summary>
+ /// <returns>The header.</returns>
+ public VisualElement GetHeader()
+ {
+ return _headerElement;
+ }
+
+ /// <summary>
+ /// Gets the footer.
+ /// </summary>
+ /// <returns>The footer.</returns>
+ public VisualElement GetFooter()
+ {
+ return _footerElement;
+ }
+
+ /// <summary>
+ /// Handles the header deleted event.
+ /// </summary>
+ /// <param name="sender">Sender of the event.</param>
+ /// <param name="e">Empty argument.</param>
+ void HeaderDeletedHandler(object sender, EventArgs e)
+ {
+ _itemContextList.Remove(_headerContext);
+ _headerContext = null;
+ }
+
+ /// <summary>
+ /// Handles the footer deleted event.
+ /// </summary>
+ /// <param name="sender">Sender of the event.</param>
+ /// <param name="e">Empty argument.</param>
+ void FooterDeletedHandler(object sender, EventArgs e)
+ {
+ _itemContextList.Remove(_footerContext);
+ _footerContext = null;
+ }
+
+ /// <summary>
+ /// Called every time an object gets realized.
+ /// </summary>
+ /// <param name="sender">Sender of the event.</param>
+ /// <param name="evt">GenListItemEventArgs.</param>
+ void OnItemAppear(object sender, GenListItemEventArgs evt)
+ {
+ ItemContext itemContext = (evt.Item.Data as ItemContext);
+
+ if (itemContext != null && itemContext.Cell != null)
+ {
+ (itemContext.Cell as ICellController).SendAppearing();
+ }
+ }
+
+ /// <summary>
+ /// Called every time an object gets unrealized.
+ /// </summary>
+ /// <param name="sender">Sender of the event.</param>
+ /// <param name="evt">GenListItemEventArgs.</param>
+ void OnItemDisappear(object sender, GenListItemEventArgs evt)
+ {
+ ItemContext itemContext = (evt.Item.Data as ItemContext);
+ if (itemContext != null && itemContext.Cell != null)
+ {
+ (itemContext.Cell as ICellController).SendDisappearing();
+ itemContext.Renderer?.SendUnrealizedCell(itemContext.Cell);
+ }
+ }
+
+
+ /// <summary>
+ /// A convenience shorthand method for derivate classes.
+ /// </summary>
+ /// <param name="cell">Cell to be added.</param>
+ protected void AddCell(Cell cell)
+ {
+ AddItem(cell);
+ }
+
+ /// <summary>
+ /// Gets the cell renderer for given cell type.
+ /// </summary>
+ /// <returns>The cell handler.</returns>
+ /// <param name="cell">Cell to be added.</param>
+ /// <param name="isGroup">If <c>true</c>, then group handlers will be included in the lookup as well.</param>
+ protected virtual CellRenderer GetCellRenderer(Cell cell, bool isGroup = false)
+ {
+ Type type = cell.GetType();
+ var cache = isGroup ? _groupCellRendererCache : _cellRendererCache;
+ if (cache.ContainsKey(type))
+ return cache[type];
+
+ CellRenderer renderer = null;
+
+ if (isGroup && type == typeof(TextCell))
+ {
+ renderer = new GroupCellTextRenderer();
+ }
+ renderer = renderer ?? Registrar.Registered.GetHandler<CellRenderer>(type);
+
+ if (renderer == null)
+ {
+ Log.Error("Cell type is not handled: {0}", cell.GetType());
+ throw new ArgumentNullException("Unsupported cell type");
+ }
+ return cache[type] = renderer;
+ }
+
+ /// <summary>
+ /// Adds the group item. Group item is actually of class GroupList because
+ /// group item has sub items (can be zero) which needs to be added.
+ /// If beforeCell is not null, new group will be added just before it.
+ /// </summary>
+ /// <param name="groupList">Group to be added.</param>
+ /// <param name="beforeCell">Before cell.</param>
+ void AddGroupItem(GroupList groupList, Cell beforeCell = null)
+ {
+ Cell groupCell = groupList.HeaderContent;
+ CellRenderer groupRenderer = GetCellRenderer(groupCell, true);
+ ItemContext groupItemContext = new ItemContext();
+ groupItemContext.Cell = groupCell;
+ groupItemContext.Renderer = groupRenderer;
+
+ if (beforeCell != null)
+ {
+ GenListItem beforeItem = GetItemContext(beforeCell)?.Item;
+ groupItemContext.Item = InsertBefore(groupRenderer.Class, groupItemContext, beforeItem, GenListItemType.Group);
+ }
+ else
+ {
+ groupItemContext.Item = Append(groupRenderer.Class, groupItemContext, GenListItemType.Group);
+ }
+
+ groupItemContext.Item.SelectionMode = GenListSelectionMode.None;
+ groupItemContext.IsGroupItem = true;
+
+ groupItemContext.ListOfSubItems = groupList;
+ groupItemContext.Item.Deleted += ItemDeletedHandler;
+ _itemContextList.Add(groupItemContext);
+ }
+
+ /// <summary>
+ /// Adds the item.
+ /// </summary>
+ /// <param name="cell">Cell to be added.</param>
+ /// <param name="groupCell">Group to which the new item should belong.</param>
+ /// <remark>If the value of <c>groupCell</c> is not null, the new item will be put into the requested group. </remark>
+ /// <param name="beforeCell">The cell before which the new item should be placed.</param>
+ /// <remarks> If the value of <c>beforeCell</c> is not null, the new item will be placed just before the requested cell. </remarks>
+ void AddItem(Cell cell, Cell groupCell = null, Cell beforeCell = null)
+ {
+ CellRenderer renderer = GetCellRenderer(cell);
+ GenListItem parentItem = null;
+
+ ItemContext itemContext = new ItemContext();
+ itemContext.Cell = cell;
+ itemContext.Renderer = renderer;
+
+ if (IsGroupingEnabled && groupCell != null)
+ {
+ var groupContext = GetItemContext(groupCell);
+ itemContext.ListOfSubItems = groupContext.ListOfSubItems;
+ parentItem = groupContext.Item;
+ }
+
+ if (beforeCell != null)
+ {
+ GenListItem beforeItem = GetItemContext(beforeCell)?.Item;
+ itemContext.Item = InsertBefore(renderer.Class, itemContext, beforeItem, GenListItemType.Normal, parentItem);
+ }
+ else
+ {
+ itemContext.Item = Append(renderer.Class, itemContext, GenListItemType.Normal, parentItem);
+ }
+
+ itemContext.Item.SelectionMode = GenListSelectionMode.Always;
+
+ cell.PropertyChanged += OnCellPropertyChanged;
+ (cell as ICellController).ForceUpdateSizeRequested += OnForceUpdateSizeRequested;
+ itemContext.Item.Deleted += ItemDeletedHandler;
+ _itemContextList.Add(itemContext);
+ }
+
+ /// <summary>
+ /// Handles item deleted event.
+ /// </summary>
+ /// <param name="sender">Sender of the event.</param>
+ /// <param name="e">Empty argument.</param>
+ void ItemDeletedHandler(object sender, EventArgs e)
+ {
+ ItemContext itemContext = (sender as GenListItem).Data as ItemContext;
+ if (itemContext.Cell != null)
+ {
+ itemContext.Cell.PropertyChanged -= OnCellPropertyChanged;
+ (itemContext.Cell as ICellController).ForceUpdateSizeRequested -= OnForceUpdateSizeRequested;
+ }
+ _itemContextList.Remove(itemContext);
+ }
+
+ /// <summary>
+ /// Invoked whenever the properties of data model change.
+ /// </summary>
+ /// <param name="sender">Sender of the event.</param>
+ /// <param name="e">PropertyChangedEventArgs.</param>
+ /// <remarks>
+ /// The purpose of this method is to propagate these changes to the presentation layer.
+ /// </remarks>
+ void OnCellPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ var cell = sender as Cell;
+ var context = GetItemContext(cell);
+ context.Renderer.SendCellPropertyChanged(cell, context.Item, e.PropertyName);
+ }
+
+ void OnForceUpdateSizeRequested(object sender, EventArgs e)
+ {
+ var cell = sender as Cell;
+ var itemContext = GetItemContext(cell);
+ if (itemContext.Item != null)
+ itemContext.Item.Update();
+ }
+
+ /// <summary>
+ /// Gets the item class used for header and footer cells.
+ /// </summary>
+ /// <returns>The header and footer item class.</returns>
+ GenItemClass GetHeaderFooterItemClass()
+ {
+ if (_headerFooterItemClass == null)
+ {
+ _headerFooterItemClass = new GenItemClass("full")
+ {
+ GetContentHandler = (data, part) =>
+ {
+ VisualElement element = data as VisualElement;
+ var renderer = Platform.GetOrCreateRenderer(element);
+
+ if (element.MinimumHeightRequest == -1)
+ {
+ SizeRequest request = element.Measure(double.PositiveInfinity, double.PositiveInfinity);
+ renderer.NativeView.MinimumHeight = (int)request.Request.Height;
+ }
+ else
+ {
+ renderer.NativeView.MinimumHeight = (int)element.MinimumHeightRequest;
+ }
+
+ return renderer.NativeView;
+ }
+ };
+ }
+ return _headerFooterItemClass;
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/MasterDetailPage.cs b/Xamarin.Forms.Platform.Tizen/Native/MasterDetailPage.cs
new file mode 100644
index 0000000..5751aaf
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/MasterDetailPage.cs
@@ -0,0 +1,364 @@
+using System;
+using ElmSharp;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// The native widget which provides Xamarin.MasterDetailPage features.
+ /// </summary>
+ public class MasterDetailPage : Box
+ {
+ /// <summary>
+ /// The portion of the screen that the MasterPage takes in Split mode.
+ /// </summary>
+ static readonly double s_splitRatio = 0.35;
+
+ /// <summary>
+ /// The portion of the screen that the MasterPage takes in Popover mode.
+ /// </summary>
+ static readonly double s_popoverRatio = 0.8;
+
+ /// <summary>
+ /// The default master behavior (a.k.a mode).
+ /// </summary>
+ static readonly MasterBehavior s_defaultMasterBehavior = (Device.Idiom == TargetIdiom.Phone) ? MasterBehavior.Popover : MasterBehavior.SplitOnLandscape;
+
+ /// <summary>
+ /// The MasterPage native container.
+ /// </summary>
+ readonly Canvas _masterCanvas;
+
+ /// <summary>
+ /// The DetailPage native container.
+ /// </summary>
+ readonly Canvas _detailCanvas;
+
+ /// <summary>
+ /// The container for <c>_masterCanvas</c> and <c>_detailCanvas</c> used in split mode.
+ /// </summary>
+ readonly Panes _splitPane;
+
+ /// <summary>
+ /// The container for <c>_masterCanvas</c> used in popover mode.
+ /// </summary>
+ readonly Panel _drawer;
+
+ /// <summary>
+ /// The <see cref="MasterBehavior"/> property value.
+ /// </summary>
+ MasterBehavior _masterBehavior = s_defaultMasterBehavior;
+
+ /// <summary>
+ /// The actual MasterDetailPage mode - either split or popover. It depends on <c>_masterBehavior</c> and screen orientation.
+ /// </summary>
+ MasterBehavior _internalMasterBehavior = MasterBehavior.Popover;
+
+ /// <summary>
+ /// The <see cref="Master"/> property value.
+ /// </summary>
+ EvasObject _master;
+
+ /// <summary>
+ /// The <see cref="Detail"/> property value.
+ /// </summary>
+ EvasObject _detail;
+
+ /// <summary>
+ /// The main widget - either <see cref="_splitPlane"/> or <see cref="_detailPage"/>, depending on the mode.
+ /// </summary>
+ EvasObject _mainWidget;
+
+ /// <summary>
+ /// The <see cref="IsPresented"/> property value.
+ /// </summary>
+ bool _isPresented;
+
+ /// <summary>
+ /// The <see cref="IsGestureEnabled"/> property value.
+ /// </summary>
+ bool _isGestureEnabled;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Xamarin.Forms.Platform.Tizen.Native.MasterDetailPage"/> class.
+ /// </summary>
+ /// <param name="parent">Parent evas object.</param>
+ public MasterDetailPage(EvasObject parent) : base(parent)
+ {
+ // we control the layout ourselves
+ Resized += (sender, e) =>
+ {
+ var g = Geometry;
+
+ // main widget should fill the area of the MasterDetailPage
+ if (_mainWidget != null)
+ {
+ _mainWidget.Geometry = g;
+ }
+
+ g.Width = (int)((s_popoverRatio * (double)g.Width));
+ _drawer.Geometry = g;
+ };
+
+ // create the controls which will hold the master and detail pages
+ _masterCanvas = new Canvas(this);
+ _masterCanvas.SetAlignment(-1.0, -1.0); // fill
+ _masterCanvas.SetWeight(1.0, 1.0); // expand
+ _masterCanvas.Resized += (sender, e) =>
+ {
+ UpdatePageGeometry(_master);
+ };
+
+ _detailCanvas = new Canvas(this);
+ _detailCanvas.SetAlignment(-1.0, -1.0); // fill
+ _detailCanvas.SetWeight(1.0, 1.0); // expand
+ _detailCanvas.Resized += (sender, e) =>
+ {
+ UpdatePageGeometry(_detail);
+ };
+
+ _splitPane = new Panes(this)
+ {
+ IsFixed = true,
+ IsHorizontal = false,
+ Proportion = s_splitRatio,
+ };
+
+ _drawer = new Panel(this)
+ {
+ Direction = PanelDirection.Left,
+ };
+ _drawer.SetScrollable(_isGestureEnabled);
+ _drawer.SetScrollableArea(1.0);
+ _drawer.Toggled += (object sender, EventArgs e) =>
+ {
+ IsPresented = _drawer.IsOpen;
+ };
+
+ IsPresentedChanged += (sender, e) =>
+ {
+ _drawer.IsOpen = IsPresented;
+ };
+
+ ConfigureLayout();
+
+ // in case of the screen rotation we may need to update the choice between split
+ // and popover behaviors and reconfigure the layout
+ Forms.Context.MainWindow.RotationChanged += (sender, e) =>
+ {
+ UpdateMasterBehavior();
+ };
+ }
+
+ /// <summary>
+ /// Occurs when the MasterPage is shown or hidden.
+ /// </summary>
+ public event EventHandler IsPresentedChanged;
+
+ /// <summary>
+ /// Gets or sets the MasterDetailPage behavior.
+ /// </summary>
+ /// <value>The behavior of the <c>MasterDetailPage</c> requested by the user.</value>
+ public MasterBehavior MasterBehavior
+ {
+ get
+ {
+ return _masterBehavior;
+ }
+
+ set
+ {
+ if (_masterBehavior != value)
+ {
+ _masterBehavior = value;
+
+ UpdateMasterBehavior();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the content of the MasterPage.
+ /// </summary>
+ /// <value>The MasterPage.</value>
+ public EvasObject Master
+ {
+ get
+ {
+ return _master;
+ }
+
+ set
+ {
+ if (_master != value)
+ {
+ _master = value;
+ UpdatePageGeometry(_master);
+ _masterCanvas.Children.Clear();
+ _masterCanvas.Children.Add(_master);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the content of the DetailPage.
+ /// </summary>
+ /// <value>The DetailPage.</value>
+ public EvasObject Detail
+ {
+ get
+ {
+ return _detail;
+ }
+
+ set
+ {
+ if (_detail != value)
+ {
+ _detail = value;
+ UpdatePageGeometry(_detail);
+ _detailCanvas.Children.Clear();
+ _detailCanvas.Children.Add(_detail);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether the MasterPage is shown.
+ /// </summary>
+ /// <value><c>true</c> if the MasterPage is presented.</value>
+ public bool IsPresented
+ {
+ get
+ {
+ return _isPresented;
+ }
+
+ set
+ {
+ if (_isPresented != value)
+ {
+ _isPresented = value;
+ IsPresentedChanged?.Invoke(this, EventArgs.Empty);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether a MasterDetailPage allows showing MasterPage with swipe gesture.
+ /// </summary>
+ /// <value><c>true</c> if the MasterPage can be revealed with a gesture.</value>
+ public bool IsGestureEnabled
+ {
+ get
+ {
+ return _isGestureEnabled;
+ }
+
+ set
+ {
+ if (_isGestureEnabled != value)
+ {
+ _isGestureEnabled = value;
+ _drawer.SetScrollable(_isGestureEnabled);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Updates the geometry of the selected page.
+ /// </summary>
+ /// <param name="page">Master or Detail page to be updated.</param>
+ void UpdatePageGeometry(EvasObject page)
+ {
+ if (page != null)
+ {
+ if (_master == page)
+ {
+ // update the geometry of the master page
+ page.Geometry = _masterCanvas.Geometry;
+ }
+ else if (_detail == page)
+ {
+ // update the geometry of the detail page
+ page.Geometry = _detailCanvas.Geometry;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Updates <see cref="_internalMasterBehavior"/> according to <see cref="MasterDetailBehavior"/> set by the user and current screen orientation.
+ /// </summary>
+ void UpdateMasterBehavior()
+ {
+ var behavior = (_masterBehavior == MasterBehavior.Default) ? s_defaultMasterBehavior : _masterBehavior;
+
+ // Screen orientation affects those 2 behaviors
+ if (behavior == MasterBehavior.SplitOnLandscape ||
+ behavior == MasterBehavior.SplitOnPortrait)
+ {
+ var orientation = Forms.Context.MainWindow.CurrentOrientation;
+
+ if (((orientation == DisplayOrientations.Landscape || orientation == DisplayOrientations.LandscapeFlipped) && behavior == MasterBehavior.SplitOnLandscape) ||
+ ((orientation == DisplayOrientations.Portrait || orientation == DisplayOrientations.PortraitFlipped) && behavior == MasterBehavior.SplitOnPortrait))
+ {
+ behavior = MasterBehavior.Split;
+ }
+ else
+ {
+ behavior = MasterBehavior.Popover;
+ }
+ }
+
+ if (behavior != _internalMasterBehavior)
+ {
+ _internalMasterBehavior = behavior;
+
+ ConfigureLayout();
+ }
+ }
+
+ /// <summary>
+ /// Composes the structure of all the necessary widgets.
+ /// </summary>
+ void ConfigureLayout()
+ {
+ _drawer.SetContent(null, true);
+ _drawer.Hide();
+
+ _splitPane.SetPartContent("left", null, true);
+ _splitPane.SetPartContent("right", null, true);
+ _splitPane.Hide();
+
+ UnPackAll();
+
+ // the structure for split mode and for popover mode looks differently
+ if (_internalMasterBehavior == MasterBehavior.Split)
+ {
+ _splitPane.SetPartContent("left", _masterCanvas, true);
+ _splitPane.SetPartContent("right", _detailCanvas, true);
+ PackEnd(_splitPane);
+
+ _splitPane.Show();
+
+ _mainWidget = _splitPane;
+
+ IsPresented = true;
+ }
+ else
+ {
+ _drawer.SetContent(_masterCanvas, true);
+ PackEnd(_detailCanvas);
+ PackEnd(_drawer);
+
+ _drawer.Show();
+
+ _mainWidget = _detailCanvas;
+
+ _drawer.IsOpen = IsPresented;
+ }
+
+ _masterCanvas.Show();
+ _detailCanvas.Show();
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/ObservableCollection.cs b/Xamarin.Forms.Platform.Tizen/Native/ObservableCollection.cs
new file mode 100644
index 0000000..d33407c
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/ObservableCollection.cs
@@ -0,0 +1,30 @@
+using System.Collections.Specialized;
+using System.Linq;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// Represents a dynamic data collection that provides notifications when items get added, removed, or when the whole list is refreshed.
+ /// </summary>
+ /// <typeparam name="T">The type of elements in the collection.</typeparam>
+ internal class ObservableCollection<T> : System.Collections.ObjectModel.ObservableCollection<T>
+ {
+ /// <summary>
+ /// Removes all items from the collection.
+ /// </summary>
+ /// <remarks>
+ /// Fisrt remove all items, send CollectionChanged event with Remove Action
+ /// Second call ClearItems of base
+ /// </remarks>
+ protected override void ClearItems()
+ {
+ var oldItems = Items.ToList();
+ Items.Clear();
+ using (BlockReentrancy())
+ {
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, oldItems));
+ }
+ base.ClearItems();
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/SearchBar.cs b/Xamarin.Forms.Platform.Tizen/Native/SearchBar.cs
new file mode 100644
index 0000000..db597ed
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/SearchBar.cs
@@ -0,0 +1,423 @@
+using System;
+using ElmSharp;
+using EColor = ElmSharp.Color;
+using ESize = ElmSharp.Size;
+using ERect = ElmSharp.Rect;
+using ERectangle = ElmSharp.Rectangle;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// Provides implementation of the search bar widget.
+ /// </summary>
+ public class SearchBar : Canvas, IMeasurable
+ {
+ /// <summary>
+ /// The height of the background of the search bar.
+ /// </summary>
+ const int BackgroundHeight = 120;
+
+ /// <summary>
+ /// The style of the cancel button.
+ /// </summary>
+ const string CancelButtonLayoutStyle = "editfield_clear";
+
+ /// <summary>
+ /// The horizontal padding of the cancel button.
+ /// </summary>
+ const int CancelButtonPaddingHorizontal = 17;
+
+ /// <summary>
+ /// The size of the cancel button.
+ /// </summary>
+ const int CancelButtonSize = 80;
+
+ /// <summary>
+ /// The height of the entry.
+ /// </summary>
+ const int EntryHeight = 54;
+
+ /// <summary>
+ /// The horizontal padding of the entry.
+ /// </summary>
+ const int EntryPaddingHorizontal = 42;
+
+ /// <summary>
+ /// The vertical padding of the entry.
+ /// </summary>
+ const int EntryPaddingVertical = 33;
+
+ /// <summary>
+ /// The height of the rectangle used to draw underline effect.
+ /// </summary>
+ const int RectangleHeight = 2;
+
+ /// <summary>
+ /// The bottom padding of the rectangle used to draw underline effect.
+ /// </summary>
+ const int RectanglePaddingBottom = 20;
+
+ /// <summary>
+ /// The horizontal padding of the rectangle used to draw underline effect.
+ /// </summary>
+ const int RectanglePaddingHorizontal = 32;
+
+ /// <summary>
+ /// The top padding of the rectangle used to draw underline effect.
+ /// </summary>
+ const int RectanglePaddingTop = 11;
+
+ //TODO: read default platform color
+
+ /// <summary>
+ /// The color of the underline rectangle.
+ /// </summary>
+ static readonly EColor s_underlineColor = EColor.Aqua;
+
+ /// <summary>
+ /// The dimmed color of the underline rectangle.
+ /// </summary>
+ static readonly EColor s_underlineDimColor = EColor.Gray;
+
+ /// <summary>
+ /// The cancel button.
+ /// </summary>
+ Button _cancelButton;
+
+ /// <summary>
+ /// The text entry.
+ /// </summary>
+ Entry _entry;
+
+ /// <summary>
+ /// The underline rectangle.
+ /// </summary>
+ ERectangle _underlineRectangle;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Xamarin.Forms.Platform.Tizen.Native.SearchBar"/> class.
+ /// </summary>
+ /// <param name="parent">Parent evas object.</param>
+ public SearchBar(EvasObject parent) : base(parent)
+ {
+ _entry = new Entry(parent)
+ {
+ IsSingleLine = true,
+ };
+ _entry.SetInputPanelReturnKeyType(InputPanelReturnKeyType.Search);
+ _entry.TextChanged += EntryTextChanged;
+ _entry.Activated += EntryActivated;
+ _entry.Focused += EntryFocused;
+ _entry.Unfocused += EntryUnfocused;
+ _entry.Show();
+
+ _cancelButton = new Button(parent);
+ _cancelButton.Style = CancelButtonLayoutStyle;
+ _cancelButton.Clicked += CancelButtonClicked;
+
+ _underlineRectangle = new ERectangle(parent)
+ {
+ Color = IsEnabled ? s_underlineColor : s_underlineDimColor,
+ };
+ _underlineRectangle.Show();
+
+ Children.Add(_entry);
+ Children.Add(_cancelButton);
+ Children.Add(_underlineRectangle);
+
+ Show();
+
+ this.LayoutUpdated += SearchBarLayoutUpdated;
+ }
+
+ /// <summary>
+ /// Occurs when the search button on the keyboard is pressed.
+ /// </summary>
+ public event EventHandler SearchButtonPressed;
+
+ /// <summary>
+ /// Occurs when the entry's text has changed.
+ /// </summary>
+ public event EventHandler<TextChangedEventArgs> TextChanged;
+
+ /// <summary>
+ /// Gets or sets the color of the cancel button.
+ /// </summary>
+ /// <value>Color of the cancel button.</value>
+ public EColor CancelButtonColor
+ {
+ get
+ {
+ return _cancelButton.Color;
+ }
+
+ set
+ {
+ if (!_cancelButton.Color.Equals(value))
+ {
+ _cancelButton.Color = value;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the font attributes of the search bar's entry.
+ /// </summary>
+ /// <value>The font attributes.</value>
+ public FontAttributes FontAttributes
+ {
+ get
+ {
+ return _entry.FontAttributes;
+ }
+
+ set
+ {
+ if (value != _entry.FontAttributes)
+ {
+ _entry.FontAttributes = value;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the font family of the search bar's entry.
+ /// </summary>
+ /// <value>The font family.</value>
+ public string FontFamily
+ {
+ get
+ {
+ return _entry.FontFamily;
+ }
+
+ set
+ {
+ if (value != _entry.FontFamily)
+ {
+ _entry.FontFamily = value;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the size of the font of the search bar's entry.
+ /// </summary>
+ /// <value>The size of the font.</value>
+ public double FontSize
+ {
+ get
+ {
+ return _entry.FontSize;
+ }
+
+ set
+ {
+ if (value != _entry.FontSize)
+ {
+ _entry.FontSize = value;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the horizontal text alignment of the search bar's entry.
+ /// </summary>
+ /// <value>The horizontal text alignment.</value>
+ public TextAlignment HorizontalTextAlignment
+ {
+ get
+ {
+ return _entry.HorizontalTextAlignment;
+ }
+
+ set
+ {
+ if (value != _entry.HorizontalTextAlignment)
+ {
+ _entry.HorizontalTextAlignment = value;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the placeholder of the search bar's entry.
+ /// </summary>
+ /// <value>The placeholder.</value>
+ public string Placeholder
+ {
+ get
+ {
+ return _entry.Placeholder;
+ }
+
+ set
+ {
+ if (value != _entry.Placeholder)
+ {
+ _entry.Placeholder = value;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the color of the placeholder.
+ /// </summary>
+ /// <value>The color of the placeholder.</value>
+ public EColor PlaceholderColor
+ {
+ get
+ {
+ return _entry.PlaceholderColor;
+ }
+
+ set
+ {
+ if (!_entry.PlaceholderColor.Equals(value))
+ {
+ _entry.PlaceholderColor = value;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the text of the search bar's entry.
+ /// </summary>
+ /// <value>The text.</value>
+ public override string Text
+ {
+ get
+ {
+ return _entry.Text;
+ }
+
+ set
+ {
+ if (value != _entry.Text)
+ {
+ _entry.Text = value;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the color of the text.
+ /// </summary>
+ /// <value>The color of the text.</value>
+ public EColor TextColor
+ {
+ get
+ {
+ return _entry.TextColor;
+ }
+
+ set
+ {
+ if (!_entry.TextColor.Equals(value))
+ {
+ _entry.TextColor = value;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Implementation of the IMeasurable.Measure() method.
+ /// </summary>
+ public ESize Measure(int availableWidth, int availableHeight)
+ {
+ ESize entrySize = _entry.Measure(availableWidth, availableHeight);
+ int width = entrySize.Width + (CancelButtonPaddingHorizontal * 2) + CancelButtonSize;
+ return new ESize(width, BackgroundHeight);
+ }
+
+ /// <summary>
+ /// Handles the event triggered by the cancel button being clicked.
+ /// </summary>
+ /// <param name="sender">Sender of the event.</param>
+ /// <param name="e">Event arguments, ignored.</param>
+ void CancelButtonClicked(object sender, EventArgs e)
+ {
+ _entry.Text = string.Empty;
+ _cancelButton.Hide();
+ }
+
+ /// <summary>
+ /// Handles the event triggered by clicking the search button on the keyboard.
+ /// </summary>
+ /// <param name="sender">Sender of the event.</param>
+ /// <param name="e">Event arguments, ignored.</param>
+ void EntryActivated(object sender, EventArgs e)
+ {
+ SearchButtonPressed?.Invoke(this, EventArgs.Empty);
+ }
+
+ /// <summary>
+ /// Handles the event triggered by entry gaining the focus.
+ /// </summary>
+ /// <param name="sender">Sender of the event.</param>
+ /// <param name="e">Event arguments, ignored.</param>
+ void EntryFocused(object sender, EventArgs e)
+ {
+ _underlineRectangle.Color = s_underlineColor;
+ }
+
+ /// <summary>
+ /// Handles the event triggered by entry's text being changed.
+ /// </summary>
+ /// <param name="sender">Sender of the event.</param>
+ /// <param name="e">Event arguments.</param>
+ void EntryTextChanged(object sender, TextChangedEventArgs e)
+ {
+ if (string.IsNullOrEmpty(e.NewTextValue))
+ {
+ _cancelButton.Hide();
+ }
+ else if (!_cancelButton.IsVisible)
+ {
+ _cancelButton.Show();
+ }
+ TextChanged?.Invoke(this, e);
+ }
+
+ /// <summary>
+ /// Handles the event triggered by entry losing the focus.
+ /// </summary>
+ /// <param name="sender">Sender of the event.</param>
+ /// <param name="e">Event arguments, ignored.</param>
+ void EntryUnfocused(object sender, EventArgs e)
+ {
+ _underlineRectangle.Color = s_underlineDimColor;
+ }
+
+ /// <summary>
+ /// Handles the event triggered by search bar's layout being changed.
+ /// </summary>
+ /// <remarks>
+ /// Updates the geometry of the widgets comprising the search bar.
+ /// </remarks>
+ /// <param name="sender">Sender of the event.</param>
+ /// <param name="e">Event arguments.</param>
+ void SearchBarLayoutUpdated(object sender, LayoutEventArgs e)
+ {
+ if (!e.HasChanged)
+ {
+ return;
+ }
+
+ _underlineRectangle.Geometry = new ERect(Geometry.Left + RectanglePaddingHorizontal,
+ Geometry.Top + EntryPaddingVertical + EntryHeight + RectanglePaddingTop,
+ Geometry.Width - (RectanglePaddingHorizontal * 2),
+ RectangleHeight);
+
+ _entry.Geometry = new ERect(Geometry.Left + EntryPaddingHorizontal,
+ Geometry.Top + EntryPaddingVertical,
+ Geometry.Width - (EntryPaddingHorizontal + (CancelButtonPaddingHorizontal * 2) + CancelButtonSize),
+ EntryHeight);
+
+ _cancelButton.Geometry = new ERect(Geometry.Right - CancelButtonSize - CancelButtonPaddingHorizontal,
+ Geometry.Top + RectanglePaddingBottom,
+ CancelButtonSize,
+ CancelButtonSize);
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/Span.cs b/Xamarin.Forms.Platform.Tizen/Native/Span.cs
new file mode 100644
index 0000000..6c479e3
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/Span.cs
@@ -0,0 +1,294 @@
+using System.Text;
+using EColor = ElmSharp.Color;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// Represent a text with attributes applied.
+ /// </summary>
+ public class Span
+ {
+ string _text;
+
+ /// <summary>
+ /// Gets or sets the formatted text.
+ /// </summary>
+ public FormattedString FormattedText { get; set; }
+
+ /// <summary>
+ /// Gets or sets the text.
+ /// </summary>
+ /// <remarks>
+ /// Setting Text to a non-null value will set the FormattedText property to null.
+ /// </remarks>
+ public string Text
+ {
+ get
+ {
+ if (FormattedText != null)
+ {
+ return FormattedText.ToString();
+ }
+ else
+ {
+ return _text;
+ }
+ }
+ set
+ {
+ if (value == null)
+ {
+ value = "";
+ }
+ else
+ {
+ FormattedText = null;
+ }
+ _text = value;
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the color for the text.
+ /// </summary>
+ public EColor ForegroundColor { get; set; }
+
+ /// <summary>
+ /// Gets or sets the background color for the text.
+ /// </summary>
+ public EColor BackgroundColor { get; set; }
+
+ /// <summary>
+ /// Gets or sets the font family for the text.
+ /// </summary>
+ public string FontFamily { get; set; }
+
+ /// <summary>
+ /// Gets or sets the font attributes for the text.
+ /// See <see cref="FontAttributes"/> for information about FontAttributes.
+ /// </summary>
+ public FontAttributes FontAttributes { get; set; }
+
+ /// <summary>
+ /// Gets or sets the font size for the text.
+ /// </summary>
+ public double FontSize { get; set; }
+
+ /// <summary>
+ /// Gets or sets the line break mode for the text.
+ /// See <see cref="LineBreakMode"/> for information about LineBreakMode.
+ /// </summary>
+ public LineBreakMode LineBreakMode { get; set; }
+
+ /// <summary>
+ /// Gets or sets the horizontal alignment mode for the text.
+ /// See <see cref="TextAlignment"/> for information about TextAlignment.
+ /// </summary>
+ public TextAlignment HorizontalTextAlignment { get; set; }
+
+ /// <summary>
+ /// Gets or sets the vertical alignment mode for the text.
+ /// See <see cref="TextAlignment"/> for information about TextAlignment.
+ /// </summary>
+ public TextAlignment VerticalTextAlignment { get; set; }
+
+ /// <summary>
+ /// Gets or sets the value that indicates whether the text has underline.
+ /// </summary>
+ public bool Underline { get; set; }
+
+ /// <summary>
+ /// Gets or sets the value that indicates whether the text has strike line though it.
+ /// </summary>
+ public bool Strikethrough { get; set; }
+
+ /// <summary>
+ /// Create a new Span instance with default attributes.
+ /// </summary>
+ public Span()
+ {
+ Text = "";
+ FontFamily = "";
+ FontSize = -1;
+ FontAttributes = FontAttributes.None;
+ ForegroundColor = EColor.White;
+ BackgroundColor = EColor.Transparent;
+ HorizontalTextAlignment = TextAlignment.Auto;
+ VerticalTextAlignment = TextAlignment.Auto;
+ LineBreakMode = LineBreakMode.MixedWrap;
+ Underline = false;
+ Strikethrough = false;
+ }
+
+ /// <summary>
+ /// This method return marked up text
+ /// </summary>
+ internal string GetMarkupText()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.AppendFormat("<span ");
+
+ sb = PrepareFormattingString(sb);
+
+ sb.Append(">");
+
+ sb.Append(GetDecoratedText());
+
+ sb.Append("</>");
+
+ return sb.ToString();
+ }
+
+ /// <summary>
+ /// This method return text decorated with markup if FormattedText is set or plain text otherwise.
+ /// </summary>
+ internal string GetDecoratedText()
+ {
+ if (FormattedText != null)
+ {
+ return FormattedText.ToMarkupString();
+ }
+ else
+ {
+ return ConvertTags(Text);
+ }
+ }
+
+ StringBuilder PrepareFormattingString(StringBuilder _formattingString)
+ {
+ var foregroundColor = ForegroundColor.ToHex();
+
+ _formattingString.AppendFormat("color={0} ", foregroundColor);
+
+ _formattingString.AppendFormat("backing_color={0} ", BackgroundColor.ToHex());
+ _formattingString.Append("backing=on ");
+
+ if (!string.IsNullOrEmpty(FontFamily))
+ {
+ _formattingString.AppendFormat("font={0} ", FontFamily);
+ }
+
+ if (FontSize != -1)
+ {
+ _formattingString.AppendFormat("font_size={0} ", FontSize);
+ }
+
+ if ((FontAttributes & FontAttributes.Bold) != 0)
+ {
+ _formattingString.Append("font_weight=Bold ");
+ }
+ if ((FontAttributes & FontAttributes.Italic) != 0)
+ {
+ _formattingString.Append("font_style=italic ");
+ }
+
+ if (Underline)
+ {
+ _formattingString.AppendFormat("underline=on underline_color={0} ", foregroundColor);
+ }
+
+ if (Strikethrough)
+ {
+ _formattingString.AppendFormat("strikethrough=on strikethrough_color={0} ", foregroundColor);
+ }
+
+ switch (HorizontalTextAlignment)
+ {
+ case TextAlignment.Auto:
+ _formattingString.Append("align=auto ");
+ break;
+
+ case TextAlignment.Start:
+ _formattingString.Append("align=left ");
+ break;
+
+ case TextAlignment.End:
+ _formattingString.Append("align=right ");
+ break;
+
+ case TextAlignment.Center:
+ _formattingString.Append("align=center ");
+ break;
+ }
+
+ switch (VerticalTextAlignment)
+ {
+ case TextAlignment.Auto:
+ case TextAlignment.Start:
+ _formattingString.Append("valign=top ");
+ break;
+
+ case TextAlignment.End:
+ _formattingString.Append("valign=bottom ");
+ break;
+
+ case TextAlignment.Center:
+ _formattingString.Append("valign=middle ");
+ break;
+ }
+
+ switch (LineBreakMode)
+ {
+ case LineBreakMode.NoWrap:
+ _formattingString.Append("wrap=none");
+ break;
+
+ case LineBreakMode.CharacterWrap:
+ _formattingString.Append("wrap=char");
+ break;
+
+ case LineBreakMode.WordWrap:
+ _formattingString.Append("wrap=word");
+ break;
+
+ case LineBreakMode.MixedWrap:
+ _formattingString.Append("wrap=mixed");
+ break;
+
+ case LineBreakMode.HeadTruncation:
+ _formattingString.Append("ellipsis=0.0");
+ break;
+
+ case LineBreakMode.MiddleTruncation:
+ _formattingString.Append("ellipsis=0.5");
+ break;
+
+ case LineBreakMode.TailTruncation:
+ _formattingString.Append("ellipsis=1.0");
+ break;
+ }
+
+ return _formattingString;
+ }
+
+ string ConvertTags(string text)
+ {
+ return text.Replace("<", "&lt;")
+ .Replace(">", "&gt;")
+ .Replace("\n", "<br>");
+ }
+
+ internal string GetStyle()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.Append("DEFAULT='");
+
+ PrepareFormattingString(sb);
+
+ sb.Append("'");
+
+ return sb.ToString();
+ }
+
+ /// <summary>
+ /// Converts string value to Span.
+ /// </summary>
+ /// <param name="text">The string text</param>
+ public static implicit operator Span(string text)
+ {
+ return new Span { Text = text };
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/TableView.cs b/Xamarin.Forms.Platform.Tizen/Native/TableView.cs
new file mode 100644
index 0000000..73388a8
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/TableView.cs
@@ -0,0 +1,67 @@
+using ElmSharp;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// Extends the ListView class to provide TableView class implementation.
+ /// </summary>
+ public class TableView : ListView
+ {
+
+ static readonly SectionCellRenderer _sectionCellRenderer = new SectionCellRenderer();
+ /// <summary>
+ /// Initializes a new instance of the TableView class.
+ /// </summary>
+ public TableView(EvasObject parent)
+ : base(parent) {
+ }
+
+ /// <summary>
+ /// Sets the root of the table.
+ /// </summary>
+ /// <param name="root">TableRoot, which is parent to one or more TableSections.</param>
+ public void ApplyTableRoot(TableRoot root)
+ {
+ Clear();
+ foreach (TableSection ts in root)
+ {
+ AddSectionTitle(ts.Title);
+ AddSource(ts);
+ }
+ }
+
+ protected override CellRenderer GetCellRenderer(Cell cell, bool isGroup = false)
+ {
+ if (cell.GetType() == typeof(SectionCell))
+ {
+ return _sectionCellRenderer;
+ }
+ return base.GetCellRenderer(cell, isGroup);
+ }
+
+ /// <summary>
+ /// Sets the section title.
+ /// </summary>
+ void AddSectionTitle(string title)
+ {
+ Cell cell = new SectionCell()
+ {
+ Text = title
+ };
+ AddCell(cell);
+ }
+
+ internal class SectionCellRenderer : GroupCellTextRenderer
+ {
+ public SectionCellRenderer()
+ {
+ DetailPart = "null";
+ }
+ protected SectionCellRenderer(string style) : base(style) { }
+ }
+ class SectionCell : TextCell
+ {
+ }
+ }
+}
+
diff --git a/Xamarin.Forms.Platform.Tizen/Native/TextAlignment.cs b/Xamarin.Forms.Platform.Tizen/Native/TextAlignment.cs
new file mode 100644
index 0000000..c7e934b
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/TextAlignment.cs
@@ -0,0 +1,25 @@
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// Enumerates values that describe alignemnt of text.
+ /// </summary>
+ public enum TextAlignment
+ {
+ /// <summary>
+ /// Aligns horizontal text according to language. Top aligned for vertical text.
+ /// </summary>
+ Auto,
+ /// <summary>
+ /// Left and top aligned for horizontal and vertical text, respectively.
+ /// </summary>
+ Start,
+ /// <summary>
+ /// Right and bottom aligned for horizontal and vertical text, respectively.
+ /// </summary>
+ End,
+ /// <summary>
+ /// Center-aligned text.
+ /// </summary>
+ Center,
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/TextHelper.cs b/Xamarin.Forms.Platform.Tizen/Native/TextHelper.cs
new file mode 100644
index 0000000..5109dad
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/TextHelper.cs
@@ -0,0 +1,54 @@
+using System;
+using ElmSharp;
+using ESize = ElmSharp.Size;
+using ELayout = ElmSharp.Layout;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// The Text Helper contains functions that assist in working with text-able objects.
+ /// </summary>
+ internal static class TextHelper
+ {
+ /// <summary>
+ /// Gets the size of raw text block.
+ /// </summary>
+ /// <param name="textable">The <see cref="EvasObject"/> with text part.</param>
+ /// <returns>Returns the size of raw text block.</returns>
+ public static ESize GetRawTextBlockSize(EvasObject textable)
+ {
+ return GetElmTextPart(textable).TextBlockNativeSize;
+ }
+
+ /// <summary>
+ /// Gets the size of formatted text block.
+ /// </summary>
+ /// <param name="textable">The <see cref="ElmSharp.EvasObject"/> with text part.</param>
+ /// <returns>Returns the size of formatted text block.</returns>
+ public static ESize GetFormattedTextBlockSize(EvasObject textable)
+ {
+ return GetElmTextPart(textable).TextBlockFormattedSize;
+ }
+
+ /// <summary>
+ /// Gets the ELM text part of evas object.
+ /// </summary>
+ /// <param name="textable">The <see cref="ElmSharp.EvasObject"/> with text part.</param>
+ /// <exception cref="ArgumentException">Throws exception when parameter <param name="textable"> isn't text-able object or doesn't have ELM text part.</exception>
+ /// <returns>Requested <see cref="ElmSharp.EdjeTextPartObject"/> instance.</returns>
+ static EdjeTextPartObject GetElmTextPart(EvasObject textable)
+ {
+ ELayout widget = textable as ELayout;
+ if (widget == null)
+ {
+ throw new ArgumentException("textable should be ElmSharp.Layout", "textable");
+ }
+ EdjeTextPartObject textPart = widget.EdjeObject["elm.text"];
+ if (textPart == null)
+ {
+ throw new ArgumentException("There is no elm.text part", "textable");
+ }
+ return textPart;
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/TimePicker.cs b/Xamarin.Forms.Platform.Tizen/Native/TimePicker.cs
new file mode 100644
index 0000000..6ee49ce
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/TimePicker.cs
@@ -0,0 +1,185 @@
+using System;
+using System.Linq;
+using System.Text.RegularExpressions;
+using ElmSharp;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// Extends the ElmSharp.DateTimeSelector class with functionality useful to renderer.
+ /// </summary>
+ public class TimePicker : DateTimeSelector
+ {
+ const string DefaultEFLFormat = "%I:%M %p";
+ //TODO need to add internationalization support
+ const string FormatExceptionMessage = "Input string was not in a correct format.";
+ const string RegexValidTimePattern = "^([h]{1,2}|[H]{1,2})[.:-]([m]{1,2})(([.:-][s]{1,2})?)(([.:-][fF]{1,7})?)(([K])?)(([z]{1,3})?)(([ ][t]{1,2})?)$";
+ const string TimeLayoutStyle = "time_layout";
+ string _dateTimeFormat;
+ TimeSpan _time;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TimePicker"/> class.
+ /// </summary>
+ /// <param name="parent">The parent EvasObject.</param>
+ public TimePicker(EvasObject parent) : base(parent)
+ {
+ Style = TimeLayoutStyle;
+ ApplyTime(Time);
+ ApplyFormat(DateTimeFormat);
+
+ DateTimeChanged += (sender, e) =>
+ {
+ Time = e.NewDate.TimeOfDay;
+ };
+ }
+
+ /// <summary>
+ /// Gets or sets the displayed date time format.
+ /// </summary>
+ public string DateTimeFormat
+ {
+ get
+ {
+ return _dateTimeFormat;
+ }
+ set
+ {
+ if (_dateTimeFormat != value)
+ {
+ ApplyFormat(value);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the displayed time.
+ /// </summary>
+ public TimeSpan Time
+ {
+ get
+ {
+ return _time;
+ }
+ set
+ {
+ if (_time != value)
+ {
+ ApplyTime(value);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Sets the <c>Format</c> property according to the given <paramref name="format"/>.
+ /// </summary>
+ /// <param name="format">The format value to be applied to the time picker.</param>
+ void ApplyFormat(string format)
+ {
+ _dateTimeFormat = format;
+ Format = ConvertToEFLFormat(_dateTimeFormat);
+ }
+
+ /// <summary>
+ /// Sets the <c>DateTime</c> property according to the given <paramref name="time"/>.
+ /// </summary>
+ /// <param name="time">The time value to be applied to the time picker.</param>
+ void ApplyTime(TimeSpan time)
+ {
+ _time = time;
+ DateTime = ConvertToDateTime(time);
+ }
+
+ /// <summary>
+ /// Converts parameter <paramref name="timeSpan"/> to <see cref="DateTime"/>.
+ /// </summary>
+ /// <param name="timeSpan">The time value to be converted to <see cref="DateTime"/>.</param>
+ /// <returns>An object representing the date 1st Jan, 1970 (minimum date of ElmSharp.DateTimeSelector) with added <paramref name="timeSpan"/>.</returns>
+ DateTime ConvertToDateTime(TimeSpan timeSpan)
+ {
+ return new DateTime(1970, 1, 1) + timeSpan;
+ }
+
+ /// <summary>
+ /// Converts standard or custom <see cref="DateTime"/> format to EFL format.
+ /// </summary>
+ /// <param name="dateTimeFormat">The <see cref="DateTime"/> format to be converted to EFL format.</param>
+ /// <exception cref="FormatException"><param name="dateTimeFormat"> does not contain a valid string representation of a date and time.</exception>
+ /// <returns>An object representing the EFL time format string.
+ /// Example:
+ /// "t" or "T" returns default EFL format "%I:%M %p"
+ /// "HH:mm tt" returns "%H:%M %p"
+ /// "h:mm" returns "%l:%M"
+ /// </returns>
+ string ConvertToEFLFormat(string dateTimeFormat)
+ {
+ if (string.IsNullOrWhiteSpace(dateTimeFormat))
+ {
+ return DefaultEFLFormat;
+ }
+
+ if (dateTimeFormat.Length == 1)
+ {
+ //Standard Time Format (DateTime)
+ if (dateTimeFormat[0] == 't' || dateTimeFormat[0] == 'T')
+ {
+ return DefaultEFLFormat;
+ }
+ else
+ {
+ throw new FormatException(FormatExceptionMessage);
+ }
+ }
+ else
+ {
+ //Custom Time Format (DateTime)
+ Regex regex = new Regex(RegexValidTimePattern);
+ if (!regex.IsMatch(dateTimeFormat))
+ {
+ throw new FormatException(FormatExceptionMessage);
+ }
+
+ string format = string.Empty;
+ int count_h = dateTimeFormat.Count(m => m == 'h'); //12h
+ int count_H = dateTimeFormat.Count(m => m == 'H'); //24h
+
+ if (count_h == 1)
+ {
+ format += "%l";
+ }
+ else if (count_h == 2)
+ {
+ format += "%I";
+ }
+ else if (count_H == 1)
+ {
+ format += "%k";
+ }
+ else if (count_H == 2)
+ {
+ format += "%H";
+ }
+
+ format += ":%M";
+ int count_t = dateTimeFormat.Count(m => m == 't');
+
+ if ((count_H > 0 && count_t > 0) ||
+ (count_h > 0 && count_t == 0))
+ {
+ throw new FormatException(FormatExceptionMessage);
+ }
+
+ if (count_t == 1)
+ {
+ format += " %P";
+ }
+ else if (count_t == 2)
+ {
+ format += " %p";
+ }
+
+ return format;
+ }
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/Window.cs b/Xamarin.Forms.Platform.Tizen/Native/Window.cs
new file mode 100644
index 0000000..87a54cd
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/Window.cs
@@ -0,0 +1,138 @@
+using System;
+using ElmSharp;
+using EWindow = ElmSharp.Window;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ public class Window : EWindow
+ {
+ Conformant _conformant;
+
+ /// <summary>
+ /// Initializes a new instance of the Window class.
+ /// </summary>
+ public Window() : base("FormsWindow")
+ {
+ Initialize();
+ }
+
+ /// <summary>
+ /// Notifies that the window has been closed.
+ /// </summary>
+ public event EventHandler Closed;
+
+ /// <summary>
+ /// Notifies that the back button has been pressed.
+ /// </summary>
+ public event EventHandler BackButtonPressed;
+
+ /// <summary>
+ /// Gets the current orientation.
+ /// </summary>
+ public DisplayOrientations CurrentOrientation
+ {
+ get
+ {
+ if (IsRotationSupported)
+ {
+ return GetDisplayOrientation();
+ }
+ else
+ {
+ return DisplayOrientations.None;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the orientation of a rectangular screen.
+ /// </summary>
+ public DisplayOrientations AvailableOrientations
+ {
+ get
+ {
+ if (IsRotationSupported)
+ {
+ return (DisplayOrientations)AvailableRotations;
+ }
+ else
+ {
+ return DisplayOrientations.None;
+ }
+ }
+ set
+ {
+ if (IsRotationSupported)
+ {
+ AvailableRotations = (DisplayRotation)value;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Sets the main page of Window.
+ /// </summary>
+ /// <param name="content">ElmSharp.EvasObject type page to be set.</param>
+ public void SetMainPage(EvasObject content)
+ {
+ _conformant.SetContent(content);
+ }
+
+ void Initialize()
+ {
+ // size
+ var size = ScreenSize;
+ Resize(size.Width, size.Height);
+
+ // events
+ Deleted += (sender, e) =>
+ {
+ Closed?.Invoke(this, EventArgs.Empty);
+ };
+ CloseRequested += (sender, e) =>
+ {
+ Unrealize();
+ };
+
+ KeyGrab(EvasKeyEventArgs.PlatformBackButtonName, false);
+ KeyUp += (s, e) =>
+ {
+ if (e.KeyName == EvasKeyEventArgs.PlatformBackButtonName)
+ {
+ BackButtonPressed?.Invoke(this, EventArgs.Empty);
+ }
+ };
+
+ Active();
+ AutoDeletion = false;
+ Show();
+
+ _conformant = new Conformant(this);
+ _conformant.SetAlignment(-1.0, -1.0); // fill
+ _conformant.SetWeight(1.0, 1.0); // expand
+ _conformant.Show();
+
+ AvailableOrientations = DisplayOrientations.Portrait | DisplayOrientations.Landscape | DisplayOrientations.PortraitFlipped | DisplayOrientations.LandscapeFlipped;
+ }
+ DisplayOrientations GetDisplayOrientation()
+ {
+ switch (Rotation)
+ {
+ case 0:
+ return Native.DisplayOrientations.Portrait;
+
+ case 90:
+ return Native.DisplayOrientations.Landscape;
+
+ case 180:
+ return Native.DisplayOrientations.PortraitFlipped;
+
+ case 270:
+ return Native.DisplayOrientations.LandscapeFlipped;
+
+ default:
+ return Native.DisplayOrientations.None;
+ }
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Platform.cs b/Xamarin.Forms.Platform.Tizen/Platform.cs
new file mode 100644
index 0000000..ab07300
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Platform.cs
@@ -0,0 +1,345 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using ElmSharp;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ internal class Platform : BindableObject, IPlatform, INavigation, IDisposable
+ {
+ internal static readonly BindableProperty RendererProperty = BindableProperty.CreateAttached("Renderer", typeof(IVisualElementRenderer), typeof(Platform), default(IVisualElementRenderer),
+ propertyChanged: (bindable, oldvalue, newvalue) =>
+ {
+ var ve = bindable as VisualElement;
+ if (ve != null)
+ ve.IsPlatformEnabled = newvalue != null;
+ });
+ internal static readonly BindableProperty PageContextProperty = BindableProperty.CreateAttached("PageContext", typeof(FormsApplication), typeof(Platform), null);
+
+ Naviframe _naviframe;
+ NavigationModel _navModel = new NavigationModel();
+ bool _disposed;
+
+ internal Platform(FormsApplication context)
+ {
+ Forms.Context.MainWindow.BackButtonPressed += (o, e) =>
+ {
+ bool handled = false;
+ if (_navModel.CurrentPage != null)
+ {
+ if (CurrentModalNavigationTask != null && !CurrentModalNavigationTask.IsCompleted)
+ {
+ handled = true;
+ }
+ else
+ {
+ handled = _navModel.CurrentPage.SendBackButtonPressed();
+ }
+ }
+ if (!handled)
+ context.Exit();
+ };
+ _naviframe = new Naviframe(Forms.Context.MainWindow)
+ {
+ PreserveContentOnPop = true,
+ DefaultBackButtonEnabled = false,
+ };
+ _naviframe.SetAlignment(-1, -1);
+ _naviframe.SetWeight(1.0, 1.0);
+ _naviframe.Show();
+ _naviframe.AnimationFinished += NaviAnimationFinished;
+ Forms.Context.MainWindow.SetMainPage(_naviframe);
+ }
+
+ ~Platform()
+ {
+ Dispose(false);
+ }
+
+ public Page Page
+ {
+ get;
+ private set;
+ }
+
+ Task CurrentModalNavigationTask { get; set; }
+ TaskCompletionSource<bool> CurrentTaskCompletionSource { get; set; }
+ IPageController CurrentPageController => _navModel.CurrentPage as IPageController;
+ IReadOnlyList<Page> INavigation.ModalStack => _navModel.Modals.ToList();
+ IReadOnlyList<Page> INavigation.NavigationStack => new List<Page>();
+
+ public static FormsApplication GetPageContext(BindableObject bindable)
+ {
+ return (FormsApplication)bindable.GetValue(Platform.PageContextProperty);
+ }
+
+ public static void SetPageContext(BindableObject bindable, FormsApplication context)
+ {
+ bindable.SetValue(Platform.PageContextProperty, context);
+ }
+
+ public static IVisualElementRenderer GetRenderer(BindableObject bindable)
+ {
+ return (IVisualElementRenderer)bindable.GetValue(Platform.RendererProperty);
+ }
+
+ public static void SetRenderer(BindableObject bindable, IVisualElementRenderer value)
+ {
+ bindable.SetValue(Platform.RendererProperty, value);
+ }
+
+ /// <summary>
+ /// Gets the renderer associated with the <c>view</c>. If it doesn't exist, creates a new one.
+ /// </summary>
+ /// <returns>Renderer associated with the <c>view</c>.</returns>
+ /// <param name="view">View for which the renderer is going to be returned.</param>
+ public static IVisualElementRenderer GetOrCreateRenderer(VisualElement view)
+ {
+ return GetRenderer(view) ?? AttachRenderer(view);
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ public void SetPage(Page newRoot)
+ {
+ if (Page != null)
+ {
+ var copyOfStack = new List<NaviItem>(_naviframe.NavigationStack);
+ for (var i = 0; i < copyOfStack.Count; i++)
+ {
+ copyOfStack[i].Delete();
+ }
+ foreach (Page page in _navModel.Roots)
+ {
+ var renderer = GetRenderer(page);
+ (page as IPageController)?.SendDisappearing();
+ renderer?.Dispose();
+ }
+ _navModel = new NavigationModel();
+ }
+
+ if (newRoot == null)
+ return;
+
+ _navModel.Push(newRoot, null);
+
+ Page = newRoot;
+ Page.Platform = this;
+
+ Platform.SetPageContext(Page, Forms.Context);
+ IVisualElementRenderer pageRenderer = AttachRenderer(Page);
+ var naviItem = _naviframe.Push(pageRenderer.NativeView);
+ naviItem.TitleBarVisible = false;
+ ((Application)Page.RealParent).NavigationProxy.Inner = this;
+
+ CurrentPageController?.SendAppearing();
+ }
+
+ public SizeRequest GetNativeSize(VisualElement view, double widthConstraint, double heightConstraint)
+ {
+ widthConstraint = widthConstraint <= -1 ? double.PositiveInfinity : widthConstraint;
+ heightConstraint = heightConstraint <= -1 ? double.PositiveInfinity : heightConstraint;
+
+ double width = !double.IsPositiveInfinity(widthConstraint) ? widthConstraint : Int32.MaxValue;
+ double height = !double.IsPositiveInfinity(heightConstraint) ? heightConstraint : Int32.MaxValue;
+
+ return GetRenderer(view).GetDesiredSize(width, height);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (_disposed) return;
+ if (disposing)
+ {
+ SetPage(null);
+ _naviframe.Unrealize();
+ }
+ _disposed = true;
+ }
+
+ protected override void OnBindingContextChanged()
+ {
+ BindableObject.SetInheritedBindingContext(Page, base.BindingContext);
+ base.OnBindingContextChanged();
+ }
+
+ static IVisualElementRenderer AttachRenderer(VisualElement view)
+ {
+ IVisualElementRenderer visualElementRenderer = Registrar.Registered.GetHandler<IVisualElementRenderer>(view.GetType());
+
+ if (null == visualElementRenderer)
+ {
+ throw new ArgumentException(String.Format("{0} doesn't have assigned renderer!", view.GetType()));
+ }
+
+ visualElementRenderer.SetElement(view);
+
+ return visualElementRenderer;
+ }
+
+ void INavigation.InsertPageBefore(Page page, Page before)
+ {
+ throw new InvalidOperationException("InsertPageBefore is not supported globally on Tizen, please use a NavigationPage.");
+ }
+
+ Task<Page> INavigation.PopAsync()
+ {
+ return ((INavigation)this).PopAsync(true);
+ }
+
+ Task<Page> INavigation.PopAsync(bool animated)
+ {
+ throw new InvalidOperationException("PopAsync is not supported globally on Tizen, please use a NavigationPage.");
+ }
+
+ Task INavigation.PopToRootAsync()
+ {
+ return ((INavigation)this).PopToRootAsync(true);
+ }
+
+ Task INavigation.PopToRootAsync(bool animated)
+ {
+ throw new InvalidOperationException("PopToRootAsync is not supported globally on Tizen, please use a NavigationPage.");
+ }
+
+ Task INavigation.PushAsync(Page root)
+ {
+ return ((INavigation)this).PushAsync(root, true);
+ }
+
+ Task INavigation.PushAsync(Page root, bool animated)
+ {
+ throw new InvalidOperationException("PushAsync is not supported globally on Tizen, please use a NavigationPage.");
+ }
+
+ void INavigation.RemovePage(Page page)
+ {
+ throw new InvalidOperationException("RemovePage is not supported globally on Tizen, please use a NavigationPage.");
+ }
+
+ Task INavigation.PushModalAsync(Page modal)
+ {
+ return ((INavigation)this).PushModalAsync(modal, true);
+ }
+
+ async Task INavigation.PushModalAsync(Page modal, bool animated)
+ {
+ CurrentPageController?.SendDisappearing();
+
+ _navModel.PushModal(modal);
+
+ modal.Platform = this;
+
+ await PushModalInternal(modal, animated);
+
+ // Verify that the modal is still on the stack
+ if (_navModel.CurrentPage == modal)
+ CurrentPageController.SendAppearing();
+ }
+
+ Task<Page> INavigation.PopModalAsync()
+ {
+ return ((INavigation)this).PopModalAsync(true);
+ }
+
+ async Task<Page> INavigation.PopModalAsync(bool animated)
+ {
+ Page modal = _navModel.PopModal();
+
+ ((IPageController)modal).SendDisappearing();
+
+ IVisualElementRenderer modalRenderer = GetRenderer(modal);
+ if (modalRenderer != null)
+ {
+ await PopModalInternal(animated);
+ }
+ Platform.GetRenderer(modal).Dispose();
+
+ CurrentPageController?.SendAppearing();
+ return modal;
+ }
+
+ async Task PushModalInternal(Page modal, bool animated)
+ {
+ TaskCompletionSource<bool> tcs = null;
+ if (CurrentModalNavigationTask != null && !CurrentModalNavigationTask.IsCompleted)
+ {
+ var previousTask = CurrentModalNavigationTask;
+ tcs = new TaskCompletionSource<bool>();
+ CurrentModalNavigationTask = tcs.Task;
+ await previousTask;
+ }
+
+ var after = _naviframe.NavigationStack.LastOrDefault();
+ NaviItem pushed = null;
+ if (animated || after == null)
+ {
+ pushed = _naviframe.Push(Platform.GetOrCreateRenderer(modal).NativeView, modal.Title);
+ }
+ else
+ {
+ pushed = _naviframe.InsertAfter(after, Platform.GetOrCreateRenderer(modal).NativeView, modal.Title);
+ }
+ pushed.TitleBarVisible = false;
+
+ bool shouldWait = animated && after != null;
+ await WaitForCompletion(shouldWait, tcs);
+ }
+
+ async Task PopModalInternal(bool animated)
+ {
+ TaskCompletionSource<bool> tcs = null;
+ if (CurrentModalNavigationTask != null && !CurrentModalNavigationTask.IsCompleted)
+ {
+ var previousTask = CurrentModalNavigationTask;
+ tcs = new TaskCompletionSource<bool>();
+ CurrentModalNavigationTask = tcs.Task;
+ await previousTask;
+ }
+
+ if (animated)
+ {
+ _naviframe.Pop();
+ }
+ else
+ {
+ _naviframe.NavigationStack.LastOrDefault()?.Delete();
+ }
+
+ bool shouldWait = animated && (_naviframe.NavigationStack.Count != 0);
+ await WaitForCompletion(shouldWait, tcs);
+ }
+
+ async Task WaitForCompletion(bool shouldWait, TaskCompletionSource<bool> tcs)
+ {
+ if (shouldWait)
+ {
+ tcs = tcs ?? new TaskCompletionSource<bool>();
+ CurrentTaskCompletionSource = tcs;
+ if (CurrentModalNavigationTask == null || CurrentModalNavigationTask.IsCompleted)
+ {
+ CurrentModalNavigationTask = CurrentTaskCompletionSource.Task;
+ }
+ }
+ else
+ {
+ tcs?.SetResult(true);
+ }
+
+ if (tcs != null)
+ await tcs.Task;
+ }
+
+ void NaviAnimationFinished(object sender, EventArgs e)
+ {
+ var tcs = CurrentTaskCompletionSource;
+ CurrentTaskCompletionSource = null;
+ tcs?.SetResult(true);
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/PlatformEffect.cs b/Xamarin.Forms.Platform.Tizen/PlatformEffect.cs
new file mode 100644
index 0000000..97dec49
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/PlatformEffect.cs
@@ -0,0 +1,11 @@
+using ElmSharp;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ /// <summary>
+ /// Base class for platform-specific effect classes.
+ /// </summary>
+ public abstract class PlatformEffect : PlatformEffect<EvasObject, EvasObject>
+ {
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Properties/AssemblyInfo.cs b/Xamarin.Forms.Platform.Tizen/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..37e0221
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Properties/AssemblyInfo.cs
@@ -0,0 +1,59 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Xamarin.Forms;
+using Xamarin.Forms.Platform.Tizen;
+
+[assembly: AssemblyVersion("0.0.1")]
+[assembly: AssemblyCompany("Samsung Electronics")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCopyright("Copyright © Samsung Electronics 2016")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyProduct("Xamarin.Forms")]
+[assembly: AssemblyTitle("Xamarin.Forms.Platform.Tizen")]
+[assembly: AssemblyTrademark("")]
+[assembly: CompilationRelaxations(8)]
+[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
+[assembly: InternalsVisibleTo("Xamarin.Forms.Platform.Tizen.Tests")]
+[assembly: ComVisible(false)]
+
+[assembly: Xamarin.Forms.Dependency(typeof(ResourcesProvider))]
+[assembly: Xamarin.Forms.Dependency(typeof(Deserializer))]
+
+[assembly: ExportRenderer(typeof(Layout), typeof(LayoutRenderer))]
+[assembly: ExportRenderer(typeof(ScrollView), typeof(ScrollViewRenderer))]
+[assembly: ExportRenderer(typeof(CarouselPage), typeof(CarouselPageRenderer))]
+[assembly: ExportRenderer(typeof(ContentPage), typeof(ContentPageRenderer))]
+[assembly: ExportRenderer(typeof(NavigationPage), typeof(NavigationPageRenderer))]
+[assembly: ExportRenderer(typeof(MasterDetailPage), typeof(MasterDetailPageRenderer))]
+[assembly: ExportRenderer(typeof(TabbedPage), typeof(TabbedPageRenderer))]
+
+[assembly: ExportRenderer(typeof(Label), typeof(LabelRenderer))]
+[assembly: ExportRenderer(typeof(Button), typeof(ButtonRenderer))]
+[assembly: ExportRenderer(typeof(Image), typeof(ImageRenderer))]
+[assembly: ExportRenderer(typeof(Slider), typeof(SliderRenderer))]
+[assembly: ExportRenderer(typeof(Picker), typeof(PickerRenderer))]
+[assembly: ExportRenderer(typeof(Frame), typeof(FrameRenderer))]
+[assembly: ExportRenderer(typeof(Stepper), typeof(StepperRenderer))]
+[assembly: ExportRenderer(typeof(DatePicker), typeof(DatePickerRenderer))]
+[assembly: ExportRenderer(typeof(TimePicker), typeof(TimePickerRenderer))]
+[assembly: ExportRenderer(typeof(ProgressBar), typeof(ProgressBarRenderer))]
+[assembly: ExportRenderer(typeof(Switch), typeof(SwitchRenderer))]
+[assembly: ExportRenderer(typeof(ListView), typeof(ListViewRenderer))]
+[assembly: ExportRenderer(typeof(BoxView), typeof(BoxViewRenderer))]
+[assembly: ExportRenderer(typeof(ActivityIndicator), typeof(ActivityIndicatorRenderer))]
+[assembly: ExportRenderer(typeof(SearchBar), typeof(SearchBarRenderer))]
+[assembly: ExportRenderer(typeof(Entry), typeof(EntryRenderer))]
+[assembly: ExportRenderer(typeof(Editor), typeof(EditorRenderer))]
+[assembly: ExportRenderer(typeof(TableView), typeof(TableViewRenderer))]
+[assembly: ExportRenderer(typeof(EvasObjectWrapper), typeof(EvasObjectWrapperRenderer))]
+
+[assembly: ExportImageSourceHandler(typeof(FileImageSource), typeof(FileImageSourceHandler))]
+[assembly: ExportImageSourceHandler(typeof(StreamImageSource), typeof(StreamImageSourceHandler))]
+[assembly: ExportImageSourceHandler(typeof(UriImageSource), typeof(UriImageSourceHandler))]
+
+[assembly: ExportCell(typeof(TextCell), typeof(TextCellRenderer))]
+[assembly: ExportCell(typeof(ImageCell), typeof(ImageCellRenderer))]
+[assembly: ExportCell(typeof(SwitchCell), typeof(SwitchCellRenderer))]
+[assembly: ExportCell(typeof(EntryCell), typeof(EntryCellRenderer))]
+[assembly: ExportCell(typeof(ViewCell), typeof(ViewCellRenderer))]
diff --git a/Xamarin.Forms.Platform.Tizen/Renderers/ActivityIndicatorRenderer.cs b/Xamarin.Forms.Platform.Tizen/Renderers/ActivityIndicatorRenderer.cs
new file mode 100644
index 0000000..04a69c6
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Renderers/ActivityIndicatorRenderer.cs
@@ -0,0 +1,57 @@
+using EProgressBar = ElmSharp.ProgressBar;
+using EColor = ElmSharp.Color;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ public class ActivityIndicatorRenderer : ViewRenderer<ActivityIndicator, EProgressBar>
+ {
+ static readonly EColor s_defaultColor = EColor.Black;
+
+ public ActivityIndicatorRenderer()
+ {
+ RegisterPropertyHandler(ActivityIndicator.ColorProperty, UpdateColor);
+ RegisterPropertyHandler(ActivityIndicator.IsRunningProperty, UpdateIsRunning);
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<ActivityIndicator> e)
+ {
+ if (Control == null)
+ {
+ var ac = new EProgressBar(Forms.Context.MainWindow)
+ {
+ Style = "process_medium",
+ IsPulseMode = true,
+ };
+ SetNativeControl(ac);
+ }
+
+ if (e.OldElement != null)
+ {
+ }
+
+ if (e.NewElement != null)
+ {
+ }
+
+ base.OnElementChanged(e);
+ }
+
+ void UpdateColor()
+ {
+ Control.Color = (Element.Color == Color.Default) ? s_defaultColor : Element.Color.ToNative();
+ }
+
+ void UpdateIsRunning()
+ {
+ if (Element.IsRunning)
+ {
+ Control.PlayPulse();
+ }
+ else
+ {
+ Control.StopPulse();
+ }
+ }
+
+ };
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Renderers/BoxViewRenderer.cs b/Xamarin.Forms.Platform.Tizen/Renderers/BoxViewRenderer.cs
new file mode 100644
index 0000000..7a9f756
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Renderers/BoxViewRenderer.cs
@@ -0,0 +1,61 @@
+using System.ComponentModel;
+using EColor = ElmSharp.Color;
+using ERectangle = ElmSharp.Rectangle;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ public class BoxViewRenderer :
+ VisualElementRenderer<BoxView>
+ {
+ static readonly EColor s_defaultColor = EColor.Transparent;
+
+ ERectangle _control;
+
+ public BoxViewRenderer()
+ {
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<BoxView> e)
+ {
+ if (_control == null)
+ {
+ _control = new ERectangle(Forms.Context.MainWindow);
+ SetNativeControl(_control);
+ }
+
+ if (e.OldElement != null)
+ {
+ }
+
+ if (e.NewElement != null)
+ {
+ UpdateColor();
+ }
+
+ base.OnElementChanged(e);
+ }
+
+ void UpdateColor()
+ {
+ Color colorToSet = Element.Color;
+
+ if (colorToSet == Color.Default)
+ {
+ colorToSet = Element.BackgroundColor;
+ }
+
+ _control.Color = (colorToSet == Color.Default) ? s_defaultColor : colorToSet.ToNative();
+ }
+
+ protected override void OnElementPropertyChanged(object sender,
+ PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == BoxView.ColorProperty.PropertyName ||
+ e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName)
+ {
+ UpdateColor();
+ }
+ base.OnElementPropertyChanged(sender, e);
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Renderers/ButtonRenderer.cs b/Xamarin.Forms.Platform.Tizen/Renderers/ButtonRenderer.cs
new file mode 100644
index 0000000..a34df54
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Renderers/ButtonRenderer.cs
@@ -0,0 +1,95 @@
+using System;
+using EColor = ElmSharp.Color;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ public class ButtonRenderer : ViewRenderer<Button, Native.Button>
+ {
+ static readonly EColor s_defaultTextColor = EColor.White;
+
+ public ButtonRenderer()
+ {
+ RegisterPropertyHandler(Button.TextProperty, UpdateText);
+ RegisterPropertyHandler(Button.FontFamilyProperty, UpdateText);
+ RegisterPropertyHandler(Button.FontSizeProperty, UpdateText);
+ RegisterPropertyHandler(Button.FontAttributesProperty, UpdateText);
+ RegisterPropertyHandler(Button.TextColorProperty, UpdateTextColor);
+ RegisterPropertyHandler(Button.ImageProperty, UpdateBitmap);
+ RegisterPropertyHandler(Button.BorderColorProperty, UpdateBorder);
+ RegisterPropertyHandler(Button.BorderRadiusProperty, UpdateBorder);
+ RegisterPropertyHandler(Button.BorderWidthProperty, UpdateBorder);
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
+ {
+ if (Control == null)
+ {
+ var button = new Native.Button(Forms.Context.MainWindow)
+ {
+ PropagateEvents = false,
+ };
+ SetNativeControl(button);
+ }
+
+ if (e.OldElement != null)
+ {
+ Control.Clicked -= ButtonClickedHandler;
+ }
+
+ if (e.NewElement != null)
+ {
+ Control.Clicked += ButtonClickedHandler;
+ }
+
+ base.OnElementChanged(e);
+ }
+
+ protected override Size MinimumSize()
+ {
+ return new Size(Control.MinimumWidth, Control.MinimumHeight);
+ }
+
+ void ButtonClickedHandler(object sender, EventArgs e)
+ {
+ IButtonController btn = Element as IButtonController;
+ if (btn != null)
+ {
+ btn.SendClicked();
+ }
+ }
+
+ void UpdateText()
+ {
+ Control.Text = Element.Text ?? "";
+ Control.FontSize = Element.FontSize;
+ Control.FontAttributes = Element.FontAttributes;
+ Control.FontFamily = Element.FontFamily;
+ }
+
+ void UpdateTextColor()
+ {
+ Control.TextColor = Element.TextColor.IsDefault ? s_defaultTextColor : Element.TextColor.ToNative();
+ }
+
+ void UpdateBitmap()
+ {
+ if (!string.IsNullOrEmpty(Element.Image))
+ {
+ Control.Image = new Native.Image(Control);
+ Control.Image.LoadFromImageSourceAsync(Element.Image);
+ }
+ else
+ {
+ Control.Image = null;
+ }
+ }
+
+ void UpdateBorder()
+ {
+ /* The simpler way is to create some specialized theme for button in
+ * tizen-theme
+ */
+ // TODO: implement border handling
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Renderers/CarouselPageRenderer.cs b/Xamarin.Forms.Platform.Tizen/Renderers/CarouselPageRenderer.cs
new file mode 100644
index 0000000..812b8ab
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Renderers/CarouselPageRenderer.cs
@@ -0,0 +1,259 @@
+using System;
+using ElmSharp;
+using EColor = ElmSharp.Color;
+using ERectangle = ElmSharp.Rectangle;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ /// <summary>
+ /// Renderer of a CarouselPage widget.
+ /// </summary>
+ public class CarouselPageRenderer : VisualElementRenderer<CarouselPage>, IVisualElementRenderer
+ {
+ /// <summary>
+ /// The minimum length of a swipe to be recognized as a page switching command, in screen pixels unit.
+ /// </summary>
+ public static readonly double s_minimumSwipeLengthX = 200.0;
+
+ // Different levels of "difficulty" in making a valid swipe gesture, determined by a maximum absolute value
+ // of an angle between a line formed by the swipe gesture and the horizontal axis, in arc degrees:
+ public static readonly double s_challengeEasyArcDegrees = 25.0;
+ public static readonly double s_challengeComfortableArcDegrees = 20.0;
+ public static readonly double s_challengeStandardArcDegrees = 15.0;
+ public static readonly double s_challengeHardArcDegrees = 10.0;
+
+ /// <summary>
+ /// The maximum allowed angle between a line formed by the swipe gesture and the horizontal axis, in arc degrees.
+ /// The gesture will be recognized as a page switching command if its angle does not exceed this value.
+ /// </summary>
+ public static readonly double s_thresholdSwipeArcDegrees = s_challengeComfortableArcDegrees;
+
+ /// <summary>
+ /// The tangent of a maximum allowed angle between the swipe line and the horizontal axis.
+ /// </summary>
+ public static readonly double s_thresholdSwipeTangent = Math.Tan(s_thresholdSwipeArcDegrees * (Math.PI / 180.0));
+
+ // A master container for the entire widget:
+ protected Box _box;
+
+ // Used for grabbing gestures over the entire screen, even if Page is smaller than it:
+ protected ERectangle _filler;
+
+ protected GestureLayer _gestureLayer;
+ protected EvasObject _page;
+
+ /// <summary>
+ /// The default constructor.
+ /// </summary>
+ public CarouselPageRenderer()
+ {
+ }
+
+ /// <summary>
+ /// Invoked whenever the CarouselPage element has been changed in Xamarin.
+ /// </summary>
+ /// <param name="e">Event parameters.</param>
+ protected override void OnElementChanged(ElementChangedEventArgs<CarouselPage> e)
+ {
+ if (NativeView == null)
+ {
+ // Creates an overlaying box which serves as a container
+ // for both page and a gesture handling layer:
+ _box = new Box(Forms.Context.MainWindow)
+ {
+ IsHorizontal = false,
+ };
+ _box.SetAlignment(-1, -1);
+ _box.SetWeight(1, 1);
+ _box.Show();
+
+ // Disallows the Box to lay out its contents. They will be laid out manually,
+ // because the page has to overlay the conformant rectangle. By default
+ // Box will lay its contents in a stack. Applying an empty method disables it:
+ _box.SetLayoutCallback(() => {
+ ResizeContentsToFullScreen();
+ });
+
+ // Creates a Rectangle used for ensuring that the gestures will get recognized:
+ _filler = new ERectangle(Forms.Context.MainWindow)
+ {
+ Color = EColor.Transparent,
+ };
+ _filler.SetAlignment(-1, -1);
+ _filler.SetWeight(1, 1);
+ _filler.Show();
+ _box.PackEnd(_filler);
+
+ // Creates a GestureLayer used for swipe gestures recognition and attaches it to the Box:
+ _gestureLayer = new GestureLayer(_box);
+ _gestureLayer.Attach(_box);
+ AddLineGestureHandler();
+
+ SetNativeControl(_box);
+ }
+
+ if (e.OldElement != null)
+ {
+ Element.CurrentPageChanged -= OnCurrentPageChanged;
+ }
+
+ if (e.NewElement != null)
+ {
+ Element.CurrentPageChanged += OnCurrentPageChanged;
+ }
+
+ // If pages have been added to the Xamarin widget and the user has not explicitly
+ // marked one of them to be displayed, displays the first one:
+ if (_page == null && Element.Children.Count > 0)
+ {
+ DisplayPage(Element.Children[0]);
+ }
+
+ base.OnElementChanged(e);
+ }
+
+ /// <summary>
+ /// Called just before the associated element is deleted.
+ /// </summary>
+ /// <param name="disposing">True if the memory release was requested on demand.</param>
+ protected override void Dispose(bool disposing)
+ {
+ if (_box != null)
+ {
+ Element.CurrentPageChanged -= OnCurrentPageChanged;
+
+ // Unpacks the page from the box to prevent it from being disposed of prematurely:
+ _box.UnPack(_page);
+
+ _box.Unrealize();
+ _box = null;
+ }
+
+ base.Dispose(disposing);
+ }
+
+ /// <summary>
+ /// Handles the process of switching between the displayed pages.
+ /// </summary>
+ /// <param name="sender">An object originating the request</param>
+ /// <param name="ea">Additional arguments to the event handler</param>
+ void OnCurrentPageChanged(object sender, EventArgs ea)
+ {
+ if (_page != null)
+ {
+ _page.Hide();
+ _box.UnPack(_page);
+ }
+
+ DisplayPage(Element.CurrentPage);
+ ResizeContentsToFullScreen();
+ }
+
+ /// <summary>
+ /// Gets the index of the currently displayed page in Element.Children collection.
+ /// </summary>
+ /// <returns>An int value representing the index of the page currently displayed,
+ /// or -1 if no page is being displayed currently.</returns>
+ int GetCurrentPageIndex()
+ {
+ int index = -1;
+ for (int k = 0; k < Element.Children.Count; ++k)
+ {
+ if (Element.Children[k] == Element.CurrentPage)
+ {
+ index = k;
+ break;
+ }
+ }
+
+ return index;
+ }
+
+ /// <summary>
+ /// Resizes the widget's contents to utilize all the available screen space.
+ /// </summary>
+ void ResizeContentsToFullScreen()
+ {
+ // Box's geometry should match Forms.Context.MainWindow's geometry
+ // minus the space occupied by the top toolbar.
+ // Applies Box's geometry to both displayed page and conformant rectangle:
+ _filler.Geometry = _page.Geometry = _box.Geometry;
+ }
+
+ /// <summary>
+ /// Adds the feature of recognizing swipes to the GestureLayer.
+ /// </summary>
+ void AddLineGestureHandler()
+ {
+ _gestureLayer.SetLineCallback(GestureLayer.GestureState.End, (line) => {
+ double horizontalDistance = line.X2 - line.X1;
+ double verticalDistance = line.Y2 - line.Y1;
+
+ // Determines whether the movement is long enough to be considered a swipe:
+ bool isLongEnough = (Math.Abs(horizontalDistance) >= s_minimumSwipeLengthX);
+
+ // Determines whether the movement is horizontal enough to be considered as a swipe:
+ // The swipe arc's tangent value (v/h) needs to be lesser than or equal to the threshold value.
+ // This approach allows for getting rid of computationally heavier atan2() function.
+ double angleTangent = Math.Abs(verticalDistance) / horizontalDistance;
+ bool isDirectionForward = (angleTangent < 0);
+
+ // Determines whether the movement has been recognized as a valid swipe:
+ bool isSwipeMatching = (isLongEnough && (Math.Abs(angleTangent) <= s_thresholdSwipeTangent));
+
+ if (isSwipeMatching)
+ {
+ // TODO: Unsure whether changes made via ItemsSource/ItemTemplate properties will be handled correctly this way.
+ // If not, it should be implemented in another method.
+ if (isDirectionForward)
+ {
+ // Tries to switch the page to the next one:
+ int currentPageIndex = GetCurrentPageIndex();
+ if (currentPageIndex < Element.Children.Count - 1)
+ {
+ // Sets the current page to the next one:
+ Element.CurrentPage = Element.Children[currentPageIndex + 1];
+ }
+ else
+ {
+ // Reacts to the case of forward-swiping when the last page is already being displayed:
+ Log.Debug("CarouselPage: Displaying the last page already - can not revolve further.");
+
+ // Note (TODO): Once we have a more sophisticated renderer able to e.g. display the animation
+ // of revolving Pages or at least indicate current overall position, some visual feedback
+ // should be provided here for the user who has haplessly tried to access a nonexistent page.
+ }
+ }
+ else
+ {
+ // Tries to switch the page to the previous one:
+ int currentPageIndex = GetCurrentPageIndex();
+ if (currentPageIndex > 0)
+ {
+ // Sets the current page to the previous one:
+ Element.CurrentPage = Element.Children[currentPageIndex - 1];
+ }
+ else
+ {
+ // Reacts to the case of backward-swiping when the first page is already being displayed:
+ Log.Debug("CarouselPage: The first page is already being displayed - can not revolve further.");
+
+ // Note (TODO): (The same as in case of scrolling forwards)
+ }
+ }
+ }
+ });
+ }
+
+ void DisplayPage(ContentPage p)
+ {
+ _page = Platform.GetOrCreateRenderer(p).NativeView;
+ _page.SetAlignment(-1, -1);
+ _page.SetWeight(1, 1);
+ _page.Show();
+ _box.PackEnd(_page);
+ }
+
+ }
+}
+
diff --git a/Xamarin.Forms.Platform.Tizen/Renderers/ContentPageRenderer.cs b/Xamarin.Forms.Platform.Tizen/Renderers/ContentPageRenderer.cs
new file mode 100644
index 0000000..c4f7965
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Renderers/ContentPageRenderer.cs
@@ -0,0 +1,65 @@
+using System;
+using EColor = ElmSharp.Color;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ /// <summary>
+ /// Renderer of ContentPage.
+ /// </summary>
+ public class ContentPageRenderer : VisualElementRenderer<ContentPage>
+ {
+ /// <summary>
+ /// Native control which holds the contents.
+ /// </summary>
+ Native.ContentPage _page;
+
+ /// <summary>
+ /// Default constructor.
+ /// </summary>
+ public ContentPageRenderer()
+ {
+ RegisterPropertyHandler(Page.BackgroundImageProperty, UpdateBackgroundImage);
+ RegisterPropertyHandler(Page.TitleProperty, UpdateTitle);
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<ContentPage> e)
+ {
+ if (null == _page)
+ {
+ _page = new Native.ContentPage(Forms.Context.MainWindow);
+ _page.LayoutUpdated += new EventHandler<Native.LayoutEventArgs>(OnLayoutUpdated);
+ SetNativeControl(_page);
+ }
+
+ base.OnElementChanged(e);
+ }
+
+ protected override void UpdateBackgroundColor()
+ {
+ // base.UpdateBackgroundColor() is not called on purpose, we don't want the regular background setting
+ if (Element.BackgroundColor.IsDefault || Element.BackgroundColor.A == 0)
+ _page.Color = EColor.Transparent;
+ else
+ _page.Color = Element.BackgroundColor.ToNative();
+ }
+
+ void UpdateBackgroundImage()
+ {
+ if (string.IsNullOrWhiteSpace(Element.BackgroundImage))
+ _page.File = null;
+ else
+ _page.File = ResourcePath.GetPath(Element.BackgroundImage);
+ }
+
+ void UpdateTitle()
+ {
+ _page.Title = Element.Title;
+ }
+
+ void OnLayoutUpdated(object sender, Native.LayoutEventArgs e)
+ {
+ DoLayout(e);
+ }
+ }
+}
+
diff --git a/Xamarin.Forms.Platform.Tizen/Renderers/DatePickerRenderer.cs b/Xamarin.Forms.Platform.Tizen/Renderers/DatePickerRenderer.cs
new file mode 100644
index 0000000..aed7b9d
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Renderers/DatePickerRenderer.cs
@@ -0,0 +1,77 @@
+using System;
+using EColor = ElmSharp.Color;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ public class DatePickerRenderer : ViewRenderer<DatePicker, Native.Button>
+ {
+ //TODO need to add internationalization support
+ const string DialogTitle = "Choose Date";
+ static readonly EColor s_defaultTextColor = EColor.White;
+
+ public DatePickerRenderer()
+ {
+ RegisterPropertyHandler(DatePicker.DateProperty, UpdateDate);
+ RegisterPropertyHandler(DatePicker.FormatProperty, UpdateDate);
+ RegisterPropertyHandler(DatePicker.TextColorProperty, UpdateTextColor);
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<DatePicker> e)
+ {
+ if (Control == null)
+ {
+ var button = new Native.Button(Forms.Context.MainWindow);
+ SetNativeControl(button);
+ }
+
+ if (e.OldElement != null)
+ {
+ Control.Clicked -= ButtonClickedHandler;
+ }
+
+ if (e.NewElement != null)
+ {
+ Control.Clicked += ButtonClickedHandler;
+ }
+
+ base.OnElementChanged(e);
+ }
+
+ void ButtonClickedHandler(object sender, EventArgs e)
+ {
+ Native.DateTimePickerDialog dialog = new Native.DateTimePickerDialog(Forms.Context.MainWindow)
+ {
+ Title = DialogTitle
+ };
+
+ dialog.InitializeDatePicker(Element.Date, Element.MinimumDate, Element.MaximumDate);
+ dialog.DateTimeChanged += DialogDateTimeChangedHandler;
+ dialog.Dismissed += DialogDismissedHandler;
+ dialog.Show();
+ }
+
+ void DialogDateTimeChangedHandler(object sender, Native.DateChangedEventArgs dcea)
+ {
+ Element.Date = dcea.NewDate;
+ Control.Text = dcea.NewDate.ToString(Element.Format);
+ }
+
+ void DialogDismissedHandler(object sender, EventArgs e)
+ {
+ var dialog = sender as Native.DateTimePickerDialog;
+ dialog.DateTimeChanged -= DialogDateTimeChangedHandler;
+ dialog.Dismissed -= DialogDismissedHandler;
+ }
+
+ void UpdateDate()
+ {
+ Control.Text = Element.Date.ToString(Element.Format);
+ }
+
+ void UpdateTextColor()
+ {
+ Control.TextColor = Element.TextColor.IsDefault ? s_defaultTextColor : Element.TextColor.ToNative();
+ }
+
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Renderers/EditorRenderer.cs b/Xamarin.Forms.Platform.Tizen/Renderers/EditorRenderer.cs
new file mode 100644
index 0000000..0f21463
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Renderers/EditorRenderer.cs
@@ -0,0 +1,88 @@
+using System;
+using EColor = ElmSharp.Color;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ public class EditorRenderer : ViewRenderer<Editor, Native.Entry>
+ {
+ static readonly EColor s_defaultTextColor = EColor.Black;
+
+ public EditorRenderer()
+ {
+ RegisterPropertyHandler(Editor.TextProperty, UpdateText);
+ RegisterPropertyHandler(Editor.TextColorProperty, UpdateTextColor);
+ RegisterPropertyHandler(Editor.FontSizeProperty, UpdateFontSize);
+ RegisterPropertyHandler(Editor.FontFamilyProperty, UpdateFontFamily);
+ RegisterPropertyHandler(Editor.FontAttributesProperty, UpdateFontAttributes);
+ RegisterPropertyHandler(Editor.KeyboardProperty, UpdateKeyboard);
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
+ {
+ if (Control == null)
+ {
+ var entry = new Native.Entry(Forms.Context.MainWindow)
+ {
+ IsSingleLine = false,
+ PropagateEvents = false,
+ };
+ SetNativeControl(entry);
+ }
+
+ if (e.OldElement != null)
+ {
+ Control.TextChanged -= TextChanged;
+ Control.Unfocused -= Completed;
+ }
+
+ if (e.NewElement != null)
+ {
+ Control.TextChanged += TextChanged;
+ Control.Unfocused += Completed;
+ }
+
+ base.OnElementChanged(e);
+ }
+
+ void TextChanged(object sender, EventArgs e)
+ {
+ Element.Text = ((Native.Entry)sender).Text;
+ }
+
+ void Completed(object sender, EventArgs e)
+ {
+ Element.SendCompleted();
+ }
+
+ void UpdateText()
+ {
+ Control.Text = Element.Text;
+ }
+
+ void UpdateTextColor()
+ {
+ Control.TextColor = Element.TextColor.IsDefault ? s_defaultTextColor : Element.TextColor.ToNative();
+ }
+
+ void UpdateFontSize()
+ {
+ Control.FontSize = Element.FontSize;
+ }
+
+ void UpdateFontFamily()
+ {
+ Control.FontFamily = Element.FontFamily;
+ }
+
+ void UpdateFontAttributes()
+ {
+ Control.FontAttributes = Element.FontAttributes;
+ }
+
+ void UpdateKeyboard()
+ {
+ Control.Keyboard = Element.Keyboard.ToNative();
+ }
+ }
+}
+
diff --git a/Xamarin.Forms.Platform.Tizen/Renderers/EntryRenderer.cs b/Xamarin.Forms.Platform.Tizen/Renderers/EntryRenderer.cs
new file mode 100644
index 0000000..95828c0
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Renderers/EntryRenderer.cs
@@ -0,0 +1,127 @@
+using System;
+using EColor = ElmSharp.Color;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ public class EntryRenderer : ViewRenderer<Entry, Native.Entry>, IDisposable
+ {
+ static readonly EColor s_defaultTextColor = EColor.Black;
+
+ static readonly EColor s_defaultPlaceholderColor = EColor.Gray;
+
+ public EntryRenderer()
+ {
+ RegisterPropertyHandler(Entry.IsPasswordProperty, UpdateIsPassword);
+ RegisterPropertyHandler(Entry.TextProperty, UpdateText);
+ RegisterPropertyHandler(Entry.TextColorProperty, UpdateTextColor);
+ RegisterPropertyHandler(Entry.FontSizeProperty, UpdateFontSize);
+ RegisterPropertyHandler(Entry.FontFamilyProperty, UpdateFontFamily);
+ RegisterPropertyHandler(Entry.FontAttributesProperty, UpdateFontAttributes);
+ RegisterPropertyHandler(Entry.HorizontalTextAlignmentProperty, UpdateHorizontalTextAlignment);
+ RegisterPropertyHandler(Entry.KeyboardProperty, UpdateKeyboard);
+ RegisterPropertyHandler(Entry.PlaceholderProperty, UpdatePlaceholder);
+ RegisterPropertyHandler(Entry.PlaceholderColorProperty, UpdatePlaceholderColor);
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
+ {
+ if (Control == null)
+ {
+ var entry = new Native.Entry(Forms.Context.MainWindow)
+ {
+ IsSingleLine = true,
+ PropagateEvents = false,
+ };
+ SetNativeControl(entry);
+ }
+
+ if (e.OldElement != null)
+ {
+ Control.TextChanged -= EntryChangedHandler;
+ Control.Activated -= EntryCompletedHandler;
+ }
+
+ if (e.NewElement != null)
+ {
+ Control.TextChanged += EntryChangedHandler;
+ Control.Activated += EntryCompletedHandler;
+ }
+
+ base.OnElementChanged(e);
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (null != Control)
+ {
+ Control.TextChanged -= EntryChangedHandler;
+ Control.Activated -= EntryCompletedHandler;
+ }
+
+ base.Dispose(disposing);
+ }
+
+ void EntryChangedHandler(object sender, EventArgs e)
+ {
+ Element.Text = Control.Text;
+ }
+
+ void EntryCompletedHandler(object sender, EventArgs e)
+ {
+ //TODO Consider if any other object should overtake focus
+ Control.SetFocus(false);
+
+ ((IEntryController)Element).SendCompleted();
+ }
+
+ void UpdateIsPassword()
+ {
+ Control.IsPassword = Element.IsPassword;
+ }
+
+ void UpdateText()
+ {
+ Control.Text = Element.Text;
+ }
+
+ void UpdateTextColor()
+ {
+ Control.TextColor = Element.TextColor.IsDefault ? s_defaultTextColor : Element.TextColor.ToNative();
+ }
+
+ void UpdateFontSize()
+ {
+ Control.FontSize = Element.FontSize;
+ }
+
+ void UpdateFontFamily()
+ {
+ Control.FontFamily = Element.FontFamily;
+ }
+
+ void UpdateFontAttributes()
+ {
+ Control.FontAttributes = Element.FontAttributes;
+ }
+
+ void UpdateHorizontalTextAlignment()
+ {
+ Control.HorizontalTextAlignment = Element.HorizontalTextAlignment.ToNative();
+ }
+
+ void UpdateKeyboard()
+ {
+ Control.Keyboard = Element.Keyboard.ToNative();
+ }
+
+ void UpdatePlaceholder()
+ {
+ Control.Placeholder = Element.Placeholder;
+ }
+
+ void UpdatePlaceholderColor()
+ {
+ Control.PlaceholderColor = Element.PlaceholderColor.IsDefault ? s_defaultPlaceholderColor : Element.PlaceholderColor.ToNative();
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Renderers/EvasObjectWrapperRenderer.cs b/Xamarin.Forms.Platform.Tizen/Renderers/EvasObjectWrapperRenderer.cs
new file mode 100644
index 0000000..4e11c27
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Renderers/EvasObjectWrapperRenderer.cs
@@ -0,0 +1,32 @@
+using ESize = ElmSharp.Size;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ public class EvasObjectWrapperRenderer : VisualElementRenderer<EvasObjectWrapper>
+ {
+ protected override void OnElementChanged(ElementChangedEventArgs<EvasObjectWrapper> e)
+ {
+ if (NativeView == null)
+ {
+ SetNativeControl(Element.EvasObject);
+ }
+
+ base.OnElementChanged(e);
+ }
+
+ protected override ESize Measure(int availableWidth, int availableHeight)
+ {
+ if (Element?.MeasureDelegate == null)
+ {
+ return base.Measure(availableWidth, availableHeight);
+ }
+
+ // The user has specified a different implementation of MeasureDelegate
+ ESize? result = Element.MeasureDelegate(this, availableWidth, availableHeight);
+
+ // If the delegate returns a ElmSharp.Size, we use it; if it returns null,
+ // fall back to the default implementation
+ return result ?? base.Measure(availableWidth, availableHeight);
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Renderers/FrameRenderer.cs b/Xamarin.Forms.Platform.Tizen/Renderers/FrameRenderer.cs
new file mode 100644
index 0000000..0684c4a
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Renderers/FrameRenderer.cs
@@ -0,0 +1,124 @@
+using ElmSharp;
+using EColor = ElmSharp.Color;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ public class FrameRenderer : ViewRenderer<Frame, Native.Canvas>
+ {
+ const int _thickness = 2;
+ const int _shadow_shift = 2;
+ const int _shadow_thickness = _thickness + 2;
+
+ static readonly EColor s_DefaultColor = EColor.Black;
+ static readonly EColor s_ShadowColor = EColor.FromRgba(80, 80, 80, 50);
+
+ Polygon _shadow = null;
+ Polygon _frame = null;
+
+ public FrameRenderer()
+ {
+ RegisterPropertyHandler(Frame.OutlineColorProperty, UpdateColor);
+ RegisterPropertyHandler(Frame.HasShadowProperty, UpdateShadowVisibility);
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<Frame> e)
+ {
+ if (Control == null)
+ {
+ SetNativeControl(new Native.Canvas(Forms.Context.MainWindow));
+
+ _shadow = new Polygon(NativeView);
+ _shadow.Color = s_ShadowColor;
+ Control.Children.Add(_shadow);
+
+ _frame = new Polygon(NativeView);
+ _frame.Show();
+ Control.Children.Add(_frame);
+ }
+
+ if (e.OldElement != null)
+ {
+ Control.LayoutUpdated -= OnLayoutUpdated;
+ }
+
+ if (e.NewElement != null)
+ {
+ Control.LayoutUpdated += OnLayoutUpdated;
+ }
+
+ base.OnElementChanged(e);
+ }
+
+ void OnLayoutUpdated(object sender, Native.LayoutEventArgs e)
+ {
+ UpdateGeometry();
+ // TODO: why is this DoLayout() required?
+ if (Element.Content != null)
+ base.DoLayout(e);
+ }
+
+ void UpdateGeometry()
+ {
+ var geometry = NativeView.Geometry;
+ DrawFrame(_frame,
+ geometry.X,
+ geometry.Y,
+ geometry.Right,
+ geometry.Bottom,
+ _thickness
+ );
+ DrawFrame(_shadow,
+ geometry.X + _shadow_shift,
+ geometry.Y + _shadow_shift,
+ geometry.Right - _thickness + _shadow_shift + _shadow_thickness,
+ geometry.Bottom - _thickness + _shadow_shift + _shadow_thickness,
+ _shadow_thickness
+ );
+ }
+
+ void DrawFrame(Polygon frame, int left, int top, int right, int bottom, int thickness)
+ {
+ frame.ClearPoints();
+ if (left + thickness >= right || top + thickness >= bottom)
+ {
+ if (left >= right || top >= bottom)
+ return;
+ // shape reduces to a rectangle
+ frame.AddPoint(left, top);
+ frame.AddPoint(right, top);
+ frame.AddPoint(right, bottom);
+ frame.AddPoint(left, bottom);
+ return;
+ }
+ // outside edge
+ frame.AddPoint(left, top);
+ frame.AddPoint(right, top);
+ frame.AddPoint(right, bottom);
+ frame.AddPoint(left, bottom);
+ frame.AddPoint(left, top + thickness);
+ // and inside edge
+ frame.AddPoint(left + thickness, top + thickness);
+ frame.AddPoint(left + thickness, bottom - thickness);
+ frame.AddPoint(right - thickness, bottom - thickness);
+ frame.AddPoint(right - thickness, top + thickness);
+ frame.AddPoint(left, top + thickness);
+ }
+
+ void UpdateColor()
+ {
+ if (Element.OutlineColor.IsDefault)
+ _frame.Color = s_DefaultColor;
+ else
+ _frame.Color = Element.OutlineColor.ToNative();
+ }
+
+ void UpdateShadowVisibility()
+ {
+ if (Element.HasShadow)
+ _shadow.Show();
+ else
+ _shadow.Hide();
+ }
+
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Renderers/IVisualElementRenderer.cs b/Xamarin.Forms.Platform.Tizen/Renderers/IVisualElementRenderer.cs
new file mode 100644
index 0000000..4702fd3
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Renderers/IVisualElementRenderer.cs
@@ -0,0 +1,39 @@
+using System;
+using ElmSharp;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ /// <summary>
+ /// Base interface for VisualElement renderer.
+ /// </summary>
+ public interface IVisualElementRenderer : IRegisterable, IDisposable
+ {
+ /// <summary>
+ /// Gets the VisualElement associated with this renderer.
+ /// </summary>
+ /// <value>The VisualElement.</value>
+ VisualElement Element
+ {
+ get;
+ }
+
+ /// <summary>
+ /// Gets the native view associated with this renderer.
+ /// </summary>
+ /// <value>The native view.</value>
+ EvasObject NativeView
+ {
+ get;
+ }
+
+ /// <summary>
+ /// Sets the VisualElement associated with this renderer.
+ /// </summary>
+ /// <param name="element">New element.</param>
+ void SetElement(VisualElement element);
+
+ SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint);
+
+ void UpdateNativeGeometry();
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Renderers/ImageRenderer.cs b/Xamarin.Forms.Platform.Tizen/Renderers/ImageRenderer.cs
new file mode 100644
index 0000000..ca06a66
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Renderers/ImageRenderer.cs
@@ -0,0 +1,105 @@
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ public class ImageRenderer : ViewRenderer<Image, Native.Image>
+ {
+ public ImageRenderer()
+ {
+ RegisterPropertyHandler(Image.SourceProperty, UpdateSource);
+ RegisterPropertyHandler(Image.AspectProperty, UpdateAspect);
+ RegisterPropertyHandler(Image.IsOpaqueProperty, UpdateIsOpaque);
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<Image> e)
+ {
+ if (Control == null)
+ {
+ var image = new Native.Image(Forms.Context.MainWindow);
+ SetNativeControl(image);
+ }
+
+ base.OnElementChanged(e);
+ }
+
+ async void UpdateSource()
+ {
+ ImageSource source = Element.Source;
+
+ ((IImageController)Element).SetIsLoading(true);
+
+ if (Control != null)
+ {
+ bool success = await Control.LoadFromImageSourceAsync(source);
+ if (!IsDisposed && success)
+ ((IVisualElementController)Element).NativeSizeChanged();
+ }
+
+ if (!IsDisposed)
+ ((IImageController)Element).SetIsLoading(false);
+ }
+
+ void UpdateAspect()
+ {
+ Control.Aspect = Element.Aspect;
+ }
+
+ void UpdateIsOpaque()
+ {
+ Control.IsOpaque = Element.IsOpaque;
+ }
+ }
+
+ public interface IImageSourceHandler : IRegisterable
+ {
+ Task<bool> LoadImageAsync(Native.Image image, ImageSource imageSource, CancellationToken cancelationToken = default(CancellationToken));
+ }
+
+ public sealed class FileImageSourceHandler : IImageSourceHandler
+ {
+ public Task<bool> LoadImageAsync(Native.Image image, ImageSource imageSource, CancellationToken cancelationToken = default(CancellationToken))
+ {
+ var filesource = imageSource as FileImageSource;
+ if (filesource != null)
+ {
+ string file = filesource.File;
+ if (!string.IsNullOrEmpty(file))
+ return image.LoadAsync(ResourcePath.GetPath(file), cancelationToken);
+ }
+ return Task.FromResult<bool>(false);
+ }
+ }
+
+ public sealed class StreamImageSourceHandler : IImageSourceHandler
+ {
+ public async Task<bool> LoadImageAsync(Native.Image image, ImageSource imageSource, CancellationToken cancelationToken = default(CancellationToken))
+ {
+ var streamsource = imageSource as StreamImageSource;
+ if (streamsource != null && streamsource.Stream != null)
+ {
+ using (var streamImage = await ((IStreamImageSource)streamsource).GetStreamAsync(cancelationToken))
+ {
+ if (streamImage != null)
+ return await image.LoadAsync(streamImage, cancelationToken);
+ }
+ }
+ return false;
+ }
+ }
+
+ public sealed class UriImageSourceHandler : IImageSourceHandler
+ {
+ public Task<bool> LoadImageAsync(Native.Image image, ImageSource imageSource, CancellationToken cancelationToken = default(CancellationToken))
+ {
+ var urisource = imageSource as UriImageSource;
+ if (urisource != null && urisource.Uri != null)
+ {
+ return image.LoadAsync(urisource.Uri, cancelationToken);
+ }
+
+ return Task.FromResult<bool>(false);
+ }
+ }
+}
+
diff --git a/Xamarin.Forms.Platform.Tizen/Renderers/LabelRenderer.cs b/Xamarin.Forms.Platform.Tizen/Renderers/LabelRenderer.cs
new file mode 100644
index 0000000..5a4744f
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Renderers/LabelRenderer.cs
@@ -0,0 +1,103 @@
+using EColor = ElmSharp.Color;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+
+ public class LabelRenderer : ViewRenderer<Label, Native.Label>
+ {
+ static readonly EColor s_defaultBackgroundColor = EColor.Transparent;
+ static readonly EColor s_defaultForegroundColor = EColor.Black;
+ static readonly EColor s_defaultTextColor = s_defaultForegroundColor;
+
+ public LabelRenderer()
+ {
+ RegisterPropertyHandler(Label.TextProperty, () => Control.Text = Element.Text);
+ RegisterPropertyHandler(Label.TextColorProperty, UpdateTextColor);
+ // FontProperty change is called also for FontSizeProperty, FontFamilyProperty and FontAttributesProperty change
+ RegisterPropertyHandler(Label.FontProperty, UpdateFontProperties);
+ RegisterPropertyHandler(Label.LineBreakModeProperty, UpdateLineBreakMode);
+ RegisterPropertyHandler(Label.HorizontalTextAlignmentProperty, UpdateTextAlignment);
+ RegisterPropertyHandler(Label.VerticalTextAlignmentProperty, UpdateTextAlignment);
+ RegisterPropertyHandler(Label.FormattedTextProperty, () => Control.FormattedText = ConvertFormattedText(Element.FormattedText));
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
+ {
+ if (Control == null)
+ {
+ var label = new Native.Label(Forms.Context.MainWindow);
+ base.SetNativeControl(label);
+ }
+
+ if (e.OldElement != null)
+ {
+ }
+
+ if (e.NewElement != null)
+ {
+ }
+
+ base.OnElementChanged(e);
+ }
+
+ protected override Size MinimumSize()
+ {
+ return new Size(Control.MinimumWidth, Control.MinimumHeight);
+ }
+
+ Native.FormattedString ConvertFormattedText(FormattedString formattedString)
+ {
+ if (formattedString == null)
+ {
+ return null;
+ }
+
+ Native.FormattedString nativeString = new Native.FormattedString();
+
+ foreach (var element in formattedString.Spans)
+ {
+ Native.Span span = new Native.Span();
+ span.FormattedText = element.Text;
+ span.FontAttributes = element.FontAttributes;
+ span.FontFamily = element.FontFamily;
+ span.FontSize = element.FontSize;
+ span.ForegroundColor = element.ForegroundColor.IsDefault ? s_defaultForegroundColor : element.ForegroundColor.ToNative();
+ span.BackgroundColor = element.BackgroundColor.IsDefault ? s_defaultBackgroundColor : element.BackgroundColor.ToNative();
+
+ nativeString.Spans.Add(span);
+ }
+
+ return nativeString;
+ }
+
+ void UpdateTextColor()
+ {
+ Control.TextColor = Element.TextColor.IsDefault ? s_defaultTextColor : Element.TextColor.ToNative();
+ }
+
+ void UpdateTextAlignment()
+ {
+ Control.HorizontalTextAlignment = Element.HorizontalTextAlignment.ToNative();
+ Control.VerticalTextAlignment = Element.VerticalTextAlignment.ToNative();
+ }
+
+ void UpdateFontProperties()
+ {
+ Control.FontSize = Element.FontSize;
+ Control.FontAttributes = Element.FontAttributes;
+ Control.FontFamily = Element.FontFamily;
+ }
+
+ void UpdateLineBreakMode()
+ {
+ if (Element.LineBreakMode == LineBreakMode.CharacterWrap)
+ Control.LineBreakMode = Native.LineBreakMode.CharacterWrap;
+ else if (Element.LineBreakMode == LineBreakMode.WordWrap)
+ Control.LineBreakMode = Native.LineBreakMode.WordWrap;
+ else if (Element.LineBreakMode == LineBreakMode.NoWrap)
+ Control.LineBreakMode = Native.LineBreakMode.NoWrap;
+ else
+ Control.LineBreakMode = Native.LineBreakMode.MixedWrap;
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Renderers/LayoutRenderer.cs b/Xamarin.Forms.Platform.Tizen/Renderers/LayoutRenderer.cs
new file mode 100644
index 0000000..61421c7
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Renderers/LayoutRenderer.cs
@@ -0,0 +1,57 @@
+using System;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ /// <summary>
+ /// Renderer of a Layout.
+ /// </summary>
+ public class LayoutRenderer : ViewRenderer<Layout, Native.Canvas>
+ {
+ /// <summary>
+ /// Default constructor.
+ /// </summary>
+ public LayoutRenderer()
+ {
+ }
+
+ protected override void UpdateLayout()
+ {
+ // in case of layouts we need to make sure that the minimum size of the native control is updated
+ // this is important in case of ScrollView, when it's content is likely to be wider/higher than the window
+ // EFL does not allow control to be larger than the window if it's minimum size is smaller than window dimensions
+ ScrollView scrollView = Element.Parent as ScrollView;
+ if (scrollView != null)
+ {
+ Size size = scrollView.ContentSize;
+ Control.MinimumWidth = ToNativeDimension(Math.Max(size.Width, scrollView.Content.Width));
+ Control.MinimumHeight = ToNativeDimension(Math.Max(size.Height, scrollView.Content.Height));
+ }
+
+ base.UpdateLayout();
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<Layout> e)
+ {
+ if (null == Control)
+ {
+ var canvas = new Native.Canvas(Forms.Context.MainWindow);
+ canvas.LayoutUpdated += OnLayoutUpdated;
+ SetNativeControl(canvas);
+ }
+
+ base.OnElementChanged(e);
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ Control.LayoutUpdated -= OnLayoutUpdated;
+
+ base.Dispose(disposing);
+ }
+
+ void OnLayoutUpdated(object sender, Native.LayoutEventArgs e)
+ {
+ DoLayout(e);
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Renderers/ListViewRenderer.cs b/Xamarin.Forms.Platform.Tizen/Renderers/ListViewRenderer.cs
new file mode 100644
index 0000000..5179d7e
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Renderers/ListViewRenderer.cs
@@ -0,0 +1,437 @@
+using System;
+using System.Collections.Specialized;
+using ElmSharp;
+using EProgressBar = ElmSharp.ProgressBar;
+using ERect = ElmSharp.Rect;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ /// <summary>
+ /// Renderer class for Xamarin ListView class. This provides necessary logic translating
+ /// Xamarin API to Tizen Native API. This is a derivate of a ViewRenderer base class.
+ /// This is a template class with two template parameters. First one is restricted to
+ /// Xamarin.Forms.View and can be accessed via property Element. This represent actual
+ /// xamarin view which represents control logic. Second one is restricted to ElmSharp.Widget
+ /// types, and can be accessed with Control property. This represents actual native control
+ /// which is used to draw control and realize xamarin forms api.
+ /// </summary>
+ public class ListViewRenderer : ViewRenderer<ListView, Native.ListView>, IDisposable
+ {
+ /// <summary>
+ /// Event handler for ScrollToRequested.
+ /// </summary>
+ readonly EventHandler<ScrollToRequestedEventArgs> _scrollToRequested;
+
+ /// <summary>
+ /// Event handler for collection changed.
+ /// </summary>
+ readonly NotifyCollectionChangedEventHandler _collectionChanged;
+
+ /// <summary>
+ /// Event handler for grouped collection changed.
+ /// </summary>
+ readonly NotifyCollectionChangedEventHandler _groupedCollectionChanged;
+
+ /// <summary>
+ /// The _lastSelectedItem and _selectedItemChanging are used for realizing ItemTapped event. Since Xamarin
+ /// needs information only when an item has been taped, native handlers need to be agreagated
+ /// and NotifyRowTapped has to be realized with this.
+ /// </summary>
+
+ GenListItem _lastSelectedItem = null;
+ int _selectedItemChanging = 0;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Xamarin.Forms.Platform.Tizen.ListViewRenderer"/> class.
+ /// Note that at this stage of construction renderer dose not have required native element. This should
+ /// only be used with xamarin engine.
+ /// </summary>
+ public ListViewRenderer()
+ {
+ _scrollToRequested = OnScrollToRequested;
+ _collectionChanged = OnCollectionChanged;
+ _groupedCollectionChanged = OnGroupedCollectionChanged;
+
+ RegisterPropertyHandler(ListView.IsGroupingEnabledProperty, UpdateIsGroupingEnabled);
+ RegisterPropertyHandler(ListView.HasUnevenRowsProperty, UpdateHasUnevenRows);
+ RegisterPropertyHandler(ListView.RowHeightProperty, UpdateRowHeight);
+ RegisterPropertyHandler(ListView.HeaderProperty, UpdateHeader);
+ RegisterPropertyHandler(ListView.SelectedItemProperty, UpdateSelectedItem);
+ RegisterPropertyHandler(ListView.FooterProperty, UpdateFooter);
+ RegisterPropertyHandler(ListView.ItemsSourceProperty, UpdateSource);
+ RegisterPropertyHandler(ListView.FooterTemplateProperty, UpdateFooter);
+ RegisterPropertyHandler(ListView.HeaderTemplateProperty, UpdateHeader);
+ }
+
+ /// <summary>
+ /// Invoked on creation of new ListView renderer. Handles the creation of a native
+ /// element and initialization of the renderer.
+ /// </summary>
+ /// <param name="e"><see cref="Xamarin.Forms.Platform.Tizen.ElementChangedEventArgs"/>.</param>
+ protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
+ {
+ if (Control == null)
+ {
+ SetNativeControl(new Native.ListView(Forms.Context.MainWindow));
+ }
+
+ if (e.OldElement != null)
+ {
+ e.OldElement.ScrollToRequested -= _scrollToRequested;
+ if (Element.IsGroupingEnabled)
+ {
+ e.OldElement.TemplatedItems.GroupedCollectionChanged -= _groupedCollectionChanged;
+ }
+ e.OldElement.TemplatedItems.CollectionChanged -= _collectionChanged;
+ Control.ItemSelected -= ListViewItemSelectedHandler;
+ Control.ItemUnselected -= ListViewItemUnselectedHandler;
+ }
+
+ if (e.NewElement != null)
+ {
+ e.NewElement.ScrollToRequested += _scrollToRequested;
+ Element.TemplatedItems.CollectionChanged += _collectionChanged;
+ Control.ItemSelected += ListViewItemSelectedHandler;
+ Control.ItemUnselected += ListViewItemUnselectedHandler;
+ }
+
+ base.OnElementChanged(e);
+ }
+
+ /// <summary>
+ /// Handles the disposing of an existing renderer instance. Results in event handlers
+ /// being detached and a Dispose() method from base class (VisualElementRenderer) being invoked.
+ /// </summary>
+ /// <param name="disposing">A boolean flag passed to the invocation of base class' Dispose() method.
+ /// <c>True</c> if the memory release was requested on demand.</param>
+ protected override void Dispose(bool disposing)
+ {
+ Element.ScrollToRequested -= _scrollToRequested;
+ Element.TemplatedItems.CollectionChanged -= _collectionChanged;
+ Element.TemplatedItems.GroupedCollectionChanged -= _groupedCollectionChanged;
+
+ base.Dispose(disposing);
+ }
+
+ /// <summary>
+ /// Handles item selected event. Note that it has to handle selection also for grouping mode as well.
+ /// As a result of this method, ItemTapped event should be invoked in Xamarin.
+ /// </summary>
+ /// <param name="sender">A native list instance from which the event has originated.</param>
+ /// <param name="e">Argument associated with handler, it holds native item for which event was raised</param>
+ void ListViewItemSelectedHandler(object sender, GenListItemEventArgs e)
+ {
+ GenListItem item = e.Item;
+
+ _lastSelectedItem = item;
+
+ if (_selectedItemChanging == 0)
+ {
+ if (item != null)
+ {
+ int index = -1;
+ if (Element.IsGroupingEnabled)
+ {
+ Native.ListView.ItemContext itemContext = item.Data as Native.ListView.ItemContext;
+ if (itemContext.IsGroupItem)
+ {
+ return;
+ }
+ else
+ {
+ int groupIndex = (Element.TemplatedItems as System.Collections.IList).IndexOf(itemContext.ListOfSubItems);
+ int inGroupIndex = itemContext.ListOfSubItems.IndexOf(itemContext.Cell);
+
+ ++_selectedItemChanging;
+ Element.NotifyRowTapped(groupIndex, inGroupIndex);
+ --_selectedItemChanging;
+ }
+ }
+ else
+ {
+ index = Element.TemplatedItems.IndexOf((item.Data as Native.ListView.ItemContext).Cell);
+
+ ++_selectedItemChanging;
+ Element.NotifyRowTapped(index);
+ --_selectedItemChanging;
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Handles item unselected event.
+ /// </summary>
+ /// <param name="sender">A native list instance from which the event has originated.</param>
+ /// <param name="e">Argument associated with handler, it holds native item for which event was raised</param>
+ void ListViewItemUnselectedHandler(object sender, GenListItemEventArgs e)
+ {
+ if (_selectedItemChanging == 0)
+ {
+ _lastSelectedItem = null;
+ }
+ }
+
+ /// <summary>
+ /// This is method handles "scroll to" requests from xamarin events.
+ /// It allows for scrolling to specified item on list view.
+ /// </summary>
+ /// <param name="sender">A native list instance from which the event has originated.</param>
+ /// <param name="e">ScrollToRequestedEventArgs.</param>
+ void OnScrollToRequested(object sender, ScrollToRequestedEventArgs e)
+ {
+ Cell cell;
+ int position;
+ var scrollArgs = (ITemplatedItemsListScrollToRequestedEventArgs)e;
+
+ var templatedItems = Element.TemplatedItems;
+ if (Element.IsGroupingEnabled)
+ {
+ var results = templatedItems.GetGroupAndIndexOfItem(scrollArgs.Group, scrollArgs.Item);
+ if (results.Item1 == -1 || results.Item2 == -1)
+ return;
+
+ var group = templatedItems.GetGroup(results.Item1);
+ cell = group[results.Item2];
+ }
+ else
+ {
+ position = templatedItems.GetGlobalIndexOfItem(scrollArgs.Item);
+ cell = templatedItems[position];
+ }
+
+ Control.ApplyScrollTo(cell, e.Position, e.ShouldAnimate);
+ }
+
+ /// <summary>
+ /// Helper class for managing proper postion of Header and Footer element.
+ /// Since both elements need to be implemented with ordinary list elements,
+ /// both header and footer are removed at first, then the list is being modified
+ /// and finally header and footer are prepended and appended to the list, respectively.
+ /// </summary>
+ class HeaderAndFooterHandler : IDisposable
+ {
+ VisualElement headerElement;
+ VisualElement footerElement;
+
+ Native.ListView Control;
+
+ public HeaderAndFooterHandler(Widget control)
+ {
+ Control = control as Native.ListView;
+
+ if (Control.HasHeader())
+ {
+ headerElement = Control.GetHeader();
+ Control.RemoveHeader();
+ }
+ if (Control.HasFooter())
+ {
+ footerElement = Control.GetFooter();
+ Control.RemoveFooter();
+ }
+ }
+
+ public void Dispose()
+ {
+ if (headerElement != null)
+ {
+ Control.SetHeader(headerElement);
+ }
+ if (footerElement != null)
+ {
+ Control.SetFooter(footerElement);
+ }
+ }
+ }
+
+ /// <summary>
+ /// This method is called whenever something changes in list view data model.
+ /// Method will not be invoked for grouping mode, but for example event with
+ /// action reset will be handled here when switching between group and no-group mode.
+ /// </summary>
+ /// <param name="sender">TemplatedItemsList<ItemsView<Cell>, Cell>.</param>
+ /// <param name="e">NotifyCollectionChangedEventArgs.</param>
+ void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ using (new HeaderAndFooterHandler(Control))
+ {
+ if (e.Action == NotifyCollectionChangedAction.Add)
+ {
+ Cell before = null;
+ if(e.NewStartingIndex + e.NewItems.Count < Element.TemplatedItems.Count)
+ {
+ before = Element.TemplatedItems[e.NewStartingIndex + e.NewItems.Count];
+ }
+ Control.AddSource(e.NewItems, before);
+ }
+ else if (e.Action == NotifyCollectionChangedAction.Remove)
+ {
+ Control.Remove(e.OldItems);
+ }
+ else if (e.Action == NotifyCollectionChangedAction.Reset)
+ {
+ UpdateSource();
+ }
+ }
+ }
+
+ /// <summary>
+ /// This method is called whenever something changes in list view data model.
+ /// Method will be invoked for grouping mode, but some action can be also handled
+ /// by OnCollectionChanged handler.
+ /// </summary>
+ /// <param name="sender">TemplatedItemsList<ItemsView<Cell>, Cell>.</param>
+ /// <param name="e">NotifyCollectionChangedEventArgs.</param>
+ void OnGroupedCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ using (new HeaderAndFooterHandler(Control))
+ {
+ if (e.Action == NotifyCollectionChangedAction.Add)
+ {
+ TemplatedItemsList<ItemsView<Cell>,Cell> itemsGroup = sender as TemplatedItemsList<ItemsView<Cell>,Cell>;
+ Cell before = null;
+ if (e.NewStartingIndex + e.NewItems.Count < itemsGroup.Count)
+ {
+ before = itemsGroup[e.NewStartingIndex + e.NewItems.Count];
+ }
+ Control.AddItemsToGroup(itemsGroup, e.NewItems, before);
+ }
+ else if (e.Action == NotifyCollectionChangedAction.Remove)
+ {
+ Control.Remove(e.OldItems);
+ }
+ else if (e.Action == NotifyCollectionChangedAction.Reset)
+ {
+ Control.ResetGroup(sender as TemplatedItemsList<ItemsView<Cell>, Cell>);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Updates the source.
+ /// </summary>
+ void UpdateSource()
+ {
+ Control.Clear();
+ Control.AddSource(Element.TemplatedItems);
+ }
+
+ /// <summary>
+ /// Updates the header.
+ /// </summary>
+ void UpdateHeader()
+ {
+ if (Element.Header == null)
+ {
+ Control.SetHeader(null);
+ return;
+ }
+
+ if (((IListViewController)Element).HeaderElement == null)
+ {
+ Device.StartTimer(new TimeSpan(0), () =>
+ {
+ Control.SetHeader(((IListViewController)Element).HeaderElement as VisualElement);
+ return false;
+ });
+ }
+ else
+ {
+ Control.SetHeader(((IListViewController)Element).HeaderElement as VisualElement);
+ }
+ }
+
+ /// <summary>
+ /// Updates the footer.
+ /// </summary>
+ void UpdateFooter()
+ {
+ if (Element.Footer == null)
+ {
+ Control.SetFooter(null);
+ return;
+ }
+
+
+ if (((IListViewController)Element).FooterElement == null)
+ {
+ Device.StartTimer(new TimeSpan(0), () =>
+ {
+ Control.SetFooter(((IListViewController)Element).FooterElement as VisualElement);
+ return false;
+ });
+ }
+ else
+ {
+ Control.SetFooter(((IListViewController)Element).FooterElement as VisualElement);
+ }
+ }
+
+ /// <summary>
+ /// Updates the has uneven rows.
+ /// </summary>
+ void UpdateHasUnevenRows()
+ {
+ Control.SetHasUnevenRows(Element.HasUnevenRows);
+ }
+
+ /// <summary>
+ /// Updates the height of the row.
+ /// </summary>
+ void UpdateRowHeight()
+ {
+ Control.UpdateRealizedItems();
+ }
+
+ /// <summary>
+ /// Updates the is grouping enabled.
+ /// </summary>
+ /// <param name="initialize">If set to <c>true</c>, this method is invoked during initialization
+ /// (otherwise it will be invoked only after property changes).</param>
+ void UpdateIsGroupingEnabled(bool initialize)
+ {
+ Control.IsGroupingEnabled = Element.IsGroupingEnabled;
+ if (Element.IsGroupingEnabled)
+ {
+ Element.TemplatedItems.GroupedCollectionChanged += _groupedCollectionChanged;
+ }
+ else
+ {
+ Element.TemplatedItems.GroupedCollectionChanged -= _groupedCollectionChanged;
+ }
+ }
+
+ /// <summary>
+ /// Method is used for programaticaly selecting choosen item.
+ /// </summary>
+ void UpdateSelectedItem()
+ {
+ if (_selectedItemChanging == 0)
+ {
+ if (Element.SelectedItem == null)
+ {
+ if (_lastSelectedItem != null)
+ {
+ _lastSelectedItem.IsSelected = false;
+ _lastSelectedItem = null;
+ }
+ }
+ else
+ {
+ var templatedItems = Element.TemplatedItems;
+ var results = templatedItems.GetGroupAndIndexOfItem(Element.SelectedItem);
+ if (results.Item1 != -1 && results.Item2 != -1)
+ {
+ var itemGroup = templatedItems.GetGroup(results.Item1);
+ var cell = itemGroup[results.Item2];
+
+ ++_selectedItemChanging;
+ Control.ApplySelectedItem(cell);
+ --_selectedItemChanging;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Renderers/MasterDetailPageRenderer.cs b/Xamarin.Forms.Platform.Tizen/Renderers/MasterDetailPageRenderer.cs
new file mode 100644
index 0000000..9da812e
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Renderers/MasterDetailPageRenderer.cs
@@ -0,0 +1,96 @@
+using ElmSharp;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ public class MasterDetailPageRenderer : VisualElementRenderer<MasterDetailPage>
+ {
+ Native.MasterDetailPage _mdpage;
+
+ /// <summary>
+ /// Default constructor.
+ /// </summary>
+ public MasterDetailPageRenderer()
+ {
+ RegisterPropertyHandler("Master", UpdateMasterPage);
+ RegisterPropertyHandler("Detail", UpdateDetailPage);
+ RegisterPropertyHandler(MasterDetailPage.IsPresentedProperty,
+ UpdateIsPresented);
+ RegisterPropertyHandler(MasterDetailPage.MasterBehaviorProperty,
+ UpdateMasterBehavior);
+ RegisterPropertyHandler(MasterDetailPage.IsGestureEnabledProperty,
+ UpdateIsGestureEnabled);
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<MasterDetailPage> e)
+ {
+ if (_mdpage == null)
+ {
+ _mdpage = new Native.MasterDetailPage(Forms.Context.MainWindow)
+ {
+ Master = GetNativePage(e.NewElement.Master),
+ Detail = GetNativePage(e.NewElement.Detail),
+ IsPresented = e.NewElement.IsPresented,
+ };
+
+ _mdpage.IsPresentedChanged += (sender, ev) =>
+ {
+ Element.IsPresented = _mdpage.IsPresented;
+ };
+ }
+
+ if (e.OldElement != null)
+ {
+ (e.OldElement as IMasterDetailPageController).BackButtonPressed -= BackButtonPressedHandler;
+ }
+
+ if (e.NewElement != null)
+ {
+ (e.NewElement as IMasterDetailPageController).BackButtonPressed += BackButtonPressedHandler;
+ }
+
+ UpdateMasterBehavior();
+ SetNativeControl(_mdpage);
+
+ base.OnElementChanged(e);
+ }
+
+ void BackButtonPressedHandler(object sender, BackButtonPressedEventArgs e)
+ {
+ if ((Element != null) && !Element.IsPresented)
+ {
+ Element.IsPresented = true;
+ e.Handled = true;
+ }
+ }
+
+ EvasObject GetNativePage(Page page)
+ {
+ var pageRenderer = Platform.GetOrCreateRenderer(page);
+ return pageRenderer.NativeView;
+ }
+
+ void UpdateMasterBehavior() {
+ _mdpage.MasterBehavior = Element.MasterBehavior;
+ }
+
+ void UpdateMasterPage()
+ {
+ _mdpage.Master = GetNativePage(Element.Master);
+ }
+
+ void UpdateDetailPage()
+ {
+ _mdpage.Detail = GetNativePage(Element.Detail);
+ }
+
+ void UpdateIsPresented()
+ {
+ _mdpage.IsPresented = Element.IsPresented;
+ }
+
+ void UpdateIsGestureEnabled()
+ {
+ _mdpage.IsGestureEnabled = Element.IsGestureEnabled;
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Renderers/NavigationPageRenderer.cs b/Xamarin.Forms.Platform.Tizen/Renderers/NavigationPageRenderer.cs
new file mode 100644
index 0000000..0d2ee2e
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Renderers/NavigationPageRenderer.cs
@@ -0,0 +1,390 @@
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Collections.Generic;
+using Xamarin.Forms.Internals;
+using ElmSharp;
+using EButton = ElmSharp.Button;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ public class NavigationPageRenderer : VisualElementRenderer<NavigationPage>, IDisposable, IVisualElementRenderer
+ {
+ Naviframe _naviFrame = null;
+ Page _previousPage = null;
+ TaskCompletionSource<bool> _currentTaskSource = null;
+ const string _partBackButton = "elm.swallow.prev_btn";
+ const string _leftToolbar = "title_left_btn";
+ const string _rightToolbar = "title_right_btn";
+ const string _defaultToolbarIcon = "naviframe/drawers";
+ const string _partTitle = "default";
+ const string _styleBackButton = "naviframe/back_btn/default";
+ readonly List<Widget> _naviItemContentPartList = new List<Widget>();
+ ToolbarTracker _toolbarTracker = null;
+
+ public NavigationPageRenderer()
+ {
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ _naviFrame.AnimationFinished -= AnimationFinishedHandler;
+ base.Dispose(disposing);
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<NavigationPage> e)
+ {
+ if (_naviFrame == null)
+ {
+ _naviFrame = new Naviframe(Forms.Context.MainWindow);
+ _naviFrame.PreserveContentOnPop = true;
+ _naviFrame.DefaultBackButtonEnabled = true;
+ _naviFrame.AnimationFinished += AnimationFinishedHandler;
+
+ SetNativeControl(_naviFrame);
+ }
+
+ if (_toolbarTracker == null)
+ {
+ _toolbarTracker = new ToolbarTracker();
+ _toolbarTracker.CollectionChanged += ToolbarTrackerOnCollectionChanged;
+ }
+
+ if (e.OldElement != null)
+ {
+ var navigation = e.OldElement as INavigationPageController;
+ navigation.PopRequested -= PopRequestedHandler;
+ navigation.PopToRootRequested -= PopToRootRequestedHandler;
+ navigation.PushRequested -= PushRequestedHandler;
+ navigation.RemovePageRequested -= RemovePageRequestedHandler;
+ navigation.InsertPageBeforeRequested -= InsertPageBeforeRequestedHandler;
+
+ var pageController = e.OldElement as IPageController;
+ pageController.InternalChildren.CollectionChanged -= PageCollectionChangedHandler;
+ }
+
+ if (e.NewElement != null)
+ {
+ var navigation = e.NewElement as INavigationPageController;
+ navigation.PopRequested += PopRequestedHandler;
+ navigation.PopToRootRequested += PopToRootRequestedHandler;
+ navigation.PushRequested += PushRequestedHandler;
+ navigation.RemovePageRequested += RemovePageRequestedHandler;
+ navigation.InsertPageBeforeRequested += InsertPageBeforeRequestedHandler;
+
+ var pageController = e.NewElement as IPageController;
+ pageController.InternalChildren.CollectionChanged += PageCollectionChangedHandler;
+
+ foreach (Page page in pageController.InternalChildren)
+ {
+ _naviFrame.Push(Platform.GetOrCreateRenderer(page).NativeView, SpanTitle(page.Title));
+ page.PropertyChanged += NavigationBarPropertyChangedHandler;
+
+ UpdateHasNavigationBar(page);
+ }
+
+ _toolbarTracker.Target = e.NewElement;
+ _previousPage = e.NewElement.CurrentPage;
+ }
+ base.OnElementChanged(e);
+ }
+
+ protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
+ {
+ base.OnElementPropertyChanged(sender, e);
+
+ if (e.PropertyName == NavigationPage.CurrentPageProperty.PropertyName)
+ {
+ (_previousPage as IPageController)?.SendDisappearing();
+ _previousPage = Element.CurrentPage;
+ (_previousPage as IPageController)?.SendAppearing();
+ }
+ }
+
+ void PageCollectionChangedHandler(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
+ {
+ if (e.OldItems != null)
+ foreach (Page page in e.OldItems)
+ page.PropertyChanged -= NavigationBarPropertyChangedHandler;
+ if (e.NewItems != null)
+ foreach (Page page in e.NewItems)
+ page.PropertyChanged += NavigationBarPropertyChangedHandler;
+ }
+
+ void ToolbarTrackerOnCollectionChanged(object sender, EventArgs eventArgs)
+ {
+ UpdateToolbarItem(Element.CurrentPage);
+ }
+
+ void NavigationBarPropertyChangedHandler(object sender, System.ComponentModel.PropertyChangedEventArgs e)
+ {
+ // this handler is invoked only for child pages (contained on a navigation stack)
+ if (e.PropertyName == NavigationPage.HasNavigationBarProperty.PropertyName)
+ UpdateHasNavigationBar(sender as Page);
+ else if (e.PropertyName == NavigationPage.HasBackButtonProperty.PropertyName ||
+ e.PropertyName == NavigationPage.BackButtonTitleProperty.PropertyName)
+ UpdateHasBackButton(sender as Page);
+ else if (e.PropertyName == Page.TitleProperty.PropertyName ||
+ e.PropertyName == NavigationPage.BarBackgroundColorProperty.PropertyName ||
+ e.PropertyName == NavigationPage.BarTextColorProperty.PropertyName ||
+ e.PropertyName == NavigationPage.TintProperty.PropertyName)
+ UpdateTitle(sender as Page);
+ }
+
+ void UpdateHasNavigationBar(Page page)
+ {
+ NaviItem item = GetNaviItemForPage(page);
+ item.TitleBarVisible = (bool)page.GetValue(NavigationPage.HasNavigationBarProperty);
+ UpdateToolbarItem(page, item);
+ }
+
+ void UpdateToolbarItem(Page page, NaviItem item = null)
+ {
+ if (item == null)
+ item = GetNaviItemForPage(page);
+
+ if (_naviFrame.NavigationStack.Count == 0 || item == null || item != _naviFrame.NavigationStack.Last())
+ return;
+
+ item.SetPartContent(_leftToolbar, null, false);
+ item.SetPartContent(_rightToolbar, null, false);
+
+ Native.Button rightButton = GetToolbarButtonIfExists(ToolbarItemOrder.Primary);
+ item.SetPartContent(_rightToolbar, rightButton);
+
+ Native.Button leftButton = GetToolbarButtonIfExists(ToolbarItemOrder.Secondary);
+ if (leftButton == null)
+ UpdateHasBackButton(page, item);
+ else
+ item.SetPartContent(_leftToolbar, leftButton);
+ }
+
+ void UpdateHasBackButton(Page page, NaviItem item = null)
+ {
+ if (item == null)
+ item = GetNaviItemForPage(page);
+
+ EButton button = null;
+
+ if ((bool)page.GetValue(NavigationPage.HasBackButtonProperty))
+ button = CreateNavigationButton((string)page.GetValue(NavigationPage.BackButtonTitleProperty));
+ item.SetPartContent(_partBackButton, button);
+ }
+
+ void UpdateTitle(Page page)
+ {
+ NaviItem item = GetNaviItemForPage(page);
+ item.SetPartText(_partTitle, SpanTitle(page.Title));
+ }
+
+ string SpanTitle(string Title)
+ {
+ Native.Span span = new Native.Span { Text = Title };
+ if (Element.BarTextColor != Color.Default)
+ {
+ span.ForegroundColor = Element.BarTextColor.ToNative();
+ }
+ //TODO: changes only background of title not all bar
+ if (Element.BarBackgroundColor != Color.Default)
+ {
+ span.BackgroundColor = Element.BarBackgroundColor.ToNative();
+ }
+ else if (Element.Tint != Color.Default)
+ {
+ //TODO: This is only for backward compatibility
+ //- remove when Tint is no longer in Xamarin API
+ span.BackgroundColor = Element.Tint.ToNative();
+ }
+ return span.GetMarkupText();
+ }
+
+ EButton CreateNavigationButton(string text)
+ {
+ EButton button = new EButton(Forms.Context.MainWindow);
+ button.Clicked += (sender, e) =>
+ {
+ if (!Element.SendBackButtonPressed())
+ Forms.Context.Exit();
+ };
+
+ button.Style = _styleBackButton;
+ button.Text = text;
+
+ _naviItemContentPartList.Add(button);
+ button.Deleted += NaviItemPartContentDeletedHandler;
+
+ return button;
+ }
+
+ void NaviItemPartContentDeletedHandler(object sender, EventArgs e)
+ {
+ _naviItemContentPartList.Remove(sender as Widget);
+ }
+
+ NaviItem GetNaviItemForPage(Page page)
+ {
+ NaviItem item = null;
+
+ if (page != null)
+ {
+ IVisualElementRenderer renderer = Platform.GetRenderer(page);
+ if (renderer != null)
+ {
+ EvasObject content = renderer.NativeView;
+
+ for (int i = _naviFrame.NavigationStack.Count - 1; i >= 0; --i)
+ if (_naviFrame.NavigationStack[i].Content == content)
+ {
+ item = _naviFrame.NavigationStack[i];
+ break;
+ }
+ }
+ }
+ return item;
+ }
+
+ Native.Button GetToolbarButtonIfExists(ToolbarItemOrder order)
+ {
+ ToolbarItem item = _toolbarTracker.ToolbarItems.Where(
+ (i => i.Order == order ||
+ (order == ToolbarItemOrder.Primary && i.Order == ToolbarItemOrder.Default)))
+ .OrderBy(i => i.Priority)
+ .FirstOrDefault();
+
+ if (item != default(ToolbarItem))
+ {
+ return GetToolbarButton(item);
+ }
+ return null;
+ }
+
+ Native.Button GetToolbarButton(ToolbarItem item)
+ {
+ Native.Button button = new Native.Button(Forms.Context.MainWindow);
+ button.Clicked += (s, e) =>
+ {
+ IMenuItemController control = item;
+ control.Activate();
+ };
+ button.Text = item.Text;
+ button.BackgroundColor = Xamarin.Forms.Color.Transparent.ToNative();
+
+ if (String.IsNullOrEmpty(item.Icon) && String.IsNullOrEmpty(item.Text))
+ {
+ button.Style = _defaultToolbarIcon;
+ }
+ else
+ {
+ Native.Image iconImage = new Native.Image(Forms.Context.MainWindow);
+ iconImage.LoadFromImageSourceAsync(item.Icon);
+ button.Image = iconImage;
+ }
+
+ return button;
+ }
+
+ void PopRequestedHandler(object sender, NavigationRequestedEventArgs nre)
+ {
+ if ((Element as IPageController).InternalChildren.Count == _naviFrame.NavigationStack.Count)
+ {
+ if (nre.Animated)
+ {
+ _naviFrame.Pop();
+
+ _currentTaskSource = new TaskCompletionSource<bool>();
+ nre.Task = _currentTaskSource.Task;
+
+ // There is no TransitionFinished (AnimationFinished) event after Pop the last page
+ if (_naviFrame.NavigationStack.Count == 0)
+ CompleteCurrentNavigationTask();
+ }
+ else
+ _naviFrame.NavigationStack.Last().Delete();
+ }
+ }
+
+ void PopToRootRequestedHandler(object sender, NavigationRequestedEventArgs nre)
+ {
+ List<NaviItem> copyOfStack = new List<NaviItem>(_naviFrame.NavigationStack);
+ copyOfStack.Reverse();
+ NaviItem topItem = copyOfStack.FirstOrDefault();
+ NaviItem rootItem = copyOfStack.LastOrDefault();
+
+ foreach (NaviItem naviItem in copyOfStack)
+ if (naviItem != rootItem && naviItem != topItem)
+ naviItem.Delete();
+
+ if (topItem != rootItem)
+ {
+ if (nre.Animated)
+ {
+ _naviFrame.Pop();
+
+ _currentTaskSource = new TaskCompletionSource<bool>();
+ nre.Task = _currentTaskSource.Task;
+ }
+ else
+ topItem.Delete();
+ }
+ }
+
+ void PushRequestedHandler(object sender, NavigationRequestedEventArgs nre)
+ {
+ if (nre.Animated || _naviFrame.NavigationStack.Count == 0)
+ {
+ _naviFrame.Push(Platform.GetOrCreateRenderer(nre.Page).NativeView, SpanTitle(nre.Page.Title));
+ _currentTaskSource = new TaskCompletionSource<bool>();
+ nre.Task = _currentTaskSource.Task;
+
+ // There is no TransitionFinished (AnimationFinished) event after the first Push
+ if (_naviFrame.NavigationStack.Count == 1)
+ CompleteCurrentNavigationTask();
+ }
+ else
+ _naviFrame.InsertAfter(_naviFrame.NavigationStack.Last(), Platform.GetOrCreateRenderer(nre.Page).NativeView, SpanTitle(nre.Page.Title));
+
+ UpdateHasNavigationBar(nre.Page);
+ }
+
+ void RemovePageRequestedHandler(object sender, NavigationRequestedEventArgs nre)
+ {
+ GetNaviItemForPage(nre.Page).Delete();
+ }
+
+ async void InsertPageBeforeRequestedHandler(object sender, NavigationRequestedEventArgs nre)
+ {
+ TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
+ if (Element.CurrentNavigationTask != null && !Element.CurrentNavigationTask.IsCompleted)
+ {
+ await Element.CurrentNavigationTask;
+ }
+ Element.CurrentNavigationTask = tcs.Task;
+
+ Device.StartTimer(TimeSpan.FromMilliseconds(0), () =>
+ {
+ EvasObject page = Platform.GetOrCreateRenderer(nre.Page).NativeView;
+ _naviFrame.InsertBefore(GetNaviItemForPage(nre.BeforePage), page, SpanTitle(nre.Page.Title));
+ tcs.SetResult(true);
+
+ UpdateHasNavigationBar(nre.Page);
+
+ return false;
+ });
+ }
+
+ void AnimationFinishedHandler(object sender, EventArgs e)
+ {
+ CompleteCurrentNavigationTask();
+ }
+
+ void CompleteCurrentNavigationTask()
+ {
+ if (_currentTaskSource != null)
+ {
+ var tmp = _currentTaskSource;
+ _currentTaskSource = null;
+ tmp.SetResult(true);
+ }
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Renderers/PickerRenderer.cs b/Xamarin.Forms.Platform.Tizen/Renderers/PickerRenderer.cs
new file mode 100644
index 0000000..9abd507
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Renderers/PickerRenderer.cs
@@ -0,0 +1,119 @@
+using System;
+using System.ComponentModel;
+using System.Collections.Generic;
+using ElmSharp;
+using EButton = ElmSharp.Button;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ public class PickerRenderer : ViewRenderer<Picker, EButton>
+ {
+ internal List _list;
+ internal Native.Dialog _dialog;
+ Dictionary<ListItem, int> _itemToItemNumber = new Dictionary<ListItem, int>();
+
+ public PickerRenderer()
+ {
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
+ {
+ if (Control == null)
+ {
+ var button = new EButton(Forms.Context.MainWindow);
+ SetNativeControl (button);
+ }
+
+ if (e.OldElement != null)
+ {
+ Control.Clicked -= OnClick;
+ ((ObservableList<String>)e.OldElement.Items).CollectionChanged -= RowsCollectionChanged;
+ }
+
+ if (e.NewElement != null)
+ {
+ UpdateSelectedIndex();
+
+ Control.Clicked += OnClick;
+ ((ObservableList<String>)e.NewElement.Items).CollectionChanged += RowsCollectionChanged;
+ }
+
+ base.OnElementChanged(e);
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ base.OnElementPropertyChanged(sender, e);
+
+ if (e.PropertyName == Picker.SelectedIndexProperty.PropertyName)
+ {
+ UpdateSelectedIndex();
+ }
+ }
+
+ void UpdateSelectedIndex()
+ {
+ Control.Text = (Element.SelectedIndex == -1 || Element.Items == null ?
+ "" : Element.Items[Element.SelectedIndex]);
+ }
+
+ void RowsCollectionChanged(object sender, EventArgs e)
+ {
+ UpdateSelectedIndex();
+ }
+
+ void OnClick(object sender, EventArgs e)
+ {
+ int i = 0;
+ _dialog = new Native.Dialog(Forms.Context.MainWindow);
+ _list = new List(_dialog);
+ _dialog.AlignmentX = -1;
+ _dialog.AlignmentY = -1;
+
+ _dialog.Title = Element.Title;
+ _dialog.Dismissed += DialogDismissed;
+ _dialog.BackButtonPressed += (object senders, EventArgs es) =>
+ {
+ _dialog.Dismiss();
+ };
+
+ foreach (var s in Element.Items)
+ {
+ ListItem item = _list.Append(s);
+ _itemToItemNumber[item] = i;
+ i++;
+ }
+ _list.ItemSelected += ItemSelected;
+ _dialog.Content = _list;
+
+ _dialog.Show();
+ _list.Show();
+ }
+
+ void ItemSelected(object senderObject, EventArgs ev)
+ {
+ Element.SelectedIndex = _itemToItemNumber[(senderObject as List).SelectedItem];
+ _dialog.Dismiss();
+ }
+
+ void DialogDismissed(object sender, EventArgs e)
+ {
+ CleanView();
+ }
+
+ void CleanView()
+ {
+ if (null != _list)
+ {
+ _list.Unrealize();
+ _itemToItemNumber.Clear();
+ _list = null;
+ }
+ if (null != _dialog)
+ {
+ _dialog.Unrealize();
+ _dialog = null;
+ }
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Renderers/ProgressBarRenderer.cs b/Xamarin.Forms.Platform.Tizen/Renderers/ProgressBarRenderer.cs
new file mode 100644
index 0000000..3aacd3f
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Renderers/ProgressBarRenderer.cs
@@ -0,0 +1,60 @@
+using System.ComponentModel;
+using EProgressBar = ElmSharp.ProgressBar;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ public class ProgressBarRenderer : ViewRenderer<ProgressBar, EProgressBar>
+ {
+ public ProgressBarRenderer()
+ {
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<ProgressBar> e)
+ {
+ if (base.Control == null)
+ {
+ var progressBar = new EProgressBar(Forms.Context.MainWindow);
+ SetNativeControl(progressBar);
+ }
+
+ if (e.OldElement != null)
+ {
+ }
+
+ if (e.NewElement != null)
+ {
+ if (e.NewElement.MinimumWidthRequest == -1 &&
+ e.NewElement.MinimumHeightRequest == -1 &&
+ e.NewElement.WidthRequest == -1 &&
+ e.NewElement.HeightRequest == -1)
+ {
+ Log.Warn("Need to size request");
+ }
+
+ UpdateAll();
+ }
+
+ base.OnElementChanged(e);
+ }
+
+ void UpdateAll()
+ {
+ UpdateProgress();
+ }
+
+ void UpdateProgress()
+ {
+ Control.Value = Element.Progress;
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ base.OnElementPropertyChanged(sender, e);
+ if (e.PropertyName == ProgressBar.ProgressProperty.PropertyName)
+ {
+ UpdateProgress();
+ }
+ }
+ }
+}
+
diff --git a/Xamarin.Forms.Platform.Tizen/Renderers/ScrollViewRenderer.cs b/Xamarin.Forms.Platform.Tizen/Renderers/ScrollViewRenderer.cs
new file mode 100755
index 0000000..29f15fb
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Renderers/ScrollViewRenderer.cs
@@ -0,0 +1,130 @@
+using System;
+using System.ComponentModel;
+using ElmSharp;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ /// <summary>
+ /// This class provides a Renderer for a ScrollView widget.
+ /// </summary>
+ public class ScrollViewRenderer : ViewRenderer<ScrollView, Scroller>
+ {
+ EvasObject _content;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Xamarin.Forms.Platform.Tizen.ScrollViewRenderer"/> class.
+ /// </summary>
+ public ScrollViewRenderer()
+ {
+ RegisterPropertyHandler("Content", FillContent);
+ }
+
+ /// <summary>
+ /// Handles the element change event.
+ /// </summary>
+ /// <param name="e">Event arguments.</param>
+ protected override void OnElementChanged(ElementChangedEventArgs<ScrollView> e)
+ {
+ if (Control == null)
+ {
+ var scrollView = new Scroller(Forms.Context.MainWindow);
+ SetNativeControl(scrollView);
+ }
+
+ if (e.OldElement != null)
+ {
+ Control.Scrolled -= ScrollViewScrolledHandler;
+ (e.OldElement as IScrollViewController).ScrollToRequested -= ScrollRequestHandler;
+ }
+
+ if (e.NewElement != null)
+ {
+ Control.Scrolled += ScrollViewScrolledHandler;
+ (e.NewElement as IScrollViewController).ScrollToRequested += ScrollRequestHandler;
+ }
+
+ UpdateAll();
+
+ base.OnElementChanged(e);
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (null != Control)
+ {
+ (Control as IScrollViewController).ScrollToRequested -= ScrollRequestHandler;
+ }
+
+ base.Dispose(disposing);
+ }
+
+ void FillContent()
+ {
+ if (_content != null)
+ {
+ Control.SetContent(null, true);
+ }
+
+ _content = Platform.GetOrCreateRenderer(Element.Content).NativeView;
+
+ if (_content != null)
+ {
+ Control.SetContent(_content, true);
+ }
+ }
+
+ void UpdateAll()
+ {
+ UpdateOrientation();
+ }
+
+ void UpdateOrientation()
+ {
+ switch (Element.Orientation)
+ {
+ case ScrollOrientation.Horizontal:
+ Control.ScrollBlock = ScrollBlock.Vertical;
+ Control.HorizontalScrollBarVisiblePolicy = ScrollBarVisiblePolicy.Visible;
+ Control.VerticalScrollBarVisiblePolicy = ScrollBarVisiblePolicy.Invisible;
+ break;
+ case ScrollOrientation.Vertical:
+ Control.ScrollBlock = ScrollBlock.Horizontal;
+ Control.HorizontalScrollBarVisiblePolicy = ScrollBarVisiblePolicy.Invisible;
+ Control.VerticalScrollBarVisiblePolicy = ScrollBarVisiblePolicy.Visible;
+ break;
+ default:
+ Control.ScrollBlock = ScrollBlock.None;
+ Control.HorizontalScrollBarVisiblePolicy = ScrollBarVisiblePolicy.Visible;
+ Control.VerticalScrollBarVisiblePolicy = ScrollBarVisiblePolicy.Visible;
+ break;
+ }
+ }
+
+ /// <summary>
+ /// An event raised on element's property change.
+ /// </summary>
+ /// <param name="sender">Sender.</param>
+ /// <param name="e">Event arguments</param>
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (ScrollView.OrientationProperty.PropertyName == e.PropertyName)
+ {
+ UpdateOrientation();
+ }
+
+ base.OnElementPropertyChanged(sender, e);
+ }
+
+ void ScrollViewScrolledHandler(object sender, EventArgs e)
+ {
+ var region = Control.CurrentRegion;
+ ((IScrollViewController)Element).SetScrolledPosition(region.X, region.Y);
+ }
+
+ void ScrollRequestHandler(object sender, ScrollToRequestedEventArgs e)
+ {
+ Rect region = new Rect(ToNativeDimension(e.ScrollX), ToNativeDimension(e.ScrollY), ToNativeDimension(Element.Width), ToNativeDimension(Element.Height));
+ Control.ScrollTo(region, e.ShouldAnimate);
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Renderers/SearchBarRenderer.cs b/Xamarin.Forms.Platform.Tizen/Renderers/SearchBarRenderer.cs
new file mode 100644
index 0000000..798a049
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Renderers/SearchBarRenderer.cs
@@ -0,0 +1,167 @@
+using System;
+using EColor = ElmSharp.Color;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ public class SearchBarRenderer : ViewRenderer<SearchBar, Native.SearchBar>
+ {
+ //TODO need to add internationalization support
+ const string DefaultPlaceholderText = "Search";
+
+ static readonly EColor s_defaultCancelButtonColor = EColor.Aqua;
+
+ //TODO: read default platform color
+ static readonly EColor s_defaultPlaceholderColor = EColor.Gray;
+ static readonly EColor s_defaultTextColor = EColor.Black;
+ /// <summary>
+ /// Creates a new instance of the <see cref="Xamarin.Forms.Platform.Tizen.SearchBarRenderer"/> class.
+ /// Registers handlers for various properties of the SearchBar widget.
+ /// </summary>
+ public SearchBarRenderer()
+ {
+ RegisterPropertyHandler(SearchBar.CancelButtonColorProperty, CancelButtonColorPropertyHandler);
+ RegisterPropertyHandler(SearchBar.FontAttributesProperty, FontAttributesPropertyHandler);
+ RegisterPropertyHandler(SearchBar.FontFamilyProperty, FontFamilyPropertyHandler);
+ RegisterPropertyHandler(SearchBar.FontSizeProperty, FontSizePropertyHandler);
+ RegisterPropertyHandler(SearchBar.HorizontalTextAlignmentProperty, HorizontalTextAlignmentPropertyHandler);
+ RegisterPropertyHandler(SearchBar.PlaceholderProperty, PlaceholderPropertyHandler);
+ RegisterPropertyHandler(SearchBar.PlaceholderColorProperty, PlaceholderColorPropertyHandler);
+ RegisterPropertyHandler(SearchBar.TextProperty, TextPropertyHandler);
+ RegisterPropertyHandler(SearchBar.TextColorProperty, TextColorPropertyHandler);
+ }
+
+ /// <summary>
+ /// A method called whenever the associated element has changed.
+ /// </summary>
+ protected override void OnElementChanged(ElementChangedEventArgs<SearchBar> e)
+ {
+ if (Control == null)
+ {
+ var searchBar = new Native.SearchBar(Forms.Context.MainWindow);
+ SetNativeControl(searchBar);
+ }
+
+ if (e.OldElement != null)
+ {
+ Control.TextChanged -= SearchBarTextChangedHandler;
+ Control.SearchButtonPressed -= SearchButtonPressedHandler;
+ }
+
+ if (e.NewElement != null)
+ {
+ Control.TextChanged += SearchBarTextChangedHandler;
+ Control.SearchButtonPressed += SearchButtonPressedHandler;
+ }
+
+ base.OnElementChanged(e);
+ }
+
+ protected override Size MinimumSize()
+ {
+ return new Size(250, 120);
+ }
+
+ /// <summary>
+ /// Called upon changing of Xamarin widget's cancel button color property.
+ /// Converts current Color to ElmSharp.Color instance and sets it in the underlying Xamarin.Forms.Platform.Tizen.Native widget.
+ /// </summary>
+ void CancelButtonColorPropertyHandler()
+ {
+ Control.CancelButtonColor = Element.CancelButtonColor.IsDefault ? s_defaultCancelButtonColor : Element.CancelButtonColor.ToNative();
+ }
+
+ /// <summary>
+ /// Called upon changing of Xamarin widget's font attributes property.
+ /// Converts current FontAttributes to ElmSharp.FontAttributes instance
+ /// and sets it in the underlying Xamarin.Forms.Platform.Tizen.Native.SearchBar widget.
+ /// </summary>
+ void FontAttributesPropertyHandler()
+ {
+ Control.FontAttributes = Element.FontAttributes;
+ }
+
+ /// <summary>
+ /// Called upon changing of Xamarin widget's font family property.
+ /// Sets current value of FontFamily property to the underlying Xamarin.Forms.Platform.Tizen.Native.SearchBar widget.
+ /// </summary>
+ void FontFamilyPropertyHandler()
+ {
+ Control.FontFamily = Element.FontFamily;
+ }
+
+ /// <summary>
+ /// Called upon changing of Xamarin widget's font size property.
+ /// Sets current value of FontSize property to the underlying Xamarin.Forms.Platform.Tizen.Native.SearchBar widget.
+ /// </summary>
+ void FontSizePropertyHandler()
+ {
+ Control.FontSize = Element.FontSize;
+ }
+
+ /// <summary>
+ /// Called upon changing of Xamarin widget's horizontal text alignment property.
+ /// Converts current HorizontalTextAlignment property's value to Xamarin.Forms.Platform.Tizen.Native.TextAlignment instance
+ /// and sets it in the underlying Xamarin.Forms.Platform.Tizen.Native.SearchBar widget.
+ /// </summary>
+ void HorizontalTextAlignmentPropertyHandler()
+ {
+ Control.HorizontalTextAlignment = Element.HorizontalTextAlignment.ToNative();
+ }
+
+ /// <summary>
+ /// Called upon changing of Xamarin widget's placeholder color property.
+ /// Converts current PlaceholderColor property value to ElmSharp.Color instance
+ /// and sets it in the underlying Xamarin.Forms.Platform.Tizen.Native.SearchBar widget.
+ /// </summary>
+ void PlaceholderColorPropertyHandler()
+ {
+ Control.PlaceholderColor = Element.PlaceholderColor.IsDefault ? s_defaultPlaceholderColor : Element.PlaceholderColor.ToNative();
+ }
+
+ /// <summary>
+ /// Called upon changing of Xamarin widget's placeholder text property.
+ /// </summary>
+ void PlaceholderPropertyHandler()
+ {
+ Control.Placeholder = Element.Placeholder == null ? DefaultPlaceholderText : Element.Placeholder;
+ }
+
+ /// <summary>
+ /// Called on every change of underlying SearchBar's Text property.
+ /// Rewrites current underlying Xamarin.Forms.Platform.Tizen.Native.SearchBar's Text contents to its Xamarin counterpart.
+ /// </summary>
+ /// <param name="sender">Sender.</param>
+ void SearchBarTextChangedHandler(object sender, EventArgs e)
+ {
+ Element.Text = Control.Text;
+ }
+
+ /// <summary>
+ /// Called when the user clicks the Search button.
+ /// </summary>
+ /// <param name="sender">Sender.</param>
+ /// <param name="e">Event arguments.</param>
+ void SearchButtonPressedHandler(object sender, EventArgs e)
+ {
+ (Element as ISearchBarController).OnSearchButtonPressed();
+ }
+
+ /// <summary>
+ /// Called upon changing of Xamarin widget's text color property.
+ /// Converts current TextColor property value to ElmSharp.Color instance
+ /// and sets it in the underlying Xamarin.Forms.Platform.Tizen.Native.SearchBar widget.
+ /// </summary>
+ void TextColorPropertyHandler()
+ {
+ Control.TextColor = Element.TextColor.IsDefault ? s_defaultTextColor : Element.TextColor.ToNative();
+ }
+
+ /// <summary>
+ /// Called upon changing of Xamarin widget's text property.
+ /// </summary>
+ void TextPropertyHandler()
+ {
+ Control.Text = Element.Text;
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Renderers/SliderRenderer.cs b/Xamarin.Forms.Platform.Tizen/Renderers/SliderRenderer.cs
new file mode 100644
index 0000000..93cc6e8
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Renderers/SliderRenderer.cs
@@ -0,0 +1,63 @@
+using System;
+using ESlider = ElmSharp.Slider;
+using ESize = ElmSharp.Size;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ public class SliderRenderer : ViewRenderer<Slider, ESlider>
+ {
+
+ public SliderRenderer()
+ {
+ RegisterPropertyHandler(Slider.ValueProperty, UpdateValue);
+ RegisterPropertyHandler(Slider.MinimumProperty, UpdateMinMax);
+ RegisterPropertyHandler(Slider.MaximumProperty, UpdateMinMax);
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<Slider> e)
+ {
+ if (Control == null)
+ {
+ var slider = new ESlider(Forms.Context.MainWindow)
+ {
+ PropagateEvents = false,
+ };
+ SetNativeControl(slider);
+ }
+
+ if (e.OldElement != null)
+ {
+ Control.ValueChanged -= SliderValueChangedHandler;
+ }
+
+ if (e.NewElement != null)
+ {
+ Control.ValueChanged += SliderValueChangedHandler;
+ }
+
+ base.OnElementChanged(e);
+ }
+
+ protected override ESize Measure(int availableWidth, int availableHeight)
+ {
+ return new ESize(Math.Min(200, availableWidth), 50);
+ }
+
+ void SliderValueChangedHandler(object sender, EventArgs e)
+ {
+ Element.Value = Control.Value;
+ }
+
+ protected void UpdateValue()
+ {
+ Control.Value = Element.Value;
+ }
+
+ protected void UpdateMinMax()
+ {
+ Control.Minimum = Element.Minimum;
+ Control.Maximum = Element.Maximum;
+ UpdateValue();
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Renderers/StepperRenderer.cs b/Xamarin.Forms.Platform.Tizen/Renderers/StepperRenderer.cs
new file mode 100644
index 0000000..09d1bf6
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Renderers/StepperRenderer.cs
@@ -0,0 +1,86 @@
+using System;
+using ElmSharp;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ public class StepperRenderer : ViewRenderer<Stepper, Spinner>
+ {
+
+ public StepperRenderer()
+ {
+ RegisterPropertyHandler(Stepper.ValueProperty, UpdateValue);
+ RegisterPropertyHandler(Stepper.MinimumProperty, UpdateMinMax);
+ RegisterPropertyHandler(Stepper.MaximumProperty, UpdateMinMax);
+ RegisterPropertyHandler(Stepper.IncrementProperty, UpdateStep);
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<Stepper> e)
+ {
+ if (Control == null)
+ {
+ var stepper = new Spinner(Forms.Context.MainWindow)
+ {
+ IsEditable = false,
+ };
+
+ SetNativeControl(stepper);
+ }
+
+ if (e.OldElement != null)
+ {
+ Control.ValueChanged -= StepperValueChangedHandler;
+ }
+
+ if (e.NewElement != null)
+ {
+ Control.ValueChanged += StepperValueChangedHandler;
+ }
+
+ base.OnElementChanged(e);
+ }
+
+ void StepperValueChangedHandler(object sender, EventArgs e)
+ {
+ double newValue = Control.Value;
+ ((IElementController)Element).SetValueFromRenderer(Stepper.ValueProperty, newValue);
+
+ // Determines how many decimal places are there in current Stepper's value.
+ // The 15 pound characters below correspond to the maximum precision of Double type.
+ var decimalValue = Decimal.Parse(newValue.ToString("0.###############"));
+
+ // GetBits() method returns an array of four 32-bit integer values.
+ // The third (0-indexing) element of an array contains the following information:
+ // bits 00-15: unused, required to be 0
+ // bits 16-23: an exponent between 0 and 28 indicating the power of 10 to divide the integer number passed as a parameter.
+ // Conversely this is the number of decimal digits in the number as well.
+ // bits 24-30: unused, required to be 0
+ // bit 31: indicates the sign. 0 means positive number, 1 is for negative numbers.
+ //
+ // The precision information needs to be extracted from bits 16-23 of third element of an array
+ // returned by GetBits() call. Right-shifting by 16 bits followed by zeroing anything else results
+ // in a nice conversion of this data to integer variable.
+ var precision = (Decimal.GetBits(decimalValue)[3] >> 16) & 0x000000FF;
+
+ // Sets Stepper's inner label decimal format to use exactly as many decimal places as needed:
+ Control.LabelFormat = string.Format("%.{0}f", precision);
+ }
+
+ protected void UpdateValue()
+ {
+ Control.Value = Element.Value;
+ }
+
+ protected void UpdateMinMax()
+ {
+ Control.Minimum = Element.Minimum;
+ Control.Maximum = Element.Maximum;
+ UpdateValue();
+ }
+
+ void UpdateStep()
+ {
+ Control.Step = Element.Increment;
+ }
+ }
+}
+
diff --git a/Xamarin.Forms.Platform.Tizen/Renderers/SwitchRenderer.cs b/Xamarin.Forms.Platform.Tizen/Renderers/SwitchRenderer.cs
new file mode 100644
index 0000000..b3539e6
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Renderers/SwitchRenderer.cs
@@ -0,0 +1,50 @@
+using System;
+using ElmSharp;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ public class SwitchRenderer : ViewRenderer<Switch, Check>
+ {
+ public SwitchRenderer()
+ {
+ RegisterPropertyHandler(Switch.IsToggledProperty, HandleToggled);
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<Switch> e)
+ {
+ if (Control == null)
+ {
+ var _switch = new Check(Forms.Context.MainWindow)
+ {
+ PropagateEvents = false,
+ };
+ SetNativeControl(_switch);
+ }
+
+ if (e.OldElement != null)
+ {
+ Control.StateChanged -= CheckChangedHandler;
+ }
+
+ if (e.NewElement != null)
+ {
+ Control.Style = "toggle";
+
+ Control.StateChanged += CheckChangedHandler;
+ }
+
+ base.OnElementChanged(e);
+ }
+
+ void CheckChangedHandler(object sender, EventArgs e)
+ {
+ Element.SetValue(Switch.IsToggledProperty, Control.IsChecked);
+ }
+
+ void HandleToggled()
+ {
+ Control.IsChecked = Element.IsToggled;
+ }
+
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Renderers/TabbedPageRenderer.cs b/Xamarin.Forms.Platform.Tizen/Renderers/TabbedPageRenderer.cs
new file mode 100644
index 0000000..db97b33
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Renderers/TabbedPageRenderer.cs
@@ -0,0 +1,190 @@
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using ElmSharp;
+using EToolbarItem = ElmSharp.ToolbarItem;
+using EToolbarItemEventArgs = ElmSharp.ToolbarItemEventArgs;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ public class TabbedPageRenderer : VisualElementRenderer<TabbedPage>, IVisualElementRenderer
+ {
+ Box _box;
+ Toolbar _tpage;
+ EvasObject _tcontent;
+ Dictionary<EToolbarItem, Page> _itemToItemPage = new Dictionary<EToolbarItem, Page>();
+
+ public TabbedPageRenderer ()
+ {
+ //Register for title change property
+ RegisterPropertyHandler(TabbedPage.TitleProperty, UpdateTitle);
+ //Register for current page change property
+ RegisterPropertyHandler("CurrentPage", CurrentPageChanged);
+ //TODO renderer should add item on EFL toolbar when new Page is added to TabbedPage
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<TabbedPage> e)
+ {
+ if (_tpage == null)
+ {
+ //Create box that holds toolbar and selected content
+ _box = new Box(Forms.Context.MainWindow)
+ {
+ AlignmentX = -1,
+ AlignmentY = -1,
+ WeightX = 1,
+ WeightY = 1,
+ IsHorizontal = false,
+ };
+ _box.Show();
+
+ //Create toolbar that is placed inside the _box
+ _tpage = new Toolbar(Forms.Context.MainWindow)
+ {
+ AlignmentX = -1,
+ WeightX = 1,
+ ShrinkMode = ToolbarShrinkMode.Expand,
+ SelectionMode = ToolbarSelectionMode.Always,
+ };
+ _tpage.Show();
+ //Add callback for item selection
+ _tpage.Selected += OnCurrentPageChanged;
+ _box.PackEnd(_tpage);
+
+ SetNativeControl(_box);
+ UpdateTitle();
+ }
+
+ base.OnElementChanged(e);
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (_box != null)
+ {
+ _box.Unrealize();
+ _box = null;
+ }
+ if (_tpage != null)
+ {
+ _tpage.Selected -= OnCurrentPageChanged;
+
+ _tpage.Unrealize();
+ _tpage = null;
+ }
+ base.Dispose(disposing);
+ }
+
+ protected override void OnElementReady()
+ {
+ FillToolbar();
+ base.OnElementReady();
+ }
+
+ void UpdateTitle()
+ {
+ _tpage.Text = Element.Title;
+ }
+
+ void UpdateTitle(Page page)
+ {
+ if (_itemToItemPage.ContainsValue(page))
+ {
+ var pair = _itemToItemPage.FirstOrDefault(x => x.Value == page);
+ pair.Key.SetPartText(null, pair.Value.Title);
+ }
+ }
+
+ void OnPageTitleChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == Page.TitleProperty.PropertyName)
+ {
+ UpdateTitle(sender as Page);
+ }
+ }
+
+ void FillToolbar()
+ {
+ var logicalChildren = (Element as IElementController).LogicalChildren;
+ bool hasIcon = false;
+
+ //add items to toolbar
+ foreach (Page child in logicalChildren)
+ {
+ var childRenderer = Platform.GetRenderer(child);
+ if (childRenderer != null)
+ {
+ childRenderer.NativeView.Hide();
+ }
+
+ EToolbarItem toolbarItem;
+ if (string.IsNullOrEmpty(child.Icon))
+ {
+ toolbarItem = _tpage.Append(child.Title);
+ }
+ else
+ {
+ //elm_toobar style and size hint must be changed at least once before adding the toolbar item having icon.
+ if (!hasIcon)
+ {
+ int windowHeight = Forms.Context.MainWindow.Geometry.Height;
+ //This value is from efl-theme-tizen-mobile theme. (NAVIFRAME_TABBAR_HEIGHT_WITH_TITLE_INC 80)
+ double requiredToolbarHeight = 80.0;
+ double toolBarWeight = requiredToolbarHeight/windowHeight;
+ _tpage.Style="tabbar";
+ _tpage.TransverseExpansion = true;
+ _tpage.SetAlignment(-1,-1);
+ _tpage.SetWeight(1, toolBarWeight);
+ _box.SetAlignment(-1,-1);
+ _box.SetWeight(1, 1- toolBarWeight);
+ hasIcon = true;
+ }
+ toolbarItem = _tpage.Append(child.Title, ResourcePath.GetPath(child.Icon));
+ }
+ _itemToItemPage.Add(toolbarItem, child);
+ if (Element.CurrentPage == child)
+ {
+ //select item on the toolbar and fill content
+ toolbarItem.IsSelected = true;
+ OnCurrentPageChanged(null, null);
+ }
+ child.PropertyChanged += OnPageTitleChanged;
+ }
+ }
+
+ void OnCurrentPageChanged(object sender, EToolbarItemEventArgs e)
+ {
+ if (_tpage.SelectedItem == null)
+ return;
+ Element.CurrentPage = _itemToItemPage[_tpage.SelectedItem];
+
+ //detach content from view without EvasObject changes
+ if (_tcontent != null)
+ {
+ //hide content that should not be visible
+ _tcontent.Hide();
+ //unpack content that is hiden an prepare for new content
+ _box.UnPack(_tcontent);
+ }
+ //create EvasObject using renderer and remember to not destroy
+ //it for better performance (creat once)
+ _tcontent = Platform.GetOrCreateRenderer(Element.CurrentPage).NativeView;
+ _tcontent.SetAlignment(-1, -1);
+ _tcontent.SetWeight(1, 1);
+ _tcontent.Show();
+ _box.PackEnd(_tcontent);
+ }
+
+ void CurrentPageChanged()
+ {
+ foreach (KeyValuePair<EToolbarItem, Page> pair in _itemToItemPage)
+ {
+ if (pair.Value == Element.CurrentPage)
+ {
+ pair.Key.IsSelected = true;
+ return;
+ }
+ }
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Renderers/TableViewRenderer.cs b/Xamarin.Forms.Platform.Tizen/Renderers/TableViewRenderer.cs
new file mode 100644
index 0000000..5912354
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Renderers/TableViewRenderer.cs
@@ -0,0 +1,79 @@
+using System;
+using ElmSharp;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ public class TableViewRenderer : ViewRenderer<TableView, Native.TableView>, IVisualElementRenderer
+ {
+ internal static BindableProperty PresentationProperty = BindableProperty.Create("Presentation", typeof(View), typeof(TableSectionBase), null, BindingMode.OneWay, null, null, null, null, null as BindableProperty.CreateDefaultValueDelegate);
+
+ public TableViewRenderer()
+ {
+ RegisterPropertyHandler(TableView.HasUnevenRowsProperty, UpdateHasUnevenRows);
+ RegisterPropertyHandler(TableView.RowHeightProperty, UpdateRowHeight);
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<TableView> e)
+ {
+ if (Control == null)
+ {
+ var _tableView = new Native.TableView(Forms.Context.MainWindow);
+ SetNativeControl(_tableView);
+ }
+
+ if (e.OldElement != null)
+ {
+ Control.ItemSelected -= ListViewItemSelectedHandler;
+ e.OldElement.ModelChanged -= OnRootPropertyChanged;
+ }
+
+ if (e.NewElement != null)
+ {
+ e.NewElement.ModelChanged += OnRootPropertyChanged;
+ Control.ItemSelected += ListViewItemSelectedHandler;
+ Control.ApplyTableRoot(e.NewElement.Root);
+ }
+
+ base.OnElementChanged(e);
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ Element.ModelChanged -= OnRootPropertyChanged;
+ base.Dispose(disposing);
+ }
+
+ void ListViewItemSelectedHandler(object sender, GenListItemEventArgs e)
+ {
+ var item = e.Item as GenListItem;
+
+ if (item != null)
+ {
+ var clickedCell = item.Data as Native.ListView.ItemContext;
+ if (null != clickedCell)
+ {
+ Element.Model.RowSelected(clickedCell.Cell);
+ }
+ }
+ }
+
+ void OnRootPropertyChanged(object sender, EventArgs e)
+ {
+ if (Element != null)
+ {
+ Control.ApplyTableRoot(Element.Root);
+ }
+ }
+
+ void UpdateHasUnevenRows()
+ {
+ //TODO Implement UnevenRows in ListView
+ }
+
+ void UpdateRowHeight()
+ {
+ Control.UpdateRealizedItems();
+ }
+
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Renderers/TimePickerRenderer.cs b/Xamarin.Forms.Platform.Tizen/Renderers/TimePickerRenderer.cs
new file mode 100644
index 0000000..9f964c1
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Renderers/TimePickerRenderer.cs
@@ -0,0 +1,101 @@
+using System;
+using System.Globalization;
+using EColor = ElmSharp.Color;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ public class TimePickerRenderer : ViewRenderer<TimePicker, Native.Button>
+ {
+ //TODO need to add internationalization support
+ const string DialogTitle = "Choose Time";
+
+ static readonly string s_defaultFormat = CultureInfo.CurrentCulture.DateTimeFormat.ShortTimePattern;
+
+ static readonly EColor s_defaultTextColor = EColor.White;
+
+ string _format;
+
+ TimeSpan _time;
+
+ public TimePickerRenderer()
+ {
+ RegisterPropertyHandler(TimePicker.FormatProperty, UpdateFormat);
+ RegisterPropertyHandler(TimePicker.TimeProperty, UpdateTime);
+ RegisterPropertyHandler(TimePicker.TextColorProperty, UpdateTextColor);
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<TimePicker> e)
+ {
+ if (Control == null)
+ {
+ var button = new Native.Button(Forms.Context.MainWindow);
+ SetNativeControl(button);
+ }
+
+ if (e.OldElement != null)
+ {
+ Control.Clicked -= ButtonClickedHandler;
+ }
+
+ if (e.NewElement != null)
+ {
+ _time = DateTime.Now.TimeOfDay;
+ _format = s_defaultFormat;
+ UpdateTimeAndFormat();
+
+ Control.Clicked += ButtonClickedHandler;
+ }
+
+ base.OnElementChanged(e);
+ }
+
+ void ButtonClickedHandler(object o, EventArgs e)
+ {
+ Native.DateTimePickerDialog dialog = new Native.DateTimePickerDialog(Forms.Context.MainWindow)
+ {
+ Title = DialogTitle
+ };
+
+ dialog.InitializeTimePicker(_time, _format);
+ dialog.DateTimeChanged += DialogDateTimeChangedHandler;
+ dialog.Dismissed += DialogDismissedHandler;
+ dialog.Show();
+ }
+
+ void DialogDateTimeChangedHandler(object sender, Native.DateChangedEventArgs dcea)
+ {
+ Element.Time = dcea.NewDate.TimeOfDay;
+ UpdateTime();
+ }
+
+ void DialogDismissedHandler(object sender, EventArgs e)
+ {
+ var dialog = sender as Native.DateTimePickerDialog;
+ dialog.DateTimeChanged -= DialogDateTimeChangedHandler;
+ dialog.Dismissed -= DialogDismissedHandler;
+ }
+
+ void UpdateFormat()
+ {
+ _format = Element.Format ?? s_defaultFormat;
+ UpdateTimeAndFormat();
+ }
+
+ void UpdateTextColor()
+ {
+ Control.TextColor = Element.TextColor.IsDefault ? s_defaultTextColor : Element.TextColor.ToNative();
+ }
+
+ void UpdateTime()
+ {
+ _time = Element.Time;
+ UpdateTimeAndFormat();
+ }
+
+ void UpdateTimeAndFormat()
+ {
+ // Xamarin using DateTime formatting (https://developer.xamarin.com/api/property/Xamarin.Forms.TimePicker.Format/)
+ Control.Text = new DateTime(_time.Ticks).ToString(_format);
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Renderers/ViewRenderer.cs b/Xamarin.Forms.Platform.Tizen/Renderers/ViewRenderer.cs
new file mode 100644
index 0000000..24debc8
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Renderers/ViewRenderer.cs
@@ -0,0 +1,46 @@
+using ElmSharp;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ /// <summary>
+ /// Base class for view renderers.
+ /// </summary>
+ public abstract class ViewRenderer<TView, TNativeView> : VisualElementRenderer<TView>
+ where TView : View
+ where TNativeView : Widget
+ {
+ /// <summary>
+ /// Default constructor.
+ /// </summary>
+ protected ViewRenderer()
+ {
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<TView> e)
+ {
+ base.OnElementChanged(e);
+
+ if (e.OldElement != null)
+ {
+ _gestureHandler.Clear();
+ _gestureHandler = null;
+ }
+
+ if (e.NewElement != null)
+ {
+ _gestureHandler = new GestureHandler(this);
+ }
+ }
+
+ /// <summary>
+ /// Native control associated with this renderer.
+ /// </summary>
+ public TNativeView Control
+ {
+ get
+ {
+ return (TNativeView)NativeView;
+ }
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Renderers/VisualElementRenderer.cs b/Xamarin.Forms.Platform.Tizen/Renderers/VisualElementRenderer.cs
new file mode 100644
index 0000000..8c5c0be
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Renderers/VisualElementRenderer.cs
@@ -0,0 +1,946 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.ComponentModel;
+using ElmSharp;
+using ELayout = ElmSharp.Layout;
+using EColor = ElmSharp.Color;
+using ESize = ElmSharp.Size;
+using ERect = ElmSharp.Rect;
+using ERectangle = ElmSharp.Rectangle;
+using EBox = ElmSharp.Box;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ /// <summary>
+ /// Base class for rendering of a Xamarin element.
+ /// </summary>
+ public abstract class VisualElementRenderer<TElement> : IVisualElementRenderer, IEffectControlProvider where TElement : VisualElement
+ {
+ /// <summary>
+ /// Holds registered element changed handlers.
+ /// </summary>
+ readonly List<EventHandler<VisualElementChangedEventArgs>> _elementChangedHandlers = new List<EventHandler<VisualElementChangedEventArgs>>();
+
+ /// <summary>
+ /// Handler for property changed events.
+ /// </summary>
+ PropertyChangedEventHandler _propertyChangedHandler;
+
+ EventHandler<EventArg<VisualElement>> _batchCommittedHandler;
+
+ /// <summary>
+ /// Flags which control status of renderer.
+ /// </summary>
+ VisualElementRendererFlags _flags = VisualElementRendererFlags.None;
+
+ /// <summary>
+ /// Holds the native view.
+ /// </summary>
+ EvasObject _view;
+
+ Dictionary<string, Action<bool>> _propertyHandlersWithInit = new Dictionary<string, Action<bool>>();
+
+ Dictionary<string, Action> _propertyHandlers = new Dictionary<string, Action>();
+
+ HashSet<string> _batchedProperties = new HashSet<string>();
+
+ ERectangle _opacityLayer;
+
+ internal GestureHandler _gestureHandler;
+
+ /// <summary>
+ /// Default constructor.
+ /// </summary>
+ protected VisualElementRenderer()
+ {
+ RegisterPropertyHandler(VisualElement.IsVisibleProperty, UpdateIsVisible);
+ RegisterPropertyHandler(VisualElement.OpacityProperty, UpdateOpacity);
+ RegisterPropertyHandler(VisualElement.IsEnabledProperty, UpdateIsEnabled);
+ RegisterPropertyHandler(VisualElement.InputTransparentProperty, UpdateInputTransparent);
+ RegisterPropertyHandler(VisualElement.BackgroundColorProperty, UpdateBackgroundColor);
+
+ RegisterPropertyHandler(VisualElement.AnchorXProperty, ApplyTransformation);
+ RegisterPropertyHandler(VisualElement.AnchorYProperty, ApplyTransformation);
+ RegisterPropertyHandler(VisualElement.ScaleProperty, ApplyTransformation);
+ RegisterPropertyHandler(VisualElement.RotationProperty, ApplyTransformation);
+ RegisterPropertyHandler(VisualElement.RotationXProperty, ApplyTransformation);
+ RegisterPropertyHandler(VisualElement.RotationYProperty, ApplyTransformation);
+ RegisterPropertyHandler(VisualElement.TranslationXProperty, ApplyTransformation);
+ RegisterPropertyHandler(VisualElement.TranslationYProperty, ApplyTransformation);
+ }
+
+ ~VisualElementRenderer()
+ {
+ Dispose(false);
+ }
+
+ event EventHandler<VisualElementChangedEventArgs> ElementChanged
+ {
+ add
+ {
+ _elementChangedHandlers.Add(value);
+ }
+ remove
+ {
+ _elementChangedHandlers.Remove(value);
+ }
+ }
+
+ /// <summary>
+ /// Gets the Xamarin element associated with this renderer.
+ /// </summary>
+ public TElement Element
+ {
+ get;
+ private set;
+ }
+
+ VisualElement IVisualElementRenderer.Element
+ {
+ get
+ {
+ return this.Element;
+ }
+ }
+
+ public EvasObject NativeView
+ {
+ get
+ {
+ return _view;
+ }
+ }
+
+ protected bool IsDisposed => (_flags == VisualElementRendererFlags.Disposed);
+
+ /// <summary>
+ /// Releases all resource used by the <see cref="Xamarin.Forms.Platform.Tizen.VisualElementRenderer"/> object.
+ /// </summary>
+ /// <remarks>Call <see cref="Dispose"/> when you are finished using the
+ /// <see cref="Xamarin.Forms.Platform.Tizen.VisualElementRenderer"/>. The <see cref="Dispose"/> method
+ /// leaves the <see cref="Xamarin.Forms.Platform.Tizen.VisualElementRenderer"/> in an unusable state.
+ /// After calling <see cref="Dispose"/>, you must release all references to the
+ /// <see cref="Xamarin.Forms.Platform.Tizen.VisualElementRenderer"/> so the garbage collector can reclaim
+ /// the memory that the <see cref="Xamarin.Forms.Platform.Tizen.VisualElementRenderer"/> was occupying.</remarks>
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ public SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
+ {
+ if (null == NativeView)
+ {
+ return new SizeRequest(new Size(0, 0));
+ }
+ else
+ {
+ int availableWidth = ToNativeDimension(widthConstraint);
+ int availableHeight = ToNativeDimension(heightConstraint);
+ ESize measured;
+
+ var nativeViewMeasurable = NativeView as Native.IMeasurable;
+ if (nativeViewMeasurable != null)
+ {
+ measured = nativeViewMeasurable.Measure(availableWidth, availableHeight);
+ }
+ else
+ {
+ measured = Measure(availableWidth, availableHeight);
+ }
+
+ return new SizeRequest(new Size(measured.Width, measured.Height), MinimumSize());
+ }
+ }
+
+ /// <summary>
+ /// Sets the element associated with this renderer.
+ /// </summary>
+ public void SetElement(TElement newElement)
+ {
+ if (newElement == null)
+ {
+ throw new ArgumentNullException("newElement");
+ }
+
+ TElement oldElement = Element;
+ if (oldElement != null)
+ {
+ throw new InvalidOperationException("oldElement");
+ }
+
+ Element = newElement;
+ if (_propertyChangedHandler == null)
+ {
+ _propertyChangedHandler = new PropertyChangedEventHandler(OnElementPropertyChanged);
+ }
+
+ if (_batchCommittedHandler == null)
+ {
+ _batchCommittedHandler = OnBatchCommitted;
+ }
+
+ // send notification
+ OnElementChanged(new ElementChangedEventArgs<TElement>(oldElement, newElement));
+
+ // store renderer for the new element
+ Platform.SetRenderer(newElement, this);
+
+ // add children
+ var logicalChildren = (newElement as IElementController).LogicalChildren;
+ foreach (Element child in logicalChildren)
+ {
+ AddChild(child);
+ }
+
+ OnElementReady();
+ }
+
+ public void UpdateNativeGeometry()
+ {
+ var x = ComputeAbsoluteX(Element);
+ var y = ComputeAbsoluteY(Element);
+ NativeView.Geometry = new ERect(ToNativeDimension(x), ToNativeDimension(y), ToNativeDimension(Element.Width), ToNativeDimension(Element.Height));
+ ApplyTransformation();
+ UpdateOpacityLayer();
+ }
+
+ void IVisualElementRenderer.SetElement(VisualElement element)
+ {
+ TElement tElement = element as TElement;
+ if (tElement == null)
+ {
+ throw new ArgumentException("Element is not of type " + typeof(TElement), "Element");
+ }
+ SetElement(tElement);
+ }
+
+ /// <summary>
+ /// Registers the effect with the element by establishing the parent-child relations needed for rendering on the specific platform.
+ /// </summary>
+ /// <param name="effect">The effect to register.</param>
+ void IEffectControlProvider.RegisterEffect(Effect effect)
+ {
+ RegisterEffect(effect);
+ }
+
+ /// <summary>
+ /// Registers the effect with the element by establishing the parent-child relations needed for rendering on the specific platform.
+ /// </summary>
+ /// <param name="effect">The effect to register.</param>
+ protected void RegisterEffect(Effect effect)
+ {
+ var platformEffect = effect as PlatformEffect;
+ if (platformEffect != null)
+ {
+ OnRegisterEffect(platformEffect);
+ }
+ }
+
+
+ protected virtual void UpdateLayout()
+ {
+ // we're updating the coordinates of native control only if they were modified
+ // via Xamarin (Settings.IgnoreBatchCommitted is set to false);
+ // otherwise native control is already in the right place
+ if (!Settings.IgnoreBatchCommitted && null != NativeView)
+ {
+ UpdateNativeGeometry();
+ }
+
+ // we're updating just immediate children
+ var logicalChildren = (Element as IElementController).LogicalChildren;
+ foreach (var child in logicalChildren)
+ {
+ Platform.GetRenderer(child)?.UpdateNativeGeometry();
+ }
+ }
+
+ /// <summary>
+ /// Disposes of underlying resources.
+ /// </summary>
+ /// <param name="disposing">True if the memory release was requested on demand.</param>
+ protected virtual void Dispose(bool disposing)
+ {
+ if ((_flags & VisualElementRendererFlags.Disposed) != 0)
+ {
+ return;
+ }
+
+ _flags |= VisualElementRendererFlags.Disposed;
+
+ if (disposing)
+ {
+ if (Element != null)
+ {
+ Element.PropertyChanged -= _propertyChangedHandler;
+ Element.BatchCommitted -= _batchCommittedHandler;
+
+ Element.ChildAdded -= OnChildAdded;
+ Element.ChildRemoved -= OnChildRemoved;
+ Element.ChildrenReordered -= OnChildrenReordered;
+
+ Element.FocusChangeRequested -= OnFocusChangeRequested;
+
+ Settings.StartIgnoringBatchCommitted();
+ Element.Layout(new Rectangle(0, 0, -1, -1));
+ Settings.StopIgnoringBatchCommitted();
+
+ var logicalChildren = (Element as IElementController).LogicalChildren;
+ foreach (var child in logicalChildren)
+ {
+ Platform.GetRenderer(child)?.Dispose();
+ }
+
+ if (Platform.GetRenderer(Element) == this)
+ {
+ Platform.SetRenderer(Element, (IVisualElementRenderer)null);
+ }
+ Element = default(TElement);
+ }
+
+ if (NativeView != null)
+ {
+ NativeView.Deleted -= NativeViewDeleted;
+ EnsureOpacityLayerIsDestroyed();
+ NativeView.Unrealize();
+ SetNativeView(null);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Notification that the associated element has changed.
+ /// </summary>
+ /// <param name="e">Event parameters.</param>
+ protected virtual void OnElementChanged(ElementChangedEventArgs<TElement> e)
+ {
+ if (null != e.OldElement)
+ {
+ e.OldElement.PropertyChanged -= _propertyChangedHandler;
+ e.OldElement.BatchCommitted -= _batchCommittedHandler;
+
+ e.OldElement.ChildAdded -= OnChildAdded;
+ e.OldElement.ChildRemoved -= OnChildRemoved;
+ e.OldElement.ChildrenReordered -= OnChildrenReordered;
+
+ e.OldElement.FocusChangeRequested -= OnFocusChangeRequested;
+
+ Settings.StartIgnoringBatchCommitted();
+ Element.Layout(new Rectangle(0, 0, -1, -1));
+ Settings.StopIgnoringBatchCommitted();
+
+ var controller = e.OldElement as IElementController;
+ if (controller != null && controller.EffectControlProvider == this)
+ {
+ controller.EffectControlProvider = null;
+ }
+ }
+
+ if (null != e.NewElement)
+ {
+ e.NewElement.PropertyChanged += _propertyChangedHandler;
+ e.NewElement.BatchCommitted += _batchCommittedHandler;
+
+ e.NewElement.ChildAdded += OnChildAdded;
+ e.NewElement.ChildRemoved += OnChildRemoved;
+ e.NewElement.ChildrenReordered += OnChildrenReordered;
+
+ e.NewElement.FocusChangeRequested += OnFocusChangeRequested;
+
+ UpdateAllProperties(true);
+
+ var controller = e.NewElement as IElementController;
+ if (controller != null)
+ {
+ controller.EffectControlProvider = this;
+ }
+ }
+
+ // TODO: handle the event
+ }
+
+ /// <summary>
+ /// Notification that the property of the associated element has changed.
+ /// </summary>
+ /// <param name="sender">Object which sent the notification.</param>
+ /// <param name="e">Event parameters.</param>
+ protected virtual void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (Element.Batched)
+ {
+ if (e.PropertyName == VisualElement.XProperty.PropertyName ||
+ e.PropertyName == VisualElement.YProperty.PropertyName ||
+ e.PropertyName == VisualElement.WidthProperty.PropertyName ||
+ e.PropertyName == VisualElement.HeightProperty.PropertyName)
+ {
+ _flags |= VisualElementRendererFlags.NeedsLayout;
+ }
+ else if (e.PropertyName == VisualElement.TranslationXProperty.PropertyName ||
+ e.PropertyName == VisualElement.TranslationYProperty.PropertyName ||
+ e.PropertyName == VisualElement.RotationProperty.PropertyName ||
+ e.PropertyName == VisualElement.RotationXProperty.PropertyName ||
+ e.PropertyName == VisualElement.RotationYProperty.PropertyName ||
+ e.PropertyName == VisualElement.ScaleProperty.PropertyName ||
+ e.PropertyName == VisualElement.AnchorXProperty.PropertyName ||
+ e.PropertyName == VisualElement.AnchorYProperty.PropertyName)
+ {
+ _flags |= VisualElementRendererFlags.NeedsTransformation;
+ }
+ else
+ {
+ _batchedProperties.Add(e.PropertyName);
+ }
+ return;
+ }
+
+ Action<bool> init;
+ if (_propertyHandlersWithInit.TryGetValue(e.PropertyName, out init))
+ {
+ init(false);
+ }
+ else
+ {
+ Action handler;
+ if (_propertyHandlers.TryGetValue(e.PropertyName, out handler))
+ {
+ handler();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Updates the attached event handlers, sets the native control.
+ /// </summary>
+ protected void SetNativeControl(EvasObject control)
+ {
+ if (NativeView != null)
+ {
+ NativeView.Moved -= OnMoved;
+ NativeView.Deleted -= NativeViewDeleted;
+ EnsureOpacityLayerIsDestroyed();
+ }
+
+ Widget widget = NativeView as Widget;
+ if (widget != null)
+ {
+ widget.Focused -= OnFocused;
+ widget.Unfocused -= OnUnfocused;
+ }
+
+ SetNativeView(control);
+
+ if (NativeView != null)
+ {
+ NativeView.Deleted += NativeViewDeleted;
+ NativeView.Moved += OnMoved;
+ }
+
+ widget = NativeView as Widget;
+ if (widget != null)
+ {
+ widget.Focused += OnFocused;
+ widget.Unfocused += OnUnfocused;
+ }
+ }
+
+ void NativeViewDeleted(object sender, EventArgs e)
+ {
+ Dispose();
+ }
+
+ void OnBatchCommitted(object sender, EventArg<VisualElement> e)
+ {
+ if (_flags.HasFlag(VisualElementRendererFlags.NeedsLayout))
+ {
+ if (!Settings.IgnoreBatchCommitted)
+ {
+ UpdateLayout();
+ // UpdateLayout already updates transformation, clear NeedsTranformation flag then
+ _flags &= ~VisualElementRendererFlags.NeedsTransformation;
+ }
+ _flags ^= VisualElementRendererFlags.NeedsLayout;
+ }
+ if (_flags.HasFlag(VisualElementRendererFlags.NeedsTransformation))
+ {
+ ApplyTransformation();
+ _flags ^= VisualElementRendererFlags.NeedsTransformation;
+ }
+
+ foreach (string property in _batchedProperties)
+ {
+ OnElementPropertyChanged(this, new PropertyChangedEventArgs(property));
+ }
+ _batchedProperties.Clear();
+ }
+
+ /// <summary>
+ /// Registers a handler which is executed when specified property changes.
+ /// </summary>
+ /// <param name="property">Handled property.</param>
+ /// <param name="handler">Action to be executed when property changes.</param>
+ protected void RegisterPropertyHandler(BindableProperty property, Action<bool> handler)
+ {
+ RegisterPropertyHandler(property.PropertyName, handler);
+ }
+
+ /// <summary>
+ /// Registers a handler which is executed when specified property changes.
+ /// </summary>
+ /// <param name="name">Name of the handled property.</param>
+ /// <param name="handler">Action to be executed when property changes.</param>
+ protected void RegisterPropertyHandler(string name, Action<bool> handler)
+ {
+ _propertyHandlersWithInit.Add(name, handler);
+ }
+
+ /// <summary>
+ /// Registers a handler which is executed when specified property changes.
+ /// </summary>
+ /// <param name="property">Handled property.</param>
+ /// <param name="handler">Action to be executed when property changes.</param>
+ protected void RegisterPropertyHandler(BindableProperty property, Action handler)
+ {
+ RegisterPropertyHandler(property.PropertyName, handler);
+ }
+
+ /// <summary>
+ /// Registers a handler which is executed when specified property changes.
+ /// </summary>
+ /// <param name="name">Name of the handled property.</param>
+ /// <param name="handler">Action to be executed when property changes.</param>
+ protected void RegisterPropertyHandler(string name, Action handler)
+ {
+ _propertyHandlers.Add(name, handler);
+ }
+
+ /// <summary>
+ /// Updates all registered properties.
+ /// </summary>
+ /// <param name="initialization">If set to <c>true</c> the method is called for an uninitialized object.</param>
+ protected void UpdateAllProperties(bool initialization)
+ {
+ foreach (KeyValuePair<string, Action<bool>> kvp in _propertyHandlersWithInit)
+ {
+ kvp.Value(initialization);
+ }
+
+ foreach (KeyValuePair<string, Action> kvp in _propertyHandlers)
+ {
+ kvp.Value();
+ }
+ }
+
+ /// <summary>
+ /// Called when Element has been set and its native counterpart
+ /// is properly initialized.
+ /// </summary>
+ protected virtual void OnElementReady()
+ {
+ }
+
+ protected void DoLayout(Native.LayoutEventArgs e)
+ {
+ Settings.StartIgnoringBatchCommitted();
+ Element.Layout(new Rectangle(Element.X, Element.Y, e.Width, e.Height));
+ if (e.HasChanged)
+ {
+ UpdateLayout();
+ }
+ Settings.StopIgnoringBatchCommitted();
+ }
+
+ protected virtual Size MinimumSize()
+ {
+ return new Size();
+ }
+
+ /// <summary>
+ /// Calculates how much space this element should take, given how much room there is.
+ /// </summary>
+ /// <returns>a desired dimensions of the element</returns>
+ protected virtual ESize Measure(int availableWidth, int availableHeight)
+ {
+ return new ESize(NativeView.MinimumWidth, NativeView.MinimumHeight);
+ }
+
+ protected virtual void UpdateBackgroundColor()
+ {
+ if (NativeView is Widget)
+ {
+ (NativeView as Widget).BackgroundColor = Element.BackgroundColor.ToNative();
+ }
+ else
+ {
+ Log.Warn("{0} uses {1} which does not support background color", this, NativeView);
+ }
+ }
+
+ /// <summary>
+ /// Converts provided value to native dimension.
+ /// </summary>
+ /// <param name="v">value to be converted.</param>
+ /// <returns>converted value</returns>
+ protected static int ToNativeDimension(double v)
+ {
+ return (int)Math.Round(v);
+ }
+
+ static double ComputeAbsoluteX(VisualElement e)
+ {
+ return e.X + (e.RealParent is VisualElement ? Platform.GetRenderer(e.RealParent).NativeView.Geometry.X : 0.0);
+ }
+
+ static double ComputeAbsoluteY(VisualElement e)
+ {
+ return e.Y + (e.RealParent is VisualElement ? Platform.GetRenderer(e.RealParent).NativeView.Geometry.Y : 0.0);
+ }
+
+ /// <summary>
+ /// Handles focus events.
+ /// </summary>
+ void OnFocused(object sender, EventArgs e)
+ {
+ if (null != Element)
+ {
+ Element.SetValue(VisualElement.IsFocusedPropertyKey, true);
+ }
+ }
+
+ /// <summary>
+ /// Handles unfocus events.
+ /// </summary>
+ void OnUnfocused(object sender, EventArgs e)
+ {
+ if (null != Element)
+ {
+ Element.SetValue(VisualElement.IsFocusedPropertyKey, false);
+ }
+ }
+
+ /// <summary>
+ /// Sets the native control, updates the control's properties.
+ /// </summary>
+ void SetNativeView(EvasObject control)
+ {
+ _view = control;
+ }
+
+ /// <summary>
+ /// Adds a new child if it's derived from the VisualElement class. Otherwise this method does nothing.
+ /// </summary>
+ /// <param name="child">Child to be added.</param>
+ void AddChild(Element child)
+ {
+ VisualElement vElement = child as VisualElement;
+ if (vElement != null)
+ {
+ var childRenderer = Platform.GetOrCreateRenderer(vElement);
+
+ // if the native view can have children, attach the new child
+ if (NativeView is Native.IContainable<EvasObject>)
+ {
+ (NativeView as Native.IContainable<EvasObject>).Children.Add(childRenderer.NativeView);
+ }
+ }
+ }
+
+ void RemoveChild(VisualElement view)
+ {
+ var renderer = Platform.GetRenderer(view);
+ var containerObject = NativeView as Native.IContainable<EvasObject>;
+ if (containerObject != null)
+ {
+ containerObject.Children.Remove(renderer.NativeView);
+ }
+
+ renderer.Dispose();
+ }
+
+ void OnChildAdded(object sender, ElementEventArgs e)
+ {
+ var view = e.Element as VisualElement;
+ if (view != null)
+ {
+ AddChild(view);
+ }
+
+ // changing the order makes sense only in case of Layouts
+ if (Element is Layout)
+ {
+ IElementController controller = Element as IElementController;
+ if (controller.LogicalChildren[controller.LogicalChildren.Count - 1] != view)
+ {
+ EnsureChildOrder();
+ }
+ }
+ }
+
+ void OnChildRemoved(object sender, ElementEventArgs e)
+ {
+ var view = e.Element as VisualElement;
+ if (view != null)
+ {
+ RemoveChild(view);
+ }
+ }
+
+ void OnChildrenReordered(object sender, EventArgs e)
+ {
+ EnsureChildOrder();
+ Layout layout = Element as Layout;
+ if (layout != null)
+ {
+ layout.InvalidateMeasureInternal(Internals.InvalidationTrigger.MeasureChanged);
+ layout.ForceLayout();
+ }
+ }
+
+ void OnFocusChangeRequested(object sender, VisualElement.FocusRequestArgs e)
+ {
+ Widget widget = NativeView as Widget;
+ if (widget == null)
+ {
+ Log.Warn("{0} is not a widget, it cannot receive focus", NativeView);
+ return;
+ }
+
+ widget.SetFocus(e.Focus);
+ e.Result = true;
+ }
+
+ /// <summary>
+ /// On register the effect
+ /// </summary>
+ /// <param name="effect">The effect to register.</param>
+ void OnRegisterEffect(PlatformEffect effect)
+ {
+ effect.Container = Element.Parent == null ? null : Platform.GetRenderer(Element.Parent).NativeView;
+ effect.Control = NativeView;
+ }
+
+ void OnMoved(object sender, EventArgs e)
+ {
+ UpdateOpacityLayer();
+ ApplyTransformation();
+ _gestureHandler?.UpdateHitBox();
+ }
+
+ void EnsureChildOrder()
+ {
+ var logicalChildren = (Element as IElementController).LogicalChildren;
+ for (var i = logicalChildren.Count - 1; i >= 0; --i)
+ {
+ var element = logicalChildren[i] as VisualElement;
+ if (element != null)
+ {
+ Platform.GetRenderer(element).NativeView?.Lower();
+ }
+ }
+ }
+
+ void UpdateIsVisible()
+ {
+ if (null != NativeView)
+ {
+ if (Element.IsVisible)
+ {
+ NativeView.Show();
+ }
+ else
+ {
+ NativeView.Hide();
+ }
+ }
+ }
+
+ void UpdateOpacity()
+ {
+ if (null != NativeView)
+ {
+ if (Element.Opacity < 1.0)
+ {
+ EnsureOpacityLayerExists();
+
+ var alpha = (int)(Element.Opacity * 255.0);
+ _opacityLayer.Color = new EColor(255, 255, 255, alpha);
+ }
+ else
+ {
+ EnsureOpacityLayerIsDestroyed();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Updates the IsEnabled property.
+ /// </summary>
+ void UpdateIsEnabled()
+ {
+ Widget widget = NativeView as Widget;
+ if (widget != null)
+ {
+ widget.IsEnabled = Element.IsEnabled;
+ }
+ }
+
+ /// <summary>
+ /// Updates the InputTransparent property.
+ /// </summary>
+ void UpdateInputTransparent()
+ {
+ NativeView.PassEvents = Element.InputTransparent;
+ }
+
+ void ApplyRotation(EvasMap map, ERect geometry, ref bool changed)
+ {
+ var rotationX = Element.RotationX;
+ var rotationY = Element.RotationY;
+ var rotationZ = Element.Rotation;
+ var anchorX = Element.AnchorX;
+ var anchorY = Element.AnchorY;
+
+ // apply rotations
+ if (rotationX != 0 || rotationY != 0 || rotationZ != 0)
+ {
+ map.Rotate3D(rotationX, rotationY, rotationZ, (int)(geometry.X + geometry.Width * anchorX),
+ (int)(geometry.Y + geometry.Height * anchorY), 0);
+ changed = true;
+ }
+ }
+
+ void ApplyScale(EvasMap map, ERect geometry, ref bool changed)
+ {
+ var scale = Element.Scale;
+
+ // apply scale factor
+ if (scale != 1.0)
+ {
+ map.Zoom(scale, scale,
+ geometry.X + (int)(geometry.Width * Element.AnchorX),
+ geometry.Y + (int)(geometry.Height * Element.AnchorY));
+ changed = true;
+ }
+ }
+
+ void ApplyTranslation(EvasMap map, ERect geometry, ref bool changed)
+ {
+ var shiftX = ToNativeDimension(Element.TranslationX);
+ var shiftY = ToNativeDimension(Element.TranslationY);
+
+ // apply translation, i.e. move/shift the object a little
+ if (shiftX != 0 || shiftY != 0)
+ {
+ if (changed)
+ {
+ // special care is taken to apply the translation last
+ Point3D p;
+ for (int i = 0; i < 4; i++)
+ {
+ p = map.GetPointCoordinate(i);
+ p.X += shiftX;
+ p.Y += shiftY;
+ map.SetPointCoordinate(i, p);
+ }
+ }
+ else
+ {
+ // in case when we only need translation, then construct the map in a simpler way
+ geometry.X += shiftX;
+ geometry.Y += shiftY;
+ map.PopulatePoints(geometry, 0);
+
+ changed = true;
+ }
+ }
+ }
+
+ public void ApplyTransformation()
+ {
+ if (null == NativeView)
+ {
+ Log.Error("Trying to apply transformation to the non-existent native control");
+ return;
+ }
+
+ // prepare the EFL effect structure
+ ERect geometry = NativeView.Geometry;
+ EvasMap map = new EvasMap(4);
+ map.PopulatePoints(geometry, 0);
+
+ bool changed = false;
+ ApplyRotation(map, geometry, ref changed);
+ ApplyScale(map, geometry, ref changed);
+ ApplyTranslation(map, geometry, ref changed);
+
+ NativeView.IsMapEnabled = changed;
+
+ if (changed)
+ {
+ NativeView.EvasMap = map;
+
+ if (_opacityLayer != null)
+ {
+ _opacityLayer.IsMapEnabled = true;
+ _opacityLayer.EvasMap = map;
+ }
+ }
+ _gestureHandler?.UpdateHitBox();
+ }
+
+ protected virtual void UpdateOpacityLayer()
+ {
+ if (_opacityLayer != null)
+ {
+ _opacityLayer.Geometry = NativeView.Geometry;
+ NativeView.SetClip(_opacityLayer);
+ }
+ }
+
+ void EnsureOpacityLayerExists()
+ {
+ if (_opacityLayer == null)
+ {
+ _opacityLayer = new ERectangle(NativeView);
+ UpdateOpacityLayer();
+ _opacityLayer.Show();
+ }
+ }
+
+ void EnsureOpacityLayerIsDestroyed()
+ {
+ if (_opacityLayer != null)
+ {
+ NativeView.SetClip(null);
+ _opacityLayer.Unrealize();
+ _opacityLayer = null;
+ }
+ }
+ }
+
+ internal static class Settings
+ {
+ static int s_ignoreCount = 0;
+
+ public static bool IgnoreBatchCommitted
+ {
+ get
+ {
+ return s_ignoreCount != 0;
+ }
+ }
+
+ public static void StartIgnoringBatchCommitted()
+ {
+ ++s_ignoreCount;
+ }
+
+ public static void StopIgnoringBatchCommitted()
+ {
+ Debug.Assert(s_ignoreCount > 0);
+ --s_ignoreCount;
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/ResourcePath.cs b/Xamarin.Forms.Platform.Tizen/ResourcePath.cs
new file mode 100644
index 0000000..d238779
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/ResourcePath.cs
@@ -0,0 +1,44 @@
+using System.IO;
+#if NET45
+using System.Reflection;
+#endif
+
+using AppFW = Tizen.Applications;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ internal static class ResourcePath
+ {
+ public static string GetPath(string res)
+ {
+ if (Path.IsPathRooted(res))
+ {
+ return res;
+ }
+
+ AppFW.Application app = AppFW.Application.Current;
+ if (app != null)
+ {
+ string resPath = app.DirectoryInfo.Resource + res;
+ if (File.Exists(resPath))
+ {
+ return resPath;
+ }
+ }
+
+#if NET45
+ string exedir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+ // ind resource in "exepath/../res/"
+ {
+ string resPath = exedir + "/../res/" + res;
+ if (File.Exists(resPath))
+ {
+ return resPath;
+ }
+ }
+#endif
+
+ return res;
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/ResourcesProvider.cs b/Xamarin.Forms.Platform.Tizen/ResourcesProvider.cs
new file mode 100644
index 0000000..15e7485
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/ResourcesProvider.cs
@@ -0,0 +1,72 @@
+namespace Xamarin.Forms.Platform.Tizen
+{
+ internal class ResourcesProvider : ISystemResourcesProvider
+ {
+ ResourceDictionary _dictionary;
+
+ public IResourceDictionary GetSystemResources()
+ {
+ _dictionary = new ResourceDictionary();
+
+ UpdateStyles();
+
+ return _dictionary;
+ }
+
+ void UpdateStyles()
+ {
+ _dictionary[Device.Styles.BodyStyleKey] = GetStyleByKey(Device.Styles.BodyStyleKey);
+ _dictionary[Device.Styles.TitleStyleKey] = GetStyleByKey(Device.Styles.TitleStyleKey);
+ _dictionary[Device.Styles.SubtitleStyleKey] = GetStyleByKey(Device.Styles.SubtitleStyleKey);
+ _dictionary[Device.Styles.CaptionStyleKey] = GetStyleByKey(Device.Styles.CaptionStyleKey);
+ _dictionary[Device.Styles.ListItemTextStyleKey] = GetStyleByKey(Device.Styles.ListItemTextStyleKey);
+ _dictionary[Device.Styles.ListItemDetailTextStyleKey] = GetStyleByKey(Device.Styles.ListItemDetailTextStyleKey);
+ }
+
+ //TODO: need Tizen API to get system values
+ Style GetStyleByKey(string key)
+ {
+ Style style = null;
+ if (key == Device.Styles.TitleStyleKey)
+ {
+ style = GetStyle(50, Color.FromRgb(250, 250, 250));
+ }
+ else if (key == Device.Styles.SubtitleStyleKey)
+ {
+ style = GetStyle(24, Color.FromRgb(250, 250, 250));
+ }
+ else if (key == Device.Styles.CaptionStyleKey)
+ {
+ style = GetStyle(24, Color.FromRgb(115, 115, 115));
+ }
+ else if (key == Device.Styles.ListItemTextStyleKey)
+ {
+ style = GetStyle(40, Color.FromRgb(0, 0, 0));
+ }
+ else if (key == Device.Styles.ListItemDetailTextStyleKey)
+ {
+ style = GetStyle(24, Color.FromRgb(0, 0, 0));
+ }
+ else
+ {
+ style = GetStyle();
+ }
+
+ return style;
+ }
+
+ Style GetStyle(int? fontSize = null, Color? textColor = null)
+ {
+ Style style = new Style(typeof(Label));
+ if (fontSize.HasValue)
+ {
+ style.Setters.Add(new Setter { Property = Label.FontSizeProperty, Value = fontSize });
+ }
+ if (textColor.HasValue)
+ {
+ style.Setters.Add(new Setter { Property = Label.TextColorProperty, Value = textColor });
+ }
+ return style;
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/TizenIsolatedStorageFile.cs b/Xamarin.Forms.Platform.Tizen/TizenIsolatedStorageFile.cs
new file mode 100644
index 0000000..0550d9d
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/TizenIsolatedStorageFile.cs
@@ -0,0 +1,125 @@
+using System;
+using System.IO;
+using System.Threading.Tasks;
+using TApplication = Tizen.Applications.Application;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ internal class TizenIsolatedStorageFile : IIsolatedStorageFile
+ {
+ string _rootPath;
+
+ internal TizenIsolatedStorageFile()
+ {
+ _rootPath = TApplication.Current.DirectoryInfo.Data;
+ }
+
+ public void CreateDirectory(string path)
+ {
+ if (path == null)
+ throw new ArgumentNullException("path");
+ Directory.CreateDirectory(Path.Combine(_rootPath, path));
+ }
+
+ public Task CreateDirectoryAsync(string path)
+ {
+ CreateDirectory(path);
+ return Task.FromResult(true);
+ }
+
+ public void MoveFile(string source, string dest)
+ {
+ if (source == null)
+ throw new ArgumentNullException("source");
+ if (dest == null)
+ throw new ArgumentNullException("dest");
+
+ File.Move(Path.Combine(_rootPath, source), Path.Combine(_rootPath, dest));
+ }
+
+ public void DeleteFile(string path)
+ {
+ if (path == null)
+ throw new ArgumentNullException("path");
+ if (path.Trim().Length == 0)
+ throw new ArgumentException("An empty path is not valid.");
+
+ File.Delete(Path.Combine(_rootPath, path));
+ }
+
+ public bool DirectoryExists(string path)
+ {
+ if (path == null)
+ throw new ArgumentNullException("path");
+ return Directory.Exists(Path.Combine(_rootPath, path));
+ }
+
+ public Task<bool> GetDirectoryExistsAsync(string path)
+ {
+ return Task.FromResult(DirectoryExists(path));
+ }
+
+ public bool FileExists(string path)
+ {
+ if (path == null)
+ throw new ArgumentNullException("path");
+ return File.Exists(Path.Combine(_rootPath, path));
+ }
+
+ public Task<bool> GetFileExistsAsync(string path)
+ {
+ return Task.FromResult(FileExists(path));
+ }
+
+ public DateTimeOffset GetLastWriteTime(string path)
+ {
+ if (path == null)
+ throw new ArgumentNullException("path");
+ if (path.Trim().Length == 0)
+ throw new ArgumentException("An empty path is not valid.");
+
+ string fullPath = Path.Combine(_rootPath, path);
+ if (File.Exists(fullPath))
+ return File.GetLastWriteTime(fullPath);
+
+ return Directory.GetLastWriteTime(fullPath);
+ }
+
+ public Task<DateTimeOffset> GetLastWriteTimeAsync(string path)
+ {
+ return Task.FromResult(GetLastWriteTime(path));
+ }
+
+ public Stream OpenFile(string path, System.IO.FileMode mode)
+ {
+ return OpenFile(path, mode, (mode == System.IO.FileMode.Append ? System.IO.FileAccess.Write : System.IO.FileAccess.ReadWrite));
+ }
+
+ public Stream OpenFile(string path, System.IO.FileMode mode, System.IO.FileAccess access)
+ {
+ return OpenFile(path, mode, access, System.IO.FileShare.Read);
+ }
+
+ public Stream OpenFile(string path, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share)
+ {
+ if (path == null)
+ throw new ArgumentNullException("path");
+ if (path.Trim().Length == 0)
+ throw new ArgumentException("An empty path is not valid.");
+
+ string fullPath = Path.Combine(_rootPath, path);
+ return new FileStream(fullPath, mode, access, share);
+ }
+
+ public Task<Stream> OpenFileAsync(string path, FileMode mode, FileAccess access)
+ {
+ return Task.FromResult(OpenFile(path, (System.IO.FileMode)mode, (System.IO.FileAccess)access));
+ }
+
+ public Task<Stream> OpenFileAsync(string path, FileMode mode, FileAccess access, FileShare share)
+ {
+ return Task.FromResult(OpenFile(path, (System.IO.FileMode)mode, (System.IO.FileAccess)access, (System.IO.FileShare)share));
+ }
+
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/TizenPlatformServices.cs b/Xamarin.Forms.Platform.Tizen/TizenPlatformServices.cs
new file mode 100644
index 0000000..7d44231
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/TizenPlatformServices.cs
@@ -0,0 +1,232 @@
+using System;
+using System.IO;
+using System.Reflection;
+using System.Collections.Generic;
+using System.Security.Cryptography;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Net.Http;
+using ElmSharp;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ internal class TizenPlatformServices : IPlatformServices
+ {
+ static MD5 checksum = MD5.Create();
+
+ static SynchronizationContext s_context;
+
+ public TizenPlatformServices()
+ {
+ s_context = SynchronizationContext.Current;
+ }
+
+ public class _Timer : ITimer
+ {
+ readonly Timer timer;
+
+ public _Timer(Timer timer)
+ {
+ this.timer = timer;
+ }
+
+ #region ITimer implementation
+ public void Change(int dueTime, int period)
+ {
+ timer.Change(dueTime, period);
+ }
+ public void Change(long dueTime, long period)
+ {
+ timer.Change((int)dueTime, (int)period);
+ }
+ public void Change(TimeSpan dueTime, TimeSpan period)
+ {
+ timer.Change(dueTime, period);
+ }
+ public void Change(uint dueTime, uint period)
+ {
+ timer.Change((int)dueTime, (int)period);
+ }
+ #endregion
+ }
+
+ public class Ticker : Xamarin.Forms.Internals.Ticker
+ {
+ readonly ITimer _timer;
+
+ public Ticker()
+ {
+ _timer = new _Timer(new Timer((object o) => HandleElapsed(o), this, Timeout.Infinite, Timeout.Infinite));
+ }
+
+ protected override void EnableTimer()
+ {
+ _timer.Change(16, 16);
+ }
+
+ protected override void DisableTimer()
+ {
+ _timer.Change(-1, -1);
+ }
+
+ void HandleElapsed(object state)
+ {
+ s_context.Post((o) => SendSignals(-1), null);
+ }
+ }
+
+ #region IPlatformServices implementation
+
+ public double GetNamedSize(NamedSize size, Type targetElementType, bool useOldSizes)
+ {
+ switch (size)
+ {
+ case NamedSize.Micro:
+ return 16;
+ case NamedSize.Small:
+ return 20;
+ case NamedSize.Default:
+ case NamedSize.Medium:
+ return 24;
+ case NamedSize.Large:
+ return 30;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ public void OpenUriAction(Uri uri)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void BeginInvokeOnMainThread(Action action)
+ {
+ if (EcoreMainloop.IsMainThread)
+ {
+ action();
+ }
+ else
+ {
+ s_context.Post((o) => action(), null);
+ }
+ }
+
+ public Xamarin.Forms.Internals.Ticker CreateTicker()
+ {
+ return new Ticker();
+ }
+
+ public void StartTimer(TimeSpan interval, Func<bool> callback)
+ {
+ Timer timer = null;
+ bool invoking = false;
+ TimerCallback onTimeout = o =>
+ {
+ if (!invoking)
+ {
+ invoking = true;
+ BeginInvokeOnMainThread(() =>
+ {
+ if (!callback())
+ {
+ timer.Dispose();
+ }
+ invoking = false;
+ }
+ );
+ }
+ };
+ timer = new Timer(onTimeout, null, Timeout.Infinite, Timeout.Infinite);
+ // set interval separarately to prevent calling onTimeout before `timer' is assigned
+ timer.Change(interval, interval);
+ }
+
+ public async Task<Stream> GetStreamAsync(Uri uri, CancellationToken cancellationToken)
+ {
+ using (var client = new HttpClient())
+ using (HttpResponseMessage response = await client.GetAsync(uri, cancellationToken))
+ return await response.Content.ReadAsStreamAsync();
+ }
+
+ public Assembly[] GetAssemblies()
+ {
+ return AppDomain.CurrentDomain.GetAssemblies();
+ }
+
+ public ITimer CreateTimer(Action<object> callback, object state, int dueTime, int period)
+ {
+ return new _Timer(new Timer((object o) => callback(o), state, dueTime, period));
+ }
+
+ public ITimer CreateTimer(Action<object> callback, object state, TimeSpan dueTime, TimeSpan period)
+ {
+ return new _Timer(new Timer((object o) => callback(o), state, dueTime, period));
+ }
+
+ public IIsolatedStorageFile GetUserStoreForApplication()
+ {
+ return new TizenIsolatedStorageFile();
+ }
+
+ static readonly char[] HexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+ public string GetMD5Hash(string input)
+ {
+ byte[] bin = checksum.ComputeHash(System.Text.Encoding.UTF8.GetBytes(input));
+ char[] hex = new char[32];
+ for (var i = 0; i < 16; ++i)
+ {
+ hex[2 * i] = HexDigits[bin[i] >> 4];
+ hex[2 * i + 1] = HexDigits[bin[i] & 0xf];
+ }
+ return new string(hex);
+ }
+
+ public bool IsInvokeRequired
+ {
+ get
+ {
+ return !EcoreMainloop.IsMainThread;
+ }
+ }
+
+ #endregion
+
+#if !NET45
+ // In .NETCore, AppDomain is not supported. The list of the assemblies should be generated manually.
+ internal class AppDomain
+ {
+ public static AppDomain CurrentDomain { get; private set; }
+
+ List<Assembly> _assemblies;
+
+ static AppDomain()
+ {
+ CurrentDomain = new AppDomain();
+ }
+
+ AppDomain()
+ {
+ _assemblies = new List<Assembly>();
+
+ // Add this renderer assembly to the list
+ RegisterAssembly(GetType().GetTypeInfo().Assembly);
+ }
+
+ internal void RegisterAssembly(Assembly asm)
+ {
+ if (!_assemblies.Contains(asm))
+ {
+ _assemblies.Add(asm);
+ }
+ }
+
+ public Assembly[] GetAssemblies()
+ {
+ return _assemblies.ToArray();
+ }
+ }
+#endif
+ }
+}
+
diff --git a/Xamarin.Forms.Platform.Tizen/TizenTitleBarVisibility.cs b/Xamarin.Forms.Platform.Tizen/TizenTitleBarVisibility.cs
new file mode 100644
index 0000000..4cd09d8
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/TizenTitleBarVisibility.cs
@@ -0,0 +1,8 @@
+namespace Xamarin.Forms.Platform.Tizen
+{
+ public enum TizenTitleBarVisibility
+ {
+ Default,
+ Never
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/ViewInitializedEventArgs.cs b/Xamarin.Forms.Platform.Tizen/ViewInitializedEventArgs.cs
new file mode 100644
index 0000000..fb05c1a
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/ViewInitializedEventArgs.cs
@@ -0,0 +1,20 @@
+using System;
+using ElmSharp;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ public class ViewInitializedEventArgs : EventArgs
+ {
+ public EvasObject NativeView
+ {
+ get;
+ internal set;
+ }
+
+ public VisualElement View
+ {
+ get;
+ internal set;
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/VisualElementChangedEventArgs.cs b/Xamarin.Forms.Platform.Tizen/VisualElementChangedEventArgs.cs
new file mode 100644
index 0000000..35e2105
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/VisualElementChangedEventArgs.cs
@@ -0,0 +1,16 @@
+namespace Xamarin.Forms.Platform.Tizen
+{
+ /// <summary>
+ /// Represents the arguments passed to every VisualElement change event.
+ /// </summary>
+ public class VisualElementChangedEventArgs : ElementChangedEventArgs<VisualElement>
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Xamarin.Forms.Platform.Tizen.VisualElementChangedEventArgs"/> class.
+ /// </summary>
+ /// <param name="oldElement">Old element.</param>
+ /// <param name="newElement">New element.</param>
+ public VisualElementChangedEventArgs(VisualElement oldElement, VisualElement newElement) : base(oldElement, newElement) {
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/VisualElementRendererFlags.cs b/Xamarin.Forms.Platform.Tizen/VisualElementRendererFlags.cs
new file mode 100644
index 0000000..731dd6a
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/VisualElementRendererFlags.cs
@@ -0,0 +1,13 @@
+using System;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ [Flags]
+ public enum VisualElementRendererFlags : byte
+ {
+ None = 0,
+ Disposed = 1,
+ NeedsLayout = 2,
+ NeedsTransformation = 4,
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Xamarin.Forms.Platform.Tizen.csproj b/Xamarin.Forms.Platform.Tizen/Xamarin.Forms.Platform.Tizen.csproj
new file mode 100644
index 0000000..818e480
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Xamarin.Forms.Platform.Tizen.csproj
@@ -0,0 +1,163 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProjectGuid>{227D2CC5-26A1-4CE7-AE25-1B18AF625B9C}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>Xamarin.Forms.Platform.Tizen</RootNamespace>
+ <AssemblyName>Xamarin.Forms.Platform.Tizen</AssemblyName>
+ <FileAlignment>512</FileAlignment>
+ </PropertyGroup>
+ <PropertyGroup>
+ <TargetFrameworkIdentifier>.NETStandard</TargetFrameworkIdentifier>
+ <TargetFrameworkVersion>v1.6</TargetFrameworkVersion>
+ <NuGetTargetMoniker>.NETStandard,Version=v1.6</NuGetTargetMoniker>
+ <AddAdditionalExplicitAssemblyReferences>false</AddAdditionalExplicitAssemblyReferences>
+ <NoStdLib>true</NoStdLib>
+ <NoWarn>$(NoWarn);1701;1702</NoWarn>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Compile Include="Cells\CellRenderer.cs" />
+ <Compile Include="Cells\EntryCellRenderer.cs" />
+ <Compile Include="Cells\ImageCellRenderer.cs" />
+ <Compile Include="Cells\SwitchCellRenderer.cs" />
+ <Compile Include="Cells\TextCellRenderer.cs" />
+ <Compile Include="Cells\ViewCellRenderer.cs" />
+ <Compile Include="Deserializer.cs" />
+ <Compile Include="ElementChangedEventArgs.cs" />
+ <Compile Include="EvasObjectWrapper.cs" />
+ <Compile Include="ExportImageSourceHandlerAttribute.cs" />
+ <Compile Include="ExportCellAttribute.cs" />
+ <Compile Include="ExportRendererAttribute.cs" />
+ <Compile Include="Extensions\ColorExtensions.cs" />
+ <Compile Include="Extensions\KeyboardExtensions.cs" />
+ <Compile Include="Extensions\LayoutExtensions.cs" />
+ <Compile Include="Extensions\ScrollToPositionExtensions.cs" />
+ <Compile Include="Extensions\TextAlignmentExtensions.cs" />
+ <Compile Include="Forms.cs" />
+ <Compile Include="GestureHandler.cs" />
+ <Compile Include="FormsApplication.cs" />
+ <Compile Include="Log\ConsoleLogger.cs" />
+ <Compile Include="Log\DlogLogger.cs" />
+ <Compile Include="Log\ILogger.cs" />
+ <Compile Include="Log\Log.cs" />
+ <Compile Include="Log\XamarinLogListener.cs" />
+ <Compile Include="Native\Box.cs" />
+ <Compile Include="Native\Button.cs" />
+ <Compile Include="Native\Canvas.cs" />
+ <Compile Include="Native\ContentPage.cs" />
+ <Compile Include="Native\DateChangedEventArgs.cs" />
+ <Compile Include="Native\DatePicker.cs" />
+ <Compile Include="Native\DateTimePickerDialog.cs" />
+ <Compile Include="Native\Dialog.cs" />
+ <Compile Include="Native\DisplayOrientations.cs" />
+ <Compile Include="Native\Entry.cs" />
+ <Compile Include="Native\FormattedString.cs" />
+ <Compile Include="Native\IContainable.cs" />
+ <Compile Include="Native\Image.cs" />
+ <Compile Include="Native\IMeasurable.cs" />
+ <Compile Include="Native\ITextable.cs" />
+ <Compile Include="Native\Keyboard.cs" />
+ <Compile Include="Native\Label.cs" />
+ <Compile Include="Native\LayoutEventArgs.cs" />
+ <Compile Include="Native\LineBreakMode.cs" />
+ <Compile Include="Native\ListView.cs" />
+ <Compile Include="Native\MasterDetailPage.cs" />
+ <Compile Include="Native\ObservableCollection.cs" />
+ <Compile Include="Native\SearchBar.cs" />
+ <Compile Include="Native\Span.cs" />
+ <Compile Include="Native\TableView.cs" />
+ <Compile Include="Native\TextAlignment.cs" />
+ <Compile Include="Native\TextHelper.cs" />
+ <Compile Include="Native\TimePicker.cs" />
+ <Compile Include="Native\Window.cs" />
+ <Compile Include="Platform.cs" />
+ <Compile Include="PlatformEffect.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="Renderers\ActivityIndicatorRenderer.cs" />
+ <Compile Include="Renderers\BoxViewRenderer.cs" />
+ <Compile Include="Renderers\ButtonRenderer.cs" />
+ <Compile Include="Renderers\CarouselPageRenderer.cs" />
+ <Compile Include="Renderers\ContentPageRenderer.cs" />
+ <Compile Include="Renderers\DatePickerRenderer.cs" />
+ <Compile Include="Renderers\EditorRenderer.cs" />
+ <Compile Include="Renderers\EntryRenderer.cs" />
+ <Compile Include="Renderers\EvasObjectWrapperRenderer.cs" />
+ <Compile Include="Renderers\FrameRenderer.cs" />
+ <Compile Include="Renderers\ImageRenderer.cs" />
+ <Compile Include="Renderers\IVisualElementRenderer.cs" />
+ <Compile Include="Renderers\LabelRenderer.cs" />
+ <Compile Include="Renderers\LayoutRenderer.cs" />
+ <Compile Include="Renderers\ListViewRenderer.cs" />
+ <Compile Include="Renderers\MasterDetailPageRenderer.cs" />
+ <Compile Include="Renderers\NavigationPageRenderer.cs" />
+ <Compile Include="Renderers\PickerRenderer.cs" />
+ <Compile Include="Renderers\ProgressBarRenderer.cs" />
+ <Compile Include="Renderers\ScrollViewRenderer.cs" />
+ <Compile Include="Renderers\SearchBarRenderer.cs" />
+ <Compile Include="Renderers\SliderRenderer.cs" />
+ <Compile Include="Renderers\StepperRenderer.cs" />
+ <Compile Include="Renderers\SwitchRenderer.cs" />
+ <Compile Include="Renderers\TabbedPageRenderer.cs" />
+ <Compile Include="Renderers\TableViewRenderer.cs" />
+ <Compile Include="Renderers\TimePickerRenderer.cs" />
+ <Compile Include="Renderers\ViewRenderer.cs" />
+ <Compile Include="Renderers\VisualElementRenderer.cs" />
+ <Compile Include="ResourcePath.cs" />
+ <Compile Include="ResourcesProvider.cs" />
+ <Compile Include="TizenPlatformServices.cs" />
+ <Compile Include="TizenTitleBarVisibility.cs" />
+ <Compile Include="ViewInitializedEventArgs.cs" />
+ <Compile Include="VisualElementChangedEventArgs.cs" />
+ <Compile Include="VisualElementRendererFlags.cs" />
+ <Compile Include="TizenIsolatedStorageFile.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="Xamarin.Forms.Platform.Tizen.project.json" />
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+ <ItemGroup>
+ <ProjectReference Include="..\Xamarin.Forms.Core\Xamarin.Forms.Core.csproj">
+ <Project>{57B8B73D-C3B5-4C42-869E-7B2F17D354AC}</Project>
+ <Name>Xamarin.Forms.Core</Name>
+ </ProjectReference>
+ </ItemGroup>
+ <PropertyGroup>
+ <!-- https://github.com/dotnet/corefxlab/tree/master/samples/NetCoreSample and
+ https://docs.microsoft.com/en-us/dotnet/articles/core/tutorials/target-dotnetcore-with-msbuild
+ -->
+ <!-- We don't use any of MSBuild's resolution logic for resolving the framework, so just set these two
+ properties to any folder that exists to skip the GetReferenceAssemblyPaths task (not target) and
+ to prevent it from outputting a warning (MSB3644).
+ -->
+ <_TargetFrameworkDirectories>$(MSBuildThisFileDirectory)</_TargetFrameworkDirectories>
+ <_FullFrameworkReferenceAssemblyPaths>$(MSBuildThisFileDirectory)</_FullFrameworkReferenceAssemblyPaths>
+ <AutoUnifyAssemblyReferences>true</AutoUnifyAssemblyReferences>
+ </PropertyGroup>
+</Project>
diff --git a/Xamarin.Forms.Platform.Tizen/Xamarin.Forms.Platform.Tizen.project.json b/Xamarin.Forms.Platform.Tizen/Xamarin.Forms.Platform.Tizen.project.json
new file mode 100644
index 0000000..02b62b9
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Xamarin.Forms.Platform.Tizen.project.json
@@ -0,0 +1,13 @@
+{
+ "dependencies": {
+ "ElmSharp": "1.1.0-*",
+ "NETStandard.Library": "1.6.0",
+ "System.Runtime.Serialization.Xml": "4.1.1",
+ "Tizen.Applications": "1.0.2"
+ },
+ "frameworks": {
+ "netstandard1.6": {
+ "imports": "portable-net45+win8+wpa81+wp8"
+ }
+ }
+}
diff --git a/Xamarin.Forms.Tizen.sln b/Xamarin.Forms.Tizen.sln
new file mode 100644
index 0000000..ecc5d8f
--- /dev/null
+++ b/Xamarin.Forms.Tizen.sln
@@ -0,0 +1,65 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 14
+VisualStudioVersion = 14.0.25420.1
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xamarin.Forms.Core", "Xamarin.Forms.Core\Xamarin.Forms.Core.csproj", "{57B8B73D-C3B5-4C42-869E-7B2F17D354AC}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xamarin.Forms.Platform", "Xamarin.Forms.Platform\Xamarin.Forms.Platform.csproj", "{67F9D3A8-F71E-4428-913F-C37AE82CDB24}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xamarin.Forms.Xaml", "Xamarin.Forms.Xaml\Xamarin.Forms.Xaml.csproj", "{9DB2F292-8034-4E06-89AD-98BBDA4306B9}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xamarin.Forms.Platform.Tizen", "Xamarin.Forms.Platform.Tizen\Xamarin.Forms.Platform.Tizen.csproj", "{227D2CC5-26A1-4CE7-AE25-1B18AF625B9C}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xamarin.Forms.Maps", "Xamarin.Forms.Maps\Xamarin.Forms.Maps.csproj", "{7D13BAC2-C6A4-416A-B07E-C169B199E52B}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xamarin.Forms.Maps.Tizen", "Xamarin.Forms.Maps.Tizen\Xamarin.Forms.Maps.Tizen.csproj", "{D29D5750-9A39-4E92-A19E-62567D660B7D}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ Turkey|Any CPU = Turkey|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {57B8B73D-C3B5-4C42-869E-7B2F17D354AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {57B8B73D-C3B5-4C42-869E-7B2F17D354AC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {57B8B73D-C3B5-4C42-869E-7B2F17D354AC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {57B8B73D-C3B5-4C42-869E-7B2F17D354AC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {57B8B73D-C3B5-4C42-869E-7B2F17D354AC}.Turkey|Any CPU.ActiveCfg = Turkey|Any CPU
+ {57B8B73D-C3B5-4C42-869E-7B2F17D354AC}.Turkey|Any CPU.Build.0 = Turkey|Any CPU
+ {67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Release|Any CPU.Build.0 = Release|Any CPU
+ {67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Turkey|Any CPU.ActiveCfg = Release|Any CPU
+ {67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Turkey|Any CPU.Build.0 = Release|Any CPU
+ {9DB2F292-8034-4E06-89AD-98BBDA4306B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9DB2F292-8034-4E06-89AD-98BBDA4306B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9DB2F292-8034-4E06-89AD-98BBDA4306B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9DB2F292-8034-4E06-89AD-98BBDA4306B9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9DB2F292-8034-4E06-89AD-98BBDA4306B9}.Turkey|Any CPU.ActiveCfg = Turkey|Any CPU
+ {9DB2F292-8034-4E06-89AD-98BBDA4306B9}.Turkey|Any CPU.Build.0 = Turkey|Any CPU
+ {227D2CC5-26A1-4CE7-AE25-1B18AF625B9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {227D2CC5-26A1-4CE7-AE25-1B18AF625B9C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {227D2CC5-26A1-4CE7-AE25-1B18AF625B9C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {227D2CC5-26A1-4CE7-AE25-1B18AF625B9C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {227D2CC5-26A1-4CE7-AE25-1B18AF625B9C}.Turkey|Any CPU.ActiveCfg = Release|Any CPU
+ {227D2CC5-26A1-4CE7-AE25-1B18AF625B9C}.Turkey|Any CPU.Build.0 = Release|Any CPU
+ {7D13BAC2-C6A4-416A-B07E-C169B199E52B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7D13BAC2-C6A4-416A-B07E-C169B199E52B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7D13BAC2-C6A4-416A-B07E-C169B199E52B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7D13BAC2-C6A4-416A-B07E-C169B199E52B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7D13BAC2-C6A4-416A-B07E-C169B199E52B}.Turkey|Any CPU.ActiveCfg = Turkey|Any CPU
+ {7D13BAC2-C6A4-416A-B07E-C169B199E52B}.Turkey|Any CPU.Build.0 = Turkey|Any CPU
+ {D29D5750-9A39-4E92-A19E-62567D660B7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D29D5750-9A39-4E92-A19E-62567D660B7D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D29D5750-9A39-4E92-A19E-62567D660B7D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D29D5750-9A39-4E92-A19E-62567D660B7D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D29D5750-9A39-4E92-A19E-62567D660B7D}.Turkey|Any CPU.ActiveCfg = Release|Any CPU
+ {D29D5750-9A39-4E92-A19E-62567D660B7D}.Turkey|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/packaging/custom-find-requires b/packaging/custom-find-requires
new file mode 100755
index 0000000..ebd22cb
--- /dev/null
+++ b/packaging/custom-find-requires
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+if [ -z "$RPM_BUILD_ROOT" ] ; then
+ echo "The $0 script is not intended to be executed directly." > /dev/stderr
+ exit 1
+fi
+
+# this grep will consume whole list of files from stdin
+if grep -qF /Xamarin.Forms.Platform.Tizen.dll ; then
+ # if it's a X.F.P.Tizen package, then find the version of elm-sharp (or elm-sharp-nuget) in use
+ rpm -qa --queryformat 'elm-sharp >= %{VERSION}\n' 'elm-sharp*' | head -n1
+fi
diff --git a/packaging/xamarin-forms-tizen.manifest b/packaging/xamarin-forms-tizen.manifest
new file mode 100755
index 0000000..75b0fa5
--- /dev/null
+++ b/packaging/xamarin-forms-tizen.manifest
@@ -0,0 +1,5 @@
+<manifest>
+ <request>
+ <domain name="_"/>
+ </request>
+</manifest>
diff --git a/packaging/xamarin-forms-tizen.spec b/packaging/xamarin-forms-tizen.spec
new file mode 100644
index 0000000..074629b
--- /dev/null
+++ b/packaging/xamarin-forms-tizen.spec
@@ -0,0 +1,94 @@
+%{!?dotnet_assembly_path: %define dotnet_assembly_path /opt/usr/share/dotnet.tizen/framework}
+
+%if 0%{?tizen_build_devel_mode}
+%define BUILDCONF Debug
+%else
+%define BUILDCONF Release
+%endif
+
+%define XF_VERSION 2.3.3.175
+#%define XF_PRETAG pre3
+
+# Increase this XF_TIZEN_VERSION when any public APIs of Xamarin.Forms.Platform.Tizen are changed.
+%define XF_TIZEN_VERSION b01
+
+Name: xamarin-forms-tizen
+Summary: Xamarin.Forms for Tizen platform
+Version: %{XF_VERSION}
+Release: %{XF_TIZEN_VERSION}
+License: MIT
+Group: Graphics & UI Framework/Libraries
+Source0: %{name}-%{version}.tar.gz
+Source1: %{name}.manifest
+
+%define NUPKG_VERSION %{XF_VERSION}%{?XF_PRETAG:-%{XF_PRETAG}}-%{XF_TIZEN_VERSION}
+
+# Instead of disabling Automatic Dependencies completely with `AutoReqProv: no'
+# let's override Mono provided portion
+%define __mono_requires %{_builddir}/%name-%version/packaging/custom-find-requires
+
+ExcludeArch: aarch64 %ix86
+
+BuildRequires: mono-compiler
+BuildRequires: mono-devel
+BuildRequires: referenceassemblies-pcl
+
+BuildRequires: dotnet-build-tools
+
+# C# API Requires
+BuildRequires: csapi-tizen-nuget
+BuildRequires: csapi-application-nuget
+BuildRequires: elm-sharp-nuget
+
+%description
+Allows one to use portable controls subsets that are mapped to native
+controls of Android, iOS, Windows Phone, and Tizen.
+
+%prep
+%setup -q
+cp %{SOURCE1} .
+
+%build
+# Restore NuGet Dependencies
+find . -name *.Tizen.project.json -print0 | xargs -n1 -0 nuget restore
+
+# Build
+xbuild Xamarin.Forms.Tizen.sln \
+ /p:Configuration=%{BUILDCONF} \
+ /p:PackageSources=/nuget
+
+# Create NuGet Packages
+nuget pack .nuspec/Xamarin.Forms.Platform.Tizen.nuspec \
+ -BasePath ./.nuspec -Version %{NUPKG_VERSION} \
+ -Properties "Configuration=%{BUILDCONF}"
+
+%install
+function install_asm()
+{
+ mkdir -p %{buildroot}%{dotnet_assembly_path}
+ install -p -m 644 $1/bin/%{BUILDCONF}/$1.dll %{buildroot}%{dotnet_assembly_path}
+}
+
+install_asm Xamarin.Forms.Core
+install_asm Xamarin.Forms.Xaml
+install_asm Xamarin.Forms.Platform
+install_asm Xamarin.Forms.Platform.Tizen
+
+mkdir -p %{buildroot}/nuget
+install -p -m 644 *.nupkg %{buildroot}/nuget
+
+%files
+%manifest %{name}.manifest
+%license LICENSE
+%attr(644,root,root) %{dotnet_assembly_path}/*.dll
+
+%package nuget
+Summary: NuGet package for %{name}
+Group: Development/Libraries
+Requires: %{name} = %{version}-%{release}
+
+%description nuget
+NuGet package for %{name}
+
+%files nuget
+/nuget/*.nupkg