summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Platform.Tizen/Native
diff options
context:
space:
mode:
Diffstat (limited to 'Xamarin.Forms.Platform.Tizen/Native')
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/Box.cs67
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/Button.cs303
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/Canvas.cs109
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/ContentPage.cs76
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/DateChangedEventArgs.cs31
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/DatePicker.cs130
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/DateTimePickerDialog.cs100
-rwxr-xr-xXamarin.Forms.Platform.Tizen/Native/Dialog.cs276
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/DisplayOrientations.cs36
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/Entry.cs396
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/FormattedString.cs128
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/IContainable.cs16
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/IMeasurable.cs20
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/ITextable.cs68
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/Image.cs124
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/Keyboard.cs84
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/Label.cs347
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/LayoutEventArgs.cs55
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/LineBreakMode.cs43
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/ListView.cs601
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/MasterDetailPage.cs364
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/ObservableCollection.cs30
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/SearchBar.cs423
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/Span.cs294
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/TableView.cs67
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/TextAlignment.cs25
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/TextHelper.cs54
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/TimePicker.cs185
-rw-r--r--Xamarin.Forms.Platform.Tizen/Native/Window.cs138
29 files changed, 4590 insertions, 0 deletions
diff --git a/Xamarin.Forms.Platform.Tizen/Native/Box.cs b/Xamarin.Forms.Platform.Tizen/Native/Box.cs
new file mode 100644
index 00000000..f22d27b5
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/Box.cs
@@ -0,0 +1,67 @@
+using System;
+using ElmSharp;
+using EBox = ElmSharp.Box;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// Extends the ElmSharp.Box class with functionality useful to Xamarin.Forms renderer.
+ /// </summary>
+ /// <remarks>
+ /// This class overrides the layout mechanism. Instead of using the native layout,
+ /// <c>LayoutUpdated</c> event is sent.
+ /// </remarks>
+ public class Box : EBox
+ {
+ /// <summary>
+ /// The last processed geometry of the Box which was reported from the native layer.
+ /// </summary>
+ Rect _previousGeometry;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Xamarin.Forms.Platform.Tizen.Native.Box"/> class.
+ /// </summary>
+ /// <param name="parent">The parent EvasObject.</param>
+ public Box(EvasObject parent) : base(parent)
+ {
+ Resized += (sender, e) => { NotifyOnLayout(); };
+ SetLayoutCallback(() => { NotifyOnLayout(); });
+ }
+
+ /// <summary>
+ /// Notifies that the layout has been updated.
+ /// </summary>
+ public event EventHandler<LayoutEventArgs> LayoutUpdated;
+
+ /// <summary>
+ /// Triggers the <c>LayoutUpdated</c> event.
+ /// </summary>
+ /// <remarks>
+ /// This method is called whenever there is a possibility that the size and/or position has been changed.
+ /// </remarks>
+ void NotifyOnLayout()
+ {
+ var g = Geometry;
+
+ if (0 == g.Width || 0 == g.Height)
+ {
+ // ignore irrelevant dimensions
+ return;
+ }
+ if (null != LayoutUpdated)
+ {
+ LayoutUpdated(this, new LayoutEventArgs()
+ {
+ HasChanged = g != _previousGeometry,
+ X = g.X,
+ Y = g.Y,
+ Width = g.Width,
+ Height = g.Height,
+ }
+ );
+ }
+
+ _previousGeometry = g;
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/Button.cs b/Xamarin.Forms.Platform.Tizen/Native/Button.cs
new file mode 100644
index 00000000..8f85da63
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/Button.cs
@@ -0,0 +1,303 @@
+using System;
+using ElmSharp;
+using EButton = ElmSharp.Button;
+using ESize = ElmSharp.Size;
+using EColor = ElmSharp.Color;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// Extends the EButton control, providing basic formatting features,
+ /// i.e. font color, size, additional image.
+ /// </summary>
+ public class Button : EButton, IMeasurable
+ {
+ /// <summary>
+ /// Holds the formatted text of the button.
+ /// </summary>
+ readonly Span _span = new Span();
+
+ /// <summary>
+ /// The internal padding of the button, helps to determine the size.
+ /// </summary>
+ readonly ESize _internalPadding;
+
+ /// <summary>
+ /// Optional image, if set will be drawn on the button.
+ /// </summary>
+ Image _image;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Xamarin.Forms.Platform.Tizen.Native.Button"/> class.
+ /// </summary>
+ /// <param name="parent">Parent evas object.</param>
+ public Button(EvasObject parent) : base(parent)
+ {
+ _internalPadding = GetInternalPadding();
+ }
+
+ /// <summary>
+ /// Gets or sets the button's text.
+ /// </summary>
+ /// <value>The text.</value>
+ public override string Text
+ {
+ get
+ {
+ return _span.Text;
+ }
+
+ set
+ {
+ if (value != _span.Text)
+ {
+ _span.Text = value;
+ ApplyTextAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the color of the text.
+ /// </summary>
+ /// <value>The color of the text.</value>
+ public EColor TextColor
+ {
+ get
+ {
+ return _span.ForegroundColor;
+ }
+
+ set
+ {
+ if (!_span.ForegroundColor.Equals(value))
+ {
+ _span.ForegroundColor = value;
+ ApplyTextAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the color of the text background.
+ /// </summary>
+ /// <value>The color of the text background.</value>
+ public EColor TextBackgroundColor
+ {
+ get
+ {
+ return _span.BackgroundColor;
+ }
+
+ set
+ {
+ if (!_span.BackgroundColor.Equals(value))
+ {
+ _span.BackgroundColor = value;
+ ApplyTextAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the font family.
+ /// </summary>
+ /// <value>The font family.</value>
+ public string FontFamily
+ {
+ get
+ {
+ return _span.FontFamily;
+ }
+
+ set
+ {
+ if (value != _span.FontFamily)
+ {
+ _span.FontFamily = value;
+ ApplyTextAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the font attributes.
+ /// </summary>
+ /// <value>The font attributes.</value>
+ public FontAttributes FontAttributes
+ {
+ get
+ {
+ return _span.FontAttributes;
+ }
+
+ set
+ {
+ if (value != _span.FontAttributes)
+ {
+ _span.FontAttributes = value;
+ ApplyTextAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the size of the font.
+ /// </summary>
+ /// <value>The size of the font.</value>
+ public double FontSize
+ {
+ get
+ {
+ return _span.FontSize;
+ }
+
+ set
+ {
+ if (value != _span.FontSize)
+ {
+ _span.FontSize = value;
+ ApplyTextAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the image to be displayed next to the button's text.
+ /// </summary>
+ /// <value>The image displayed on the button.</value>
+ public Image Image
+ {
+ get
+ {
+ return _image;
+ }
+
+ set
+ {
+ if (value != _image)
+ {
+ ApplyImage(value);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Implementation of the IMeasurable.Measure() method.
+ /// </summary>
+ public ESize Measure(int availableWidth, int availableHeight)
+ {
+ var size = Geometry;
+
+ // resize the control using the whole available width
+ Resize(availableWidth, size.Height);
+
+ // measure the button's text, use it as a hint for the size
+ var rawSize = Native.TextHelper.GetRawTextBlockSize(this);
+ var formattedSize = Native.TextHelper.GetFormattedTextBlockSize(this);
+
+ // restore the original size
+ Resize(size.Width, size.Height);
+
+ var padding = _internalPadding;
+
+ if (rawSize.Width > availableWidth)
+ {
+ // if the raw text width is larger than the available width, use
+ // either formatted size or internal padding, whichever is bigger
+ return new ESize()
+ {
+ Width = Math.Max(padding.Width, formattedSize.Width),
+ Height = Math.Max(padding.Height, Math.Min(formattedSize.Height, Math.Max(rawSize.Height, availableHeight))),
+ };
+ }
+ else
+ {
+ // otherwise use the formatted size along with padding
+ return new ESize()
+ {
+ Width = padding.Width + formattedSize.Width,
+ Height = Math.Max(padding.Height, formattedSize.Height),
+ };
+ }
+ }
+
+ /// <summary>
+ /// Applies the button's text and its style.
+ /// </summary>
+ void ApplyTextAndStyle()
+ {
+ SetInternalTextAndStyle(_span.GetDecoratedText(), _span.GetStyle());
+ }
+
+ /// <summary>
+ /// Sets the button's internal text and its style.
+ /// </summary>
+ /// <param name="formattedText">Formatted text, supports HTML tags.</param>
+ /// <param name="textStyle">Style applied to the formattedText.</param>
+ void SetInternalTextAndStyle(string formattedText, string textStyle)
+ {
+ string emission = "elm,state,text,visible";
+
+ if (string.IsNullOrEmpty(formattedText))
+ {
+ formattedText = null;
+ textStyle = null;
+ emission = "elm,state,text,hidden";
+ }
+
+ base.Text = formattedText;
+
+ var textblock = EdjeObject["elm.text"];
+
+ if (textblock != null)
+ {
+ textblock.TextStyle = textStyle;
+ }
+
+ EdjeObject.EmitSignal(emission, "elm");
+ }
+
+ /// <summary>
+ /// Gets the internal padding of the button.
+ /// </summary>
+ /// <returns>The internal padding.</returns>
+ ESize GetInternalPadding()
+ {
+ var edje = EdjeObject;
+
+ return new ESize
+ {
+ Width = (edje["padding_top_left"]?.Geometry.Width ?? 64) + (edje["padding_bottom_right"]?.Geometry.Width ?? 64),
+ Height = edje["bg"]?.Geometry.Height ?? 64
+ };
+ }
+
+ /// <summary>
+ /// Applies the image to be displayed on the button. If value is <c>null</c>,
+ /// image will be removed.
+ /// </summary>
+ /// <param name="image">Image to be displayed or null.</param>
+ void ApplyImage(Image image)
+ {
+ _image = image;
+
+ SetInternalImage();
+ }
+
+ /// <summary>
+ /// Sets the internal image. If value is <c>null</c>, image will be removed.
+ /// </summary>
+ void SetInternalImage()
+ {
+ if (_image == null)
+ {
+ SetPartContent("icon", null);
+ }
+ else
+ {
+ SetPartContent("icon", _image);
+ }
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/Canvas.cs b/Xamarin.Forms.Platform.Tizen/Native/Canvas.cs
new file mode 100644
index 00000000..c345abf2
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/Canvas.cs
@@ -0,0 +1,109 @@
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using ElmSharp;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// A Canvas provides a class which can be a container for other controls.
+ /// </summary>
+ /// <remarks>
+ /// This class is used as a container view for Layouts from Xamarin.Forms.Platform.Tizen framework.
+ /// It is used for implementing xamarin pages and layouts.
+ /// </remarks>
+ public class Canvas : Box, IContainable<EvasObject>
+ {
+ /// <summary>
+ /// The list of Views.
+ /// </summary>
+ readonly ObservableCollection<EvasObject> _children = new ObservableCollection<EvasObject>();
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Xamarin.Forms.Platform.Tizen.Native.Canvas"/> class.
+ /// </summary>
+ /// <remarks>Canvas doesn't support replacing its children, this will be ignored.</remarks>
+ /// <param name="parent">Parent of this instance.</param>
+ public Canvas(EvasObject parent) : base(parent)
+ {
+ _children.CollectionChanged += (o, e) =>
+ {
+ if (e.Action == NotifyCollectionChangedAction.Add)
+ {
+ foreach (var v in e.NewItems)
+ {
+ var view = v as EvasObject;
+ if (null != view)
+ {
+ OnAdd(view);
+ }
+ }
+ }
+ else if (e.Action == NotifyCollectionChangedAction.Remove)
+ {
+ foreach (var v in e.OldItems)
+ {
+ var view = v as EvasObject;
+ if (null != view)
+ {
+ OnRemove(view);
+ }
+ }
+ }
+ else if (e.Action == NotifyCollectionChangedAction.Reset)
+ {
+ OnRemoveAll();
+ }
+ };
+ }
+
+ /// <summary>
+ /// Gets list of native elements that are placed in the canvas.
+ /// </summary>
+ public IList<EvasObject> Children
+ {
+ get
+ {
+ return _children;
+ }
+ }
+
+ /// <summary>
+ /// Provides destruction for native element and contained elements.
+ /// </summary>
+ protected override void OnUnrealize()
+ {
+ foreach (var child in _children)
+ {
+ child.Unrealize();
+ }
+
+ base.OnUnrealize();
+ }
+
+ /// <summary>
+ /// Adds a new child to a container.
+ /// </summary>
+ /// <param name="view">Native element which will be added</param>
+ void OnAdd(EvasObject view)
+ {
+ PackEnd(view);
+ }
+
+ /// <summary>
+ /// Removes a child from a container.
+ /// </summary>
+ /// <param name="view">Child element to be removed from canvas</param>
+ void OnRemove(EvasObject view)
+ {
+ UnPack(view);
+ }
+
+ /// <summary>
+ /// Removes all children from a canvas.
+ /// </summary>
+ void OnRemoveAll()
+ {
+ UnPackAll();
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/ContentPage.cs b/Xamarin.Forms.Platform.Tizen/Native/ContentPage.cs
new file mode 100644
index 00000000..32f24a0d
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/ContentPage.cs
@@ -0,0 +1,76 @@
+using System;
+using System.Collections.Generic;
+using ElmSharp;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// A basic page which can hold a single view.
+ /// </summary>
+ public class ContentPage : Background, IContainable<EvasObject>
+ {
+ /// <summary>
+ /// The name of the part to be used when setting content.
+ /// </summary>
+ public const string ContentPartName = "overlay";
+
+ /// <summary>
+ /// Exposes the Children property, mapping it to the _canvas' Children property.
+ /// </summary>
+ public IList<EvasObject> Children => _canvas.Children;
+
+ /// <summary>
+ /// The canvas, used as a container for other objects.
+ /// </summary>
+ /// <remarks>
+ /// The canvas holds all the Views that the ContentPage is composed of.
+ /// </remarks>
+ internal Canvas _canvas;
+
+ /// <summary>
+ /// Initializes a new instance of the ContentPage class.
+ /// </summary>
+ public ContentPage(EvasObject parent) : base(parent)
+ {
+ _canvas = new Canvas(this);
+ SetPartContent(ContentPartName, _canvas);
+ }
+
+ /// <summary>
+ /// Gets or sets the title.
+ /// </summary>
+ /// <value>The current title.p</value>
+ public string Title
+ {
+ get;
+ set;
+ }
+
+ /// <summary>
+ /// Allows custom handling of events emitted when the layout has been updated.
+ /// </summary>
+ public event EventHandler<LayoutEventArgs> LayoutUpdated
+ {
+ add
+ {
+ _canvas.LayoutUpdated += value;
+ }
+ remove
+ {
+ _canvas.LayoutUpdated -= value;
+ }
+ }
+
+ /// <summary>
+ /// Handles the disposing of a ContentPage
+ /// </summary>
+ /// <remarks>
+ /// Takes the proper care of discarding the canvas, then calls the base method.
+ /// </remarks>
+ protected override void OnUnrealize()
+ {
+ _canvas.Unrealize();
+ base.OnUnrealize();
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/DateChangedEventArgs.cs b/Xamarin.Forms.Platform.Tizen/Native/DateChangedEventArgs.cs
new file mode 100644
index 00000000..c238e9e9
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/DateChangedEventArgs.cs
@@ -0,0 +1,31 @@
+using System;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// Event arguments for <see cref="DatePicker.DateChanged"/> event.
+ /// </summary>
+ public class DateChangedEventArgs : EventArgs
+ {
+ /// <summary>
+ /// The date that was on the element at the time that the user selected it.
+ /// </summary>
+ public DateTime OldDate { get; private set; }
+
+ /// <summary>
+ /// The date that the user entered.
+ /// </summary>
+ public DateTime NewDate { get; private set; }
+
+ /// <summary>
+ /// Creates a new <see cref="DateChangedEventArgs"/> object that represents a change from <paramref name="oldDate"/> to <paramref name="newDate"/>.
+ /// </summary>
+ /// <param name="oldDate">Old date of <see cref="DatePicker"/>.</param>
+ /// <param name="newDate">Current date of <see cref="DatePicker"/>.</param>
+ public DateChangedEventArgs(DateTime oldDate, DateTime newDate)
+ {
+ this.OldDate = oldDate;
+ this.NewDate = newDate;
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/DatePicker.cs b/Xamarin.Forms.Platform.Tizen/Native/DatePicker.cs
new file mode 100644
index 00000000..fabd5269
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/DatePicker.cs
@@ -0,0 +1,130 @@
+using System;
+using ElmSharp;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// Extends the ElmSharp.DateTimeSelector class with functionality useful to renderer.
+ /// </summary>
+ public class DatePicker : DateTimeSelector
+ {
+ const string DateLayoutStyle = "date_layout";
+ const string DefaultEFLFormat = "%d/%b/%Y";
+ static readonly DateTime s_defaultMaximumDate = new DateTime(2037, 12, 31);
+ static readonly DateTime s_defaultMinimumDate = new DateTime(1970, 1, 1);
+ DateTime _date;
+ DateTime _maxDate;
+ DateTime _minDate;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DatePicker"/> class.
+ /// </summary>
+ /// <param name="parent">The parent EvasObject.</param>
+ public DatePicker(EvasObject parent) : base(parent)
+ {
+ SetFieldVisible(DateTimeFieldType.Hour, false);
+ Style = DateLayoutStyle;
+ ApplyDate(Date);
+ ApplyMinDate(s_defaultMinimumDate);
+ ApplyMaxDate(s_defaultMaximumDate);
+ //TODO use date format currently set on the platform
+ Format = DefaultEFLFormat;
+
+ DateTimeChanged += (sender, e) =>
+ {
+ Date = e.NewDate;
+ };
+ }
+
+ /// <summary>
+ /// Gets or sets the displayed date.
+ /// </summary>
+ public DateTime Date
+ {
+ get
+ {
+ return _date;
+ }
+ set
+ {
+ if (_date != value)
+ {
+ ApplyDate(value);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets of sets the highest date selectable for this <see cref="DatePicker"/>.
+ /// </summary>
+ /// <remarks>
+ /// Default value is 31st Dec, 2037.
+ /// </remarks>
+ public DateTime MaximumDate
+ {
+ get
+ {
+ return _maxDate;
+ }
+ set
+ {
+ if (_maxDate != value)
+ {
+ ApplyMaxDate(value);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets of sets the lowest date selectable for this <see cref="DatePicker"/>.
+ /// </summary>
+ /// <remarks>
+ /// Default value is 1st Jan, 1970.
+ /// </remarks>
+ public DateTime MinimumDate
+ {
+ get
+ {
+ return _minDate;
+ }
+ set
+ {
+ if (_minDate != value)
+ {
+ ApplyMinDate(value);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Sets the <c>DateTime</c> property according to the given <paramref name="date"/>.
+ /// </summary>
+ /// <param name="date">The date value to be applied to the date picker.</param>
+ void ApplyDate(DateTime date)
+ {
+ _date = date;
+ DateTime = date;
+ }
+
+ /// <summary>
+ /// Sets the <c>MaximumDateTime</c> property according to the given <paramref name="maxDate"/>.
+ /// </summary>
+ /// <param name="maxDate">The maximum date value to be applied to the date picker.</param>
+ void ApplyMaxDate(DateTime maxDate)
+ {
+ _maxDate = maxDate;
+ MaximumDateTime = maxDate;
+ }
+
+ /// <summary>
+ /// Sets the <c>MinimumDateTime</c> property according to the given <paramref name="minDate"/>.
+ /// </summary>
+ /// <param name="minDate">The minimum date value to be applied to the date picker.</param>
+ void ApplyMinDate(DateTime minDate)
+ {
+ _minDate = minDate;
+ MinimumDateTime = minDate;
+ }
+ }
+}
+
diff --git a/Xamarin.Forms.Platform.Tizen/Native/DateTimePickerDialog.cs b/Xamarin.Forms.Platform.Tizen/Native/DateTimePickerDialog.cs
new file mode 100644
index 00000000..64b1b3b3
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/DateTimePickerDialog.cs
@@ -0,0 +1,100 @@
+using System;
+using ElmSharp;
+using EButton = ElmSharp.Button;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ public class DateTimePickerDialog : Dialog
+ {
+ DateTimeSelector _dateTimePicker;
+ EvasObject _parent;
+
+ /// <summary>
+ /// Creates a dialog window.
+ /// </summary>
+ public DateTimePickerDialog(EvasObject parent) : base(parent)
+ {
+ _parent = parent;
+ Initialize();
+ }
+
+ /// <summary>
+ /// Occurs when the date of this dialog has changed.
+ /// </summary>
+ public event EventHandler<DateChangedEventArgs> DateTimeChanged;
+
+ /// <summary>
+ /// Gets the <see cref="DateTimePicker"/> contained in this dialog.
+ /// </summary>
+ public DateTimeSelector DateTimePicker
+ {
+ get
+ {
+ return _dateTimePicker;
+ }
+ private set
+ {
+ if (_dateTimePicker != value)
+ {
+ ApplyDateTimePicker(value);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Creates date picker in dialog window.
+ /// </summary>
+ public void InitializeDatePicker(DateTime date, DateTime minimumDate, DateTime maximumDate)
+ {
+ var datePicker = new DatePicker(this)
+ {
+ Date = date,
+ MinimumDate = minimumDate,
+ MaximumDate = maximumDate
+ };
+ Content = DateTimePicker = datePicker;
+ }
+
+ /// <summary>
+ /// Creates time picker in dialog window.
+ /// </summary>
+ public void InitializeTimePicker(TimeSpan time, string format)
+ {
+ var timePicker = new TimePicker(this)
+ {
+ Time = time,
+ DateTimeFormat = format
+ };
+ Content = DateTimePicker = timePicker;
+ }
+
+ void ApplyDateTimePicker(DateTimeSelector dateTimePicker)
+ {
+ _dateTimePicker = dateTimePicker;
+ Content = _dateTimePicker;
+ }
+
+ void Initialize()
+ {
+ //TODO need to add internationalization support
+ PositiveButton = new EButton(_parent) { Text = "Set" };
+ PositiveButton.Clicked += (s, e) =>
+ {
+ DateTime oldDate = DateTimePicker.DateTime;
+ DateTimeChanged?.Invoke(this, new DateChangedEventArgs(oldDate, DateTimePicker.DateTime));
+ Hide();
+ };
+
+ //TODO need to add internationalization support
+ NegativeButton = new EButton(_parent) { Text = "Cancel" };
+ NegativeButton.Clicked += (s, e) =>
+ {
+ Hide();
+ };
+ BackButtonPressed += (object s, EventArgs e) =>
+ {
+ Hide();
+ };
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/Dialog.cs b/Xamarin.Forms.Platform.Tizen/Native/Dialog.cs
new file mode 100755
index 00000000..e616ff5a
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/Dialog.cs
@@ -0,0 +1,276 @@
+using System;
+using ElmSharp;
+using EButton = ElmSharp.Button;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// Base class for Dialogs.
+ /// A dialog is a small window that prompts the user to make a decision or enter additional information.
+ /// </summary>
+ public class Dialog : Popup
+ {
+ EButton _positiveButton;
+ EButton _neutralButton;
+ EButton _negativeButton;
+ EvasObject _content;
+ string _title;
+
+ /// <summary>
+ /// Creates a dialog window that uses the default dialog theme.
+ /// </summary>
+ public Dialog(EvasObject parent) : base(parent)
+ {
+ Initialize();
+ }
+
+ /// <summary>
+ /// Occurs when the hardware Back button is pressed.
+ /// </summary>
+ public event EventHandler BackButtonPressed;
+
+ /// <summary>
+ /// Occurs whenever the dialog is first displayed.
+ /// </summary>
+ public event EventHandler Shown;
+
+ /// <summary>
+ /// Enumerates the three valid positions of a dialog button.
+ /// </summary>
+ enum ButtonPosition
+ {
+ Positive,
+ Neutral,
+ Negative
+ }
+
+ /// <summary>
+ /// Gets or sets the title of the dialog
+ /// </summary>
+ public string Title
+ {
+ get
+ {
+ return _title;
+ }
+ set
+ {
+ if (_title != value)
+ {
+ ApplyTitle(value);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the content to display in that dialog.
+ /// </summary>
+ public EvasObject Content
+ {
+ get
+ {
+ return _content;
+ }
+ set
+ {
+ if (_content != value)
+ {
+ ApplyContent(value);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the positive button used in the dialog
+ /// </summary>
+ public EButton PositiveButton
+ {
+ get
+ {
+ return _positiveButton;
+ }
+ set
+ {
+ if (_positiveButton != value)
+ {
+ ApplyButton(ButtonPosition.Positive, value);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the neutral button used in the dialog
+ /// </summary>
+ public EButton NeutralButton
+ {
+ get
+ {
+ return _neutralButton;
+ }
+ set
+ {
+ if (_neutralButton != value)
+ {
+ ApplyButton(ButtonPosition.Neutral, value);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the negative button used in the dialog
+ /// </summary>
+ public EButton NegativeButton
+ {
+ get
+ {
+ return _negativeButton;
+ }
+ set
+ {
+ if (_negativeButton != value)
+ {
+ ApplyButton(ButtonPosition.Negative, value);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Starts the dialog and displays it on screen.
+ /// </summary>
+ public new void Show()
+ {
+ base.Show();
+ Shown?.Invoke(this, EventArgs.Empty);
+ }
+
+ /// <summary>
+ /// Handles the disposing of a dialog widget.
+ /// </summary>
+ protected override void OnUnrealize()
+ {
+ _content?.Unrealize();
+
+ ApplyButton(ButtonPosition.Positive, null);
+ ApplyButton(ButtonPosition.Neutral, null);
+ ApplyButton(ButtonPosition.Negative, null);
+ ApplyContent(null);
+
+ UngrabBackKey();
+
+ base.OnUnrealize();
+ }
+
+ /// <summary>
+ /// Called when the dialog is shown.
+ /// </summary>
+ /// <remarks>When shown, the dialog will register itself for the back key press event handling.</remarks>
+ protected virtual void OnShown()
+ {
+ GrabBackKey();
+ }
+
+ /// <summary>
+ /// Called when the dialog is dismissed.
+ /// </summary>
+ /// <remarks>When dismissed, the dialog will unregister itself from the back key press event handling.</remarks>
+ protected virtual void OnDismissed()
+ {
+ UngrabBackKey();
+ }
+
+ /// <summary>
+ /// Handles the initialization process.
+ /// </summary>
+ /// <remarks>Creates handlers for vital events</remarks>
+ void Initialize()
+ {
+ // Adds a handler for the Dismissed event.
+ // In effect, unregisters this instance from being affected by the hardware back key presses.
+ Dismissed += (s, e) =>
+ {
+ OnDismissed();
+ };
+
+ // Adds a handler for the Shown event.
+ // In effect, registers this instance to be affected by the hardware back key presses.
+ Shown += (s, e) =>
+ {
+ OnShown();
+ };
+
+ // Adds a handler for the KeyUp event.
+ // The handler checks whether the key just pressed is a back key
+ // and if that is the case, invokes the back button press handler of this instance.
+ KeyUp += (s, e) =>
+ {
+ if (e.KeyName == EvasKeyEventArgs.PlatformBackButtonName)
+ BackButtonPressed?.Invoke(this, EventArgs.Empty);
+ };
+ }
+
+ /// <summary>
+ /// Changes the dialog title.
+ /// </summary>
+ /// <param name="title">New dialog title.</param>
+ void ApplyTitle(string title)
+ {
+ _title = title;
+
+ SetPartText("title,text", _title);
+ }
+
+ /// <summary>
+ /// Puts the button in one of the three available slots.
+ /// </summary>
+ /// <param name="position">The slot to be occupied by the button expressed as a <see cref="ButtonPosition"/></param>
+ /// <param name="button">The new button.</param>
+ void ApplyButton(ButtonPosition position, EButton button)
+ {
+ switch (position)
+ {
+ case ButtonPosition.Positive:
+ _positiveButton = button;
+ SetPartContent("button3", _positiveButton, true);
+ break;
+
+ case ButtonPosition.Neutral:
+ _neutralButton = button;
+ SetPartContent("button2", _neutralButton, true);
+ break;
+
+ case ButtonPosition.Negative:
+ _negativeButton = button;
+ SetPartContent("button1", _negativeButton, true);
+ break;
+ }
+ }
+
+ /// <summary>
+ /// Updates the content of the dialog.
+ /// </summary>
+ /// <param name="content">New dialog content.</param>
+ void ApplyContent(EvasObject content)
+ {
+ _content = content;
+
+ SetPartContent("default", _content, true);
+ }
+
+ /// <summary>
+ /// Registers this instance to be affected by pressing the hardware back key.
+ /// </summary>
+ void GrabBackKey()
+ {
+ KeyGrab(EvasKeyEventArgs.PlatformBackButtonName, true);
+ }
+
+ /// <summary>
+ /// Unregisters this instance from being affected by pressing the hardware back key.
+ /// </summary>
+ void UngrabBackKey()
+ {
+ KeyUngrab(EvasKeyEventArgs.PlatformBackButtonName);
+ }
+
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/DisplayOrientations.cs b/Xamarin.Forms.Platform.Tizen/Native/DisplayOrientations.cs
new file mode 100644
index 00000000..efb09529
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/DisplayOrientations.cs
@@ -0,0 +1,36 @@
+using System;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// Enumeration for the orientation of a rectangular screen.
+ /// </summary>
+ [Flags]
+ public enum DisplayOrientations
+ {
+ /// <summary>
+ /// No display orientation is specified.
+ /// </summary>
+ None = 0,
+
+ /// <summary>
+ /// The display is oriented in a natural position.
+ /// </summary>
+ Portrait = 1,
+
+ /// <summary>
+ /// The display's left side is at the top.
+ /// </summary>
+ Landscape = 2,
+
+ /// <summary>
+ /// The display is upside down.
+ /// </summary>
+ PortraitFlipped = 4,
+
+ /// <summary>
+ /// The display's right side is at the top.
+ /// </summary>
+ LandscapeFlipped = 8
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/Entry.cs b/Xamarin.Forms.Platform.Tizen/Native/Entry.cs
new file mode 100644
index 00000000..808155c5
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/Entry.cs
@@ -0,0 +1,396 @@
+using System;
+using ElmSharp;
+using EEntry = ElmSharp.Entry;
+using EColor = ElmSharp.Color;
+using ESize = ElmSharp.Size;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// Extends the Entry control, providing basic formatting features,
+ /// i.e. font color, size, placeholder.
+ /// </summary>
+ public class Entry : EEntry, IMeasurable
+ {
+ /// <summary>
+ /// Holds the formatted text of the entry.
+ /// </summary>
+ readonly Span _span = new Span();
+
+ /// <summary>
+ /// Holds the formatted text of the placeholder.
+ /// </summary>
+ readonly Span _placeholderSpan = new Span();
+
+ /// <summary>
+ /// Helps to detect whether the text change was initiated by the user
+ /// or via the Text property.
+ /// </summary>
+ int _changedByUserCallbackDepth;
+
+ /// <summary>
+ /// The type of the keyboard used by the entry.
+ /// </summary>
+ Keyboard _keyboard;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Xamarin.Forms.Platform.Tizen.Native.Entry"/> class.
+ /// </summary>
+ /// <param name="parent">Parent evas object.</param>
+ public Entry(EvasObject parent) : base(parent)
+ {
+ Scrollable = true;
+
+ ChangedByUser += (s, e) =>
+ {
+ _changedByUserCallbackDepth++;
+
+ Text = GetInternalText();
+
+ _changedByUserCallbackDepth--;
+ };
+
+ ApplyKeyboard(Keyboard.Normal);
+ }
+
+ /// <summary>
+ /// Occurs when the text has changed.
+ /// </summary>
+ public event EventHandler<TextChangedEventArgs> TextChanged;
+
+ /// <summary>
+ /// Gets or sets the text.
+ /// </summary>
+ /// <value>The text.</value>
+ public override string Text
+ {
+ get
+ {
+ return _span.Text;
+ }
+
+ set
+ {
+ if (value != _span.Text)
+ {
+ var old = _span.Text;
+ _span.Text = value;
+ ApplyTextAndStyle();
+ Device.StartTimer(TimeSpan.FromTicks(1), () =>
+ {
+ TextChanged?.Invoke(this, new TextChangedEventArgs(old, value));
+ return false;
+ });
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the color of the text.
+ /// </summary>
+ /// <value>The color of the text.</value>
+ public EColor TextColor
+ {
+ get
+ {
+ return _span.ForegroundColor;
+ }
+
+ set
+ {
+ if (!_span.ForegroundColor.Equals(value))
+ {
+ _span.ForegroundColor = value;
+ ApplyTextAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the font family of the text and the placeholder.
+ /// </summary>
+ /// <value>The font family of the text and the placeholder.</value>
+ public string FontFamily
+ {
+ get
+ {
+ return _span.FontFamily;
+ }
+
+ set
+ {
+ if (value != _span.FontFamily)
+ {
+ _span.FontFamily = value;
+ ApplyTextAndStyle();
+
+ _placeholderSpan.FontFamily = value;
+ ApplyPlaceholderAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the font attributes of the text and the placeholder.
+ /// </summary>
+ /// <value>The font attributes of the text and the placeholder.</value>
+ public FontAttributes FontAttributes
+ {
+ get
+ {
+ return _span.FontAttributes;
+ }
+
+ set
+ {
+ if (value != _span.FontAttributes)
+ {
+ _span.FontAttributes = value;
+ ApplyTextAndStyle();
+
+ _placeholderSpan.FontAttributes = value;
+ ApplyPlaceholderAndStyle();
+ }
+ }
+ }
+
+
+ /// <summary>
+ /// Gets or sets the size of the font of both text and placeholder.
+ /// </summary>
+ /// <value>The size of the font of both text and placeholder.</value>
+ public double FontSize
+ {
+ get
+ {
+ return _span.FontSize;
+ }
+
+ set
+ {
+ if (value != _span.FontSize)
+ {
+ _span.FontSize = value;
+ ApplyTextAndStyle();
+
+ _placeholderSpan.FontSize = value;
+ ApplyPlaceholderAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the horizontal text alignment of both text and placeholder.
+ /// </summary>
+ /// <value>The horizontal text alignment of both text and placeholder.</value>
+ public TextAlignment HorizontalTextAlignment
+ {
+ get
+ {
+ return _span.HorizontalTextAlignment;
+ }
+
+ set
+ {
+ if (value != _span.HorizontalTextAlignment)
+ {
+ _span.HorizontalTextAlignment = value;
+ ApplyTextAndStyle();
+
+ _placeholderSpan.HorizontalTextAlignment = value;
+ ApplyPlaceholderAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the keyboard type used by the entry.
+ /// </summary>
+ /// <value>The keyboard type.</value>
+ public Keyboard Keyboard
+ {
+ get
+ {
+ return _keyboard;
+ }
+
+ set
+ {
+ if (value != _keyboard)
+ {
+ ApplyKeyboard(value);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the placeholder's text.
+ /// </summary>
+ /// <value>The placeholder's text.</value>
+ public string Placeholder
+ {
+ get
+ {
+ return _placeholderSpan.Text;
+ }
+
+ set
+ {
+ if (value != _placeholderSpan.Text)
+ {
+ _placeholderSpan.Text = value;
+ ApplyPlaceholderAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the color of the placeholder's text.
+ /// </summary>
+ /// <value>The color of the placeholder's text.</value>
+ public EColor PlaceholderColor
+ {
+ get
+ {
+ return _placeholderSpan.ForegroundColor;
+ }
+
+ set
+ {
+ if (!_placeholderSpan.ForegroundColor.Equals(value))
+ {
+ _placeholderSpan.ForegroundColor = value;
+ ApplyPlaceholderAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Implementation of the IMeasurable.Measure() method.
+ /// </summary>
+ public ESize Measure(int availableWidth, int availableHeight)
+ {
+ var originalSize = Geometry;
+ // resize the control using the whole available width
+ Resize(availableWidth, originalSize.Height);
+
+ ESize rawSize;
+ ESize formattedSize;
+ var edjeTextBlock = EdjeObject["elm.guide"];
+
+ // if there's no text, but there's a placeholder, use it for measurements
+ if (string.IsNullOrEmpty(Text) && !string.IsNullOrEmpty(Placeholder) && edjeTextBlock != null)
+ {
+ rawSize = edjeTextBlock.TextBlockNativeSize;
+ formattedSize = edjeTextBlock.TextBlockFormattedSize;
+ }
+ else
+ {
+ // there's text in the entry, use it instead
+ rawSize = Native.TextHelper.GetRawTextBlockSize(this);
+ formattedSize = Native.TextHelper.GetFormattedTextBlockSize(this);
+ }
+
+ // restore the original size
+ Resize(originalSize.Width, originalSize.Height);
+
+ // Set bottom padding for lower case letters that have segments below the bottom line of text (g, j, p, q, y).
+ var verticalPadding = (int)Math.Ceiling(0.05 * FontSize);
+ var horizontalPadding = (int)Math.Ceiling(0.2 * FontSize);
+ rawSize.Height += verticalPadding;
+ formattedSize.Height += verticalPadding;
+ formattedSize.Width += horizontalPadding;
+
+ // if the raw text width is larger than available width, we use the available width,
+ // while height is set to the smallest height value
+ if (rawSize.Width > availableWidth)
+ {
+ return new ESize
+ {
+ Width = availableWidth,
+ Height = Math.Min(formattedSize.Height, Math.Max(rawSize.Height, availableHeight)),
+ };
+ }
+ else
+ {
+ // width is fine, return the formatted text size
+ return formattedSize;
+ }
+ }
+
+ /// <summary>
+ /// Applies entry's text and its style.
+ /// </summary>
+ void ApplyTextAndStyle()
+ {
+ SetInternalTextAndStyle(_span.GetDecoratedText(), _span.GetStyle());
+ }
+
+ /// <summary>
+ /// Sets entry's internal text and its style.
+ /// </summary>
+ /// <param name="formattedText">Formatted text, supports HTML tags.</param>
+ /// <param name="textStyle">Style applied to the formattedText.</param>
+ void SetInternalTextAndStyle(string formattedText, string textStyle)
+ {
+ if (_changedByUserCallbackDepth == 0)
+ {
+ base.Text = formattedText;
+ base.TextStyle = textStyle;
+ }
+ }
+
+ /// <summary>
+ /// Gets the internal text representation of the entry.
+ /// </summary>
+ /// <returns>The internal text representation.</returns>
+ string GetInternalText()
+ {
+ return Entry.ConvertMarkupToUtf8(base.Text);
+ }
+
+ /// <summary>
+ /// Applies the keyboard type to be used by the entry.
+ /// </summary>
+ /// <param name="keyboard">Keyboard type to be used.</param>
+ void ApplyKeyboard(Keyboard keyboard)
+ {
+ SetInternalKeyboard(_keyboard = keyboard);
+ }
+
+ /// <summary>
+ /// Configures the ElmSharp.Entry with specified keyboard type and displays
+ /// the keyboard automatically unless the provided type is Keyboard.None.
+ /// </summary>
+ /// <param name="keyboard">Keyboard type to be used.</param>
+ void SetInternalKeyboard(Keyboard keyboard)
+ {
+ if (keyboard == Keyboard.None)
+ {
+ SetInputPanelEnabled(false);
+ }
+ else
+ {
+ SetInputPanelEnabled(true);
+ SetInputPanelLayout((InputPanelLayout)keyboard);
+ }
+ }
+
+ /// <summary>
+ /// Applies placeholders's text and its style.
+ /// </summary>
+ void ApplyPlaceholderAndStyle()
+ {
+ SetInternalPlaceholderAndStyle(_placeholderSpan.GetMarkupText());
+ }
+
+ /// <summary>
+ /// Sets placeholder's internal text and style.
+ /// </summary>
+ /// <param name="markupText">Markup text to be used as a placeholder.</param>
+ void SetInternalPlaceholderAndStyle(string markupText)
+ {
+ SetPartText("guide", markupText ?? "");
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/FormattedString.cs b/Xamarin.Forms.Platform.Tizen/Native/FormattedString.cs
new file mode 100644
index 00000000..f782efd1
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/FormattedString.cs
@@ -0,0 +1,128 @@
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// Represents a text with attributes applied to some parts.
+ /// </summary>
+ /// <remarks>
+ /// Formatted string consists of spans that represent text segments with various attributes applied.
+ /// </remarks>
+ public class FormattedString
+ {
+ /// <summary>
+ /// A flag indicating whether the instance contains just a plain string without any formatting.
+ /// </summary>
+ /// <remarks>
+ /// <c>true</c> if the instance contains an unformatted string.
+ /// </remarks>
+ readonly bool _just_string;
+
+ /// <summary>
+ /// Holds the unformatted string.
+ /// </summary>
+ /// <remarks>
+ /// The contents of this field are accurate if and only if the _just_string flag is set.
+ /// </remarks>
+ readonly string _string;
+
+ /// <summary>
+ /// Holds the collection of span elements.
+ /// </summary>
+ /// <remarks>
+ /// Span elements are basically chunks of text with uniform formatting.
+ /// </remarks>
+ readonly ObservableCollection<Span> _spans;
+
+ /// <summary>
+ /// Returns the collection of span elements.
+ /// </summary>
+ public IList<Span> Spans { get { return _spans; } }
+
+ /// <summary>
+ /// Creates a new FormattedString instance with an empty string.
+ /// </summary>
+ public FormattedString()
+ {
+ _just_string = false;
+ _spans = new ObservableCollection<Span>();
+ }
+
+ /// <summary>
+ /// Creates a new FormattedString instance based on given <c>str</c>.
+ /// </summary>
+ /// <param name="str">
+ /// A string used to make a new FormattedString instance.
+ /// </param>
+ public FormattedString(string str)
+ {
+ _just_string = true;
+ _string = str;
+ }
+
+ /// <summary>
+ /// Returns the plain text of the FormattedString as an unformatted string.
+ /// </summary>
+ /// <returns>
+ /// The text content of the FormattedString without any format applied.
+ /// </returns>
+ public override string ToString()
+ {
+ if (_just_string)
+ {
+ return _string;
+ }
+ else
+ {
+ return string.Concat(from span in this.Spans select span.Text);
+ }
+ }
+
+ /// <summary>
+ /// Returns the markup text representation of the FormattedString instance.
+ /// </summary>
+ /// <returns>The string containing a markup text.</returns>
+ internal string ToMarkupString()
+ {
+ if (_just_string)
+ {
+ return _string;
+ }
+ else
+ {
+ return string.Concat(from span in this.Spans select span.GetMarkupText());
+ }
+ }
+
+ /// <summary>
+ /// Casts the FormattedString to a string.
+ /// </summary>
+ /// <param name="formatted">The FormattedString instance which will be used for the conversion.</param>
+ public static explicit operator string (FormattedString formatted)
+ {
+ return formatted.ToString();
+ }
+
+ /// <summary>
+ /// Casts the string to a FormattedString.
+ /// </summary>
+ /// <param name="text">The text which will be put in a new FormattedString instance.</param>
+ public static implicit operator FormattedString(string text)
+ {
+ return new FormattedString(text);
+ }
+
+ /// <summary>
+ /// Casts the Span to a FormattedString.
+ /// </summary>
+ /// <param name="span">The span which will be used for the conversion.</param>
+ public static implicit operator FormattedString(Span span)
+ {
+ return new FormattedString()
+ {
+ Spans = { span }
+ };
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/IContainable.cs b/Xamarin.Forms.Platform.Tizen/Native/IContainable.cs
new file mode 100644
index 00000000..dfee0cbe
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/IContainable.cs
@@ -0,0 +1,16 @@
+using System.Collections.Generic;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// Interface defining methods for managing elements of the container.
+ /// </summary>
+ /// <typeparam name="T">The type of element that can be added to the container.</typeparam>
+ public interface IContainable<T>
+ {
+ /// <summary>
+ /// The children collection of an element.
+ /// </summary>
+ IList<T> Children { get; }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/IMeasurable.cs b/Xamarin.Forms.Platform.Tizen/Native/IMeasurable.cs
new file mode 100644
index 00000000..13ee1c12
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/IMeasurable.cs
@@ -0,0 +1,20 @@
+using ESize = ElmSharp.Size;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// Interface of the controls which can measure their size taking into
+ /// account the available area.
+ /// </summary>
+ public interface IMeasurable
+ {
+ /// <summary>
+ /// Measures the size of the control in order to fit it into the
+ /// available area.
+ /// </summary>
+ /// <param name="availableWidth">Available width.</param>
+ /// <param name="availableHeight">Available height.</param>
+ /// <returns>Size of the control that fits the available area.</returns>
+ ESize Measure(int availableWidth, int availableHeight);
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/ITextable.cs b/Xamarin.Forms.Platform.Tizen/Native/ITextable.cs
new file mode 100644
index 00000000..876646eb
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/ITextable.cs
@@ -0,0 +1,68 @@
+using EColor = ElmSharp.Color;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// Interface defining properties of formattable text.
+ /// </summary>
+ public interface ITextable
+ {
+ /// <summary>
+ /// Get or sets the formatted text.
+ /// </summary>
+ FormattedString FormattedText { get; set; }
+
+ /// <summary>
+ /// Gets or sets the text.
+ /// </summary>
+ string Text { get; set; }
+
+ /// <summary>
+ /// Gets or sets the color for the text.
+ /// </summary>
+ EColor TextColor { get; set; }
+
+ /// <summary>
+ /// Gets or sets the background color for the text.
+ /// </summary>
+ EColor TextBackgroundColor { get; set; }
+
+ /// <summary>
+ /// Gets or sets the font family for the text.
+ /// </summary>
+ string FontFamily { get; set; }
+
+ /// <summary>
+ /// Gets or sets the font attributes for the text.
+ /// See <see cref="FontAttributes"/> for information about FontAttributes.
+ /// </summary>
+ FontAttributes FontAttributes { get; set; }
+
+ /// <summary>
+ /// Gets or sets the font size for the text.
+ /// </summary>
+ double FontSize { get; set; }
+
+ /// <summary>
+ /// Gets or sets the horizontal alignment mode for the text.
+ /// See <see cref="TextAlignment"/> for information about TextAlignment.
+ /// </summary>
+ TextAlignment HorizontalTextAlignment { get; set; }
+
+ /// <summary>
+ /// Gets or sets the vertical alignment mode for the text.
+ /// See <see cref="TextAlignment"/> for information about TextAlignment.
+ /// </summary>
+ TextAlignment VerticalTextAlignment { get; set; }
+
+ /// <summary>
+ /// Gets or sets the value that indicates whether the text has underline.
+ /// </summary>
+ bool Underline { get; set; }
+
+ /// <summary>
+ /// Gets or sets the value that indicates whether the text has strike line though it.
+ /// </summary>
+ bool Strikethrough { get; set; }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/Image.cs b/Xamarin.Forms.Platform.Tizen/Native/Image.cs
new file mode 100644
index 00000000..7321c5b1
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/Image.cs
@@ -0,0 +1,124 @@
+using System.Threading.Tasks;
+using ElmSharp;
+using EImage = ElmSharp.Image;
+using ESize = ElmSharp.Size;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// Extends the ElmSharp.Image class with functionality useful to renderer.
+ /// </summary>
+ public class Image : EImage, IMeasurable
+ {
+ Aspect _aspect;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Xamarin.Forms.Platform.Tizen.Native.Image"/> class.
+ /// </summary>
+ /// <param name="parent">The parent EvasObject.</param>
+ public Image(EvasObject parent) : base(parent)
+ {
+ IsScaling = true;
+ CanScaleUp = true;
+ CanScaleDown = true;
+
+ ApplyAspect(Aspect.AspectFit);
+ }
+
+ /// <summary>
+ /// Gets or sets the image aspect ratio preserving option.
+ /// </summary>
+ /// <value>The aspect option.</value>
+ public Aspect Aspect
+ {
+ get
+ {
+ return _aspect;
+ }
+
+ set
+ {
+ if (_aspect != value)
+ {
+ ApplyAspect(value);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Loads image data from the given <see cref="Xamarin.Forms.ImageSource"/> asynchronously.
+ /// </summary>
+ /// <returns>A task which will be completed when image data is loaded.</returns>
+ /// <param name="source">Image source specifying from where the image data has to be loaded.</param>
+ public Task<bool> LoadFromImageSourceAsync(ImageSource source)
+ {
+ IImageSourceHandler handler;
+ if (source != null && (handler = Registrar.Registered.GetHandler<IImageSourceHandler>(source.GetType())) != null)
+ {
+ return handler.LoadImageAsync(this, source);
+ }
+ return Task.FromResult<bool>(false);
+ }
+
+ /// <summary>
+ /// Implements the <see cref="Xamarin.Forms.Platform.Tizen.Native.IMeasurable"/> interface.
+ /// </summary>
+ /// <param name="availableWidth">Available width.</param>
+ /// <param name="availableHeight">Available height.</param>
+ public ESize Measure(int availableWidth, int availableHeight)
+ {
+ var imageSize = ObjectSize;
+
+ var size = new ESize()
+ {
+ Width = imageSize.Width,
+ Height = imageSize.Height,
+ };
+
+ if (0 != availableWidth && 0 != availableHeight
+ && (imageSize.Width > availableWidth || imageSize.Height > availableHeight))
+ {
+ // when available size is limited and insufficient for the image ...
+ double imageRatio = (double)imageSize.Width / (double)imageSize.Height;
+ double availableRatio = (double)availableWidth / (double)availableHeight;
+ // depending on the relation between availableRatio and imageRatio, copy the availableWidth or availableHeight
+ // and calculate the size which preserves the image ratio, but does not exceed the available size
+ size.Width = availableRatio > imageRatio ? imageSize.Width * availableHeight / imageSize.Height : availableWidth;
+ size.Height = availableRatio > imageRatio ? availableHeight : imageSize.Height * availableWidth / imageSize.Width;
+ }
+
+ return size;
+ }
+
+ /// <summary>
+ /// Sets the <c>IsFixedAspect</c> and <c>CanFillOutside</c> properties according to the given <paramref name="aspect"/>.
+ /// </summary>
+ /// <param name="aspect">The aspect setting to be applied to the image.</param>
+ void ApplyAspect(Aspect aspect)
+ {
+ _aspect = aspect;
+
+ switch (_aspect)
+ {
+ case Aspect.AspectFit:
+ IsFixedAspect = true;
+ CanFillOutside = false;
+ break;
+
+ case Aspect.AspectFill:
+ IsFixedAspect = true;
+ CanFillOutside = true;
+ break;
+
+ case Aspect.Fill:
+ IsFixedAspect = false;
+ CanFillOutside = false;
+ break;
+
+ default:
+ Log.Warn("Invalid Aspect value: {0}", _aspect);
+ break;
+ }
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/Keyboard.cs b/Xamarin.Forms.Platform.Tizen/Native/Keyboard.cs
new file mode 100644
index 00000000..4223ca08
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/Keyboard.cs
@@ -0,0 +1,84 @@
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// Keyboard layout type on entry control.
+ /// </summary>
+ public enum Keyboard
+ {
+ /// <summary>
+ /// Disable Keyboard
+ /// </summary>
+ None = -1,
+
+ /// <summary>
+ /// Keyboard layout type default.
+ /// </summary>
+ Normal,
+
+ /// <summary>
+ /// Keyboard layout type number.
+ /// </summary>
+ Number,
+
+ /// <summary>
+ /// Keyboard layout type email.
+ /// </summary>
+ Email,
+
+ /// <summary>
+ /// Keyboard layout type url.
+ /// </summary>
+ Url,
+
+ /// <summary>
+ /// Keyboard layout type phone.
+ /// </summary>
+ PhoneNumber,
+
+ /// <summary>
+ /// Keyboard layout type ip.
+ /// </summary>
+ Ip,
+
+ /// <summary>
+ /// Keyboard layout type month.
+ /// </summary>
+ Month,
+
+ /// <summary>
+ /// Keyboard layout type number.
+ /// </summary>
+ NumberOnly,
+
+ /// <summary>
+ /// Keyboard layout type error type. Do not use it directly!
+ /// </summary>
+ Invalid,
+
+ /// <summary>
+ /// Keyboard layout type hexadecimal.
+ /// </summary>
+ Hex,
+
+ /// <summary>
+ /// Keyboard layout type terminal type, esc, alt, ctrl, etc.
+ /// </summary>
+ Terminal,
+
+ /// <summary>
+ /// Keyboard layout type password.
+ /// </summary>
+ Password,
+
+ /// <summary>
+ /// Keyboard layout type date and time.
+ /// </summary>
+ DateTime,
+
+ /// <summary>
+ /// Keyboard layout type emoticons.
+ /// </summary>
+ Emoticon
+ }
+}
+
diff --git a/Xamarin.Forms.Platform.Tizen/Native/Label.cs b/Xamarin.Forms.Platform.Tizen/Native/Label.cs
new file mode 100644
index 00000000..cba2ba20
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/Label.cs
@@ -0,0 +1,347 @@
+using System;
+using ElmSharp;
+using ELabel = ElmSharp.Label;
+using EColor = ElmSharp.Color;
+using ESize = ElmSharp.Size;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// The Label class extends <c>ElmSharp.Label</c> to be better suited for Xamarin renderers.
+ /// Mainly the formatted text support.
+ /// </summary>
+ public class Label : ELabel, ITextable, IMeasurable
+ {
+ /// <summary>
+ /// The _span holds the content of the label.
+ /// </summary>
+ readonly Span _span = new Span();
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Xamarin.Forms.Platform.Tizen.Native.Label"/> class.
+ /// </summary>
+ /// <param name="parent">Parent evas object.</param>
+ public Label(EvasObject parent) : base(parent)
+ {
+ }
+
+ /// <summary>
+ /// Get or sets the formatted text.
+ /// </summary>
+ /// <remarks>Setting <c>FormattedText</c> changes the value of the <c>Text</c> property.</remarks>
+ /// <value>The formatted text.</value>
+ public FormattedString FormattedText
+ {
+ get
+ {
+ return _span.FormattedText;
+ }
+
+ set
+ {
+ if (value != _span.FormattedText)
+ {
+ _span.FormattedText = value;
+ ApplyTextAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the text.
+ /// </summary>
+ /// <remarks>Setting <c>Text</c> overwrites the value of the <c>FormattedText</c> property too.</remarks>
+ /// <value>The content of the label.</value>
+ public override string Text
+ {
+ get
+ {
+ return _span.Text;
+ }
+
+ set
+ {
+ if (value != _span.Text)
+ {
+ _span.Text = value;
+ ApplyTextAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the color of the formatted text.
+ /// </summary>
+ /// <value>The color of the text.</value>
+ public EColor TextColor
+ {
+ get
+ {
+ return _span.ForegroundColor;
+ }
+
+ set
+ {
+ if (!_span.ForegroundColor.Equals(value))
+ {
+ _span.ForegroundColor = value;
+ ApplyTextAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the background color for the text.
+ /// </summary>
+ /// <value>The color of the label's background.</value>
+ public EColor TextBackgroundColor
+ {
+ get
+ {
+ return _span.BackgroundColor;
+ }
+
+ set
+ {
+ if (!_span.BackgroundColor.Equals(value))
+ {
+ _span.BackgroundColor = value;
+ ApplyTextAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the font family for the text.
+ /// </summary>
+ /// <value>The font family.</value>
+ public string FontFamily
+ {
+ get
+ {
+ return _span.FontFamily;
+ }
+
+ set
+ {
+ if (value != _span.FontFamily)
+ {
+ _span.FontFamily = value;
+ ApplyTextAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the font attributes.
+ /// </summary>
+ /// <value>The font attributes.</value>
+ public FontAttributes FontAttributes
+ {
+ get
+ {
+ return _span.FontAttributes;
+ }
+
+ set
+ {
+ if (value != _span.FontAttributes)
+ {
+ _span.FontAttributes = value;
+ ApplyTextAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the font size for the text.
+ /// </summary>
+ /// <value>The size of the font.</value>
+ public double FontSize
+ {
+ get
+ {
+ return _span.FontSize;
+ }
+
+ set
+ {
+ if (value != _span.FontSize)
+ {
+ _span.FontSize = value;
+ ApplyTextAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the line wrap option.
+ /// </summary>
+ /// <value>The line break mode.</value>
+ public LineBreakMode LineBreakMode
+ {
+ get
+ {
+ return _span.LineBreakMode;
+ }
+
+ set
+ {
+ if (value != _span.LineBreakMode)
+ {
+ _span.LineBreakMode = value;
+ ApplyTextAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the horizontal text alignment.
+ /// </summary>
+ /// <value>The horizontal text alignment.</value>
+ public TextAlignment HorizontalTextAlignment
+ {
+ get
+ {
+ return _span.HorizontalTextAlignment;
+ }
+
+ set
+ {
+ if (value != _span.HorizontalTextAlignment)
+ {
+ _span.HorizontalTextAlignment = value;
+ ApplyTextAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the vertical text alignment.
+ /// </summary>
+ /// <value>The vertical text alignment.</value>
+ public TextAlignment VerticalTextAlignment
+ {
+ get
+ {
+ return _span.VerticalTextAlignment;
+ }
+
+ set
+ {
+ if (value != _span.VerticalTextAlignment)
+ {
+ _span.VerticalTextAlignment = value;
+ ApplyTextAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the value that indicates whether the text is underlined.
+ /// </summary>
+ /// <value><c>true</c> if the text is underlined.</value>
+ public bool Underline
+ {
+ get
+ {
+ return _span.Underline;
+ }
+
+ set
+ {
+ if (value != _span.Underline)
+ {
+ _span.Underline = value;
+ ApplyTextAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the value that indicates whether the text is striked out.
+ /// </summary>
+ /// <value><c>true</c> if the text is striked out.</value>
+ public bool Strikethrough
+ {
+ get
+ {
+ return _span.Strikethrough;
+ }
+
+ set
+ {
+ if (value != _span.Strikethrough)
+ {
+ _span.Strikethrough = value;
+ ApplyTextAndStyle();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Implements <see cref="Xamarin.Forms.Platform.Tizen.Native.IMeasurable"/> to provide a desired size of the label.
+ /// </summary>
+ /// <param name="availableWidth">Available width.</param>
+ /// <param name="availableHeight">Available height.</param>
+ /// <returns>Size of the control that fits the available area.</returns>
+ public ESize Measure(int availableWidth, int availableHeight)
+ {
+ var size = Geometry;
+
+ Resize(availableWidth, size.Height);
+
+ var rawSize = Native.TextHelper.GetRawTextBlockSize(this);
+ var formattedSize = Native.TextHelper.GetFormattedTextBlockSize(this);
+ Resize(size.Width, size.Height);
+
+ // Set bottom padding for lower case letters that have segments below the bottom line of text (g, j, p, q, y).
+ var verticalPadding = (int)Math.Ceiling(0.05 * FontSize);
+ var horizontalPadding = (int)Math.Ceiling(0.2 * FontSize);
+ rawSize.Height += verticalPadding;
+ formattedSize.Height += verticalPadding;
+ formattedSize.Width += horizontalPadding;
+
+ if (rawSize.Width > availableWidth)
+ {
+ return new ESize()
+ {
+ Width = formattedSize.Width,
+ Height = Math.Min(formattedSize.Height, Math.Max(rawSize.Height, availableHeight)),
+ };
+ }
+ else
+ {
+ return formattedSize;
+ }
+ }
+
+ void ApplyTextAndStyle()
+ {
+ SetInternalTextAndStyle(_span.GetDecoratedText(), _span.GetStyle());
+ }
+
+ void SetInternalTextAndStyle(string formattedText, string textStyle)
+ {
+ string emission = "elm,state,text,visible";
+
+ if (string.IsNullOrEmpty(formattedText))
+ {
+ formattedText = null;
+ textStyle = null;
+ emission = "elm,state,text,hidden";
+ }
+
+ base.Text = formattedText;
+
+ var textblock = EdjeObject["elm.text"];
+
+ if (textblock != null)
+ {
+ textblock.TextStyle = textStyle;
+ }
+
+ EdjeObject.EmitSignal(emission, "elm");
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/LayoutEventArgs.cs b/Xamarin.Forms.Platform.Tizen/Native/LayoutEventArgs.cs
new file mode 100644
index 00000000..6668f8d7
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/LayoutEventArgs.cs
@@ -0,0 +1,55 @@
+using System;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// Holds information about size of the area which can be used for layout.
+ /// </summary>
+ public class LayoutEventArgs : EventArgs
+ {
+ /// <summary>
+ /// Whether or not the dimensions have changed.
+ /// </summary>
+ public bool HasChanged
+ {
+ get;
+ internal set;
+ }
+
+ /// <summary>
+ /// X coordinate of the layout area, relative to the main window.
+ /// </summary>
+ public int X
+ {
+ get;
+ internal set;
+ }
+
+ /// <summary>
+ /// Y coordinate of the layout area, relative to the main window.
+ /// </summary>
+ public int Y
+ {
+ get;
+ internal set;
+ }
+
+ /// <summary>
+ /// Width of the layout area.
+ /// </summary>
+ public int Width
+ {
+ get;
+ internal set;
+ }
+
+ /// <summary>
+ /// Height of the layout area.
+ /// </summary>
+ public int Height
+ {
+ get;
+ internal set;
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/LineBreakMode.cs b/Xamarin.Forms.Platform.Tizen/Native/LineBreakMode.cs
new file mode 100644
index 00000000..27253ad1
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/LineBreakMode.cs
@@ -0,0 +1,43 @@
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// Enumerates values that describe options for line braking.
+ /// </summary>
+ public enum LineBreakMode
+ {
+ /// <summary>
+ /// Do not wrap text.
+ /// </summary>
+ NoWrap,
+
+ /// <summary>
+ /// Wrap at character boundaries.
+ /// </summary>
+ CharacterWrap,
+
+ /// <summary>
+ /// Wrap at word boundaries.
+ /// </summary>
+ WordWrap,
+
+ /// <summary>
+ /// Tries to wrap at word boundaries, and then wrap at a character boundary if the word is too long.
+ /// </summary>
+ MixedWrap,
+
+ /// <summary>
+ /// Truncate the head of text.
+ /// </summary>
+ HeadTruncation,
+
+ /// <summary>
+ /// Truncate the middle of text. This may be done, for example, by replacing it with an ellipsis.
+ /// </summary>
+ MiddleTruncation,
+
+ /// <summary>
+ /// Truncate the tail of text.
+ /// </summary>
+ TailTruncation
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/ListView.cs b/Xamarin.Forms.Platform.Tizen/Native/ListView.cs
new file mode 100644
index 00000000..d5531dc4
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/ListView.cs
@@ -0,0 +1,601 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.ComponentModel;
+using ElmSharp;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// Type alias which identifies list of cells whose data model was transformed by Xamarin.
+ /// </summary>
+ using GroupList = TemplatedItemsList<ItemsView<Cell>, Cell>;
+
+ /// <summary>
+ /// Native ListView implementation for Xamarin renderer
+ /// </summary>
+ /// <remarks>
+ /// This internally uses GenList class.
+ /// One should note that it is optimized for displaying many elements which may be
+ /// unavailable at first. This means that only currently visible elements will be constructed.
+ /// Whenever element disappears from visible space its content is destroyed for time being.
+ /// This is carried out by so called Cell Handlers.
+ /// </remarks>
+ public class ListView : GenList
+ {
+ /// <summary>
+ /// ItemContext helper class. This represents the association between Xamarin.Forms.Cell and
+ /// native elements. It also stores useful context for them.
+ /// </summary>
+ public class ItemContext
+ {
+ public ItemContext()
+ {
+ Item = null;
+ Cell = null;
+ Renderer = null;
+ ListOfSubItems = null;
+ }
+
+ public GenListItem Item;
+ public Cell Cell;
+ public bool IsGroupItem;
+ public CellRenderer Renderer;
+ internal TemplatedItemsList<ItemsView<Cell>, Cell> ListOfSubItems;
+ }
+
+ /// <summary>
+ /// The item context list for each added element.
+ /// </summary>
+ readonly List<ItemContext> _itemContextList = new List<ItemContext>();
+
+ /// <summary>
+ /// Registered cell handlers.
+ /// </summary>
+ protected readonly IDictionary<Type, CellRenderer> _cellRendererCache = new Dictionary<Type, CellRenderer>();
+
+ /// <summary>
+ /// Registered group handlers.
+ /// </summary>
+ protected readonly IDictionary<Type, CellRenderer> _groupCellRendererCache = new Dictionary<Type, CellRenderer>();
+
+ /// <summary>
+ /// The header context.
+ /// </summary>
+ ItemContext _headerContext;
+
+ /// <summary>
+ /// The header element.
+ /// </summary>
+ VisualElement _headerElement;
+
+ /// <summary>
+ /// The footer context.
+ /// </summary>
+ ItemContext _footerContext;
+
+ /// <summary>
+ /// The footer element.
+ /// </summary>
+ VisualElement _footerElement;
+
+ /// <summary>
+ /// The item class for header and footer.
+ /// </summary>
+ GenItemClass _headerFooterItemClass = null;
+
+ /// <summary>
+ /// Gets or sets a value indicating whether this instance has grouping enabled.
+ /// </summary>
+ /// <value><c>true</c> if this instance has grouping enabled.</value>
+ public bool IsGroupingEnabled { get; set; }
+
+ /// <summary>
+ /// Constructor of ListView native control.
+ /// </summary>
+ /// <param name="parent">ElmSharp object which is parent of particular list view</param>
+ public ListView(EvasObject parent)
+ : base(parent)
+ {
+ ItemRealized += OnItemAppear;
+ ItemUnrealized += OnItemDisappear;
+ }
+
+ /// <summary>
+ /// Gets the item context based on Cell item.
+ /// </summary>
+ /// <returns>The item context.</returns>
+ /// <param name="cell">Cell for which context should be found.</param>
+ internal ItemContext GetItemContext(Cell cell)
+ {
+ if (cell == null)
+ {
+ return null;
+ }
+ else
+ {
+ return _itemContextList.Find(X => X.Cell == cell);
+ }
+ }
+
+ /// <summary>
+ /// Sets the HasUnevenRows property.
+ /// </summary>
+ /// <param name="hasUnevenRows">If <c>true</c>, the list will allow uneven sizes for its rows.</param>
+ public void SetHasUnevenRows(bool hasUnevenRows)
+ {
+ Homogeneous = !hasUnevenRows;
+ UpdateRealizedItems();
+ }
+
+ /// <summary>
+ /// Adds elements to the list and defines its presentation based on Cell type.
+ /// </summary>
+ /// <param name="_source">IEnumerable on Cell collection.</param>
+ /// <param name="beforeCell">Cell before which new items will be placed.
+ /// Null value may also be passed as this parameter, which results in appending new items to the end.
+ /// </param>
+ public void AddSource(IEnumerable _source, Cell beforeCell = null)
+ {
+ foreach (var data in _source)
+ {
+ GroupList groupList = data as GroupList;
+ if (groupList != null)
+ {
+ AddGroupItem(groupList, beforeCell);
+ foreach (var item in groupList)
+ {
+ AddItem(item as Cell, groupList.HeaderContent);
+ }
+ }
+ else
+ {
+ AddItem(data as Cell, null, beforeCell);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Deletes all items from a given group.
+ /// </summary>
+ /// <param name="group">Group of items to be deleted.</param>
+ internal void ResetGroup(GroupList group)
+ {
+ var items = _itemContextList.FindAll(x => x.ListOfSubItems == group && x.Cell != group.HeaderContent);
+ foreach (var item in items)
+ {
+ item.Item?.Delete();
+ }
+ }
+
+ /// <summary>
+ /// Adds items to the group.
+ /// </summary>
+ /// <param name="itemGroup">Group to which elements will be added.</param>
+ /// <param name="newItems">New list items to be added.</param>
+ /// <param name="cellBefore">A reference to the Cell already existing in a ListView.
+ /// Newly added cells will be put just before this cell.</param>
+ public void AddItemsToGroup(IEnumerable itemGroup, IEnumerable newItems, Cell cellBefore = null)
+ {
+ ItemContext groupCtx = GetItemContext((itemGroup as GroupList)?.HeaderContent);
+ if (groupCtx != null)
+ {
+ foreach (var item in newItems)
+ {
+ AddItem(item as Cell, groupCtx.Cell, cellBefore);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Removes the specified cells.
+ /// </summary>
+ /// <param name="cells">Cells to be removed.</param>
+ public void Remove(IEnumerable cells)
+ {
+ foreach (var data in cells)
+ {
+ var group = data as GroupList;
+ if (group != null)
+ {
+ ItemContext groupCtx = GetItemContext(group.HeaderContent);
+ Remove(groupCtx.ListOfSubItems);
+ groupCtx.Item.Delete();
+ }
+ else
+ {
+ ItemContext itemCtx = GetItemContext(data as Cell);
+ itemCtx?.Item?.Delete();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Scrolls the list to a specified cell.
+ /// </summary>
+ /// <remarks>
+ /// Different scrolling behaviors are also possible. The element may be positioned in the center,
+ /// top or bottom of the visible part of the list depending on the value of the <c>position</c> parameter.
+ /// </remarks>
+ /// <param name="cell">Cell which will be displayed after scrolling .</param>
+ /// <param name="position">This will defines scroll to behavior based on ScrollToPosition values.</param>
+ /// <param name="animated">If <c>true</c>, scrolling will be animated. Otherwise the cell will be moved instantaneously.</param>
+ public void ApplyScrollTo(Cell cell, ScrollToPosition position, bool animated)
+ {
+ GenListItem item = GetItemContext(cell)?.Item;
+ if (item != null)
+ this.ScrollTo(item, position.ToNative(), animated);
+ }
+
+ /// <summary>
+ /// Selects the specified cell.
+ /// </summary>
+ /// <param name="cell">Cell to be selected.</param>
+ public void ApplySelectedItem(Cell cell)
+ {
+ GenListItem item = GetItemContext(cell)?.Item;
+ if (item != null)
+ item.IsSelected = true;
+ }
+
+ /// <summary>
+ /// Sets the header.
+ /// </summary>
+ /// <param name="header">Header of the list.</param>
+ public void SetHeader(VisualElement header)
+ {
+ if (header == null)
+ {
+ if (HasHeader())
+ {
+ RemoveHeader();
+ }
+
+ return;
+ }
+
+ GenItemClass headerTemplate = GetHeaderFooterItemClass();
+
+ _headerElement = header;
+ if (HasHeader())
+ {
+ FirstItem.UpdateItemClass(headerTemplate, header);
+ }
+ else
+ {
+ _headerContext = new ItemContext();
+ _headerContext.Item = _itemContextList.Count > 0 ? InsertBefore(headerTemplate, header, FirstItem) : Append(headerTemplate, header);
+ _headerContext.Item.SelectionMode = GenListSelectionMode.None;
+ _headerContext.Item.Deleted += HeaderDeletedHandler;
+ _itemContextList.Insert(0, _headerContext);
+ }
+ }
+
+ /// <summary>
+ /// Sets the footer.
+ /// </summary>
+ /// <param name="footer">Footer of the list.</param>
+ public void SetFooter(VisualElement footer)
+ {
+ if (footer == null)
+ {
+ if (HasFooter())
+ {
+ RemoveFooter();
+ }
+ return;
+ }
+
+ GenItemClass footerTemplate = GetHeaderFooterItemClass();
+
+ _footerElement = footer;
+ if (HasFooter())
+ {
+ _footerContext.Item.UpdateItemClass(footerTemplate, footer);
+ }
+ else
+ {
+ _footerContext = new ItemContext();
+ _footerContext.Item = Append(footerTemplate, footer);
+ _footerContext.Item.SelectionMode = GenListSelectionMode.None;
+ _footerContext.Item.Deleted += FooterDeletedHandler;
+ _itemContextList.Add(_footerContext);
+ }
+ }
+
+ /// <summary>
+ /// Removes the header.
+ /// </summary>
+ public void RemoveHeader()
+ {
+ _itemContextList.Remove(_headerContext);
+ _headerContext?.Item?.Delete();
+ _headerContext = null;
+ _headerElement = null;
+ }
+
+ /// <summary>
+ /// Removes the footer.
+ /// </summary>
+ public void RemoveFooter()
+ {
+ _itemContextList.Remove(_footerContext);
+ _footerContext?.Item?.Delete();
+ _footerContext = null;
+ _footerElement = null;
+ }
+
+ /// <summary>
+ /// Determines whether this instance has a header.
+ /// </summary>
+ /// <returns><c>true</c> if the header is present.</returns>
+ public bool HasHeader()
+ {
+ return _headerContext != null;
+ }
+
+ /// <summary>
+ /// Determines whether this instance has a footer.
+ /// </summary>
+ /// <returns><c>true</c> if the footer is present.</returns>
+ public bool HasFooter()
+ {
+ return _footerContext != null;
+ }
+
+ /// <summary>
+ /// Gets the header.
+ /// </summary>
+ /// <returns>The header.</returns>
+ public VisualElement GetHeader()
+ {
+ return _headerElement;
+ }
+
+ /// <summary>
+ /// Gets the footer.
+ /// </summary>
+ /// <returns>The footer.</returns>
+ public VisualElement GetFooter()
+ {
+ return _footerElement;
+ }
+
+ /// <summary>
+ /// Handles the header deleted event.
+ /// </summary>
+ /// <param name="sender">Sender of the event.</param>
+ /// <param name="e">Empty argument.</param>
+ void HeaderDeletedHandler(object sender, EventArgs e)
+ {
+ _itemContextList.Remove(_headerContext);
+ _headerContext = null;
+ }
+
+ /// <summary>
+ /// Handles the footer deleted event.
+ /// </summary>
+ /// <param name="sender">Sender of the event.</param>
+ /// <param name="e">Empty argument.</param>
+ void FooterDeletedHandler(object sender, EventArgs e)
+ {
+ _itemContextList.Remove(_footerContext);
+ _footerContext = null;
+ }
+
+ /// <summary>
+ /// Called every time an object gets realized.
+ /// </summary>
+ /// <param name="sender">Sender of the event.</param>
+ /// <param name="evt">GenListItemEventArgs.</param>
+ void OnItemAppear(object sender, GenListItemEventArgs evt)
+ {
+ ItemContext itemContext = (evt.Item.Data as ItemContext);
+
+ if (itemContext != null && itemContext.Cell != null)
+ {
+ (itemContext.Cell as ICellController).SendAppearing();
+ }
+ }
+
+ /// <summary>
+ /// Called every time an object gets unrealized.
+ /// </summary>
+ /// <param name="sender">Sender of the event.</param>
+ /// <param name="evt">GenListItemEventArgs.</param>
+ void OnItemDisappear(object sender, GenListItemEventArgs evt)
+ {
+ ItemContext itemContext = (evt.Item.Data as ItemContext);
+ if (itemContext != null && itemContext.Cell != null)
+ {
+ (itemContext.Cell as ICellController).SendDisappearing();
+ itemContext.Renderer?.SendUnrealizedCell(itemContext.Cell);
+ }
+ }
+
+
+ /// <summary>
+ /// A convenience shorthand method for derivate classes.
+ /// </summary>
+ /// <param name="cell">Cell to be added.</param>
+ protected void AddCell(Cell cell)
+ {
+ AddItem(cell);
+ }
+
+ /// <summary>
+ /// Gets the cell renderer for given cell type.
+ /// </summary>
+ /// <returns>The cell handler.</returns>
+ /// <param name="cell">Cell to be added.</param>
+ /// <param name="isGroup">If <c>true</c>, then group handlers will be included in the lookup as well.</param>
+ protected virtual CellRenderer GetCellRenderer(Cell cell, bool isGroup = false)
+ {
+ Type type = cell.GetType();
+ var cache = isGroup ? _groupCellRendererCache : _cellRendererCache;
+ if (cache.ContainsKey(type))
+ return cache[type];
+
+ CellRenderer renderer = null;
+
+ if (isGroup && type == typeof(TextCell))
+ {
+ renderer = new GroupCellTextRenderer();
+ }
+ renderer = renderer ?? Registrar.Registered.GetHandler<CellRenderer>(type);
+
+ if (renderer == null)
+ {
+ Log.Error("Cell type is not handled: {0}", cell.GetType());
+ throw new ArgumentNullException("Unsupported cell type");
+ }
+ return cache[type] = renderer;
+ }
+
+ /// <summary>
+ /// Adds the group item. Group item is actually of class GroupList because
+ /// group item has sub items (can be zero) which needs to be added.
+ /// If beforeCell is not null, new group will be added just before it.
+ /// </summary>
+ /// <param name="groupList">Group to be added.</param>
+ /// <param name="beforeCell">Before cell.</param>
+ void AddGroupItem(GroupList groupList, Cell beforeCell = null)
+ {
+ Cell groupCell = groupList.HeaderContent;
+ CellRenderer groupRenderer = GetCellRenderer(groupCell, true);
+ ItemContext groupItemContext = new ItemContext();
+ groupItemContext.Cell = groupCell;
+ groupItemContext.Renderer = groupRenderer;
+
+ if (beforeCell != null)
+ {
+ GenListItem beforeItem = GetItemContext(beforeCell)?.Item;
+ groupItemContext.Item = InsertBefore(groupRenderer.Class, groupItemContext, beforeItem, GenListItemType.Group);
+ }
+ else
+ {
+ groupItemContext.Item = Append(groupRenderer.Class, groupItemContext, GenListItemType.Group);
+ }
+
+ groupItemContext.Item.SelectionMode = GenListSelectionMode.None;
+ groupItemContext.IsGroupItem = true;
+
+ groupItemContext.ListOfSubItems = groupList;
+ groupItemContext.Item.Deleted += ItemDeletedHandler;
+ _itemContextList.Add(groupItemContext);
+ }
+
+ /// <summary>
+ /// Adds the item.
+ /// </summary>
+ /// <param name="cell">Cell to be added.</param>
+ /// <param name="groupCell">Group to which the new item should belong.</param>
+ /// <remark>If the value of <c>groupCell</c> is not null, the new item will be put into the requested group. </remark>
+ /// <param name="beforeCell">The cell before which the new item should be placed.</param>
+ /// <remarks> If the value of <c>beforeCell</c> is not null, the new item will be placed just before the requested cell. </remarks>
+ void AddItem(Cell cell, Cell groupCell = null, Cell beforeCell = null)
+ {
+ CellRenderer renderer = GetCellRenderer(cell);
+ GenListItem parentItem = null;
+
+ ItemContext itemContext = new ItemContext();
+ itemContext.Cell = cell;
+ itemContext.Renderer = renderer;
+
+ if (IsGroupingEnabled && groupCell != null)
+ {
+ var groupContext = GetItemContext(groupCell);
+ itemContext.ListOfSubItems = groupContext.ListOfSubItems;
+ parentItem = groupContext.Item;
+ }
+
+ if (beforeCell != null)
+ {
+ GenListItem beforeItem = GetItemContext(beforeCell)?.Item;
+ itemContext.Item = InsertBefore(renderer.Class, itemContext, beforeItem, GenListItemType.Normal, parentItem);
+ }
+ else
+ {
+ itemContext.Item = Append(renderer.Class, itemContext, GenListItemType.Normal, parentItem);
+ }
+
+ itemContext.Item.SelectionMode = GenListSelectionMode.Always;
+
+ cell.PropertyChanged += OnCellPropertyChanged;
+ (cell as ICellController).ForceUpdateSizeRequested += OnForceUpdateSizeRequested;
+ itemContext.Item.Deleted += ItemDeletedHandler;
+ _itemContextList.Add(itemContext);
+ }
+
+ /// <summary>
+ /// Handles item deleted event.
+ /// </summary>
+ /// <param name="sender">Sender of the event.</param>
+ /// <param name="e">Empty argument.</param>
+ void ItemDeletedHandler(object sender, EventArgs e)
+ {
+ ItemContext itemContext = (sender as GenListItem).Data as ItemContext;
+ if (itemContext.Cell != null)
+ {
+ itemContext.Cell.PropertyChanged -= OnCellPropertyChanged;
+ (itemContext.Cell as ICellController).ForceUpdateSizeRequested -= OnForceUpdateSizeRequested;
+ }
+ _itemContextList.Remove(itemContext);
+ }
+
+ /// <summary>
+ /// Invoked whenever the properties of data model change.
+ /// </summary>
+ /// <param name="sender">Sender of the event.</param>
+ /// <param name="e">PropertyChangedEventArgs.</param>
+ /// <remarks>
+ /// The purpose of this method is to propagate these changes to the presentation layer.
+ /// </remarks>
+ void OnCellPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ var cell = sender as Cell;
+ var context = GetItemContext(cell);
+ context.Renderer.SendCellPropertyChanged(cell, context.Item, e.PropertyName);
+ }
+
+ void OnForceUpdateSizeRequested(object sender, EventArgs e)
+ {
+ var cell = sender as Cell;
+ var itemContext = GetItemContext(cell);
+ if (itemContext.Item != null)
+ itemContext.Item.Update();
+ }
+
+ /// <summary>
+ /// Gets the item class used for header and footer cells.
+ /// </summary>
+ /// <returns>The header and footer item class.</returns>
+ GenItemClass GetHeaderFooterItemClass()
+ {
+ if (_headerFooterItemClass == null)
+ {
+ _headerFooterItemClass = new GenItemClass("full")
+ {
+ GetContentHandler = (data, part) =>
+ {
+ VisualElement element = data as VisualElement;
+ var renderer = Platform.GetOrCreateRenderer(element);
+
+ if (element.MinimumHeightRequest == -1)
+ {
+ SizeRequest request = element.Measure(double.PositiveInfinity, double.PositiveInfinity);
+ renderer.NativeView.MinimumHeight = (int)request.Request.Height;
+ }
+ else
+ {
+ renderer.NativeView.MinimumHeight = (int)element.MinimumHeightRequest;
+ }
+
+ return renderer.NativeView;
+ }
+ };
+ }
+ return _headerFooterItemClass;
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/MasterDetailPage.cs b/Xamarin.Forms.Platform.Tizen/Native/MasterDetailPage.cs
new file mode 100644
index 00000000..5751aaf8
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/MasterDetailPage.cs
@@ -0,0 +1,364 @@
+using System;
+using ElmSharp;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// The native widget which provides Xamarin.MasterDetailPage features.
+ /// </summary>
+ public class MasterDetailPage : Box
+ {
+ /// <summary>
+ /// The portion of the screen that the MasterPage takes in Split mode.
+ /// </summary>
+ static readonly double s_splitRatio = 0.35;
+
+ /// <summary>
+ /// The portion of the screen that the MasterPage takes in Popover mode.
+ /// </summary>
+ static readonly double s_popoverRatio = 0.8;
+
+ /// <summary>
+ /// The default master behavior (a.k.a mode).
+ /// </summary>
+ static readonly MasterBehavior s_defaultMasterBehavior = (Device.Idiom == TargetIdiom.Phone) ? MasterBehavior.Popover : MasterBehavior.SplitOnLandscape;
+
+ /// <summary>
+ /// The MasterPage native container.
+ /// </summary>
+ readonly Canvas _masterCanvas;
+
+ /// <summary>
+ /// The DetailPage native container.
+ /// </summary>
+ readonly Canvas _detailCanvas;
+
+ /// <summary>
+ /// The container for <c>_masterCanvas</c> and <c>_detailCanvas</c> used in split mode.
+ /// </summary>
+ readonly Panes _splitPane;
+
+ /// <summary>
+ /// The container for <c>_masterCanvas</c> used in popover mode.
+ /// </summary>
+ readonly Panel _drawer;
+
+ /// <summary>
+ /// The <see cref="MasterBehavior"/> property value.
+ /// </summary>
+ MasterBehavior _masterBehavior = s_defaultMasterBehavior;
+
+ /// <summary>
+ /// The actual MasterDetailPage mode - either split or popover. It depends on <c>_masterBehavior</c> and screen orientation.
+ /// </summary>
+ MasterBehavior _internalMasterBehavior = MasterBehavior.Popover;
+
+ /// <summary>
+ /// The <see cref="Master"/> property value.
+ /// </summary>
+ EvasObject _master;
+
+ /// <summary>
+ /// The <see cref="Detail"/> property value.
+ /// </summary>
+ EvasObject _detail;
+
+ /// <summary>
+ /// The main widget - either <see cref="_splitPlane"/> or <see cref="_detailPage"/>, depending on the mode.
+ /// </summary>
+ EvasObject _mainWidget;
+
+ /// <summary>
+ /// The <see cref="IsPresented"/> property value.
+ /// </summary>
+ bool _isPresented;
+
+ /// <summary>
+ /// The <see cref="IsGestureEnabled"/> property value.
+ /// </summary>
+ bool _isGestureEnabled;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Xamarin.Forms.Platform.Tizen.Native.MasterDetailPage"/> class.
+ /// </summary>
+ /// <param name="parent">Parent evas object.</param>
+ public MasterDetailPage(EvasObject parent) : base(parent)
+ {
+ // we control the layout ourselves
+ Resized += (sender, e) =>
+ {
+ var g = Geometry;
+
+ // main widget should fill the area of the MasterDetailPage
+ if (_mainWidget != null)
+ {
+ _mainWidget.Geometry = g;
+ }
+
+ g.Width = (int)((s_popoverRatio * (double)g.Width));
+ _drawer.Geometry = g;
+ };
+
+ // create the controls which will hold the master and detail pages
+ _masterCanvas = new Canvas(this);
+ _masterCanvas.SetAlignment(-1.0, -1.0); // fill
+ _masterCanvas.SetWeight(1.0, 1.0); // expand
+ _masterCanvas.Resized += (sender, e) =>
+ {
+ UpdatePageGeometry(_master);
+ };
+
+ _detailCanvas = new Canvas(this);
+ _detailCanvas.SetAlignment(-1.0, -1.0); // fill
+ _detailCanvas.SetWeight(1.0, 1.0); // expand
+ _detailCanvas.Resized += (sender, e) =>
+ {
+ UpdatePageGeometry(_detail);
+ };
+
+ _splitPane = new Panes(this)
+ {
+ IsFixed = true,
+ IsHorizontal = false,
+ Proportion = s_splitRatio,
+ };
+
+ _drawer = new Panel(this)
+ {
+ Direction = PanelDirection.Left,
+ };
+ _drawer.SetScrollable(_isGestureEnabled);
+ _drawer.SetScrollableArea(1.0);
+ _drawer.Toggled += (object sender, EventArgs e) =>
+ {
+ IsPresented = _drawer.IsOpen;
+ };
+
+ IsPresentedChanged += (sender, e) =>
+ {
+ _drawer.IsOpen = IsPresented;
+ };
+
+ ConfigureLayout();
+
+ // in case of the screen rotation we may need to update the choice between split
+ // and popover behaviors and reconfigure the layout
+ Forms.Context.MainWindow.RotationChanged += (sender, e) =>
+ {
+ UpdateMasterBehavior();
+ };
+ }
+
+ /// <summary>
+ /// Occurs when the MasterPage is shown or hidden.
+ /// </summary>
+ public event EventHandler IsPresentedChanged;
+
+ /// <summary>
+ /// Gets or sets the MasterDetailPage behavior.
+ /// </summary>
+ /// <value>The behavior of the <c>MasterDetailPage</c> requested by the user.</value>
+ public MasterBehavior MasterBehavior
+ {
+ get
+ {
+ return _masterBehavior;
+ }
+
+ set
+ {
+ if (_masterBehavior != value)
+ {
+ _masterBehavior = value;
+
+ UpdateMasterBehavior();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the content of the MasterPage.
+ /// </summary>
+ /// <value>The MasterPage.</value>
+ public EvasObject Master
+ {
+ get
+ {
+ return _master;
+ }
+
+ set
+ {
+ if (_master != value)
+ {
+ _master = value;
+ UpdatePageGeometry(_master);
+ _masterCanvas.Children.Clear();
+ _masterCanvas.Children.Add(_master);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the content of the DetailPage.
+ /// </summary>
+ /// <value>The DetailPage.</value>
+ public EvasObject Detail
+ {
+ get
+ {
+ return _detail;
+ }
+
+ set
+ {
+ if (_detail != value)
+ {
+ _detail = value;
+ UpdatePageGeometry(_detail);
+ _detailCanvas.Children.Clear();
+ _detailCanvas.Children.Add(_detail);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether the MasterPage is shown.
+ /// </summary>
+ /// <value><c>true</c> if the MasterPage is presented.</value>
+ public bool IsPresented
+ {
+ get
+ {
+ return _isPresented;
+ }
+
+ set
+ {
+ if (_isPresented != value)
+ {
+ _isPresented = value;
+ IsPresentedChanged?.Invoke(this, EventArgs.Empty);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether a MasterDetailPage allows showing MasterPage with swipe gesture.
+ /// </summary>
+ /// <value><c>true</c> if the MasterPage can be revealed with a gesture.</value>
+ public bool IsGestureEnabled
+ {
+ get
+ {
+ return _isGestureEnabled;
+ }
+
+ set
+ {
+ if (_isGestureEnabled != value)
+ {
+ _isGestureEnabled = value;
+ _drawer.SetScrollable(_isGestureEnabled);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Updates the geometry of the selected page.
+ /// </summary>
+ /// <param name="page">Master or Detail page to be updated.</param>
+ void UpdatePageGeometry(EvasObject page)
+ {
+ if (page != null)
+ {
+ if (_master == page)
+ {
+ // update the geometry of the master page
+ page.Geometry = _masterCanvas.Geometry;
+ }
+ else if (_detail == page)
+ {
+ // update the geometry of the detail page
+ page.Geometry = _detailCanvas.Geometry;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Updates <see cref="_internalMasterBehavior"/> according to <see cref="MasterDetailBehavior"/> set by the user and current screen orientation.
+ /// </summary>
+ void UpdateMasterBehavior()
+ {
+ var behavior = (_masterBehavior == MasterBehavior.Default) ? s_defaultMasterBehavior : _masterBehavior;
+
+ // Screen orientation affects those 2 behaviors
+ if (behavior == MasterBehavior.SplitOnLandscape ||
+ behavior == MasterBehavior.SplitOnPortrait)
+ {
+ var orientation = Forms.Context.MainWindow.CurrentOrientation;
+
+ if (((orientation == DisplayOrientations.Landscape || orientation == DisplayOrientations.LandscapeFlipped) && behavior == MasterBehavior.SplitOnLandscape) ||
+ ((orientation == DisplayOrientations.Portrait || orientation == DisplayOrientations.PortraitFlipped) && behavior == MasterBehavior.SplitOnPortrait))
+ {
+ behavior = MasterBehavior.Split;
+ }
+ else
+ {
+ behavior = MasterBehavior.Popover;
+ }
+ }
+
+ if (behavior != _internalMasterBehavior)
+ {
+ _internalMasterBehavior = behavior;
+
+ ConfigureLayout();
+ }
+ }
+
+ /// <summary>
+ /// Composes the structure of all the necessary widgets.
+ /// </summary>
+ void ConfigureLayout()
+ {
+ _drawer.SetContent(null, true);
+ _drawer.Hide();
+
+ _splitPane.SetPartContent("left", null, true);
+ _splitPane.SetPartContent("right", null, true);
+ _splitPane.Hide();
+
+ UnPackAll();
+
+ // the structure for split mode and for popover mode looks differently
+ if (_internalMasterBehavior == MasterBehavior.Split)
+ {
+ _splitPane.SetPartContent("left", _masterCanvas, true);
+ _splitPane.SetPartContent("right", _detailCanvas, true);
+ PackEnd(_splitPane);
+
+ _splitPane.Show();
+
+ _mainWidget = _splitPane;
+
+ IsPresented = true;
+ }
+ else
+ {
+ _drawer.SetContent(_masterCanvas, true);
+ PackEnd(_detailCanvas);
+ PackEnd(_drawer);
+
+ _drawer.Show();
+
+ _mainWidget = _detailCanvas;
+
+ _drawer.IsOpen = IsPresented;
+ }
+
+ _masterCanvas.Show();
+ _detailCanvas.Show();
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/ObservableCollection.cs b/Xamarin.Forms.Platform.Tizen/Native/ObservableCollection.cs
new file mode 100644
index 00000000..d33407c9
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/ObservableCollection.cs
@@ -0,0 +1,30 @@
+using System.Collections.Specialized;
+using System.Linq;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// Represents a dynamic data collection that provides notifications when items get added, removed, or when the whole list is refreshed.
+ /// </summary>
+ /// <typeparam name="T">The type of elements in the collection.</typeparam>
+ internal class ObservableCollection<T> : System.Collections.ObjectModel.ObservableCollection<T>
+ {
+ /// <summary>
+ /// Removes all items from the collection.
+ /// </summary>
+ /// <remarks>
+ /// Fisrt remove all items, send CollectionChanged event with Remove Action
+ /// Second call ClearItems of base
+ /// </remarks>
+ protected override void ClearItems()
+ {
+ var oldItems = Items.ToList();
+ Items.Clear();
+ using (BlockReentrancy())
+ {
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, oldItems));
+ }
+ base.ClearItems();
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/SearchBar.cs b/Xamarin.Forms.Platform.Tizen/Native/SearchBar.cs
new file mode 100644
index 00000000..db597ed9
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/SearchBar.cs
@@ -0,0 +1,423 @@
+using System;
+using ElmSharp;
+using EColor = ElmSharp.Color;
+using ESize = ElmSharp.Size;
+using ERect = ElmSharp.Rect;
+using ERectangle = ElmSharp.Rectangle;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// Provides implementation of the search bar widget.
+ /// </summary>
+ public class SearchBar : Canvas, IMeasurable
+ {
+ /// <summary>
+ /// The height of the background of the search bar.
+ /// </summary>
+ const int BackgroundHeight = 120;
+
+ /// <summary>
+ /// The style of the cancel button.
+ /// </summary>
+ const string CancelButtonLayoutStyle = "editfield_clear";
+
+ /// <summary>
+ /// The horizontal padding of the cancel button.
+ /// </summary>
+ const int CancelButtonPaddingHorizontal = 17;
+
+ /// <summary>
+ /// The size of the cancel button.
+ /// </summary>
+ const int CancelButtonSize = 80;
+
+ /// <summary>
+ /// The height of the entry.
+ /// </summary>
+ const int EntryHeight = 54;
+
+ /// <summary>
+ /// The horizontal padding of the entry.
+ /// </summary>
+ const int EntryPaddingHorizontal = 42;
+
+ /// <summary>
+ /// The vertical padding of the entry.
+ /// </summary>
+ const int EntryPaddingVertical = 33;
+
+ /// <summary>
+ /// The height of the rectangle used to draw underline effect.
+ /// </summary>
+ const int RectangleHeight = 2;
+
+ /// <summary>
+ /// The bottom padding of the rectangle used to draw underline effect.
+ /// </summary>
+ const int RectanglePaddingBottom = 20;
+
+ /// <summary>
+ /// The horizontal padding of the rectangle used to draw underline effect.
+ /// </summary>
+ const int RectanglePaddingHorizontal = 32;
+
+ /// <summary>
+ /// The top padding of the rectangle used to draw underline effect.
+ /// </summary>
+ const int RectanglePaddingTop = 11;
+
+ //TODO: read default platform color
+
+ /// <summary>
+ /// The color of the underline rectangle.
+ /// </summary>
+ static readonly EColor s_underlineColor = EColor.Aqua;
+
+ /// <summary>
+ /// The dimmed color of the underline rectangle.
+ /// </summary>
+ static readonly EColor s_underlineDimColor = EColor.Gray;
+
+ /// <summary>
+ /// The cancel button.
+ /// </summary>
+ Button _cancelButton;
+
+ /// <summary>
+ /// The text entry.
+ /// </summary>
+ Entry _entry;
+
+ /// <summary>
+ /// The underline rectangle.
+ /// </summary>
+ ERectangle _underlineRectangle;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Xamarin.Forms.Platform.Tizen.Native.SearchBar"/> class.
+ /// </summary>
+ /// <param name="parent">Parent evas object.</param>
+ public SearchBar(EvasObject parent) : base(parent)
+ {
+ _entry = new Entry(parent)
+ {
+ IsSingleLine = true,
+ };
+ _entry.SetInputPanelReturnKeyType(InputPanelReturnKeyType.Search);
+ _entry.TextChanged += EntryTextChanged;
+ _entry.Activated += EntryActivated;
+ _entry.Focused += EntryFocused;
+ _entry.Unfocused += EntryUnfocused;
+ _entry.Show();
+
+ _cancelButton = new Button(parent);
+ _cancelButton.Style = CancelButtonLayoutStyle;
+ _cancelButton.Clicked += CancelButtonClicked;
+
+ _underlineRectangle = new ERectangle(parent)
+ {
+ Color = IsEnabled ? s_underlineColor : s_underlineDimColor,
+ };
+ _underlineRectangle.Show();
+
+ Children.Add(_entry);
+ Children.Add(_cancelButton);
+ Children.Add(_underlineRectangle);
+
+ Show();
+
+ this.LayoutUpdated += SearchBarLayoutUpdated;
+ }
+
+ /// <summary>
+ /// Occurs when the search button on the keyboard is pressed.
+ /// </summary>
+ public event EventHandler SearchButtonPressed;
+
+ /// <summary>
+ /// Occurs when the entry's text has changed.
+ /// </summary>
+ public event EventHandler<TextChangedEventArgs> TextChanged;
+
+ /// <summary>
+ /// Gets or sets the color of the cancel button.
+ /// </summary>
+ /// <value>Color of the cancel button.</value>
+ public EColor CancelButtonColor
+ {
+ get
+ {
+ return _cancelButton.Color;
+ }
+
+ set
+ {
+ if (!_cancelButton.Color.Equals(value))
+ {
+ _cancelButton.Color = value;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the font attributes of the search bar's entry.
+ /// </summary>
+ /// <value>The font attributes.</value>
+ public FontAttributes FontAttributes
+ {
+ get
+ {
+ return _entry.FontAttributes;
+ }
+
+ set
+ {
+ if (value != _entry.FontAttributes)
+ {
+ _entry.FontAttributes = value;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the font family of the search bar's entry.
+ /// </summary>
+ /// <value>The font family.</value>
+ public string FontFamily
+ {
+ get
+ {
+ return _entry.FontFamily;
+ }
+
+ set
+ {
+ if (value != _entry.FontFamily)
+ {
+ _entry.FontFamily = value;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the size of the font of the search bar's entry.
+ /// </summary>
+ /// <value>The size of the font.</value>
+ public double FontSize
+ {
+ get
+ {
+ return _entry.FontSize;
+ }
+
+ set
+ {
+ if (value != _entry.FontSize)
+ {
+ _entry.FontSize = value;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the horizontal text alignment of the search bar's entry.
+ /// </summary>
+ /// <value>The horizontal text alignment.</value>
+ public TextAlignment HorizontalTextAlignment
+ {
+ get
+ {
+ return _entry.HorizontalTextAlignment;
+ }
+
+ set
+ {
+ if (value != _entry.HorizontalTextAlignment)
+ {
+ _entry.HorizontalTextAlignment = value;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the placeholder of the search bar's entry.
+ /// </summary>
+ /// <value>The placeholder.</value>
+ public string Placeholder
+ {
+ get
+ {
+ return _entry.Placeholder;
+ }
+
+ set
+ {
+ if (value != _entry.Placeholder)
+ {
+ _entry.Placeholder = value;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the color of the placeholder.
+ /// </summary>
+ /// <value>The color of the placeholder.</value>
+ public EColor PlaceholderColor
+ {
+ get
+ {
+ return _entry.PlaceholderColor;
+ }
+
+ set
+ {
+ if (!_entry.PlaceholderColor.Equals(value))
+ {
+ _entry.PlaceholderColor = value;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the text of the search bar's entry.
+ /// </summary>
+ /// <value>The text.</value>
+ public override string Text
+ {
+ get
+ {
+ return _entry.Text;
+ }
+
+ set
+ {
+ if (value != _entry.Text)
+ {
+ _entry.Text = value;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the color of the text.
+ /// </summary>
+ /// <value>The color of the text.</value>
+ public EColor TextColor
+ {
+ get
+ {
+ return _entry.TextColor;
+ }
+
+ set
+ {
+ if (!_entry.TextColor.Equals(value))
+ {
+ _entry.TextColor = value;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Implementation of the IMeasurable.Measure() method.
+ /// </summary>
+ public ESize Measure(int availableWidth, int availableHeight)
+ {
+ ESize entrySize = _entry.Measure(availableWidth, availableHeight);
+ int width = entrySize.Width + (CancelButtonPaddingHorizontal * 2) + CancelButtonSize;
+ return new ESize(width, BackgroundHeight);
+ }
+
+ /// <summary>
+ /// Handles the event triggered by the cancel button being clicked.
+ /// </summary>
+ /// <param name="sender">Sender of the event.</param>
+ /// <param name="e">Event arguments, ignored.</param>
+ void CancelButtonClicked(object sender, EventArgs e)
+ {
+ _entry.Text = string.Empty;
+ _cancelButton.Hide();
+ }
+
+ /// <summary>
+ /// Handles the event triggered by clicking the search button on the keyboard.
+ /// </summary>
+ /// <param name="sender">Sender of the event.</param>
+ /// <param name="e">Event arguments, ignored.</param>
+ void EntryActivated(object sender, EventArgs e)
+ {
+ SearchButtonPressed?.Invoke(this, EventArgs.Empty);
+ }
+
+ /// <summary>
+ /// Handles the event triggered by entry gaining the focus.
+ /// </summary>
+ /// <param name="sender">Sender of the event.</param>
+ /// <param name="e">Event arguments, ignored.</param>
+ void EntryFocused(object sender, EventArgs e)
+ {
+ _underlineRectangle.Color = s_underlineColor;
+ }
+
+ /// <summary>
+ /// Handles the event triggered by entry's text being changed.
+ /// </summary>
+ /// <param name="sender">Sender of the event.</param>
+ /// <param name="e">Event arguments.</param>
+ void EntryTextChanged(object sender, TextChangedEventArgs e)
+ {
+ if (string.IsNullOrEmpty(e.NewTextValue))
+ {
+ _cancelButton.Hide();
+ }
+ else if (!_cancelButton.IsVisible)
+ {
+ _cancelButton.Show();
+ }
+ TextChanged?.Invoke(this, e);
+ }
+
+ /// <summary>
+ /// Handles the event triggered by entry losing the focus.
+ /// </summary>
+ /// <param name="sender">Sender of the event.</param>
+ /// <param name="e">Event arguments, ignored.</param>
+ void EntryUnfocused(object sender, EventArgs e)
+ {
+ _underlineRectangle.Color = s_underlineDimColor;
+ }
+
+ /// <summary>
+ /// Handles the event triggered by search bar's layout being changed.
+ /// </summary>
+ /// <remarks>
+ /// Updates the geometry of the widgets comprising the search bar.
+ /// </remarks>
+ /// <param name="sender">Sender of the event.</param>
+ /// <param name="e">Event arguments.</param>
+ void SearchBarLayoutUpdated(object sender, LayoutEventArgs e)
+ {
+ if (!e.HasChanged)
+ {
+ return;
+ }
+
+ _underlineRectangle.Geometry = new ERect(Geometry.Left + RectanglePaddingHorizontal,
+ Geometry.Top + EntryPaddingVertical + EntryHeight + RectanglePaddingTop,
+ Geometry.Width - (RectanglePaddingHorizontal * 2),
+ RectangleHeight);
+
+ _entry.Geometry = new ERect(Geometry.Left + EntryPaddingHorizontal,
+ Geometry.Top + EntryPaddingVertical,
+ Geometry.Width - (EntryPaddingHorizontal + (CancelButtonPaddingHorizontal * 2) + CancelButtonSize),
+ EntryHeight);
+
+ _cancelButton.Geometry = new ERect(Geometry.Right - CancelButtonSize - CancelButtonPaddingHorizontal,
+ Geometry.Top + RectanglePaddingBottom,
+ CancelButtonSize,
+ CancelButtonSize);
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/Span.cs b/Xamarin.Forms.Platform.Tizen/Native/Span.cs
new file mode 100644
index 00000000..6c479e34
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/Span.cs
@@ -0,0 +1,294 @@
+using System.Text;
+using EColor = ElmSharp.Color;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// Represent a text with attributes applied.
+ /// </summary>
+ public class Span
+ {
+ string _text;
+
+ /// <summary>
+ /// Gets or sets the formatted text.
+ /// </summary>
+ public FormattedString FormattedText { get; set; }
+
+ /// <summary>
+ /// Gets or sets the text.
+ /// </summary>
+ /// <remarks>
+ /// Setting Text to a non-null value will set the FormattedText property to null.
+ /// </remarks>
+ public string Text
+ {
+ get
+ {
+ if (FormattedText != null)
+ {
+ return FormattedText.ToString();
+ }
+ else
+ {
+ return _text;
+ }
+ }
+ set
+ {
+ if (value == null)
+ {
+ value = "";
+ }
+ else
+ {
+ FormattedText = null;
+ }
+ _text = value;
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the color for the text.
+ /// </summary>
+ public EColor ForegroundColor { get; set; }
+
+ /// <summary>
+ /// Gets or sets the background color for the text.
+ /// </summary>
+ public EColor BackgroundColor { get; set; }
+
+ /// <summary>
+ /// Gets or sets the font family for the text.
+ /// </summary>
+ public string FontFamily { get; set; }
+
+ /// <summary>
+ /// Gets or sets the font attributes for the text.
+ /// See <see cref="FontAttributes"/> for information about FontAttributes.
+ /// </summary>
+ public FontAttributes FontAttributes { get; set; }
+
+ /// <summary>
+ /// Gets or sets the font size for the text.
+ /// </summary>
+ public double FontSize { get; set; }
+
+ /// <summary>
+ /// Gets or sets the line break mode for the text.
+ /// See <see cref="LineBreakMode"/> for information about LineBreakMode.
+ /// </summary>
+ public LineBreakMode LineBreakMode { get; set; }
+
+ /// <summary>
+ /// Gets or sets the horizontal alignment mode for the text.
+ /// See <see cref="TextAlignment"/> for information about TextAlignment.
+ /// </summary>
+ public TextAlignment HorizontalTextAlignment { get; set; }
+
+ /// <summary>
+ /// Gets or sets the vertical alignment mode for the text.
+ /// See <see cref="TextAlignment"/> for information about TextAlignment.
+ /// </summary>
+ public TextAlignment VerticalTextAlignment { get; set; }
+
+ /// <summary>
+ /// Gets or sets the value that indicates whether the text has underline.
+ /// </summary>
+ public bool Underline { get; set; }
+
+ /// <summary>
+ /// Gets or sets the value that indicates whether the text has strike line though it.
+ /// </summary>
+ public bool Strikethrough { get; set; }
+
+ /// <summary>
+ /// Create a new Span instance with default attributes.
+ /// </summary>
+ public Span()
+ {
+ Text = "";
+ FontFamily = "";
+ FontSize = -1;
+ FontAttributes = FontAttributes.None;
+ ForegroundColor = EColor.White;
+ BackgroundColor = EColor.Transparent;
+ HorizontalTextAlignment = TextAlignment.Auto;
+ VerticalTextAlignment = TextAlignment.Auto;
+ LineBreakMode = LineBreakMode.MixedWrap;
+ Underline = false;
+ Strikethrough = false;
+ }
+
+ /// <summary>
+ /// This method return marked up text
+ /// </summary>
+ internal string GetMarkupText()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.AppendFormat("<span ");
+
+ sb = PrepareFormattingString(sb);
+
+ sb.Append(">");
+
+ sb.Append(GetDecoratedText());
+
+ sb.Append("</>");
+
+ return sb.ToString();
+ }
+
+ /// <summary>
+ /// This method return text decorated with markup if FormattedText is set or plain text otherwise.
+ /// </summary>
+ internal string GetDecoratedText()
+ {
+ if (FormattedText != null)
+ {
+ return FormattedText.ToMarkupString();
+ }
+ else
+ {
+ return ConvertTags(Text);
+ }
+ }
+
+ StringBuilder PrepareFormattingString(StringBuilder _formattingString)
+ {
+ var foregroundColor = ForegroundColor.ToHex();
+
+ _formattingString.AppendFormat("color={0} ", foregroundColor);
+
+ _formattingString.AppendFormat("backing_color={0} ", BackgroundColor.ToHex());
+ _formattingString.Append("backing=on ");
+
+ if (!string.IsNullOrEmpty(FontFamily))
+ {
+ _formattingString.AppendFormat("font={0} ", FontFamily);
+ }
+
+ if (FontSize != -1)
+ {
+ _formattingString.AppendFormat("font_size={0} ", FontSize);
+ }
+
+ if ((FontAttributes & FontAttributes.Bold) != 0)
+ {
+ _formattingString.Append("font_weight=Bold ");
+ }
+ if ((FontAttributes & FontAttributes.Italic) != 0)
+ {
+ _formattingString.Append("font_style=italic ");
+ }
+
+ if (Underline)
+ {
+ _formattingString.AppendFormat("underline=on underline_color={0} ", foregroundColor);
+ }
+
+ if (Strikethrough)
+ {
+ _formattingString.AppendFormat("strikethrough=on strikethrough_color={0} ", foregroundColor);
+ }
+
+ switch (HorizontalTextAlignment)
+ {
+ case TextAlignment.Auto:
+ _formattingString.Append("align=auto ");
+ break;
+
+ case TextAlignment.Start:
+ _formattingString.Append("align=left ");
+ break;
+
+ case TextAlignment.End:
+ _formattingString.Append("align=right ");
+ break;
+
+ case TextAlignment.Center:
+ _formattingString.Append("align=center ");
+ break;
+ }
+
+ switch (VerticalTextAlignment)
+ {
+ case TextAlignment.Auto:
+ case TextAlignment.Start:
+ _formattingString.Append("valign=top ");
+ break;
+
+ case TextAlignment.End:
+ _formattingString.Append("valign=bottom ");
+ break;
+
+ case TextAlignment.Center:
+ _formattingString.Append("valign=middle ");
+ break;
+ }
+
+ switch (LineBreakMode)
+ {
+ case LineBreakMode.NoWrap:
+ _formattingString.Append("wrap=none");
+ break;
+
+ case LineBreakMode.CharacterWrap:
+ _formattingString.Append("wrap=char");
+ break;
+
+ case LineBreakMode.WordWrap:
+ _formattingString.Append("wrap=word");
+ break;
+
+ case LineBreakMode.MixedWrap:
+ _formattingString.Append("wrap=mixed");
+ break;
+
+ case LineBreakMode.HeadTruncation:
+ _formattingString.Append("ellipsis=0.0");
+ break;
+
+ case LineBreakMode.MiddleTruncation:
+ _formattingString.Append("ellipsis=0.5");
+ break;
+
+ case LineBreakMode.TailTruncation:
+ _formattingString.Append("ellipsis=1.0");
+ break;
+ }
+
+ return _formattingString;
+ }
+
+ string ConvertTags(string text)
+ {
+ return text.Replace("<", "&lt;")
+ .Replace(">", "&gt;")
+ .Replace("\n", "<br>");
+ }
+
+ internal string GetStyle()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.Append("DEFAULT='");
+
+ PrepareFormattingString(sb);
+
+ sb.Append("'");
+
+ return sb.ToString();
+ }
+
+ /// <summary>
+ /// Converts string value to Span.
+ /// </summary>
+ /// <param name="text">The string text</param>
+ public static implicit operator Span(string text)
+ {
+ return new Span { Text = text };
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/TableView.cs b/Xamarin.Forms.Platform.Tizen/Native/TableView.cs
new file mode 100644
index 00000000..73388a84
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/TableView.cs
@@ -0,0 +1,67 @@
+using ElmSharp;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// Extends the ListView class to provide TableView class implementation.
+ /// </summary>
+ public class TableView : ListView
+ {
+
+ static readonly SectionCellRenderer _sectionCellRenderer = new SectionCellRenderer();
+ /// <summary>
+ /// Initializes a new instance of the TableView class.
+ /// </summary>
+ public TableView(EvasObject parent)
+ : base(parent) {
+ }
+
+ /// <summary>
+ /// Sets the root of the table.
+ /// </summary>
+ /// <param name="root">TableRoot, which is parent to one or more TableSections.</param>
+ public void ApplyTableRoot(TableRoot root)
+ {
+ Clear();
+ foreach (TableSection ts in root)
+ {
+ AddSectionTitle(ts.Title);
+ AddSource(ts);
+ }
+ }
+
+ protected override CellRenderer GetCellRenderer(Cell cell, bool isGroup = false)
+ {
+ if (cell.GetType() == typeof(SectionCell))
+ {
+ return _sectionCellRenderer;
+ }
+ return base.GetCellRenderer(cell, isGroup);
+ }
+
+ /// <summary>
+ /// Sets the section title.
+ /// </summary>
+ void AddSectionTitle(string title)
+ {
+ Cell cell = new SectionCell()
+ {
+ Text = title
+ };
+ AddCell(cell);
+ }
+
+ internal class SectionCellRenderer : GroupCellTextRenderer
+ {
+ public SectionCellRenderer()
+ {
+ DetailPart = "null";
+ }
+ protected SectionCellRenderer(string style) : base(style) { }
+ }
+ class SectionCell : TextCell
+ {
+ }
+ }
+}
+
diff --git a/Xamarin.Forms.Platform.Tizen/Native/TextAlignment.cs b/Xamarin.Forms.Platform.Tizen/Native/TextAlignment.cs
new file mode 100644
index 00000000..c7e934bf
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/TextAlignment.cs
@@ -0,0 +1,25 @@
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// Enumerates values that describe alignemnt of text.
+ /// </summary>
+ public enum TextAlignment
+ {
+ /// <summary>
+ /// Aligns horizontal text according to language. Top aligned for vertical text.
+ /// </summary>
+ Auto,
+ /// <summary>
+ /// Left and top aligned for horizontal and vertical text, respectively.
+ /// </summary>
+ Start,
+ /// <summary>
+ /// Right and bottom aligned for horizontal and vertical text, respectively.
+ /// </summary>
+ End,
+ /// <summary>
+ /// Center-aligned text.
+ /// </summary>
+ Center,
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/TextHelper.cs b/Xamarin.Forms.Platform.Tizen/Native/TextHelper.cs
new file mode 100644
index 00000000..5109dad4
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/TextHelper.cs
@@ -0,0 +1,54 @@
+using System;
+using ElmSharp;
+using ESize = ElmSharp.Size;
+using ELayout = ElmSharp.Layout;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// The Text Helper contains functions that assist in working with text-able objects.
+ /// </summary>
+ internal static class TextHelper
+ {
+ /// <summary>
+ /// Gets the size of raw text block.
+ /// </summary>
+ /// <param name="textable">The <see cref="EvasObject"/> with text part.</param>
+ /// <returns>Returns the size of raw text block.</returns>
+ public static ESize GetRawTextBlockSize(EvasObject textable)
+ {
+ return GetElmTextPart(textable).TextBlockNativeSize;
+ }
+
+ /// <summary>
+ /// Gets the size of formatted text block.
+ /// </summary>
+ /// <param name="textable">The <see cref="ElmSharp.EvasObject"/> with text part.</param>
+ /// <returns>Returns the size of formatted text block.</returns>
+ public static ESize GetFormattedTextBlockSize(EvasObject textable)
+ {
+ return GetElmTextPart(textable).TextBlockFormattedSize;
+ }
+
+ /// <summary>
+ /// Gets the ELM text part of evas object.
+ /// </summary>
+ /// <param name="textable">The <see cref="ElmSharp.EvasObject"/> with text part.</param>
+ /// <exception cref="ArgumentException">Throws exception when parameter <param name="textable"> isn't text-able object or doesn't have ELM text part.</exception>
+ /// <returns>Requested <see cref="ElmSharp.EdjeTextPartObject"/> instance.</returns>
+ static EdjeTextPartObject GetElmTextPart(EvasObject textable)
+ {
+ ELayout widget = textable as ELayout;
+ if (widget == null)
+ {
+ throw new ArgumentException("textable should be ElmSharp.Layout", "textable");
+ }
+ EdjeTextPartObject textPart = widget.EdjeObject["elm.text"];
+ if (textPart == null)
+ {
+ throw new ArgumentException("There is no elm.text part", "textable");
+ }
+ return textPart;
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/TimePicker.cs b/Xamarin.Forms.Platform.Tizen/Native/TimePicker.cs
new file mode 100644
index 00000000..6ee49ce3
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/TimePicker.cs
@@ -0,0 +1,185 @@
+using System;
+using System.Linq;
+using System.Text.RegularExpressions;
+using ElmSharp;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ /// <summary>
+ /// Extends the ElmSharp.DateTimeSelector class with functionality useful to renderer.
+ /// </summary>
+ public class TimePicker : DateTimeSelector
+ {
+ const string DefaultEFLFormat = "%I:%M %p";
+ //TODO need to add internationalization support
+ const string FormatExceptionMessage = "Input string was not in a correct format.";
+ const string RegexValidTimePattern = "^([h]{1,2}|[H]{1,2})[.:-]([m]{1,2})(([.:-][s]{1,2})?)(([.:-][fF]{1,7})?)(([K])?)(([z]{1,3})?)(([ ][t]{1,2})?)$";
+ const string TimeLayoutStyle = "time_layout";
+ string _dateTimeFormat;
+ TimeSpan _time;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TimePicker"/> class.
+ /// </summary>
+ /// <param name="parent">The parent EvasObject.</param>
+ public TimePicker(EvasObject parent) : base(parent)
+ {
+ Style = TimeLayoutStyle;
+ ApplyTime(Time);
+ ApplyFormat(DateTimeFormat);
+
+ DateTimeChanged += (sender, e) =>
+ {
+ Time = e.NewDate.TimeOfDay;
+ };
+ }
+
+ /// <summary>
+ /// Gets or sets the displayed date time format.
+ /// </summary>
+ public string DateTimeFormat
+ {
+ get
+ {
+ return _dateTimeFormat;
+ }
+ set
+ {
+ if (_dateTimeFormat != value)
+ {
+ ApplyFormat(value);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the displayed time.
+ /// </summary>
+ public TimeSpan Time
+ {
+ get
+ {
+ return _time;
+ }
+ set
+ {
+ if (_time != value)
+ {
+ ApplyTime(value);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Sets the <c>Format</c> property according to the given <paramref name="format"/>.
+ /// </summary>
+ /// <param name="format">The format value to be applied to the time picker.</param>
+ void ApplyFormat(string format)
+ {
+ _dateTimeFormat = format;
+ Format = ConvertToEFLFormat(_dateTimeFormat);
+ }
+
+ /// <summary>
+ /// Sets the <c>DateTime</c> property according to the given <paramref name="time"/>.
+ /// </summary>
+ /// <param name="time">The time value to be applied to the time picker.</param>
+ void ApplyTime(TimeSpan time)
+ {
+ _time = time;
+ DateTime = ConvertToDateTime(time);
+ }
+
+ /// <summary>
+ /// Converts parameter <paramref name="timeSpan"/> to <see cref="DateTime"/>.
+ /// </summary>
+ /// <param name="timeSpan">The time value to be converted to <see cref="DateTime"/>.</param>
+ /// <returns>An object representing the date 1st Jan, 1970 (minimum date of ElmSharp.DateTimeSelector) with added <paramref name="timeSpan"/>.</returns>
+ DateTime ConvertToDateTime(TimeSpan timeSpan)
+ {
+ return new DateTime(1970, 1, 1) + timeSpan;
+ }
+
+ /// <summary>
+ /// Converts standard or custom <see cref="DateTime"/> format to EFL format.
+ /// </summary>
+ /// <param name="dateTimeFormat">The <see cref="DateTime"/> format to be converted to EFL format.</param>
+ /// <exception cref="FormatException"><param name="dateTimeFormat"> does not contain a valid string representation of a date and time.</exception>
+ /// <returns>An object representing the EFL time format string.
+ /// Example:
+ /// "t" or "T" returns default EFL format "%I:%M %p"
+ /// "HH:mm tt" returns "%H:%M %p"
+ /// "h:mm" returns "%l:%M"
+ /// </returns>
+ string ConvertToEFLFormat(string dateTimeFormat)
+ {
+ if (string.IsNullOrWhiteSpace(dateTimeFormat))
+ {
+ return DefaultEFLFormat;
+ }
+
+ if (dateTimeFormat.Length == 1)
+ {
+ //Standard Time Format (DateTime)
+ if (dateTimeFormat[0] == 't' || dateTimeFormat[0] == 'T')
+ {
+ return DefaultEFLFormat;
+ }
+ else
+ {
+ throw new FormatException(FormatExceptionMessage);
+ }
+ }
+ else
+ {
+ //Custom Time Format (DateTime)
+ Regex regex = new Regex(RegexValidTimePattern);
+ if (!regex.IsMatch(dateTimeFormat))
+ {
+ throw new FormatException(FormatExceptionMessage);
+ }
+
+ string format = string.Empty;
+ int count_h = dateTimeFormat.Count(m => m == 'h'); //12h
+ int count_H = dateTimeFormat.Count(m => m == 'H'); //24h
+
+ if (count_h == 1)
+ {
+ format += "%l";
+ }
+ else if (count_h == 2)
+ {
+ format += "%I";
+ }
+ else if (count_H == 1)
+ {
+ format += "%k";
+ }
+ else if (count_H == 2)
+ {
+ format += "%H";
+ }
+
+ format += ":%M";
+ int count_t = dateTimeFormat.Count(m => m == 't');
+
+ if ((count_H > 0 && count_t > 0) ||
+ (count_h > 0 && count_t == 0))
+ {
+ throw new FormatException(FormatExceptionMessage);
+ }
+
+ if (count_t == 1)
+ {
+ format += " %P";
+ }
+ else if (count_t == 2)
+ {
+ format += " %p";
+ }
+
+ return format;
+ }
+ }
+ }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Native/Window.cs b/Xamarin.Forms.Platform.Tizen/Native/Window.cs
new file mode 100644
index 00000000..87a54cd3
--- /dev/null
+++ b/Xamarin.Forms.Platform.Tizen/Native/Window.cs
@@ -0,0 +1,138 @@
+using System;
+using ElmSharp;
+using EWindow = ElmSharp.Window;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ public class Window : EWindow
+ {
+ Conformant _conformant;
+
+ /// <summary>
+ /// Initializes a new instance of the Window class.
+ /// </summary>
+ public Window() : base("FormsWindow")
+ {
+ Initialize();
+ }
+
+ /// <summary>
+ /// Notifies that the window has been closed.
+ /// </summary>
+ public event EventHandler Closed;
+
+ /// <summary>
+ /// Notifies that the back button has been pressed.
+ /// </summary>
+ public event EventHandler BackButtonPressed;
+
+ /// <summary>
+ /// Gets the current orientation.
+ /// </summary>
+ public DisplayOrientations CurrentOrientation
+ {
+ get
+ {
+ if (IsRotationSupported)
+ {
+ return GetDisplayOrientation();
+ }
+ else
+ {
+ return DisplayOrientations.None;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the orientation of a rectangular screen.
+ /// </summary>
+ public DisplayOrientations AvailableOrientations
+ {
+ get
+ {
+ if (IsRotationSupported)
+ {
+ return (DisplayOrientations)AvailableRotations;
+ }
+ else
+ {
+ return DisplayOrientations.None;
+ }
+ }
+ set
+ {
+ if (IsRotationSupported)
+ {
+ AvailableRotations = (DisplayRotation)value;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Sets the main page of Window.
+ /// </summary>
+ /// <param name="content">ElmSharp.EvasObject type page to be set.</param>
+ public void SetMainPage(EvasObject content)
+ {
+ _conformant.SetContent(content);
+ }
+
+ void Initialize()
+ {
+ // size
+ var size = ScreenSize;
+ Resize(size.Width, size.Height);
+
+ // events
+ Deleted += (sender, e) =>
+ {
+ Closed?.Invoke(this, EventArgs.Empty);
+ };
+ CloseRequested += (sender, e) =>
+ {
+ Unrealize();
+ };
+
+ KeyGrab(EvasKeyEventArgs.PlatformBackButtonName, false);
+ KeyUp += (s, e) =>
+ {
+ if (e.KeyName == EvasKeyEventArgs.PlatformBackButtonName)
+ {
+ BackButtonPressed?.Invoke(this, EventArgs.Empty);
+ }
+ };
+
+ Active();
+ AutoDeletion = false;
+ Show();
+
+ _conformant = new Conformant(this);
+ _conformant.SetAlignment(-1.0, -1.0); // fill
+ _conformant.SetWeight(1.0, 1.0); // expand
+ _conformant.Show();
+
+ AvailableOrientations = DisplayOrientations.Portrait | DisplayOrientations.Landscape | DisplayOrientations.PortraitFlipped | DisplayOrientations.LandscapeFlipped;
+ }
+ DisplayOrientations GetDisplayOrientation()
+ {
+ switch (Rotation)
+ {
+ case 0:
+ return Native.DisplayOrientations.Portrait;
+
+ case 90:
+ return Native.DisplayOrientations.Landscape;
+
+ case 180:
+ return Native.DisplayOrientations.PortraitFlipped;
+
+ case 270:
+ return Native.DisplayOrientations.LandscapeFlipped;
+
+ default:
+ return Native.DisplayOrientations.None;
+ }
+ }
+ }
+}