216acbe2b3549259b45b0f4def0cfc4eeb602481
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.Components / Controls / AlertDialog.cs
1 /*
2  * Copyright(c) 2021 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 using System;
19 using System.ComponentModel;
20 using System.Collections.Generic;
21 using Tizen.NUI.BaseComponents;
22
23 namespace Tizen.NUI.Components
24 {
25     /// <summary>
26     /// AlertDialog class shows a dialog with title, message and action buttons.
27     /// </summary>
28     /// <since_tizen> 9 </since_tizen>
29     public partial class AlertDialog : Control
30     {
31         private string title = null;
32         private string message = null;
33
34         private View titleContent = null;
35         private View content = null;
36         private View actionContent = null;
37         private IEnumerable<View> actionContentViews = null;
38
39         private View defaultTitleContent = null;
40         private View defaultContent = null;
41         private View defaultActionContent = null;
42         // FIXME: Now AlertDialog.Padding Top and Bottom increases AlertDialog size incorrectly.
43         //        Until the bug is fixed, padding view is added after action content.
44         private View defaultActionContentPadding = null;
45
46         private bool styleApplied = false;
47
48         /// <summary>
49         /// Creates a new instance of AlertDialog.
50         /// </summary>
51         /// <since_tizen> 9 </since_tizen>
52         public AlertDialog() : base()
53         {
54             Initialize();
55         }
56
57         /// <summary>
58         /// Creates a new instance of AlertDialog.
59         /// </summary>
60         /// <param name="style">Creates AlertDialog by special style defined in UX.</param>
61         /// <since_tizen> 9 </since_tizen>
62         public AlertDialog(string style) : base(style)
63         {
64             Initialize();
65         }
66
67         /// <summary>
68         /// Creates a new instance of AlertDialog.
69         /// </summary>
70         /// <param name="alertDialogStyle">Creates AlertDialog by style customized by user.</param>
71         /// <since_tizen> 9 </since_tizen>
72         public AlertDialog(AlertDialogStyle alertDialogStyle) : base(alertDialogStyle)
73         {
74             Initialize();
75         }
76
77         /// <inheritdoc/>
78         [EditorBrowsable(EditorBrowsableState.Never)]
79         protected override void Dispose(DisposeTypes type)
80         {
81             if (disposed)
82             {
83                 return;
84             }
85
86             if (type == DisposeTypes.Explicit)
87             {
88                 if (titleContent != null)
89                 {
90                     Utility.Dispose(titleContent);
91                 }
92
93                 if (content != null)
94                 {
95                     Utility.Dispose(content);
96                 }
97
98                 if (actionContent != null)
99                 {
100                     Utility.Dispose(actionContent);
101                 }
102
103                 // FIXME: Now AlertDialog.Padding Top and Bottom increases AlertDialog size incorrectly.
104                 //        Until the bug is fixed, padding view is added after action content.
105                 if (defaultActionContentPadding != null)
106                 {
107                     Utility.Dispose(defaultActionContentPadding);
108                 }
109             }
110
111             base.Dispose(type);
112         }
113
114         /// <summary>
115         /// Applies style to AlertDialog.
116         /// </summary>
117         /// <param name="viewStyle">The style to apply.</param>
118         /// <since_tizen> 9 </since_tizen>
119         public override void ApplyStyle(ViewStyle viewStyle)
120         {
121             styleApplied = false;
122
123             base.ApplyStyle(viewStyle);
124
125             var alertDialogStyle = viewStyle as AlertDialogStyle;
126
127             if (alertDialogStyle == null)
128             {
129                 return;
130             }
131
132             // Apply Title style.
133             if ((alertDialogStyle.TitleTextLabel != null) && (DefaultTitleContent is TextLabel))
134             {
135                 ((TextLabel)DefaultTitleContent)?.ApplyStyle(alertDialogStyle.TitleTextLabel);
136             }
137
138             // Apply Message style.
139             if ((alertDialogStyle.MessageTextLabel != null) && (DefaultContent is TextLabel))
140             {
141                 ((TextLabel)DefaultContent)?.ApplyStyle(alertDialogStyle.MessageTextLabel);
142             }
143
144             // Apply ActionContent style.
145             if (alertDialogStyle.ActionContent != null)
146             {
147                 DefaultActionContent?.ApplyStyle(alertDialogStyle.ActionContent);
148             }
149
150             styleApplied = true;
151
152             // Calculate dialog position and children's positions based on padding sizes.
153             CalculatePosition();
154         }
155
156         /// <summary>
157         /// Title text of AlertDialog.
158         /// Title text is set to TitleContent's Text if TitleContent is TextLabel.
159         /// If TitleContent's Text is set manually by user, then it is not guaranteed that TitleContent's Text is the same with Title text.
160         /// </summary>
161         /// <since_tizen> 9 </since_tizen>
162         public string Title
163         {
164             get
165             {
166                 return GetValue(TitleProperty) as string;
167             }
168             set
169             {
170                 SetValue(TitleProperty, value);
171                 NotifyPropertyChanged();
172             }
173         }
174         private string InternalTitle
175         {
176             get
177             {
178                 return title;
179             }
180             set
181             {
182                 if (title == value)
183                 {
184                     return;
185                 }
186
187                 title = value;
188
189                 if (TitleContent is TextLabel textLabel)
190                 {
191                     textLabel.Text = title;
192                 }
193             }
194         }
195
196         /// <summary>
197         /// Title content of AlertDialog.
198         /// TitleContent is added as a child of AlertDialog automatically.
199         /// Title text is set to TitleContent's Text if TitleContent is TextLabel.
200         /// If TitleContent's Text is set manually by user, then it is not guaranteed that TitleContent's Text is the same with Title text.
201         /// </summary>
202         /// <since_tizen> 9 </since_tizen>
203         public View TitleContent
204         {
205             get
206             {
207                 return GetValue(TitleContentProperty) as View;
208             }
209             set
210             {
211                 SetValue(TitleContentProperty, value);
212                 NotifyPropertyChanged();
213             }
214         }
215         private View InternalTitleContent
216         {
217             get
218             {
219                 return titleContent;
220             }
221             set
222             {
223                 if (titleContent == value)
224                 {
225                     return;
226                 }
227
228                 if (titleContent != null)
229                 {
230                     Remove(titleContent);
231                 }
232
233                 titleContent = value;
234                 if (titleContent == null)
235                 {
236                     return;
237                 }
238
239                 if (titleContent is TextLabel textLabel)
240                 {
241                     textLabel.Text = Title;
242                 }
243
244                 ResetContent();
245             }
246         }
247
248         /// <summary>
249         /// Message text of AlertDialog.
250         /// Message text is set to Content's Text if Content is TextLabel.
251         /// If Content's Text is set manually by user, then it is not guaranteed that Content's Text is the same with Message text.
252         /// </summary>
253         /// <since_tizen> 9 </since_tizen>
254         public string Message
255         {
256             get
257             {
258                 return GetValue(MessageProperty) as string;
259             }
260             set
261             {
262                 SetValue(MessageProperty, value);
263                 NotifyPropertyChanged();
264             }
265         }
266         private string InternalMessage
267         {
268             get
269             {
270                 return message;
271             }
272             set
273             {
274                 if (message == value)
275                 {
276                     return;
277                 }
278
279                 message = value;
280
281                 if (Content is TextLabel textLabel)
282                 {
283                     textLabel.Text = message;
284                 }
285             }
286         }
287
288         /// <summary>
289         /// Content of AlertDialog.
290         /// Content is added as a child of AlertDialog automatically.
291         /// Message text is set to Content's Text if Content is TextLabel.
292         /// If Content's Text is set manually by user, then it is not guaranteed that Content's Text is the same with Message text.
293         /// </summary>
294         /// <since_tizen> 9 </since_tizen>
295         public View Content
296         {
297             get
298             {
299                 return GetValue(ContentProperty) as View;
300             }
301             set
302             {
303                 SetValue(ContentProperty, value);
304                 NotifyPropertyChanged();
305             }
306         }
307         private View InternalContent
308         {
309             get
310             {
311                 return content;
312             }
313             set
314             {
315                 if (content == value)
316                 {
317                     return;
318                 }
319
320                 if (content != null)
321                 {
322                     Remove(content);
323                 }
324
325                 content = value;
326                 if (content == null)
327                 {
328                     return;
329                 }
330
331                 if (content is TextLabel textLabel)
332                 {
333                     textLabel.Text = message;
334                 }
335
336                 ResetContent();
337             }
338         }
339
340         /// <summary>
341         /// Action views of AlertDialog.
342         /// Action views are added as children of ActionContent.
343         /// When Actions are set, previous Actions are removed from ActionContent.
344         /// </summary>
345         /// <since_tizen> 9 </since_tizen>
346         public IEnumerable<View> Actions
347         {
348             get
349             {
350                 return actionContentViews;
351             }
352             set
353             {
354                 if (ActionContent == null)
355                 {
356                     actionContentViews = value;
357                     return;
358                 }
359
360                 if (actionContentViews != null)
361                 {
362                     foreach (var oldAction in actionContentViews)
363                     {
364                         if (ActionContent.Children?.Contains(oldAction) == true)
365                         {
366                             ActionContent.Children.Remove(oldAction);
367                         }
368                     }
369                 }
370
371                 actionContentViews = value;
372
373                 if (actionContentViews == null)
374                 {
375                     return;
376                 }
377
378                 foreach (var action in actionContentViews)
379                 {
380                     ActionContent.Add(action);
381                 }
382             }
383         }
384
385         /// <summary>
386         /// Action content of AlertDialog.
387         /// ActionContent is added as a child of AlertDialog automatically.
388         /// Actions are added as children of ActionContent.
389         /// </summary>
390         /// <since_tizen> 9 </since_tizen>
391         public View ActionContent
392         {
393             get
394             {
395                 return GetValue(ActionContentProperty) as View;
396             }
397             set
398             {
399                 SetValue(ActionContentProperty, value);
400                 NotifyPropertyChanged();
401             }
402         }
403         private View InternalActionContent
404         {
405              get
406              {
407                 return actionContent;
408              }
409              set
410              {
411                 if (actionContent == value)
412                  {
413                      return;
414                  }
415
416                 var oldActionContent = actionContent;
417                 actionContent = value;
418
419                 // Add views first before remove previous action content
420                 // not to cause Garbage Collector collects views.
421                 if ((actionContent != null) && (Actions != null))
422                 {
423                     foreach (var action in Actions)
424                     {
425                         actionContent.Add(action);
426                     }
427                 }
428
429                 if (oldActionContent != null)
430                 {
431                     Remove(oldActionContent);
432                 }
433
434                 if (actionContent == null)
435                 {
436                     return;
437                 }
438
439                 ResetContent();
440             }
441         }
442
443         /// <summary>
444         /// AccessibilityGetName.
445         /// </summary>
446         [EditorBrowsable(EditorBrowsableState.Never)]
447         protected override string AccessibilityGetName()
448         {
449             if (!String.IsNullOrEmpty(Title))
450             {
451                 return Title;
452             }
453             else
454             {
455                 return Message;
456             }
457         }
458
459         /// <summary>
460         /// Initialize AT-SPI object.
461         /// </summary>
462         [EditorBrowsable(EditorBrowsableState.Never)]
463         public override void OnInitialize()
464         {
465             base.OnInitialize();
466             SetAccessibilityConstructor(Role.Dialog);
467             AppendAccessibilityAttribute("sub-role", "Alert");
468             Show(); // calls AddPopup()
469         }
470
471         /// <summary>
472         /// Informs AT-SPI bridge about the set of AT-SPI states associated with this object.
473         /// </summary>
474         [EditorBrowsable(EditorBrowsableState.Never)]
475         protected override AccessibilityStates AccessibilityCalculateStates(ulong states)
476         {
477             var accessibilityStates = base.AccessibilityCalculateStates(states);
478             FlagSetter(ref accessibilityStates, AccessibilityStates.Modal, true);
479             return accessibilityStates;
480         }
481
482
483         /// <summary>
484         /// Default title content of AlertDialog.
485         /// If Title is set, then default title content is automatically displayed.
486         /// </summary>
487         [EditorBrowsable(EditorBrowsableState.Never)]
488         protected View DefaultTitleContent
489         {
490             get
491             {
492                 if (defaultTitleContent == null)
493                 {
494                     defaultTitleContent = CreateDefaultTitleContent();
495                 }
496
497                 return defaultTitleContent;
498             }
499         }
500
501         /// <summary>
502         /// Default content of AlertDialog.
503         /// If Message is set, then default content is automatically displayed.
504         /// </summary>
505         [EditorBrowsable(EditorBrowsableState.Never)]
506         protected View DefaultContent
507         {
508             get
509             {
510                 if (defaultContent == null)
511                 {
512                     defaultContent = CreateDefaultContent();
513                 }
514
515                 return defaultContent;
516             }
517         }
518
519         /// <summary>
520         /// Default action content of AlertDialog.
521         /// If Actions are set, then default action content is automatically displayed.
522         /// </summary>
523         [EditorBrowsable(EditorBrowsableState.Never)]
524         protected View DefaultActionContent
525         {
526             get
527             {
528                 if (defaultActionContent == null)
529                 {
530                     defaultActionContent = CreateDefaultActionContent();
531                 }
532
533                 // FIXME: Now AlertDialog.Padding Top and Bottom increases AlertDialog size incorrectly.
534                 //        Until the bug is fixed, padding view is added after action content.
535                 if (defaultActionContentPadding == null)
536                 {
537                     defaultActionContentPadding = CreateDefaultActionContentPadding();
538                 }
539
540                 return defaultActionContent;
541             }
542         }
543
544         private void Initialize()
545         {
546             Layout = new LinearLayout()
547             {
548                 LinearOrientation = LinearLayout.Orientation.Vertical,
549                 LinearAlignment = LinearLayout.Alignment.CenterHorizontal,
550             };
551
552             this.Relayout += OnRelayout;
553
554             TitleContent = DefaultTitleContent;
555
556             Content = DefaultContent;
557
558             ActionContent = DefaultActionContent;
559         }
560
561         private void ResetContent()
562         {
563             //To keep the order of TitleContent, Content and ActionContent,
564             //the existing contents are removed and added again.
565             if (titleContent != null)
566             {
567                 Remove(titleContent);
568             }
569
570             if (content != null)
571             {
572                 Remove(content);
573             }
574
575             if (actionContent != null)
576             {
577                 Remove(actionContent);
578             }
579
580             if (titleContent != null)
581             {
582                 Add(titleContent);
583             }
584
585             if (content != null)
586             {
587                 Add(content);
588             }
589
590             if (actionContent != null)
591             {
592                 Add(actionContent);
593
594                 // FIXME: Now AlertDialog.Padding Top and Bottom increases AlertDialog size incorrectly.
595                 //        Until the bug is fixed, padding view is added after action content.
596                 if (actionContent == defaultActionContent)
597                 {
598                     if (defaultActionContentPadding != null)
599                     {
600                         Add(defaultActionContentPadding);
601                     }
602                 }
603             }
604         }
605
606         private TextLabel CreateDefaultTitleContent()
607         {
608             return new TextLabel();
609         }
610
611         private TextLabel CreateDefaultContent()
612         {
613             return new TextLabel();
614         }
615
616         private View CreateDefaultActionContent()
617         {
618             return new View()
619             {
620                 Layout = new LinearLayout()
621                 {
622                     LinearOrientation = LinearLayout.Orientation.Horizontal,
623                     LinearAlignment = LinearLayout.Alignment.Center,
624                     // FIXME: This CellPadding value should be written in AlertDialogStyle.
625                     //        However, if this is called in other place, then flicking issue happens.
626                     CellPadding = new Size2D(80, 0),
627                 },
628             };
629         }
630
631         // FIXME: Now AlertDialog.Padding Top and Bottom increases AlertDialog size incorrectly.
632         //        Until the bug is fixed, padding view is added after action content.
633         private View CreateDefaultActionContentPadding()
634         {
635             var layout = Layout as LinearLayout;
636
637             if ((layout == null) || (defaultActionContent == null))
638             {
639                 return null;
640             }
641
642             View paddingView = null;
643
644             using (Size2D size = new Size2D(defaultActionContent.Size2D.Width, defaultActionContent.Size2D.Height))
645             {
646                 if (layout.LinearOrientation == LinearLayout.Orientation.Horizontal)
647                 {
648                     size.Width = 40;
649                 }
650                 else
651                 {
652                     size.Height = 40;
653                 }
654
655                 paddingView = new View()
656                 {
657                     Size2D = new Size2D(size.Width, size.Height),
658                 };
659             }
660
661             return paddingView;
662         }
663
664         private void OnRelayout(object sender, EventArgs e)
665         {
666             // Calculate dialog position and children's positions based on padding sizes.
667             CalculatePosition();
668         }
669
670         // Calculate dialog position and children's positions based on padding sizes.
671         private void CalculatePosition()
672         {
673             if (styleApplied == false)
674             {
675                 return;
676             }
677
678             var size = Size2D;
679             var parent = GetParent();
680             Size2D parentSize;
681
682             if ((parent != null) && (parent is View))
683             {
684                 parentSize = ((View)parent).Size;
685             }
686             else
687             {
688                 parentSize = NUIApplication.GetDefaultWindow().Size;
689             }
690
691             Position2D = new Position2D((parentSize.Width - size.Width) / 2, (parentSize.Height - size.Height) / 2);
692         }
693     }
694 }