Support new features(RadialProgress) of Tizen.CircularUI (#210) accepted/tizen/5.5/unified/20200427.230217 accepted/tizen/unified/20200428.001324 submit/tizen/20200427.090300 submit/tizen_5.5/20200427.090248
author유리나/Common Platform Lab(SR)/Staff Engineer/삼성전자 <rina6350.you@samsung.com>
Fri, 24 Apr 2020 01:56:06 +0000 (10:56 +0900)
committer윤정현/Common Platform Lab(SR)/Staff Engineer/삼성전자 <jh0506.yun@samsung.com>
Fri, 24 Apr 2020 01:56:06 +0000 (10:56 +0900)
* [Tizen] Fix dependency service registration correctly (#10287)

* [Bug] [Core+WPF] Grid + ListView -> first row height not correct + width not correct + ArgumentOutOfRangeException when tapped (#8440) fixes #6436 fixes #8039 fixes #9029 fixes #5682 fixes #6842

* [Bug] [WPF] Grid + ListView -> first row height not correct

* Fix indention

* DynamicResource for listview colors

* Fix indention, use tabs instead of spaces

* Fix cell control

* Fix renderer, update ListViewSelectionColor

* Fix cell is not displayed

* Fix invalid cell width 9029

* Cell_Appearing

* Update controlgallery

* Fix 6842 - invalid layout after windows maximized

* Remove unused using

* Get children from InternalChildren

* Fix for grid layout in listview cell

* Fix ListView selection issue

* [Tizen] Adds OverlayContent to Application (#10310)

* [SwipeView] Added IsEnabled and IsVisible properties to SwipeItem (#9949)

* Added IsEnabled and IsVisible properties to SwipeItem

* Fixed wrong animation value

* Removed GetVisibleSwipeItemsByDirection to avoid create unnecessary SwipeItems instance
fixes #9881

* Applying proper style on SwitchCell depending on target idiom (#403)

* Protect corner case of updating OverlayContent (#404)

* [Tizen] Add Shell renderer for watch (#393)

* Update ColorTypeConverter to support HSVA (#398)

* Create wearable_app_requirement.md

* Adds Color.FromHsva (#397)

* Update ColorTypeConverter to support HSVA

* [Tizen] Adds RadioButton (#402)

* [Tizen] Activate IRotaryActionWidget when it has focus (#396)

* [Tizen] Activate IRotaryActionWidget when it has focus

* [Tizen] Add IRotaryInteraction and WatchList

* [Tizen] Change callback name

* [Tizen] Add WatchScroller

* [Tizen] Support scrollbar visibility properties of ListView

* [Tizen] Add IndicatorView (#357)

* [Tizen] Enable Page.ToolbarItem on wearable (#401)

* [Tizen] Invoke ItemsView Scrolled event

* [Tizen] Changed CarouselView scroll logic

* [Tizen] Add IndicatorView

* [Tizen] Add Shell renderer for watch

* [Tizen] Modify ExportRendererAttribute

* [Tizen] Enable rotary event on Shell

* [Tizen] Fix scroll event issue on ShellSection

* [Tizen] Update NavigationView of ShellRenderer

* [Tizen] Modify the property for rotary focused object

* [Tizen] Remove TargetProfile enum

* [Tizen] Modify callbacks for rotary event to allow to override

* Update Xamarin.Forms version to 4.6.0-pre4 (#270)

* Adds ICircleSurfaceProvider and ICircleSurfaceConsumer (#261)

* Merged branch `dev` (#271)

* Add FlatViewCell (#260)

* Adds OverlayContent to Application (#262)

* Add rotary event manager (#259)

* Add rotary event manager

* Change internal event to delegate

* Remove unused using statements

* Change class name to RotaryEventManager

* Update using directive

* Update CircularUI.cs

* Revert "Adds OverlayContent to Application (#262)"

This reverts commit 1d101475e93ce201f3c44c086a9c90ba819acdac.

Co-authored-by: Jay Cho <chojoong@gmail.com>
* Merge branch `add-circularui-chart` (#273)

* Adds CircularUI.Chart

* Add RadialProgress (#272)

* Add RadialProgress

* remove unused solution file

* Change namespace of FormsCircularUI

* Fix RadialProgress namespace

* Update FormsCircularUI.Init()

Co-authored-by: Seungkeun Lee <sngn.lee@samsung.com>
* meets alignment for Tizen.Wearable.CircularUI.Forms

* Meets alignment for Tizen.Wearable.CircularUI.Forms.Renderer

* Meets alignment for Xamarin.Forms

86 files changed:
src/XSF/Tizen.Wearable.CircularUI.Chart.Forms.Renderer/CircularUIChart.cs [new file with mode: 0644]
src/XSF/Tizen.Wearable.CircularUI.Chart.Forms/RadialProgress.cs [new file with mode: 0644]
src/XSF/Tizen.Wearable.CircularUI.Forms.Renderer/CircleImageRenderer.cs [changed mode: 0644->0755]
src/XSF/Tizen.Wearable.CircularUI.Forms.Renderer/CirclePageRenderer.cs
src/XSF/Tizen.Wearable.CircularUI.Forms.Renderer/CircleStepperRenderer.cs
src/XSF/Tizen.Wearable.CircularUI.Forms.Renderer/CircleSurfaceViewRenderer.cs
src/XSF/Tizen.Wearable.CircularUI.Forms.Renderer/CircleWidgetRendererExtension.cs
src/XSF/Tizen.Wearable.CircularUI.Forms.Renderer/CircularUI.cs
src/XSF/Tizen.Wearable.CircularUI.Forms.Renderer/CircularUIForms.cs
src/XSF/Tizen.Wearable.CircularUI.Forms.Renderer/ContentButtonRenderer.cs
src/XSF/Tizen.Wearable.CircularUI.Forms.Renderer/FlatViewCellRenderer.cs [new file with mode: 0644]
src/XSF/Tizen.Wearable.CircularUI.Forms.Renderer/GoogleMapViewRenderer.cs
src/XSF/Tizen.Wearable.CircularUI.Forms.Renderer/IndexPageRenderer.cs
src/XSF/Tizen.Wearable.CircularUI.Forms.Renderer/InformationPopupImplementation.cs
src/XSF/Tizen.Wearable.CircularUI.Forms.Renderer/MediaPlayerImpl.cs
src/XSF/Tizen.Wearable.CircularUI.Forms.Renderer/MediaSourceHandler.cs [deleted file]
src/XSF/Tizen.Wearable.CircularUI.Forms.Renderer/NativeCirclePage.cs
src/XSF/Tizen.Wearable.CircularUI.Forms.Renderer/RotaryService.cs [new file with mode: 0644]
src/XSF/Tizen.Wearable.CircularUI.Forms.Renderer/Shell/IShellItemRenderer.cs
src/XSF/Tizen.Wearable.CircularUI.Forms.Renderer/Shell/NavigationDrawer.cs
src/XSF/Tizen.Wearable.CircularUI.Forms.Renderer/Shell/NavigationView.cs
src/XSF/Tizen.Wearable.CircularUI.Forms.Renderer/Shell/ShellContentRenderer.cs
src/XSF/Tizen.Wearable.CircularUI.Forms.Renderer/Shell/ShellItemRenderer.cs
src/XSF/Tizen.Wearable.CircularUI.Forms.Renderer/Shell/ShellRenderer.cs
src/XSF/Tizen.Wearable.CircularUI.Forms.Renderer/Shell/ShellRendererFactory.cs
src/XSF/Tizen.Wearable.CircularUI.Forms.Renderer/Shell/ShellSectionItemsRenderer.cs
src/XSF/Tizen.Wearable.CircularUI.Forms.Renderer/Shell/ShellSectionNavigationRenderer.cs
src/XSF/Tizen.Wearable.CircularUI.Forms.Renderer/ThemeLoader.cs
src/XSF/Tizen.Wearable.CircularUI.Forms.Renderer/TizenCircleSurfaceEffect.cs [changed mode: 0644->0755]
src/XSF/Tizen.Wearable.CircularUI.Forms.Renderer/TwoButtonPopupImplementation.cs
src/XSF/Tizen.Wearable.CircularUI.Forms.Renderer/Widget/FormsWidgetApplication.cs
src/XSF/Tizen.Wearable.CircularUI.Forms/CircleDateTimeSelector.cs
src/XSF/Tizen.Wearable.CircularUI.Forms/CircleListView.cs
src/XSF/Tizen.Wearable.CircularUI.Forms/CirclePage.cs
src/XSF/Tizen.Wearable.CircularUI.Forms/CircleScrollView.cs
src/XSF/Tizen.Wearable.CircularUI.Forms/CircleStepper.cs
src/XSF/Tizen.Wearable.CircularUI.Forms/CircleSurfaceView.cs
src/XSF/Tizen.Wearable.CircularUI.Forms/CircularShell.cs
src/XSF/Tizen.Wearable.CircularUI.Forms/ContentButton.cs
src/XSF/Tizen.Wearable.CircularUI.Forms/EmbeddingControlsConverter.cs [deleted file]
src/XSF/Tizen.Wearable.CircularUI.Forms/FileMediaSource.cs [deleted file]
src/XSF/Tizen.Wearable.CircularUI.Forms/FlatViewCell.cs [moved from src/XSF/Tizen.Wearable.CircularUI.Forms.Renderer/IMediaViewProvider.cs with 67% similarity]
src/XSF/Tizen.Wearable.CircularUI.Forms/ICircleSurfaceConsumer.cs [moved from src/XSF/Tizen.Wearable.CircularUI.Forms/PlaybackState.cs with 71% similarity]
src/XSF/Tizen.Wearable.CircularUI.Forms/ICircleSurfaceProvider.cs [moved from src/XSF/Tizen.Wearable.CircularUI.Forms/IVideoOutput.cs with 62% similarity]
src/XSF/Tizen.Wearable.CircularUI.Forms/IPlatformMediaPlayer.cs [deleted file]
src/XSF/Tizen.Wearable.CircularUI.Forms/IRotaryService.cs [new file with mode: 0644]
src/XSF/Tizen.Wearable.CircularUI.Forms/MediaPlayer.cs
src/XSF/Tizen.Wearable.CircularUI.Forms/MediaSource.cs [deleted file]
src/XSF/Tizen.Wearable.CircularUI.Forms/MediaSourceConverter.cs [deleted file]
src/XSF/Tizen.Wearable.CircularUI.Forms/MediaView.cs
src/XSF/Tizen.Wearable.CircularUI.Forms/RotaryEventManager.cs [moved from src/XSF/Tizen.Wearable.CircularUI.Forms/FileMediaSourceConverter.cs with 52% similarity]
src/XSF/Tizen.Wearable.CircularUI.Forms/UriMediaSource.cs [deleted file]
src/XSF/Xamarin.Forms.Core/AppThemeChangedEventArgs.cs
src/XSF/Xamarin.Forms.Core/Expander.cs
src/XSF/Xamarin.Forms.Core/ExpanderState.cs
src/XSF/Xamarin.Forms.Core/ISwipeItem.cs
src/XSF/Xamarin.Forms.Core/ListView.cs
src/XSF/Xamarin.Forms.Core/MenuItem.cs
src/XSF/Xamarin.Forms.Core/PlatformConfiguration/TizenSpecific/Application.cs
src/XSF/Xamarin.Forms.Core/PlatformConfiguration/TizenSpecific/IVideoOutput.cs
src/XSF/Xamarin.Forms.Core/Shell/BaseShellItem.cs
src/XSF/Xamarin.Forms.Core/Shell/NavigableElement.cs
src/XSF/Xamarin.Forms.Core/StackLayout.cs
src/XSF/Xamarin.Forms.Core/SwipeItem.cs
src/XSF/Xamarin.Forms.Core/SwipeItemView.cs
src/XSF/Xamarin.Forms.Platform.Tizen/Cells/SwitchCellRenderer.cs
src/XSF/Xamarin.Forms.Platform.Tizen/ExportRendererAttribute.cs
src/XSF/Xamarin.Forms.Platform.Tizen/Forms.cs
src/XSF/Xamarin.Forms.Platform.Tizen/FormsApplication.cs
src/XSF/Xamarin.Forms.Platform.Tizen/Native/EmbeddingControls.cs
src/XSF/Xamarin.Forms.Platform.Tizen/Native/MediaPlayerImpl.cs
src/XSF/Xamarin.Forms.Platform.Tizen/Properties/AssemblyInfo.cs
src/XSF/Xamarin.Forms.Platform.Tizen/Renderers/IndicatorViewRenderer.cs
src/XSF/Xamarin.Forms.Platform.Tizen/Renderers/PageRenderer.cs
src/XSF/Xamarin.Forms.Platform.Tizen/Renderers/RadioButtonRenderer.cs
src/XSF/Xamarin.Forms.Platform.Tizen/Renderers/ViewRenderer.cs
src/XSF/Xamarin.Forms.Platform.Tizen/Shell/Watch/IShellItemRenderer.cs [new file with mode: 0644]
src/XSF/Xamarin.Forms.Platform.Tizen/Shell/Watch/NavigationDrawer.cs [new file with mode: 0644]
src/XSF/Xamarin.Forms.Platform.Tizen/Shell/Watch/NavigationView.cs [new file with mode: 0644]
src/XSF/Xamarin.Forms.Platform.Tizen/Shell/Watch/ShellContentRenderer.cs [new file with mode: 0644]
src/XSF/Xamarin.Forms.Platform.Tizen/Shell/Watch/ShellItemRenderer.cs [new file with mode: 0644]
src/XSF/Xamarin.Forms.Platform.Tizen/Shell/Watch/ShellRenderer.cs [new file with mode: 0644]
src/XSF/Xamarin.Forms.Platform.Tizen/Shell/Watch/ShellRendererFactory.cs [new file with mode: 0644]
src/XSF/Xamarin.Forms.Platform.Tizen/Shell/Watch/ShellSectionItemsRenderer.cs [new file with mode: 0644]
src/XSF/Xamarin.Forms.Platform.Tizen/Shell/Watch/ShellSectionNavigationRenderer.cs [new file with mode: 0644]
src/XSF/Xamarin.Forms.Platform.Tizen/StaticRegistrar.cs

diff --git a/src/XSF/Tizen.Wearable.CircularUI.Chart.Forms.Renderer/CircularUIChart.cs b/src/XSF/Tizen.Wearable.CircularUI.Chart.Forms.Renderer/CircularUIChart.cs
new file mode 100644 (file)
index 0000000..2133157
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://floralicense.org/license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Tizen.Wearable.CircularUI.Chart.Forms
+{
+    public static class FormsCircularUIChart
+    {
+        public static readonly string Tag = "CircularUI.Chart";
+
+        public static bool IsInitialized { get; private set; }
+
+        public static void Init()
+        {
+            if (IsInitialized) return;
+            IsInitialized = true;
+        }
+    }
+}
diff --git a/src/XSF/Tizen.Wearable.CircularUI.Chart.Forms/RadialProgress.cs b/src/XSF/Tizen.Wearable.CircularUI.Chart.Forms/RadialProgress.cs
new file mode 100644 (file)
index 0000000..878f48c
--- /dev/null
@@ -0,0 +1,321 @@
+using Xamarin.Forms;
+using SkiaSharp.Views.Forms;
+using SkiaSharp;
+using Xamarin.Forms.Internals;
+
+namespace Tizen.Wearable.CircularUI.Forms
+{
+    public class RadialProgress : SKCanvasView
+    {
+        public static readonly BindableProperty ProgressProperty = BindableProperty.Create(nameof(Progress), typeof(double), typeof(RadialProgress), defaultValue: 0.0, coerceValue: (bo, v) => ((double)v).Clamp(0, 1), propertyChanged: (b, o, n) => ((RadialProgress)b).InvalidateSurface());
+        public static readonly BindableProperty ThicknessProperty = BindableProperty.Create(nameof(Thickness), typeof(double), typeof(RadialProgress), defaultValue: 30d, propertyChanged: (b, o, n) => ((RadialProgress)b).InvalidateSurface());
+        public static readonly BindableProperty RadialBackgroundColorProperty = BindableProperty.Create(nameof(RadialBackgroundColor), typeof(Color), typeof(RadialProgress), defaultValue: Color.Default, propertyChanged: (b, o, n) => ((RadialProgress)b).InvalidateSurface());
+        public static readonly BindableProperty RadialColorProperty = BindableProperty.Create(nameof(RadialColor), typeof(Color), typeof(RadialProgress), defaultValue: Color.Default, propertyChanged: (b, o, n) => ((RadialProgress)b).InvalidateSurface());
+        public static readonly BindableProperty RadialStartColorProperty = BindableProperty.Create(nameof(RadialStartColor), typeof(Color), typeof(RadialProgress), defaultValue: Color.FromHex("#9cff00"), propertyChanged: (b, o, n) => ((RadialProgress)b).InvalidateSurface());
+        public static readonly BindableProperty RadialMiddleColorProperty = BindableProperty.Create(nameof(RadialMiddleColor), typeof(Color), typeof(RadialProgress), defaultValue: Color.FromHex("#aaff00"), propertyChanged: (b, o, n) => ((RadialProgress)b).InvalidateSurface());
+        public static readonly BindableProperty RadialEndColorProperty = BindableProperty.Create(nameof(RadialEndColor), typeof(Color), typeof(RadialProgress), defaultValue: Color.FromHex("#d8fe00"), propertyChanged: (b, o, n) => ((RadialProgress)b).InvalidateSurface());
+        public static readonly BindableProperty TextColorProperty = BindableProperty.Create(nameof(TextColor), typeof(Color), typeof(RadialProgress), defaultValue: Color.Accent, propertyChanged: (b, o, n) => ((RadialProgress)b).InvalidateSurface());
+        public static readonly BindableProperty TextFormatProperty = BindableProperty.Create(nameof(TextFormat), typeof(string), typeof(RadialProgress), defaultValue: "{0:P0}", propertyChanged: (b, o, n) => ((RadialProgress)b).InvalidateSurface());
+        public static readonly BindableProperty FontSizeProperty = BindableProperty.Create(nameof(FontSize), typeof(double), typeof(RadialProgress), defaultValue: 20d, propertyChanged: (b, o, n) => ((RadialProgress)b).InvalidateSurface());
+        public static readonly BindableProperty HasLabelProperty = BindableProperty.Create(nameof(HasLabel), typeof(bool), typeof(RadialProgress), defaultValue: false, propertyChanged: (b, o, n) => ((RadialProgress)b).InvalidateSurface());
+        public static readonly BindableProperty StartAngleProperty = BindableProperty.Create(nameof(StartAngle), typeof(double), typeof(RadialProgress), defaultValue: 0d, propertyChanged: (b, o, n) => ((RadialProgress)b).InvalidateSurface());
+        public static readonly BindableProperty SweepAngleProperty = BindableProperty.Create(nameof(SweepAngle), typeof(double), typeof(RadialProgress), defaultValue: 365d, propertyChanged: (b, o, n) => ((RadialProgress)b).InvalidateSurface());
+        public static readonly BindableProperty StartStrokeCapProperty = BindableProperty.Create(nameof(StartStrokeCap), typeof(RadialProgressStrokeCap), typeof(RadialProgress), defaultValue: RadialProgressStrokeCap.Round, propertyChanged: (b, o, n) => ((RadialProgress)b).InvalidateSurface());
+        public static readonly BindableProperty EndStrokeCapProperty = BindableProperty.Create(nameof(EndStrokeCap), typeof(RadialProgressStrokeCap), typeof(RadialProgress), defaultValue: RadialProgressStrokeCap.Round, propertyChanged: (b, o, n) => ((RadialProgress)b).InvalidateSurface());
+
+        public RadialProgress()
+        {
+            HorizontalOptions = LayoutOptions.FillAndExpand;
+            VerticalOptions = LayoutOptions.FillAndExpand;
+        }
+
+        public double Progress
+        {
+            get
+            {
+                return (double)GetValue(ProgressProperty);
+            }
+            set
+            {
+                SetValue(ProgressProperty, value);
+            }
+        }
+
+        public double Thickness
+        {
+            get
+            {
+                return (double)GetValue(ThicknessProperty);
+            }
+            set
+            {
+                SetValue(ThicknessProperty, value);
+            }
+
+        }
+
+        public Color RadialBackgroundColor
+        {
+            get
+            {
+                return (Color)GetValue(RadialBackgroundColorProperty);
+            }
+            set
+            {
+                SetValue(RadialBackgroundColorProperty, value);
+            }
+        }
+
+        public Color RadialColor
+        {
+            get
+            {
+                return (Color)GetValue(RadialColorProperty);
+            }
+            set
+            {
+                SetValue(RadialColorProperty, value);
+                RadialStartColor = RadialMiddleColor = RadialEndColor = value;
+            }
+        }
+
+        public Color RadialStartColor
+        {
+            get
+            {
+                return (Color)GetValue(RadialStartColorProperty);
+            }
+            set
+            {
+                SetValue(RadialStartColorProperty, value);
+            }
+        }
+
+        public Color RadialMiddleColor
+        {
+            get
+            {
+                return (Color)GetValue(RadialMiddleColorProperty);
+            }
+            set
+            {
+                SetValue(RadialMiddleColorProperty, value);
+            }
+        }
+
+        public Color RadialEndColor
+        {
+            get
+            {
+                return (Color)GetValue(RadialEndColorProperty);
+            }
+            set
+            {
+                SetValue(RadialEndColorProperty, value);
+            }
+        }
+
+        public Color TextColor
+        {
+            get
+            {
+                return (Color)GetValue(TextColorProperty);
+            }
+            set
+            {
+                SetValue(TextColorProperty, value);
+            }
+        }
+
+        public RadialProgressStrokeCap StartStrokeCap
+        {
+            get
+            {
+                return (RadialProgressStrokeCap)GetValue(StartStrokeCapProperty);
+            }
+            set
+            {
+                SetValue(StartStrokeCapProperty, value);
+            }
+        }
+
+        public RadialProgressStrokeCap EndStrokeCap
+        {
+            get
+            {
+                return (RadialProgressStrokeCap)GetValue(EndStrokeCapProperty);
+            }
+            set
+            {
+                SetValue(EndStrokeCapProperty, value);
+            }
+        }
+
+        public string TextFormat
+        {
+            get
+            {
+                return (string)GetValue(TextFormatProperty);
+            }
+            set
+            {
+                SetValue(TextFormatProperty, value);
+            }
+        }
+
+        public double FontSize
+        {
+            get
+            {
+                return (double)GetValue(FontSizeProperty);
+            }
+            set
+            {
+                SetValue(FontSizeProperty, value);
+            }
+        }
+
+        public bool HasLabel
+        {
+            get
+            {
+                return (bool)GetValue(HasLabelProperty);
+            }
+            set
+            {
+                SetValue(HasLabelProperty, value);
+            }
+        }
+
+        public double StartAngle
+        {
+            get
+            {
+                return (double)GetValue(StartAngleProperty);
+            }
+            set
+            {
+                SetValue(StartAngleProperty, value);
+            }
+        }
+
+        public double SweepAngle
+        {
+            get
+            {
+                return (double)GetValue(SweepAngleProperty);
+            }
+            set
+            {
+                SetValue(SweepAngleProperty, value);
+            }
+        }
+
+        protected override void OnPaintSurface(SKPaintSurfaceEventArgs e)
+        {
+            base.OnPaintSurface(e);
+            SKImageInfo info = e.Info;
+            SKSurface surface = e.Surface;
+            SKCanvas canvas = surface.Canvas;
+            canvas.Clear();
+
+            using (var primaryPaint = new SKPaint
+            {
+                IsAntialias = true,
+                Style = SKPaintStyle.Stroke,
+                StrokeCap = SKStrokeCap.Butt,
+                StrokeWidth = (float)Thickness,
+            })
+            using (var path = new SKPath())
+            using (var backgroundPath = new SKPath())
+            {
+                var margin = (float)Thickness / 2.0f;
+
+                if (RadialBackgroundColor != Color.Default && RadialBackgroundColor != Color.Transparent)
+                {
+                    primaryPaint.Color = RadialBackgroundColor.ToSKColor();
+                    backgroundPath.AddArc(new SKRect(margin, margin, info.Width - margin, info.Height - margin), -90 + (float)StartAngle, (float)SweepAngle);
+                    canvas.DrawPath(backgroundPath, primaryPaint);
+                    if (backgroundPath.PointCount > 0)
+                    {
+                        if (StartStrokeCap == RadialProgressStrokeCap.Round)
+                        {
+                            primaryPaint.StrokeCap = SKStrokeCap.Round;
+                            canvas.DrawPoint(backgroundPath.Points[0], primaryPaint);
+                        }
+                        if (EndStrokeCap == RadialProgressStrokeCap.Round)
+                        {
+                            primaryPaint.StrokeCap = SKStrokeCap.Round;
+                            canvas.DrawPoint(backgroundPath.Points[backgroundPath.PointCount - 1], primaryPaint);
+                        }
+                    }
+                }
+
+                SKColor[] colors = new SKColor[]
+                {
+                    RadialStartColor.ToSKColor(),
+                    RadialStartColor.ToSKColor(),
+                    RadialMiddleColor.ToSKColor(),
+                    RadialMiddleColor.ToSKColor(),
+                    RadialEndColor.ToSKColor(),
+                    RadialEndColor.ToSKColor(),
+                    RadialStartColor.ToSKColor(),
+                };
+
+                float[] colorPos = new float[]
+                {
+                    0,                // start
+                    (1 / 6.0f) * 1,   // start
+                    (1 / 6.0f) * 2,   // mid
+                    (1 / 6.0f) * 3,   // mid
+                    (1 / 6.0f) * 4,   // end
+                    (1 / 6.0f) * 5.7f,// end
+                    0.95f              // start
+                };
+
+                primaryPaint.StrokeCap = SKStrokeCap.Butt;
+
+                primaryPaint.Shader = SKShader.CreateSweepGradient(new SKPoint(info.Rect.MidX, info.Rect.MidY), colors, colorPos, SKShaderTileMode.Repeat, -90 + (float)StartAngle, -90 + (float)StartAngle + (float)SweepAngle);
+
+                path.AddArc(new SKRect(margin, margin, info.Width - margin, info.Height - margin), -90 + (float)StartAngle, (float)SweepAngle * (float)Progress);
+                canvas.DrawPath(path, primaryPaint);
+
+                if (path.PointCount > 2)
+                {
+                    if (StartStrokeCap == RadialProgressStrokeCap.Round)
+                    {
+                        primaryPaint.StrokeCap = SKStrokeCap.Round;
+                        canvas.DrawPoint(path.Points[0], primaryPaint);
+                    }
+
+                    if (EndStrokeCap == RadialProgressStrokeCap.Round)
+                    {
+                        primaryPaint.StrokeCap = SKStrokeCap.Round;
+                        canvas.DrawPoint(path.Points[path.PointCount - 1], primaryPaint);
+                    }
+                }
+
+                if (HasLabel)
+                {
+                    using (var fontPaint = new SKPaint
+                    {
+                        IsAntialias = true,
+                        Color = TextColor.ToSKColor(),
+                        TextSize = (float)FontSize,
+                    })
+                    using (var fontPath = new SKPath())
+                    {
+                        fontPath.AddPathReverse(path);
+                        canvas.DrawTextOnPath(string.Format(TextFormat, Progress), fontPath, 0, (float)FontSize / 3.0f, fontPaint);
+                    }
+                }
+            }
+        }
+    }
+
+    public enum RadialProgressStrokeCap
+    {
+        Butt = 0,
+        Round = 1,
+    }
+}
old mode 100644 (file)
new mode 100755 (executable)
index 63c4282..aed0ebf
@@ -21,6 +21,8 @@ using System.ComponentModel;
 using Tizen.Wearable.CircularUI.Forms;
 using Tizen.Wearable.CircularUI.Forms.Renderer;
 using Xamarin.Forms;
+using TLog = Tizen.Log;
+using FormsCircularUI = Tizen.Wearable.CircularUI.Forms.Renderer.FormsCircularUI;
 
 [assembly: ExportRenderer(typeof(CircleImage), typeof(CircleImageRenderer))]
 namespace Tizen.Wearable.CircularUI.Forms.Renderer
@@ -93,7 +95,7 @@ namespace Tizen.Wearable.CircularUI.Forms.Renderer
                                        }
                                        else
                                        {
-                                               global::Tizen.Log.Error(FormsCircularUI.Tag, $"streamImage == null ");
+                                               TLog.Error(FormsCircularUI.Tag, $"streamImage == null ");
                                        }
                                }
                        }
index 5b0753b..a49e7aa 100644 (file)
@@ -69,6 +69,7 @@ namespace Tizen.Wearable.CircularUI.Forms.Renderer
                        {
                                _circlePage.SetElement(e.NewElement);
 
+                               e.NewElement.CircleSurface = _circlePage.Surface;
                                e.NewElement.Appearing += OnPageAppearing;
                                e.NewElement.Disappearing += OnPageDisappearing;
                                var toolbarItems = e.NewElement.ToolbarItems as ObservableCollection<XToolbarItem>;
index f903828..866f812 100644 (file)
@@ -40,7 +40,7 @@ namespace Tizen.Wearable.CircularUI.Forms.Renderer
                        RegisterPropertyHandler(Stepper.MaximumProperty, UpdateMaximum);
                        RegisterPropertyHandler(Stepper.ValueProperty, UpdateValue);
                        RegisterPropertyHandler(Stepper.IncrementProperty, UpdateIncrement);
-            RegisterPropertyHandler(CircleStepper.IsWrapEnabledProperty, UpdateWrapEnabled);
+                       RegisterPropertyHandler(CircleStepper.IsWrapEnabledProperty, UpdateWrapEnabled);
                }
 
                protected override void OnElementChanged(ElementChangedEventArgs<CircleStepper> e)
@@ -185,12 +185,12 @@ namespace Tizen.Wearable.CircularUI.Forms.Renderer
                        }
                }
 
-        void UpdateWrapEnabled()
-        {
-            if (null != Control && null != Element)
-            {
-                Control.IsWrapEnabled = Element.IsWrapEnabled;
-            }
-        }
+               void UpdateWrapEnabled()
+               {
+                       if (null != Control && null != Element)
+                       {
+                               Control.IsWrapEnabled = Element.IsWrapEnabled;
+                       }
+               }
        }
 }
\ No newline at end of file
index 55077b5..e53e60e 100644 (file)
@@ -28,109 +28,110 @@ using ELayout = ElmSharp.Layout;
 [assembly: ExportRenderer(typeof(CircleSurfaceView), typeof(Tizen.Wearable.CircularUI.Forms.Renderer.CircleSurfaceViewRenderer))]
 namespace Tizen.Wearable.CircularUI.Forms.Renderer
 {
-    public class CircleSurfaceViewRenderer : ViewRenderer<CircleSurfaceView, Box>
-    {
+       public class CircleSurfaceViewRenderer : ViewRenderer<CircleSurfaceView, Box>
+       {
 
-        Dictionary<ICircleSurfaceItem, ICircleWidget> _circleSurfaceItems;
-        ELayout _surfaceLayout;
-        CircleSurface _circleSurface;
+               Dictionary<ICircleSurfaceItem, ICircleWidget> _circleSurfaceItems;
+               ELayout _surfaceLayout;
+               CircleSurface _circleSurface;
 
-        protected override void OnElementChanged(ElementChangedEventArgs<CircleSurfaceView> e)
-        {
-            if (Control == null)
-            {
-                var box = new Box(XForms.NativeParent);
-                box.SetLayoutCallback(OnLayout);
-                _surfaceLayout = new ELayout(box);
-                _circleSurface = new CircleSurface(_surfaceLayout);
-                _circleSurfaceItems = new Dictionary<ICircleSurfaceItem, ICircleWidget>();
-                box.PackEnd(_surfaceLayout);
-                _surfaceLayout.Show();
-                SetNativeControl(box);
-            }
+               protected override void OnElementChanged(ElementChangedEventArgs<CircleSurfaceView> e)
+               {
+                       if (Control == null)
+                       {
+                               var box = new Box(XForms.NativeParent);
+                               box.SetLayoutCallback(OnLayout);
+                               _surfaceLayout = new ELayout(box);
+                               _circleSurface = new CircleSurface(_surfaceLayout);
+                               _circleSurfaceItems = new Dictionary<ICircleSurfaceItem, ICircleWidget>();
+                               box.PackEnd(_surfaceLayout);
+                               _surfaceLayout.Show();
+                               SetNativeControl(box);
+                       }
 
-            if (e.NewElement != null)
-            {
-                var items = e.NewElement.CircleSurfaceItems as ObservableCollection<ICircleSurfaceItem>;
-                items.CollectionChanged += OnCircleSurfaceItemsChanged;
-                foreach (var item in items)
-                {
-                    AddCircleSurfaceItem(item);
-                }
-            }
+                       if (e.NewElement != null)
+                       {
+                               e.NewElement.CircleSurface = _circleSurface;
+                               var items = e.NewElement.CircleSurfaceItems as ObservableCollection<ICircleSurfaceItem>;
+                               items.CollectionChanged += OnCircleSurfaceItemsChanged;
+                               foreach (var item in items)
+                               {
+                                       AddCircleSurfaceItem(item);
+                               }
+                       }
 
-            if (e.OldElement != null)
-            {
-                var items = e.OldElement.CircleSurfaceItems as ObservableCollection<ICircleSurfaceItem>;
-                foreach (var item in items)
-                {
-                    RemoveCircleSurfaceItem(item);
-                }
-                items.CollectionChanged -= OnCircleSurfaceItemsChanged;
-            }
+                       if (e.OldElement != null)
+                       {
+                               var items = e.OldElement.CircleSurfaceItems as ObservableCollection<ICircleSurfaceItem>;
+                               foreach (var item in items)
+                               {
+                                       RemoveCircleSurfaceItem(item);
+                               }
+                               items.CollectionChanged -= OnCircleSurfaceItemsChanged;
+                       }
 
-            base.OnElementChanged(e);
-        }
+                       base.OnElementChanged(e);
+               }
 
-        protected override void Dispose(bool disposing)
-        {
-            if (Element != null)
-            {
-                var items = Element.CircleSurfaceItems as ObservableCollection<ICircleSurfaceItem>;
-                foreach (var item in items)
-                {
-                    RemoveCircleSurfaceItem(item);
-                }
-                items.CollectionChanged -= OnCircleSurfaceItemsChanged;
-            }
-            base.Dispose(disposing);
-        }
+               protected override void Dispose(bool disposing)
+               {
+                       if (Element != null)
+                       {
+                               var items = Element.CircleSurfaceItems as ObservableCollection<ICircleSurfaceItem>;
+                               foreach (var item in items)
+                               {
+                                       RemoveCircleSurfaceItem(item);
+                               }
+                               items.CollectionChanged -= OnCircleSurfaceItemsChanged;
+                       }
+                       base.Dispose(disposing);
+               }
 
-        void OnLayout()
-        {
-            var rect = Control.Geometry;
-            Element.Layout(rect.ToDP());
-            _surfaceLayout.Geometry = rect;
-        }
+               void OnLayout()
+               {
+                       var rect = Control.Geometry;
+                       Element.Layout(rect.ToDP());
+                       _surfaceLayout.Geometry = rect;
+               }
 
-        void OnCircleSurfaceItemsChanged(object sender, NotifyCollectionChangedEventArgs e)
-        {
-            if (e.Action == NotifyCollectionChangedAction.Add ||
-                e.Action == NotifyCollectionChangedAction.Replace)
-            {
-                foreach (ICircleSurfaceItem item in e.NewItems)
-                    AddCircleSurfaceItem(item);
-            }
-            if (e.Action == NotifyCollectionChangedAction.Remove ||
-                e.Action == NotifyCollectionChangedAction.Replace)
-            {
-                foreach (ICircleSurfaceItem item in e.OldItems)
-                    RemoveCircleSurfaceItem(item);
-            }
-        }
+               void OnCircleSurfaceItemsChanged(object sender, NotifyCollectionChangedEventArgs e)
+               {
+                       if (e.Action == NotifyCollectionChangedAction.Add ||
+                               e.Action == NotifyCollectionChangedAction.Replace)
+                       {
+                               foreach (ICircleSurfaceItem item in e.NewItems)
+                                       AddCircleSurfaceItem(item);
+                       }
+                       if (e.Action == NotifyCollectionChangedAction.Remove ||
+                               e.Action == NotifyCollectionChangedAction.Replace)
+                       {
+                               foreach (ICircleSurfaceItem item in e.OldItems)
+                                       RemoveCircleSurfaceItem(item);
+                       }
+               }
 
-        void AddCircleSurfaceItem(ICircleSurfaceItem item)
-        {
-            if (item is CircleProgressBarSurfaceItem)
-            {
-                var widget = new CircleProgressBarSurfaceItemImplements(item as CircleProgressBarSurfaceItem, _surfaceLayout, _circleSurface);
-                _circleSurfaceItems[item] = widget;
-            }
-            else if (item is CircleSliderSurfaceItem)
-            {
-                var widget = new CircleSliderSurfaceItemImplements(item as CircleSliderSurfaceItem, _surfaceLayout, _circleSurface);
-                _circleSurfaceItems[item] = widget;
-            }
-        }
+               void AddCircleSurfaceItem(ICircleSurfaceItem item)
+               {
+                       if (item is CircleProgressBarSurfaceItem)
+                       {
+                               var widget = new CircleProgressBarSurfaceItemImplements(item as CircleProgressBarSurfaceItem, _surfaceLayout, _circleSurface);
+                               _circleSurfaceItems[item] = widget;
+                       }
+                       else if (item is CircleSliderSurfaceItem)
+                       {
+                               var widget = new CircleSliderSurfaceItemImplements(item as CircleSliderSurfaceItem, _surfaceLayout, _circleSurface);
+                               _circleSurfaceItems[item] = widget;
+                       }
+               }
 
-        void RemoveCircleSurfaceItem(ICircleSurfaceItem item)
-        {
-            if (_circleSurfaceItems.TryGetValue(item, out var widget))
-            {
-                EvasObject obj = widget as EvasObject;
-                obj?.Unrealize();
-                _circleSurfaceItems.Remove(item);
-            }
-        }
-    }
-}
+               void RemoveCircleSurfaceItem(ICircleSurfaceItem item)
+               {
+                       if (_circleSurfaceItems.TryGetValue(item, out var widget))
+                       {
+                               EvasObject obj = widget as EvasObject;
+                               obj?.Unrealize();
+                               _circleSurfaceItems.Remove(item);
+                       }
+               }
+       }
+}
\ No newline at end of file
index 238acb3..b1b262b 100644 (file)
  * limitations under the License.
  */
 
-using System;
+using ElmSharp.Wearable;
 using Xamarin.Forms;
 using Xamarin.Forms.Platform.Tizen;
+using XForms = Xamarin.Forms.Forms;
 
 namespace Tizen.Wearable.CircularUI.Forms.Renderer
 {
        static class CircleWidgetRendererExtension
        {
-               internal static ElmSharp.Wearable.CircleSurface GetSurface(this IVisualElementRenderer renderer)
+               internal static CircleSurface GetSurface(this IVisualElementRenderer renderer)
                {
-                       if (null != renderer.Element)
+                       Element element = renderer.Element;
+                       CircleSurface circleSurface = null;
+                       if (element is ICircleSurfaceConsumer circleElement)
+                       {
+                               var provider = circleElement.CircleSurfaceProvider;
+                               if (provider != null)
+                               {
+                                       circleSurface = (CircleSurface)provider.CircleSurface;
+                               }
+                               else
+                               {
+                                       circleSurface = GetSurfaceRecursively(element);
+                               }
+                       }
+                       return circleSurface??XForms.CircleSurface;
+               }
+
+               internal static CircleSurface GetSurfaceRecursively(Element element)
                        {
-                               Element element = renderer.Element;
                                while (element != null)
                                {
                                        if (element is CirclePage)
@@ -38,14 +55,12 @@ namespace Tizen.Wearable.CircularUI.Forms.Renderer
                                        {
                                                if (effect is TizenCircleSurfaceEffect)
                                                {
-                                                       return CircleSurfaceEffectBehavior.GetSurface(element) as ElmSharp.Wearable.CircleSurface;
+                                               return CircleSurfaceEffectBehavior.GetSurface(element) as CircleSurface;
                                                }
                                        }
-
                                        element = element.Parent;
                                }
-                       }
-                       throw new CircleSurfaceNotFoundException();
+                       return null;
                }
        }
 }
\ No newline at end of file
index 44b0155..f40c4c9 100644 (file)
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-using ElmSharp;
+using System;
 using System.Diagnostics;
 using Tizen.Applications;
+using Tizen.Wearable.CircularUI.Forms.Renderer;
 
-namespace Tizen.Wearable.CircularUI.Forms.Renderer
+namespace Tizen.Wearable.CircularUI.Forms
 {
        public class InitOptions
        {
@@ -51,7 +52,6 @@ namespace Tizen.Wearable.CircularUI.Forms.Renderer
 
                public static void Init(string apiKey)
                {
-
                        if (!string.IsNullOrEmpty(apiKey))
                        {
                                GoogleMaps.Init(apiKey);
@@ -60,7 +60,73 @@ namespace Tizen.Wearable.CircularUI.Forms.Renderer
                        {
                                Debug.Assert(!string.IsNullOrEmpty(apiKey), "apiKey is null or empty!");
                        }
+                       Init();
+               }
 
+               public static void Init(CoreApplication context)
+               {
+                       var resPath = context?.DirectoryInfo?.Resource;
+                       if (!string.IsNullOrEmpty(resPath))
+                               ThemeLoader.Initialize(resPath);
+
+                       Init();
+               }
+
+               public static void Init(InitOptions options)
+               {
+                       var resPath = options.Context?.DirectoryInfo?.Resource;
+                       if (!string.IsNullOrEmpty(resPath))
+                               ThemeLoader.Initialize(resPath);
+
+                       Init(options.GoogleMapsAPIKey);
+               }
+       }
+}
+
+namespace Tizen.Wearable.CircularUI.Forms.Renderer
+{
+       [Obsolete("This class is obsolete as of version 1.5.0. Please use Tizen.Wearable.CircularUI.Forms.InitOptions instead.")]
+       public class InitOptions
+       {
+               public CoreApplication Context { get; set; }
+
+               public string GoogleMapsAPIKey { get; set; }
+
+               public InitOptions(CoreApplication application)
+               {
+                       Context = application;
+               }
+
+               public InitOptions(CoreApplication application, string googleMapsAPIKey)
+               {
+                       Context = application;
+                       GoogleMapsAPIKey = googleMapsAPIKey;
+               }
+       }
+
+       [Obsolete("This class is obsolete as of version 1.5.0. Please use Tizen.Wearable.CircularUI.Forms.FormsCircularUI instead.")]
+       public static class FormsCircularUI
+       {
+               public static readonly string Tag = "CircularUI";
+
+               public static bool IsInitialized { get; private set; }
+
+               public static void Init()
+               {
+                       if (IsInitialized) return;
+                       IsInitialized = true;
+               }
+
+               public static void Init(string apiKey)
+               {
+                       if (!string.IsNullOrEmpty(apiKey))
+                       {
+                               GoogleMaps.Init(apiKey);
+                       }
+                       else
+                       {
+                               Debug.Assert(!string.IsNullOrEmpty(apiKey), "apiKey is null or empty!");
+                       }
                        Init();
                }
 
index 059ff33..fb6dcf3 100644 (file)
@@ -42,7 +42,7 @@ namespace Tizen.Wearable.CircularUI.Forms
                        DependencyService.Register<IInformationPopup, InformationPopupImplementation>();
                        DependencyService.Register<ITwoButtonPopup, TwoButtonPopupImplementation>();
                        DependencyService.Register<IToast, ToastImplementation>();
-                       DependencyService.Register<IPlatformMediaPlayer, MediaPlayerImpl>();
+                       DependencyService.Register<IPlatformMediaPlayer2, MediaPlayerImpl>();
                }
 
        }
index 593e7fd..d3bff08 100644 (file)
@@ -30,57 +30,57 @@ using System.ComponentModel;
 
 namespace Tizen.Wearable.CircularUI.Forms.Renderer
 {
-    public class ContentButtonRenderer : LayoutRenderer
-    {
-        EButton _button;
+       public class ContentButtonRenderer : LayoutRenderer
+       {
+               EButton _button;
 
-        ContentButton Button => Element as ContentButton;
+               ContentButton Button => Element as ContentButton;
 
-        protected override void OnElementChanged(ElementChangedEventArgs<XFLayout> e)
-        {
-            base.OnElementChanged(e);
-            Initialize();
-        }
+               protected override void OnElementChanged(ElementChangedEventArgs<XFLayout> e)
+               {
+                       base.OnElementChanged(e);
+                       Initialize();
+               }
 
-        void Initialize()
-        {
-            if (_button == null)
-            {
-                _button = new EButton(XForms.NativeParent);
-                _button.BackgroundColor = EColor.Transparent;
-                _button.SetPartColor("effect", EColor.Transparent);
-                _button.SetPartColor("effect_pressed", EColor.Transparent);
-                _button.Show();
+               void Initialize()
+               {
+                       if (_button == null)
+                       {
+                               _button = new EButton(XForms.NativeParent);
+                               _button.BackgroundColor = EColor.Transparent;
+                               _button.SetPartColor("effect", EColor.Transparent);
+                               _button.SetPartColor("effect_pressed", EColor.Transparent);
+                               _button.Show();
 
-                _button.Pressed += OnPressed;
-                _button.Released += OnReleased;
-                _button.Clicked += OnClicked;
+                               _button.Pressed += OnPressed;
+                               _button.Released += OnReleased;
+                               _button.Clicked += OnClicked;
 
-                Control.PackEnd(_button);
-            }
-        }
+                               Control.PackEnd(_button);
+                       }
+               }
 
-        protected override void UpdateLayout()
-        {
-            base.UpdateLayout();
+               protected override void UpdateLayout()
+               {
+                       base.UpdateLayout();
 
-            _button.Geometry = Control.Geometry;
-            _button.RaiseTop();
-        }
+                       _button.Geometry = Control.Geometry;
+                       _button.RaiseTop();
+               }
 
-        void OnPressed(object sender, EventArgs args)
-        {
-            Button?.SendPressed();
-        }
+               void OnPressed(object sender, EventArgs args)
+               {
+                       Button?.SendPressed();
+               }
 
-        void OnReleased(object sender, EventArgs args)
-        {
-            Button?.SendReleased();
-        }
+               void OnReleased(object sender, EventArgs args)
+               {
+                       Button?.SendReleased();
+               }
 
-        void OnClicked(object sender, EventArgs args)
-        {
-            Button?.SendClicked();
-        }
-    }
-}
+               void OnClicked(object sender, EventArgs args)
+               {
+                       Button?.SendClicked();
+               }
+       }
+}
\ No newline at end of file
diff --git a/src/XSF/Tizen.Wearable.CircularUI.Forms.Renderer/FlatViewCellRenderer.cs b/src/XSF/Tizen.Wearable.CircularUI.Forms.Renderer/FlatViewCellRenderer.cs
new file mode 100644 (file)
index 0000000..0771a25
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://floralicense.org/license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using Tizen.Wearable.CircularUI.Forms;
+using Tizen.Wearable.CircularUI.Forms.Renderer;
+using Xamarin.Forms;
+using Xamarin.Forms.Platform.Tizen;
+
+[assembly: ExportRenderer(typeof(FlatViewCell), typeof(FlatViewCellRenderer))]
+namespace Tizen.Wearable.CircularUI.Forms.Renderer
+{
+       public class FlatViewCellRenderer : ViewCellRenderer
+       {
+               public FlatViewCellRenderer()
+               {
+                       Style = "full_effect_off";
+               }
+       }
+}
\ No newline at end of file
index 255ae91..29c7023 100644 (file)
  */
 
 using System;
-using System.Collections.ObjectModel;
+using SCObjectModel = System.Collections.ObjectModel;
 using System.Collections.Specialized;
 using System.Text;
 using Tizen.Wearable.CircularUI.Forms;
 using Tizen.Wearable.CircularUI.Forms.Renderer;
 using Xamarin.Forms;
 using Xamarin.Forms.Platform.Tizen;
-using TNative = Xamarin.Forms.Platform.Tizen.Native;
+using Xamarin.Forms.Platform.Tizen.Native;
 using TChromium = Tizen.WebView.Chromium;
 using TWebView = Tizen.WebView.WebView;
 using XForms = Xamarin.Forms.Forms;
+using Circular = Tizen.Wearable.CircularUI.Forms.FormsCircularUI;
 
 [assembly: ExportRenderer(typeof(GoogleMapView), typeof(GoogleMapViewRenderer))]
 
 namespace Tizen.Wearable.CircularUI.Forms.Renderer
 {
-       public class GoogleMapViewRenderer : ViewRenderer<GoogleMapView, TNative.WebViewContainer>
+       public class GoogleMapViewRenderer : ViewRenderer<GoogleMapView, WebViewContainer>
        {
                private const string HtmlStyle = "<html><head><style>html, body {height: 100%; margin: 0; padding: 0;} #map {height: 100%;}</style>";
                private const string GoogleMapURL = "http://maps.googleapis.com/maps/api/";
@@ -50,7 +51,7 @@ namespace Tizen.Wearable.CircularUI.Forms.Renderer
                        {
                                TChromium.Initialize();
                                XForms.Context.Terminated += (sender, arg) => TChromium.Shutdown();
-                               SetNativeControl(new TNative.WebViewContainer(XForms.NativeParent));
+                               SetNativeControl(new WebViewContainer(XForms.NativeParent));
                                NativeWebView.LoadStarted += OnLoadStarted;
                                NativeWebView.LoadFinished += OnLoadFinished;
                                NativeWebView.LoadError += OnLoadError;
@@ -58,13 +59,13 @@ namespace Tizen.Wearable.CircularUI.Forms.Renderer
 
                        if (e.OldElement != null)
                        {
-                               ((ObservableCollection<Marker>)e.OldElement.Markers).CollectionChanged -= OnCollectionChanged;
+                               ((SCObjectModel.ObservableCollection<Marker>)e.OldElement.Markers).CollectionChanged -= OnCollectionChanged;
                                e.OldElement.LoadMapRequested -= OnLoadMapRequested;
                        }
 
                        if (e.NewElement != null)
                        {
-                               ((ObservableCollection<Marker>)e.NewElement.Markers).CollectionChanged += OnCollectionChanged;
+                               ((SCObjectModel.ObservableCollection<Marker>)e.NewElement.Markers).CollectionChanged += OnCollectionChanged;
                                e.NewElement.LoadMapRequested += OnLoadMapRequested;
                                LoadMap();
                        }
@@ -84,7 +85,7 @@ namespace Tizen.Wearable.CircularUI.Forms.Renderer
                                }
                                if (Element != null)
                                {
-                                       ((ObservableCollection<Marker>)Element.Markers).CollectionChanged -= OnCollectionChanged;
+                                       ((SCObjectModel.ObservableCollection<Marker>)Element.Markers).CollectionChanged -= OnCollectionChanged;
                                        Element.LoadMapRequested -= OnLoadMapRequested;
                                }
                        }
@@ -94,19 +95,19 @@ namespace Tizen.Wearable.CircularUI.Forms.Renderer
                private void OnLoadError(object sender, global::Tizen.WebView.SmartCallbackLoadErrorArgs e)
                {
                        string url = e.Url;
-                       Log.Error(FormsCircularUI.Tag, $"OnLoadError() url:{url}");
+                       Log.Error(Circular.Tag, $"OnLoadError() url:{url}");
                }
 
                private void OnLoadStarted(object sender, EventArgs e)
                {
                        string url = NativeWebView.Url;
-                       Log.Debug(FormsCircularUI.Tag, "OnLoadStarted()");
+                       Log.Debug(Circular.Tag, "OnLoadStarted()");
                }
 
                private void OnLoadFinished(object sender, EventArgs e)
                {
                        string url = NativeWebView.Url;
-                       Log.Debug(FormsCircularUI.Tag, "OnLoadFinished()");
+                       Log.Debug(Circular.Tag, "OnLoadFinished()");
                        NativeWebView.SetFocus(true);
                }
 
@@ -198,7 +199,7 @@ namespace Tizen.Wearable.CircularUI.Forms.Renderer
                        int index = 1;
                        if (_sb == null || _sb.Length == 0) return _sb;
 
-                       Log.Debug(FormsCircularUI.Tag, $"Markers count:{Element.Markers.Count}");
+                       Log.Debug(Circular.Tag, $"Markers count:{Element.Markers.Count}");
                        foreach (var marker in Element.Markers)
                        {
                                _sb.AppendLine();
@@ -224,7 +225,7 @@ namespace Tizen.Wearable.CircularUI.Forms.Renderer
 
                private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
                {
-                       Log.Debug(FormsCircularUI.Tag, $"e.Action:{e.Action}");
+                       Log.Debug(Circular.Tag, $"e.Action:{e.Action}");
                        switch (e.Action)
                        {
                                case NotifyCollectionChangedAction.Add:
@@ -241,7 +242,7 @@ namespace Tizen.Wearable.CircularUI.Forms.Renderer
 
                private void OnLoadMapRequested(object sender, EventArgs eventArgs)
                {
-                       Log.Debug(FormsCircularUI.Tag, $"Load Requested");
+                       Log.Debug(Circular.Tag, $"Load Requested");
                        LoadMap();
                }
        }
index e509ffd..9985603 100644 (file)
@@ -23,6 +23,7 @@ using ElmSharp;
 using Xamarin.Forms;
 using Xamarin.Forms.Platform.Tizen;
 using XForms = Xamarin.Forms.Forms;
+using Circular = Tizen.Wearable.CircularUI.Forms.FormsCircularUI;
 
 [assembly: ExportRenderer(typeof(IndexPage), typeof(IndexPageRenderer))]
 
@@ -157,7 +158,7 @@ namespace Tizen.Wearable.CircularUI.Forms.Renderer
 
                private void OnPageScrolled(object sender, EventArgs e)
                {
-                       Log.Debug(FormsCircularUI.Tag, $"OnPageScrolled()  _pageIndex:{_pageIndex}, HorizontalPageIndex:{_scroller.HorizontalPageIndex}, _isUpdateCarousel:{_isUpdateCarousel}");
+       Log.Debug(Circular.Tag, $"OnPageScrolled()  _pageIndex:{_pageIndex}, HorizontalPageIndex:{_scroller.HorizontalPageIndex}, _isUpdateCarousel:{_isUpdateCarousel}");
                        if (_isUpdateCarousel)
                        {
                                _isUpdateCarousel = false;
index d867e35..df0a943 100644 (file)
@@ -18,6 +18,7 @@ using System;
 using Xamarin.Forms;
 using Xamarin.Forms.Platform.Tizen;
 using XForms = Xamarin.Forms.Forms;
+using Circular = Tizen.Wearable.CircularUI.Forms.FormsCircularUI;
 
 [assembly: Dependency(typeof(Tizen.Wearable.CircularUI.Forms.Renderer.InformationPopupImplementation))]
 
@@ -183,7 +184,7 @@ namespace Tizen.Wearable.CircularUI.Forms.Renderer
                {
                        if (!XForms.IsInitialized)
                        {
-                               Log.Debug(FormsCircularUI.Tag, "Tizen Forms is not initialized");
+                               Log.Debug(Circular.Tag, "Tizen Forms is not initialized");
                                return;
                        }
 
@@ -259,7 +260,7 @@ namespace Tizen.Wearable.CircularUI.Forms.Renderer
 
                                if (_buttonBgColor != Color.Default)
                                {
-                                       Log.Debug(FormsCircularUI.Tag, $"InformationPopup set button background color:{_buttonBgColor.ToNative()}");
+                                       Log.Debug(Circular.Tag, $"InformationPopup set button background color:{_buttonBgColor.ToNative()}");
                                        _bottomButton.BackgroundColor = _buttonBgColor.ToNative();
                                }
                        }
@@ -275,9 +276,9 @@ namespace Tizen.Wearable.CircularUI.Forms.Renderer
                void UpdateTitle()
                {
                        string title = _title?.Replace("&", "&amp;")
-                                                                 .Replace("<", "&lt;")
-                                                                 .Replace(">", "&gt;")
-                                                                 .Replace(Environment.NewLine, "<br>");
+                                                               .Replace("<", "&lt;")
+                                                               .Replace(">", "&gt;")
+                                                               .Replace(Environment.NewLine, "<br>");
 
                        if (!_isProgressRunning)
                        {
@@ -376,7 +377,7 @@ namespace Tizen.Wearable.CircularUI.Forms.Renderer
 
                void OnDismissed(object sender, EventArgs e)
                {
-                       Log.Debug(FormsCircularUI.Tag, $"OnDismissed called");
+                       Log.Debug(Circular.Tag, $"OnDismissed called");
                        Dispose();
                }
        }
index 1ec4e05..9bb1089 100644 (file)
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Threading.Tasks;
-using Tizen.Multimedia;
 using Tizen.Wearable.CircularUI.Forms.Renderer;
 using Xamarin.Forms;
-using Xamarin.Forms.Platform.Tizen;
-using XForms = Xamarin.Forms.Forms;
+using Xamarin.Forms.PlatformConfiguration.TizenSpecific;
+using XMediaPlayerImpl = Xamarin.Forms.Platform.Tizen.Native.MediaPlayerImpl;
+using PlayerState = Xamarin.Forms.PlatformConfiguration.TizenSpecific.PlaybackState;
 
-[assembly: Xamarin.Forms.Dependency(typeof(MediaPlayerImpl))]
+[assembly: Dependency(typeof(MediaPlayerImpl))]
 namespace Tizen.Wearable.CircularUI.Forms.Renderer
 {
-       public class MediaPlayerImpl : IPlatformMediaPlayer
+       //TODO : This class sholud be removed when the related bug in Xamarin.Forms is fixed (#10287).
+       public class EmbeddingControls2 : Xamarin.Forms.Platform.Tizen.Native.EmbeddingControls
        {
-               Player _player;
-
-               bool _cancelToStart;
-               DisplayAspectMode _aspectMode = DisplayAspectMode.AspectFit;
-               Task _taskPrepare;
-               TaskCompletionSource<bool> _tcsForStreamInfo;
-               IVideoOutput _videoOutput;
-               MediaSource _source;
-
-               public MediaPlayerImpl()
-               {
-                       _player = new Player();
-                       _player.PlaybackCompleted += OnPlaybackCompleted;
-                       _player.BufferingProgressChanged += OnBufferingProgressChanged;
-               }
-
-               public bool UsesEmbeddingControls
-               {
-                       get; set;
-               }
-
-               public bool AutoPlay { get; set; }
-
-               public bool AutoStop { get; set; }
-
-               public double Volume
+               public EmbeddingControls2() : base()
                {
-                       get => _player.Volume;
-                       set => _player.Volume = (float)value;
+                       PlayImage.Clicked += OnImageButtonClicked;
+                       PauseImage.Clicked += OnImageButtonClicked;
                }
 
-               public int Duration => _player.StreamInfo.GetDuration();
-
-               public bool IsMuted
+               async void OnImageButtonClicked(object sender, EventArgs e)
                {
-                       get => _player.Muted;
-                       set => _player.Muted = value;
-               }
-
-               public int Position
-               {
-                       get
-                       {
-                               if (_player.State == PlayerState.Idle || _player.State == PlayerState.Preparing)
-                                       return 0;
-                               return _player.GetPlayPosition();
-                       }
-               }
-
-               public DisplayAspectMode AspectMode
-               {
-                       get { return _aspectMode; }
-                       set
-                       {
-                               _aspectMode = value;
-                               ApplyAspectMode();
-                       }
-               }
-
-               bool HasSource => _source != null;
-
-               IVideoOutput VideoOutput
-               {
-                       get { return _videoOutput; }
-                       set
-                       {
-                               if (TargetView != null)
-                                       TargetView.PropertyChanged -= OnTargetViewPropertyChanged;
-
-                               _videoOutput = value;
-
-                               if (TargetView != null)
-                                       TargetView.PropertyChanged += OnTargetViewPropertyChanged;
-                       }
-               }
-
-               VisualElement TargetView => VideoOutput?.MediaView;
-
-               Task TaskPrepare
-               {
-                       get => _taskPrepare ?? Task.CompletedTask;
-                       set => _taskPrepare = value;
-               }
-
-               public event EventHandler UpdateStreamInfo;
-               public event EventHandler PlaybackCompleted;
-               public event EventHandler PlaybackStarted;
-               public event EventHandler<BufferingProgressUpdatedEventArgs> BufferingProgressUpdated;
-               public event EventHandler PlaybackStopped;
-               public event EventHandler PlaybackPaused;
-
-
-               public async Task<bool> Start()
-               {
-                       Log.Debug(FormsCircularUI.Tag, "Start");
-
-                       _cancelToStart = false;
-                       if (!HasSource)
-                               return false;
-
-                       if (_player.State == PlayerState.Idle)
-                       {
-                               await Prepare();
-                       }
-
-                       if (_cancelToStart)
-                               return false;
-
-                       try
-                       {
-                               _player.Start();
-                       }
-                       catch (Exception e)
-                       {
-                               Log.Error(FormsCircularUI.Tag, $"Error On Start : {e.Message}");
-                               return false;
-                       }
-                       PlaybackStarted?.Invoke(this, EventArgs.Empty);
-                       return true;
-               }
-
-               public void Pause()
-               {
-                       Log.Debug(FormsCircularUI.Tag, "Pause");
-
-                       try
-                       {
-                               _player.Pause();
-                               PlaybackPaused.Invoke(this, EventArgs.Empty);
-                       }
-                       catch (Exception e)
-                       {
-                               Log.Error(FormsCircularUI.Tag, $"Error on Pause : {e.Message}");
-                       }
-               }
-
-               public void Stop()
-               {
-                       Log.Debug(FormsCircularUI.Tag, "Stop");
-
-                       _cancelToStart = true;
-                       var unusedTask = ChangeToIdleState();
-                       PlaybackStopped.Invoke(this, EventArgs.Empty);
-               }
-
-               public void SetDisplay(IVideoOutput output)
-               {
-                       VideoOutput = output;
-               }
-
-               public async Task<int> Seek(int ms)
-               {
-                       try
-                       {
-                               await _player.SetPlayPositionAsync(ms, false);
-                       }
-                       catch (Exception e)
+                       if (BindingContext is MediaPlayer player)
                        {
-                               Log.Error(FormsCircularUI.Tag, $"Fail to seek : {e.Message}");
-                       }
-                       return Position;
-               }
-
-               public void SetSource(MediaSource source)
-               {
-                       _source = source;
-               }
-
-               public async Task<Stream> GetAlbumArts()
-               {
-                       if (_player.State == PlayerState.Idle)
-                       {
-                               if (_tcsForStreamInfo == null || _tcsForStreamInfo.Task.IsCompleted)
-                               {
-                                       _tcsForStreamInfo = new TaskCompletionSource<bool>();
-                               }
-                               await _tcsForStreamInfo.Task;
-                       }
-                       await TaskPrepare;
-
-                       var imageData = _player.StreamInfo.GetAlbumArt();
-                       if (imageData == null)
-                               return null;
-                       return new MemoryStream(imageData);
-               }
-
-               public async Task<IDictionary<string, string>> GetMetadata()
-               {
-                       if (_player.State == PlayerState.Idle)
-                       {
-                               if (_tcsForStreamInfo == null || _tcsForStreamInfo.Task.IsCompleted)
-                               {
-                                       _tcsForStreamInfo = new TaskCompletionSource<bool>();
-                               }
-                               await _tcsForStreamInfo.Task;
-                       }
-                       await TaskPrepare;
-
-                       Dictionary<string, string> metadata = new Dictionary<string, string>
-                       {
-                               [nameof(StreamMetadataKey.Album)] = _player.StreamInfo.GetMetadata(StreamMetadataKey.Album),
-                               [nameof(StreamMetadataKey.Artist)] = _player.StreamInfo.GetMetadata(StreamMetadataKey.Artist),
-                               [nameof(StreamMetadataKey.Author)] = _player.StreamInfo.GetMetadata(StreamMetadataKey.Author),
-                               [nameof(StreamMetadataKey.Genre)] = _player.StreamInfo.GetMetadata(StreamMetadataKey.Genre),
-                               [nameof(StreamMetadataKey.Title)] = _player.StreamInfo.GetMetadata(StreamMetadataKey.Title),
-                               [nameof(StreamMetadataKey.Year)] = _player.StreamInfo.GetMetadata(StreamMetadataKey.Year)
-                       };
-                       return metadata;
-               }
-
-               void ApplyDisplay()
-               {
-                       if (VideoOutput == null)
-                       {
-                               _player.Display = null;
-                       }
-                       else
-                       {
-                               var renderer = Platform.GetRenderer(TargetView);
-                               if (renderer is IMediaViewProvider provider && provider.GetMediaView() != null)
-                               {
-                                       try
-                                       {
-                                               Display display = new Display(provider.GetMediaView());
-                                               _player.Display = display;
-                                               _player.DisplaySettings.Mode = _aspectMode.ToMultimeida();
-                                       }
-                                       catch
-                                       {
-                                               Log.Error(FormsCircularUI.Tag, "Error on MediaView");
-                                       }
-                               }
-                       }
-               }
-
-               async Task ApplySource()
-               {
-                       if (_source == null)
-                       {
-                               return;
-                       }
-                       IMediaSourceHandler handler = XForms.GetHandlerForObject<IMediaSourceHandler>(_source);
-                       await handler.SetSource(_player, _source);
-               }
-
-               async void OnTargetViewPropertyChanged(object sender, global::System.ComponentModel.PropertyChangedEventArgs e)
-               {
-                       if (e.PropertyName == "Renderer")
-                       {
-                               if (Platform.GetRenderer(sender as BindableObject) != null && HasSource && AutoPlay)
+                               if (player.State == PlayerState.Playing)
                                {
-                                       await Start();
+                                       player.Pause();
                                }
-                               else if (Platform.GetRenderer(sender as BindableObject) == null && AutoStop)
+                               else
                                {
-                                       Stop();
+                                       await player.Start();
                                }
                        }
                }
-
-               async Task Prepare()
-               {
-                       TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
-                       var prevTask = TaskPrepare;
-                       TaskPrepare = tcs.Task;
-                       await prevTask;
-
-                       if (_player.State == PlayerState.Ready)
-                               return;
-
-                       ApplyDisplay();
-                       await ApplySource();
-
-                       try
-                       {
-                               await _player.PrepareAsync();
-                               UpdateStreamInfo?.Invoke(this, EventArgs.Empty);
-                               _tcsForStreamInfo?.TrySetResult(true);
-                       }
-                       catch (Exception e)
-                       {
-                               Log.Error(FormsCircularUI.Tag, $"Error on prepare : {e.Message}");
-                       }
-                       tcs.SetResult(true);
-               }
-
-               async void ApplyAspectMode()
-               {
-                       if (_player.State == PlayerState.Preparing)
-                       {
-                               await TaskPrepare;
-                       }
-                       _player.DisplaySettings.Mode = AspectMode.ToMultimeida();
-               }
-
-               void OnBufferingProgressChanged(object sender, BufferingProgressChangedEventArgs e)
-               {
-                       BufferingProgressUpdated?.Invoke(this, new BufferingProgressUpdatedEventArgs { Progress = e.Percent / 100.0 });
-               }
-
-               void OnPlaybackCompleted(object sender, EventArgs e)
-               {
-                       PlaybackCompleted?.Invoke(this, EventArgs.Empty);
-               }
-
-               async Task ChangeToIdleState()
-               {
-                       switch (_player.State)
-                       {
-                               case PlayerState.Playing:
-                               case PlayerState.Paused:
-                                       _player.Stop();
-                                       _player.Unprepare();
-                                       break;
-                               case PlayerState.Ready:
-                                       _player.Unprepare();
-                                       break;
-                               case PlayerState.Preparing:
-                                       await TaskPrepare;
-                                       _player.Unprepare();
-                                       break;
-                       }
-               }
        }
 
-       public static class MultimediaConvertExtensions
+       //TODO : This class sholud be removed when the related bug in Xamarin.Forms is fixed (#10287).
+       public class MediaPlayerImpl : XMediaPlayerImpl, IPlatformMediaPlayer2
        {
-               public static Multimedia.Rectangle ToMultimedia(this ElmSharp.Rect rect)
+               public MediaPlayerImpl() : base()
                {
-                       return new Multimedia.Rectangle(rect.X, rect.Y, rect.Width, rect.Height);
                }
 
-               public static PlayerDisplayMode ToMultimeida(this DisplayAspectMode mode)
+               public new View GetEmbeddingControlView(IMediaPlayer player)
                {
-                       PlayerDisplayMode ret = PlayerDisplayMode.LetterBox;
-                       switch (mode)
+                       return new EmbeddingControls2
                        {
-                               case DisplayAspectMode.AspectFill:
-                                       ret = PlayerDisplayMode.CroppedFull;
-                                       break;
-                               case DisplayAspectMode.AspectFit:
-                                       ret = PlayerDisplayMode.LetterBox;
-                                       break;
-                               case DisplayAspectMode.Fill:
-                                       ret = PlayerDisplayMode.FullScreen;
-                                       break;
-                               case DisplayAspectMode.OrignalSize:
-                                       ret = PlayerDisplayMode.OriginalOrFull;
-                                       break;
-                       }
-                       return ret;
+                               BindingContext = player
+                       };
                }
        }
 }
\ No newline at end of file
diff --git a/src/XSF/Tizen.Wearable.CircularUI.Forms.Renderer/MediaSourceHandler.cs b/src/XSF/Tizen.Wearable.CircularUI.Forms.Renderer/MediaSourceHandler.cs
deleted file mode 100644 (file)
index 256b641..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved
- *
- * Licensed under the Flora License, Version 1.1 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://floralicense.org/license/
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-using System.Threading.Tasks;
-using Tizen.Multimedia;
-using CircularUI = Tizen.Wearable.CircularUI.Forms;
-using Tizen.Wearable.CircularUI.Forms;
-using Tizen.Wearable.CircularUI.Forms.Renderer;
-using Xamarin.Forms;
-using Xamarin.Forms.Platform.Tizen;
-
-[assembly: ExportHandler(typeof(CircularUI.UriMediaSource), typeof(UriMediaSourceHandler))]
-[assembly: ExportHandler(typeof(CircularUI.FileMediaSource), typeof(FileMediaSourceHandler))]
-namespace Tizen.Wearable.CircularUI.Forms.Renderer
-{
-
-       public interface IMediaSourceHandler : IRegisterable
-       {
-               Task<bool> SetSource(Player player, MediaSource imageSource);
-       }
-
-       public sealed class UriMediaSourceHandler : IMediaSourceHandler
-       {
-               public Task<bool> SetSource(Player player, MediaSource source)
-               {
-                       if (source is UriMediaSource uriSource)
-                       {
-                               Log.Info(FormsCircularUI.Tag, $"Set UriMediaSource");
-                               var uri = uriSource.Uri;
-                               player.SetSource(new MediaUriSource(uri.IsFile ? uri.LocalPath : uri.AbsoluteUri));
-                       }
-                       return Task.FromResult<bool>(true);
-               }
-       }
-
-       public sealed class FileMediaSourceHandler : IMediaSourceHandler
-       {
-               public Task<bool> SetSource(Player player, MediaSource source)
-               {
-                       if (source is FileMediaSource fileSource)
-                       {
-                               Log.Info(FormsCircularUI.Tag, $"Set FileMediaSource");
-                               player.SetSource(new MediaUriSource(ResourcePath.GetPath(fileSource.File)));
-                       }
-                       return Task.FromResult<bool>(true);
-               }
-       }
-}
\ No newline at end of file
index ba681de..ef0bc84 100644 (file)
@@ -24,6 +24,7 @@ using Xamarin.Forms.Platform.Tizen;
 using Xamarin.Forms.Platform.Tizen.Native;
 using XForms = Xamarin.Forms.Forms;
 using XToolbarItem = Xamarin.Forms.ToolbarItem;
+using ERotaryEventManager = ElmSharp.Wearable.RotaryEventManager;
 
 namespace Tizen.Wearable.CircularUI.Forms.Renderer
 {
@@ -155,7 +156,7 @@ namespace Tizen.Wearable.CircularUI.Forms.Renderer
                {
                        if (_currentRotaryFocusObject is IRotaryEventReceiver)
                        {
-                               RotaryEventManager.Rotated += OnRotaryEventChanged;
+                               ERotaryEventManager.Rotated += OnRotaryEventChanged;
                        }
                        else if (_currentRotaryFocusObject is IRotaryFocusable)
                        {
@@ -167,7 +168,7 @@ namespace Tizen.Wearable.CircularUI.Forms.Renderer
                {
                        if (_currentRotaryFocusObject is IRotaryEventReceiver)
                        {
-                               RotaryEventManager.Rotated -= OnRotaryEventChanged;
+                               ERotaryEventManager.Rotated -= OnRotaryEventChanged;
                        }
                        else if (_currentRotaryFocusObject is IRotaryFocusable)
                        {
diff --git a/src/XSF/Tizen.Wearable.CircularUI.Forms.Renderer/RotaryService.cs b/src/XSF/Tizen.Wearable.CircularUI.Forms.Renderer/RotaryService.cs
new file mode 100644 (file)
index 0000000..f637268
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://floralicense.org/license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using Xamarin.Forms;
+using ERotaryEventManager = ElmSharp.Wearable.RotaryEventManager;
+using ERotaryEventArgs = ElmSharp.Wearable.RotaryEventArgs;
+
+[assembly: Dependency(typeof(Tizen.Wearable.CircularUI.Forms.Renderer.RotaryService))]
+namespace Tizen.Wearable.CircularUI.Forms.Renderer
+{
+       public class RotaryService : IRotaryService
+       {
+               EventHandler<RotaryEventArgs> _rotated;
+
+               public event EventHandler<RotaryEventArgs> Rotated
+               {
+                       add
+                       {
+                               if (_rotated == null)
+                               {
+                                       ERotaryEventManager.Rotated += OnRotaryChanged;
+                               }
+                               _rotated += value;
+                       }
+                       remove
+                       {
+                               _rotated -= value;
+                               if (_rotated == null)
+                               {
+                                       ERotaryEventManager.Rotated -= OnRotaryChanged;
+                               }
+                       }
+               }
+
+               void OnRotaryChanged(ERotaryEventArgs args)
+               {
+                       _rotated?.Invoke(this, new RotaryEventArgs() { IsClockwise = args.IsClockwise });
+               }
+       }
+}
\ No newline at end of file
index bdeaa59..aeffc75 100644 (file)
@@ -4,9 +4,9 @@ using Xamarin.Forms;
 
 namespace Tizen.Wearable.CircularUI.Forms.Renderer
 {
-    public interface IShellItemRenderer : IDisposable
-    {
-        BaseShellItem Item { get; }
-        EvasObject NativeView { get; }
-    }
-}
+       public interface IShellItemRenderer : IDisposable
+       {
+               BaseShellItem Item { get; }
+               EvasObject NativeView { get; }
+       }
+}
\ No newline at end of file
index 3e0cb46..342021d 100644 (file)
@@ -10,533 +10,534 @@ using EImage = ElmSharp.Image;
 using ELayout = ElmSharp.Layout;
 using EWidget = ElmSharp.Widget;
 using EButton = ElmSharp.Button;
+using ERotaryEventManager = ElmSharp.Wearable.RotaryEventManager;
 
 namespace Tizen.Wearable.CircularUI.Forms.Renderer
 {
-    public class NavigationDrawer : ELayout, IAnimatable
-    {
-        static readonly int TouchWidth = 50;
-        static readonly int IconSize = 40;
-        static readonly string DefaultIcon = "Tizen.Wearable.CircularUI.Forms.Renderer.res.wc_visual_cue.png";
-
-        Box _mainLayout;
-        Box _contentGestureBox;
-        Box _contentBox;
-        Box _drawerBox;
-        Box _drawerContentBox;
-        Box _drawerIconBox;
-
-        EvasObject _content;
-        EvasObject _drawerContent;
-
-        EImage _drawerIcon;
-        EButton _touchArea;
-
-        GestureLayer _gestureOnContent;
-        GestureLayer _gestureOnDrawer;
-
-        ImageSource _drawerIconSource;
-
-        bool _isOpen;
-        bool _isDefaultIcon;
-
-        CancellationTokenSource _fadeInCancelTokenSource = null;
-
-        bool HasDrawer => _drawerBox != null;
-
-        public NavigationDrawer(EvasObject parent) : base(parent)
-        {
-            Initialize();
-        }
-
-        public int HandlerHeight { get; set; } = 40;
-
-        public bool IsOpen
-        {
-            get
-            {
-                return _isOpen;
-            }
-            set
-            {
-                if (_isOpen != value)
-                {
-                    if (value)
-                    {
-                        Open();
-                    }
-                    else
-                    {
-                        Close();
-                    }
-                }
-            }
-        }
-
-        EColor _handlerBackgroundColor = EColor.Transparent;
-        public EColor HandlerBackgroundColor
-        {
-            get => _handlerBackgroundColor;
-            set
-            {
-                _handlerBackgroundColor = value;
-                UpdateHandlerBackgroundColor();
-            }
-        }
-
-        public event EventHandler Toggled;
-
-        public void SetMainContent(EvasObject content)
-        {
-            if (content == null)
-            {
-                UnsetMainContent();
-                return;
-            }
-
-            _content = content;
-            _content.Show();
-            _contentBox.PackEnd(_content);
-            _content.Geometry = _contentBox.Geometry;
-        }
-
-        public void SetDrawerContent(EvasObject content)
-        {
-            InitializeDrawerBox();
-
-            if (content == null)
-            {
-                UnsetDrawerContent();
-                return;
-            }
-
-            _drawerContent = content;
-            _drawerContent.Show();
-            _drawerContentBox.PackEnd(_drawerContent);
-
-            _drawerContentBox.Show();
-            _drawerIconBox.Show();
-
-            if (_drawerContent is NavigationView nv)
-            {
-                nv.Dragged += (s, e) =>
-                {
-                    if (e.State == DraggedState.EdgeTop)
-                    {
-                        Close();
-                    }
-                };
-            }
-        }
-
-        public void UpdateDrawerIcon(ImageSource source)
-        {
-            _drawerIconSource = source;
-            if (HasDrawer)
-            {
-                SetDrawerIcon(_drawerIconSource);
-            }
-        }
-
-        public async void Open(uint length = 300)
-        {
-            if (!HasDrawer)
-                return;
-
-            var toMove = _drawerBox.Geometry;
-            toMove.Y = 0;
-
-            await RunMoveAnimation(_drawerBox, toMove, length);
-
-            if (!_isOpen)
-            {
-                _isOpen = true;
-                Toggled?.Invoke(this, EventArgs.Empty);
-            }
-            OnLayout();
-            OnDrawerLayout();
-            FlipIcon();
-        }
-
-        public async void Close(uint length = 300)
-        {
-            if (!HasDrawer)
-                return;
-
-            var toMove = _drawerBox.Geometry;
-            toMove.Y = Geometry.Height - HandlerHeight;
-
-            await RunMoveAnimation(_drawerBox, toMove, length);
-
-            if (_isOpen)
-            {
-                _isOpen = false;
-                Toggled?.Invoke(this, EventArgs.Empty);
-            }
-            OnLayout();
-            OnDrawerLayout();
-            ResetIcon();
-            StartHighlightAnimation(_drawerIcon);
-        }
-
-        void IAnimatable.BatchBegin()
-        {
-        }
-
-        void IAnimatable.BatchCommit()
-        {
-        }
-
-        protected override IntPtr CreateHandle(EvasObject parent)
-        {
-            _mainLayout = new Box(parent);
-            return _mainLayout.Handle;
-        }
-
-        void Initialize()
-        {
-            _mainLayout.SetLayoutCallback(OnLayout);
-
-            _contentGestureBox = new Box(_mainLayout);
-            _contentGestureBox.Show();
-            _mainLayout.PackEnd(_contentGestureBox);
-
-            _contentBox = new Box(_mainLayout);
-            _contentBox.SetLayoutCallback(OnContentLayout);
-            _contentBox.Show();
-            _mainLayout.PackEnd(_contentBox);
-        }
-
-        void InitializeDrawerBox()
-        {
-            if (_drawerBox != null)
-                return;
-
-            _drawerBox = new Box(_mainLayout);
-            _drawerBox.SetLayoutCallback(OnDrawerLayout);
-            _drawerBox.Show();
-            _mainLayout.PackEnd(_drawerBox);
-
-            _drawerContentBox = new Box(_drawerBox);
-            _drawerBox.PackEnd(_drawerContentBox);
-
-            _drawerIconBox = new Box(_drawerBox)
-            {
-                BackgroundColor = _handlerBackgroundColor
-            };
-            _drawerBox.PackEnd(_drawerIconBox);
-
-            _drawerIcon = new EImage(_drawerIconBox)
-            {
-                AlignmentY = 0.5,
-                AlignmentX = 0.5,
-                MinimumHeight = IconSize,
-                MinimumWidth = IconSize,
-            };
-            _drawerIcon.Show();
-            _drawerIconBox.PackEnd(_drawerIcon);
-            SetDrawerIcon(_drawerIconSource);
-
-            _touchArea = new EButton(_drawerBox)
-            {
-                Color = EColor.Transparent,
-                BackgroundColor = EColor.Transparent,
-            };
-            _touchArea.SetPartColor("effect", EColor.Transparent);
-            _touchArea.Show();
-            _touchArea.RepeatEvents = true;
-            _touchArea.Clicked += OnIconClicked;
-
-            _drawerBox.PackEnd(_touchArea);
-
-            _gestureOnContent = new GestureLayer(_contentGestureBox);
-            _gestureOnContent.SetMomentumCallback(GestureLayer.GestureState.Start, OnContentDragStarted);
-            _gestureOnContent.SetMomentumCallback(GestureLayer.GestureState.End, OnContentDragEnded);
-            _gestureOnContent.SetMomentumCallback(GestureLayer.GestureState.Abort, OnContentDragEnded);
-            _gestureOnContent.Attach(_contentGestureBox);
-            _contentBox.RepeatEvents = true;
-
-            _gestureOnDrawer = new GestureLayer(_drawerIconBox);
-            _gestureOnDrawer.SetMomentumCallback(GestureLayer.GestureState.Move, OnDrawerDragged);
-            _gestureOnDrawer.SetMomentumCallback(GestureLayer.GestureState.End, OnDrawerDragEnded);
-            _gestureOnDrawer.SetMomentumCallback(GestureLayer.GestureState.Abort, OnDrawerDragEnded);
-            _gestureOnDrawer.Attach(_drawerIconBox);
-
-            RotaryEventManager.Rotated += OnRotateEventReceived;
-        }
-
-        void SetDrawerIcon(ImageSource source)
-        {
-            if (source == null)
-            {
-                _drawerIcon.LoadFromImageSourceAsync(ImageSource.FromResource(DefaultIcon, GetType().Assembly));
-                _isDefaultIcon = true;
-            }
-            else
-            {
-                _isDefaultIcon = false;
-                if (source is FileImageSource fsource)
-                {
-                    _drawerIcon.Load(fsource.ToAbsPath());
-                }
-                else
-                {
-                    _drawerIcon.LoadFromImageSourceAsync(source);
-                }
-            }
-        }
-
-        void UpdateHandlerBackgroundColor()
-        {
-            if (_drawerIconBox != null)
-            {
-                _drawerIconBox.BackgroundColor = _handlerBackgroundColor;
-            }
-        }
-
-        void OnIconClicked(object sender, EventArgs e)
-        {
-            if (IsOpen)
-                Close();
-            else
-                Open();
-        }
-
-        async Task<bool> ShowAsync(EWidget target, Easing easing = null, uint length = 300, CancellationToken cancelltaionToken = default(CancellationToken))
-        {
-            var tcs = new TaskCompletionSource<bool>();
-
-            await Task.Delay(1000);
-
-            if (cancelltaionToken.IsCancellationRequested)
-            {
-                cancelltaionToken.ThrowIfCancellationRequested();
-            }
-
-            target.Show();
-            var opacity = target.Opacity;
-
-            if (opacity == 255 || opacity == -1)
-                return true;
-
-            new Animation((progress) =>
-            {
-                target.Opacity = opacity + (int)((255 - opacity) * progress);
-
-            }).Commit(this, "FadeIn", length: length, finished: (p, e) =>
-            {
-                target.Opacity = 255;
-                tcs.SetResult(true);
-                StartHighlightAnimation(_drawerIcon);
-            });
-
-            return await tcs.Task;
-        }
-
-        void OnLayout()
-        {
-            var bound = Geometry;
-            _contentGestureBox.Geometry = bound;
-            _contentBox.Geometry = bound;
-            if (_drawerBox != null)
-            {
-                bound.Y = _isOpen ? 0 : (bound.Height - HandlerHeight);
-                _drawerBox.Geometry = bound;
-            }
-        }
-
-        void OnContentLayout()
-        {
-            if (_content != null)
-            {
-                _content.Geometry = _contentBox.Geometry;
-            }
-        }
-
-        void OnDrawerLayout()
-        {
-            this.AbortAnimation("HighlightAnimation");
-
-            var bound = _drawerBox.Geometry;
-
-            var currentY = bound.Y;
-            var ratio = currentY / (double)(Geometry.Height - HandlerHeight);
-
-            var contentBound = bound;
-            contentBound.Y += (int)(HandlerHeight * ratio);
-            _drawerContentBox.Geometry = contentBound;
-
-            var drawerHandleBound = bound;
-            drawerHandleBound.Height = HandlerHeight;
-            _drawerIconBox.Geometry = drawerHandleBound;
-
-            var drawerTouchBound = drawerHandleBound;
-            drawerTouchBound.Width = TouchWidth;
-            drawerTouchBound.X = drawerHandleBound.X + (drawerHandleBound.Width - TouchWidth) / 2;
-            _touchArea.Geometry = drawerTouchBound;
-        }
-
-        async Task<bool> HideAsync(EWidget target, Easing easing = null, uint length = 300)
-        {
-            var tcs = new TaskCompletionSource<bool>();
-
-            var opacity = target.Opacity;
-            if (opacity == -1)
-                opacity = 255;
-
-            new Animation((progress) =>
-            {
-                target.Opacity = opacity - (int)(progress * opacity);
-
-            }).Commit(this, "FadeOut", length: length, finished: (p, e) =>
-            {
-                target.Opacity = 0;
-                target.Hide();
-                tcs.SetResult(true);
-            });
-
-            return await tcs.Task;
-        }
-
-        void StartHighlightAnimation(EWidget target)
-        {
-            if (!_isDefaultIcon || this.AnimationIsRunning("HighlightAnimation"))
-                return;
-
-            int count = 2;
-            var bound = target.Geometry;
-            var y = bound.Y;
-            var dy = bound.Y - bound.Height / 3;
-
-            var anim = new Animation();
-
-            var transfAnim = new Animation((f) =>
-            {
-                bound.Y = (int)f;
-                var map = new EvasMap(4);
-                map.PopulatePoints(bound, 0);
-                target.IsMapEnabled = true;
-                target.EvasMap = map;
-            }, y, dy);
-
-            var opacityAnim = new Animation(f => target.Opacity = (int)f, 255, 40);
-
-            anim.Add(0, 1, opacityAnim);
-            anim.Add(0, 1, transfAnim);
-
-            anim.Commit(this, "HighlightAnimation", 16, 800, finished: (f, b) =>
-            {
-                target.Opacity = 255;
-                target.IsMapEnabled = false;
-            }, repeat:() => --count > 0);
-        }
-
-        async void OnRotateEventReceived(EventArgs args)
-        {
-            _fadeInCancelTokenSource?.Cancel();
-            _fadeInCancelTokenSource = new CancellationTokenSource();
-
-            if (!_isOpen)
-            {
-                var token = _fadeInCancelTokenSource.Token;
-                await HideAsync(_drawerBox);
-                _ = ShowAsync(_drawerBox, cancelltaionToken: token);
-            }
-        }
-
-        void OnContentDragStarted(GestureLayer.MomentumData moment)
-        {
-            _fadeInCancelTokenSource?.Cancel();
-            _fadeInCancelTokenSource = null;
-
-            if (!_isOpen)
-            {
-                _ = HideAsync(_drawerBox);
-            }
-        }
-
-        void OnContentDragEnded(GestureLayer.MomentumData moment)
-        {
-            _fadeInCancelTokenSource = new CancellationTokenSource();
-            _ = ShowAsync(_drawerBox, cancelltaionToken: _fadeInCancelTokenSource.Token);
-        }
-
-        void OnDrawerDragged(GestureLayer.MomentumData moment)
-        {
-            var toMove = _drawerBox.Geometry;
-            toMove.Y = (moment.Y2 < 0) ? 0 : moment.Y2;
-            _drawerBox.Geometry = toMove;
-            OnDrawerLayout();
-        }
-
-        void OnDrawerDragEnded(GestureLayer.MomentumData moment)
-        {
-            if (_drawerBox.Geometry.Y < (_mainLayout.Geometry.Height / 2))
-            {
-                Open();
-            }
-            else
-            {
-                Close();
-            }
-        }
-
-        void FlipIcon()
-        {
-            if (_isDefaultIcon)
-            {
-                _drawerIcon.Orientation = ImageOrientation.FlipVertical;
-            }
-        }
-
-        void ResetIcon()
-        {
-            _drawerIcon.Orientation = ImageOrientation.None;
-        }
-
-        Task RunMoveAnimation(EvasObject target, Rect dest, uint length, Easing easing = null)
-        {
-            var tcs = new TaskCompletionSource<bool>();
-
-            var dx = target.Geometry.X - dest.X;
-            var dy = target.Geometry.Y - dest.Y;
-
-            new Animation((progress) =>
-            {
-                var toMove = dest;
-                toMove.X += (int)(dx * (1 - progress));
-                toMove.Y += (int)(dy * (1 - progress));
-                target.Geometry = toMove;
-                OnDrawerLayout();
-            }).Commit(this, "Move", length: length, finished: (s, e) =>
-            {
-                target.Geometry = dest;
-                tcs.SetResult(true);
-            });
-            return tcs.Task;
-        }
-
-        void UnsetMainContent()
-        {
-            if (_content != null)
-            {
-                _contentBox.UnPack(_content);
-                _content.Hide();
-                _content = null;
-            }
-        }
-
-        void UnsetDrawerContent()
-        {
-            if (_drawerContent != null)
-            {
-                _drawerContentBox.UnPack(_drawerContent);
-                _drawerContent.Hide();
-                _drawerContent = null;
-
-                _drawerContentBox.Hide();
-                _drawerIconBox.Hide();
-            }
-        }
-    }
+       public class NavigationDrawer : ELayout, IAnimatable
+       {
+               static readonly int TouchWidth = 50;
+               static readonly int IconSize = 40;
+               static readonly string DefaultIcon = "Tizen.Wearable.CircularUI.Forms.Renderer.res.wc_visual_cue.png";
+
+               Box _mainLayout;
+               Box _contentGestureBox;
+               Box _contentBox;
+               Box _drawerBox;
+               Box _drawerContentBox;
+               Box _drawerIconBox;
+
+               EvasObject _content;
+               EvasObject _drawerContent;
+
+               EImage _drawerIcon;
+               EButton _touchArea;
+
+               GestureLayer _gestureOnContent;
+               GestureLayer _gestureOnDrawer;
+
+               ImageSource _drawerIconSource;
+
+               bool _isOpen;
+               bool _isDefaultIcon;
+
+               CancellationTokenSource _fadeInCancelTokenSource = null;
+
+               bool HasDrawer => _drawerBox != null;
+
+               public NavigationDrawer(EvasObject parent) : base(parent)
+               {
+                       Initialize();
+               }
+
+               public int HandlerHeight { get; set; } = 40;
+
+               public bool IsOpen
+               {
+                       get
+                       {
+                               return _isOpen;
+                       }
+                       set
+                       {
+                               if (_isOpen != value)
+                               {
+                                       if (value)
+                                       {
+                                               Open();
+                                       }
+                                       else
+                                       {
+                                               Close();
+                                       }
+                               }
+                       }
+               }
+
+               EColor _handlerBackgroundColor = EColor.Transparent;
+               public EColor HandlerBackgroundColor
+               {
+                       get => _handlerBackgroundColor;
+                       set
+                       {
+                               _handlerBackgroundColor = value;
+                               UpdateHandlerBackgroundColor();
+                       }
+               }
+
+               public event EventHandler Toggled;
+
+               public void SetMainContent(EvasObject content)
+               {
+                       if (content == null)
+                       {
+                               UnsetMainContent();
+                               return;
+                       }
+
+                       _content = content;
+                       _content.Show();
+                       _contentBox.PackEnd(_content);
+                       _content.Geometry = _contentBox.Geometry;
+               }
+
+               public void SetDrawerContent(EvasObject content)
+               {
+                       InitializeDrawerBox();
+
+                       if (content == null)
+                       {
+                               UnsetDrawerContent();
+                               return;
+                       }
+
+                       _drawerContent = content;
+                       _drawerContent.Show();
+                       _drawerContentBox.PackEnd(_drawerContent);
+
+                       _drawerContentBox.Show();
+                       _drawerIconBox.Show();
+
+                       if (_drawerContent is NavigationView nv)
+                       {
+                               nv.Dragged += (s, e) =>
+                               {
+                                       if (e.State == DraggedState.EdgeTop)
+                                       {
+                                               Close();
+                                       }
+                               };
+                       }
+               }
+
+               public void UpdateDrawerIcon(ImageSource source)
+               {
+                       _drawerIconSource = source;
+                       if (HasDrawer)
+                       {
+                               SetDrawerIcon(_drawerIconSource);
+                       }
+               }
+
+               public async void Open(uint length = 300)
+               {
+                       if (!HasDrawer)
+                               return;
+
+                       var toMove = _drawerBox.Geometry;
+                       toMove.Y = 0;
+
+                       await RunMoveAnimation(_drawerBox, toMove, length);
+
+                       if (!_isOpen)
+                       {
+                               _isOpen = true;
+                               Toggled?.Invoke(this, EventArgs.Empty);
+                       }
+                       OnLayout();
+                       OnDrawerLayout();
+                       FlipIcon();
+               }
+
+               public async void Close(uint length = 300)
+               {
+                       if (!HasDrawer)
+                               return;
+
+                       var toMove = _drawerBox.Geometry;
+                       toMove.Y = Geometry.Height - HandlerHeight;
+
+                       await RunMoveAnimation(_drawerBox, toMove, length);
+
+                       if (_isOpen)
+                       {
+                               _isOpen = false;
+                               Toggled?.Invoke(this, EventArgs.Empty);
+                       }
+                       OnLayout();
+                       OnDrawerLayout();
+                       ResetIcon();
+                       StartHighlightAnimation(_drawerIcon);
+               }
+
+               void IAnimatable.BatchBegin()
+               {
+               }
+
+               void IAnimatable.BatchCommit()
+               {
+               }
+
+               protected override IntPtr CreateHandle(EvasObject parent)
+               {
+                       _mainLayout = new Box(parent);
+                       return _mainLayout.Handle;
+               }
+
+               void Initialize()
+               {
+                       _mainLayout.SetLayoutCallback(OnLayout);
+
+                       _contentGestureBox = new Box(_mainLayout);
+                       _contentGestureBox.Show();
+                       _mainLayout.PackEnd(_contentGestureBox);
+
+                       _contentBox = new Box(_mainLayout);
+                       _contentBox.SetLayoutCallback(OnContentLayout);
+                       _contentBox.Show();
+                       _mainLayout.PackEnd(_contentBox);
+               }
+
+               void InitializeDrawerBox()
+               {
+                       if (_drawerBox != null)
+                               return;
+
+                       _drawerBox = new Box(_mainLayout);
+                       _drawerBox.SetLayoutCallback(OnDrawerLayout);
+                       _drawerBox.Show();
+                       _mainLayout.PackEnd(_drawerBox);
+
+                       _drawerContentBox = new Box(_drawerBox);
+                       _drawerBox.PackEnd(_drawerContentBox);
+
+                       _drawerIconBox = new Box(_drawerBox)
+                       {
+                               BackgroundColor = _handlerBackgroundColor
+                       };
+                       _drawerBox.PackEnd(_drawerIconBox);
+
+                       _drawerIcon = new EImage(_drawerIconBox)
+                       {
+                               AlignmentY = 0.5,
+                               AlignmentX = 0.5,
+                               MinimumHeight = IconSize,
+                               MinimumWidth = IconSize,
+                       };
+                       _drawerIcon.Show();
+                       _drawerIconBox.PackEnd(_drawerIcon);
+                       SetDrawerIcon(_drawerIconSource);
+
+                       _touchArea = new EButton(_drawerBox)
+                       {
+                               Color = EColor.Transparent,
+                               BackgroundColor = EColor.Transparent,
+                       };
+                       _touchArea.SetPartColor("effect", EColor.Transparent);
+                       _touchArea.Show();
+                       _touchArea.RepeatEvents = true;
+                       _touchArea.Clicked += OnIconClicked;
+
+                       _drawerBox.PackEnd(_touchArea);
+
+                       _gestureOnContent = new GestureLayer(_contentGestureBox);
+                       _gestureOnContent.SetMomentumCallback(GestureLayer.GestureState.Start, OnContentDragStarted);
+                       _gestureOnContent.SetMomentumCallback(GestureLayer.GestureState.End, OnContentDragEnded);
+                       _gestureOnContent.SetMomentumCallback(GestureLayer.GestureState.Abort, OnContentDragEnded);
+                       _gestureOnContent.Attach(_contentGestureBox);
+                       _contentBox.RepeatEvents = true;
+
+                       _gestureOnDrawer = new GestureLayer(_drawerIconBox);
+                       _gestureOnDrawer.SetMomentumCallback(GestureLayer.GestureState.Move, OnDrawerDragged);
+                       _gestureOnDrawer.SetMomentumCallback(GestureLayer.GestureState.End, OnDrawerDragEnded);
+                       _gestureOnDrawer.SetMomentumCallback(GestureLayer.GestureState.Abort, OnDrawerDragEnded);
+                       _gestureOnDrawer.Attach(_drawerIconBox);
+
+                       ERotaryEventManager.Rotated += OnRotateEventReceived;
+               }
+
+               void SetDrawerIcon(ImageSource source)
+               {
+                       if (source == null)
+                       {
+                               _drawerIcon.LoadFromImageSourceAsync(ImageSource.FromResource(DefaultIcon, GetType().Assembly));
+                               _isDefaultIcon = true;
+                       }
+                       else
+                       {
+                               _isDefaultIcon = false;
+                               if (source is FileImageSource fsource)
+                               {
+                                       _drawerIcon.Load(fsource.ToAbsPath());
+                               }
+                               else
+                               {
+                                       _drawerIcon.LoadFromImageSourceAsync(source);
+                               }
+                       }
+               }
+
+               void UpdateHandlerBackgroundColor()
+               {
+                       if (_drawerIconBox != null)
+                       {
+                               _drawerIconBox.BackgroundColor = _handlerBackgroundColor;
+                       }
+               }
+
+               void OnIconClicked(object sender, EventArgs e)
+               {
+                       if (IsOpen)
+                               Close();
+                       else
+                               Open();
+               }
+
+               async Task<bool> ShowAsync(EWidget target, Easing easing = null, uint length = 300, CancellationToken cancelltaionToken = default(CancellationToken))
+               {
+                       var tcs = new TaskCompletionSource<bool>();
+
+                       await Task.Delay(1000);
+
+                       if (cancelltaionToken.IsCancellationRequested)
+                       {
+                               cancelltaionToken.ThrowIfCancellationRequested();
+                       }
+
+                       target.Show();
+                       var opacity = target.Opacity;
+
+                       if (opacity == 255 || opacity == -1)
+                               return true;
+
+                       new Animation((progress) =>
+                       {
+                               target.Opacity = opacity + (int)((255 - opacity) * progress);
+
+                       }).Commit(this, "FadeIn", length: length, finished: (p, e) =>
+                       {
+                               target.Opacity = 255;
+                               tcs.SetResult(true);
+                               StartHighlightAnimation(_drawerIcon);
+                       });
+
+                       return await tcs.Task;
+               }
+
+               void OnLayout()
+               {
+                       var bound = Geometry;
+                       _contentGestureBox.Geometry = bound;
+                       _contentBox.Geometry = bound;
+                       if (_drawerBox != null)
+                       {
+                               bound.Y = _isOpen ? 0 : (bound.Height - HandlerHeight);
+                               _drawerBox.Geometry = bound;
+                       }
+               }
+
+               void OnContentLayout()
+               {
+                       if (_content != null)
+                       {
+                               _content.Geometry = _contentBox.Geometry;
+                       }
+               }
+
+               void OnDrawerLayout()
+               {
+                       this.AbortAnimation("HighlightAnimation");
+
+                       var bound = _drawerBox.Geometry;
+
+                       var currentY = bound.Y;
+                       var ratio = currentY / (double)(Geometry.Height - HandlerHeight);
+
+                       var contentBound = bound;
+                       contentBound.Y += (int)(HandlerHeight * ratio);
+                       _drawerContentBox.Geometry = contentBound;
+
+                       var drawerHandleBound = bound;
+                       drawerHandleBound.Height = HandlerHeight;
+                       _drawerIconBox.Geometry = drawerHandleBound;
+
+                       var drawerTouchBound = drawerHandleBound;
+                       drawerTouchBound.Width = TouchWidth;
+                       drawerTouchBound.X = drawerHandleBound.X + (drawerHandleBound.Width - TouchWidth) / 2;
+                       _touchArea.Geometry = drawerTouchBound;
+               }
+
+               async Task<bool> HideAsync(EWidget target, Easing easing = null, uint length = 300)
+               {
+                       var tcs = new TaskCompletionSource<bool>();
+
+                       var opacity = target.Opacity;
+                       if (opacity == -1)
+                               opacity = 255;
+
+                       new Animation((progress) =>
+                       {
+                               target.Opacity = opacity - (int)(progress * opacity);
+
+                       }).Commit(this, "FadeOut", length: length, finished: (p, e) =>
+                       {
+                               target.Opacity = 0;
+                               target.Hide();
+                               tcs.SetResult(true);
+                       });
+
+                       return await tcs.Task;
+               }
+
+               void StartHighlightAnimation(EWidget target)
+               {
+                       if (!_isDefaultIcon || this.AnimationIsRunning("HighlightAnimation"))
+                               return;
+
+                       int count = 2;
+                       var bound = target.Geometry;
+                       var y = bound.Y;
+                       var dy = bound.Y - bound.Height / 3;
+
+                       var anim = new Animation();
+
+                       var transfAnim = new Animation((f) =>
+                       {
+                               bound.Y = (int)f;
+                               var map = new EvasMap(4);
+                               map.PopulatePoints(bound, 0);
+                               target.IsMapEnabled = true;
+                               target.EvasMap = map;
+                       }, y, dy);
+
+                       var opacityAnim = new Animation(f => target.Opacity = (int)f, 255, 40);
+
+                       anim.Add(0, 1, opacityAnim);
+                       anim.Add(0, 1, transfAnim);
+
+                       anim.Commit(this, "HighlightAnimation", 16, 800, finished: (f, b) =>
+                       {
+                               target.Opacity = 255;
+                               target.IsMapEnabled = false;
+                       }, repeat:() => --count > 0);
+               }
+
+               async void OnRotateEventReceived(EventArgs args)
+               {
+                       _fadeInCancelTokenSource?.Cancel();
+                       _fadeInCancelTokenSource = new CancellationTokenSource();
+
+                       if (!_isOpen)
+                       {
+                               var token = _fadeInCancelTokenSource.Token;
+                               await HideAsync(_drawerBox);
+                               _ = ShowAsync(_drawerBox, cancelltaionToken: token);
+                       }
+               }
+
+               void OnContentDragStarted(GestureLayer.MomentumData moment)
+               {
+                       _fadeInCancelTokenSource?.Cancel();
+                       _fadeInCancelTokenSource = null;
+
+                       if (!_isOpen)
+                       {
+                               _ = HideAsync(_drawerBox);
+                       }
+               }
+
+               void OnContentDragEnded(GestureLayer.MomentumData moment)
+               {
+                       _fadeInCancelTokenSource = new CancellationTokenSource();
+                       _ = ShowAsync(_drawerBox, cancelltaionToken: _fadeInCancelTokenSource.Token);
+               }
+
+               void OnDrawerDragged(GestureLayer.MomentumData moment)
+               {
+                       var toMove = _drawerBox.Geometry;
+                       toMove.Y = (moment.Y2 < 0) ? 0 : moment.Y2;
+                       _drawerBox.Geometry = toMove;
+                       OnDrawerLayout();
+               }
+
+               void OnDrawerDragEnded(GestureLayer.MomentumData moment)
+               {
+                       if (_drawerBox.Geometry.Y < (_mainLayout.Geometry.Height / 2))
+                       {
+                               Open();
+                       }
+                       else
+                       {
+                               Close();
+                       }
+               }
+
+               void FlipIcon()
+               {
+                       if (_isDefaultIcon)
+                       {
+                               _drawerIcon.Orientation = ImageOrientation.FlipVertical;
+                       }
+               }
+
+               void ResetIcon()
+               {
+                       _drawerIcon.Orientation = ImageOrientation.None;
+               }
+
+               Task RunMoveAnimation(EvasObject target, Rect dest, uint length, Easing easing = null)
+               {
+                       var tcs = new TaskCompletionSource<bool>();
+
+                       var dx = target.Geometry.X - dest.X;
+                       var dy = target.Geometry.Y - dest.Y;
+
+                       new Animation((progress) =>
+                       {
+                               var toMove = dest;
+                               toMove.X += (int)(dx * (1 - progress));
+                               toMove.Y += (int)(dy * (1 - progress));
+                               target.Geometry = toMove;
+                               OnDrawerLayout();
+                       }).Commit(this, "Move", length: length, finished: (s, e) =>
+                       {
+                               target.Geometry = dest;
+                               tcs.SetResult(true);
+                       });
+                       return tcs.Task;
+               }
+
+               void UnsetMainContent()
+               {
+                       if (_content != null)
+                       {
+                               _contentBox.UnPack(_content);
+                               _content.Hide();
+                               _content = null;
+                       }
+               }
+
+               void UnsetDrawerContent()
+               {
+                       if (_drawerContent != null)
+                       {
+                               _drawerContentBox.UnPack(_drawerContent);
+                               _drawerContent.Hide();
+                               _drawerContent = null;
+
+                               _drawerContentBox.Hide();
+                               _drawerIconBox.Hide();
+                       }
+               }
+       }
 }
\ No newline at end of file
index 1206d9d..bd07f3c 100644 (file)
@@ -12,354 +12,354 @@ using ELayout = ElmSharp.Layout;
 
 namespace Tizen.Wearable.CircularUI.Forms.Renderer
 {
-    public class NavigationView : ELayout
-    {
-        readonly int _dafaultIconSize = 60;
-
-        class Item : INotifyPropertyChanged
-        {
-            Element _source;
-            public Element Source
-            {
-                get
-                {
-                    return _source;
-                }
-                set
-                {
-                    if (_source != null)
-                    {
-                        _source.PropertyChanged -= OnElementPropertyChanged;
-                    }
-                    _source = value;
-                    _source.PropertyChanged += OnElementPropertyChanged;
-                    UpdateContent();
-                }
-            }
-
-            public string Text { get; set; }
-            public string Icon { get; set; }
-
-            public event PropertyChangedEventHandler PropertyChanged;
-
-            void UpdateContent()
-            {
-                if (Source is BaseShellItem shellItem)
-                {
-                    Text = shellItem.Title;
-                    Icon = (shellItem.Icon as FileImageSource)?.ToAbsPath();
-                }
-                else if (Source is MenuItem menuItem)
-                {
-                    Text = menuItem.Text;
-                    Icon = (menuItem.IconImageSource as FileImageSource)?.ToAbsPath();
-                }
-                else
-                {
-                    Text = null;
-                    Icon = null;
-                }
-                SendPropertyChanged();
-            }
-
-            void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
-            {
-                UpdateContent();
-            }
-
-            void SendPropertyChanged([CallerMemberName] string propertyName = "")
-            {
-                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
-            }
-        }
-
-        Box _outterBox;
-        ELayout _surfaceLayout;
-        CircleSurface _surface;
-        CircleGenList _naviMenu;
-
-        GenItemClass _defaultClass;
-        SmartEvent _draggedUpCallback;
-        SmartEvent _draggedDownCallback;
-
-        GenListItem _header;
-        GenListItem _footer;
-
-        List<List<Element>> _itemCache;
-        List<GenListItem> _items = new List<GenListItem>();
-
-        public NavigationView(EvasObject parent) : base(parent)
-        {
-            InitializeComponent();
-        }
-
-        public event EventHandler<SelectedItemChangedEventArgs> ItemSelected;
-
-        public event EventHandler<DraggedEventArgs> Dragged;
-
-
-        EColor _backgroundColor = EColor.Black;
-        public override EColor BackgroundColor
-        {
-            get => _backgroundColor;
-            set
-            {
-                _backgroundColor = value.IsDefault ? EColor.Black : value;
-                UpdateBackgroundColor();
-            }
-        }
-
-        EColor _foregroundColor = EColor.Default;
-        public EColor ForegroundColor
-        {
-            get => _foregroundColor;
-            set
-            {
-                _foregroundColor = value;
-                UpdateForegroundColor();
-            }
-        }
-
-
-        public void Build(List<List<Element>> items)
-        {
-            // Only update when items was changed
-            if (!IsUpdated(items))
-            {
-                return;
-            }
-            _itemCache = items;
-
-            ClearItemPropertyChangedHandler();
-            _naviMenu.Clear();
-            _items.Clear();
-            // header
-            _header = _naviMenu.Append(_defaultClass, new Item { Text = "" });
-
-            // TODO. need to improve, need to support group
-            foreach (var group in items)
-            {
-                foreach (var item in group)
-                {
-                    var data = new Item
-                    {
-                        Source = item
-                    };
-                    if (item is BaseShellItem shellItem)
-                    {
-                        data.Text = shellItem.Title;
-                        data.Icon = (shellItem.Icon as FileImageSource)?.ToAbsPath();
-                    }
-                    else if (item is MenuItem menuItem)
-                    {
-                        data.Text = menuItem.Text;
-                        data.Icon = (menuItem.IconImageSource as FileImageSource)?.ToAbsPath();
-                    }
-                    var genitem = _naviMenu.Append(_defaultClass, data, GenListItemType.Normal);
-                    genitem.SetPartColor("bg", _backgroundColor);
-                    _items.Add(genitem);
-                    data.PropertyChanged += OnItemPropertyChanged;
-                }
-            }
-            _footer = _naviMenu.Append(_defaultClass, new Item { Text = "" });
-        }
-
-        public void Activate()
-        {
-            (_naviMenu as IRotaryActionWidget)?.Activate();
-        }
-        public void Deactivate()
-        {
-            (_naviMenu as IRotaryActionWidget)?.Deactivate();
-        }
-
-        protected override IntPtr CreateHandle(EvasObject parent)
-        {
-            _outterBox = new Box(parent);
-            return _outterBox.Handle;
-        }
-
-        void InitializeComponent()
-        {
-            _outterBox.SetLayoutCallback(OnLayout);
-
-            _surfaceLayout = new ELayout(this);
-            _surfaceLayout.Show();
-            _surface = new CircleSurface(_surfaceLayout);
-
-            _naviMenu = new CircleGenList(this, _surface)
-            {
-                Homogeneous = true,
-                BackgroundColor = _backgroundColor
-            };
-            _naviMenu.Show();
-
-            _draggedUpCallback = new SmartEvent(_naviMenu, "drag,start,up");
-            _draggedUpCallback.On += (s, e) =>
-            {
-                if (_footer.TrackObject.IsVisible)
-                {
-                    Dragged?.Invoke(this, new DraggedEventArgs(DraggedState.EdgeBottom));
-                }
-                else
-                {
-                    Dragged?.Invoke(this, new DraggedEventArgs(DraggedState.Up));
-                }
-            };
-
-            _draggedDownCallback = new SmartEvent(_naviMenu, "drag,start,down");
-            _draggedDownCallback.On += (s, e) =>
-            {
-                if (_header.TrackObject.IsVisible)
-                {
-                    Dragged?.Invoke(this, new DraggedEventArgs(DraggedState.EdgeTop));
-                }
-                else
-                {
-                    Dragged?.Invoke(this, new DraggedEventArgs(DraggedState.Down));
-                }
-            };
-
-            _outterBox.PackEnd(_naviMenu);
-            _outterBox.PackEnd(_surfaceLayout);
-
-            _surfaceLayout.StackAbove(_naviMenu);
-
-            _defaultClass = new GenItemClass("1icon_1text")
-            {
-                GetTextHandler = (obj, part) =>
-                {
-                    if (part == "elm.text")
-                    {
-                        var text = (obj as Item).Text;
-                        if (_foregroundColor != EColor.Default)
-                            return $"<span color='{_foregroundColor.ToHex()}'>{text}</span>";
-                        else
-                            return text;
-                    }
-                    return null;
-                },
-                GetContentHandler = (obj, part) =>
-                {
-                    if (part == "elm.swallow.icon" && obj is Item menuItem && !string.IsNullOrEmpty(menuItem.Icon))
-                    {
-                        var icon = new ElmSharp.Image(Xamarin.Forms.Forms.NativeParent)
-                        {
-                            AlignmentX = -1,
-                            AlignmentY = -1,
-                            WeightX = 1.0,
-                            WeightY = 1.0,
-                            MinimumWidth = _dafaultIconSize,
-                            MinimumHeight = _dafaultIconSize,
-                        };
-                        icon.Show();
-                        icon.Load(menuItem.Icon);
-                        return icon;
-                    }
-                    return null;
-                }
-            };
-
-            _naviMenu.ItemSelected += OnItemSelected;
-
-        }
-
-        void OnItemSelected(object sender, GenListItemEventArgs e)
-        {
-            ItemSelected?.Invoke(this, new SelectedItemChangedEventArgs((e.Item.Data as Item).Source, -1));
-        }
-
-        void OnLayout()
-        {
-            _surfaceLayout.Geometry = Geometry;
-            _naviMenu.Geometry = Geometry;
-        }
-
-        void UpdateBackgroundColor()
-        {
-            _naviMenu.BackgroundColor = _backgroundColor;
-            foreach (var item in _items)
-            {
-                item.SetPartColor("bg", _backgroundColor);
-            }
-        }
-
-        void UpdateForegroundColor()
-        {
-            foreach (var item in _items)
-            {
-                item.Update();
-            }
-        }
-
-        bool IsUpdated(List<List<Element>> items)
-        {
-            if (_itemCache == null)
-                return true;
-
-            if (_itemCache.Count != items.Count)
-                return true;
-
-            for (int i = 0; i < items.Count; i++)
-            {
-                if (_itemCache[i].Count != items[i].Count)
-                    return true;
-
-                for (int j = 0; j < items[i].Count; j++)
-                {
-                    if (_itemCache[i][j] != items[i][j])
-                        return true;
-                }
-            }
-            return false;
-        }
-
-        void ClearItemPropertyChangedHandler()
-        {
-            foreach (var item in _items)
-            {
-                (item.Data as Item).PropertyChanged -= OnItemPropertyChanged;
-            }
-        }
-
-        void OnItemPropertyChanged(object sender, PropertyChangedEventArgs e)
-        {
-            var item = _items.Where((d) => d.Data == sender).FirstOrDefault();
-            item?.Update();
-        }
-
-    }
-    public enum DraggedState
-    {
-        EdgeTop,
-        Up,
-        Down,
-        EdgeBottom,
-    }
-
-    public class DraggedEventArgs
-    {
-        public DraggedState State { get; private set; }
-
-        public DraggedEventArgs(DraggedState state)
-        {
-            State = state;
-        }
-    }
-
-    static class FileImageSourceEX
-    {
-        public static string ToAbsPath(this FileImageSource source)
-        {
-            return ResourcePath.GetPath(source.File);
-        }
-    }
-
-    static class ColorEX
-    {
-        public static string ToHex(this EColor c)
-        {
-            return string.Format("#{0:X2}{1:X2}{2:X2}{3:X2}", c.R, c.G, c.B, c.A);
-        }
-    }
-}
+       public class NavigationView : ELayout
+       {
+               readonly int _dafaultIconSize = 60;
+
+               class Item : INotifyPropertyChanged
+               {
+                       Element _source;
+                       public Element Source
+                       {
+                               get
+                               {
+                                       return _source;
+                               }
+                               set
+                               {
+                                       if (_source != null)
+                                       {
+                                               _source.PropertyChanged -= OnElementPropertyChanged;
+                                       }
+                                       _source = value;
+                                       _source.PropertyChanged += OnElementPropertyChanged;
+                                       UpdateContent();
+                               }
+                       }
+
+                       public string Text { get; set; }
+                       public string Icon { get; set; }
+
+                       public event PropertyChangedEventHandler PropertyChanged;
+
+                       void UpdateContent()
+                       {
+                               if (Source is BaseShellItem shellItem)
+                               {
+                                       Text = shellItem.Title;
+                                       Icon = (shellItem.Icon as FileImageSource)?.ToAbsPath();
+                               }
+                               else if (Source is MenuItem menuItem)
+                               {
+                                       Text = menuItem.Text;
+                                       Icon = (menuItem.IconImageSource as FileImageSource)?.ToAbsPath();
+                               }
+                               else
+                               {
+                                       Text = null;
+                                       Icon = null;
+                               }
+                               SendPropertyChanged();
+                       }
+
+                       void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+                       {
+                               UpdateContent();
+                       }
+
+                       void SendPropertyChanged([CallerMemberName] string propertyName = "")
+                       {
+                               PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+                       }
+               }
+
+               Box _outterBox;
+               ELayout _surfaceLayout;
+               CircleSurface _surface;
+               CircleGenList _naviMenu;
+
+               GenItemClass _defaultClass;
+               SmartEvent _draggedUpCallback;
+               SmartEvent _draggedDownCallback;
+
+               GenListItem _header;
+               GenListItem _footer;
+
+               List<List<Element>> _itemCache;
+               List<GenListItem> _items = new List<GenListItem>();
+
+               public NavigationView(EvasObject parent) : base(parent)
+               {
+                       InitializeComponent();
+               }
+
+               public event EventHandler<SelectedItemChangedEventArgs> ItemSelected;
+
+               public event EventHandler<DraggedEventArgs> Dragged;
+
+
+               EColor _backgroundColor = EColor.Black;
+               public override EColor BackgroundColor
+               {
+                       get => _backgroundColor;
+                       set
+                       {
+                               _backgroundColor = value.IsDefault ? EColor.Black : value;
+                               UpdateBackgroundColor();
+                       }
+               }
+
+               EColor _foregroundColor = EColor.Default;
+               public EColor ForegroundColor
+               {
+                       get => _foregroundColor;
+                       set
+                       {
+                               _foregroundColor = value;
+                               UpdateForegroundColor();
+                       }
+               }
+
+
+               public void Build(List<List<Element>> items)
+               {
+                       // Only update when items was changed
+                       if (!IsUpdated(items))
+                       {
+                               return;
+                       }
+                       _itemCache = items;
+
+                       ClearItemPropertyChangedHandler();
+                       _naviMenu.Clear();
+                       _items.Clear();
+                       // header
+                       _header = _naviMenu.Append(_defaultClass, new Item { Text = "" });
+
+                       // TODO. need to improve, need to support group
+                       foreach (var group in items)
+                       {
+                               foreach (var item in group)
+                               {
+                                       var data = new Item
+                                       {
+                                               Source = item
+                                       };
+                                       if (item is BaseShellItem shellItem)
+                                       {
+                                               data.Text = shellItem.Title;
+                                               data.Icon = (shellItem.Icon as FileImageSource)?.ToAbsPath();
+                                       }
+                                       else if (item is MenuItem menuItem)
+                                       {
+                                               data.Text = menuItem.Text;
+                                               data.Icon = (menuItem.IconImageSource as FileImageSource)?.ToAbsPath();
+                                       }
+                                       var genitem = _naviMenu.Append(_defaultClass, data, GenListItemType.Normal);
+                                       genitem.SetPartColor("bg", _backgroundColor);
+                                       _items.Add(genitem);
+                                       data.PropertyChanged += OnItemPropertyChanged;
+                               }
+                       }
+                       _footer = _naviMenu.Append(_defaultClass, new Item { Text = "" });
+               }
+
+               public void Activate()
+               {
+                       (_naviMenu as IRotaryActionWidget)?.Activate();
+               }
+               public void Deactivate()
+               {
+                       (_naviMenu as IRotaryActionWidget)?.Deactivate();
+               }
+
+               protected override IntPtr CreateHandle(EvasObject parent)
+               {
+                       _outterBox = new Box(parent);
+                       return _outterBox.Handle;
+               }
+
+               void InitializeComponent()
+               {
+                       _outterBox.SetLayoutCallback(OnLayout);
+
+                       _surfaceLayout = new ELayout(this);
+                       _surfaceLayout.Show();
+                       _surface = new CircleSurface(_surfaceLayout);
+
+                       _naviMenu = new CircleGenList(this, _surface)
+                       {
+                               Homogeneous = true,
+                               BackgroundColor = _backgroundColor
+                       };
+                       _naviMenu.Show();
+
+                       _draggedUpCallback = new SmartEvent(_naviMenu, "drag,start,up");
+                       _draggedUpCallback.On += (s, e) =>
+                       {
+                               if (_footer.TrackObject.IsVisible)
+                               {
+                                       Dragged?.Invoke(this, new DraggedEventArgs(DraggedState.EdgeBottom));
+                               }
+                               else
+                               {
+                                       Dragged?.Invoke(this, new DraggedEventArgs(DraggedState.Up));
+                               }
+                       };
+
+                       _draggedDownCallback = new SmartEvent(_naviMenu, "drag,start,down");
+                       _draggedDownCallback.On += (s, e) =>
+                       {
+                               if (_header.TrackObject.IsVisible)
+                               {
+                                       Dragged?.Invoke(this, new DraggedEventArgs(DraggedState.EdgeTop));
+                               }
+                               else
+                               {
+                                       Dragged?.Invoke(this, new DraggedEventArgs(DraggedState.Down));
+                               }
+                       };
+
+                       _outterBox.PackEnd(_naviMenu);
+                       _outterBox.PackEnd(_surfaceLayout);
+
+                       _surfaceLayout.StackAbove(_naviMenu);
+
+                       _defaultClass = new GenItemClass("1icon_1text")
+                       {
+                               GetTextHandler = (obj, part) =>
+                               {
+                                       if (part == "elm.text")
+                                       {
+                                               var text = (obj as Item).Text;
+                                               if (_foregroundColor != EColor.Default)
+                                                       return $"<span color='{_foregroundColor.ToHex()}'>{text}</span>";
+                                               else
+                                                       return text;
+                                       }
+                                       return null;
+                               },
+                               GetContentHandler = (obj, part) =>
+                               {
+                                       if (part == "elm.swallow.icon" && obj is Item menuItem && !string.IsNullOrEmpty(menuItem.Icon))
+                                       {
+                                               var icon = new ElmSharp.Image(Xamarin.Forms.Forms.NativeParent)
+                                               {
+                                                       AlignmentX = -1,
+                                                       AlignmentY = -1,
+                                                       WeightX = 1.0,
+                                                       WeightY = 1.0,
+                                                       MinimumWidth = _dafaultIconSize,
+                                                       MinimumHeight = _dafaultIconSize,
+                                               };
+                                               icon.Show();
+                                               icon.Load(menuItem.Icon);
+                                               return icon;
+                                       }
+                                       return null;
+                               }
+                       };
+
+                       _naviMenu.ItemSelected += OnItemSelected;
+
+               }
+
+               void OnItemSelected(object sender, GenListItemEventArgs e)
+               {
+                       ItemSelected?.Invoke(this, new SelectedItemChangedEventArgs((e.Item.Data as Item).Source, -1));
+               }
+
+               void OnLayout()
+               {
+                       _surfaceLayout.Geometry = Geometry;
+                       _naviMenu.Geometry = Geometry;
+               }
+
+               void UpdateBackgroundColor()
+               {
+                       _naviMenu.BackgroundColor = _backgroundColor;
+                       foreach (var item in _items)
+                       {
+                               item.SetPartColor("bg", _backgroundColor);
+                       }
+               }
+
+               void UpdateForegroundColor()
+               {
+                       foreach (var item in _items)
+                       {
+                               item.Update();
+                       }
+               }
+
+               bool IsUpdated(List<List<Element>> items)
+               {
+                       if (_itemCache == null)
+                               return true;
+
+                       if (_itemCache.Count != items.Count)
+                               return true;
+
+                       for (int i = 0; i < items.Count; i++)
+                       {
+                               if (_itemCache[i].Count != items[i].Count)
+                                       return true;
+
+                               for (int j = 0; j < items[i].Count; j++)
+                               {
+                                       if (_itemCache[i][j] != items[i][j])
+                                               return true;
+                               }
+                       }
+                       return false;
+               }
+
+               void ClearItemPropertyChangedHandler()
+               {
+                       foreach (var item in _items)
+                       {
+                               (item.Data as Item).PropertyChanged -= OnItemPropertyChanged;
+                       }
+               }
+
+               void OnItemPropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       var item = _items.Where((d) => d.Data == sender).FirstOrDefault();
+                       item?.Update();
+               }
+
+       }
+       public enum DraggedState
+       {
+               EdgeTop,
+               Up,
+               Down,
+               EdgeBottom,
+       }
+
+       public class DraggedEventArgs
+       {
+               public DraggedState State { get; private set; }
+
+               public DraggedEventArgs(DraggedState state)
+               {
+                       State = state;
+               }
+       }
+
+       static class FileImageSourceEX
+       {
+               public static string ToAbsPath(this FileImageSource source)
+               {
+                       return ResourcePath.GetPath(source.File);
+               }
+       }
+
+       static class ColorEX
+       {
+               public static string ToHex(this EColor c)
+               {
+                       return string.Format("#{0:X2}{1:X2}{2:X2}{3:X2}", c.R, c.G, c.B, c.A);
+               }
+       }
+}
\ No newline at end of file
index d6f31cb..47c0673 100644 (file)
@@ -4,37 +4,37 @@ using Xamarin.Forms.Platform.Tizen;
 
 namespace Tizen.Wearable.CircularUI.Forms.Renderer
 {
-    public class ShellContentRenderer : IShellItemRenderer
-    {
-        public ShellContentRenderer(ShellContent content)
-        {
-            ShellContent = content;
-            NativeView = GetNativeView(content);
-        }
+       public class ShellContentRenderer : IShellItemRenderer
+       {
+               public ShellContentRenderer(ShellContent content)
+               {
+                       ShellContent = content;
+                       NativeView = GetNativeView(content);
+               }
 
-        public ShellContent ShellContent { get; protected set; }
+               public ShellContent ShellContent { get; protected set; }
 
-        public BaseShellItem Item => ShellContent;
+               public BaseShellItem Item => ShellContent;
 
-        public EvasObject NativeView { get; protected set; }
+               public EvasObject NativeView { get; protected set; }
 
-        public void Dispose()
-        {
-            Dispose(true);
-        }
+               public void Dispose()
+               {
+                       Dispose(true);
+               }
 
-        protected virtual void Dispose(bool disposing)
-        {
-            if (disposing)
-            {
-                NativeView?.Unrealize();
-            }
-        }
+               protected virtual void Dispose(bool disposing)
+               {
+                       if (disposing)
+                       {
+                               NativeView?.Unrealize();
+                       }
+               }
 
-        static EvasObject GetNativeView(ShellContent content)
-        {
-            var page = (content as IShellContentController).GetOrCreateContent();
-            return Platform.GetOrCreateRenderer(page).NativeView;
-        }
-    }
-}
+               static EvasObject GetNativeView(ShellContent content)
+               {
+                       var page = (content as IShellContentController).GetOrCreateContent();
+                       return Platform.GetOrCreateRenderer(page).NativeView;
+               }
+       }
+}
\ No newline at end of file
index 9f74894..908851a 100644 (file)
@@ -6,92 +6,92 @@ using XForms = Xamarin.Forms.Forms;
 
 namespace Tizen.Wearable.CircularUI.Forms.Renderer
 {
-    public class ShellItemRenderer : IShellItemRenderer
-    {
-        Box _mainLayout;
-        EvasObject _currentItem;
-        Dictionary<BaseShellItem, IShellItemRenderer> _rendererCache = new Dictionary<BaseShellItem, IShellItemRenderer>();
+       public class ShellItemRenderer : IShellItemRenderer
+       {
+               Box _mainLayout;
+               EvasObject _currentItem;
+               Dictionary<BaseShellItem, IShellItemRenderer> _rendererCache = new Dictionary<BaseShellItem, IShellItemRenderer>();
 
-        public ShellItemRenderer(ShellItem item)
-        {
-            ShellItem = item;
-            ShellItem.PropertyChanged += OnItemPropertyChanged;
-            InitializeComponent();
-            UpdateCurrentItem();
-        }
+               public ShellItemRenderer(ShellItem item)
+               {
+                       ShellItem = item;
+                       ShellItem.PropertyChanged += OnItemPropertyChanged;
+                       InitializeComponent();
+                       UpdateCurrentItem();
+               }
 
-        public ShellItem ShellItem { get; protected set; }
+               public ShellItem ShellItem { get; protected set; }
 
-        public BaseShellItem Item => ShellItem;
+               public BaseShellItem Item => ShellItem;
 
-        public EvasObject NativeView => _mainLayout;
+               public EvasObject NativeView => _mainLayout;
 
-        public void Dispose()
-        {
-            Dispose(true);
-        }
+               public void Dispose()
+               {
+                       Dispose(true);
+               }
 
-        protected virtual void Dispose(bool disposing)
-        {
-            if (disposing)
-            {
-                ResetCurrentItem();
-                ShellItem.PropertyChanged -= OnItemPropertyChanged;
-            }
-        }
+               protected virtual void Dispose(bool disposing)
+               {
+                       if (disposing)
+                       {
+                               ResetCurrentItem();
+                               ShellItem.PropertyChanged -= OnItemPropertyChanged;
+                       }
+               }
 
-        void InitializeComponent()
-        {
-            _mainLayout = new Box(XForms.NativeParent);
-            _mainLayout.SetLayoutCallback(OnLayout);
-        }
+               void InitializeComponent()
+               {
+                       _mainLayout = new Box(XForms.NativeParent);
+                       _mainLayout.SetLayoutCallback(OnLayout);
+               }
 
-        void UpdateCurrentItem()
-        {
-            ResetCurrentItem();
-            var currentItem = ShellItem.CurrentItem;
-            if (currentItem != null)
-            {
-                if (!_rendererCache.TryGetValue(currentItem, out IShellItemRenderer renderer))
-                {
-                    renderer = ShellRendererFactory.Default.CreateShellNavigationRenderer(currentItem);
-                    _rendererCache[currentItem] = renderer;
-                }
-                SetCurrentItem(renderer.NativeView);
-            }
-        }
+               void UpdateCurrentItem()
+               {
+                       ResetCurrentItem();
+                       var currentItem = ShellItem.CurrentItem;
+                       if (currentItem != null)
+                       {
+                               if (!_rendererCache.TryGetValue(currentItem, out IShellItemRenderer renderer))
+                               {
+                                       renderer = ShellRendererFactory.Default.CreateShellNavigationRenderer(currentItem);
+                                       _rendererCache[currentItem] = renderer;
+                               }
+                               SetCurrentItem(renderer.NativeView);
+                       }
+               }
 
-        void SetCurrentItem(EvasObject item)
-        {
-            _currentItem = item;
-            _currentItem.Show();
-            _mainLayout.PackEnd(_currentItem);
-        }
+               void SetCurrentItem(EvasObject item)
+               {
+                       _currentItem = item;
+                       _currentItem.Show();
+                       _mainLayout.PackEnd(_currentItem);
+               }
 
-        void ResetCurrentItem()
-        {
-            if (_currentItem != null)
-            {
-                _mainLayout.UnPack(_currentItem);
-                _currentItem.Hide();
-                _currentItem = null;
-            }
-        }
+               void ResetCurrentItem()
+               {
+                       if (_currentItem != null)
+                       {
+                               _mainLayout.UnPack(_currentItem);
+                               _currentItem.Hide();
+                               _currentItem = null;
+                       }
+               }
 
-        void OnItemPropertyChanged(object sender, PropertyChangedEventArgs e)
-        {
-            if (e.PropertyName == nameof(ShellItem.CurrentItem))
-            {
-                UpdateCurrentItem();
-            }
-        }
+               void OnItemPropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       if (e.PropertyName == nameof(ShellItem.CurrentItem))
+                       {
+                               UpdateCurrentItem();
+                       }
+               }
 
-        void OnLayout()
-        {
-            if (_currentItem != null)
-            {
-                _currentItem.Geometry = _mainLayout.Geometry;
-            }
-        }
-    }
-}
+               void OnLayout()
+               {
+                       if (_currentItem != null)
+                       {
+                               _currentItem.Geometry = _mainLayout.Geometry;
+                       }
+               }
+       }
+}
\ No newline at end of file
index dca82a1..525e369 100644 (file)
@@ -11,232 +11,232 @@ using Tizen.Wearable.CircularUI.Forms;
 [assembly: ExportRenderer(typeof(CircularShell), typeof(Tizen.Wearable.CircularUI.Forms.Renderer.ShellRenderer))]
 namespace Tizen.Wearable.CircularUI.Forms.Renderer
 {
-    public class ShellRenderer : VisualElementRenderer<XShell>
-    {
-        NavigationDrawer _drawer;
-        NavigationView _navigationView;
-
-        Dictionary<BaseShellItem, IShellItemRenderer> _rendererCache = new Dictionary<BaseShellItem, IShellItemRenderer>();
-
-        public ShellRenderer()
-        {
-            RegisterPropertyHandler(XShell.CurrentItemProperty, UpdateCurrentItem);
-            RegisterPropertyHandler(XShell.FlyoutIsPresentedProperty, UpdateFlyoutIsPresented);
-            RegisterPropertyHandler(XShell.FlyoutBehaviorProperty, UpdateFlyoutBehavior);
-            RegisterPropertyHandler(XShell.FlyoutIconProperty, UpdateFlyoutIcon);
-            RegisterPropertyHandler(XShell.FlyoutBackgroundColorProperty, UpdateFlyoutBackgroundColor);
-            RegisterPropertyHandler(CircularShell.FlyoutIconBackgroundColorProperty, UpdateFlyoutIconBackgroundColor);
-            RegisterPropertyHandler(CircularShell.FlyoutForegroundColorProperty, UpdateFlyoutForegroundColor);
-        }
-
-        protected override void OnElementChanged(ElementChangedEventArgs<XShell> e)
-        {
-            InitializeComponent();
-            base.OnElementChanged(e);
-        }
-
-        protected override void OnElementReady()
-        {
-            base.OnElementReady();
-            UpdateFlyoutMenu();
-            (Element as IShellController).StructureChanged += OnNavigationStructureChanged;
-        }
-
-        protected override void Dispose(bool disposing)
-        {
-            if (disposing)
-            {
-                foreach (var renderer in _rendererCache.Values)
-                {
-                    renderer.Dispose();
-                }
-                (Element as IShellController).StructureChanged -= OnNavigationStructureChanged;
-            }
-            base.Dispose(disposing);
-        }
-
-        void InitializeComponent()
-        {
-            if (_drawer == null)
-            {
-                _drawer = new NavigationDrawer(XForms.NativeParent);
-                _drawer.IsOpen = Element.FlyoutIsPresented;
-                _drawer.Toggled += OnNavigationDrawerToggled;
-                SetNativeView(_drawer);
-            }
-        }
-
-        void OnNavigationStructureChanged(object sender, EventArgs e)
-        {
-            UpdateFlyoutMenu();
-        }
-
-        void UpdateFlyoutMenu()
-        {
-            if (Element.FlyoutBehavior == FlyoutBehavior.Disabled)
-                return;
-
-            var flyoutItems = (Element as IShellController).GenerateFlyoutGrouping();
-            int itemCount = 0;
-            foreach (var item in flyoutItems)
-            {
-                itemCount += item.Count;
-            }
-
-            if (itemCount > 1)
-            {
-                InitializeNavigationDrawer();
-                _navigationView.Build(flyoutItems);
-            }
-            else
-            {
-                DeinitializeNavigationView();
-            }
-        }
-
-        void InitializeNavigationDrawer()
-        {
-            if (_navigationView != null)
-            {
-                return;
-            }
-
-            _navigationView = new NavigationView(XForms.NativeParent)
-            {
-                AlignmentX = -1,
-                AlignmentY = -1,
-                WeightX = 1,
-                WeightY = 1,
-            };
-            _navigationView.Show();
-            _navigationView.ItemSelected += OnMenuItemSelected;
-
-            _drawer.SetDrawerContent(_navigationView);
-        }
-
-        void OnNavigationDrawerToggled(object sender, EventArgs e)
-        {
-            if (_drawer.IsOpen)
-            {
-                _navigationView.Activate();
-            }
-            else
-            {
-                _navigationView.Deactivate();
-
-                var stack = (Element.CurrentItem.CurrentItem as ShellSection)?.Stack;
-                var currentPage = stack?.LastOrDefault<Page>();
-
-                if (currentPage == null)
-                {
-                   currentPage = (Element.CurrentItem.CurrentItem.CurrentItem as IShellContentController)?.Page;
-                }
-
-                if (currentPage != null)
-                {
-                    var renderer = Platform.GetOrCreateRenderer(currentPage);
-                    (renderer as CirclePageRenderer)?.UpdateRotaryFocusObject();
-                }
-            }
-
-            Element.SetValueFromRenderer(XShell.FlyoutIsPresentedProperty, _drawer.IsOpen);
-        }
-
-        void DeinitializeNavigationView()
-        {
-            if (_navigationView == null)
-                return;
-            _drawer.SetDrawerContent(null);
-            _navigationView.Unrealize();
-            _navigationView = null;
-        }
-
-        void OnMenuItemSelected(object sender, SelectedItemChangedEventArgs e)
-        {
-            ((IShellController)Element).OnFlyoutItemSelected(e.SelectedItem as Element);
-        }
-
-        void UpdateCurrentItem()
-        {
-            ResetCurrentItem();
-            if (Element.CurrentItem != null)
-            {
-                if (!_rendererCache.TryGetValue(Element.CurrentItem, out IShellItemRenderer renderer))
-                {
-                    renderer = ShellRendererFactory.Default.CreateItemRenderer(Element.CurrentItem);
-                    _rendererCache[Element.CurrentItem] = renderer;
-                }
-                SetCurrentItem(renderer.NativeView);
-            }
-        }
-
-        void UpdateFlyoutBehavior(bool init)
-        {
-            if (init)
-                return;
-
-            if (Element.FlyoutBehavior == FlyoutBehavior.Disabled)
-            {
-                DeinitializeNavigationView();
-            }
-            else if (Element.FlyoutBehavior == FlyoutBehavior.Flyout)
-            {
-                UpdateFlyoutMenu();
-            }
-            else if (Element.FlyoutBehavior == FlyoutBehavior.Locked)
-            {
-                // Locked behavior is not supported on circularshell
-            }
-        }
-
-        void UpdateFlyoutIcon(bool init)
-        {
-            if (init && Element.FlyoutIcon == null)
-                return;
-
-            _drawer.UpdateDrawerIcon(Element.FlyoutIcon);
-        }
-
-        void UpdateFlyoutBackgroundColor(bool init)
-        {
-            if (init && Element.FlyoutBackgroundColor.IsDefault)
-                return;
-
-            if (_navigationView != null)
-            {
-                _navigationView.BackgroundColor = Element.FlyoutBackgroundColor.ToNative();
-            }
-        }
-
-        void UpdateFlyoutForegroundColor(bool init)
-        {
-            if (init && CircularShell.GetFlyoutForegroundColor(Element).IsDefault)
-                return;
-
-            if (_navigationView != null)
-            {
-                _navigationView.ForegroundColor = CircularShell.GetFlyoutForegroundColor(Element).ToNative();
-            }
-        }
-
-        void UpdateFlyoutIconBackgroundColor()
-        {
-            _drawer.HandlerBackgroundColor = CircularShell.GetFlyoutIconBackgroundColor(Element).ToNative();
-        }
-
-        void UpdateFlyoutIsPresented()
-        {
-            _drawer.IsOpen = Element.FlyoutIsPresented;
-        }
-
-        void SetCurrentItem(EvasObject item)
-        {
-            _drawer.SetMainContent(item);
-        }
-
-        void ResetCurrentItem()
-        {
-            _drawer.SetMainContent(null);
-        }
-    }
+       public class ShellRenderer : VisualElementRenderer<XShell>
+       {
+               NavigationDrawer _drawer;
+               NavigationView _navigationView;
+
+               Dictionary<BaseShellItem, IShellItemRenderer> _rendererCache = new Dictionary<BaseShellItem, IShellItemRenderer>();
+
+               public ShellRenderer()
+               {
+                       RegisterPropertyHandler(XShell.CurrentItemProperty, UpdateCurrentItem);
+                       RegisterPropertyHandler(XShell.FlyoutIsPresentedProperty, UpdateFlyoutIsPresented);
+                       RegisterPropertyHandler(XShell.FlyoutBehaviorProperty, UpdateFlyoutBehavior);
+                       RegisterPropertyHandler(XShell.FlyoutIconProperty, UpdateFlyoutIcon);
+                       RegisterPropertyHandler(XShell.FlyoutBackgroundColorProperty, UpdateFlyoutBackgroundColor);
+                       RegisterPropertyHandler(CircularShell.FlyoutIconBackgroundColorProperty, UpdateFlyoutIconBackgroundColor);
+                       RegisterPropertyHandler(CircularShell.FlyoutForegroundColorProperty, UpdateFlyoutForegroundColor);
+               }
+
+               protected override void OnElementChanged(ElementChangedEventArgs<XShell> e)
+               {
+                       InitializeComponent();
+                       base.OnElementChanged(e);
+               }
+
+               protected override void OnElementReady()
+               {
+                       base.OnElementReady();
+                       UpdateFlyoutMenu();
+                       (Element as IShellController).StructureChanged += OnNavigationStructureChanged;
+               }
+
+               protected override void Dispose(bool disposing)
+               {
+                       if (disposing)
+                       {
+                               foreach (var renderer in _rendererCache.Values)
+                               {
+                                       renderer.Dispose();
+                               }
+                               (Element as IShellController).StructureChanged -= OnNavigationStructureChanged;
+                       }
+                       base.Dispose(disposing);
+               }
+
+               void InitializeComponent()
+               {
+                       if (_drawer == null)
+                       {
+                               _drawer = new NavigationDrawer(XForms.NativeParent);
+                               _drawer.IsOpen = Element.FlyoutIsPresented;
+                               _drawer.Toggled += OnNavigationDrawerToggled;
+                               SetNativeView(_drawer);
+                       }
+               }
+
+               void OnNavigationStructureChanged(object sender, EventArgs e)
+               {
+                       UpdateFlyoutMenu();
+               }
+
+               void UpdateFlyoutMenu()
+               {
+                       if (Element.FlyoutBehavior == FlyoutBehavior.Disabled)
+                               return;
+
+                       var flyoutItems = (Element as IShellController).GenerateFlyoutGrouping();
+                       int itemCount = 0;
+                       foreach (var item in flyoutItems)
+                       {
+                               itemCount += item.Count;
+                       }
+
+                       if (itemCount > 1)
+                       {
+                               InitializeNavigationDrawer();
+                               _navigationView.Build(flyoutItems);
+                       }
+                       else
+                       {
+                               DeinitializeNavigationView();
+                       }
+               }
+
+               void InitializeNavigationDrawer()
+               {
+                       if (_navigationView != null)
+                       {
+                               return;
+                       }
+
+                       _navigationView = new NavigationView(XForms.NativeParent)
+                       {
+                               AlignmentX = -1,
+                               AlignmentY = -1,
+                               WeightX = 1,
+                               WeightY = 1,
+                       };
+                       _navigationView.Show();
+                       _navigationView.ItemSelected += OnMenuItemSelected;
+
+                       _drawer.SetDrawerContent(_navigationView);
+               }
+
+               void OnNavigationDrawerToggled(object sender, EventArgs e)
+               {
+                       if (_drawer.IsOpen)
+                       {
+                               _navigationView.Activate();
+                       }
+                       else
+                       {
+                               _navigationView.Deactivate();
+
+                               var stack = (Element.CurrentItem.CurrentItem as ShellSection)?.Stack;
+                               var currentPage = stack?.LastOrDefault<Page>();
+
+                               if (currentPage == null)
+                               {
+                                       currentPage = (Element.CurrentItem.CurrentItem.CurrentItem as IShellContentController)?.Page;
+                               }
+
+                               if (currentPage != null)
+                               {
+                                       var renderer = Platform.GetOrCreateRenderer(currentPage);
+                                       (renderer as CirclePageRenderer)?.UpdateRotaryFocusObject();
+                               }
+                       }
+
+                       Element.SetValueFromRenderer(XShell.FlyoutIsPresentedProperty, _drawer.IsOpen);
+               }
+
+               void DeinitializeNavigationView()
+               {
+                       if (_navigationView == null)
+                               return;
+                       _drawer.SetDrawerContent(null);
+                       _navigationView.Unrealize();
+                       _navigationView = null;
+               }
+
+               void OnMenuItemSelected(object sender, SelectedItemChangedEventArgs e)
+               {
+                       ((IShellController)Element).OnFlyoutItemSelected(e.SelectedItem as Element);
+               }
+
+               void UpdateCurrentItem()
+               {
+                       ResetCurrentItem();
+                       if (Element.CurrentItem != null)
+                       {
+                               if (!_rendererCache.TryGetValue(Element.CurrentItem, out IShellItemRenderer renderer))
+                               {
+                                       renderer = ShellRendererFactory.Default.CreateItemRenderer(Element.CurrentItem);
+                                       _rendererCache[Element.CurrentItem] = renderer;
+                               }
+                               SetCurrentItem(renderer.NativeView);
+                       }
+               }
+
+               void UpdateFlyoutBehavior(bool init)
+               {
+                       if (init)
+                               return;
+
+                       if (Element.FlyoutBehavior == FlyoutBehavior.Disabled)
+                       {
+                               DeinitializeNavigationView();
+                       }
+                       else if (Element.FlyoutBehavior == FlyoutBehavior.Flyout)
+                       {
+                               UpdateFlyoutMenu();
+                       }
+                       else if (Element.FlyoutBehavior == FlyoutBehavior.Locked)
+                       {
+                               // Locked behavior is not supported on circularshell
+                       }
+               }
+
+               void UpdateFlyoutIcon(bool init)
+               {
+                       if (init && Element.FlyoutIcon == null)
+                               return;
+
+                       _drawer.UpdateDrawerIcon(Element.FlyoutIcon);
+               }
+
+               void UpdateFlyoutBackgroundColor(bool init)
+               {
+                       if (init && Element.FlyoutBackgroundColor.IsDefault)
+                               return;
+
+                       if (_navigationView != null)
+                       {
+                               _navigationView.BackgroundColor = Element.FlyoutBackgroundColor.ToNative();
+                       }
+               }
+
+               void UpdateFlyoutForegroundColor(bool init)
+               {
+                       if (init && CircularShell.GetFlyoutForegroundColor(Element).IsDefault)
+                               return;
+
+                       if (_navigationView != null)
+                       {
+                               _navigationView.ForegroundColor = CircularShell.GetFlyoutForegroundColor(Element).ToNative();
+                       }
+               }
+
+               void UpdateFlyoutIconBackgroundColor()
+               {
+                       _drawer.HandlerBackgroundColor = CircularShell.GetFlyoutIconBackgroundColor(Element).ToNative();
+               }
+
+               void UpdateFlyoutIsPresented()
+               {
+                       _drawer.IsOpen = Element.FlyoutIsPresented;
+               }
+
+               void SetCurrentItem(EvasObject item)
+               {
+                       _drawer.SetMainContent(item);
+               }
+
+               void ResetCurrentItem()
+               {
+                       _drawer.SetMainContent(null);
+               }
+       }
 }
\ No newline at end of file
index e24034c..5895d44 100644 (file)
@@ -2,52 +2,52 @@
 
 namespace Tizen.Wearable.CircularUI.Forms.Renderer
 {
-    public class ShellRendererFactory
-    {
-        static ShellRendererFactory _instance;
-        public static ShellRendererFactory Default
-        {
-            get
-            {
-                if (_instance == null)
-                {
-                    _instance = new ShellRendererFactory();
-                }
-                return _instance;
-            }
-            set
-            {
-                _instance = value;
-            }
+       public class ShellRendererFactory
+       {
+               static ShellRendererFactory _instance;
+               public static ShellRendererFactory Default
+               {
+                       get
+                       {
+                               if (_instance == null)
+                               {
+                                       _instance = new ShellRendererFactory();
+                               }
+                               return _instance;
+                       }
+                       set
+                       {
+                               _instance = value;
+                       }
 
-        }
+               }
 
-        public virtual IShellItemRenderer CreateItemRenderer(ShellItem item)
-        {
-            if (item.Items.Count == 1)
-            {
-                return CreateShellNavigationRenderer(item.CurrentItem);
-            }
-            return new ShellItemRenderer(item);
-        }
+               public virtual IShellItemRenderer CreateItemRenderer(ShellItem item)
+               {
+                       if (item.Items.Count == 1)
+                       {
+                               return CreateShellNavigationRenderer(item.CurrentItem);
+                       }
+                       return new ShellItemRenderer(item);
+               }
 
-        public virtual IShellItemRenderer CreateShellNavigationRenderer(ShellSection item)
-        {
-            return new ShellSectionNavigationRenderer(item);
-        }
+               public virtual IShellItemRenderer CreateShellNavigationRenderer(ShellSection item)
+               {
+                       return new ShellSectionNavigationRenderer(item);
+               }
 
-        public virtual IShellItemRenderer CreateItemRenderer(ShellSection item)
-        {
-            if (item.Items.Count == 1)
-            {
-                return CreateItemRenderer(item.CurrentItem);
-            }
-            return new ShellSectionItemsRenderer(item);
-        }
+               public virtual IShellItemRenderer CreateItemRenderer(ShellSection item)
+               {
+                       if (item.Items.Count == 1)
+                       {
+                               return CreateItemRenderer(item.CurrentItem);
+                       }
+                       return new ShellSectionItemsRenderer(item);
+               }
 
-        public virtual IShellItemRenderer CreateItemRenderer(ShellContent item)
-        {
-            return new ShellContentRenderer(item);
-        }
-    }
-}
+               public virtual IShellItemRenderer CreateItemRenderer(ShellContent item)
+               {
+                       return new ShellContentRenderer(item);
+               }
+       }
+}
\ No newline at end of file
index 7b6d684..3e72aca 100644 (file)
@@ -8,283 +8,283 @@ using XForms = Xamarin.Forms.Forms;
 
 namespace Tizen.Wearable.CircularUI.Forms.Renderer
 {
-    public class ShellSectionItemsRenderer : IShellItemRenderer
-    {
-        const int ItemMaxCount = 20;
-        const int OddMiddleItem = 10;
-        const int EvenMiddleItem = 11;
-
-        Box _mainLayout;
-        Index _indexIndicator;
-        Scroller _scroller;
-        Box _innerContainer;
-        List<ItemHolder> _items = new List<ItemHolder>();
-
-        int _currentIndex = -1;
-        Rect _lastLayoutBound;
-        int _updateByCode;
-
-
-        public ShellSectionItemsRenderer(ShellSection shellSection)
-        {
-            ShellSection = shellSection;
-            ShellSection.PropertyChanged += OnSectionPropertyChanged;
-            (ShellSection.Items as INotifyCollectionChanged).CollectionChanged += OnItemsChanged;
-            InitializeComponent();
-            UpdateItems();
-        }
-
-        public ShellSection ShellSection { get; protected set; }
-
-        public BaseShellItem Item => ShellSection;
-
-        public EvasObject NativeView => _mainLayout;
-
-        public void Dispose()
-        {
-            Dispose(true);
-        }
-
-        protected virtual void Dispose(bool disposing)
-        {
-            if (disposing)
-            {
-                _mainLayout?.Unrealize();
-                (ShellSection.Items as INotifyCollectionChanged).CollectionChanged -= OnItemsChanged;
-                ShellSection.PropertyChanged -= OnSectionPropertyChanged;
-            }
-        }
-
-        void InitializeComponent()
-        {   
-            _mainLayout = new Box(XForms.NativeParent)
-            {
-                AlignmentX = -1,
-                AlignmentY = -1,
-                WeightX = 1,
-                WeightY = 1,
-            };
-            _mainLayout.Show();
-            _mainLayout.SetLayoutCallback(OnLayout);
-
-            _indexIndicator = new Index(_mainLayout)
-            {
-                IsHorizontal = true,
-                AutoHide = false,
-                Style = IndexStyle.Circle,
-            };
-            _indexIndicator.Show();
-
-            _scroller = new Scroller(_mainLayout);
-            _scroller.PageScrolled += OnPageScrolled;
-            _scroller.DragStart += OnDragStarted;
-
-            // Disables the visibility of the scrollbar in both directions:
-            _scroller.HorizontalScrollBarVisiblePolicy = ScrollBarVisiblePolicy.Invisible;
-            _scroller.VerticalScrollBarVisiblePolicy = ScrollBarVisiblePolicy.Invisible;
-            // Sets the limit of scroll to one page maximum:
-            _scroller.HorizontalPageScrollLimit = 1;
-            _scroller.SetPageSize(1.0, 1.0);
-            _scroller.SetAlignment(-1, -1);
-            _scroller.SetWeight(1.0, 1.0);
-            _scroller.Show();
-
-            _innerContainer = new Box(_mainLayout);
-            _innerContainer.SetLayoutCallback(OnInnerLayoutUpdate);
-            _innerContainer.SetAlignment(-1, -1);
-            _innerContainer.SetWeight(1.0, 1.0);
-            _innerContainer.Show();
-            _scroller.SetContent(_innerContainer);
-
-            _mainLayout.PackEnd(_indexIndicator);
-            _mainLayout.PackEnd(_scroller);
-            _indexIndicator.StackAbove(_scroller);
-        }
-
-        void OnDragStarted(object sender, EventArgs e)
-        {
-            if (_currentIndex - 1 >= 0 && !_items[_currentIndex - 1].IsRealized)
-            {
-                RealizeItem(_items[_currentIndex - 1]);
-            }
-            if (_currentIndex + 1 < _items.Count && !_items[_currentIndex + 1].IsRealized)
-            {
-                RealizeItem(_items[_currentIndex + 1]);
-            }
-        }
-
-        void UpdateItems()
-        {
-            _items.Clear();
-            _indexIndicator.Clear();
-            _innerContainer.UnPackAll();
-            _lastLayoutBound = default(Rect);
-
-            foreach (var item in ShellSection.Items)
-            {
-                var indexItem = _indexIndicator.Append(null);
-                indexItem.Style = GetItemStyle(ShellSection.Items.Count, _items.Count);
-                _items.Add(new ItemHolder
-                {
-                    IsRealized = false,
-                    IndexItem = indexItem,
-                    Item = item
-                });
-            }
-            _indexIndicator.Update(0);
-            UpdateCurrentPage(ShellSection.Items.IndexOf(ShellSection.CurrentItem));
-        }
-
-        void RealizeItem(ItemHolder item)
-        {
-            var renderer = ShellRendererFactory.Default.CreateItemRenderer(item.Item);
-            renderer.NativeView.Show();
-            item.NativeView = renderer.NativeView;
-            item.IsRealized = true;
-            _innerContainer.PackEnd(item.NativeView);
-            item.NativeView.StackBelow(_indexIndicator);
-            item.NativeView.Geometry = item.Bound;
-        }
-
-        void UpdateCurrentPage(int index)
-        {
-            if (_currentIndex == index)
-                return;
-
-            _currentIndex = index;
-            UpdateCurrentIndexIndicator();
-            if (!_items[index].IsRealized)
-            {
-                RealizeItem(_items[index]);
-            }
-            UpdateFocusPolicy();
-        }
-
-        void UpdateFocusPolicy()
-        {
-            foreach (var item in _items)
-            {
-                if (item.IsRealized)
-                {
-                    if (item.NativeView is ElmSharp.Widget widget)
-                    {
-                        widget.AllowTreeFocus = (_items[_currentIndex] == item);
-                    }
-                }
-            }
-        }
-
-        void UpdateCurrentIndexIndicator()
-        {
-            if (_currentIndex >= 0 && _currentIndex < _items.Count)
-            {
-                _items[_currentIndex].IndexItem.Select(true);
-            }
-        }
-        void OnItemsChanged(object sender, NotifyCollectionChangedEventArgs e)
-        {
-            UpdateItems();
-        }
-
-        void OnPageScrolled(object sender, EventArgs e)
-        {
-            if (_updateByCode > 0)
-            {
-                return;
-            }
-
-            if (_currentIndex < 0 || ShellSection.Items.Count <= _currentIndex)
-            {
-                return;
-            }
-
-            UpdateCurrentPage(_scroller.HorizontalPageIndex);
-            var currentItem = ShellSection.Items[_currentIndex];
-            ShellSection.SetValueFromRenderer(ShellSection.CurrentItemProperty, currentItem);
-        }
-
-        void OnSectionPropertyChanged(object sender, PropertyChangedEventArgs e)
-        {
-            if (e.PropertyName == nameof(ShellSection.CurrentItem))
-            {
-                var newIndex = ShellSection.Items.IndexOf(ShellSection.CurrentItem);
-                if (_currentIndex != newIndex)
-                {
-                    UpdateCurrentPage(newIndex);
-                    _updateByCode++;
-                    _scroller.ScrollTo(newIndex, 0, false);
-                    _updateByCode--;
-                }
-            }
-        }
-
-        void OnLayout()
-        {
-            _indexIndicator.Geometry = _mainLayout.Geometry;
-            _scroller.Geometry = _mainLayout.Geometry;
-        }
-
-        void OnInnerLayoutUpdate()
-        {
-            if (_lastLayoutBound == _innerContainer.Geometry)
-            {
-                return;
-            }
-            _lastLayoutBound = _innerContainer.Geometry;
-
-            var layoutBound = _innerContainer.Geometry.Size;
-            int baseX = _innerContainer.Geometry.X;
-
-            Rect bound = _scroller.Geometry;
-            int index = 0;
-            foreach (var item in _items)
-            {
-                bound.X = baseX + index * bound.Width;
-                item.Bound = bound;
-                if (item.IsRealized)
-                {
-                    item.NativeView.Geometry = bound;
-                }
-                index++;
-            }
-            _innerContainer.MinimumWidth = _items.Count * bound.Width;
-
-            if (_items.Count > _currentIndex && _currentIndex >= 0)
-            {
-                _updateByCode++;
-                _scroller.ScrollTo(_currentIndex, 0, false);
-                _updateByCode--;
-            }
-        }
-
-        static string GetItemStyle(int itemCount, int offset)
-        {
-            string returnValue = string.Empty;
-            int startItem;
-            int styleNumber;
-
-            if (itemCount % 2 == 0)  //Item count is even.
-            {
-                startItem = EvenMiddleItem - itemCount / 2;
-                styleNumber = startItem + offset;
-                returnValue = "item/even_" + styleNumber;
-            }
-            else  //Item count is odd.
-            {
-                startItem = OddMiddleItem - itemCount / 2;
-                styleNumber = startItem + offset;
-                returnValue = "item/odd_" + styleNumber;
-            }
-            return returnValue;
-        }
-
-        class ItemHolder
-        {
-            public bool IsRealized { get; set; }
-            public Rect Bound { get; set; }
-            public EvasObject NativeView { get; set; }
-            public IndexItem IndexItem { get; set; }
-            public ShellContent Item { get; set; }
-        }
-    }
+       public class ShellSectionItemsRenderer : IShellItemRenderer
+       {
+               const int ItemMaxCount = 20;
+               const int OddMiddleItem = 10;
+               const int EvenMiddleItem = 11;
+
+               Box _mainLayout;
+               Index _indexIndicator;
+               Scroller _scroller;
+               Box _innerContainer;
+               List<ItemHolder> _items = new List<ItemHolder>();
+
+               int _currentIndex = -1;
+               Rect _lastLayoutBound;
+               int _updateByCode;
+
+
+               public ShellSectionItemsRenderer(ShellSection shellSection)
+               {
+                       ShellSection = shellSection;
+                       ShellSection.PropertyChanged += OnSectionPropertyChanged;
+                       (ShellSection.Items as INotifyCollectionChanged).CollectionChanged += OnItemsChanged;
+                       InitializeComponent();
+                       UpdateItems();
+               }
+
+               public ShellSection ShellSection { get; protected set; }
+
+               public BaseShellItem Item => ShellSection;
+
+               public EvasObject NativeView => _mainLayout;
+
+               public void Dispose()
+               {
+                       Dispose(true);
+               }
+
+               protected virtual void Dispose(bool disposing)
+               {
+                       if (disposing)
+                       {
+                               _mainLayout?.Unrealize();
+                               (ShellSection.Items as INotifyCollectionChanged).CollectionChanged -= OnItemsChanged;
+                               ShellSection.PropertyChanged -= OnSectionPropertyChanged;
+                       }
+               }
+
+               void InitializeComponent()
+               {
+                       _mainLayout = new Box(XForms.NativeParent)
+                       {
+                               AlignmentX = -1,
+                               AlignmentY = -1,
+                               WeightX = 1,
+                               WeightY = 1,
+                       };
+                       _mainLayout.Show();
+                       _mainLayout.SetLayoutCallback(OnLayout);
+
+                       _indexIndicator = new Index(_mainLayout)
+                       {
+                               IsHorizontal = true,
+                               AutoHide = false,
+                               Style = IndexStyle.Circle,
+                       };
+                       _indexIndicator.Show();
+
+                       _scroller = new Scroller(_mainLayout);
+                       _scroller.PageScrolled += OnPageScrolled;
+                       _scroller.DragStart += OnDragStarted;
+
+                       // Disables the visibility of the scrollbar in both directions:
+                       _scroller.HorizontalScrollBarVisiblePolicy = ScrollBarVisiblePolicy.Invisible;
+                       _scroller.VerticalScrollBarVisiblePolicy = ScrollBarVisiblePolicy.Invisible;
+                       // Sets the limit of scroll to one page maximum:
+                       _scroller.HorizontalPageScrollLimit = 1;
+                       _scroller.SetPageSize(1.0, 1.0);
+                       _scroller.SetAlignment(-1, -1);
+                       _scroller.SetWeight(1.0, 1.0);
+                       _scroller.Show();
+
+                       _innerContainer = new Box(_mainLayout);
+                       _innerContainer.SetLayoutCallback(OnInnerLayoutUpdate);
+                       _innerContainer.SetAlignment(-1, -1);
+                       _innerContainer.SetWeight(1.0, 1.0);
+                       _innerContainer.Show();
+                       _scroller.SetContent(_innerContainer);
+
+                       _mainLayout.PackEnd(_indexIndicator);
+                       _mainLayout.PackEnd(_scroller);
+                       _indexIndicator.StackAbove(_scroller);
+               }
+
+               void OnDragStarted(object sender, EventArgs e)
+               {
+                       if (_currentIndex - 1 >= 0 && !_items[_currentIndex - 1].IsRealized)
+                       {
+                               RealizeItem(_items[_currentIndex - 1]);
+                       }
+                       if (_currentIndex + 1 < _items.Count && !_items[_currentIndex + 1].IsRealized)
+                       {
+                               RealizeItem(_items[_currentIndex + 1]);
+                       }
+               }
+
+               void UpdateItems()
+               {
+                       _items.Clear();
+                       _indexIndicator.Clear();
+                       _innerContainer.UnPackAll();
+                       _lastLayoutBound = default(Rect);
+
+                       foreach (var item in ShellSection.Items)
+                       {
+                               var indexItem = _indexIndicator.Append(null);
+                               indexItem.Style = GetItemStyle(ShellSection.Items.Count, _items.Count);
+                               _items.Add(new ItemHolder
+                               {
+                                       IsRealized = false,
+                                       IndexItem = indexItem,
+                                       Item = item
+                               });
+                       }
+                       _indexIndicator.Update(0);
+                       UpdateCurrentPage(ShellSection.Items.IndexOf(ShellSection.CurrentItem));
+               }
+
+               void RealizeItem(ItemHolder item)
+               {
+                       var renderer = ShellRendererFactory.Default.CreateItemRenderer(item.Item);
+                       renderer.NativeView.Show();
+                       item.NativeView = renderer.NativeView;
+                       item.IsRealized = true;
+                       _innerContainer.PackEnd(item.NativeView);
+                       item.NativeView.StackBelow(_indexIndicator);
+                       item.NativeView.Geometry = item.Bound;
+               }
+
+               void UpdateCurrentPage(int index)
+               {
+                       if (_currentIndex == index)
+                               return;
+
+                       _currentIndex = index;
+                       UpdateCurrentIndexIndicator();
+                       if (!_items[index].IsRealized)
+                       {
+                               RealizeItem(_items[index]);
+                       }
+                       UpdateFocusPolicy();
+               }
+
+               void UpdateFocusPolicy()
+               {
+                       foreach (var item in _items)
+                       {
+                               if (item.IsRealized)
+                               {
+                                       if (item.NativeView is ElmSharp.Widget widget)
+                                       {
+                                               widget.AllowTreeFocus = (_items[_currentIndex] == item);
+                                       }
+                               }
+                       }
+               }
+
+               void UpdateCurrentIndexIndicator()
+               {
+                       if (_currentIndex >= 0 && _currentIndex < _items.Count)
+                       {
+                               _items[_currentIndex].IndexItem.Select(true);
+                       }
+               }
+               void OnItemsChanged(object sender, NotifyCollectionChangedEventArgs e)
+               {
+                       UpdateItems();
+               }
+
+               void OnPageScrolled(object sender, EventArgs e)
+               {
+                       if (_updateByCode > 0)
+                       {
+                               return;
+                       }
+
+                       if (_currentIndex < 0 || ShellSection.Items.Count <= _currentIndex)
+                       {
+                               return;
+                       }
+
+                       UpdateCurrentPage(_scroller.HorizontalPageIndex);
+                       var currentItem = ShellSection.Items[_currentIndex];
+                       ShellSection.SetValueFromRenderer(ShellSection.CurrentItemProperty, currentItem);
+               }
+
+               void OnSectionPropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       if (e.PropertyName == nameof(ShellSection.CurrentItem))
+                       {
+                               var newIndex = ShellSection.Items.IndexOf(ShellSection.CurrentItem);
+                               if (_currentIndex != newIndex)
+                               {
+                                       UpdateCurrentPage(newIndex);
+                                       _updateByCode++;
+                                       _scroller.ScrollTo(newIndex, 0, false);
+                                       _updateByCode--;
+                               }
+                       }
+               }
+
+               void OnLayout()
+               {
+                       _indexIndicator.Geometry = _mainLayout.Geometry;
+                       _scroller.Geometry = _mainLayout.Geometry;
+               }
+
+               void OnInnerLayoutUpdate()
+               {
+                       if (_lastLayoutBound == _innerContainer.Geometry)
+                       {
+                               return;
+                       }
+                       _lastLayoutBound = _innerContainer.Geometry;
+
+                       var layoutBound = _innerContainer.Geometry.Size;
+                       int baseX = _innerContainer.Geometry.X;
+
+                       Rect bound = _scroller.Geometry;
+                       int index = 0;
+                       foreach (var item in _items)
+                       {
+                               bound.X = baseX + index * bound.Width;
+                               item.Bound = bound;
+                               if (item.IsRealized)
+                               {
+                                       item.NativeView.Geometry = bound;
+                               }
+                               index++;
+                       }
+                       _innerContainer.MinimumWidth = _items.Count * bound.Width;
+
+                       if (_items.Count > _currentIndex && _currentIndex >= 0)
+                       {
+                               _updateByCode++;
+                               _scroller.ScrollTo(_currentIndex, 0, false);
+                               _updateByCode--;
+                       }
+               }
+
+               static string GetItemStyle(int itemCount, int offset)
+               {
+                       string returnValue = string.Empty;
+                       int startItem;
+                       int styleNumber;
+
+                       if (itemCount % 2 == 0)  //Item count is even.
+                       {
+                               startItem = EvenMiddleItem - itemCount / 2;
+                               styleNumber = startItem + offset;
+                               returnValue = "item/even_" + styleNumber;
+                       }
+                       else  //Item count is odd.
+                       {
+                               startItem = OddMiddleItem - itemCount / 2;
+                               styleNumber = startItem + offset;
+                               returnValue = "item/odd_" + styleNumber;
+                       }
+                       return returnValue;
+               }
+
+               class ItemHolder
+               {
+                       public bool IsRealized { get; set; }
+                       public Rect Bound { get; set; }
+                       public EvasObject NativeView { get; set; }
+                       public IndexItem IndexItem { get; set; }
+                       public ShellContent Item { get; set; }
+               }
+       }
 }
\ No newline at end of file
index 7a860e7..ca5d705 100644 (file)
@@ -10,199 +10,199 @@ using XForms = Xamarin.Forms.Forms;
 
 namespace Tizen.Wearable.CircularUI.Forms.Renderer
 {
-    class SimpleViewStack : Box
-    {
-        EvasObject _lastTop;
-
-        public SimpleViewStack(EvasObject parent) : base(parent)
-        {
-            InternalStack = new List<EvasObject>();
-            SetLayoutCallback(OnLayout);
-        }
-
-        List<EvasObject> InternalStack { get; set; }
-
-        public IReadOnlyList<EvasObject> Stack => InternalStack;
-
-        public void Push(EvasObject view)
-        {
-            InternalStack.Add(view);
-            PackEnd(view);
-            UpdateTopView();
-        }
-
-        public void Pop()
-        {
-            if (_lastTop != null)
-            {
-                var tobeRemoved = _lastTop;
-                InternalStack.Remove(tobeRemoved);
-                UnPack(tobeRemoved);
-                UpdateTopView();
-
-                // if Pop was called by removed page,
-                // Unrealize cause deletation of NativeCallback, it could be a cause of crash
-                Device.BeginInvokeOnMainThread(() =>
-                {
-                    tobeRemoved.Unrealize();
-                });
-            }
-        }
-
-        public void PopToRoot()
-        {
-            while (InternalStack.Count > 1)
-            {
-                Pop();
-            }
-        }
-
-        public void Insert(EvasObject before, EvasObject view)
-        {
-            view.Hide();
-            var idx = InternalStack.IndexOf(before);
-            InternalStack.Insert(idx, view);
-            PackEnd(view);
-            UpdateTopView();
-        }
-
-        public void Remove(EvasObject view)
-        {
-            InternalStack.Remove(view);
-            UnPack(view);
-            UpdateTopView();
-            Device.BeginInvokeOnMainThread(() =>
-            {
-                view?.Unrealize();
-            });
-        }
-
-        void UpdateTopView()
-        {
-            if (_lastTop != InternalStack.LastOrDefault())
-            {
-                _lastTop?.Hide();
-                _lastTop = InternalStack.LastOrDefault();
-                _lastTop.Show();
-            }
-        }
-
-        void OnLayout()
-        {
-            foreach (var view in Stack)
-            {
-                view.Geometry = Geometry;
-            }
-        }
-        
-    }
-
-    public class ShellSectionNavigationRenderer : IShellItemRenderer
-    {
-        SimpleViewStack _viewStack;
-        IShellItemRenderer _rootPageRenderer;
-
-        public ShellSectionNavigationRenderer(ShellSection item)
-        {
-            ShellSection = item;
-            (ShellSection as IShellSectionController).NavigationRequested += OnNavigationRequested;
-            InitializeComponent();
-        }
-
-        public ShellSection ShellSection { get; protected set; }
-
-        public BaseShellItem Item => ShellSection;
-
-        public EvasObject NativeView => _viewStack;
-
-        public void Dispose()
-        {
-            Dispose(true);
-        }
-
-        protected virtual void Dispose(bool disposing)
-        {
-            if (disposing)
-            {
-                _rootPageRenderer?.Dispose();
-                _viewStack?.Unrealize();
-                (ShellSection as IShellSectionController).NavigationRequested -= OnNavigationRequested;
-            }
-        }
-
-        void InitializeComponent()
-        {
-            _viewStack = new SimpleViewStack(XForms.NativeParent);
-            _viewStack.Show();
-
-            _rootPageRenderer = ShellRendererFactory.Default.CreateItemRenderer(ShellSection);
-            _viewStack.Push(_rootPageRenderer.NativeView);
-        }
-
-        void OnInsertRequest(NavigationRequestedEventArgs request)
-        {
-            var before = Platform.GetRenderer(request.BeforePage)?.NativeView ?? null;
-            if (before == null)
-            {
-                request.Task = Task.FromException<bool>(new ArgumentException("Can't found page on stack", nameof(request.BeforePage)));
-                return;
-            }
-            var renderer = Platform.GetOrCreateRenderer(request.Page);
-            _viewStack.Insert(before, renderer.NativeView);
-            request.Task = Task.FromResult(true);
-        }
-
-        void OnPushRequest(NavigationRequestedEventArgs request)
-        {
-            var renderer = Platform.GetOrCreateRenderer(request.Page);
-            _viewStack.Push(renderer.NativeView);
-            request.Task = Task.FromResult(true);
-        }
-
-        void OnPopRequest(NavigationRequestedEventArgs request)
-        {
-            _viewStack.Pop();
-            request.Task = Task.FromResult(true);
-        }
-
-        void OnPopToRootRequest(NavigationRequestedEventArgs request)
-        {
-            _viewStack.PopToRoot();
-            request.Task = Task.FromResult(true);
-        }
-
-        void OnRemoveRequest(NavigationRequestedEventArgs request)
-        {
-            var renderer = Platform.GetRenderer(request.Page);
-            if (renderer == null)
-            {
-                request.Task = Task.FromException<bool>(new ArgumentException("Can't found page on stack", nameof(request.Page)));
-                return;
-            }
-            _viewStack.Remove(renderer.NativeView);
-            request.Task = Task.FromResult(true);
-        }
-
-        void OnNavigationRequested(object sender, NavigationRequestedEventArgs e)
-        {
-            switch (e.RequestType)
-            {
-                case NavigationRequestType.Insert:
-                    OnInsertRequest(e);
-                    break;
-                case NavigationRequestType.Push:
-                    OnPushRequest(e);
-                    break;
-                case NavigationRequestType.Pop:
-                    OnPopRequest(e);
-                    break;
-                case NavigationRequestType.PopToRoot:
-                    OnPopToRootRequest(e);
-                    break;
-                case NavigationRequestType.Remove:
-                    OnRemoveRequest(e);
-                    break;
-            }
-        }
-    }
-}
+       class SimpleViewStack : Box
+       {
+               EvasObject _lastTop;
+
+               public SimpleViewStack(EvasObject parent) : base(parent)
+               {
+                       InternalStack = new List<EvasObject>();
+                       SetLayoutCallback(OnLayout);
+               }
+
+               List<EvasObject> InternalStack { get; set; }
+
+               public IReadOnlyList<EvasObject> Stack => InternalStack;
+
+               public void Push(EvasObject view)
+               {
+                       InternalStack.Add(view);
+                       PackEnd(view);
+                       UpdateTopView();
+               }
+
+               public void Pop()
+               {
+                       if (_lastTop != null)
+                       {
+                               var tobeRemoved = _lastTop;
+                               InternalStack.Remove(tobeRemoved);
+                               UnPack(tobeRemoved);
+                               UpdateTopView();
+
+                               // if Pop was called by removed page,
+                               // Unrealize cause deletation of NativeCallback, it could be a cause of crash
+                               Device.BeginInvokeOnMainThread(() =>
+                               {
+                                       tobeRemoved.Unrealize();
+                               });
+                       }
+               }
+
+               public void PopToRoot()
+               {
+                       while (InternalStack.Count > 1)
+                       {
+                               Pop();
+                       }
+               }
+
+               public void Insert(EvasObject before, EvasObject view)
+               {
+                       view.Hide();
+                       var idx = InternalStack.IndexOf(before);
+                       InternalStack.Insert(idx, view);
+                       PackEnd(view);
+                       UpdateTopView();
+               }
+
+               public void Remove(EvasObject view)
+               {
+                       InternalStack.Remove(view);
+                       UnPack(view);
+                       UpdateTopView();
+                       Device.BeginInvokeOnMainThread(() =>
+                       {
+                               view?.Unrealize();
+                       });
+               }
+
+               void UpdateTopView()
+               {
+                       if (_lastTop != InternalStack.LastOrDefault())
+                       {
+                               _lastTop?.Hide();
+                               _lastTop = InternalStack.LastOrDefault();
+                               _lastTop.Show();
+                       }
+               }
+
+               void OnLayout()
+               {
+                       foreach (var view in Stack)
+                       {
+                               view.Geometry = Geometry;
+                       }
+               }
+
+       }
+
+       public class ShellSectionNavigationRenderer : IShellItemRenderer
+       {
+               SimpleViewStack _viewStack;
+               IShellItemRenderer _rootPageRenderer;
+
+               public ShellSectionNavigationRenderer(ShellSection item)
+               {
+                       ShellSection = item;
+                       (ShellSection as IShellSectionController).NavigationRequested += OnNavigationRequested;
+                       InitializeComponent();
+               }
+
+               public ShellSection ShellSection { get; protected set; }
+
+               public BaseShellItem Item => ShellSection;
+
+               public EvasObject NativeView => _viewStack;
+
+               public void Dispose()
+               {
+                       Dispose(true);
+               }
+
+               protected virtual void Dispose(bool disposing)
+               {
+                       if (disposing)
+                       {
+                               _rootPageRenderer?.Dispose();
+                               _viewStack?.Unrealize();
+                               (ShellSection as IShellSectionController).NavigationRequested -= OnNavigationRequested;
+                       }
+               }
+
+               void InitializeComponent()
+               {
+                       _viewStack = new SimpleViewStack(XForms.NativeParent);
+                       _viewStack.Show();
+
+                       _rootPageRenderer = ShellRendererFactory.Default.CreateItemRenderer(ShellSection);
+                       _viewStack.Push(_rootPageRenderer.NativeView);
+               }
+
+               void OnInsertRequest(NavigationRequestedEventArgs request)
+               {
+                       var before = Platform.GetRenderer(request.BeforePage)?.NativeView ?? null;
+                       if (before == null)
+                       {
+                               request.Task = Task.FromException<bool>(new ArgumentException("Can't found page on stack", nameof(request.BeforePage)));
+                               return;
+                       }
+                       var renderer = Platform.GetOrCreateRenderer(request.Page);
+                       _viewStack.Insert(before, renderer.NativeView);
+                       request.Task = Task.FromResult(true);
+               }
+
+               void OnPushRequest(NavigationRequestedEventArgs request)
+               {
+                       var renderer = Platform.GetOrCreateRenderer(request.Page);
+                       _viewStack.Push(renderer.NativeView);
+                       request.Task = Task.FromResult(true);
+               }
+
+               void OnPopRequest(NavigationRequestedEventArgs request)
+               {
+                       _viewStack.Pop();
+                       request.Task = Task.FromResult(true);
+               }
+
+               void OnPopToRootRequest(NavigationRequestedEventArgs request)
+               {
+                       _viewStack.PopToRoot();
+                       request.Task = Task.FromResult(true);
+               }
+
+               void OnRemoveRequest(NavigationRequestedEventArgs request)
+               {
+                       var renderer = Platform.GetRenderer(request.Page);
+                       if (renderer == null)
+                       {
+                               request.Task = Task.FromException<bool>(new ArgumentException("Can't found page on stack", nameof(request.Page)));
+                               return;
+                       }
+                       _viewStack.Remove(renderer.NativeView);
+                       request.Task = Task.FromResult(true);
+               }
+
+               void OnNavigationRequested(object sender, NavigationRequestedEventArgs e)
+               {
+                       switch (e.RequestType)
+                       {
+                               case NavigationRequestType.Insert:
+                                       OnInsertRequest(e);
+                                       break;
+                               case NavigationRequestType.Push:
+                                       OnPushRequest(e);
+                                       break;
+                               case NavigationRequestType.Pop:
+                                       OnPopRequest(e);
+                                       break;
+                               case NavigationRequestType.PopToRoot:
+                                       OnPopToRootRequest(e);
+                                       break;
+                               case NavigationRequestType.Remove:
+                                       OnRemoveRequest(e);
+                                       break;
+                       }
+               }
+       }
+}
\ No newline at end of file
index e7bbc2f..6b5e569 100644 (file)
@@ -15,6 +15,7 @@
  */
 using ElmSharp;
 using System.IO;
+using Circular = Tizen.Wearable.CircularUI.Forms.FormsCircularUI;
 
 namespace Tizen.Wearable.CircularUI.Forms.Renderer
 {
@@ -22,28 +23,28 @@ namespace Tizen.Wearable.CircularUI.Forms.Renderer
     {
         const string CircularUITheme = "/usr/share/elm-sharp/tizen-circular-ui-theme.edj";
 
-        public static string AppResourcePath { get; private set; }
+               public static string AppResourcePath { get; private set; }
 
-        public static bool IsInitialized { get; private set; }
+               public static bool IsInitialized { get; private set; }
 
-        public static void Initialize(string resourcePath)
-        {
-            if (!IsInitialized)
-            {
-                AppResourcePath = resourcePath;
-                IsInitialized = true;
-                AddThemeOverlay(CircularUITheme);
-            }
-        }
+               public static void Initialize(string resourcePath)
+               {
+                       if (!IsInitialized)
+                       {
+                               AppResourcePath = resourcePath;
+                               IsInitialized = true;
+                               AddThemeOverlay(CircularUITheme);
+                       }
+               }
 
         public static void AddThemeOverlay(string themeFilePath)
         {
             if (!IsInitialized)
             {
-                Log.Error(FormsCircularUI.Tag, $"ThemeLoader is not initialized properly");
+                                       Log.Error(Circular.Tag, $"ThemeLoader is not initialized properly");
                 return;
             }
             Elementary.AddThemeOverlay(themeFilePath);
         }
     }
-}
+}
\ No newline at end of file
old mode 100644 (file)
new mode 100755 (executable)
index 1d54156..f3e1f09
@@ -23,6 +23,7 @@ using System.Text;
 using Xamarin.Forms;
 using Xamarin.Forms.Platform.Tizen;
 using XForms = Xamarin.Forms.Forms;
+using ERotaryEventManager = ElmSharp.Wearable.RotaryEventManager;
 
 [assembly: ExportEffect(typeof(Tizen.Wearable.CircularUI.Forms.Renderer.TizenCircleSurfaceEffect), "CircleSurfaceEffect")]
 namespace Tizen.Wearable.CircularUI.Forms.Renderer
@@ -151,7 +152,7 @@ namespace Tizen.Wearable.CircularUI.Forms.Renderer
                        if (focusable is IRotaryEventReceiver)
                        {
                                _rotaryReceiver = focusable as IRotaryEventReceiver;
-                               RotaryEventManager.Rotated += OnRotaryEventChanged;
+                               ERotaryEventManager.Rotated += OnRotaryEventChanged;
                        }
                        else if (focusable is IRotaryFocusable)
                        {
@@ -169,7 +170,7 @@ namespace Tizen.Wearable.CircularUI.Forms.Renderer
                        if (focusable is IRotaryEventReceiver)
                        {
                                _rotaryReceiver = null;
-                               RotaryEventManager.Rotated -= OnRotaryEventChanged;
+                               ERotaryEventManager.Rotated -= OnRotaryEventChanged;
                        }
                        else if (focusable is IRotaryFocusable)
                        {
index 078ab7a..f5141a2 100644 (file)
@@ -18,6 +18,7 @@ using System;
 using Xamarin.Forms;
 using Xamarin.Forms.Platform.Tizen;
 using XForms = Xamarin.Forms.Forms;
+using Circular = Tizen.Wearable.CircularUI.Forms.FormsCircularUI;
 
 [assembly: Dependency(typeof(Tizen.Wearable.CircularUI.Forms.Renderer.TwoButtonPopupImplementation))]
 
@@ -205,7 +206,7 @@ namespace Tizen.Wearable.CircularUI.Forms.Renderer
                {
                        if (!XForms.IsInitialized)
                        {
-                               Log.Debug(FormsCircularUI.Tag, "Tizen Forms is not initialized");
+                               Log.Debug(Circular.Tag, "Tizen Forms is not initialized");
                                return;
                        }
 
@@ -263,7 +264,7 @@ namespace Tizen.Wearable.CircularUI.Forms.Renderer
 
                                if (_firstButtonBgColor != Color.Default)
                                {
-                                       Log.Debug(FormsCircularUI.Tag, $"TwoButtonPopup set first button background color:{_firstButtonBgColor.ToNative()}");
+                                       Log.Debug(Circular.Tag, $"TwoButtonPopup set first button background color:{_firstButtonBgColor.ToNative()}");
                                        _firstButton.BackgroundColor = _firstButtonBgColor.ToNative();
                                }
                        }
@@ -309,7 +310,7 @@ namespace Tizen.Wearable.CircularUI.Forms.Renderer
 
                                if (_secondButtonBgColor != Color.Default)
                                {
-                                       Log.Debug(FormsCircularUI.Tag, $"TwoButtonPopup set second button background color:{_secondButtonBgColor.ToNative()}");
+                                       Log.Debug(Circular.Tag, $"TwoButtonPopup set second button background color:{_secondButtonBgColor.ToNative()}");
                                        _secondButton.BackgroundColor = _secondButtonBgColor.ToNative();
                                }
 
@@ -333,9 +334,9 @@ namespace Tizen.Wearable.CircularUI.Forms.Renderer
                void UpdateTitle()
                {
                        string title = _title?.Replace("&", "&amp;")
-                                                                 .Replace("<", "&lt;")
-                                                                 .Replace(">", "&gt;")
-                                                                 .Replace(Environment.NewLine, "<br>");
+                                                               .Replace("<", "&lt;")
+                                                               .Replace(">", "&gt;")
+                                                               .Replace(Environment.NewLine, "<br>");
                        _layout.SetPartText("elm.text.title", title);
                }
 
@@ -394,7 +395,7 @@ namespace Tizen.Wearable.CircularUI.Forms.Renderer
 
                void OnDismissed(object sender, EventArgs e)
                {
-                       Log.Debug(FormsCircularUI.Tag, $"OnDismissed called");
+                       Log.Debug(Circular.Tag, $"OnDismissed called");
                        Dispose();
                }
        }
index fca5e65..ee280b6 100644 (file)
@@ -17,6 +17,7 @@
 using System;
 using System.Collections.Generic;
 using Tizen.Applications;
+using Circular = Tizen.Wearable.CircularUI.Forms.FormsCircularUI;
 
 namespace Tizen.Wearable.CircularUI.Forms.Renderer.Widget
 {
@@ -35,7 +36,7 @@ namespace Tizen.Wearable.CircularUI.Forms.Renderer.Widget
                public override void Exit()
                {
                        // It is intended, disable to terminate.
-                       Log.Debug(FormsCircularUI.Tag, "Exit() called!");
+       Log.Debug(Circular.Tag, "Exit() called!");
                }
        }
 }
\ No newline at end of file
index 8e2a260..814a88a 100644 (file)
@@ -24,7 +24,7 @@ namespace Tizen.Wearable.CircularUI.Forms
        /// The CircleDateTimeSelector is a view that can change the value by bezel action by touching each item of "Year: Month: Day" and "Hour: Minute: AM / PM"
        /// </summary>
        /// <since_tizen> 4 </since_tizen>
-       public class CircleDateTimeSelector : Xamarin.Forms.View, IRotaryFocusable
+       public class CircleDateTimeSelector : Xamarin.Forms.View, IRotaryFocusable, ICircleSurfaceConsumer
        {
                /// <summary>
                /// BindableProperty. Identifies the MarkerColor bindable property.
@@ -156,6 +156,7 @@ namespace Tizen.Wearable.CircularUI.Forms
                        get => (bool)GetValue(IsVisibleOfMinuteProperty);
                        set => SetValue(IsVisibleOfMinuteProperty, value);
                }
+
                /// <summary>
                /// Gets or sets a boolean value that indicates whether the AmPm field type is visible.
                /// </summary>
@@ -165,5 +166,11 @@ namespace Tizen.Wearable.CircularUI.Forms
                        get => (bool)GetValue(IsVisibleOfAmPmProperty);
                        set => SetValue(IsVisibleOfAmPmProperty, value);
                }
+
+               /// <summary>
+               /// Gets or sets a CircleSurfaceProvider.
+               /// </summary>
+               /// <since_tizen> 4 </since_tizen>
+               public ICircleSurfaceProvider CircleSurfaceProvider { get; set; }
        }
 }
\ No newline at end of file
index c5e79b5..b234140 100644 (file)
@@ -24,7 +24,7 @@ namespace Tizen.Wearable.CircularUI.Forms
        /// You can move the list through bezel action.
        /// </summary>
        /// <since_tizen> 4 </since_tizen>
-       public class CircleListView : ListView, IRotaryFocusable
+       public class CircleListView : ListView, IRotaryFocusable, ICircleSurfaceConsumer
        {
                /// <summary>
                /// BindableProperty. Identifies the Header, Footer cancel the Fish Eye Effect or not.
@@ -60,5 +60,11 @@ namespace Tizen.Wearable.CircularUI.Forms
                        get => (Color)GetValue(BarColorProperty);
                        set => SetValue(BarColorProperty, value);
                }
+
+               /// <summary>
+               /// Gets or sets a CircleSurfaceProvider.
+               /// </summary>
+               /// <since_tizen> 4 </since_tizen>
+               public ICircleSurfaceProvider CircleSurfaceProvider { get; set; }
        }
 }
\ No newline at end of file
index 77c8668..85beff4 100644 (file)
@@ -27,7 +27,7 @@ namespace Tizen.Wearable.CircularUI.Forms
        /// It has an ActionButton, and can use the MenuItem type as text, icon, command, and so on.
        /// </summary>
        /// <since_tizen> 4 </since_tizen>
-       public class CirclePage : ContentPage
+       public class CirclePage : ContentPage, ICircleSurfaceProvider
        {
                /// <summary>
                /// BindableProperty. Identifies the ActionButton bindable property.
@@ -65,6 +65,9 @@ namespace Tizen.Wearable.CircularUI.Forms
                [EditorBrowsable(EditorBrowsableState.Never)]
                public bool Appeared { get; set; }
 
+               [EditorBrowsable(EditorBrowsableState.Never)]
+               public object CircleSurface { get; set; }
+
                /// <summary>
                /// Gets or sets ActionButton that presents a menu item and associates it with a command
                /// </summary>
index 724ec0f..a71c37b 100644 (file)
@@ -23,7 +23,7 @@ namespace Tizen.Wearable.CircularUI.Forms
        /// The CircleScrollView is extension of Xamarin.Forms.ScrollView.
        /// </summary>
        /// <since_tizen> 4 </since_tizen>
-       public class CircleScrollView : ScrollView, IRotaryFocusable
+       public class CircleScrollView : ScrollView, IRotaryFocusable, ICircleSurfaceConsumer
        {
                /// <summary>
                /// BindableProperty. Identifies the Header, Footer cancel the Fish Eye Effect or not.
@@ -40,5 +40,11 @@ namespace Tizen.Wearable.CircularUI.Forms
                        get => (Color)GetValue(BarColorProperty);
                        set => SetValue(BarColorProperty, value);
                }
+
+               /// <summary>
+               /// Gets or sets a CircleSurfaceProvider.
+               /// </summary>
+               /// <since_tizen> 4 </since_tizen>
+               public ICircleSurfaceProvider CircleSurfaceProvider { get; set; }
        }
 }
\ No newline at end of file
index f1323d8..1e0cf39 100644 (file)
@@ -23,7 +23,7 @@ namespace Tizen.Wearable.CircularUI.Forms
        /// The CircleStepper is a class that extends Xamarin.Forms.Stepper for Circular UI.
        /// </summary>
        /// <since_tizen> 4 </since_tizen>
-       public class CircleStepper : Xamarin.Forms.Stepper, IRotaryFocusable
+       public class CircleStepper : Stepper, IRotaryFocusable, ICircleSurfaceConsumer
        {
                /// <summary>
                /// BindableProperty. Identifies the MarkerColor bindable property.
@@ -50,61 +50,67 @@ namespace Tizen.Wearable.CircularUI.Forms
                public static readonly BindableProperty TitleProperty = BindableProperty.Create(nameof(Title), typeof(string), typeof(CircleStepper), null);
 
                /// <summary>
-        /// BindableProperty. Identifies whether min/max value is wrapped or not.
-        /// </summary>
-        /// <since_tizen> 4 </since_tizen>
-        public static readonly BindableProperty IsWrapEnabledProperty = BindableProperty.Create(nameof(IsWrapEnabled), typeof(bool), typeof(CircleStepper), true);
+               /// BindableProperty. Identifies whether min/max value is wrapped or not.
+               /// </summary>
+               /// <since_tizen> 4 </since_tizen>
+               public static readonly BindableProperty IsWrapEnabledProperty = BindableProperty.Create(nameof(IsWrapEnabled), typeof(bool), typeof(CircleStepper), true);
 
-        /// <summary>
+               /// <summary>
                /// Gets or sets Marker color
                /// </summary>
                /// <since_tizen> 4 </since_tizen>
                [Obsolete("MarkerColor is obsolete as of Tizen.NET version 4.0.0 and is no longer supported")]
-        public Color MarkerColor
-        {
-            get => (Color)GetValue(MarkerColorProperty);
-            set => SetValue(MarkerColorProperty, value);
-        }
+               public Color MarkerColor
+               {
+                       get => (Color)GetValue(MarkerColorProperty);
+                       set => SetValue(MarkerColorProperty, value);
+               }
 
                /// <summary>
                /// Gets or sets length of Marker
                /// </summary>
                /// <since_tizen> 4 </since_tizen>
                [Obsolete("MarkerLineWidth is obsolete as of Tizen.NET version 4.0.0 and is no longer supported")]
-        public int MarkerLineWidth
-        {
-            get => (int)GetValue(MarkerLineWidthProperty);
-            set => SetValue(MarkerLineWidthProperty, value);
-        }
+               public int MarkerLineWidth
+               {
+                       get => (int)GetValue(MarkerLineWidthProperty);
+                       set => SetValue(MarkerLineWidthProperty, value);
+               }
 
                /// <summary>
                /// Gets or sets format in which Value is shown
                /// </summary>
                /// <since_tizen> 4 </since_tizen>
-        public string LabelFormat
-        {
-            get => (string)GetValue(LabelFormatProperty);
-            set => SetValue(LabelFormatProperty, value);
-        }
+               public string LabelFormat
+               {
+                       get => (string)GetValue(LabelFormatProperty);
+                       set => SetValue(LabelFormatProperty, value);
+               }
 
                /// <summary>
                /// Gets or sets title
                /// </summary>
                /// <since_tizen> 4 </since_tizen>
-        public string Title
-        {
-            get => (string)GetValue(TitleProperty);
-            set => SetValue(TitleProperty, value);
-        }
+               public string Title
+               {
+                       get => (string)GetValue(TitleProperty);
+                       set => SetValue(TitleProperty, value);
+               }
+
+               /// <summary>
+               /// Gets or sets a status of Value is wrapped.
+               /// </summary>
+               /// <since_tizen> 4 </since_tizen>
+               public bool IsWrapEnabled
+               {
+                       get => (bool)GetValue(IsWrapEnabledProperty);
+                       set => SetValue(IsWrapEnabledProperty, value);
+               }
 
-        /// <summary>
-        /// Gets or sets a status of Value is wrapped.
-        /// </summary>
-        /// <since_tizen> 4 </since_tizen>
-        public bool IsWrapEnabled
-        {
-            get => (bool)GetValue(IsWrapEnabledProperty);
-            set => SetValue(IsWrapEnabledProperty, value);
-        }
+               /// <summary>
+               /// Gets or sets a CircleSurfaceProvider.
+               /// </summary>
+               /// <since_tizen> 4 </since_tizen>
+               public ICircleSurfaceProvider CircleSurfaceProvider { get; set; }
        }
 }
\ No newline at end of file
index ccdc95b..4bf63db 100644 (file)
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
 using System.Collections.Specialized;
+using System.ComponentModel;
 using Xamarin.Forms;
 
 namespace Tizen.Wearable.CircularUI.Forms
 {
-    public class CircleSurfaceView : View
-    {
-        public IList<ICircleSurfaceItem> CircleSurfaceItems { get; internal set; }
+       public class CircleSurfaceView : View, ICircleSurfaceProvider
+       {
+               public IList<ICircleSurfaceItem> CircleSurfaceItems { get; internal set; }
 
-        public CircleSurfaceView()
-        {
-            var circleSurfaceItems = new ObservableCollection<ICircleSurfaceItem>();
-            circleSurfaceItems.CollectionChanged += OnCircleObjectItemsCollectionChanged;
-            CircleSurfaceItems = circleSurfaceItems;
-        }
+               [EditorBrowsable(EditorBrowsableState.Never)]
+               public object CircleSurface { get; set; }
 
-        protected override void OnBindingContextChanged()
-        {
-            base.OnBindingContextChanged();
-            foreach (CircleSurfaceItem item in CircleSurfaceItems)
-            {
-                SetInheritedBindingContext(item, BindingContext);
-            }
-        }
+               public CircleSurfaceView()
+               {
+                       var circleSurfaceItems = new ObservableCollection<ICircleSurfaceItem>();
+                       circleSurfaceItems.CollectionChanged += OnCircleObjectItemsCollectionChanged;
+                       CircleSurfaceItems = circleSurfaceItems;
+               }
 
-        void OnCircleObjectItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
-        {
-            if (args.Action != NotifyCollectionChangedAction.Add)
-                return;
-            foreach (Element item in args.NewItems)
-                item.Parent = this;
-        }
-    }
-}
+               protected override void OnBindingContextChanged()
+               {
+                       base.OnBindingContextChanged();
+                       foreach (CircleSurfaceItem item in CircleSurfaceItems)
+                       {
+                               SetInheritedBindingContext(item, BindingContext);
+                       }
+               }
+
+               void OnCircleObjectItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
+               {
+                       if (args.Action != NotifyCollectionChangedAction.Add)
+                               return;
+                       foreach (Element item in args.NewItems)
+                               item.Parent = this;
+               }
+       }
+}
\ No newline at end of file
index 54b6ff3..cd3625d 100644 (file)
@@ -2,51 +2,51 @@
 
 namespace Tizen.Wearable.CircularUI.Forms
 {
-    public class CircularShell : Shell
-    {
-        public static readonly BindableProperty FlyoutIconBackgroundColorProperty = BindableProperty.CreateAttached("FlyoutIconBackgroundColor", typeof(Color), typeof(Shell), Color.Default);
-        public static readonly BindableProperty FlyoutForegroundColorProperty = BindableProperty.CreateAttached("FlyoutForegroundColor", typeof(Color), typeof(Shell), Color.Default);
-
-        public static Color GetFlyoutIconBackgroundColor(BindableObject element)
-        {
-            return (Color)element.GetValue(FlyoutIconBackgroundColorProperty);
-        }
-
-        public static void SetFlyoutIconBackgroundColor(BindableObject element, Color color)
-        {
-            element.SetValue(FlyoutIconBackgroundColorProperty, color);
-        }
-
-        public static Color GetFlyoutForegroundColor(BindableObject element)
-        {
-            return (Color)element.GetValue(FlyoutForegroundColorProperty);
-        }
-
-        public static void SetFlyoutForegroundColor(BindableObject element, Color color)
-        {
-            element.SetValue(FlyoutForegroundColorProperty, color);
-        }
-
-        public Color FlyoutIconBackgroundColor
-        {
-            get => (Color)GetValue(FlyoutIconBackgroundColorProperty);
-            set => SetValue(FlyoutIconBackgroundColorProperty, value);
-        }
-
-        public Color FlyoutForegroundColor
-        {
-            get => (Color)GetValue(FlyoutForegroundColorProperty);
-            set => SetValue(FlyoutForegroundColorProperty, value);
-        }
-
-        protected override bool OnBackButtonPressed()
-        {
-            if (FlyoutIsPresented)
-            {
-                FlyoutIsPresented = false;
-                return true;
-            }
-            return base.OnBackButtonPressed();
-        }
-    }
-}
+       public class CircularShell : Shell
+       {
+               public static readonly BindableProperty FlyoutIconBackgroundColorProperty = BindableProperty.CreateAttached("FlyoutIconBackgroundColor", typeof(Color), typeof(Shell), Color.Default);
+               public static readonly BindableProperty FlyoutForegroundColorProperty = BindableProperty.CreateAttached("FlyoutForegroundColor", typeof(Color), typeof(Shell), Color.Default);
+
+               public static Color GetFlyoutIconBackgroundColor(BindableObject element)
+               {
+                       return (Color)element.GetValue(FlyoutIconBackgroundColorProperty);
+               }
+
+               public static void SetFlyoutIconBackgroundColor(BindableObject element, Color color)
+               {
+                       element.SetValue(FlyoutIconBackgroundColorProperty, color);
+               }
+
+               public static Color GetFlyoutForegroundColor(BindableObject element)
+               {
+                       return (Color)element.GetValue(FlyoutForegroundColorProperty);
+               }
+
+               public static void SetFlyoutForegroundColor(BindableObject element, Color color)
+               {
+                       element.SetValue(FlyoutForegroundColorProperty, color);
+               }
+
+               public Color FlyoutIconBackgroundColor
+               {
+                       get => (Color)GetValue(FlyoutIconBackgroundColorProperty);
+                       set => SetValue(FlyoutIconBackgroundColorProperty, value);
+               }
+
+               public Color FlyoutForegroundColor
+               {
+                       get => (Color)GetValue(FlyoutForegroundColorProperty);
+                       set => SetValue(FlyoutForegroundColorProperty, value);
+               }
+
+               protected override bool OnBackButtonPressed()
+               {
+                       if (FlyoutIsPresented)
+                       {
+                               FlyoutIsPresented = false;
+                               return true;
+                       }
+                       return base.OnBackButtonPressed();
+               }
+       }
+}
\ No newline at end of file
index 47b9e2a..21dca53 100644 (file)
@@ -21,164 +21,164 @@ using Xamarin.Forms;
 
 namespace Tizen.Wearable.CircularUI.Forms
 {
-    /// <summary>
-    /// The ContentButton is a Button, which allows you to customize the View to be displayed.
-    /// </summary>
-    /// <since_tizen> 4 </since_tizen>
-    public class ContentButton : ContentView, IButtonController
-    {
-        /// <summary>
-        /// BindableProperty. Identifies the Command bindable property.
-        /// </summary>
-        /// <since_tizen> 4 </since_tizen>
-        public static readonly BindableProperty CommandProperty = BindableProperty.Create(nameof(Command), typeof(ICommand), typeof(ContentButton), null, 
-            propertyChanging: OnCommandChanging, propertyChanged: OnCommandChanged);
-
-        /// <summary>
-        /// BindableProperty. Identifies the CommandParameter bindable property.
-        /// </summary>
-        /// <since_tizen> 4 </since_tizen>
-        public static readonly BindableProperty CommandParameterProperty = BindableProperty.Create(nameof(CommandParameter), typeof(object), typeof(ContentButton), null, 
-            propertyChanged: (bindable, oldvalue, newvalue) => CommandCanExcuteChanged(bindable, EventArgs.Empty));
-
-        /// <summary>
-        /// Gets or sets command that is executed when the button is clicked.
-        /// </summary>
-        /// <since_tizen> 4 </since_tizen>
-        public ICommand Command
-        {
-            get => (ICommand)GetValue(CommandProperty);
-            set => SetValue(CommandProperty, value);
-        }
-
-        /// <summary>
-        /// Gets or sets command paramter that is executed when the button is clicked.
-        /// </summary>
-        /// <since_tizen> 4 </since_tizen>
-        public object CommandParameter
-        {
-            get => GetValue(CommandParameterProperty);
-            set => SetValue(CommandParameterProperty, value);
-        }
-
-        /// <summary>
-        /// Occurs when the button is clicked.
-        /// </summary>
-        /// <since_tizen> 4 </since_tizen>
-        public event EventHandler Clicked;
-
-        /// <summary>
-        /// Occurs when the button is pressed.
-        /// </summary>
-        /// <since_tizen> 4 </since_tizen>
-        public event EventHandler Pressed;
-
-        /// <summary>
-        /// Occurs when the button is released.
-        /// </summary>
-        /// <since_tizen> 4 </since_tizen>
-        public event EventHandler Released;
-
-        bool IsEnabledCore
-        {
-            set => SetValueCore(IsEnabledProperty, value);
-        }
-
-        /// <summary>
-        /// For internal use.
-        /// </summary>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public void SendClicked()
-        {
-            if (IsEnabled)
-            {
-                Command?.Execute(CommandParameter);
-                Clicked?.Invoke(this, EventArgs.Empty);
-            }
-        }
-
-        /// <summary>
-        /// For internal use.
-        /// </summary>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public void SendPressed()
-        {
-            if (IsEnabled)
-            {
-                Pressed?.Invoke(this, EventArgs.Empty);
-            }
-        }
-
-        /// <summary>
-        /// For internal use.
-        /// </summary>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public void SendReleased()
-        {
-            if (IsEnabled)
-            {
-                Released?.Invoke(this, EventArgs.Empty);
-            }
-        }
-
-        protected override void OnBindingContextChanged()
-        {
-            base.OnBindingContextChanged();
-
-            View content = Content;
-            if (content != null)
-            {
-                SetInheritedBindingContext(content, BindingContext);
-            }
-        }
-
-        static void OnCommandChanged(BindableObject bindable, object oldCommand, object newCommand)
-        {
-            ContentButton button = (ContentButton)bindable;
-            if (newCommand is ICommand command)
-            {
-                command.CanExecuteChanged += button.OnCommandCanExecuteChanged;
-            }
-            CommandChanged(button);
-        }
-
-        static void CommandChanged(ContentButton button)
-        {
-            if(button.Command != null)
-            {
-                CommandCanExcuteChanged(button, EventArgs.Empty);
-            }
-            else
-            {
-                button.IsEnabledCore = true;
-            }
-        }
-
-        static void OnCommandChanging(BindableObject bindable, object oldCommand, object newCommand)
-        {
-            ContentButton button = (ContentButton)bindable;
-            if (oldCommand != null)
-            {
-                (oldCommand as ICommand).CanExecuteChanged -= button.OnCommandCanExecuteChanged;
-            }
-        }
-
-        static void CommandCanExcuteChanged(object sender, EventArgs e)
-        {
-            var button = (ContentButton)sender;
-            if (button.Command != null)
-            {
-                button.IsEnabledCore = button.Command.CanExecute(button.CommandParameter);
-            }
-        }
-
-        void OnCommandCanExecuteChanged(object sender, EventArgs e)
-        {
-            ContentButton button = (ContentButton)sender;
-            if (button.Command != null)
-            {
-                button.IsEnabledCore = button.Command.CanExecute(button.CommandParameter);
-            }
-        }
-    }
-}
+       /// <summary>
+       /// The ContentButton is a Button, which allows you to customize the View to be displayed.
+       /// </summary>
+       /// <since_tizen> 4 </since_tizen>
+       public class ContentButton : ContentView, IButtonController
+       {
+               /// <summary>
+               /// BindableProperty. Identifies the Command bindable property.
+               /// </summary>
+               /// <since_tizen> 4 </since_tizen>
+               public static readonly BindableProperty CommandProperty = BindableProperty.Create(nameof(Command), typeof(ICommand), typeof(ContentButton), null,
+                       propertyChanging: OnCommandChanging, propertyChanged: OnCommandChanged);
+
+               /// <summary>
+               /// BindableProperty. Identifies the CommandParameter bindable property.
+               /// </summary>
+               /// <since_tizen> 4 </since_tizen>
+               public static readonly BindableProperty CommandParameterProperty = BindableProperty.Create(nameof(CommandParameter), typeof(object), typeof(ContentButton), null,
+                       propertyChanged: (bindable, oldvalue, newvalue) => CommandCanExcuteChanged(bindable, EventArgs.Empty));
+
+               /// <summary>
+               /// Gets or sets command that is executed when the button is clicked.
+               /// </summary>
+               /// <since_tizen> 4 </since_tizen>
+               public ICommand Command
+               {
+                       get => (ICommand)GetValue(CommandProperty);
+                       set => SetValue(CommandProperty, value);
+               }
+
+               /// <summary>
+               /// Gets or sets command paramter that is executed when the button is clicked.
+               /// </summary>
+               /// <since_tizen> 4 </since_tizen>
+               public object CommandParameter
+               {
+                       get => GetValue(CommandParameterProperty);
+                       set => SetValue(CommandParameterProperty, value);
+               }
+
+               /// <summary>
+               /// Occurs when the button is clicked.
+               /// </summary>
+               /// <since_tizen> 4 </since_tizen>
+               public event EventHandler Clicked;
+
+               /// <summary>
+               /// Occurs when the button is pressed.
+               /// </summary>
+               /// <since_tizen> 4 </since_tizen>
+               public event EventHandler Pressed;
+
+               /// <summary>
+               /// Occurs when the button is released.
+               /// </summary>
+               /// <since_tizen> 4 </since_tizen>
+               public event EventHandler Released;
+
+               bool IsEnabledCore
+               {
+                       set => SetValueCore(IsEnabledProperty, value);
+               }
+
+               /// <summary>
+               /// For internal use.
+               /// </summary>
+               [EditorBrowsable(EditorBrowsableState.Never)]
+               public void SendClicked()
+               {
+                       if (IsEnabled)
+                       {
+                               Command?.Execute(CommandParameter);
+                               Clicked?.Invoke(this, EventArgs.Empty);
+                       }
+               }
+
+               /// <summary>
+               /// For internal use.
+               /// </summary>
+               [EditorBrowsable(EditorBrowsableState.Never)]
+               public void SendPressed()
+               {
+                       if (IsEnabled)
+                       {
+                               Pressed?.Invoke(this, EventArgs.Empty);
+                       }
+               }
+
+               /// <summary>
+               /// For internal use.
+               /// </summary>
+               [EditorBrowsable(EditorBrowsableState.Never)]
+               public void SendReleased()
+               {
+                       if (IsEnabled)
+                       {
+                               Released?.Invoke(this, EventArgs.Empty);
+                       }
+               }
+
+               protected override void OnBindingContextChanged()
+               {
+                       base.OnBindingContextChanged();
+
+                       View content = Content;
+                       if (content != null)
+                       {
+                               SetInheritedBindingContext(content, BindingContext);
+                       }
+               }
+
+               static void OnCommandChanged(BindableObject bindable, object oldCommand, object newCommand)
+               {
+                       ContentButton button = (ContentButton)bindable;
+                       if (newCommand is ICommand command)
+                       {
+                               command.CanExecuteChanged += button.OnCommandCanExecuteChanged;
+                       }
+                       CommandChanged(button);
+               }
+
+               static void CommandChanged(ContentButton button)
+               {
+                       if(button.Command != null)
+                       {
+                               CommandCanExcuteChanged(button, EventArgs.Empty);
+                       }
+                       else
+                       {
+                               button.IsEnabledCore = true;
+                       }
+               }
+
+               static void OnCommandChanging(BindableObject bindable, object oldCommand, object newCommand)
+               {
+                       ContentButton button = (ContentButton)bindable;
+                       if (oldCommand != null)
+                       {
+                               (oldCommand as ICommand).CanExecuteChanged -= button.OnCommandCanExecuteChanged;
+                       }
+               }
+
+               static void CommandCanExcuteChanged(object sender, EventArgs e)
+               {
+                       var button = (ContentButton)sender;
+                       if (button.Command != null)
+                       {
+                               button.IsEnabledCore = button.Command.CanExecute(button.CommandParameter);
+                       }
+               }
+
+               void OnCommandCanExecuteChanged(object sender, EventArgs e)
+               {
+                       ContentButton button = (ContentButton)sender;
+                       if (button.Command != null)
+                       {
+                               button.IsEnabledCore = button.Command.CanExecute(button.CommandParameter);
+                       }
+               }
+       }
+}
\ No newline at end of file
diff --git a/src/XSF/Tizen.Wearable.CircularUI.Forms/EmbeddingControlsConverter.cs b/src/XSF/Tizen.Wearable.CircularUI.Forms/EmbeddingControlsConverter.cs
deleted file mode 100644 (file)
index 023cd8f..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved
- *
- * Licensed under the Flora License, Version 1.1 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://floralicense.org/license/
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Text;
-using Xamarin.Forms;
-
-namespace Tizen.Wearable.CircularUI.Forms
-{
-       /// <summary>
-       /// Class that the XAML parser uses to convert Progress to Bound.
-       /// </summary>
-       internal class ProgressToBoundTextConverter : IValueConverter
-       {
-               public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
-               {
-                       double progress = (double)value;
-                       if (Double.IsNaN(progress))
-                       {
-                               progress = 0d;
-                       }
-                       return new Rectangle(80, 0, progress*0.5, 1);
-               }
-
-               public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
-               {
-                       Rectangle rect = (Rectangle)value;
-                       return rect.Width;
-               }
-       }
-
-       /// <summary>
-       /// Class that the XAML parser uses to convert milliseconds to Text format.
-       /// </summary>
-       internal class MillisecondToTextConverter : IValueConverter
-       {
-               public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
-               {
-                       int millisecond = (int)value;
-                       int second = (millisecond / 1000) % 60;
-                       int min = (millisecond / 1000 / 60) % 60;
-                       int hour = (millisecond / 1000 / 60 / 60);
-                       if (hour > 0)
-                       {
-                               return string.Format("{0:d2}:{1:d2}:{2:d2}", hour, min, second);
-                       }
-                       else
-                       {
-                               return string.Format("{0:d2}:{1:d2}", min, second);
-                       }
-
-               }
-
-               public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
-               {
-                       throw new NotImplementedException();
-               }
-       }
-}
\ No newline at end of file
diff --git a/src/XSF/Tizen.Wearable.CircularUI.Forms/FileMediaSource.cs b/src/XSF/Tizen.Wearable.CircularUI.Forms/FileMediaSource.cs
deleted file mode 100644 (file)
index 9a60366..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved
- *
- * Licensed under the Flora License, Version 1.1 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://floralicense.org/license/
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-using Xamarin.Forms;
-
-namespace Tizen.Wearable.CircularUI.Forms
-{
-       /// <summary>
-       /// A MediaSource that reads a media from a file.
-       /// </summary>
-       [TypeConverter(typeof(FileMediaSourceConverter))]
-       public sealed class FileMediaSource : MediaSource
-       {
-               /// <summary>
-               /// Identifies the File bindable property.
-               /// </summary>
-               public static readonly BindableProperty FileProperty = BindableProperty.Create("File", typeof(string), typeof(FileMediaSource), default(string));
-
-               /// <summary>
-               /// Gets or sets the file from which this FileMediaSource will load a media.
-               /// </summary>
-               public string File
-               {
-                       get { return (string)GetValue(FileProperty); }
-                       set { SetValue(FileProperty, value); }
-               }
-
-               /// <summary>
-               /// Returns a string representation of `File`.
-               /// </summary>
-               /// <returns></returns>
-               public override string ToString()
-               {
-                       return $"File: {File}";
-               }
-
-               /// <summary>
-               /// Allows implicit casting from a string.
-               /// </summary>
-               /// <param name="file"></param>
-               public static implicit operator FileMediaSource(string file)
-               {
-                       return (FileMediaSource)FromFile(file);
-               }
-
-               /// <summary>
-               /// Allows implicit casting to a string.
-               /// </summary>
-               /// <param name="file"></param>
-               public static implicit operator string(FileMediaSource file)
-               {
-                       return file?.File;
-               }
-
-               protected override void OnPropertyChanged(string propertyName = null)
-               {
-                       if (propertyName == FileProperty.PropertyName)
-                               OnSourceChanged();
-                       base.OnPropertyChanged(propertyName);
-               }
-       }
-}
\ No newline at end of file
  * limitations under the License.
  */
 
-using MMView = Tizen.Multimedia.MediaView;
+using Xamarin.Forms;
 
-namespace Tizen.Wearable.CircularUI.Forms.Renderer
+namespace Tizen.Wearable.CircularUI.Forms
 {
-       public interface IMediaViewProvider
+       /// <summary>
+       /// FlatViewCell contains a developer-defined Xamarin.Forms.View.
+       /// It has no fish-eye effect while ViewCell has fish-eye effect.
+       /// </summary>
+       /// <since_tizen> 6 </since_tizen>
+       public class FlatViewCell : ViewCell
        {
-               MMView GetMediaView();
        }
 }
\ No newline at end of file
 namespace Tizen.Wearable.CircularUI.Forms
 {
        /// <summary>
-       /// Enumerates values that define the playback state of the media content.
+       /// The ICircleSurfaceConsumer is an interface represents the CircleSurfaceObject.
        /// </summary>
-       public enum PlaybackState
+       /// <since_tizen> 4 </since_tizen>
+       public interface ICircleSurfaceConsumer
        {
                /// <summary>
-               /// Stopped.
+               /// Gets or sets CircleSurfaceProvider
                /// </summary>
-               Stopped,
-               /// <summary>
-               /// Playing.
-               /// </summary>
-               Playing,
-               /// <summary>
-               /// Paused while playing media.
-               /// </summary>
-               Paused
+               /// <since_tizen> 4 </since_tizen>
+               ICircleSurfaceProvider CircleSurfaceProvider { get; set; }
        }
 }
\ No newline at end of file
  * limitations under the License.
  */
 
-using System;
-using Xamarin.Forms;
+using System.ComponentModel;
 
 namespace Tizen.Wearable.CircularUI.Forms
 {
        /// <summary>
-       /// Enumeration that specifies the video ouput.
+       /// The ICircleSurfaceProvider is an interface providing the CircleSurface.
        /// </summary>
-       public enum VideoOuputType
+       /// <since_tizen> 4 </since_tizen>
+       public interface ICircleSurfaceProvider
        {
                /// <summary>
-               /// An overlay type.
+               /// Gets or sets CircleSurface object. For internal use only
                /// </summary>
-               Overlay,
-               /// <summary>
-               /// A buffer type.
-               /// </summary>
-               Buffer,
-       }
-
-       /// <summary>
-       /// Interface indicating the ouput type of the media.
-       /// </summary>
-       public interface IVideoOutput
-       {
-               VisualElement MediaView { get; }
-               View Controller { get; set; }
-               VideoOuputType OuputType { get; }
+               /// <since_tizen> 4 </since_tizen>
+               [EditorBrowsable(EditorBrowsableState.Never)]
+               object CircleSurface { get; set; }
        }
 }
\ No newline at end of file
diff --git a/src/XSF/Tizen.Wearable.CircularUI.Forms/IPlatformMediaPlayer.cs b/src/XSF/Tizen.Wearable.CircularUI.Forms/IPlatformMediaPlayer.cs
deleted file mode 100644 (file)
index d4e4258..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved
- *
- * Licensed under the Flora License, Version 1.1 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://floralicense.org/license/
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Threading.Tasks;
-
-namespace Tizen.Wearable.CircularUI.Forms
-{
-       /// <summary>
-       /// Enumerates values that define how a media content is displayed.
-       /// </summary>
-       public enum DisplayAspectMode
-       {
-               /// <summary>
-               /// Scale the media content to so it exactly fills the view.
-               /// </summary>
-               Fill,
-               /// <summary>
-               /// Scale the media content to fit the view.
-               /// </summary>
-               AspectFit,
-               /// <summary>
-               /// Scale the media content to fill the view.
-               /// </summary>
-               AspectFill,
-               /// <summary>
-               /// The original size of the media content.
-               /// </summary>
-               OrignalSize
-       }
-
-       /// <summary>
-       /// Internal use only. Contains arguments for the event that is raised when the buffering progress is updated.
-       /// </summary>
-       public class BufferingProgressUpdatedEventArgs : EventArgs
-       {
-               /// <summary>
-               /// The value indicating the buffering status(0-1).
-               /// </summary>
-               public double Progress { get; set; }
-       }
-
-       /// <summary>
-       /// For internal use by platform renderers.
-       /// </summary>
-       public interface IPlatformMediaPlayer
-       {
-               bool UsesEmbeddingControls { get; set; }
-               bool AutoPlay { get; set; }
-               bool AutoStop { get; set; }
-               double Volume { get; set; }
-               bool IsMuted { get; set; }
-               int Position { get; }
-
-               int Duration { get; }
-
-               DisplayAspectMode AspectMode { get; set; }
-
-               event EventHandler PlaybackCompleted;
-               event EventHandler PlaybackStarted;
-               event EventHandler PlaybackPaused;
-               event EventHandler PlaybackStopped;
-               event EventHandler UpdateStreamInfo;
-               event EventHandler<BufferingProgressUpdatedEventArgs> BufferingProgressUpdated;
-
-               void SetDisplay(IVideoOutput output);
-
-               void SetSource(MediaSource source);
-
-               Task<bool> Start();
-               void Stop();
-               void Pause();
-               Task<int> Seek(int ms);
-               Task<Stream> GetAlbumArts();
-
-               Task<IDictionary<string, string>> GetMetadata();
-       }
-}
\ No newline at end of file
diff --git a/src/XSF/Tizen.Wearable.CircularUI.Forms/IRotaryService.cs b/src/XSF/Tizen.Wearable.CircularUI.Forms/IRotaryService.cs
new file mode 100644 (file)
index 0000000..5edf56e
--- /dev/null
@@ -0,0 +1,9 @@
+using System;
+
+namespace Tizen.Wearable.CircularUI.Forms
+{
+       interface IRotaryService
+       {
+               event EventHandler<RotaryEventArgs> Rotated;
+       }
+}
\ No newline at end of file
index 9cc02b1..0206336 100644 (file)
@@ -22,19 +22,27 @@ using System.Threading;
 using System.Threading.Tasks;
 using Xamarin.Forms;
 using Xamarin.Forms.Internals;
+using Xamarin.Forms.PlatformConfiguration.TizenSpecific;
 using Xamarin.Forms.Platform.Tizen.Native;
 
 namespace Tizen.Wearable.CircularUI.Forms
 {
+
+       //TODO : This interface sholud be removed when the related bug in Xamarin.Forms is fixed.
+       [EditorBrowsable(EditorBrowsableState.Never)]
+       public interface IPlatformMediaPlayer2 : IPlatformMediaPlayer
+       {
+
+       }
        /// <summary>
        /// MediaPlayer provieds the essential components to play the media contents.
        /// </summary>
-       public class MediaPlayer : Element
+       public class MediaPlayer : Element, IMediaPlayer, IDisposable
        {
                /// <summary>
                /// Identifies the Source bindable property.
                /// </summary>
-               public static readonly BindableProperty SourceProperty = BindableProperty.Create(nameof(Source), typeof(MediaSource), typeof(MediaPlayer), default(MediaSource), propertyChanging: OnSourceChanging, propertyChanged: OnSourceChanged);
+               public static readonly BindableProperty SourceProperty = BindableProperty.Create(nameof(Source), typeof(MediaSource), typeof(MediaPlayer), default(MediaSource), propertyChanged: OnSourceChanged);
                /// <summary>
                /// Identifies the VideoOutput bindable property.
                /// </summary>
@@ -93,18 +101,22 @@ namespace Tizen.Wearable.CircularUI.Forms
                /// </summary>
                public static readonly BindableProperty IsBufferingProperty = IsBufferingPropertyKey.BindableProperty;
 
-               IPlatformMediaPlayer _impl;
+               bool _disposed = false;
+               IPlatformMediaPlayer2 _impl;
                bool _isPlaying;
                bool _controlsAlwaysVisible;
                CancellationTokenSource _hideTimerCTS = new CancellationTokenSource();
-               Lazy<View> _controls = null;
+               Lazy<View> _controls;
 
                /// <summary>
                /// Initializes a new instance of the MediaPlayer class.
                /// </summary>
                public MediaPlayer()
                {
-                       _impl = DependencyService.Get<IPlatformMediaPlayer>(fetchTarget: DependencyFetchTarget.NewInstance);
+                       //TODO: IPlatformMediaPlayer2 should be replaced to IPlatformMediaPlayer if Xamarin.Forms ready
+                       _impl = DependencyService.Get<IPlatformMediaPlayer2>(fetchTarget: DependencyFetchTarget.NewInstance);
+                       if (_impl != null)
+                       {
                        _impl.UpdateStreamInfo += OnUpdateStreamInfo;
                        _impl.PlaybackCompleted += SendPlaybackCompleted;
                        _impl.PlaybackStarted += SendPlaybackStarted;
@@ -120,11 +132,14 @@ namespace Tizen.Wearable.CircularUI.Forms
                        _controlsAlwaysVisible = false;
                        _controls = new Lazy<View>(() =>
                        {
-                               return new EmbeddingControls
+                                       return _impl.GetEmbeddingControlView(this);
+                               });
+                       }
+               }
+
+               ~MediaPlayer()
                                {
-                                       BindingContext = this
-                               };
-                       });
+                       Dispose(false);
                }
 
                /// <summary>
@@ -435,6 +450,31 @@ namespace Tizen.Wearable.CircularUI.Forms
                        return _impl.GetMetadata();
                }
 
+               public void Dispose()
+               {
+                       Dispose(true);
+                       GC.SuppressFinalize(this);
+               }
+
+               protected virtual void Dispose(bool disposing)
+               {
+                       if (_disposed)
+                               return;
+
+                       if (disposing)
+                       {
+                               _impl.UpdateStreamInfo -= OnUpdateStreamInfo;
+                               _impl.PlaybackCompleted -= SendPlaybackCompleted;
+                               _impl.PlaybackStarted -= SendPlaybackStarted;
+                               _impl.PlaybackPaused -= SendPlaybackPaused;
+                               _impl.PlaybackStopped -= SendPlaybackStopped;
+                               _impl.BufferingProgressUpdated -= OnUpdateBufferingProgress;
+                               _impl.Dispose();
+                       }
+
+                       _disposed = true;
+               }
+
                void UpdateAutoPlay()
                {
                        _impl.AutoPlay = AutoPlay;
@@ -504,15 +544,6 @@ namespace Tizen.Wearable.CircularUI.Forms
                        _impl.SetSource(Source);
                }
 
-               void OnSourceChanging(MediaSource oldValue, MediaSource newValue)
-               {
-                       if (oldValue != null)
-                               oldValue.SourceChanged -= OnSourceChanged;
-
-                       if (newValue != null)
-                               newValue.SourceChanged += OnSourceChanged;
-               }
-
                void OnVideoOutputChanged()
                {
                        if (VideoOutput != null)
@@ -630,11 +661,6 @@ namespace Tizen.Wearable.CircularUI.Forms
                        }
                }
 
