[NUI] Change Button, TimePicker and etc 's Focusable property as true
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.Components / Controls / Navigation / AppBar.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.ComponentModel;
19 using System.Collections.Generic;
20 using Tizen.NUI.BaseComponents;
21
22 namespace Tizen.NUI.Components
23 {
24     /// <summary>
25     /// The AppBar class is a class which shows title text and provides navigation
26     /// and action functions on Page.
27     /// </summary>
28     /// <since_tizen> 9 </since_tizen>
29     public partial class AppBar : Control
30     {
31         private bool autoNavigationContent = true;
32
33         private string title = null;
34
35         private View navigationContent = null;
36         private View titleContent = null;
37         private View actionContent = null;
38         private IEnumerable<View> actionContentViews = null;
39
40         private View defaultNavigationContent = null;
41         private View defaultTitleContent = null;
42         private View defaultActionContent = null;
43
44         private Extents navigationPadding;
45         private Extents actionPadding;
46         private ViewStyle actionViewStyle;
47         private ButtonStyle actionButtonStyle;
48
49         private bool styleApplied = false;
50
51         /// <summary>
52         /// Creates a new instance of AppBar.
53         /// </summary>
54         /// <since_tizen> 9 </since_tizen>
55         public AppBar() : base()
56         {
57             Initialize();
58             //to use GetNextFocusableView
59             SetKeyboardNavigationSupport(true);
60         }
61
62         /// <summary>
63         /// Creates a new instance of AppBar.
64         /// </summary>
65         /// <param name="style">Creates AppBar by special style defined in UX.</param>
66         /// <since_tizen> 9 </since_tizen>
67         public AppBar(string style) : base(style)
68         {
69             Initialize();
70             //to use GetNextFocusableView
71             SetKeyboardNavigationSupport(true);
72         }
73
74         /// <summary>
75         /// Creates a new instance of AppBar.
76         /// </summary>
77         /// <param name="appBarStyle">Creates AppBar by style customized by user.</param>
78         /// <since_tizen> 9 </since_tizen>
79         public AppBar(AppBarStyle appBarStyle) : base(appBarStyle)
80         {
81             Initialize();
82             //to use GetNextFocusableView
83             SetKeyboardNavigationSupport(true);
84         }
85
86         /// <summary>
87         /// Disposes AppBar and all children on it.
88         /// </summary>
89         /// <param name="type">Dispose type.</param>
90         [EditorBrowsable(EditorBrowsableState.Never)]
91         protected override void Dispose(DisposeTypes type)
92         {
93             if (disposed)
94             {
95                 return;
96             }
97
98             if (type == DisposeTypes.Explicit)
99             {
100                 if (navigationContent == defaultNavigationContent)
101                 {
102                     if (navigationContent != null)
103                     {
104                         Utility.Dispose(navigationContent);
105                     }
106                 }
107                 else
108                 {
109                     if (navigationContent != null)
110                     {
111                         Utility.Dispose(navigationContent);
112                     }
113
114                     if (defaultNavigationContent != null)
115                     {
116                         Utility.Dispose(defaultNavigationContent);
117                     }
118                 }
119
120                 if (titleContent == defaultTitleContent)
121                 {
122                     if (titleContent != null)
123                     {
124                         Utility.Dispose(titleContent);
125                     }
126                 }
127                 else
128                 {
129                     if (titleContent != null)
130                     {
131                         Utility.Dispose(titleContent);
132                     }
133
134                     if (defaultTitleContent != null)
135                     {
136                         Utility.Dispose(defaultTitleContent);
137                     }
138                 }
139
140                 if (actionContent == defaultActionContent)
141                 {
142                     if (actionContent != null)
143                     {
144                         Utility.Dispose(actionContent);
145                     }
146                 }
147                 else
148                 {
149                     if (actionContent != null)
150                     {
151                         Utility.Dispose(actionContent);
152                     }
153
154                     if (defaultActionContent != null)
155                     {
156                         Utility.Dispose(defaultActionContent);
157                     }
158                 }
159             }
160
161             base.Dispose(type);
162         }
163
164         /// <summary>
165         /// Navigation content of AppBar.
166         /// NavigationContent is added as a child of AppBar automatically.
167         /// If AutoNavigationContent is set to be true and NavigationContent is not set,
168         /// then default navigation content is automatically displayed.
169         /// </summary>
170         /// <since_tizen> 9 </since_tizen>
171         public View NavigationContent
172         {
173             get
174             {
175                 return GetValue(NavigationContentProperty) as View;
176             }
177             set
178             {
179                 SetValue(NavigationContentProperty, value);
180                 NotifyPropertyChanged();
181             }
182         }
183         private View InternalNavigationContent
184         {
185             get
186             {
187                 return navigationContent;
188             }
189             set
190             {
191                 if (navigationContent == value)
192                 {
193                     return;
194                 }
195
196                 if (navigationContent != null)
197                 {
198                     Remove(navigationContent);
199                 }
200
201                 navigationContent = value;
202                 if (navigationContent == null)
203                 {
204                     return;
205                 }
206                 navigationContent.Focusable = true;
207                 ResetContent();
208             }
209         }
210
211         /// <summary>
212         /// Title text of AppBar.
213         /// Title sets title text to the default title content.
214         /// If TitleContent is not TextLabel, then Title does not set title text of the TitleContent.
215         /// </summary>
216         /// <since_tizen> 9 </since_tizen>
217         public string Title
218         {
219             get
220             {
221                 return GetValue(TitleProperty) as string;
222             }
223             set
224             {
225                 SetValue(TitleProperty, value);
226                 NotifyPropertyChanged();
227             }
228         }
229         private string InternalTitle
230         {
231             get
232             {
233                 return title;
234             }
235             set
236             {
237                 if (title == value)
238                 {
239                     return;
240                 }
241
242                 title = value;
243
244                 if (TitleContent is TextLabel textLabel)
245                 {
246                     textLabel.Text = title;
247                 }
248             }
249         }
250
251         /// <summary>
252         /// Title content of AppBar.
253         /// TitleContent is added as a child of AppBar automatically.
254         /// If TitleContent is not TextLabel, then Title does not set title text of the TitleContent.
255         /// </summary>
256         /// <since_tizen> 9 </since_tizen>
257         public View TitleContent
258         {
259             get
260             {
261                 return GetValue(TitleContentProperty) as View;
262             }
263             set
264             {
265                 SetValue(TitleContentProperty, value);
266                 NotifyPropertyChanged();
267             }
268         }
269         private View InternalTitleContent
270         {
271             get
272             {
273                 return titleContent;
274             }
275             set
276             {
277                 if (titleContent == value)
278                 {
279                     return;
280                 }
281
282                 if (titleContent != null)
283                 {
284                     Remove(titleContent);
285                 }
286
287                 titleContent = value;
288                 if (titleContent == null)
289                 {
290                     return;
291                 }
292
293                 if (titleContent is TextLabel textLabel)
294                 {
295                     textLabel.Text = Title;
296                 }
297                 else
298                 {
299                     titleContent.Focusable = true;
300                 }
301
302                 ResetContent();
303             }
304         }
305
306         /// <summary>
307         /// Action views of AppBar.
308         /// Action views are added to ActionContent of AppBar.
309         /// If action views and action buttons are set to Actions, then proper style look for action views and action buttons are automatically applied to action views and action buttons.
310         /// e.g. size, button icon color, etc.
311         /// If you do not want to apply framework's style look for action views and action buttons, then please use ActionContent.Add(actionView) instead of setting Actions.
312         /// </summary>
313         /// <since_tizen> 9 </since_tizen>
314         public IEnumerable<View> Actions
315         {
316             get
317             {
318                 return actionContentViews;
319             }
320             set
321             {
322                 if (ActionContent == null)
323                 {
324                     actionContentViews = value;
325                     return;
326                 }
327
328                 if (actionContentViews != null)
329                 {
330                     foreach (var oldAction in actionContentViews)
331                     {
332                         if (ActionContent.Children?.Contains(oldAction) == true)
333                         {
334                             ActionContent.Children.Remove(oldAction);
335                         }
336                     }
337                 }
338
339                 actionContentViews = value;
340
341                 if (actionContentViews == null)
342                 {
343                     return;
344                 }
345
346                 foreach (var action in actionContentViews)
347                 {
348                     // Apply Action and ActionButton styles.
349                     if ((action is Button) && (actionButtonStyle != null))
350                     {
351                         action.ApplyStyle(actionButtonStyle);
352                     }
353                     else if (actionViewStyle != null)
354                     {
355                         action.ApplyStyle(actionViewStyle);
356                     }
357
358                     ActionContent.Add(action);
359                 }
360             }
361         }
362
363         /// <summary>
364         /// Action content of AppBar.
365         /// ActionContent is added as a child of AppBar automatically.
366         /// Action content contains action views and action buttons by Actions.
367         /// The Action and ActionButton styles of AppBarStyle are applied to actions only by setting Actions.
368         /// </summary>
369         /// <since_tizen> 9 </since_tizen>
370         public View ActionContent
371         {
372             get
373             {
374                 return GetValue(ActionContentProperty) as View;
375             }
376             set
377             {
378                 SetValue(ActionContentProperty, value);
379                 NotifyPropertyChanged();
380             }
381         }
382         private View InternalActionContent
383         {
384             get
385             {
386                 return actionContent;
387             }
388             set
389             {
390                 if (actionContent == value)
391                 {
392                     return;
393                 }
394
395                 var oldActionContent = actionContent;
396                 actionContent = value;
397
398                 // Add views first before remove previous action content
399                 // not to cause Garbage Collector collects views.
400                 if ((actionContent != null) && (Actions != null))
401                 {
402                     foreach (var action in Actions)
403                     {
404                         // Apply Action and ActionButton styles.
405                         if ((action is Button) && (actionButtonStyle != null))
406                         {
407                             action.ApplyStyle(actionButtonStyle);
408                         }
409                         else if (actionViewStyle != null)
410                         {
411                             action.ApplyStyle(actionViewStyle);
412                         }
413
414                         actionContent.Add(action);
415                     }
416                 }
417
418                 if (oldActionContent != null)
419                 {
420                     Remove(oldActionContent);
421                 }
422
423                 if (actionContent == null)
424                 {
425                     return;
426                 }
427
428                 ResetContent();
429             }
430         }
431
432         /// <summary>
433         /// Flag to indicate if default navigation content is automatically set or not.
434         /// The default value is true.
435         /// If AutoNavigationContent is set to be true and NavigationContent is not set,
436         /// then default navigation content is automatically displayed.
437         /// If default navigation content is clicked, it calls navigator pop operation.
438         /// </summary>
439         /// <since_tizen> 9 </since_tizen>
440         public bool AutoNavigationContent
441         {
442             get
443             {
444                 return (bool)GetValue(AutoNavigationContentProperty);
445             }
446             set
447             {
448                 SetValue(AutoNavigationContentProperty, value);
449                 NotifyPropertyChanged();
450             }
451         }
452         private bool InternalAutoNavigationContent
453         {
454             get
455             {
456                 return autoNavigationContent;
457             }
458
459             set
460             {
461                 if (autoNavigationContent == value)
462                 {
463                     return;
464                 }
465
466                 autoNavigationContent = value;
467
468                 if (autoNavigationContent == true)
469                 {
470                     if (NavigationContent == null)
471                     {
472                         NavigationContent = DefaultNavigationContent;
473                     }
474                 }
475                 else if (NavigationContent == DefaultNavigationContent)
476                 {
477                     NavigationContent = null;
478                 }
479             }
480         }
481
482         /// <summary>
483         /// Default navigation content of AppBar set automatically by default.
484         /// If AutoNavigationContent is set to be true and NavigationContent is not set,
485         /// then default navigation content is automatically displayed.
486         /// If default navigation content is clicked, it calls navigator pop operation.
487         /// </summary>
488         [EditorBrowsable(EditorBrowsableState.Never)]
489         protected View DefaultNavigationContent
490         {
491             get
492             {
493                 // TODO: Do not set default navigation content if there is no previous page.
494                 if (defaultNavigationContent == null)
495                 {
496                     defaultNavigationContent = CreateDefaultNavigationContent();
497                 }
498
499                 return defaultNavigationContent;
500             }
501         }
502
503         /// <summary>
504         /// Default title content of AppBar set automatically by default.
505         /// If TitleContent is not set by user, then default title content is
506         /// automatically displayed.
507         /// </summary>
508         [EditorBrowsable(EditorBrowsableState.Never)]
509         protected View DefaultTitleContent
510         {
511             get
512             {
513                 if (defaultTitleContent == null)
514                 {
515                     defaultTitleContent = CreateDefaultTitleContent();
516                 }
517
518                 return defaultTitleContent;
519             }
520         }
521
522         /// <summary>
523         /// Default action content of AppBar set automatically by default.
524         /// If ActionContent is not set by user, then default action content is
525         /// automatically displayed.
526         /// </summary>
527         [EditorBrowsable(EditorBrowsableState.Never)]
528         protected View DefaultActionContent
529         {
530             get
531             {
532                 if (defaultActionContent == null)
533                 {
534                     defaultActionContent = CreateDefaultActionContent();
535                 }
536
537                 return defaultActionContent;
538             }
539         }
540
541         /// <inheritdoc/>
542         [EditorBrowsable(EditorBrowsableState.Never)]
543         public override void OnInitialize()
544         {
545             base.OnInitialize();
546
547             SetAccessibilityConstructor(Role.TitleBar);
548         }
549
550         /// <summary>
551         /// Applies style to AppBar.
552         /// </summary>
553         /// <param name="viewStyle">The style to apply.</param>
554         /// <since_tizen> 9 </since_tizen>
555         public override void ApplyStyle(ViewStyle viewStyle)
556         {
557             styleApplied = false;
558
559             base.ApplyStyle(viewStyle);
560
561             var appBarStyle = viewStyle as AppBarStyle;
562
563             if (appBarStyle == null)
564             {
565                 return;
566             }
567
568             if (appBarStyle.NavigationPadding != null)
569             {
570                 navigationPadding = new Extents(appBarStyle.NavigationPadding);
571             }
572
573             if (appBarStyle.ActionPadding != null)
574             {
575                 actionPadding = new Extents(appBarStyle.ActionPadding);
576             }
577
578             // Apply Back Button style.
579             if ((appBarStyle.BackButton != null) && (DefaultNavigationContent is Button button))
580             {
581                 button.ApplyStyle(appBarStyle.BackButton);
582             }
583
584             // Apply Title style.
585             if ((appBarStyle.TitleTextLabel != null) && (DefaultTitleContent is TextLabel textLabel))
586             {
587                 textLabel.ApplyStyle(appBarStyle.TitleTextLabel);
588             }
589
590             // Apply ActionCellPadding style.
591             if (DefaultActionContent?.Layout is LinearLayout linearLayout && appBarStyle.ActionCellPadding != null)
592             {
593                 linearLayout.CellPadding = new Size2D(appBarStyle.ActionCellPadding.Width, appBarStyle.ActionCellPadding.Height);
594             }
595
596             // Apply Action and ActionButton styles.
597             if (DefaultActionContent?.Children != null)
598             {
599                 foreach (var action in DefaultActionContent?.Children)
600                 {
601                     if ((action is Button) && (appBarStyle.ActionButton != null))
602                     {
603                         action.ApplyStyle(appBarStyle.ActionButton);
604                     }
605                     else if (appBarStyle.ActionView != null)
606                     {
607                         action.ApplyStyle(appBarStyle.ActionView);
608                     }
609                 }
610             }
611
612             if (actionButtonStyle == null) actionButtonStyle = (ButtonStyle)appBarStyle.ActionButton?.Clone();
613             else actionButtonStyle.MergeDirectly(appBarStyle.ActionButton);
614
615             if (actionViewStyle == null) actionViewStyle = (ViewStyle)appBarStyle.ActionView?.Clone();
616             else actionViewStyle.MergeDirectly(appBarStyle.ActionView);
617
618
619             styleApplied = true;
620
621             // Calculate children's positions based on padding sizes.
622             CalculatePosition();
623         }
624
625         /// <summary>
626         /// Gets AppBar style.
627         /// </summary>
628         /// <returns>The default AppBar style.</returns>
629         [EditorBrowsable(EditorBrowsableState.Never)]
630         protected override ViewStyle CreateViewStyle()
631         {
632             return new AppBarStyle();
633         }
634
635         private void Initialize()
636         {
637             // Navigation, Title and Action are located horizontally.
638             Layout = new LinearLayout()
639             {
640                 LinearOrientation = LinearLayout.Orientation.Horizontal,
641                 LinearAlignment = LinearLayout.Alignment.CenterVertical,
642             };
643
644             WidthSpecification = LayoutParamPolicies.MatchParent;
645
646             if (AutoNavigationContent == true)
647             {
648                 NavigationContent = DefaultNavigationContent;
649             }
650
651             TitleContent = DefaultTitleContent;
652
653             ActionContent = DefaultActionContent;
654         }
655
656         private View CreateDefaultNavigationContent()
657         {
658             var backButton = new Button();
659
660             backButton.Clicked += (object sender, ClickedEventArgs args) =>
661             {
662                 // The page of app bar is popped when default back button is clicked.
663                 var page = GetParent() as Page;
664                 if (page != null)
665                 {
666                     var navigator = page.GetParent() as Navigator;
667                     if (navigator != null)
668                     {
669                         navigator.Pop();
670                     }
671                 }
672             };
673
674             return backButton;
675         }
676
677         private View CreateDefaultTitleContent()
678         {
679             return new TextLabel()
680             {
681                 HeightSpecification = LayoutParamPolicies.MatchParent,
682             };
683         }
684
685         private View CreateDefaultActionContent()
686         {
687             return new View()
688             {
689                 Layout = new LinearLayout()
690                 {
691                     LinearOrientation = LinearLayout.Orientation.Horizontal,
692                     LinearAlignment = LinearLayout.Alignment.End,
693                 },
694                 WidthSpecification = LayoutParamPolicies.MatchParent,
695                 HeightSpecification = LayoutParamPolicies.MatchParent,
696             };
697         }
698
699         private void ResetContent()
700         {
701             // To keep the order of NavigationContent, TitleContent and ActionContent,
702             // the existing contents are removed and added again.
703             if ((navigationContent != null) && Children.Contains(navigationContent))
704             {
705                 Remove(navigationContent);
706             }
707
708             if ((titleContent != null) && Children.Contains(titleContent))
709             {
710                 Remove(titleContent);
711             }
712
713             if ((actionContent != null) && Children.Contains(actionContent))
714             {
715                 Remove(actionContent);
716             }
717
718             if (navigationContent != null)
719             {
720                 Add(navigationContent);
721             }
722
723             if (titleContent != null)
724             {
725                 Add(titleContent);
726             }
727
728             if (actionContent != null)
729             {
730                 Add(actionContent);
731             }
732
733             // Calculate children's positions based on padding sizes.
734             CalculatePosition();
735         }
736
737         private void CalculatePosition()
738         {
739             if (styleApplied == false)
740             {
741                 return;
742             }
743
744             // Apply NavigationPadding style.
745             if ((NavigationContent != null) && (navigationPadding != null))
746             {
747                 if (NavigationContent.Margin.NotEqualTo(navigationPadding))
748                 {
749                     NavigationContent.Margin.CopyFrom(navigationPadding);
750                 }
751             }
752
753             // Apply ActionPadding style.
754             if ((ActionContent != null) && (actionPadding != null))
755             {
756                 if (ActionContent.Margin.NotEqualTo(actionPadding))
757                 {
758                     ActionContent.Margin.CopyFrom(actionPadding);
759                 }
760             }
761         }
762
763         /// <summary>
764         /// ToDo : only key navigation is enabled, but value editing is not yet added. for example, after enter key and up/down key the value need be changed.
765         /// </summary>
766         /// <param name="currentFocusedView"></param>
767         /// <param name="direction"></param>
768         /// <param name="loopEnabled"></param>
769         /// <returns></returns>
770         [EditorBrowsable(EditorBrowsableState.Never)]
771         public override View GetNextFocusableView(View currentFocusedView, View.FocusDirection direction, bool loopEnabled)
772         {
773             if (currentFocusedView == null)
774             {
775                 if (navigationContent != null && navigationContent.Focusable)
776                 {
777                     return navigationContent;
778                 }
779                 else if (titleContent != null && titleContent.Focusable)
780                 {
781                     return titleContent;
782                 }
783                 else
784                 {
785                     return null;
786                 }
787             }
788             else if (currentFocusedView == this)
789             {
790                 return null;
791             }
792             else if (currentFocusedView == navigationContent)
793             {
794                 if (direction == View.FocusDirection.Up)
795                 {
796                     return null;
797                 }
798                 else if (direction == View.FocusDirection.Right)
799                 {
800                     if (titleContent != null && titleContent.Focusable)
801                     {
802                         return titleContent;
803                     }
804                     else
805                     {
806                         return null;
807                     }
808                 }
809                 else if (direction == View.FocusDirection.Down)
810                 {
811                     return null;
812                 }
813                 else if (direction == View.FocusDirection.Left)
814                 {
815                     return null;
816                 }
817             }
818             else if (currentFocusedView == titleContent)
819             {
820                 if (direction == View.FocusDirection.Up)
821                 {
822                     return null;
823                 }
824                 else if (direction == View.FocusDirection.Right)
825                 {
826                     return null;
827                 }
828                 else if (direction == View.FocusDirection.Down)
829                 {
830                     return null;
831                 }
832                 else if (direction == View.FocusDirection.Left)
833                 {
834                     if (navigationContent != null && navigationContent.Focusable)
835                     {
836                         return navigationContent;
837                     }
838                     else
839                     {
840                         return null;
841                     }
842                 }
843             }
844             return null;
845         }
846
847         /// <inheritdoc/>
848         [EditorBrowsable(EditorBrowsableState.Never)]
849         protected internal override View PassFocusableViewInsideIfNeeded()
850         {
851             if (navigationContent != null && navigationContent.Focusable)
852             {
853                 return navigationContent;
854             }
855             else if (titleContent != null && titleContent.Focusable)
856             {
857                 return titleContent;
858             }
859             return this;
860         }
861
862     }
863 }