[NUI] Introduce NUI TimePicker
authorWoochanlee <wc0917.lee@samsung.com>
Wed, 31 Mar 2021 12:11:00 +0000 (21:11 +0900)
committerhuiyueun <35286162+huiyueun@users.noreply.github.com>
Tue, 20 Apr 2021 06:13:00 +0000 (15:13 +0900)
src/Tizen.NUI.Components/Controls/TimePicker.cs [new file with mode: 0755]
src/Tizen.NUI.Components/Style/TimePickerStyle.cs [new file with mode: 0755]
src/Tizen.NUI.Components/Theme/DefaultTheme.cs
src/Tizen.NUI.Components/Theme/DefaultThemeCommon.cs
test/Tizen.NUI.Samples/Tizen.NUI.Samples/Samples/TimePickerSample.cs [new file with mode: 0755]

diff --git a/src/Tizen.NUI.Components/Controls/TimePicker.cs b/src/Tizen.NUI.Components/Controls/TimePicker.cs
new file mode 100755 (executable)
index 0000000..38a6991
--- /dev/null
@@ -0,0 +1,400 @@
+/* Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+using System;
+using Tizen.NUI;
+using Tizen.NUI.BaseComponents;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
+
+namespace Tizen.NUI.Components
+{
+    /// <summary>
+    /// TimeChangedEventArgs is a class to notify changed TimePicker value argument which will sent to user.
+    /// </summary>
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public class TimeChangedEventArgs : EventArgs
+    {
+        /// <summary>
+        /// TimeChangedEventArgs default constructor.
+        /// <param name="hour">hour value of TimePicker.</param>
+        /// <param name="minute">minute value of TimePicker.</param>
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]   
+        public TimeChangedEventArgs(int hour, int minute)
+        {
+            Hour = hour;
+            Minute = minute;
+        }
+
+        /// <summary>
+        /// TimeChangedEventArgs default constructor.
+        /// <returns>The current hour value of TimePicker.</returns>
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]   
+        public int Hour { get; }
+
+        /// <summary>
+        /// TimeChangedEventArgs default constructor.
+        /// <returns>The current minute value of TimePicker.</returns>
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]   
+        public int Minute { get; }
+    }
+
+    /// <summary>
+    /// TimePicker is a class which provides a function that allows the user to select 
+    /// a time through a scrolling motion by expressing the specified value as a list.
+    /// TimePicker expresses the current time using the locale information of the system.
+    /// </summary>
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public class TimePicker : Control
+    {
+        private bool isAm;
+        private bool is24HourView;
+        private int hour;
+        private int minute;
+        private String[] ampmText;
+        private Picker hourPicker;
+        private Picker minutePicker;
+        private Picker ampmPicker;
+        private TimePickerStyle timePickerStyle => ViewStyle as TimePickerStyle;
+
+        /// <summary>
+        /// Creates a new instance of TimePicker.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public TimePicker()
+        {
+            Initialize();
+        }
+
+        /// <summary>
+        /// Creates a new instance of TimePicker.
+        /// </summary>
+        /// <param name="style">Creates TimePicker by special style defined in UX.</param>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public TimePicker(string style) : base(style)
+        {
+            Initialize();
+        }
+
+        /// <summary>
+        /// Creates a new instance of TimePicker.
+        /// </summary>
+        /// <param name="timePickerStyle">Creates TimePicker by style customized by user.</param>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public TimePicker(TimePickerStyle timePickerStyle) : base(timePickerStyle)
+        {
+            Initialize();
+        }
+
+        /// <summary>
+        /// Dispose TimePicker and all children on it.
+        /// </summary>
+        /// <param name="type">Dispose type.</param>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        protected override void Dispose(DisposeTypes type)
+        {
+            if (disposed)
+            {
+                return;
+            }
+
+            if (type == DisposeTypes.Explicit)
+            {
+                Remove(hourPicker);
+                Utility.Dispose(hourPicker);
+                hourPicker = null;
+                Remove(minutePicker);
+                Utility.Dispose(minutePicker);
+                minutePicker = null;
+                Remove(ampmPicker);
+                Utility.Dispose(ampmPicker);
+                ampmPicker = null;
+            }
+
+            base.Dispose(type);
+        }
+
+        /// <summary>
+        /// An event emitted when TimePicker value changed, user can subscribe or unsubscribe to this event handler.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public event EventHandler<TimeChangedEventArgs> TimeChanged;
+
+        /// <summary>
+        /// The hour value of TimePicker.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public int Hour
+        {
+            get
+            {
+                return hour;
+            }
+            set
+            {
+                if (value < 1 || value > 24 || value == hour) return;
+
+                hour = value;
+                if (!is24HourView)
+                {
+                    if (hour >= 12 && hour <= 23) 
+                    {
+                        isAm = false;
+                        if (hour == 12) hourPicker.CurrentValue = hour;
+                        else hourPicker.CurrentValue = hour -= 12;
+                        ampmPicker.CurrentValue = 2;
+                    }
+                    else 
+                    {
+                        isAm = true;
+                        hourPicker.CurrentValue = hour;
+                        ampmPicker.CurrentValue = 1;
+                    }
+                }
+                else hourPicker.CurrentValue = hour;
+            }
+        }
+
+        /// <summary>
+        /// The Minute value of TimePicker.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public int Minute
+        {
+            get
+            {
+                return minute;
+            }
+            set
+            {
+                if (value < 1 || value > 60 || value == minute) return;
+
+                minute = value;
+                minutePicker.CurrentValue = minute;
+            }
+        }
+
+        /// <summary>
+        /// The is24hourview value of TimePicker.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public bool Is24HourView
+        {
+            get
+            {
+                return is24HourView;
+            }
+            set
+            {
+                if (is24HourView == value) return;
+
+                is24HourView = value;
+                if (value == true)
+                {
+                    Remove(ampmPicker);
+                    hourPicker.MaxValue = 24;
+                }
+                else 
+                {
+                    hourPicker.MaxValue = 12;
+                    PickersOrderSet(true);
+                    SetAmpmText();
+                }
+            }
+        }
+
+        /// <summary>
+        /// Initialize TimePicker object.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public override void OnInitialize()
+        {
+            base.OnInitialize();
+
+            hourPicker = new Picker()
+            {
+                MinValue = 1,
+                MaxValue = 24,
+            };
+            hourPicker.ValueChanged += OnHourValueChanged;
+
+            minutePicker = new Picker()
+            {
+                MinValue = 0,
+                MaxValue = 59,
+            };
+            minutePicker.ValueChanged += OnMinuteValueChanged;
+
+            ampmPicker = new Picker()
+            {
+                MinValue = 1,
+                MaxValue = 2,
+            };
+            ampmPicker.ValueChanged += OnAmpmValueChanged;
+        }
+
+        /// <summary>
+        /// Applies style to TimePicker.
+        /// </summary>
+        /// <param name="viewStyle">The style to apply.</param>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public override void ApplyStyle(ViewStyle viewStyle)
+        {
+            base.ApplyStyle(viewStyle);
+
+            //Apply CellPadding.
+            if (timePickerStyle?.CellPadding != null && Layout != null)
+                ((LinearLayout)Layout).CellPadding = new Size2D(timePickerStyle.CellPadding.Width, timePickerStyle.CellPadding.Height);
+            
+            //Apply Internal Pickers style.
+            if (timePickerStyle?.Pickers != null && hourPicker != null && minutePicker != null && ampmPicker != null)
+            {
+                hourPicker.ApplyStyle(timePickerStyle.Pickers);
+                minutePicker.ApplyStyle(timePickerStyle.Pickers);
+                ampmPicker.ApplyStyle(timePickerStyle.Pickers);
+            }
+        }
+                
+        [SuppressMessage("Microsoft.Reliability",
+                         "CA2000:DisposeObjectsBeforeLosingScope",
+                         Justification = "The CellPadding will be dispose when the time picker disposed")]
+        private void Initialize()
+        {
+            HeightSpecification = LayoutParamPolicies.MatchParent;
+
+            Layout = new LinearLayout() { 
+                LinearOrientation = LinearLayout.Orientation.Horizontal,
+                CellPadding = new Size(timePickerStyle.CellPadding.Width, timePickerStyle.CellPadding.Height),
+            };
+
+            is24HourView = true;
+
+            PickersOrderSet(false);
+
+            if (!is24HourView) 
+            {
+                SetAmpmText();
+                hourPicker.MaxValue = 12;
+            }
+        }
+
+        private void OnHourValueChanged(object sender, ValueChangedEventArgs e)
+        {
+            if (hour == e.Value) return;
+
+            if (!is24HourView)
+            {
+                if (isAm) 
+                {
+                    if (e.Value == 12) hour = 24;
+                    else hour = e.Value;
+                }
+                else 
+                {
+                    if (e.Value == 12) hour = 12;
+                    else hour = e.Value + 12;
+                }
+            }
+            else
+                hour = e.Value;
+            
+            OnTimeChanged();
+        }
+
+        private void OnMinuteValueChanged(object sender, ValueChangedEventArgs e)
+        { 
+            if (minute == e.Value) return;
+
+            minute = e.Value;
+
+            OnTimeChanged();
+        }
+
+        private void OnAmpmValueChanged(object sender, ValueChangedEventArgs e)
+        { 
+            if ((isAm && e.Value == 1) || (!isAm && e.Value == 2)) return;
+
+            if (e.Value == 1)
+            { //AM
+                if (hour >= 12 || hour < 24)
+                { 
+                    if (hour == 12) hour += 12;
+                    else hour -= 12;
+                }
+                isAm = true;
+            }
+            else 
+            { //PM
+                if (hour == 24 || hour < 12) 
+                {
+                     if (hour == 24) hour -= 12;
+                     else hour += 12; 
+                }
+                isAm = false;
+            }
+
+            OnTimeChanged();
+        }
+
+        private void OnTimeChanged()
+        { 
+            TimeChangedEventArgs eventArgs = new TimeChangedEventArgs(hour, minute);
+            TimeChanged?.Invoke(this, eventArgs);
+        }
+
+        private void PickersOrderSet(bool ampmForceSet)
+        {
+            //FIXME: Check the pickers located in already proper position or not.
+            Remove(hourPicker);
+            Remove(minutePicker);
+            Remove(ampmPicker);
+
+            //Get current system locale's time pattern
+            String locale = Environment.GetEnvironmentVariable("LC_TIME");
+            DateTimeFormatInfo timeFormatInfo = new CultureInfo(locale, false ).DateTimeFormat;
+            String timePattern = timeFormatInfo.ShortTimePattern;
+            String[] timePatternArray = timePattern.Split(' ', ':');
+
+            foreach (String format in timePatternArray) {
+                if (format.IndexOf("H") != -1|| format.IndexOf("h") != -1)  Add(hourPicker);
+                else if (format.IndexOf("M") != -1 || format.IndexOf("m") != -1) Add(minutePicker);
+                else if (format.IndexOf("t") != -1) 
+                {
+                    is24HourView = false;
+                    ampmForceSet = false;
+                    Add(ampmPicker);
+                }
+            }
+
+            if (ampmForceSet) Add(ampmPicker);
+        }
+
+        private void SetAmpmText()
+        {
+            //FIXME: There is no localeChanged Event for Component now
+            //       AMPM text has to update when system locale changed.
+            String locale = Environment.GetEnvironmentVariable("LC_TIME");
+            CultureInfo info = new CultureInfo(locale);
+            ampmText = new string[] {info.DateTimeFormat.AMDesignator, info.DateTimeFormat.PMDesignator};
+            ampmPicker.DisplayedValues = new ReadOnlyCollection<string>(ampmText);
+        }
+    }
+}
diff --git a/src/Tizen.NUI.Components/Style/TimePickerStyle.cs b/src/Tizen.NUI.Components/Style/TimePickerStyle.cs
new file mode 100755 (executable)
index 0000000..d6151ca
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright(c) 2021 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+using System.ComponentModel;
+using Tizen.NUI.BaseComponents;
+using Tizen.NUI.Binding;
+
+namespace Tizen.NUI.Components
+{
+    /// <summary>
+    /// TimePickerStyle is a class which saves TimePickerStyle's ux data.
+    /// </summary>
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public class TimePickerStyle : ControlStyle
+    {
+        /// <summary>
+        /// Creates a new instance of a TimePickerStyle.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public TimePickerStyle() : base()
+        {
+        }
+
+        /// <summary>
+        /// Creates a new instance of a TimePickerStyle with style.
+        /// </summary>
+        /// <param name="style">Creates TimePickerStyle by style customized by user.</param>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public TimePickerStyle(TimePickerStyle style) : base(style)
+        {
+        }
+
+        /// <summary>
+        /// Gets or sets the TimePickerStyle internal pickers style.
+        /// </summary>
+        public PickerStyle Pickers { get; set;} = new PickerStyle();
+
+        /// <summary>
+        /// Gets or sets the TimePickerStyle internal pickers padding.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public Size2D CellPadding { get; set; } = new Size2D();
+
+        /// <summary>
+        /// Style's clone function.
+        /// </summary>
+        /// <param name="bindableObject">The style that needs to copy.</param>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public override void CopyFrom(BindableObject bindableObject)
+        {
+            base.CopyFrom(bindableObject);
+
+            if (bindableObject is TimePickerStyle timePickerStyle)
+            {
+                Pickers.CopyFrom(timePickerStyle.Pickers);
+                CellPadding = (timePickerStyle.CellPadding == null) ?
+                               new Size2D() : new Size2D(timePickerStyle.CellPadding.Width, timePickerStyle.CellPadding.Height);
+            }
+        }
+    }
+}
index 3ec7e9a..71a108b 100755 (executable)
@@ -148,6 +148,20 @@ namespace Tizen.NUI.Components
                     .Add<VerticalAlignment?>("/MessageTextLabel/VerticalAlignment", (ViewStyle style, VerticalAlignment? value) => ((AlertDialogStyle)style).MessageTextLabel.VerticalAlignment = value)
                     .AddSelector<Color>("/MessageTextLabel/TextColor", (ViewStyle style, Selector<Color> value) => ((AlertDialogStyle)style).MessageTextLabel.TextColor = value)
                     .Add<Size>("/ActionContent/Size", (ViewStyle style, Size value) => ((AlertDialogStyle)style).ActionContent.Size = value),
+                
+                // TimePicker
+                (new ExternalThemeKeyList(typeof(TimePicker), typeof(TimePickerStyle)))
+                    .Add<Size>("/CellPadding", (ViewStyle style, Size value) => ((TimePickerStyle)style).Size = value)
+                    .Add<Size>("/Pickers/Size", (ViewStyle style, Size value) => ((TimePickerStyle)style).Pickers.Size = value)
+                    .AddSelector<float?>("/Pickers/ItemTextLabel/PixelSize", (ViewStyle style, Selector<float?> value) => ((TimePickerStyle)style).Pickers.ItemTextLabel.PixelSize = value, ControlState.Selected)
+                    .Add<Size>("/Pickers/ItemTextLabel/Size", (ViewStyle style, Size value) => ((TimePickerStyle)style).Pickers.ItemTextLabel.Size = value)
+                    .AddSelector<Color>("/Pickers/ItemTextLabel/TextColor", (ViewStyle style, Selector<Color> value) => ((TimePickerStyle)style).Pickers.ItemTextLabel.TextColor = value, ControlState.Selected)
+                    .AddSelector("/Pickers/ItemTextLabel/BackgroundColor", (ViewStyle style, Selector<Color> value) => ((TimePickerStyle)style).Pickers.ItemTextLabel.BackgroundColor = value, ControlState.Selected)
+                    .Add<float?>("/Pickers/Divider/SizeHeight", (ViewStyle style, float? value) => ((TimePickerStyle)style).Pickers.Divider.SizeHeight = value)
+                    .Add<Position>("/Pickers/Divider/Position", (ViewStyle style, Position value) => ((TimePickerStyle)style).Pickers.Divider.Position = value)
+                    .AddSelector("/Pickers/Divider/BackgroundColor", (ViewStyle style, Selector<Color> value) => ((TimePickerStyle)style).Pickers.Divider.BackgroundColor = value, ControlState.Selected)
+                    .Add<Size>("/Pickers/StartScrollOffset", (ViewStyle style, Size value) => ((TimePickerStyle)style).Pickers.StartScrollOffset = value),
+
             };
 
             return actionSet;
index 3039d08..ca7c016 100755 (executable)
@@ -539,6 +539,37 @@ namespace Tizen.NUI.Components
                 },
             });
 
+            theme.AddStyleWithoutClone("Tizen.NUI.Components.TimePicker", new TimePickerStyle()
+            {
+                CellPadding = new Size(50, 339),
+
+                Pickers = new PickerStyle()
+                {
+                    Size = new Size(160, 339),
+                    ItemTextLabel = new TextLabelStyle()
+                    {
+                        //FIXME: Should be check PointSize. given size from UX is too large.
+                        PixelSize = 32,
+                        VerticalAlignment = VerticalAlignment.Center,
+                        HorizontalAlignment = HorizontalAlignment.Center,
+                        Size = new Size(0,72),
+                        TextColor = new Selector<Color>()
+                        {
+                            Normal = new Color("#000C2BFF"),
+                        },
+                        BackgroundColor = Color.White,
+                    },
+                    Divider = new ViewStyle()
+                    {
+                        SizeHeight = 2.0f,
+                        WidthResizePolicy = ResizePolicyType.FillToParent,
+                        Position = new Position(0, 132),
+                        BackgroundColor = new Color("#0A0E4AFF"),
+                    },
+                    StartScrollOffset = new Size2D(0, 12),
+                }
+            });
+
             return theme;
         }
     }
diff --git a/test/Tizen.NUI.Samples/Tizen.NUI.Samples/Samples/TimePickerSample.cs b/test/Tizen.NUI.Samples/Tizen.NUI.Samples/Samples/TimePickerSample.cs
new file mode 100755 (executable)
index 0000000..4ae8761
--- /dev/null
@@ -0,0 +1,46 @@
+using System;
+using System.Collections.Generic;
+using Tizen.NUI;
+using Tizen.NUI.BaseComponents;
+using Tizen.NUI.Components;
+
+namespace Tizen.NUI.Samples
+{
+    //Please expand Window size, When it runs on Ubuntu.
+    public class TimePickerSample : IExample
+    {
+        private static int pickerWidth = 580;
+        private static int pickerHeight = 339;
+        private Window window;
+        private TimePicker timePicker;
+
+        private void TimeChanged(object sender, TimeChangedEventArgs e)
+        {
+            Console.WriteLine(" Time " + e.Hour + " " + e.Minute);
+        }
+
+        public void Activate()
+        {
+            window = NUIApplication.GetDefaultWindow();
+            window.BackgroundColor = Color.White;
+
+            timePicker = new TimePicker()
+            {
+                Size = new Size(pickerWidth, pickerHeight),
+                Position = new Position(Window.Instance.Size.Width / 2 - pickerWidth / 2, Window.Instance.Size.Height/ 2 - pickerHeight / 2),
+                Hour = 12,
+                Minute = 30,
+                Is24HourView = false,
+            };
+            timePicker.TimeChanged += TimeChanged;
+            window.Add(timePicker);
+        }
+        
+        public void Deactivate()
+        {
+            window.Remove(timePicker);
+            timePicker.Dispose();
+            timePicker = null;
+        }
+    }
+}