From b38be65ac2498cbb99adf7d7b2cad490eb67829d Mon Sep 17 00:00:00 2001 From: Woochanlee Date: Wed, 31 Mar 2021 21:11:00 +0900 Subject: [PATCH] [NUI] Introduce NUI TimePicker --- src/Tizen.NUI.Components/Controls/TimePicker.cs | 400 +++++++++++++++++++++ src/Tizen.NUI.Components/Style/TimePickerStyle.cs | 74 ++++ src/Tizen.NUI.Components/Theme/DefaultTheme.cs | 14 + .../Theme/DefaultThemeCommon.cs | 31 ++ .../Tizen.NUI.Samples/Samples/TimePickerSample.cs | 46 +++ 5 files changed, 565 insertions(+) create mode 100755 src/Tizen.NUI.Components/Controls/TimePicker.cs create mode 100755 src/Tizen.NUI.Components/Style/TimePickerStyle.cs create mode 100755 test/Tizen.NUI.Samples/Tizen.NUI.Samples/Samples/TimePickerSample.cs diff --git a/src/Tizen.NUI.Components/Controls/TimePicker.cs b/src/Tizen.NUI.Components/Controls/TimePicker.cs new file mode 100755 index 0000000..38a6991 --- /dev/null +++ b/src/Tizen.NUI.Components/Controls/TimePicker.cs @@ -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 +{ + /// + /// TimeChangedEventArgs is a class to notify changed TimePicker value argument which will sent to user. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public class TimeChangedEventArgs : EventArgs + { + /// + /// TimeChangedEventArgs default constructor. + /// hour value of TimePicker. + /// minute value of TimePicker. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public TimeChangedEventArgs(int hour, int minute) + { + Hour = hour; + Minute = minute; + } + + /// + /// TimeChangedEventArgs default constructor. + /// The current hour value of TimePicker. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public int Hour { get; } + + /// + /// TimeChangedEventArgs default constructor. + /// The current minute value of TimePicker. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public int Minute { get; } + } + + /// + /// 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. + /// + [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; + + /// + /// Creates a new instance of TimePicker. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public TimePicker() + { + Initialize(); + } + + /// + /// Creates a new instance of TimePicker. + /// + /// Creates TimePicker by special style defined in UX. + [EditorBrowsable(EditorBrowsableState.Never)] + public TimePicker(string style) : base(style) + { + Initialize(); + } + + /// + /// Creates a new instance of TimePicker. + /// + /// Creates TimePicker by style customized by user. + [EditorBrowsable(EditorBrowsableState.Never)] + public TimePicker(TimePickerStyle timePickerStyle) : base(timePickerStyle) + { + Initialize(); + } + + /// + /// Dispose TimePicker and all children on it. + /// + /// Dispose type. + [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); + } + + /// + /// An event emitted when TimePicker value changed, user can subscribe or unsubscribe to this event handler. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public event EventHandler TimeChanged; + + /// + /// The hour value of TimePicker. + /// + [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; + } + } + + /// + /// The Minute value of TimePicker. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public int Minute + { + get + { + return minute; + } + set + { + if (value < 1 || value > 60 || value == minute) return; + + minute = value; + minutePicker.CurrentValue = minute; + } + } + + /// + /// The is24hourview value of TimePicker. + /// + [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(); + } + } + } + + /// + /// Initialize TimePicker object. + /// + [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; + } + + /// + /// Applies style to TimePicker. + /// + /// The style to apply. + [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(ampmText); + } + } +} diff --git a/src/Tizen.NUI.Components/Style/TimePickerStyle.cs b/src/Tizen.NUI.Components/Style/TimePickerStyle.cs new file mode 100755 index 0000000..d6151ca --- /dev/null +++ b/src/Tizen.NUI.Components/Style/TimePickerStyle.cs @@ -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 +{ + /// + /// TimePickerStyle is a class which saves TimePickerStyle's ux data. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public class TimePickerStyle : ControlStyle + { + /// + /// Creates a new instance of a TimePickerStyle. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public TimePickerStyle() : base() + { + } + + /// + /// Creates a new instance of a TimePickerStyle with style. + /// + /// Creates TimePickerStyle by style customized by user. + [EditorBrowsable(EditorBrowsableState.Never)] + public TimePickerStyle(TimePickerStyle style) : base(style) + { + } + + /// + /// Gets or sets the TimePickerStyle internal pickers style. + /// + public PickerStyle Pickers { get; set;} = new PickerStyle(); + + /// + /// Gets or sets the TimePickerStyle internal pickers padding. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public Size2D CellPadding { get; set; } = new Size2D(); + + /// + /// Style's clone function. + /// + /// The style that needs to copy. + [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); + } + } + } +} diff --git a/src/Tizen.NUI.Components/Theme/DefaultTheme.cs b/src/Tizen.NUI.Components/Theme/DefaultTheme.cs index 3ec7e9a..71a108b 100755 --- a/src/Tizen.NUI.Components/Theme/DefaultTheme.cs +++ b/src/Tizen.NUI.Components/Theme/DefaultTheme.cs @@ -148,6 +148,20 @@ namespace Tizen.NUI.Components .Add("/MessageTextLabel/VerticalAlignment", (ViewStyle style, VerticalAlignment? value) => ((AlertDialogStyle)style).MessageTextLabel.VerticalAlignment = value) .AddSelector("/MessageTextLabel/TextColor", (ViewStyle style, Selector value) => ((AlertDialogStyle)style).MessageTextLabel.TextColor = value) .Add("/ActionContent/Size", (ViewStyle style, Size value) => ((AlertDialogStyle)style).ActionContent.Size = value), + + // TimePicker + (new ExternalThemeKeyList(typeof(TimePicker), typeof(TimePickerStyle))) + .Add("/CellPadding", (ViewStyle style, Size value) => ((TimePickerStyle)style).Size = value) + .Add("/Pickers/Size", (ViewStyle style, Size value) => ((TimePickerStyle)style).Pickers.Size = value) + .AddSelector("/Pickers/ItemTextLabel/PixelSize", (ViewStyle style, Selector value) => ((TimePickerStyle)style).Pickers.ItemTextLabel.PixelSize = value, ControlState.Selected) + .Add("/Pickers/ItemTextLabel/Size", (ViewStyle style, Size value) => ((TimePickerStyle)style).Pickers.ItemTextLabel.Size = value) + .AddSelector("/Pickers/ItemTextLabel/TextColor", (ViewStyle style, Selector value) => ((TimePickerStyle)style).Pickers.ItemTextLabel.TextColor = value, ControlState.Selected) + .AddSelector("/Pickers/ItemTextLabel/BackgroundColor", (ViewStyle style, Selector value) => ((TimePickerStyle)style).Pickers.ItemTextLabel.BackgroundColor = value, ControlState.Selected) + .Add("/Pickers/Divider/SizeHeight", (ViewStyle style, float? value) => ((TimePickerStyle)style).Pickers.Divider.SizeHeight = value) + .Add("/Pickers/Divider/Position", (ViewStyle style, Position value) => ((TimePickerStyle)style).Pickers.Divider.Position = value) + .AddSelector("/Pickers/Divider/BackgroundColor", (ViewStyle style, Selector value) => ((TimePickerStyle)style).Pickers.Divider.BackgroundColor = value, ControlState.Selected) + .Add("/Pickers/StartScrollOffset", (ViewStyle style, Size value) => ((TimePickerStyle)style).Pickers.StartScrollOffset = value), + }; return actionSet; diff --git a/src/Tizen.NUI.Components/Theme/DefaultThemeCommon.cs b/src/Tizen.NUI.Components/Theme/DefaultThemeCommon.cs index 3039d08..ca7c016 100755 --- a/src/Tizen.NUI.Components/Theme/DefaultThemeCommon.cs +++ b/src/Tizen.NUI.Components/Theme/DefaultThemeCommon.cs @@ -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() + { + 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 index 0000000..4ae8761 --- /dev/null +++ b/test/Tizen.NUI.Samples/Tizen.NUI.Samples/Samples/TimePickerSample.cs @@ -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; + } + } +} -- 2.7.4