[NUI] Introduce NUI TimePicker
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.Components / Controls / TimePicker.cs
1 /* Copyright (c) 2021 Samsung Electronics Co., Ltd.
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  *
15  */
16 using System;
17 using Tizen.NUI;
18 using Tizen.NUI.BaseComponents;
19 using System.Collections.Generic;
20 using System.Collections.ObjectModel;
21 using System.ComponentModel;
22 using System.Diagnostics.CodeAnalysis;
23 using System.Globalization;
24
25 namespace Tizen.NUI.Components
26 {
27     /// <summary>
28     /// TimeChangedEventArgs is a class to notify changed TimePicker value argument which will sent to user.
29     /// </summary>
30     [EditorBrowsable(EditorBrowsableState.Never)]
31     public class TimeChangedEventArgs : EventArgs
32     {
33         /// <summary>
34         /// TimeChangedEventArgs default constructor.
35         /// <param name="hour">hour value of TimePicker.</param>
36         /// <param name="minute">minute value of TimePicker.</param>
37         /// </summary>
38         [EditorBrowsable(EditorBrowsableState.Never)]   
39         public TimeChangedEventArgs(int hour, int minute)
40         {
41             Hour = hour;
42             Minute = minute;
43         }
44
45         /// <summary>
46         /// TimeChangedEventArgs default constructor.
47         /// <returns>The current hour value of TimePicker.</returns>
48         /// </summary>
49         [EditorBrowsable(EditorBrowsableState.Never)]   
50         public int Hour { get; }
51
52         /// <summary>
53         /// TimeChangedEventArgs default constructor.
54         /// <returns>The current minute value of TimePicker.</returns>
55         /// </summary>
56         [EditorBrowsable(EditorBrowsableState.Never)]   
57         public int Minute { get; }
58     }
59
60     /// <summary>
61     /// TimePicker is a class which provides a function that allows the user to select 
62     /// a time through a scrolling motion by expressing the specified value as a list.
63     /// TimePicker expresses the current time using the locale information of the system.
64     /// </summary>
65     [EditorBrowsable(EditorBrowsableState.Never)]
66     public class TimePicker : Control
67     {
68         private bool isAm;
69         private bool is24HourView;
70         private int hour;
71         private int minute;
72         private String[] ampmText;
73         private Picker hourPicker;
74         private Picker minutePicker;
75         private Picker ampmPicker;
76         private TimePickerStyle timePickerStyle => ViewStyle as TimePickerStyle;
77
78         /// <summary>
79         /// Creates a new instance of TimePicker.
80         /// </summary>
81         [EditorBrowsable(EditorBrowsableState.Never)]
82         public TimePicker()
83         {
84             Initialize();
85         }
86
87         /// <summary>
88         /// Creates a new instance of TimePicker.
89         /// </summary>
90         /// <param name="style">Creates TimePicker by special style defined in UX.</param>
91         [EditorBrowsable(EditorBrowsableState.Never)]
92         public TimePicker(string style) : base(style)
93         {
94             Initialize();
95         }
96
97         /// <summary>
98         /// Creates a new instance of TimePicker.
99         /// </summary>
100         /// <param name="timePickerStyle">Creates TimePicker by style customized by user.</param>
101         [EditorBrowsable(EditorBrowsableState.Never)]
102         public TimePicker(TimePickerStyle timePickerStyle) : base(timePickerStyle)
103         {
104             Initialize();
105         }
106
107         /// <summary>
108         /// Dispose TimePicker and all children on it.
109         /// </summary>
110         /// <param name="type">Dispose type.</param>
111         [EditorBrowsable(EditorBrowsableState.Never)]
112         protected override void Dispose(DisposeTypes type)
113         {
114             if (disposed)
115             {
116                 return;
117             }
118
119             if (type == DisposeTypes.Explicit)
120             {
121                 Remove(hourPicker);
122                 Utility.Dispose(hourPicker);
123                 hourPicker = null;
124                 Remove(minutePicker);
125                 Utility.Dispose(minutePicker);
126                 minutePicker = null;
127                 Remove(ampmPicker);
128                 Utility.Dispose(ampmPicker);
129                 ampmPicker = null;
130             }
131
132             base.Dispose(type);
133         }
134
135         /// <summary>
136         /// An event emitted when TimePicker value changed, user can subscribe or unsubscribe to this event handler.
137         /// </summary>
138         [EditorBrowsable(EditorBrowsableState.Never)]
139         public event EventHandler<TimeChangedEventArgs> TimeChanged;
140
141         /// <summary>
142         /// The hour value of TimePicker.
143         /// </summary>
144         [EditorBrowsable(EditorBrowsableState.Never)]
145         public int Hour
146         {
147             get
148             {
149                 return hour;
150             }
151             set
152             {
153                 if (value < 1 || value > 24 || value == hour) return;
154
155                 hour = value;
156                 if (!is24HourView)
157                 {
158                     if (hour >= 12 && hour <= 23) 
159                     {
160                         isAm = false;
161                         if (hour == 12) hourPicker.CurrentValue = hour;
162                         else hourPicker.CurrentValue = hour -= 12;
163                         ampmPicker.CurrentValue = 2;
164                     }
165                     else 
166                     {
167                         isAm = true;
168                         hourPicker.CurrentValue = hour;
169                         ampmPicker.CurrentValue = 1;
170                     }
171                 }
172                 else hourPicker.CurrentValue = hour;
173             }
174         }
175
176         /// <summary>
177         /// The Minute value of TimePicker.
178         /// </summary>
179         [EditorBrowsable(EditorBrowsableState.Never)]
180         public int Minute
181         {
182             get
183             {
184                 return minute;
185             }
186             set
187             {
188                 if (value < 1 || value > 60 || value == minute) return;
189
190                 minute = value;
191                 minutePicker.CurrentValue = minute;
192             }
193         }
194
195         /// <summary>
196         /// The is24hourview value of TimePicker.
197         /// </summary>
198         [EditorBrowsable(EditorBrowsableState.Never)]
199         public bool Is24HourView
200         {
201             get
202             {
203                 return is24HourView;
204             }
205             set
206             {
207                 if (is24HourView == value) return;
208
209                 is24HourView = value;
210                 if (value == true)
211                 {
212                     Remove(ampmPicker);
213                     hourPicker.MaxValue = 24;
214                 }
215                 else 
216                 {
217                     hourPicker.MaxValue = 12;
218                     PickersOrderSet(true);
219                     SetAmpmText();
220                 }
221             }
222         }
223
224         /// <summary>
225         /// Initialize TimePicker object.
226         /// </summary>
227         [EditorBrowsable(EditorBrowsableState.Never)]
228         public override void OnInitialize()
229         {
230             base.OnInitialize();
231
232             hourPicker = new Picker()
233             {
234                 MinValue = 1,
235                 MaxValue = 24,
236             };
237             hourPicker.ValueChanged += OnHourValueChanged;
238
239             minutePicker = new Picker()
240             {
241                 MinValue = 0,
242                 MaxValue = 59,
243             };
244             minutePicker.ValueChanged += OnMinuteValueChanged;
245
246             ampmPicker = new Picker()
247             {
248                 MinValue = 1,
249                 MaxValue = 2,
250             };
251             ampmPicker.ValueChanged += OnAmpmValueChanged;
252         }
253
254         /// <summary>
255         /// Applies style to TimePicker.
256         /// </summary>
257         /// <param name="viewStyle">The style to apply.</param>
258         [EditorBrowsable(EditorBrowsableState.Never)]
259         public override void ApplyStyle(ViewStyle viewStyle)
260         {
261             base.ApplyStyle(viewStyle);
262
263             //Apply CellPadding.
264             if (timePickerStyle?.CellPadding != null && Layout != null)
265                 ((LinearLayout)Layout).CellPadding = new Size2D(timePickerStyle.CellPadding.Width, timePickerStyle.CellPadding.Height);
266             
267             //Apply Internal Pickers style.
268             if (timePickerStyle?.Pickers != null && hourPicker != null && minutePicker != null && ampmPicker != null)
269             {
270                 hourPicker.ApplyStyle(timePickerStyle.Pickers);
271                 minutePicker.ApplyStyle(timePickerStyle.Pickers);
272                 ampmPicker.ApplyStyle(timePickerStyle.Pickers);
273             }
274         }
275                 
276         [SuppressMessage("Microsoft.Reliability",
277                          "CA2000:DisposeObjectsBeforeLosingScope",
278                          Justification = "The CellPadding will be dispose when the time picker disposed")]
279         private void Initialize()
280         {
281             HeightSpecification = LayoutParamPolicies.MatchParent;
282
283             Layout = new LinearLayout() { 
284                 LinearOrientation = LinearLayout.Orientation.Horizontal,
285                 CellPadding = new Size(timePickerStyle.CellPadding.Width, timePickerStyle.CellPadding.Height),
286             };
287
288             is24HourView = true;
289
290             PickersOrderSet(false);
291
292             if (!is24HourView) 
293             {
294                 SetAmpmText();
295                 hourPicker.MaxValue = 12;
296             }
297         }
298
299         private void OnHourValueChanged(object sender, ValueChangedEventArgs e)
300         {
301             if (hour == e.Value) return;
302
303             if (!is24HourView)
304             {
305                 if (isAm) 
306                 {
307                     if (e.Value == 12) hour = 24;
308                     else hour = e.Value;
309                 }
310                 else 
311                 {
312                     if (e.Value == 12) hour = 12;
313                     else hour = e.Value + 12;
314                 }
315             }
316             else
317                 hour = e.Value;
318             
319             OnTimeChanged();
320         }
321
322         private void OnMinuteValueChanged(object sender, ValueChangedEventArgs e)
323         { 
324             if (minute == e.Value) return;
325
326             minute = e.Value;
327
328             OnTimeChanged();
329         }
330
331         private void OnAmpmValueChanged(object sender, ValueChangedEventArgs e)
332         { 
333             if ((isAm && e.Value == 1) || (!isAm && e.Value == 2)) return;
334
335             if (e.Value == 1)
336             { //AM
337                 if (hour >= 12 || hour < 24)
338                 { 
339                     if (hour == 12) hour += 12;
340                     else hour -= 12;
341                 }
342                 isAm = true;
343             }
344             else 
345             { //PM
346                 if (hour == 24 || hour < 12) 
347                 {
348                      if (hour == 24) hour -= 12;
349                      else hour += 12; 
350                 }
351                 isAm = false;
352             }
353
354             OnTimeChanged();
355         }
356
357         private void OnTimeChanged()
358         { 
359             TimeChangedEventArgs eventArgs = new TimeChangedEventArgs(hour, minute);
360             TimeChanged?.Invoke(this, eventArgs);
361         }
362
363         private void PickersOrderSet(bool ampmForceSet)
364         {
365             //FIXME: Check the pickers located in already proper position or not.
366             Remove(hourPicker);
367             Remove(minutePicker);
368             Remove(ampmPicker);
369
370             //Get current system locale's time pattern
371             String locale = Environment.GetEnvironmentVariable("LC_TIME");
372             DateTimeFormatInfo timeFormatInfo = new CultureInfo(locale, false ).DateTimeFormat;
373             String timePattern = timeFormatInfo.ShortTimePattern;
374             String[] timePatternArray = timePattern.Split(' ', ':');
375
376             foreach (String format in timePatternArray) {
377                 if (format.IndexOf("H") != -1|| format.IndexOf("h") != -1)  Add(hourPicker);
378                 else if (format.IndexOf("M") != -1 || format.IndexOf("m") != -1) Add(minutePicker);
379                 else if (format.IndexOf("t") != -1) 
380                 {
381                     is24HourView = false;
382                     ampmForceSet = false;
383                     Add(ampmPicker);
384                 }
385             }
386
387             if (ampmForceSet) Add(ampmPicker);
388         }
389
390         private void SetAmpmText()
391         {
392             //FIXME: There is no localeChanged Event for Component now
393             //       AMPM text has to update when system locale changed.
394             String locale = Environment.GetEnvironmentVariable("LC_TIME");
395             CultureInfo info = new CultureInfo(locale);
396             ampmText = new string[] {info.DateTimeFormat.AMDesignator, info.DateTimeFormat.PMDesignator};
397             ampmPicker.DisplayedValues = new ReadOnlyCollection<string>(ampmText);
398         }
399     }
400 }