-
-               static void OnSourceChanging(BindableObject bindable, object oldValue, object newValue)
-               {
-                       (bindable as MediaPlayer)?.OnSourceChanging(oldValue as MediaSource, newValue as MediaSource);
-               }
                static void OnSourceChanged(BindableObject bindable, object oldValue, object newValue)
                {
                        (bindable as MediaPlayer)?.OnSourceChanged(bindable, EventArgs.Empty);
diff --git a/src/XSF/Tizen.Wearable.CircularUI.Forms/MediaSource.cs b/src/XSF/Tizen.Wearable.CircularUI.Forms/MediaSource.cs
deleted file mode 100644 (file)
index 2fea1b2..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved
- *
- * Licensed under the Flora License, Version 1.1 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://floralicense.org/license/
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-using System;
-using System.IO;
-using System.Reflection;
-using System.Threading;
-using System.Threading.Tasks;
-using Xamarin.Forms;
-
-namespace Tizen.Wearable.CircularUI.Forms
-{
-       /// <summary>
-       /// Abstract class whose implementors load media contents from files or the Web.
-       /// </summary>
-       [TypeConverter(typeof(MediaSourceConverter))]
-       public abstract class MediaSource : Element
-       {
-               protected MediaSource()
-               {
-               }
-
-               /// <summary>
-               /// Returns a new MediaSource that reads from file.
-               /// </summary>
-               /// <param name="file">The file path to use as a media source.</param>
-               /// <returns>Returns the MediaSource.</returns>
-               public static MediaSource FromFile(string file)
-               {
-                       return new FileMediaSource { File = file };
-               }
-
-               /// <summary>
-               /// Returns a new MediaSource that reads from uri.
-               /// </summary>
-               /// <param name="uri">The uri path to use as a media source.</param>
-               /// <returns>Returns the MediaSource.</returns>
-               public static MediaSource FromUri(Uri uri)
-               {
-                       if (!uri.IsAbsoluteUri)
-                               throw new ArgumentException("uri is relative");
-                       return new UriMediaSource { Uri = uri };
-               }
-
-               public static implicit operator MediaSource(string source)
-               {
-                       return Uri.TryCreate(source, UriKind.Absolute, out Uri uri) && uri.Scheme != "file" ? FromUri(uri) : FromFile(source);
-               }
-
-               public static implicit operator MediaSource(Uri uri)
-               {
-                       if (!uri.IsAbsoluteUri)
-                               throw new ArgumentException("uri is relative");
-                       return FromUri(uri);
-               }
-
-               protected void OnSourceChanged()
-               {
-                       SourceChanged?.Invoke(this, EventArgs.Empty);
-               }
-
-               internal event EventHandler SourceChanged;
-       }
-}
\ No newline at end of file
diff --git a/src/XSF/Tizen.Wearable.CircularUI.Forms/MediaSourceConverter.cs b/src/XSF/Tizen.Wearable.CircularUI.Forms/MediaSourceConverter.cs
deleted file mode 100644 (file)
index 09ecfaa..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved
- *
- * Licensed under the Flora License, Version 1.1 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://floralicense.org/license/
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-using System;
-using Xamarin.Forms;
-
-namespace Tizen.Wearable.CircularUI.Forms
-{
-       /// <summary>
-       /// Class that takes a string representation of a media file location and returns a MeidaSource from the specified resource.
-       /// </summary>
-       [TypeConverter(typeof(MediaSource))]
-       public sealed class MediaSourceConverter : TypeConverter
-       {
-               /// <summary>
-               /// Returns a media source created from a URI that is contained in value.
-               /// </summary>
-               /// <param name="value">The value to convert.</param>
-               /// <returns>MediaSource</returns>
-               public override object ConvertFromInvariantString(string value)
-               {
-                       if (value != null)
-                       {
-                               return Uri.TryCreate(value, UriKind.Absolute, out Uri uri) && uri.Scheme != "file" ? MediaSource.FromUri(uri) : MediaSource.FromFile(value);
-                       }
-
-                       throw new InvalidOperationException(string.Format("Cannot convert \"{0}\" into {1}", value, typeof(MediaSource)));
-               }
-       }
-}
\ No newline at end of file
index ae245b6..a69c8a2 100644 (file)
@@ -15,6 +15,8 @@
  */
 
 using Xamarin.Forms;
+using Xamarin.Forms.PlatformConfiguration.TizenSpecific;
+using XVisualElement = Xamarin.Forms.VisualElement;
 
 namespace Tizen.Wearable.CircularUI.Forms
 {
@@ -45,7 +47,8 @@ namespace Tizen.Wearable.CircularUI.Forms
                /// </summary>
                public virtual VideoOuputType OuputType => VideoOuputType.Buffer;
 
-               VisualElement IVideoOutput.MediaView => this;
+       XVisualElement IVideoOutput.MediaView => this;
+
                View IVideoOutput.Controller
                {
                        get
@@ -20,22 +20,34 @@ using Xamarin.Forms;
 namespace Tizen.Wearable.CircularUI.Forms
 {
        /// <summary>
-       /// A TypeConverter that converts to FileMediaSource.
+       /// The RotaryEventManager provides an event for the global rotary event for wearable devices.
        /// </summary>
-       [TypeConverter(typeof(FileMediaSource))]
-       public sealed class FileMediaSourceConverter : TypeConverter
+       /// <since_tizen> 4 </since_tizen>
+       /// <example>
+       /// <code>
+       /// RotaryEventManager.Rotated += (s, e) =>
+       /// {
+       ///      Console.WriteLine($"Rotated! Rotated direction: ${e.IsClockwise}");
+       /// };
+       /// </code>
+       /// </example>
+       public static class RotaryEventManager
        {
+               static IRotaryService ProxyService { get; } = DependencyService.Get<IRotaryService>();
+
                /// <summary>
-               /// Creates a file media source given a path to a media.
+               /// Rotated will be triggered when the rotatable device's Bezel is rotated.
                /// </summary>
-               /// <param name="value">The value to convert.</param>
-               /// <returns>FileMediaSource</returns>
-               public override object ConvertFromInvariantString(string value)
+               public static event EventHandler<RotaryEventArgs> Rotated
                {
-                       if (value != null)
-                               return (FileMediaSource)MediaSource.FromFile(value);
-
-                       throw new InvalidOperationException(string.Format("Cannot convert \"{0}\" into {1}", value, typeof(FileMediaSource)));
+                       add
+                       {
+                               ProxyService.Rotated += value;
+                       }
+                       remove
+                       {
+                               ProxyService.Rotated -= value;
+                       }
                }
        }
 }
\ No newline at end of file
diff --git a/src/XSF/Tizen.Wearable.CircularUI.Forms/UriMediaSource.cs b/src/XSF/Tizen.Wearable.CircularUI.Forms/UriMediaSource.cs
deleted file mode 100644 (file)
index d18394a..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved
- *
- * Licensed under the Flora License, Version 1.1 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://floralicense.org/license/
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-using System;
-using System.Threading.Tasks;
-using Xamarin.Forms;
-
-namespace Tizen.Wearable.CircularUI.Forms
-{
-       /// <summary>
-       /// A MediaSource that loads a media from a URI
-       /// </summary>
-       [TypeConverter(typeof(MediaSourceConverter))]
-       public sealed class UriMediaSource : MediaSource
-       {
-               /// <summary>
-               /// Identifies the File bindable property.
-               /// </summary>
-               public static readonly BindableProperty UriProperty = BindableProperty.Create("Uri", typeof(Uri), typeof(UriImageSource), default(Uri), validateValue: (bindable, value) => value == null || ((Uri)value).IsAbsoluteUri);
-
-               /// <summary>
-               /// Gets or sets the URI for the media to get.
-               /// </summary>
-               public Uri Uri
-               {
-                       get { return (Uri)GetValue(UriProperty); }
-                       set { SetValue(UriProperty, value); }
-               }
-
-               /// <summary>
-               /// Returns the path to the file for the media, prefixed with the string, "Uri: ".
-               /// </summary>
-               /// <returns></returns>
-               public override string ToString()
-               {
-                       return $"Uri: {Uri}";
-               }
-
-               /// <summary>
-               /// Allows implicit casting from a Uri.
-               /// </summary>
-               /// <param name="uri"></param>
-               public static implicit operator UriMediaSource(Uri uri)
-               {
-                       return (UriMediaSource)FromUri(uri);
-               }
-
-               /// <summary>
-               /// Allows implicit casting to a string.
-               /// </summary>
-               /// <param name="uri"></param>
-               public static implicit operator string(UriMediaSource uri)
-               {
-                       return uri?.ToString();
-               }
-
-               protected override void OnPropertyChanged(string propertyName = null)
-               {
-                       if (propertyName == UriProperty.PropertyName)
-                               OnSourceChanged();
-                       base.OnPropertyChanged(propertyName);
-               }
-       }
-}
\ No newline at end of file
index 3f4194d..c01c0e1 100644 (file)
@@ -2,11 +2,11 @@
 
 namespace Xamarin.Forms
 {
-    public class AppThemeChangedEventArgs : EventArgs
-    {
-        public AppThemeChangedEventArgs(OSAppTheme appTheme) =>
-            RequestedTheme = appTheme;
+       public class AppThemeChangedEventArgs : EventArgs
+       {
+               public AppThemeChangedEventArgs(OSAppTheme appTheme) =>
+                       RequestedTheme = appTheme;
 
-        public OSAppTheme RequestedTheme { get; }
-    }
+               public OSAppTheme RequestedTheme { get; }
+       }
 }
\ No newline at end of file
index b3d4b09..00171a4 100644 (file)
@@ -72,7 +72,7 @@ namespace Xamarin.Forms
                }
 
                protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
-               {                       
+               {
                        VerifyExperimental();
                        return base.OnMeasure(widthConstraint, heightConstraint);
                }
@@ -373,4 +373,4 @@ namespace Xamarin.Forms
                                });
                }
        }
