[NUI] Rebase develnui (DevelNUI only patches --> master) (#3910)
[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.BaseComponents;
18 using System.Collections.Generic;
19 using System.Collections.ObjectModel;
20 using System.ComponentModel;
21 using System.Diagnostics.CodeAnalysis;
22 using System.Globalization;
23 using Tizen.NUI.Binding;
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     /// <since_tizen> 9 </since_tizen>
31     public class TimeChangedEventArgs : EventArgs
32     {
33         /// <summary>
34         /// TimeChangedEventArgs default constructor.
35         /// <param name="time">time value of TimePicker.</param>
36         /// </summary>
37         [EditorBrowsable(EditorBrowsableState.Never)]   
38         public TimeChangedEventArgs(DateTime time)
39         {
40             Time = time;
41         }
42
43         /// <summary>
44         /// TimeChangedEventArgs default constructor.
45         /// <returns>The current time value of TimePicker.</returns>
46         /// </summary>
47         /// <since_tizen> 9 </since_tizen>
48         public DateTime Time { get; }
49     }
50
51     /// <summary>
52     /// TimePicker is a class which provides a function that allows the user to select 
53     /// a time through a scrolling motion by expressing the specified value as a list.
54     /// TimePicker expresses the current time using the locale information of the system.
55     /// </summary>
56     /// <since_tizen> 9 </since_tizen>
57     public class TimePicker : Control
58     {
59         /// <summary>
60         /// TimeProperty
61         /// </summary>
62         [EditorBrowsable(EditorBrowsableState.Never)]
63         public static readonly BindableProperty TimeProperty = BindableProperty.Create(nameof(Time), typeof(DateTime), typeof(TimePicker), default(DateTime), propertyChanged: (bindable, oldValue, newValue) =>
64         {
65             var instance = (TimePicker)bindable;
66             if (newValue != null)
67             {
68                 instance.InternalTime = (DateTime)newValue;
69             }
70         },
71         defaultValueCreator: (bindable) =>
72         {
73             var instance = (TimePicker)bindable;
74             return instance.InternalTime;
75         });
76
77         /// <summary>
78         /// Is24HourViewProperty
79         /// </summary>
80         [EditorBrowsable(EditorBrowsableState.Never)]
81         public static readonly BindableProperty Is24HourViewProperty = BindableProperty.Create(nameof(Is24HourView), typeof(bool), typeof(TimePicker), default(bool), propertyChanged: (bindable, oldValue, newValue) =>
82         {
83             var instance = (TimePicker)bindable;
84             if (newValue != null)
85             {
86                 instance.InternalIs24HourView = (bool)newValue;
87             }
88         },
89         defaultValueCreator: (bindable) =>
90         {
91             var instance = (TimePicker)bindable;
92             return instance.InternalIs24HourView;
93         });
94
95         private bool isAm;
96         private bool is24HourView;
97         private DateTime currentTime;
98         private String[] ampmText;
99         private Picker hourPicker;
100         private Picker minutePicker;
101         private Picker ampmPicker;
102
103         /// <summary>
104         /// Creates a new instance of TimePicker.
105         /// </summary>
106         /// <since_tizen> 9 </since_tizen>
107         public TimePicker()
108         {
109         }
110
111         /// <summary>
112         /// Creates a new instance of TimePicker.
113         /// </summary>
114         /// <param name="style">Creates TimePicker by special style defined in UX.</param>
115         /// <since_tizen> 9 </since_tizen>
116         public TimePicker(string style) : base(style)
117         {
118         }
119
120         /// <summary>
121         /// Creates a new instance of TimePicker.
122         /// </summary>
123         /// <param name="timePickerStyle">Creates TimePicker by style customized by user.</param>
124         /// <since_tizen> 9 </since_tizen>
125         public TimePicker(TimePickerStyle timePickerStyle) : base(timePickerStyle)
126         {
127         }
128
129         /// <summary>
130         /// Dispose TimePicker and all children on it.
131         /// </summary>
132         /// <param name="type">Dispose type.</param>
133         [EditorBrowsable(EditorBrowsableState.Never)]
134         protected override void Dispose(DisposeTypes type)
135         {
136             if (disposed)
137             {
138                 return;
139             }
140
141             if (type == DisposeTypes.Explicit)
142             {
143                 Remove(hourPicker);
144                 Utility.Dispose(hourPicker);
145                 hourPicker = null;
146                 Remove(minutePicker);
147                 Utility.Dispose(minutePicker);
148                 minutePicker = null;
149                 Remove(ampmPicker);
150                 Utility.Dispose(ampmPicker);
151                 ampmPicker = null;
152             }
153
154             base.Dispose(type);
155         }
156
157         /// <summary>
158         /// An event emitted when TimePicker value changed, user can subscribe or unsubscribe to this event handler.
159         /// </summary>
160         /// <since_tizen> 9 </since_tizen>
161         public event EventHandler<TimeChangedEventArgs> TimeChanged;
162
163         /// <summary>
164         /// The hour value of TimePicker.
165         /// </summary>
166         /// <since_tizen> 9 </since_tizen>
167         public DateTime Time
168         {
169             get
170             {
171                 return (DateTime)GetValue(TimeProperty);
172             }
173             set
174             {
175                 SetValue(TimeProperty, value);
176                 NotifyPropertyChanged();
177             }
178         }
179         private DateTime InternalTime
180         {
181             get
182             {
183                 return currentTime;
184             }
185             set
186             {
187                 currentTime = value;
188                 if (!is24HourView)
189                 {
190                     if (currentTime.Hour >= 12 && currentTime.Hour <= 23)
191                     {
192                         isAm = false;
193                         if (currentTime.Hour == 12) hourPicker.CurrentValue = currentTime.Hour;
194                         else hourPicker.CurrentValue = currentTime.Hour - 12;
195                         ampmPicker.CurrentValue = 2;
196                     }
197                     else 
198                     {
199                         isAm = true;
200                         hourPicker.CurrentValue = currentTime.Hour;
201                         ampmPicker.CurrentValue = 1;
202                     }
203                 }
204                 else hourPicker.CurrentValue = currentTime.Hour;
205
206                 minutePicker.CurrentValue = currentTime.Minute;
207             }
208         }
209
210         /// <summary>
211         /// The is24hourview value of TimePicker.
212         /// </summary>
213         /// <since_tizen> 9 </since_tizen>
214         public bool Is24HourView
215         {
216             get
217             {
218                 return (bool)GetValue(Is24HourViewProperty);
219             }
220             set
221             {
222                 SetValue(Is24HourViewProperty, value);
223                 NotifyPropertyChanged();
224             }
225         }
226         private bool InternalIs24HourView
227         {
228             get
229             {
230                 return is24HourView;
231             }
232             set
233             {
234                 if (is24HourView == value) return;
235
236                 Console.WriteLine(" Is 24 Hour View");
237                 is24HourView = value;
238                 if (value == true)
239                 {
240                     Remove(ampmPicker);
241                     hourPicker.MinValue = 0;
242                     hourPicker.MaxValue = 23;
243                     hourPicker.CurrentValue = currentTime.Hour;
244                 }
245                 else 
246                 {
247                     hourPicker.MinValue = 1;
248                     hourPicker.MaxValue = 12;
249                     PickersOrderSet(true);
250                     SetAmpmText();
251                     if (currentTime.Hour > 12)
252                     {
253                         ampmPicker.CurrentValue = 2;
254                         hourPicker.CurrentValue = currentTime.Hour - 12;
255                     }
256                 }
257             }
258         }
259
260         /// <summary>
261         /// Initialize TimePicker object.
262         /// </summary>
263         [EditorBrowsable(EditorBrowsableState.Never)]
264         public override void OnInitialize()
265         {
266             base.OnInitialize();
267             SetAccessibilityConstructor(Role.DateEditor);
268
269             hourPicker = new Picker()
270             {
271                 MinValue = 1,
272                 MaxValue = 12,
273             };
274             hourPicker.ValueChanged += OnHourValueChanged;
275
276             minutePicker = new Picker()
277             {
278                 MinValue = 0,
279                 MaxValue = 59,
280             };
281             minutePicker.ValueChanged += OnMinuteValueChanged;
282
283             ampmPicker = new Picker()
284             {
285                 MinValue = 1,
286                 MaxValue = 2,
287             };
288             ampmPicker.ValueChanged += OnAmpmValueChanged;
289
290             currentTime = DateTime.Now;
291             Console.WriteLine(" Time " + currentTime.Hour + " " + currentTime.Minute);
292             if (currentTime.Hour > 12)
293             {
294                 ampmPicker.CurrentValue = 2;
295                 hourPicker.CurrentValue = currentTime.Hour - 12;
296             }
297             else
298                 hourPicker.CurrentValue = currentTime.Hour;
299
300             minutePicker.CurrentValue = currentTime.Minute;
301
302             Initialize();
303         }
304
305         /// <summary>
306         /// Applies style to TimePicker.
307         /// </summary>
308         /// <param name="viewStyle">The style to apply.</param>
309         [EditorBrowsable(EditorBrowsableState.Never)]
310         public override void ApplyStyle(ViewStyle viewStyle)
311         {
312             base.ApplyStyle(viewStyle);
313
314             var timePickerStyle = viewStyle as TimePickerStyle;
315
316             if (timePickerStyle == null) return;
317
318             //Apply CellPadding.
319             if (timePickerStyle?.CellPadding != null && Layout != null)
320                 ((LinearLayout)Layout).CellPadding = new Size2D(timePickerStyle.CellPadding.Width, timePickerStyle.CellPadding.Height);
321             
322             //Apply Internal Pickers style.
323             if (timePickerStyle?.Pickers != null && hourPicker != null && minutePicker != null && ampmPicker != null)
324             {
325                 hourPicker.ApplyStyle(timePickerStyle.Pickers);
326                 minutePicker.ApplyStyle(timePickerStyle.Pickers);
327                 ampmPicker.ApplyStyle(timePickerStyle.Pickers);
328             }
329         }
330                 
331         [SuppressMessage("Microsoft.Reliability",
332                          "CA2000:DisposeObjectsBeforeLosingScope",
333                          Justification = "The CellPadding will be dispose when the time picker disposed")]
334         private void Initialize()
335         {
336             HeightSpecification = LayoutParamPolicies.MatchParent;
337
338             Layout = new LinearLayout() { 
339                 LinearOrientation = LinearLayout.Orientation.Horizontal,
340             };
341             Console.WriteLine("initialize");
342
343             is24HourView = false;
344
345             PickersOrderSet(false);
346             SetAmpmText();
347         }
348
349         private void ChangeTime(int hour, int minute, bool hourUpdate)
350         {
351             if (hourUpdate)
352                 currentTime = new DateTime(currentTime.Year, currentTime.Month, currentTime.Day, hour, currentTime.Minute, 0);
353             else
354                 currentTime = new DateTime(currentTime.Year, currentTime.Month, currentTime.Day, currentTime.Hour, minute, 0);
355         }
356
357         private void OnHourValueChanged(object sender, ValueChangedEventArgs e)
358         {
359             if (currentTime.Hour == e.Value) return;
360
361             if (!is24HourView)
362             {
363                 if (isAm) 
364                 {
365                     if (e.Value == 12) ChangeTime(12, 0, true);
366                     else ChangeTime(e.Value, 0, true);
367                 }
368                 else 
369                 {
370                     if (e.Value == 12) ChangeTime(0, 0, true);
371                     else ChangeTime(e.Value + 12, 0, true);
372                 }
373             }
374             else
375                 ChangeTime(e.Value, 0, true);
376             
377             OnTimeChanged();
378         }
379
380         private void OnMinuteValueChanged(object sender, ValueChangedEventArgs e)
381         { 
382             if (currentTime.Minute == e.Value) return;
383
384             ChangeTime(0, e.Value, false);
385
386             OnTimeChanged();
387         }
388
389         private void OnAmpmValueChanged(object sender, ValueChangedEventArgs e)
390         { 
391             if ((isAm && e.Value == 1) || (!isAm && e.Value == 2)) return;
392
393             if (e.Value == 1)
394             { //AM
395                 if (currentTime.Hour > 12 || currentTime.Hour == 0)
396                 { 
397                     if (currentTime.Hour == 0) ChangeTime(12, 0, true);
398                     else ChangeTime(currentTime.Hour - 12, 0, true);
399                 }
400                 isAm = true;
401             }
402             else 
403             { //PM
404                 if (currentTime.Hour > 0 && currentTime.Hour <= 12)
405                 {
406                      if (currentTime.Hour == 12) ChangeTime(0, 0, true);
407                      else ChangeTime(currentTime.Hour + 12, 0, true);
408                 }
409                 isAm = false;
410             }
411
412             OnTimeChanged();
413         }
414
415         private void OnTimeChanged()
416         { 
417             TimeChangedEventArgs eventArgs = new TimeChangedEventArgs(currentTime);
418             TimeChanged?.Invoke(this, eventArgs);
419         }
420
421         private void PickersOrderSet(bool ampmForceSet)
422         {
423             //FIXME: Check the pickers located in already proper position or not.
424             Remove(hourPicker);
425             Remove(minutePicker);
426             Remove(ampmPicker);
427
428             //Get current system locale's time pattern
429             DateTimeFormatInfo timeFormatInfo = CultureInfo.CurrentCulture.DateTimeFormat;
430             String timePattern = timeFormatInfo.ShortTimePattern;
431             String[] timePatternArray = timePattern.Split(' ', ':');
432
433             foreach (String format in timePatternArray) {
434                 if (format.IndexOf("H") != -1|| format.IndexOf("h") != -1)  Add(hourPicker);
435                 else if (format.IndexOf("M") != -1 || format.IndexOf("m") != -1) Add(minutePicker);
436                 else if (format.IndexOf("t") != -1) 
437                 {
438                     is24HourView = false;
439                     ampmForceSet = false;
440                     Add(ampmPicker);
441                 }
442             }
443
444             if (ampmForceSet) Add(ampmPicker);
445         }
446
447         private void SetAmpmText()
448         {
449             //FIXME: There is no localeChanged Event for Component now
450             //       AMPM text has to update when system locale changed.
451             CultureInfo info = CultureInfo.CurrentCulture;
452             ampmText = new string[] {info.DateTimeFormat.AMDesignator, info.DateTimeFormat.PMDesignator};
453             ampmPicker.DisplayedValues = new ReadOnlyCollection<string>(ampmText);
454         }
455     }
456 }