[NUI] Fix AlertDialog not to use ViewStyle. (#3268)
[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 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 title;
167             }
168             set
169             {
170                 if (title == value)
171                 {
172                     return;
173                 }
174
175                 title = value;
176
177                 if (TitleContent is TextLabel textLabel)
178                 {
179                     textLabel.Text = title;
180                 }
181             }
182         }
183
184         /// <summary>
185         /// Title content of AlertDialog.
186         /// TitleContent is added as a child of AlertDialog automatically.
187         /// Title text is set to TitleContent's Text if TitleContent is TextLabel.
188         /// If TitleContent's Text is set manually by user, then it is not guaranteed that TitleContent's Text is the same with Title text.
189         /// </summary>
190         /// <since_tizen> 9 </since_tizen>
191         public View TitleContent
192         {
193             get
194             {
195                 return titleContent;
196             }
197             set
198             {
199                 if (titleContent == value)
200                 {
201                     return;
202                 }
203
204                 if (titleContent != null)
205                 {
206                     Remove(titleContent);
207                 }
208
209                 titleContent = value;
210                 if (titleContent == null)
211                 {
212                     return;
213                 }
214
215                 if (titleContent is TextLabel textLabel)
216                 {
217                     textLabel.Text = Title;
218                 }
219
220                 ResetContent();
221             }
222         }
223
224         /// <summary>
225         /// Message text of AlertDialog.
226         /// Message text is set to Content's Text if Content is TextLabel.
227         /// If Content's Text is set manually by user, then it is not guaranteed that Content's Text is the same with Message text.
228         /// </summary>
229         /// <since_tizen> 9 </since_tizen>
230         public string Message
231         {
232             get
233             {
234                 return message;
235             }
236             set
237             {
238                 if (message == value)
239                 {
240                     return;
241                 }
242
243                 message = value;
244
245                 if (Content is TextLabel textLabel)
246                 {
247                     textLabel.Text = message;
248                 }
249             }
250         }
251
252         /// <summary>
253         /// Content of AlertDialog.
254         /// Content is added as a child of AlertDialog automatically.
255         /// Message text is set to Content's Text if Content is TextLabel.
256         /// If Content's Text is set manually by user, then it is not guaranteed that Content's Text is the same with Message text.
257         /// </summary>
258         /// <since_tizen> 9 </since_tizen>
259         public View Content
260         {
261             get
262             {
263                 return content;
264             }
265             set
266             {
267                 if (content == value)
268                 {
269                     return;
270                 }
271
272                 if (content != null)
273                 {
274                     Remove(content);
275                 }
276
277                 content = value;
278                 if (content == null)
279                 {
280                     return;
281                 }
282
283                 if (content is TextLabel textLabel)
284                 {
285                     textLabel.Text = message;
286                 }
287
288                 ResetContent();
289             }
290         }
291
292         /// <summary>
293         /// Action views of AlertDialog.
294         /// Action views are added as children of ActionContent.
295         /// When Actions are set, previous Actions are removed from ActionContent.
296         /// </summary>
297         /// <since_tizen> 9 </since_tizen>
298         public IEnumerable<View> Actions
299         {
300             get
301             {
302                 return actionContentViews;
303             }
304             set
305             {
306                 if (ActionContent == null)
307                 {
308                     actionContentViews = value;
309                     return;
310                 }
311
312                 if (actionContentViews != null)
313                 {
314                     foreach (var oldAction in actionContentViews)
315                     {
316                         if (ActionContent.Children?.Contains(oldAction) == true)
317                         {
318                             ActionContent.Children.Remove(oldAction);
319                         }
320                     }
321                 }
322
323                 actionContentViews = value;
324
325                 if (actionContentViews == null)
326                 {
327                     return;
328                 }
329
330                 foreach (var action in actionContentViews)
331                 {
332                     ActionContent.Add(action);
333                 }
334             }
335         }
336
337         /// <summary>
338         /// Action content of AlertDialog.
339         /// ActionContent is added as a child of AlertDialog automatically.
340         /// Actions are added as children of ActionContent.
341         /// </summary>
342         /// <since_tizen> 9 </since_tizen>
343         public View ActionContent
344         {
345              get
346              {
347                 return actionContent;
348              }
349              set
350              {
351                 if (actionContent == value)
352                  {
353                      return;
354                  }
355
356                 var oldActionContent = actionContent;
357                 actionContent = value;
358
359                 // Add views first before remove previous action content
360                 // not to cause Garbage Collector collects views.
361                 if ((actionContent != null) && (Actions != null))
362                 {
363                     foreach (var action in Actions)
364                     {
365                         actionContent.Add(action);
366                     }
367                 }
368
369                 if (oldActionContent != null)
370                 {
371                     Remove(oldActionContent);
372                 }
373
374                 if (actionContent == null)
375                 {
376                     return;
377                 }
378
379                 ResetContent();
380             }
381         }
382
383         /// <summary>
384         /// AccessibilityGetName.
385         /// </summary>
386         [EditorBrowsable(EditorBrowsableState.Never)]
387         protected override string AccessibilityGetName()
388         {
389             if (!String.IsNullOrEmpty(Title))
390             {
391                 return Title;
392             }
393             else
394             {
395                 return Message;
396             }
397         }
398
399         /// <summary>
400         /// Initialize AT-SPI object.
401         /// </summary>
402         [EditorBrowsable(EditorBrowsableState.Never)]
403         public override void OnInitialize()
404         {
405             base.OnInitialize();
406             SetAccessibilityConstructor(Role.Dialog);
407             AppendAccessibilityAttribute("sub-role", "Alert");
408             Show(); // calls AddPopup()
409         }
410
411         /// <summary>
412         /// Informs AT-SPI bridge about the set of AT-SPI states associated with this object.
413         /// </summary>
414         [EditorBrowsable(EditorBrowsableState.Never)]
415         protected override AccessibilityStates AccessibilityCalculateStates()
416         {
417             var states = base.AccessibilityCalculateStates();
418             FlagSetter(ref states, AccessibilityStates.Modal, true);
419             return states;
420         }
421
422
423         /// <summary>
424         /// Default title content of AlertDialog.
425         /// If Title is set, then default title content is automatically displayed.
426         /// </summary>
427         [EditorBrowsable(EditorBrowsableState.Never)]
428         protected View DefaultTitleContent
429         {
430             get
431             {
432                 if (defaultTitleContent == null)
433                 {
434                     defaultTitleContent = CreateDefaultTitleContent();
435                 }
436
437                 return defaultTitleContent;
438             }
439         }
440
441         /// <summary>
442         /// Default content of AlertDialog.
443         /// If Message is set, then default content is automatically displayed.
444         /// </summary>
445         [EditorBrowsable(EditorBrowsableState.Never)]
446         protected View DefaultContent
447         {
448             get
449             {
450                 if (defaultContent == null)
451                 {
452                     defaultContent = CreateDefaultContent();
453                 }
454
455                 return defaultContent;
456             }
457         }
458
459         /// <summary>
460         /// Default action content of AlertDialog.
461         /// If Actions are set, then default action content is automatically displayed.
462         /// </summary>
463         [EditorBrowsable(EditorBrowsableState.Never)]
464         protected View DefaultActionContent
465         {
466             get
467             {
468                 if (defaultActionContent == null)
469                 {
470                     defaultActionContent = CreateDefaultActionContent();
471                 }
472
473                 // FIXME: Now AlertDialog.Padding Top and Bottom increases AlertDialog size incorrectly.
474                 //        Until the bug is fixed, padding view is added after action content.
475                 if (defaultActionContentPadding == null)
476                 {
477                     defaultActionContentPadding = CreateDefaultActionContentPadding();
478                 }
479
480                 return defaultActionContent;
481             }
482         }
483
484         private void Initialize()
485         {
486             Layout = new LinearLayout()
487             {
488                 LinearOrientation = LinearLayout.Orientation.Vertical,
489                 LinearAlignment = LinearLayout.Alignment.CenterHorizontal,
490             };
491
492             this.Relayout += OnRelayout;
493
494             TitleContent = DefaultTitleContent;
495
496             Content = DefaultContent;
497
498             ActionContent = DefaultActionContent;
499         }
500
501         private void ResetContent()
502         {
503             //To keep the order of TitleContent, Content and ActionContent,
504             //the existing contents are removed and added again.
505             if (titleContent != null)
506             {
507                 Remove(titleContent);
508             }
509
510             if (content != null)
511             {
512                 Remove(content);
513             }
514
515             if (actionContent != null)
516             {
517                 Remove(actionContent);
518             }
519
520             if (titleContent != null)
521             {
522                 Add(titleContent);
523             }
524
525             if (content != null)
526             {
527                 Add(content);
528             }
529
530             if (actionContent != null)
531             {
532                 Add(actionContent);
533
534                 // FIXME: Now AlertDialog.Padding Top and Bottom increases AlertDialog size incorrectly.
535                 //        Until the bug is fixed, padding view is added after action content.
536                 if (actionContent == defaultActionContent)
537                 {
538                     if (defaultActionContentPadding != null)
539                     {
540                         Add(defaultActionContentPadding);
541                     }
542                 }
543             }
544         }
545
546         private TextLabel CreateDefaultTitleContent()
547         {
548             return new TextLabel();
549         }
550
551         private TextLabel CreateDefaultContent()
552         {
553             return new TextLabel();
554         }
555
556         private View CreateDefaultActionContent()
557         {
558             return new View()
559             {
560                 Layout = new LinearLayout()
561                 {
562                     LinearOrientation = LinearLayout.Orientation.Horizontal,
563                     LinearAlignment = LinearLayout.Alignment.Center,
564                     // FIXME: This CellPadding value should be written in AlertDialogStyle.
565                     //        However, if this is called in other place, then flicking issue happens.
566                     CellPadding = new Size2D(80, 0),
567                 },
568             };
569         }
570
571         // FIXME: Now AlertDialog.Padding Top and Bottom increases AlertDialog size incorrectly.
572         //        Until the bug is fixed, padding view is added after action content.
573         private View CreateDefaultActionContentPadding()
574         {
575             var layout = Layout as LinearLayout;
576
577             if ((layout == null) || (defaultActionContent == null))
578             {
579                 return null;
580             }
581
582             View paddingView = null;
583
584             using (Size2D size = new Size2D(defaultActionContent.Size2D.Width, defaultActionContent.Size2D.Height))
585             {
586                 if (layout.LinearOrientation == LinearLayout.Orientation.Horizontal)
587                 {
588                     size.Width = 40;
589                 }
590                 else
591                 {
592                     size.Height = 40;
593                 }
594
595                 paddingView = new View()
596                 {
597                     Size2D = new Size2D(size.Width, size.Height),
598                 };
599             }
600
601             return paddingView;
602         }
603
604         private void OnRelayout(object sender, EventArgs e)
605         {
606             // Calculate dialog position and children's positions based on padding sizes.
607             CalculatePosition();
608         }
609
610         // Calculate dialog position and children's positions based on padding sizes.
611         private void CalculatePosition()
612         {
613             if (styleApplied == false)
614             {
615                 return;
616             }
617
618             var size = Size2D;
619             var parent = GetParent();
620             Size2D parentSize;
621
622             if ((parent != null) && (parent is View))
623             {
624                 parentSize = ((View)parent).Size;
625             }
626             else
627             {
628                 parentSize = NUIApplication.GetDefaultWindow().Size;
629             }
630
631             Position2D = new Position2D((parentSize.Width - size.Width) / 2, (parentSize.Height - size.Height) / 2);
632         }
633     }
634 }