-}
+}
\ No newline at end of file
index 4f6515b..2645f9a 100644 (file)
@@ -7,4 +7,4 @@
                Collapsing,
                Collapsed
        }
-}
+}
\ No newline at end of file
index 34a97f3..619af20 100644 (file)
@@ -5,6 +5,7 @@ namespace Xamarin.Forms
 {
        public interface ISwipeItem
        {
+               bool IsVisible { get; set; }
                ICommand Command { get; set; }
                object CommandParameter { get; set; }
 
index 0fea42a..c2e5532 100644 (file)
@@ -437,7 +437,7 @@ namespace Xamarin.Forms
                        _previousGroupSelected = groupIndex;
 
                        // A11y: Keyboards and screen readers can deselect items, allowing -1 to be possible
-                       if (cell == null && inGroupIndex != -1)
+                       if (cell == null && inGroupIndex >= 0)
                        {
                                cell = group[inGroupIndex];
                        }
index a6e3620..833ce9c 100644 (file)
@@ -96,8 +96,8 @@ namespace Xamarin.Forms
                public IList<string> @class
                {
                        get { return _mergedStyle.StyleClass; }
-                       set 
-                       { 
+                       set
+                       {
                                _mergedStyle.StyleClass = value;
                        }
                }
index e8b9486..14079d1 100644 (file)
                        SetUseBezelInteraction(config.Element, value);
                        return config;
                }
+
+               public static readonly BindableProperty OverlayContentProperty
+                  = BindableProperty.CreateAttached("OverlayContent", typeof(View), typeof(FormsElement), default(View));
+
+               public static View GetOverlayContent(BindableObject application)
+               {
+                       return (View)application.GetValue(OverlayContentProperty);
+               }
+
+               public static void SetOverlayContent(BindableObject application, View value)
+               {
+                       application.SetValue(OverlayContentProperty, value);
+               }
+
+               public static View GetOverlayContent(this IPlatformElementConfiguration<Tizen, FormsElement> config)
+               {
+                       return GetOverlayContent(config.Element);
+               }
+
+               public static IPlatformElementConfiguration<Tizen, FormsElement> SetOverlayContent(this IPlatformElementConfiguration<Tizen, FormsElement> config, View value)
+               {
+                       SetOverlayContent(config.Element, value);
+                       return config;
+               }
        }
 }
