c7223b42c63b292013b9c29c9fa96a10d1c05dce
[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         }
91         
92         /// <summary>
93         /// Creates a new instance of DatePicker.
94         /// </summary>
95         /// <param name="style">Creates DatePicker by special style defined in UX.</param>
96         /// <since_tizen> 9 </since_tizen>
97         public DatePicker(string style) : base(style)
98         {
99         }
100
101         /// <summary>
102         /// Creates a new instance of DatePicker.
103         /// </summary>
104         /// <param name="datePickerStyle">Creates DatePicker by style customized by user.</param>
105         /// <since_tizen> 9 </since_tizen>
106         public DatePicker(DatePickerStyle datePickerStyle) : base(datePickerStyle)
107         {
108         }
109
110
111         /// <summary>
112         /// Dispose DatePicker and all children on it.
113         /// </summary>
114         /// <param name="type">Dispose type.</param>
115         [EditorBrowsable(EditorBrowsableState.Never)]
116         protected override void Dispose(DisposeTypes type)
117         {
118             if (disposed)
119             {
120                 return;
121             }
122
123             if (type == DisposeTypes.Explicit)
124             {
125                 Remove(monthPicker);
126                 Utility.Dispose(monthPicker);
127                 monthPicker = null;
128                 Remove(dayPicker);
129                 Utility.Dispose(dayPicker);
130                 dayPicker = null;
131                 Remove(yearPicker);
132                 Utility.Dispose(yearPicker);
133                 yearPicker = null;
134             }
135
136             base.Dispose(type);
137         }
138
139         /// <summary>
140         /// An event emitted when DatePicker value changed, user can subscribe or unsubscribe to this event handler.
141         /// </summary>
142         /// <since_tizen> 9 </since_tizen>
143         public event EventHandler<DateChangedEventArgs> DateChanged;
144         
145         /// <summary>
146         /// The Date value of DatePicker.
147         /// </summary>
148         /// <since_tizen> 9 </since_tizen>
149         public DateTime Date
150         {
151             get
152             {
153                 return (DateTime)GetValue(DateProperty);
154             }
155             set
156             {
157                 SetValue(DateProperty, value);
158                 NotifyPropertyChanged();
159             }
160         }
161         private DateTime InternalDate
162         {
163             get
164             {
165                 return currentDate;
166             }
167             set
168             {
169                 currentDate = value;
170                 dayPicker.CurrentValue = currentDate.Day;
171                 monthPicker.CurrentValue = currentDate.Month;
172                 yearPicker.CurrentValue = currentDate.Year;
173             }
174         }
175
176         /// <summary>
177         /// Initialize TimePicker object.
178         /// </summary>
179         [EditorBrowsable(EditorBrowsableState.Never)]
180         public override void OnInitialize()
181         {
182             base.OnInitialize();
183             SetAccessibilityConstructor(Role.DateEditor);
184
185             dayPicker = new Picker()
186             {
187                 MinValue = 1,
188                 MaxValue = 31,
189             };
190             dayPicker.ValueChanged += OnDayValueChanged;
191
192             monthPicker = new Picker()
193             {
194                 MinValue = 1,
195                 MaxValue = 12,
196             };
197             monthPicker.ValueChanged += OnMonthValueChanged;
198
199             yearPicker = new Picker()
200             {
201                 MinValue = 1970,
202                 MaxValue = 2100,
203             };
204             yearPicker.ValueChanged += OnYearValueChanged;
205
206             currentDate = DateTime.Now;
207             dayPicker.CurrentValue = currentDate.Day;
208             monthPicker.CurrentValue = currentDate.Month;
209             yearPicker.CurrentValue = currentDate.Year;
210
211             Initialize();
212         }
213
214         /// <inheritdoc/>
215         [EditorBrowsable(EditorBrowsableState.Never)]
216         [SuppressMessage("Microsoft.Reliability",
217                          "CA2000:DisposeObjectsBeforeLosingScope",
218                          Justification = "The CellPadding will be dispose when the date picker disposed")]
219         public override void ApplyStyle(ViewStyle viewStyle)
220         {
221             base.ApplyStyle(viewStyle);
222
223             if (viewStyle is DatePickerStyle datePickerStyle && Layout is LinearLayout linearLayout)
224             {
225                 linearLayout.CellPadding = new Size(datePickerStyle.CellPadding.Width, datePickerStyle.CellPadding.Height);
226
227                 yearPicker.ApplyStyle(datePickerStyle.Pickers);
228                 monthPicker.ApplyStyle(datePickerStyle.Pickers);
229                 dayPicker.ApplyStyle(datePickerStyle.Pickers);
230             }
231         }
232
233         private void Initialize()
234         {
235             HeightSpecification = LayoutParamPolicies.MatchParent;
236
237             Layout = new LinearLayout() { 
238                 LinearOrientation = LinearLayout.Orientation.Horizontal,
239             };
240
241             PickersOrderSet();
242             SetMonthText();
243             MaxDaySet();
244         }
245
246         private void OnDayValueChanged(object sender, ValueChangedEventArgs e)
247         {
248             if (currentDate.Day == e.Value) return;
249
250             currentDate = new DateTime(currentDate.Year, currentDate.Month, e.Value);
251             
252             OnDateChanged();
253         }
254
255         private void OnMonthValueChanged(object sender, ValueChangedEventArgs e)
256         { 
257             if (currentDate.Month == e.Value) return;
258
259             currentDate = new DateTime(currentDate.Year, e.Value, currentDate.Day);
260             MaxDaySet();
261
262             OnDateChanged();
263         }
264
265         private void OnYearValueChanged(object sender, ValueChangedEventArgs e)
266         { 
267             if (currentDate.Year == e.Value) return;
268
269             currentDate = new DateTime(e.Value, currentDate.Month, currentDate.Day);
270             MaxDaySet();
271
272             OnDateChanged();
273         }
274
275         private void OnDateChanged()
276         { 
277             DateChangedEventArgs eventArgs = new DateChangedEventArgs(currentDate);
278             DateChanged?.Invoke(this, eventArgs);
279         }
280
281         private void MaxDaySet()
282         {
283             int maxDaysInMonth = DateTime.DaysInMonth(currentDate.Year, currentDate.Month);
284             dayPicker.MaxValue = maxDaysInMonth;
285             if (currentDate.Day > maxDaysInMonth)
286             {
287                 currentDate = new DateTime(currentDate.Year, currentDate.Month, maxDaysInMonth);
288                 dayPicker.CurrentValue = maxDaysInMonth;
289                 return;
290             }
291             currentDate = new DateTime(currentDate.Year, currentDate.Month, currentDate.Day);
292         }
293
294         //FIXME: There is no way to know when system locale changed in NUI.
295         //       Pickers order and Month text has to be follow system locale.
296         private void PickersOrderSet()
297         {           
298             String locale = Environment.GetEnvironmentVariable("LC_TIME");
299             DateTimeFormatInfo DateFormat = new CultureInfo(locale, false ).DateTimeFormat;
300             String temp = DateFormat.ShortDatePattern;
301             String[] strArray = temp.Split(' ', '/');
302             foreach (String format in strArray) {
303                 if (format.IndexOf("M") != -1|| format.IndexOf("m") != -1)  Add(monthPicker);
304                 else if (format.IndexOf("d") != -1 || format.IndexOf("D") != -1) Add(dayPicker);
305                 else if (format.IndexOf("y") != -1 || format.IndexOf("Y") != -1) Add(yearPicker);
306             }
307         }
308
309         private void SetMonthText()
310         {
311             String locale = Environment.GetEnvironmentVariable("LC_TIME");
312             CultureInfo info = new CultureInfo(locale);
313             monthPicker.DisplayedValues = new ReadOnlyCollection<string>(info.DateTimeFormat.AbbreviatedMonthNames);
314         }
315     }
316 }