--- /dev/null
+/*
+ * 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;
+ }
+ }
+}
--- /dev/null
+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,
+ }
+}
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
}
else
{
- global::Tizen.Log.Error(FormsCircularUI.Tag, $"streamImage == null ");
+ TLog.Error(FormsCircularUI.Tag, $"streamImage == null ");
}
}
}
{
_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>;
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)
}
}
- 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
[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
* 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)
{
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
* 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
{
public static void Init(string apiKey)
{
-
if (!string.IsNullOrEmpty(apiKey))
{
GoogleMaps.Init(apiKey);
{
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();
}
DependencyService.Register<IInformationPopup, InformationPopupImplementation>();
DependencyService.Register<ITwoButtonPopup, TwoButtonPopupImplementation>();
DependencyService.Register<IToast, ToastImplementation>();
- DependencyService.Register<IPlatformMediaPlayer, MediaPlayerImpl>();
+ DependencyService.Register<IPlatformMediaPlayer2, MediaPlayerImpl>();
}
}
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
--- /dev/null
+/*
+ * 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
*/
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/";
{
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;
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();
}
}
if (Element != null)
{
- ((ObservableCollection<Marker>)Element.Markers).CollectionChanged -= OnCollectionChanged;
+ ((SCObjectModel.ObservableCollection<Marker>)Element.Markers).CollectionChanged -= OnCollectionChanged;
Element.LoadMapRequested -= OnLoadMapRequested;
}
}
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);
}
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();
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:
private void OnLoadMapRequested(object sender, EventArgs eventArgs)
{
- Log.Debug(FormsCircularUI.Tag, $"Load Requested");
+ Log.Debug(Circular.Tag, $"Load Requested");
LoadMap();
}
}
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))]
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;
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))]
{
if (!XForms.IsInitialized)
{
- Log.Debug(FormsCircularUI.Tag, "Tizen Forms is not initialized");
+ Log.Debug(Circular.Tag, "Tizen Forms is not initialized");
return;
}
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();
}
}
void UpdateTitle()
{
string title = _title?.Replace("&", "&")
- .Replace("<", "<")
- .Replace(">", ">")
- .Replace(Environment.NewLine, "<br>");
+ .Replace("<", "<")
+ .Replace(">", ">")
+ .Replace(Environment.NewLine, "<br>");
if (!_isProgressRunning)
{
void OnDismissed(object sender, EventArgs e)
{
- Log.Debug(FormsCircularUI.Tag, $"OnDismissed called");
+ Log.Debug(Circular.Tag, $"OnDismissed called");
Dispose();
}
}
* 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
+++ /dev/null
-/*
- * 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
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
{
{
if (_currentRotaryFocusObject is IRotaryEventReceiver)
{
- RotaryEventManager.Rotated += OnRotaryEventChanged;
+ ERotaryEventManager.Rotated += OnRotaryEventChanged;
}
else if (_currentRotaryFocusObject is IRotaryFocusable)
{
{
if (_currentRotaryFocusObject is IRotaryEventReceiver)
{
- RotaryEventManager.Rotated -= OnRotaryEventChanged;
+ ERotaryEventManager.Rotated -= OnRotaryEventChanged;
}
else if (_currentRotaryFocusObject is IRotaryFocusable)
{
--- /dev/null
+/*
+ * 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
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
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
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
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
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
[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
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
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
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
*/
using ElmSharp;
using System.IO;
+using Circular = Tizen.Wearable.CircularUI.Forms.FormsCircularUI;
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
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
if (focusable is IRotaryEventReceiver)
{
_rotaryReceiver = focusable as IRotaryEventReceiver;
- RotaryEventManager.Rotated += OnRotaryEventChanged;
+ ERotaryEventManager.Rotated += OnRotaryEventChanged;
}
else if (focusable is IRotaryFocusable)
{
if (focusable is IRotaryEventReceiver)
{
_rotaryReceiver = null;
- RotaryEventManager.Rotated -= OnRotaryEventChanged;
+ ERotaryEventManager.Rotated -= OnRotaryEventChanged;
}
else if (focusable is IRotaryFocusable)
{
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))]
{
if (!XForms.IsInitialized)
{
- Log.Debug(FormsCircularUI.Tag, "Tizen Forms is not initialized");
+ Log.Debug(Circular.Tag, "Tizen Forms is not initialized");
return;
}
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();
}
}
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();
}
void UpdateTitle()
{
string title = _title?.Replace("&", "&")
- .Replace("<", "<")
- .Replace(">", ">")
- .Replace(Environment.NewLine, "<br>");
+ .Replace("<", "<")
+ .Replace(">", ">")
+ .Replace(Environment.NewLine, "<br>");
_layout.SetPartText("elm.text.title", title);
}
void OnDismissed(object sender, EventArgs e)
{
- Log.Debug(FormsCircularUI.Tag, $"OnDismissed called");
+ Log.Debug(Circular.Tag, $"OnDismissed called");
Dispose();
}
}
using System;
using System.Collections.Generic;
using Tizen.Applications;
+using Circular = Tizen.Wearable.CircularUI.Forms.FormsCircularUI;
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
/// 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.
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>
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
/// 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.
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
/// 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.
[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>
/// 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.
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
/// 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.
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
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
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
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
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/*
- * 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
--- /dev/null
+using System;
+
+namespace Tizen.Wearable.CircularUI.Forms
+{
+ interface IRotaryService
+ {
+ event EventHandler<RotaryEventArgs> Rotated;
+ }
+}
\ No newline at end of file
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>
/// </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;
_controlsAlwaysVisible = false;
_controls = new Lazy<View>(() =>
{
- return new EmbeddingControls
+ return _impl.GetEmbeddingControlView(this);
+ });
+ }
+ }
+
+ ~MediaPlayer()
{
- BindingContext = this
- };
- });
+ Dispose(false);
}
/// <summary>
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;
_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)
}
}
-
- 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);
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/*
- * 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
*/
using Xamarin.Forms;
+using Xamarin.Forms.PlatformConfiguration.TizenSpecific;
+using XVisualElement = Xamarin.Forms.VisualElement;
namespace Tizen.Wearable.CircularUI.Forms
{
/// </summary>
public virtual VideoOuputType OuputType => VideoOuputType.Buffer;
- VisualElement IVideoOutput.MediaView => this;
+ XVisualElement IVideoOutput.MediaView => this;
+
View IVideoOutput.Controller
{
get
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
+++ /dev/null
-/*
- * 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
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
}
protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
- {
+ {
VerifyExperimental();
return base.OnMeasure(widthConstraint, heightConstraint);
}
});
}
}
-}
+}
\ No newline at end of file
Collapsing,
Collapsed
}
-}
+}
\ No newline at end of file
{
public interface ISwipeItem
{
+ bool IsVisible { get; set; }
ICommand Command { get; set; }
object CommandParameter { get; set; }
_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];
}
public IList<string> @class
{
get { return _mergedStyle.StyleClass; }
- set
- {
+ set
+ {
_mergedStyle.StyleClass = value;
}
}
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;
+ }
}
}
event EventHandler PlaybackStarted;
event EventHandler PlaybackStopped;
+ Task<bool> Start();
+ void Stop();
+ void Pause();
}
public interface IPlatformMediaPlayer : IDisposable
{
Navigation.ModalStack[Navigation.ModalStack.Count - 1]
.OnAppearing(action);
-
+
return;
}
else if(Navigation.NavigationStack.Count > 1)
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);
[TypeConverter(typeof(ListStringTypeConverter))]
public IList<string> @class {
get { return _mergedStyle.StyleClass; }
- set
- {
- _mergedStyle.StyleClass = value;
+ set
+ {
+ _mergedStyle.StyleClass = value;
}
}
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]);
}
}
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;
{
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)]
{
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
{
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
{
}
- 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; }
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)
{
}
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
get; internal set;
}
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static Element RotaryFocusObject
+ {
+ get; internal set;
+ }
+
public static bool IsInitialized
{
get;
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);
}
}
- 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)
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)
{
_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)
MainWindow.Active();
MainWindow.Show();
+ // in case of no use of preloaded window
if (BaseLayout == null)
{
var conformant = new Conformant(MainWindow);
async void OnImageButtonClicked(object sender, EventArgs e)
{
- if (BindingContext is MediaPlayer player)
+ if (BindingContext is IMediaPlayer player)
{
if (player.State == PlayerState.Playing)
{
using Tizen.Multimedia;
using Xamarin.Forms.PlatformConfiguration.TizenSpecific;
-[assembly: Xamarin.Forms.Dependency(typeof(IPlatformMediaPlayer))]
namespace Xamarin.Forms.Platform.Tizen.Native
{
public class MediaPlayerImpl : IPlatformMediaPlayer
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))]
[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
Control.UpdateSelectedIndex(Element.Position);
}
}
-}
+}
\ No newline at end of file
_page.LayoutUpdated += OnLayoutUpdated;
SetNativeView(_page);
}
+
base.OnElementChanged(e);
}
{
Element.SetValueFromRenderer(RadioButton.IsCheckedProperty, Control.GroupValue == 1 ? true : false);
}
-
+
void UpdateIsChecked()
{
Control.GroupValue = Element.IsChecked ? 1 : 0;
Control.EdjeObject.EmitSignal(emission, "elm");
}
}
-}
+}
\ No newline at end of file
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
--- /dev/null
+using System;
+using ElmSharp;
+
+namespace Xamarin.Forms.Platform.Tizen.Watch
+{
+ public interface IShellItemRenderer : IDisposable
+ {
+ BaseShellItem Item { get; }
+ EvasObject NativeView { get; }
+ }
+}
--- /dev/null
+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
--- /dev/null
+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);
+ }
+ }
+}
--- /dev/null
+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;
+ }
+ }
+}
--- /dev/null
+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;
+ }
+ }
+ }
+}
--- /dev/null
+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
--- /dev/null
+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);
+ }
+ }
+}
--- /dev/null
+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
--- /dev/null
+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;
+ }
+ }
+ }
+}
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());
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());