index 1da6194..c9ce7ce 100644 (file)
@@ -42,6 +42,9 @@ namespace Xamarin.Forms.PlatformConfiguration.TizenSpecific
                event EventHandler PlaybackStarted;
                event EventHandler PlaybackStopped;
 
+               Task<bool> Start();
+               void Stop();
+               void Pause();
                }
 
        public interface IPlatformMediaPlayer : IDisposable
index 9a6390f..76cd873 100644 (file)
@@ -155,7 +155,7 @@ namespace Xamarin.Forms
                                {
                                        Navigation.ModalStack[Navigation.ModalStack.Count - 1]
                                                .OnAppearing(action);
-                                       
+
                                        return;
                                }
                                else if(Navigation.NavigationStack.Count > 1)
@@ -380,7 +380,7 @@ namespace Xamarin.Forms
                                if (Device.RuntimePlatform == Device.UWP)
                                {
                                        defaultImageClass.Setters.Add(new Setter { Property = Image.HorizontalOptionsProperty, Value = LayoutOptions.Start });
-                                       defaultImageClass.Setters.Add(new Setter { Property = Image.MarginProperty, Value = new Thickness(12, 0, 12, 0) });                                     
+                                       defaultImageClass.Setters.Add(new Setter { Property = Image.MarginProperty, Value = new Thickness(12, 0, 12, 0) });
                                }
 
                                image.SetBinding(Image.SourceProperty, iconBinding);
