[NUI] Apply IsEnabled feature on Pickers.
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.Components / Controls / DatePicker.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.Globalization;
23 using System.Diagnostics.CodeAnalysis;
24 using Tizen.NUI.Binding;
25
26 namespace Tizen.NUI.Components
27 {
28     /// <summary>
29     /// DateChangedEventArgs is a class to notify changed DatePicker value argument which will sent to user.
30     /// </summary>
31     /// <since_tizen> 9 </since_tizen>
32     public class DateChangedEventArgs : EventArgs
33     {
34         /// <summary>
35         /// DateChangedEventArgs default constructor.
36         /// <param name="date">date value of DatePicker.</param>
37         /// </summary>
38         [EditorBrowsable(EditorBrowsableState.Never)]
39         public DateChangedEventArgs(DateTime date)
40         {
41             Date = date;
42         }
43
44         /// <summary>
45         /// DateChangedEventArgs default constructor.
46         /// <returns>The current date value of DatePicker.</returns>
47         /// </summary>
48         /// <since_tizen> 9 </since_tizen>
49         public DateTime Date { get; }
50     }
51
52     /// <summary>
53     /// DatePicker is a class which provides a function that allows the user to select
54     /// a date through a scrolling motion by expressing the specified value as a list.
55     /// DatePicker expresses the current date using the locale information of the system.
56     /// Year range is 1970~2038 (glibc time_t struct min, max value)
57     /// </summary>
58     /// <since_tizen> 9 </since_tizen>
59     public class DatePicker : Control
60     {
61         /// <summary>
62         /// DateProperty
63         /// </summary>
64         [EditorBrowsable(EditorBrowsableState.Never)]
65         public static readonly BindableProperty DateProperty = BindableProperty.Create(nameof(Date), typeof(DateTime), typeof(DatePicker), default(DateTime), propertyChanged: (bindable, oldValue, newValue) =>
66         {
67             var instance = (DatePicker)bindable;
68             if (newValue != null)
69             {
70                 instance.InternalDate = (DateTime)newValue;
71             }
72         },
73         defaultValueCreator: (bindable) =>
74         {
75             var instance = (DatePicker)bindable;
76             return instance.InternalDate;
77         });
78
79         private DateTime currentDate;
80         private Picker dayPicker;
81         private Picker monthPicker;
82         private Picker yearPicker;
83
84         /// <summary>
85         /// Creates a new instance of DatePicker.
86         /// </summary>
87         /// <since_tizen> 9 </since_tizen>
88         public DatePicker()
89         {
90             SetKeyboardNavigationSupport(true);
91         }
92
93         /// <summary>
94         /// Creates a new instance of DatePicker.
95         /// </summary>
96         /// <param name="style">Creates DatePicker by special style defined in UX.</param>
97         /// <since_tizen> 9 </since_tizen>
98         public DatePicker(string style) : base(style)
99         {
100             SetKeyboardNavigationSupport(true);
101         }
102
103         /// <summary>
104         /// Creates a new instance of DatePicker.
105         /// </summary>
106         /// <param name="datePickerStyle">Creates DatePicker by style customized by user.</param>
107         /// <since_tizen> 9 </since_tizen>
108         public DatePicker(DatePickerStyle datePickerStyle) : base(datePickerStyle)
109         {
110             SetKeyboardNavigationSupport(true);
111         }
112
113         /// <inheritdoc/>
114         [EditorBrowsable(EditorBrowsableState.Never)]
115         protected override void OnEnabled(bool enabled)
116         {
117             base.OnEnabled(enabled);
118
119             dayPicker.IsEnabled = enabled;
120             monthPicker.IsEnabled = enabled;
121             yearPicker.IsEnabled = enabled;
122         }
123
124         /// <summary>
125         /// Dispose DatePicker and all children on it.
126         /// </summary>
127         /// <param name="type">Dispose type.</param>
128         [EditorBrowsable(EditorBrowsableState.Never)]
129         protected override void Dispose(DisposeTypes type)
130         {
131             if (disposed)
132             {
133                 return;
134             }
135
136             if (type == DisposeTypes.Explicit)
137             {
138                 Remove(monthPicker);
139                 Utility.Dispose(monthPicker);
140                 monthPicker = null;
141                 Remove(dayPicker);
142                 Utility.Dispose(dayPicker);
143                 dayPicker = null;
144                 Remove(yearPicker);
145                 Utility.Dispose(yearPicker);
146                 yearPicker = null;
147             }
148
149             base.Dispose(type);
150         }
151
152         /// <summary>
153         /// An event emitted when DatePicker value changed, user can subscribe or unsubscribe to this event handler.
154         /// </summary>
155         /// <since_tizen> 9 </since_tizen>
156         public event EventHandler<DateChangedEventArgs> DateChanged;
157
158         /// <summary>
159         /// The Date value of DatePicker.
160         /// </summary>
161         /// <since_tizen> 9 </since_tizen>
162         public DateTime Date
163         {
164             get
165             {
166                 return (DateTime)GetValue(DateProperty);
167             }
168             set
169             {
170                 SetValue(DateProperty, value);
171                 NotifyPropertyChanged();
172             }
173         }
174         private DateTime InternalDate
175         {
176             get
177             {
178                 return currentDate;
179             }
180             set
181             {
182                 currentDate = value;
183                 dayPicker.CurrentValue = currentDate.Day;
184                 monthPicker.CurrentValue = currentDate.Month;
185                 yearPicker.CurrentValue = currentDate.Year;
186             }
187         }
188
189         /// <summary>
190         /// Initialize TimePicker object.
191         /// </summary>
192         [EditorBrowsable(EditorBrowsableState.Never)]
193         public override void OnInitialize()
194         {
195             base.OnInitialize();
196             AccessibilityRole = Role.DateEditor;
197
198             dayPicker = new Picker()
199             {
200                 MinValue = 1,
201                 MaxValue = 31,
202                 Focusable = true,
203             };
204             dayPicker.ValueChanged += OnDayValueChanged;
205
206             monthPicker = new Picker()
207             {
208                 MinValue = 1,
209                 MaxValue = 12,
210                 Focusable = true,
211             };
212             monthPicker.ValueChanged += OnMonthValueChanged;
213
214             yearPicker = new Picker()
215             {
216                 MinValue = 1970,
217                 MaxValue = 2100,
218                 Focusable = true,
219             };
220             yearPicker.ValueChanged += OnYearValueChanged;
221
222             currentDate = DateTime.Now;
223             dayPicker.CurrentValue = currentDate.Day;
224             monthPicker.CurrentValue = currentDate.Month;
225             yearPicker.CurrentValue = currentDate.Year;
226
227             Initialize();
228         }
229
230         /// <inheritdoc/>
231         [EditorBrowsable(EditorBrowsableState.Never)]
232         [SuppressMessage("Microsoft.Reliability",
233                          "CA2000:DisposeObjectsBeforeLosingScope",
234                          Justification = "The CellPadding will be dispose when the date picker disposed")]
235         public override void ApplyStyle(ViewStyle viewStyle)
236         {
237             base.ApplyStyle(viewStyle);
238
239             if (viewStyle is DatePickerStyle datePickerStyle && Layout is LinearLayout linearLayout)
240             {
241                 linearLayout.CellPadding = new Size(datePickerStyle.CellPadding.Width, datePickerStyle.CellPadding.Height);
242
243                 yearPicker.ApplyStyle(datePickerStyle.Pickers);
244                 monthPicker.ApplyStyle(datePickerStyle.Pickers);
245                 dayPicker.ApplyStyle(datePickerStyle.Pickers);
246             }
247         }
248
249         /// <summary>
250         /// ToDo : only key navigation is enabled, and value editing is added as an very simple operation. by toggling enter key, it switches edit mode.
251         /// ToDo : this should be fixed and changed properly by owner. (And UX SPEC should be referenced also)
252         /// </summary>
253         /// <param name="currentFocusedView"></param>
254         /// <param name="direction"></param>
255         /// <param name="loopEnabled"></param>
256         /// <returns></returns>
257         [EditorBrowsable(EditorBrowsableState.Never)]
258         public override View GetNextFocusableView(View currentFocusedView, View.FocusDirection direction, bool loopEnabled)
259         {
260             if (currentFocusedView == yearPicker)
261             {
262                 if (direction == View.FocusDirection.Right)
263                 {
264                     return monthPicker;
265                 }
266             }
267             else if (currentFocusedView == monthPicker)
268             {
269                 if (direction == View.FocusDirection.Right)
270                 {
271                     return dayPicker;
272                 }
273                 else if (direction == View.FocusDirection.Left)
274                 {
275                     return yearPicker;
276                 }
277             }
278             else if (currentFocusedView == dayPicker)
279             {
280                 if (direction == View.FocusDirection.Left)
281                 {
282                     return monthPicker;
283                 }
284             }
285             return null;
286         }
287
288         private void Initialize()
289         {
290             HeightSpecification = LayoutParamPolicies.MatchParent;
291
292             Layout = new LinearLayout()
293             {
294                 LinearOrientation = LinearLayout.Orientation.Horizontal,
295             };
296
297             PickersOrderSet();
298             SetMonthText();
299             MaxDaySet(currentDate.Year, currentDate.Month);
300         }
301
302         private void OnDayValueChanged(object sender, ValueChangedEventArgs e)
303         {
304             if (currentDate.Day == e.Value) return;
305
306             currentDate = new DateTime(currentDate.Year, currentDate.Month, e.Value);
307
308             OnDateChanged();
309         }
310
311         private void OnMonthValueChanged(object sender, ValueChangedEventArgs e)
312         {
313             if (currentDate.Month == e.Value) return;
314
315             MaxDaySet(currentDate.Year, e.Value);
316
317             OnDateChanged();
318         }
319
320         private void OnYearValueChanged(object sender, ValueChangedEventArgs e)
321         {
322             if (currentDate.Year == e.Value) return;
323
324             MaxDaySet(e.Value, currentDate.Month);
325
326             OnDateChanged();
327         }
328
329         private void OnDateChanged()
330         {
331             DateChangedEventArgs eventArgs = new DateChangedEventArgs(currentDate);
332             DateChanged?.Invoke(this, eventArgs);
333         }
334
335         private void MaxDaySet(int year, int month)
336         {
337             int maxDaysInMonth = DateTime.DaysInMonth(year, month);
338             dayPicker.MaxValue = maxDaysInMonth;
339             if (currentDate.Day > maxDaysInMonth)
340             {
341                 currentDate = new DateTime(year, month, maxDaysInMonth);
342                 dayPicker.CurrentValue = maxDaysInMonth;
343                 return;
344             }
345             currentDate = new DateTime(year, month, currentDate.Day);
346         }
347
348         //FIXME: There is no way to know when system locale changed in NUI.
349         //       Pickers order and Month text has to be follow system locale.
350         private void PickersOrderSet()
351         {
352             DateTimeFormatInfo DateFormat = CultureInfo.CurrentCulture.DateTimeFormat;
353             String temp = DateFormat.ShortDatePattern;
354             String[] strArray = temp.Split(' ', '/');
355             foreach (String format in strArray)
356             {
357                 if (format.IndexOf("M") != -1 || format.IndexOf("m") != -1) Add(monthPicker);
358                 else if (format.IndexOf("d") != -1 || format.IndexOf("D") != -1) Add(dayPicker);
359                 else if (format.IndexOf("y") != -1 || format.IndexOf("Y") != -1) Add(yearPicker);
360             }
361         }
362
363         private void SetMonthText()
364         {
365             CultureInfo info = CultureInfo.CurrentCulture;
366             monthPicker.DisplayedValues = new ReadOnlyCollection<string>(info.DateTimeFormat.AbbreviatedMonthNames);
367         }
368     }
369 }