index a397d05..b52c6ed 100644 (file)
@@ -43,9 +43,9 @@ namespace Xamarin.Forms
                [TypeConverter(typeof(ListStringTypeConverter))]
                public IList<string> @class {
                        get { return _mergedStyle.StyleClass; }
-                       set 
-                       { 
-                               _mergedStyle.StyleClass = value; 
+                       set
+                       {
+                               _mergedStyle.StyleClass = value;
                        }
                }
 
index 2fb191c..f5b1798 100644 (file)
@@ -61,7 +61,7 @@ namespace Xamarin.Forms
                        for (var i = 0; i < LogicalChildrenInternal.Count; i++)
                        {
                                var child = (View)LogicalChildrenInternal[i];
-                               if (child.IsVisible)
+                               if (child.IsVisible && layoutInformationCopy.Plots != null)
                                        LayoutChildIntoBoundingRegion(child, layoutInformationCopy.Plots[i], layoutInformationCopy.Requests[i]);
                        }
                }
@@ -95,7 +95,7 @@ namespace Xamarin.Forms
 
                void AlignOffAxis(LayoutInformation layout, StackOrientation orientation, double widthConstraint, double heightConstraint)
                {
-                       for (var i = 0; i < layout.Plots.Length; i++)
+                       for (var i = 0; i < layout.Plots?.Length; i++)
                        {
                                if (!((View)LogicalChildrenInternal[i]).IsVisible)
                                        continue;
index c197dec..976a3a3 100644 (file)
@@ -7,12 +7,20 @@ namespace Xamarin.Forms
        {
                public static readonly BindableProperty BackgroundColorProperty = BindableProperty.Create(nameof(BackgroundColor), typeof(Color), typeof(SwipeItem), Color.Default);
 
+               public static readonly BindableProperty IsVisibleProperty = BindableProperty.Create(nameof(IsVisible), typeof(bool), typeof(SwipeItem), true);
+
                public Color BackgroundColor
                {
                        get { return (Color)GetValue(BackgroundColorProperty); }
                        set { SetValue(BackgroundColorProperty, value); }
                }
 
+               public bool IsVisible
+               {
+                       get { return (bool)GetValue(IsVisibleProperty); }
+                       set { SetValue(IsVisibleProperty, value); }
+               }
+
                public event EventHandler<EventArgs> Invoked;
 
                [EditorBrowsable(EditorBrowsableState.Never)]
index e78b2aa..1e2de7d 100644 (file)
@@ -6,9 +6,12 @@ namespace Xamarin.Forms
 {
        public class SwipeItemView : ContentView, ISwipeItem
        {
-               public static readonly BindableProperty CommandProperty = BindableProperty.Create(nameof(Command), typeof(ICommand), typeof(SwipeItemView));
+               public static readonly BindableProperty CommandProperty = BindableProperty.Create(nameof(Command), typeof(ICommand), typeof(SwipeItemView), null,
+                       propertyChanging: (bo, o, n) => ((SwipeItemView)bo).OnCommandChanging(),
+                       propertyChanged: (bo, o, n) => ((SwipeItemView)bo).OnCommandChanged());
 
-               public static readonly BindableProperty CommandParameterProperty = BindableProperty.Create(nameof(CommandParameter), typeof(object), typeof(SwipeItemView));
+               public static readonly BindableProperty CommandParameterProperty = BindableProperty.Create(nameof(CommandParameter), typeof(object), typeof(SwipeItemView), null,
+                       propertyChanged: (bo, o, n) => ((SwipeItemView)bo).OnCommandParameterChanged());
 
                public ICommand Command
                {
@@ -32,5 +35,36 @@ namespace Xamarin.Forms
 
                        Invoked?.Invoke(this, EventArgs.Empty);
                }
+
+               void OnCommandChanged()
+               {
+                       IsEnabled = Command?.CanExecute(CommandParameter) ?? true;
+
+                       if (Command == null)
+                               return;
+
+                       Command.CanExecuteChanged += OnCommandCanExecuteChanged;
+               }
+
+               void OnCommandChanging()
+               {
+                       if (Command == null)
+                               return;
+
+                       Command.CanExecuteChanged -= OnCommandCanExecuteChanged;
+               }
+
+               void OnCommandParameterChanged()
+               {
+                       if (Command == null)
+                               return;
+
+                       IsEnabled = Command.CanExecute(CommandParameter);
+               }
+
+               void OnCommandCanExecuteChanged(object sender, EventArgs eventArgs)
+               {
+                       IsEnabled = Command.CanExecute(CommandParameter);
+               }
        }
 }
\ No newline at end of file
index 3bc6c3d..721628e 100644 (file)
@@ -11,10 +11,10 @@ namespace Xamarin.Forms.Platform.Tizen
                {
                }
 
-               public SwitchCellRenderer() : this("default")
+               public SwitchCellRenderer() : this(Device.Idiom == TargetIdiom.Watch ? "1text.1icon.1" : "default")
                {
                        MainPart = "elm.text";
-                       SwitchPart = "elm.swallow.end";
+                       SwitchPart = Device.Idiom == TargetIdiom.Watch ? "elm.icon" : "elm.swallow.end";
                }
 
                protected string MainPart { get; set; }
index 18b1382..ee62349 100644 (file)
@@ -1,10 +1,13 @@
 using System;
+using Xamarin.Forms.Platform.Tizen;
 
 namespace Xamarin.Forms
 {
        [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
        public sealed class ExportRendererAttribute : HandlerAttribute
        {
+               TargetIdiom Target { get; set; }
+
                public ExportRendererAttribute(Type handler, Type target) : this(handler, target, null)
                {
                }
@@ -12,5 +15,22 @@ namespace Xamarin.Forms
                public ExportRendererAttribute(Type handler, Type target, Type[] supportedVisuals) : base(handler, target, supportedVisuals)
                {
                }
+
+               public ExportRendererAttribute(Type handler, Type target, TargetIdiom targetIdiom) : this(handler, target, null, targetIdiom)
+               {
+               }
+
+               public ExportRendererAttribute(Type handler, Type target, Type[] supportedVisuals, TargetIdiom targetIdiom) : base(handler, target, supportedVisuals)
+               {
+                       Target = targetIdiom;
+               }
+
+               public override bool ShouldRegister()
+               {
+                       if (Target == TargetIdiom.Unsupported)
+                               return true;
+
+                       return (Target == Device.Idiom);
+               }
        }
 }
\ No newline at end of file
index 0b47129..9706a34 100644 (file)
@@ -225,6 +225,12 @@ namespace Xamarin.Forms
                        get; internal set;
                }
 
+               [EditorBrowsable(EditorBrowsableState.Never)]
+               public static Element RotaryFocusObject
+               {
+                       get; internal set;
+               }
+
                public static bool IsInitialized
                {
                        get;
@@ -412,6 +418,28 @@ namespace Xamarin.Forms
                                Device.info = info;
                        }
 
+                       string profile = ((TizenDeviceInfo)Device.Info).Profile;
+                       if (profile == "mobile")
+                       {
+                               Device.SetIdiom(TargetIdiom.Phone);
+                       }
+                       else if (profile == "tv")
+                       {
+                               Device.SetIdiom(TargetIdiom.TV);
+                       }
+                       else if (profile == "desktop")
+                       {
+                               Device.SetIdiom(TargetIdiom.Desktop);
+                       }
+                       else if (profile == "wearable")
+                       {
+                               Device.SetIdiom(TargetIdiom.Watch);
+                       }
+                       else
+                       {
+                               Device.SetIdiom(TargetIdiom.Unsupported);
+                       }
+
                        if (!Forms.IsInitialized && !Forms.IsPreloaded)
                        {
                                StaticRegistrar.RegisterHandlers(CircularUIForms.StaticHandlers);
@@ -519,33 +547,9 @@ namespace Xamarin.Forms
                                }
                        }
 
-                       if (!Forms.IsInitialized)
-                       {
-                               string profile = ((TizenDeviceInfo)Device.Info).Profile;
-                               if (profile == "mobile")
-                               {
-                                       Device.SetIdiom(TargetIdiom.Phone);
-                               }
-                               else if (profile == "tv")
-                               {
-                                       Device.SetIdiom(TargetIdiom.TV);
-                               }
-                               else if (profile == "desktop")
-                               {
-                                       Device.SetIdiom(TargetIdiom.Desktop);
-                               }
-                               else if (profile == "wearable")
-                               {
-                                       Device.SetIdiom(TargetIdiom.Watch);
-                               }
-                               else
-                               {
-                                       Device.SetIdiom(TargetIdiom.Unsupported);
-                               }
-                               Color.SetAccent(GetAccentColor(profile));
-                               ExpressionSearch.Default = new TizenExpressionSearch();
-                               IsInitialized = true;
-                       }
+                       Color.SetAccent(GetAccentColor(profile));
+                       ExpressionSearch.Default = new TizenExpressionSearch();
+                       IsInitialized = true;
                }
 
                static Color GetAccentColor(string profile)
index 956b645..7e8751a 100644 (file)
@@ -130,7 +130,11 @@ namespace Xamarin.Forms.Platform.Tizen
                        application.SendStart();
                        application.PropertyChanged += new PropertyChangedEventHandler(this.AppOnPropertyChanged);
                        SetPage(_application.MainPage);
-                       _useBezelInteration = Device.Idiom == TargetIdiom.Watch && Specific.GetUseBezelInteraction(_application);
+                       if (Device.Idiom == TargetIdiom.Watch)
+                       {
+                               _useBezelInteration = Specific.GetUseBezelInteraction(_application);
+                               UpdateOverlayContent();
+                       }
                }
 
                void AppOnPropertyChanged(object sender, PropertyChangedEventArgs args)
@@ -145,7 +149,24 @@ namespace Xamarin.Forms.Platform.Tizen
                                {
                                        _useBezelInteration = Specific.GetUseBezelInteraction(_application);
                                }
+                               else if (Specific.OverlayContentProperty.PropertyName == args.PropertyName)
+                               {
+                                       UpdateOverlayContent();
+                               }
+                       }
+               }
+
+               void UpdateOverlayContent()
+               {
+                       EvasObject nativeView = null;
+                       var content = Specific.GetOverlayContent(_application);
+                       if(content != null)
+                       {
+                               var renderer = Platform.GetOrCreateRenderer(content);
+                       (renderer as LayoutRenderer)?.RegisterOnLayoutUpdated();
+                               nativeView = renderer?.NativeView;
                        }
+                       Forms.BaseLayout.SetPartContent("elm.swallow.overlay", nativeView);
                }
 
                void SetPage(Page page)
@@ -177,6 +198,7 @@ namespace Xamarin.Forms.Platform.Tizen
                        MainWindow.Active();
                        MainWindow.Show();
 
+                       // in case of no use of preloaded window
                        if (BaseLayout == null)
                        {
                                var conformant = new Conformant(MainWindow);
index 52cee7c..da5bab5 100644 (file)
@@ -177,7 +177,7 @@ namespace Xamarin.Forms.Platform.Tizen.Native
 
                async void OnImageButtonClicked(object sender, EventArgs e)
                {
-                       if (BindingContext is MediaPlayer player)
+                       if (BindingContext is IMediaPlayer player)
                        {
                                if (player.State == PlayerState.Playing)
                                {
index faabd3d..f5931ee 100644 (file)
@@ -6,7 +6,6 @@ using System.Threading.Tasks;
 using Tizen.Multimedia;
 using Xamarin.Forms.PlatformConfiguration.TizenSpecific;
 
-[assembly: Xamarin.Forms.Dependency(typeof(IPlatformMediaPlayer))]
 namespace Xamarin.Forms.Platform.Tizen.Native
 {
        public class MediaPlayerImpl : IPlatformMediaPlayer
index 60d42b2..3f11956 100644 (file)
@@ -1,10 +1,12 @@
 using Xamarin.Forms;
 using Xamarin.Forms.Platform.Tizen;
+using MediaPlayerImpl = Xamarin.Forms.Platform.Tizen.Native.MediaPlayerImpl;
 
 [assembly: Dependency(typeof(ResourcesProvider))]
 [assembly: Dependency(typeof(Deserializer))]
 [assembly: Dependency(typeof(NativeBindingService))]
 [assembly: Dependency(typeof(NativeValueConverterService))]
+[assembly: Dependency(typeof(MediaPlayerImpl))]
 
 [assembly: ExportRenderer(typeof(Layout), typeof(LayoutRenderer))]
 [assembly: ExportRenderer(typeof(ScrollView), typeof(ScrollViewRenderer))]
@@ -60,4 +62,5 @@ using Xamarin.Forms.Platform.Tizen;
 [assembly: ExportHandler(typeof(TapGestureRecognizer), typeof(TapGestureHandler))]
 [assembly: ExportHandler(typeof(PinchGestureRecognizer), typeof(PinchGestureHandler))]
 [assembly: ExportHandler(typeof(PanGestureRecognizer), typeof(PanGestureHandler))]
-[assembly: ExportHandler(typeof(SwipeGestureRecognizer), typeof(SwipeGestureHandler))]
\ No newline at end of file
+[assembly: ExportHandler(typeof(SwipeGestureRecognizer), typeof(SwipeGestureHandler))]
+[assembly: ExportRenderer(typeof(Shell), typeof(Xamarin.Forms.Platform.Tizen.Watch.ShellRenderer), TargetIdiom.Watch)]
\ No newline at end of file
index d339695..f013917 100644 (file)
@@ -56,4 +56,4 @@ namespace Xamarin.Forms.Platform.Tizen
                        Control.UpdateSelectedIndex(Element.Position);
                }
        }
-}
+}
\ No newline at end of file
index 4cda34e..641a964 100644 (file)
@@ -33,6 +33,7 @@ namespace Xamarin.Forms.Platform.Tizen
                                _page.LayoutUpdated += OnLayoutUpdated;
                                SetNativeView(_page);
                        }
+
                        base.OnElementChanged(e);
                }
 
index 81c6524..a39ef4b 100644 (file)
@@ -61,7 +61,7 @@ namespace Xamarin.Forms.Platform.Tizen
                {
                        Element.SetValueFromRenderer(RadioButton.IsCheckedProperty, Control.GroupValue == 1 ? true : false);
                }
-               
+
                void UpdateIsChecked()
                {
                        Control.GroupValue = Element.IsChecked ? 1 : 0;
@@ -114,4 +114,4 @@ namespace Xamarin.Forms.Platform.Tizen
                        Control.EdjeObject.EmitSignal(emission, "elm");
                }
        }
-}
+}
\ No newline at end of file
index 285f76b..3577ceb 100644 (file)
@@ -115,11 +115,18 @@ namespace Xamarin.Forms.Platform.Tizen
                                if (Specific.GetUseBezelInteraction(Application.Current))
                                {
                                        if (enable)
+                                       {
                                                ri.RotaryWidget?.Activate();
+                                               Forms.RotaryFocusObject = Element;
+                                       }
                                        else
+                                       {
                                                ri.RotaryWidget?.Deactivate();
+                                               if (Forms.RotaryFocusObject == Element)
+                                                       Forms.RotaryFocusObject = null;
+                                       }
                                }
                        }
                }
        }
-}
+}
\ No newline at end of file
diff --git a/src/XSF/Xamarin.Forms.Platform.Tizen/Shell/Watch/IShellItemRenderer.cs b/src/XSF/Xamarin.Forms.Platform.Tizen/Shell/Watch/IShellItemRenderer.cs
new file mode 100644 (file)
index 0000000..05d2dbf
--- /dev/null
@@ -0,0 +1,11 @@
+using System;
+using ElmSharp;
+
+namespace Xamarin.Forms.Platform.Tizen.Watch
+{
+       public interface IShellItemRenderer : IDisposable
+       {
+               BaseShellItem Item { get; }
+               EvasObject NativeView { get; }
+       }
+}
diff --git a/src/XSF/Xamarin.Forms.Platform.Tizen/Shell/Watch/NavigationDrawer.cs b/src/XSF/Xamarin.Forms.Platform.Tizen/Shell/Watch/NavigationDrawer.cs
new file mode 100644 (file)
index 0000000..491f6f9
--- /dev/null
@@ -0,0 +1,540 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using ElmSharp;
+using ElmSharp.Wearable;
+using EButton = ElmSharp.Button;
+using EColor = ElmSharp.Color;
+using EImage = ElmSharp.Image;
+using ELayout = ElmSharp.Layout;
+using EWidget = ElmSharp.Widget;
+
+namespace Xamarin.Forms.Platform.Tizen.Watch
+{
+       public class NavigationDrawer : ELayout, IAnimatable
+       {
+               static readonly int TouchWidth = 50;
+               static readonly int IconSize = 40;
+               static readonly string DefaultIcon = "Xamarin.Forms.Platform.Tizen.Resource.wc_visual_cue.png";
+
+               Box _mainLayout;
+               Box _contentGestureBox;
+               Box _contentBox;
+               Box _drawerBox;
+               Box _drawerContentBox;
+               Box _drawerIconBox;
+
+               EvasObject _content;
+               EvasObject _drawerContent;
+
+               EImage _drawerIcon;
+               EButton _touchArea;
+
+               GestureLayer _gestureOnContent;
+               GestureLayer _gestureOnDrawer;
+
+               ImageSource _drawerIconSource;
+
+               bool _isOpen;
+               bool _isDefaultIcon;
+
+               CancellationTokenSource _fadeInCancelTokenSource = null;
+
+               bool HasDrawer => _drawerBox != null;
+
+               public NavigationDrawer(EvasObject parent) : base(parent)
+               {
+                       Initialize();
+               }
+
+               public int HandlerHeight { get; set; } = 40;
+
+               public bool IsOpen
+               {
+                       get
+                       {
+                               return _isOpen;
+                       }
+                       set
+                       {
+                               if (_isOpen != value)
+                               {
+                                       if (value)
+                                       {
+                                               Open();
+                                       }
+                                       else
+                                       {
+                                               Close();
+                                       }
+                               }
+                       }
+               }
+
+               EColor _handlerBackgroundColor = EColor.Transparent;
+               public EColor HandlerBackgroundColor
+               {
+                       get => _handlerBackgroundColor;
+                       set
+                       {
+                               _handlerBackgroundColor = value;
+                               UpdateHandlerBackgroundColor();
+                       }
+               }
+
+               public event EventHandler Toggled;
+
+               public void SetMainContent(EvasObject content)
+               {
+                       if (content == null)
+                       {
+                               UnsetMainContent();
+                               return;
+                       }
+
+                       _content = content;
+                       _content.Show();
+                       _contentBox.PackEnd(_content);
+                       _content.Geometry = _contentBox.Geometry;
+               }
+
+               public void SetDrawerContent(EvasObject content)
+               {
+                       InitializeDrawerBox();
+
+                       if (content == null)
+                       {
+                               UnsetDrawerContent();
+                               return;
+                       }
+
+                       _drawerContent = content;
+                       _drawerContent.Show();
+                       _drawerContentBox.PackEnd(_drawerContent);
+
+                       _drawerContentBox.Show();
+                       _drawerIconBox.Show();
+
+                       if (_drawerContent is NavigationView nv)
+                       {
+                               nv.Dragged += (s, e) =>
+                               {
+                                       if (e.State == DraggedState.EdgeTop)
+                                       {
+                                               Close();
+                                       }
+                               };
+                       }
+               }
+
+               public void UpdateDrawerIcon(ImageSource source)
+               {
+                       _drawerIconSource = source;
+                       if (HasDrawer)
+                       {
+                               SetDrawerIcon(_drawerIconSource);
+                       }
+               }
+
+               public async void Open(uint length = 300)
+               {
+                       if (!HasDrawer)
+                               return;
+
+                       var toMove = _drawerBox.Geometry;
+                       toMove.Y = 0;
+
+                       await RunMoveAnimation(_drawerBox, toMove, length);
+
+                       if (!_isOpen)
+                       {
+                               _isOpen = true;
+                               Toggled?.Invoke(this, EventArgs.Empty);
+                       }
+                       OnLayout();
+                       OnDrawerLayout();
+                       FlipIcon();
+               }
+
+               public async void Close(uint length = 300)
+               {
+                       if (!HasDrawer)
+                               return;
+
+                       var toMove = _drawerBox.Geometry;
+                       toMove.Y = Geometry.Height - HandlerHeight;
+
+                       await RunMoveAnimation(_drawerBox, toMove, length);
+
+                       if (_isOpen)
+                       {
+                               _isOpen = false;
+                               Toggled?.Invoke(this, EventArgs.Empty);
+                       }
+                       OnLayout();
+                       OnDrawerLayout();
+                       ResetIcon();
+                       StartHighlightAnimation(_drawerIcon);
+               }
+
+               void IAnimatable.BatchBegin()
+               {
+               }
+
+               void IAnimatable.BatchCommit()
+               {
+               }
+
+               protected override IntPtr CreateHandle(EvasObject parent)
+               {
+                       _mainLayout = new Box(parent);
+                       return _mainLayout.Handle;
+               }
+
+               void Initialize()
+               {
+                       _mainLayout.SetLayoutCallback(OnLayout);
+
+                       _contentGestureBox = new Box(_mainLayout);
+                       _contentGestureBox.Show();
+                       _mainLayout.PackEnd(_contentGestureBox);
+
+                       _contentBox = new Box(_mainLayout);
+                       _contentBox.SetLayoutCallback(OnContentLayout);
+                       _contentBox.Show();
+                       _mainLayout.PackEnd(_contentBox);
+               }
+
+               void InitializeDrawerBox()
+               {
+                       if (_drawerBox != null)
+                               return;
+
+                       _drawerBox = new Box(_mainLayout);
+                       _drawerBox.SetLayoutCallback(OnDrawerLayout);
+                       _drawerBox.Show();
+                       _mainLayout.PackEnd(_drawerBox);
+
+                       _drawerContentBox = new Box(_drawerBox);
+                       _drawerBox.PackEnd(_drawerContentBox);
+
+                       _drawerIconBox = new Box(_drawerBox)
+                       {
+                               BackgroundColor = _handlerBackgroundColor
+                       };
+                       _drawerBox.PackEnd(_drawerIconBox);
+
+                       _drawerIcon = new EImage(_drawerIconBox)
+                       {
+                               AlignmentY = 0.5,
+                               AlignmentX = 0.5,
+                               MinimumHeight = IconSize,
+                               MinimumWidth = IconSize,
+                       };
+                       _drawerIcon.Show();
+                       _drawerIconBox.PackEnd(_drawerIcon);
+                       SetDrawerIcon(_drawerIconSource);
+
+                       _touchArea = new EButton(_drawerBox)
+                       {
+                               Color = EColor.Transparent,
+                               BackgroundColor = EColor.Transparent,
+                       };
+                       _touchArea.SetPartColor("effect", EColor.Transparent);
+                       _touchArea.Show();
+                       _touchArea.RepeatEvents = true;
+                       _touchArea.Clicked += OnIconClicked;
+
+                       _drawerBox.PackEnd(_touchArea);
+
+                       _gestureOnContent = new GestureLayer(_contentGestureBox);
+                       _gestureOnContent.SetMomentumCallback(GestureLayer.GestureState.Start, OnContentDragStarted);
+                       _gestureOnContent.SetMomentumCallback(GestureLayer.GestureState.End, OnContentDragEnded);
+                       _gestureOnContent.SetMomentumCallback(GestureLayer.GestureState.Abort, OnContentDragEnded);
+                       _gestureOnContent.Attach(_contentGestureBox);
+                       _contentBox.RepeatEvents = true;
+
+                       _gestureOnDrawer = new GestureLayer(_drawerIconBox);
+                       _gestureOnDrawer.SetMomentumCallback(GestureLayer.GestureState.Move, OnDrawerDragged);
+                       _gestureOnDrawer.SetMomentumCallback(GestureLayer.GestureState.End, OnDrawerDragEnded);
+                       _gestureOnDrawer.SetMomentumCallback(GestureLayer.GestureState.Abort, OnDrawerDragEnded);
+                       _gestureOnDrawer.Attach(_drawerIconBox);
+
+                       RotaryEventManager.Rotated += OnRotateEventReceived;
+               }
+
+               void SetDrawerIcon(ImageSource source)
+               {
+                       if (source == null)
+                       {
+                               _ = _drawerIcon.LoadFromImageSourceAsync(ImageSource.FromResource(DefaultIcon, GetType().Assembly));
+                               _isDefaultIcon = true;
+                       }
+                       else
+                       {
+                               _isDefaultIcon = false;
+                               if (source is FileImageSource fsource)
+                               {
+                                       _drawerIcon.Load(fsource.ToAbsPath());
+                               }
+                               else
+                               {
+                                       _ = _drawerIcon.LoadFromImageSourceAsync(source);
+                               }
+                       }
+               }
+
+               void UpdateHandlerBackgroundColor()
+               {
+                       if (_drawerIconBox != null)
+                       {
+                               _drawerIconBox.BackgroundColor = _handlerBackgroundColor;
+                       }
+               }
+
+               void OnIconClicked(object sender, EventArgs e)
+               {
+                       if (IsOpen)
+                               Close();
+                       else
+                               Open();
+               }
+
+               async Task<bool> ShowAsync(EWidget target, Easing easing = null, uint length = 300, CancellationToken cancelltaionToken = default(CancellationToken))
+               {
+                       var tcs = new TaskCompletionSource<bool>();
+
+                       await Task.Delay(1000);
+
+                       if (cancelltaionToken.IsCancellationRequested)
+                       {
+                               cancelltaionToken.ThrowIfCancellationRequested();
+                       }
+
+                       target.Show();
+                       var opacity = target.Opacity;
+
+                       if (opacity == 255 || opacity == -1)
+                               return true;
+
+                       new Animation((progress) =>
+                       {
+                               target.Opacity = opacity + (int)((255 - opacity) * progress);
+
+                       }).Commit(this, "FadeIn", length: length, finished: (p, e) =>
+                       {
+                               target.Opacity = 255;
+                               tcs.SetResult(true);
+                               StartHighlightAnimation(_drawerIcon);
+                       });
+
+                       return await tcs.Task;
+               }
+
+               void OnLayout()
+               {
+                       var bound = Geometry;
+                       _contentGestureBox.Geometry = bound;
+                       _contentBox.Geometry = bound;
+                       if (_drawerBox != null)
+                       {
+                               bound.Y = _isOpen ? 0 : (bound.Height - HandlerHeight);
+                               _drawerBox.Geometry = bound;
+                       }
+               }
+
+               void OnContentLayout()
+               {
+                       if (_content != null)
+                       {
+                               _content.Geometry = _contentBox.Geometry;
+                       }
+               }
+
+               void OnDrawerLayout()
+               {
+                       this.AbortAnimation("HighlightAnimation");
+
+                       var bound = _drawerBox.Geometry;
+
+                       var currentY = bound.Y;
+                       var ratio = currentY / (double)(Geometry.Height - HandlerHeight);
+
+                       var contentBound = bound;
+                       contentBound.Y += (int)(HandlerHeight * ratio);
+                       _drawerContentBox.Geometry = contentBound;
+
+                       var drawerHandleBound = bound;
+                       drawerHandleBound.Height = HandlerHeight;
+                       _drawerIconBox.Geometry = drawerHandleBound;
+
+                       var drawerTouchBound = drawerHandleBound;
+                       drawerTouchBound.Width = TouchWidth;
+                       drawerTouchBound.X = drawerHandleBound.X + (drawerHandleBound.Width - TouchWidth) / 2;
+                       _touchArea.Geometry = drawerTouchBound;
+               }
+
+               async Task<bool> HideAsync(EWidget target, Easing easing = null, uint length = 300)
+               {
+                       var tcs = new TaskCompletionSource<bool>();
+
+                       var opacity = target.Opacity;
+                       if (opacity == -1)
+                               opacity = 255;
+
+                       new Animation((progress) =>
+                       {
+                               target.Opacity = opacity - (int)(progress * opacity);
+
+                       }).Commit(this, "FadeOut", length: length, finished: (p, e) =>
+                       {
+                               target.Opacity = 0;
+                               target.Hide();
+                               tcs.SetResult(true);
+                       });
+
+                       return await tcs.Task;
+               }
+
+               void StartHighlightAnimation(EWidget target)
+               {
+                       if (!_isDefaultIcon || this.AnimationIsRunning("HighlightAnimation"))
+                               return;
+
+                       int count = 2;
+                       var bound = target.Geometry;
+                       var y = bound.Y;
+                       var dy = bound.Y - bound.Height / 3;
+
+                       var anim = new Animation();
+
+                       var transfAnim = new Animation((f) =>
+                       {
+                               bound.Y = (int)f;
+                               var map = new EvasMap(4);
+                               map.PopulatePoints(bound, 0);
+                               target.IsMapEnabled = true;
+                               target.EvasMap = map;
+                       }, y, dy);
+
+                       var opacityAnim = new Animation(f => target.Opacity = (int)f, 255, 40);
+
+                       anim.Add(0, 1, opacityAnim);
+                       anim.Add(0, 1, transfAnim);
+
+                       anim.Commit(this, "HighlightAnimation", 16, 800, finished: (f, b) =>
+                       {
+                               target.Opacity = 255;
+                               target.IsMapEnabled = false;
+                       }, repeat: () => --count > 0);
+               }
+
+               async void OnRotateEventReceived(EventArgs args)
+               {
+                       _fadeInCancelTokenSource?.Cancel();
+                       _fadeInCancelTokenSource = new CancellationTokenSource();
+
+                       if (!_isOpen)
+                       {
+                               var token = _fadeInCancelTokenSource.Token;
+                               await HideAsync(_drawerBox);
+                               _ = ShowAsync(_drawerBox, cancelltaionToken: token);
+                       }
+               }
+
+               void OnContentDragStarted(GestureLayer.MomentumData moment)
+               {
+                       _fadeInCancelTokenSource?.Cancel();
+                       _fadeInCancelTokenSource = null;
+
+                       if (!_isOpen)
+                       {
+                               _ = HideAsync(_drawerBox);
+                       }
+               }
+
+               void OnContentDragEnded(GestureLayer.MomentumData moment)
+               {
+                       _fadeInCancelTokenSource = new CancellationTokenSource();
+                       _ = ShowAsync(_drawerBox, cancelltaionToken: _fadeInCancelTokenSource.Token);
+               }
+
+               void OnDrawerDragged(GestureLayer.MomentumData moment)
+               {
+                       var toMove = _drawerBox.Geometry;
+                       toMove.Y = (moment.Y2 < 0) ? 0 : moment.Y2;
+                       _drawerBox.Geometry = toMove;
+                       OnDrawerLayout();
+               }
+
+               void OnDrawerDragEnded(GestureLayer.MomentumData moment)
+               {
+                       if (_drawerBox.Geometry.Y < (_mainLayout.Geometry.Height / 2))
+                       {
+                               Open();
+                       }
+                       else
+                       {
+                               Close();
+                       }
+               }
+
+               void FlipIcon()
+               {
+                       if (_isDefaultIcon)
+                       {
+                               _drawerIcon.Orientation = ImageOrientation.FlipVertical;
+                       }
+               }
+
+               void ResetIcon()
+               {
+                       _drawerIcon.Orientation = ImageOrientation.None;
+               }
+
+               Task RunMoveAnimation(EvasObject target, Rect dest, uint length, Easing easing = null)
+               {
+                       var tcs = new TaskCompletionSource<bool>();
+
+                       var dx = target.Geometry.X - dest.X;
+                       var dy = target.Geometry.Y - dest.Y;
+
+                       new Animation((progress) =>
+                       {
+                               var toMove = dest;
+                               toMove.X += (int)(dx * (1 - progress));
+                               toMove.Y += (int)(dy * (1 - progress));
+                               target.Geometry = toMove;
+                               OnDrawerLayout();
+                       }).Commit(this, "Move", length: length, finished: (s, e) =>
+                       {
+                               target.Geometry = dest;
+                               tcs.SetResult(true);
+                       });
+                       return tcs.Task;
+               }
+
+               void UnsetMainContent()
+               {
+                       if (_content != null)
+                       {
+                               _contentBox.UnPack(_content);
+                               _content.Hide();
+                               _content = null;
+                       }
+               }
+
+               void UnsetDrawerContent()
+               {
+                       if (_drawerContent != null)
+                       {
+                               _drawerContentBox.UnPack(_drawerContent);
+                               _drawerContent.Hide();
+                               _drawerContent = null;
+
+                               _drawerContentBox.Hide();
+                               _drawerIconBox.Hide();
+                       }
+               }
+       }
+}
\ No newline at end of file
diff --git a/src/XSF/Xamarin.Forms.Platform.Tizen/Shell/Watch/NavigationView.cs b/src/XSF/Xamarin.Forms.Platform.Tizen/Shell/Watch/NavigationView.cs
new file mode 100644 (file)
index 0000000..630f18f
--- /dev/null
@@ -0,0 +1,363 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using ElmSharp;
+using ElmSharp.Wearable;
+using EColor = ElmSharp.Color;
+using ELayout = ElmSharp.Layout;
+
+namespace Xamarin.Forms.Platform.Tizen.Watch
+{
+       public class NavigationView : ELayout
+       {
+               readonly int _dafaultIconSize = 60;
+
+               class Item : INotifyPropertyChanged
+               {
+                       Element _source;
+                       public Element Source
+                       {
+                               get
+                               {
+                                       return _source;
+                               }
+                               set
+                               {
+                                       if (_source != null)
+                                       {
+                                               _source.PropertyChanged -= OnElementPropertyChanged;
+                                       }
+                                       _source = value;
+                                       _source.PropertyChanged += OnElementPropertyChanged;
+                                       UpdateContent();
+                               }
+                       }
+
+                       public string Text { get; set; }
+                       public string Icon { get; set; }
+
+                       public event PropertyChangedEventHandler PropertyChanged;
+
+                       void UpdateContent()
+                       {
+                               if (Source is BaseShellItem shellItem)
+                               {
+                                       Text = shellItem.Title;
+                                       Icon = (shellItem.Icon as FileImageSource)?.ToAbsPath();
+                               }
+                               else if (Source is MenuItem menuItem)
+                               {
+                                       Text = menuItem.Text;
+                                       Icon = (menuItem.IconImageSource as FileImageSource)?.ToAbsPath();
+                               }
+                               else
+                               {
+                                       Text = null;
+                                       Icon = null;
+                               }
+                               SendPropertyChanged();
+                       }
+
+                       void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+                       {
+                               UpdateContent();
+                       }
+
+                       void SendPropertyChanged([CallerMemberName] string propertyName = "")
+                       {
+                               PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+                       }
+               }
+
+               Box _outterBox;
+               ELayout _surfaceLayout;
+               CircleSurface _surface;
+               CircleGenList _naviMenu;
+
+               GenItemClass _defaultClass;
+               SmartEvent _draggedUpCallback;
+               SmartEvent _draggedDownCallback;
+
+               GenListItem _header;
+               GenListItem _footer;
+
+               List<List<Element>> _itemCache;
+               List<GenListItem> _items = new List<GenListItem>();
+
+               public NavigationView(EvasObject parent) : base(parent)
+               {
+                       InitializeComponent();
+               }
+
+               public event EventHandler<SelectedItemChangedEventArgs> ItemSelected;
+
+               public event EventHandler<DraggedEventArgs> Dragged;
+
+
+               EColor _backgroundColor = EColor.Black;
+               public override EColor BackgroundColor
+               {
+                       get => _backgroundColor;
+                       set
+                       {
+                               _backgroundColor = value.IsDefault ? EColor.Black : value;
+                               UpdateBackgroundColor();
+                       }
+               }
+
+               EColor _foregroundColor = EColor.Default;
+               public EColor ForegroundColor
+               {
+                       get => _foregroundColor;
+                       set
+                       {
+                               _foregroundColor = value;
+                               UpdateForegroundColor();
+                       }
+               }
+
+
+               public void Build(List<List<Element>> items)
+               {
+                       // Only update when items was changed
+                       if (!IsUpdated(items))
+                       {
+                               return;
+                       }
+                       _itemCache = items;
+
+                       ClearItemPropertyChangedHandler();
+                       _naviMenu.Clear();
+                       _items.Clear();
+                       // header
+                       _header = _naviMenu.Append(_defaultClass, new Item { Text = "" });
+
+                       // TODO. need to improve, need to support group
+                       foreach (var group in items)
+                       {
+                               foreach (var item in group)
+                               {
+                                       var data = new Item
+                                       {
+                                               Source = item
+                                       };
+                                       if (item is BaseShellItem shellItem)
+                                       {
+                                               data.Text = shellItem.Title;
+                                               data.Icon = (shellItem.Icon as FileImageSource)?.ToAbsPath();
+                                       }
+                                       else if (item is MenuItem menuItem)
+                                       {
+                                               data.Text = menuItem.Text;
+                                               data.Icon = (menuItem.IconImageSource as FileImageSource)?.ToAbsPath();
+                                       }
+                                       var genitem = _naviMenu.Append(_defaultClass, data, GenListItemType.Normal);
+                                       genitem.SetPartColor("bg", _backgroundColor);
+                                       _items.Add(genitem);
+                                       data.PropertyChanged += OnItemPropertyChanged;
+                               }
+                       }
+                       _footer = _naviMenu.Append(_defaultClass, new Item { Text = "" });
+               }
+
+               public void Activate()
+               {
+                       (_naviMenu as IRotaryActionWidget)?.Activate();
+               }
+               public void Deactivate()
+               {
+                       (_naviMenu as IRotaryActionWidget)?.Deactivate();
+               }
+
+               protected override IntPtr CreateHandle(EvasObject parent)
+               {
+                       _outterBox = new Box(parent);
+                       return _outterBox.Handle;
+               }
+
+               void InitializeComponent()
+               {
+                       _outterBox.SetLayoutCallback(OnLayout);
+
+                       _surfaceLayout = new ELayout(this);
+                       _surfaceLayout.Show();
+                       _surface = new CircleSurface(_surfaceLayout);
+
+                       _naviMenu = new CircleGenList(this, _surface)
+                       {
+                               Homogeneous = true,
+                               BackgroundColor = _backgroundColor
+                       };
+                       _naviMenu.Show();
+
+                       _draggedUpCallback = new SmartEvent(_naviMenu, "drag,start,up");
+                       _draggedUpCallback.On += (s, e) =>
+                       {
+                               if (_footer.TrackObject.IsVisible)
+                               {
+                                       Dragged?.Invoke(this, new DraggedEventArgs(DraggedState.EdgeBottom));
+                               }
+                               else
+                               {
+                                       Dragged?.Invoke(this, new DraggedEventArgs(DraggedState.Up));
+                               }
+                       };
+
+                       _draggedDownCallback = new SmartEvent(_naviMenu, "drag,start,down");
+                       _draggedDownCallback.On += (s, e) =>
+                       {
+                               if (_header.TrackObject.IsVisible)
+                               {
+                                       Dragged?.Invoke(this, new DraggedEventArgs(DraggedState.EdgeTop));
+                               }
+                               else
+                               {
+                                       Dragged?.Invoke(this, new DraggedEventArgs(DraggedState.Down));
+                               }
+                       };
+
+                       _outterBox.PackEnd(_naviMenu);
+                       _outterBox.PackEnd(_surfaceLayout);
+
+                       _surfaceLayout.StackAbove(_naviMenu);
+
+                       _defaultClass = new GenItemClass("1icon_1text")
+                       {
+                               GetTextHandler = (obj, part) =>
+                               {
+                                       if (part == "elm.text")
+                                       {
+                                               var text = (obj as Item).Text;
+                                               if (_foregroundColor != EColor.Default)
+                                                       return $"<span color='{_foregroundColor.ToHex()}'>{text}</span>";
+                                               else
+                                                       return text;
+                                       }
+                                       return null;
+                               },
+                               GetContentHandler = (obj, part) =>
+                               {
+                                       if (part == "elm.swallow.icon" && obj is Item menuItem && !string.IsNullOrEmpty(menuItem.Icon))
+                                       {
+                                               var icon = new ElmSharp.Image(Xamarin.Forms.Forms.NativeParent)
+                                               {
+                                                       AlignmentX = -1,
+                                                       AlignmentY = -1,
+                                                       WeightX = 1.0,
+                                                       WeightY = 1.0,
+                                                       MinimumWidth = _dafaultIconSize,
+                                                       MinimumHeight = _dafaultIconSize,
+                                               };
+                                               icon.Show();
+                                               icon.Load(menuItem.Icon);
+                                               return icon;
+                                       }
+                                       return null;
+                               }
+                       };
+
+                       _naviMenu.ItemSelected += OnItemSelected;
+
+               }
+
+               void OnItemSelected(object sender, GenListItemEventArgs e)
+               {
+                       ItemSelected?.Invoke(this, new SelectedItemChangedEventArgs((e.Item.Data as Item).Source, -1));
+               }
+
+               void OnLayout()
+               {
+                       _surfaceLayout.Geometry = Geometry;
+                       _naviMenu.Geometry = Geometry;
+               }
+
+               void UpdateBackgroundColor()
+               {
+                       _naviMenu.BackgroundColor = _backgroundColor;
+                       foreach (var item in _items)
+                       {
+                               item.SetPartColor("bg", _backgroundColor);
+                       }
+               }
+
+               void UpdateForegroundColor()
+               {
+                       foreach (var item in _items)
+                       {
+                               item.Update();
+                       }
+               }
+
+               bool IsUpdated(List<List<Element>> items)
+               {
+                       if (_itemCache == null)
+                               return true;
+
+                       if (_itemCache.Count != items.Count)
+                               return true;
+
+                       for (int i = 0; i < items.Count; i++)
+                       {
+                               if (_itemCache[i].Count != items[i].Count)
+                                       return true;
+
+                               for (int j = 0; j < items[i].Count; j++)
+                               {
+                                       if (_itemCache[i][j] != items[i][j])
+                                               return true;
+                               }
+                       }
+                       return false;
+               }
+
+               void ClearItemPropertyChangedHandler()
+               {
+                       foreach (var item in _items)
+                       {
+                               (item.Data as Item).PropertyChanged -= OnItemPropertyChanged;
+                       }
+               }
+
+               void OnItemPropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       var item = _items.Where((d) => d.Data == sender).FirstOrDefault();
+                       item?.Update();
+               }
+
+       }
+       public enum DraggedState
+       {
+               EdgeTop,
+               Up,
+               Down,
+               EdgeBottom,
+       }
+
+       public class DraggedEventArgs
+       {
+               public DraggedState State { get; private set; }
+
+               public DraggedEventArgs(DraggedState state)
+               {
+                       State = state;
+               }
+       }
+
+       static class FileImageSourceEX
+       {
+               public static string ToAbsPath(this FileImageSource source)
+               {
+                       return ResourcePath.GetPath(source.File);
+               }
+       }
+
+       static class ColorEX
+       {
+               public static string ToHex(this EColor c)
+               {
+                       return string.Format("#{0:X2}{1:X2}{2:X2}{3:X2}", c.R, c.G, c.B, c.A);
+               }
+       }
+}
diff --git a/src/XSF/Xamarin.Forms.Platform.Tizen/Shell/Watch/ShellContentRenderer.cs b/src/XSF/Xamarin.Forms.Platform.Tizen/Shell/Watch/ShellContentRenderer.cs
new file mode 100644 (file)
index 0000000..1e28fc6
--- /dev/null
@@ -0,0 +1,38 @@
+using ElmSharp;
+
+namespace Xamarin.Forms.Platform.Tizen.Watch
+{
+       public class ShellContentRenderer : IShellItemRenderer
+       {
+               public ShellContentRenderer(ShellContent content)
+               {
+                       ShellContent = content;
+                       NativeView = GetNativeView(content);
+               }
+
+               public ShellContent ShellContent { get; protected set; }
+
+               public BaseShellItem Item => ShellContent;
+
+               public EvasObject NativeView { get; protected set; }
+
+               public void Dispose()
+               {
+                       Dispose(true);
+               }
+
+               protected virtual void Dispose(bool disposing)
+               {
+                       if (disposing)
+                       {
+                               NativeView?.Unrealize();
+                       }
+               }
+
+               static EvasObject GetNativeView(ShellContent content)
+               {
+                       var page = (content as IShellContentController).GetOrCreateContent();
+                       return Platform.GetOrCreateRenderer(page).NativeView;
+               }
+       }
+}
diff --git a/src/XSF/Xamarin.Forms.Platform.Tizen/Shell/Watch/ShellItemRenderer.cs b/src/XSF/Xamarin.Forms.Platform.Tizen/Shell/Watch/ShellItemRenderer.cs
new file mode 100644 (file)
index 0000000..f566cb5
--- /dev/null
@@ -0,0 +1,95 @@
+using System.Collections.Generic;
+using System.ComponentModel;
+using ElmSharp;
+
+namespace Xamarin.Forms.Platform.Tizen.Watch
+{
+       public class ShellItemRenderer : IShellItemRenderer
+       {
+               Box _mainLayout;
+               EvasObject _currentItem;
+               Dictionary<BaseShellItem, IShellItemRenderer> _rendererCache = new Dictionary<BaseShellItem, IShellItemRenderer>();
+
+               public ShellItemRenderer(ShellItem item)
+               {
+                       ShellItem = item;
+                       ShellItem.PropertyChanged += OnItemPropertyChanged;
+                       InitializeComponent();
+                       UpdateCurrentItem();
+               }
+
+               public ShellItem ShellItem { get; protected set; }
+
+               public BaseShellItem Item => ShellItem;
+
+               public EvasObject NativeView => _mainLayout;
+
+               public void Dispose()
+               {
+                       Dispose(true);
+               }
+
+               protected virtual void Dispose(bool disposing)
+               {
+                       if (disposing)
+                       {
+                               ResetCurrentItem();
+                               ShellItem.PropertyChanged -= OnItemPropertyChanged;
+                       }
+               }
+
+               void InitializeComponent()
+               {
+                       _mainLayout = new Box(Forms.NativeParent);
+                       _mainLayout.SetLayoutCallback(OnLayout);
+               }
+
+               void UpdateCurrentItem()
+               {
+                       ResetCurrentItem();
+                       var currentItem = ShellItem.CurrentItem;
+                       if (currentItem != null)
+                       {
+                               if (!_rendererCache.TryGetValue(currentItem, out IShellItemRenderer renderer))
+                               {
+                                       renderer = ShellRendererFactory.Default.CreateShellNavigationRenderer(currentItem);
+                                       _rendererCache[currentItem] = renderer;
+                               }
+                               SetCurrentItem(renderer.NativeView);
+                       }
+               }
+
+               void SetCurrentItem(EvasObject item)
+               {
+                       _currentItem = item;
+                       _currentItem.Show();
+                       _mainLayout.PackEnd(_currentItem);
+               }
+
+               void ResetCurrentItem()
+               {
+                       if (_currentItem != null)
+                       {
+                               _mainLayout.UnPack(_currentItem);
+                               _currentItem.Hide();
+                               _currentItem = null;
+                       }
+               }
+
+               void OnItemPropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       if (e.PropertyName == nameof(ShellItem.CurrentItem))
+                       {
+                               UpdateCurrentItem();
+                       }
+               }
+
+               void OnLayout()
+               {
+                       if (_currentItem != null)
+                       {
+                               _currentItem.Geometry = _mainLayout.Geometry;
+                       }
+               }
+       }
+}
diff --git a/src/XSF/Xamarin.Forms.Platform.Tizen/Shell/Watch/ShellRenderer.cs b/src/XSF/Xamarin.Forms.Platform.Tizen/Shell/Watch/ShellRenderer.cs
new file mode 100644 (file)
index 0000000..0baa595
--- /dev/null
@@ -0,0 +1,205 @@
+using ElmSharp;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Xamarin.Forms;
+
+namespace Xamarin.Forms.Platform.Tizen.Watch
+{
+       public class ShellRenderer : VisualElementRenderer<Shell>
+       {
+               NavigationDrawer _drawer;
+               NavigationView _navigationView;
+
+               Dictionary<BaseShellItem, IShellItemRenderer> _rendererCache = new Dictionary<BaseShellItem, IShellItemRenderer>();
+
+               public ShellRenderer()
+               {
+                       RegisterPropertyHandler(Shell.CurrentItemProperty, UpdateCurrentItem);
+                       RegisterPropertyHandler(Shell.FlyoutIsPresentedProperty, UpdateFlyoutIsPresented);
+                       RegisterPropertyHandler(Shell.FlyoutBehaviorProperty, UpdateFlyoutBehavior);
+                       RegisterPropertyHandler(Shell.FlyoutIconProperty, UpdateFlyoutIcon);
+                       RegisterPropertyHandler(Shell.FlyoutBackgroundColorProperty, UpdateFlyoutBackgroundColor);
+               }
+
+               protected override void OnElementChanged(ElementChangedEventArgs<Shell> e)
+               {
+                       InitializeComponent();
+                       base.OnElementChanged(e);
+               }
+
+               protected override void OnElementReady()
+               {
+                       base.OnElementReady();
+                       UpdateFlyoutMenu();
+                       (Element as IShellController).StructureChanged += OnNavigationStructureChanged;
+               }
+
+               protected override void Dispose(bool disposing)
+               {
+                       if (disposing)
+                       {
+                               foreach (var renderer in _rendererCache.Values)
+                               {
+                                       renderer.Dispose();
+                               }
+                               (Element as IShellController).StructureChanged -= OnNavigationStructureChanged;
+                       }
+                       base.Dispose(disposing);
+               }
+
+               void InitializeComponent()
+               {
+                       if (_drawer == null)
+                       {
+                               _drawer = new NavigationDrawer(Forms.NativeParent);
+                               _drawer.IsOpen = Element.FlyoutIsPresented;
+                               _drawer.Toggled += OnNavigationDrawerToggled;
+                               SetNativeView(_drawer);
+                       }
+               }
+
+               void OnNavigationStructureChanged(object sender, EventArgs e)
+               {
+                       UpdateFlyoutMenu();
+               }
+
+               void UpdateFlyoutMenu()
+               {
+                       if (Element.FlyoutBehavior == FlyoutBehavior.Disabled)
+                               return;
+
+                       var flyoutItems = (Element as IShellController).GenerateFlyoutGrouping();
+                       int itemCount = 0;
+                       foreach (var item in flyoutItems)
+                       {
+                               itemCount += item.Count;
+                       }
+
+                       if (itemCount > 1)
+                       {
+                               InitializeNavigationDrawer();
+                               _navigationView.Build(flyoutItems);
+                       }
+                       else
+                       {
+                               DeinitializeNavigationView();
+                       }
+               }
+
+               void InitializeNavigationDrawer()
+               {
+                       if (_navigationView != null)
+                       {
+                               return;
+                       }
+
+                       _navigationView = new NavigationView(Forms.NativeParent)
+                       {
+                               AlignmentX = -1,
+                               AlignmentY = -1,
+                               WeightX = 1,
+                               WeightY = 1,
+                       };
+                       _navigationView.Show();
+                       _navigationView.ItemSelected += OnMenuItemSelected;
+
+                       _drawer.SetDrawerContent(_navigationView);
+               }
+
+               protected virtual void OnNavigationDrawerToggled(object sender, EventArgs e)
+               {
+                       if (_drawer.IsOpen)
+                       {
+                               _navigationView.Activate();
+                       }
+                       else
+                       {
+                               _navigationView.Deactivate();
+                       }
+
+                       Element.SetValueFromRenderer(Shell.FlyoutIsPresentedProperty, _drawer.IsOpen);
+               }
+
+               void DeinitializeNavigationView()
+               {
+                       if (_navigationView == null)
+                               return;
+                       _drawer.SetDrawerContent(null);
+                       _navigationView.Unrealize();
+                       _navigationView = null;
+               }
+
+               void OnMenuItemSelected(object sender, SelectedItemChangedEventArgs e)
+               {
+                       ((IShellController)Element).OnFlyoutItemSelected(e.SelectedItem as Element);
+               }
+
+               void UpdateCurrentItem()
+               {
+                       ResetCurrentItem();
+                       if (Element.CurrentItem != null)
+                       {
+                               if (!_rendererCache.TryGetValue(Element.CurrentItem, out IShellItemRenderer renderer))
+                               {
+                                       renderer = ShellRendererFactory.Default.CreateItemRenderer(Element.CurrentItem);
+                                       _rendererCache[Element.CurrentItem] = renderer;
+                               }
+                               SetCurrentItem(renderer.NativeView);
+                       }
+               }
+
+               void UpdateFlyoutBehavior(bool init)
+               {
+                       if (init)
+                               return;
+
+                       if (Element.FlyoutBehavior == FlyoutBehavior.Disabled)
+                       {
+                               DeinitializeNavigationView();
+                       }
+                       else if (Element.FlyoutBehavior == FlyoutBehavior.Flyout)
+                       {
+                               UpdateFlyoutMenu();
+                       }
+                       else if (Element.FlyoutBehavior == FlyoutBehavior.Locked)
+                       {
+                               // Locked behavior is not supported on circularshell
+                       }
+               }
+
+               void UpdateFlyoutIcon(bool init)
+               {
+                       if (init && Element.FlyoutIcon == null)
+                               return;
+
+                       _drawer.UpdateDrawerIcon(Element.FlyoutIcon);
+               }
+
+               void UpdateFlyoutBackgroundColor(bool init)
+               {
+                       if (init && Element.FlyoutBackgroundColor.IsDefault)
+                               return;
+
+                       if (_navigationView != null)
+                       {
+                               _navigationView.BackgroundColor = Element.FlyoutBackgroundColor.ToNative();
+                       }
+               }
+
+               void UpdateFlyoutIsPresented()
+               {
+                       _drawer.IsOpen = Element.FlyoutIsPresented;
+               }
+
+               void SetCurrentItem(EvasObject item)
+               {
+                       _drawer.SetMainContent(item);
+               }
+
+               void ResetCurrentItem()
+               {
+                       _drawer.SetMainContent(null);
+               }
+       }
+}
\ No newline at end of file
diff --git a/src/XSF/Xamarin.Forms.Platform.Tizen/Shell/Watch/ShellRendererFactory.cs b/src/XSF/Xamarin.Forms.Platform.Tizen/Shell/Watch/ShellRendererFactory.cs
new file mode 100644 (file)
index 0000000..f6a25e0
--- /dev/null
@@ -0,0 +1,51 @@
+namespace Xamarin.Forms.Platform.Tizen.Watch
+{
+       public class ShellRendererFactory
+       {
+               static ShellRendererFactory _instance;
+               public static ShellRendererFactory Default
+               {
+                       get
+                       {
+                               if (_instance == null)
+                               {
+                                       _instance = new ShellRendererFactory();
+                               }
+                               return _instance;
+                       }
+                       set
+                       {
+                               _instance = value;
+                       }
+
+               }
+
+               public virtual IShellItemRenderer CreateItemRenderer(ShellItem item)
+               {
+                       if (item.Items.Count == 1)
+                       {
+                               return CreateShellNavigationRenderer(item.CurrentItem);
+                       }
+                       return new ShellItemRenderer(item);
+               }
+
+               public virtual IShellItemRenderer CreateShellNavigationRenderer(ShellSection item)
+               {
+                       return new ShellSectionNavigationRenderer(item);
+               }
+
+               public virtual IShellItemRenderer CreateItemRenderer(ShellSection item)
+               {
+                       if (item.Items.Count == 1)
+                       {
+                               return CreateItemRenderer(item.CurrentItem);
+                       }
+                       return new ShellSectionItemsRenderer(item);
+               }
+
+               public virtual IShellItemRenderer CreateItemRenderer(ShellContent item)
+               {
+                       return new ShellContentRenderer(item);
+               }
+       }
+}
diff --git a/src/XSF/Xamarin.Forms.Platform.Tizen/Shell/Watch/ShellSectionItemsRenderer.cs b/src/XSF/Xamarin.Forms.Platform.Tizen/Shell/Watch/ShellSectionItemsRenderer.cs
new file mode 100644 (file)
index 0000000..4df7f51
--- /dev/null
@@ -0,0 +1,349 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.ComponentModel;
+using ElmSharp;
+using ElmSharp.Wearable;
+
+namespace Xamarin.Forms.Platform.Tizen.Watch
+{
+       internal static class IndexStyle
+       {
+               public const string Thumbnail = "thumbnail";
+               public const string Circle = "circle";
+       }
+
+       public class ShellSectionItemsRenderer : IShellItemRenderer
+       {
+               const int ItemMaxCount = 20;
+               const int OddMiddleItem = 10;
+               const int EvenMiddleItem = 11;
+
+               Box _mainBox;
+               Index _indexIndicator;
+               Scroller _scroller;
+               Box _innerContainer;
+               List<ItemHolder> _items = new List<ItemHolder>();
+
+               int _currentIndex = -1;
+               Rect _lastLayoutBound;
+               int _updateByCode;
+               bool _isScrolling;
+
+               public ShellSectionItemsRenderer(ShellSection shellSection)
+               {
+                       ShellSection = shellSection;
+                       ShellSection.PropertyChanged += OnSectionPropertyChanged;
+                       (ShellSection.Items as INotifyCollectionChanged).CollectionChanged += OnItemsChanged;
+                       InitializeComponent();
+                       UpdateItems();
+               }
+
+               public ShellSection ShellSection { get; protected set; }
+
+               public BaseShellItem Item => ShellSection;
+
+               public EvasObject NativeView => _mainBox;
+
+               public void Dispose()
+               {
+                       Dispose(true);
+               }
+
+               protected virtual void Dispose(bool disposing)
+               {
+                       if (disposing)
+                       {
+                               _mainBox?.Unrealize();
+                               (ShellSection.Items as INotifyCollectionChanged).CollectionChanged -= OnItemsChanged;
+                               ShellSection.PropertyChanged -= OnSectionPropertyChanged;
+                       }
+               }
+
+               void InitializeComponent()
+               {
+                       _mainBox = new Box(Forms.NativeParent)
+                       {
+                               AlignmentX = -1,
+                               AlignmentY = -1,
+                               WeightX = 1,
+                               WeightY = 1,
+                       };
+                       _mainBox.Show();
+                       _mainBox.SetLayoutCallback(OnLayout);
+
+                       _indexIndicator = new Index(_mainBox)
+                       {
+                               IsHorizontal = true,
+                               AutoHide = false,
+                               Style = IndexStyle.Circle,
+                       };
+                       _indexIndicator.Show();
+
+                       _scroller = new Scroller(_mainBox);
+                       _scroller.Scrolled += OnScrolled;
+                       _scroller.PageScrolled += OnScrollStop;
+
+                       //PageScrolled event is not invoked when a user scrolls beyond the end using bezel
+                       var scrollAnimationStop = new SmartEvent(_scroller, "scroll,anim,stop");
+                       scrollAnimationStop.On += OnScrollStop;
+
+                       _scroller.Focused += OnFocused;
+                       _scroller.Unfocused += OnUnfocused;
+
+                       // Disables the visibility of the scrollbar in both directions:
+                       _scroller.HorizontalScrollBarVisiblePolicy = ScrollBarVisiblePolicy.Invisible;
+                       _scroller.VerticalScrollBarVisiblePolicy = ScrollBarVisiblePolicy.Invisible;
+
+                       // Sets the limit of scroll to one page maximum:
+                       _scroller.HorizontalPageScrollLimit = 1;
+                       _scroller.SetPageSize(1.0, 1.0);
+                       _scroller.SetAlignment(-1, -1);
+                       _scroller.SetWeight(1.0, 1.0);
+                       _scroller.Show();
+
+                       _innerContainer = new Box(_mainBox);
+                       _innerContainer.SetLayoutCallback(OnInnerLayoutUpdate);
+                       _innerContainer.SetAlignment(-1, -1);
+                       _innerContainer.SetWeight(1.0, 1.0);
+                       _innerContainer.Show();
+                       _scroller.SetContent(_innerContainer);
+
+                       _mainBox.PackEnd(_indexIndicator);
+                       _mainBox.PackEnd(_scroller);
+                       _indexIndicator.StackAbove(_scroller);
+               }
+
+               void UpdateItems()
+               {
+                       _items.Clear();
+                       _indexIndicator.Clear();
+                       _innerContainer.UnPackAll();
+                       _lastLayoutBound = default(Rect);
+
+                       foreach (var item in ShellSection.Items)
+                       {
+                               var indexItem = _indexIndicator.Append(null);
+                               indexItem.Style = GetItemStyle(ShellSection.Items.Count, _items.Count);
+                               _items.Add(new ItemHolder
+                               {
+                                       IsRealized = false,
+                                       IndexItem = indexItem,
+                                       Item = item
+                               });
+                       }
+                       _indexIndicator.Update(0);
+                       UpdateCurrentPage(ShellSection.Items.IndexOf(ShellSection.CurrentItem));
+               }
+
+               void RealizeItem(int index)
+               {
+                       if (index < 0 || _items.Count <= index)
+                               return;
+
+                       if (!_items[index].IsRealized)
+                               RealizeItem(_items[index]);
+               }
+
+               void RealizeItem(ItemHolder item)
+               {
+                       var renderer = ShellRendererFactory.Default.CreateItemRenderer(item.Item);
+                       renderer.NativeView.Show();
+
+                       item.NativeView = renderer.NativeView;
+                       item.IsRealized = true;
+                       _innerContainer.PackEnd(item.NativeView);
+                       item.NativeView.StackBelow(_indexIndicator);
+                       item.NativeView.Geometry = item.Bound;
+               }
+
+               void UpdateCurrentPage(int index)
+               {
+                       RealizeItem(index - 1);
+                       RealizeItem(index);
+                       RealizeItem(index + 1);
+
+                       UpdateCurrentIndex(index);
+                       UpdateFocusPolicy();
+               }
+
+               void UpdateFocusPolicy()
+               {
+                       foreach (var item in _items)
+                       {
+                               if (item.IsRealized)
+                               {
+                                       if (item.NativeView is ElmSharp.Widget widget)
+                                       {
+                                               widget.AllowTreeFocus = (_items[_currentIndex] == item);
+                                       }
+                               }
+                       }
+               }
+
+               void UpdateCurrentIndex(int index)
+               {
+                       if (index >= 0 && index < _items.Count)
+                       {
+                               _currentIndex = index;
+                               _items[index].IndexItem.Select(true);
+                       }
+               }
+
+               void OnItemsChanged(object sender, NotifyCollectionChangedEventArgs e)
+               {
+                       UpdateItems();
+               }
+
+               void OnFocused(object sender, EventArgs e)
+               {
+                       RotaryEventManager.Rotated += OnRotated;
+                       var item = _items[_currentIndex].NativeView as Widget;
+                       item?.SetFocus(true);
+               }
+
+               void OnUnfocused(object sender, EventArgs e)
+               {
+                       RotaryEventManager.Rotated -= OnRotated;
+               }
+
+               void OnScrollStop(object sender, EventArgs e)
+               {
+                       if (_updateByCode > 0)
+                               return;
+
+                       UpdateCurrentPage(_currentIndex);
+                       var currentItem = ShellSection.Items[_currentIndex];
+                       ShellSection.SetValueFromRenderer(ShellSection.CurrentItemProperty, currentItem);
+                       _isScrolling = false;
+
+                       var item = _items[_currentIndex].NativeView as Widget;
+                       item?.SetFocus(true);
+               }
+
+               protected void OnRotated(RotaryEventArgs args)
+               {
+                       OnRotated(args.IsClockwise);
+               }
+
+               protected virtual bool OnRotated(bool isClockwise)
+               {
+                       if (Forms.RotaryFocusObject != null)
+                               return false;
+
+                       MoveNextPage(isClockwise);
+                       return true;
+               }
+
+               protected void MoveNextPage(bool isClockwise)
+               {
+                       var index = _currentIndex;
+                       _isScrolling = true;
+
+                       if (isClockwise)
+                       {
+                               RealizeItem(index + 2);
+                               _scroller.ScrollTo(index + 1, 0, true);
+                       }
+                       else
+                       {
+                               RealizeItem(index - 2);
+                               _scroller.ScrollTo(index - 1, 0, true);
+                       }
+               }
+
+               void OnScrolled(object sender, EventArgs e)
+               {
+                       _isScrolling = true;
+                       if (_currentIndex != _scroller.HorizontalPageIndex)
+                       {
+                               UpdateCurrentIndex(_scroller.HorizontalPageIndex);
+                       }
+               }
+
+               void OnSectionPropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       if (e.PropertyName == nameof(ShellSection.CurrentItem))
+                       {
+                               var newIndex = ShellSection.Items.IndexOf(ShellSection.CurrentItem);
+                               if (_currentIndex != newIndex)
+                               {
+                                       UpdateCurrentPage(newIndex);
+                                       _updateByCode++;
+                                       _scroller.ScrollTo(newIndex, 0, false);
+                                       _updateByCode--;
+                               }
+                       }
+               }
+
+               void OnLayout()
+               {
+                       _indexIndicator.Geometry = _mainBox.Geometry;
+                       _scroller.Geometry = _mainBox.Geometry;
+               }
+
+               void OnInnerLayoutUpdate()
+               {
+                       if (_lastLayoutBound == _innerContainer.Geometry)
+                       {
+                               return;
+                       }
+                       _lastLayoutBound = _innerContainer.Geometry;
+
+                       var layoutBound = _innerContainer.Geometry.Size;
+                       int baseX = _innerContainer.Geometry.X;
+
+                       Rect bound = _scroller.Geometry;
+                       int index = 0;
+                       foreach (var item in _items)
+                       {
+                               bound.X = baseX + index * bound.Width;
+                               item.Bound = bound;
+                               if (item.IsRealized)
+                               {
+                                       item.NativeView.Geometry = bound;
+                               }
+                               index++;
+                       }
+                       _innerContainer.MinimumWidth = _items.Count * bound.Width;
+
+
+                       if (_items.Count > _currentIndex && _currentIndex >= 0 && !_isScrolling)
+                       {
+                               _updateByCode++;
+                               _scroller.ScrollTo(_currentIndex, 0, false);
+                               _updateByCode--;
+                       }
+               }
+
+               static string GetItemStyle(int itemCount, int offset)
+               {
+                       string returnValue = string.Empty;
+                       int startItem;
+                       int styleNumber;
+
+                       if (itemCount % 2 == 0)  //Item count is even.
+                       {
+                               startItem = EvenMiddleItem - itemCount / 2;
+                               styleNumber = startItem + offset;
+                               returnValue = "item/even_" + styleNumber;
+                       }
+                       else  //Item count is odd.
+                       {
+                               startItem = OddMiddleItem - itemCount / 2;
+                               styleNumber = startItem + offset;
+                               returnValue = "item/odd_" + styleNumber;
+                       }
+                       return returnValue;
+               }
+
+               class ItemHolder
+               {
+                       public bool IsRealized { get; set; }
+                       public Rect Bound { get; set; }
+                       public EvasObject NativeView { get; set; }
+                       public IndexItem IndexItem { get; set; }
+                       public ShellContent Item { get; set; }
+               }
+       }
+}
\ No newline at end of file
diff --git a/src/XSF/Xamarin.Forms.Platform.Tizen/Shell/Watch/ShellSectionNavigationRenderer.cs b/src/XSF/Xamarin.Forms.Platform.Tizen/Shell/Watch/ShellSectionNavigationRenderer.cs
new file mode 100644 (file)
index 0000000..b7e7d45
--- /dev/null
@@ -0,0 +1,205 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using ElmSharp;
+using Xamarin.Forms.Internals;
+
+namespace Xamarin.Forms.Platform.Tizen.Watch
+{
+       class SimpleViewStack : Box
+       {
+               EvasObject _lastTop;
+
+               public SimpleViewStack(EvasObject parent) : base(parent)
+               {
+                       InternalStack = new List<EvasObject>();
+                       SetLayoutCallback(OnLayout);
+               }
+
+               List<EvasObject> InternalStack { get; set; }
+
+               public IReadOnlyList<EvasObject> Stack => InternalStack;
+
+               public void Push(EvasObject view)
+               {
+                       InternalStack.Add(view);
+                       PackEnd(view);
+                       UpdateTopView();
+               }
+
+               public void Pop()
+               {
+                       if (_lastTop != null)
+                       {
+                               var tobeRemoved = _lastTop;
+                               InternalStack.Remove(tobeRemoved);
+                               UnPack(tobeRemoved);
+                               UpdateTopView();
+
+                               // if Pop was called by removed page,
+                               // Unrealize cause deletation of NativeCallback, it could be a cause of crash
+                               Device.BeginInvokeOnMainThread(() =>
+                               {
+                                       tobeRemoved.Unrealize();
+                               });
+                       }
+               }
+
+               public void PopToRoot()
+               {
+                       while (InternalStack.Count > 1)
+                       {
+                               Pop();
+                       }
+               }
+
+               public void Insert(EvasObject before, EvasObject view)
+               {
+                       view.Hide();
+                       var idx = InternalStack.IndexOf(before);
+                       InternalStack.Insert(idx, view);
+                       PackEnd(view);
+                       UpdateTopView();
+               }
+
+               public void Remove(EvasObject view)
+               {
+                       InternalStack.Remove(view);
+                       UnPack(view);
+                       UpdateTopView();
+                       Device.BeginInvokeOnMainThread(() =>
+                       {
+                               view?.Unrealize();
+                       });
+               }
+
+               void UpdateTopView()
+               {
+                       if (_lastTop != InternalStack.LastOrDefault())
+                       {
+                               _lastTop?.Hide();
+                               _lastTop = InternalStack.LastOrDefault();
+                               _lastTop.Show();
+                       }
+               }
+
+               void OnLayout()
+               {
+                       foreach (var view in Stack)
+                       {
+                               view.Geometry = Geometry;
+                       }
+               }
+
+       }
+
+       public class ShellSectionNavigationRenderer : IShellItemRenderer
+       {
+               SimpleViewStack _viewStack;
+               IShellItemRenderer _rootPageRenderer;
+
+               public ShellSectionNavigationRenderer(ShellSection item)
+               {
+                       ShellSection = item;
+                       (ShellSection as IShellSectionController).NavigationRequested += OnNavigationRequested;
+                       InitializeComponent();
+               }
+
+               public ShellSection ShellSection { get; protected set; }
+
+               public BaseShellItem Item => ShellSection;
+
+               public EvasObject NativeView => _viewStack;
+
+               public void Dispose()
+               {
+                       Dispose(true);
+               }
+
+               protected virtual void Dispose(bool disposing)
+               {
+                       if (disposing)
+                       {
+                               _rootPageRenderer?.Dispose();
+                               _viewStack?.Unrealize();
+                               (ShellSection as IShellSectionController).NavigationRequested -= OnNavigationRequested;
+                       }
+               }
+
+               void InitializeComponent()
+               {
+                       _viewStack = new SimpleViewStack(Forms.NativeParent);
+                       _viewStack.Show();
+
+                       _rootPageRenderer = ShellRendererFactory.Default.CreateItemRenderer(ShellSection);
+                       _viewStack.Push(_rootPageRenderer.NativeView);
+               }
+
+               void OnInsertRequest(NavigationRequestedEventArgs request)
+               {
+                       var before = Platform.GetRenderer(request.BeforePage)?.NativeView ?? null;
+                       if (before == null)
+                       {
+                               request.Task = Task.FromException<bool>(new ArgumentException("Can't found page on stack", nameof(request.BeforePage)));
+                               return;
+                       }
+                       var renderer = Platform.GetOrCreateRenderer(request.Page);
+                       _viewStack.Insert(before, renderer.NativeView);
+                       request.Task = Task.FromResult(true);
+               }
+
+               void OnPushRequest(NavigationRequestedEventArgs request)
+               {
+                       var renderer = Platform.GetOrCreateRenderer(request.Page);
+                       _viewStack.Push(renderer.NativeView);
+                       request.Task = Task.FromResult(true);
+               }
+
+               void OnPopRequest(NavigationRequestedEventArgs request)
+               {
+                       _viewStack.Pop();
+                       request.Task = Task.FromResult(true);
+               }
+
+               void OnPopToRootRequest(NavigationRequestedEventArgs request)
+               {
+                       _viewStack.PopToRoot();
+                       request.Task = Task.FromResult(true);
+               }
+
+               void OnRemoveRequest(NavigationRequestedEventArgs request)
+               {
+                       var renderer = Platform.GetRenderer(request.Page);
+                       if (renderer == null)
+                       {
+                               request.Task = Task.FromException<bool>(new ArgumentException("Can't found page on stack", nameof(request.Page)));
+                               return;
+                       }
+                       _viewStack.Remove(renderer.NativeView);
+                       request.Task = Task.FromResult(true);
+               }
+
+               void OnNavigationRequested(object sender, NavigationRequestedEventArgs e)
+               {
+                       switch (e.RequestType)
+                       {
+                               case NavigationRequestType.Insert:
+                                       OnInsertRequest(e);
+                                       break;
+                               case NavigationRequestType.Push:
+                                       OnPushRequest(e);
+                                       break;
+                               case NavigationRequestType.Pop:
+                                       OnPopRequest(e);
+                                       break;
+                               case NavigationRequestType.PopToRoot:
+                                       OnPopToRootRequest(e);
+                                       break;
+                               case NavigationRequestType.Remove:
+                                       OnRemoveRequest(e);
+                                       break;
+                       }
+               }
+       }
+}
index 0912efb..f5854cb 100644 (file)
@@ -78,7 +78,6 @@ namespace Xamarin.Forms.Platform.Tizen
                                Registered.Register(typeof(NavigationPage), () => new NavigationPageRenderer());
                                Registered.Register(typeof(MasterDetailPage), () => new MasterDetailPageRenderer());
                                Registered.Register(typeof(TabbedPage), () => new TabbedPageRenderer());
-                               Registered.Register(typeof(Shell), () => new ShellRenderer());
                                Registered.Register(typeof(Label), () => new LabelRenderer());
                                Registered.Register(typeof(Button), () => new ButtonRenderer());
                                Registered.Register(typeof(Image), () => new ImageRenderer());
@@ -108,6 +107,14 @@ namespace Xamarin.Forms.Platform.Tizen
                                Registered.Register(typeof(MediaElement), () => new MediaElementRenderer());
                                Registered.Register(typeof(IndicatorView), () => new IndicatorViewRenderer());
                                Registered.Register(typeof(RadioButton), () => new RadioButtonRenderer());
+                       if (Device.Idiom == TargetIdiom.Watch)
+                       {
+                               Registered.Register(typeof(Shell), () => new Watch.ShellRenderer());
+                       }
+                       else
+                       {
+                               Registered.Register(typeof(Shell), () => new ShellRenderer());
+                       }
 
                                //ImageSourceHandlers
                                Registered.Register(typeof(FileImageSource), () => new FileImageSourceHandler());