[NUI] Fix ContentPageLayout to measure AppBar prior to Content
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.Components / Controls / Navigation / ContentPage.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 using System.ComponentModel;
18 using Tizen.NUI.BaseComponents;
19
20 namespace Tizen.NUI.Components
21 {
22     /// <summary>
23     /// The ContentPage class is a class which is a formatted full screen page.
24     /// ContentPage contains title app bar and content.
25     /// </summary>
26     /// <since_tizen> 9 </since_tizen>
27     public partial class ContentPage : Page
28     {
29         private AppBar appBar = null;
30         private View content = null;
31
32         /// <summary>
33         /// Creates a new instance of a ContentPage.
34         /// </summary>
35         /// <since_tizen> 9 </since_tizen>
36         public ContentPage() : base()
37         {
38             Layout = new ContentPageLayout();
39
40             // ContentPage matches to parent by default.
41             WidthSpecification = LayoutParamPolicies.MatchParent;
42             HeightSpecification = LayoutParamPolicies.MatchParent;
43         }
44
45         /// <summary>
46         /// Dispose ContentPage and all children on it.
47         /// </summary>
48         /// <param name="type">Dispose type.</param>
49         [EditorBrowsable(EditorBrowsableState.Never)]
50         protected override void Dispose(DisposeTypes type)
51         {
52             if (disposed)
53             {
54                 return;
55             }
56
57             if (type == DisposeTypes.Explicit)
58             {
59                 if (appBar != null)
60                 {
61                     Utility.Dispose(appBar);
62                 }
63
64                 if (content != null)
65                 {
66                     Utility.Dispose(content);
67                 }
68             }
69
70             base.Dispose(type);
71         }
72
73         /// <inheritdoc/>
74         [EditorBrowsable(EditorBrowsableState.Never)]
75         public override void OnInitialize()
76         {
77             base.OnInitialize();
78
79             AccessibilityRole = Role.PageTab;
80         }
81
82         /// <summary>
83         /// AppBar of ContentPage.
84         /// AppBar is added as a child of ContentPage automatically.
85         /// AppBar is positioned at the top of the Page.
86         /// </summary>
87         /// <since_tizen> 9 </since_tizen>
88         public AppBar AppBar
89         {
90             get
91             {
92                 return GetValue(AppBarProperty) as AppBar;
93             }
94             set
95             {
96                 SetValue(AppBarProperty, value);
97                 NotifyPropertyChanged();
98             }
99         }
100         private AppBar InternalAppBar
101         {
102             get
103             {
104                 return appBar;
105             }
106             set
107             {
108                 if (appBar == value)
109                 {
110                     return;
111                 }
112
113                 if (appBar != null)
114                 {
115                     Remove(appBar);
116                 }
117
118                 appBar = value;
119                 if (appBar == null)
120                 {
121                     return;
122                 }
123
124                 Add(appBar);
125             }
126         }
127
128         /// <summary>
129         /// Content of ContentPage.
130         /// Content is added as a child of ContentPage automatically.
131         /// Content is positioned below AppBar.
132         /// Content is resized to fill the full screen except AppBar.
133         /// </summary>
134         /// <since_tizen> 9 </since_tizen>
135         public View Content
136         {
137             get
138             {
139                 return GetValue(ContentProperty) as View;
140             }
141             set
142             {
143                 SetValue(ContentProperty, value);
144                 NotifyPropertyChanged();
145             }
146         }
147
148         /// <summary>
149         /// for the case of ContentPage, it sets key focus on AppBar's NavigationContent
150         /// </summary>
151         protected internal override void RestoreKeyFocus()
152         {
153             if (FocusManager.Instance.IsDefaultAlgorithmEnabled())
154             {
155                 if (LastFocusedView)
156                 {
157                     FocusManager.Instance.SetCurrentFocusView(LastFocusedView);
158                 }
159                 else
160                 {
161                     if (AppBar != null)
162                     {
163                         FocusManager.Instance.SetCurrentFocusView(AppBar.PassFocusableViewInsideIfNeeded());
164                     }
165                     else
166                     {
167                         FocusManager.Instance.ClearFocus();
168                     }
169                 }
170             }
171         }
172
173         private View InternalContent
174         {
175             get
176             {
177                 return content;
178             }
179             set
180             {
181                 if (content == value)
182                 {
183                     return;
184                 }
185
186                 if (content != null)
187                 {
188                     Remove(content);
189                 }
190
191                 content = value;
192                 if (content == null)
193                 {
194                     return;
195                 }
196
197                 Add(content);
198             }
199         }
200
201         private class ContentPageLayout : AbsoluteLayout
202         {
203             protected override void OnMeasure(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec)
204             {
205                 float maxWidth = SuggestedMinimumWidth.AsDecimal();
206                 float maxHeight = SuggestedMinimumHeight.AsDecimal();
207
208                 MeasuredSize.StateType childWidthState = MeasuredSize.StateType.MeasuredSizeOK;
209                 MeasuredSize.StateType childHeightState = MeasuredSize.StateType.MeasuredSizeOK;
210
211                 var appBar = (Owner as ContentPage)?.AppBar;
212                 var content = (Owner as ContentPage)?.Content;
213
214                 bool measureAppBarLayout = false;
215
216                 if ((appBar != null) && (appBar.Layout != null) && (LayoutChildren.Contains(appBar.Layout)) && appBar.Layout.SetPositionByLayout)
217                 {
218                     MeasureChildWithoutPadding(appBar.Layout, widthMeasureSpec, heightMeasureSpec);
219                     measureAppBarLayout = true;
220                 }
221
222                 foreach (var childLayout in LayoutChildren)
223                 {
224                     if (!childLayout.SetPositionByLayout)
225                     {
226                         continue;
227                     }
228
229                     if ((content != null) && (content == childLayout.Owner) && (content.HeightSpecification == LayoutParamPolicies.MatchParent) && measureAppBarLayout)
230                     {
231                         var contentSizeH = heightMeasureSpec.Size.AsDecimal() - Padding.Top - Padding.Bottom - content.Margin.Top - content.Margin.Bottom - (appBar?.Layout.MeasuredHeight.Size.AsDecimal() ?? 0);
232                         MeasureSpecification contentHeightSpec = new MeasureSpecification(new LayoutLength(contentSizeH), MeasureSpecification.ModeType.Exactly);
233                         MeasureChildWithoutPadding(childLayout, widthMeasureSpec, contentHeightSpec);
234                     }
235                     else if (!measureAppBarLayout || (appBar != childLayout.Owner)) // if childLayout is not appBar.Layout
236                     {
237                         MeasureChildWithoutPadding(childLayout, widthMeasureSpec, heightMeasureSpec);
238                     }
239
240                     float childRight = childLayout.MeasuredWidth.Size.AsDecimal() + childLayout.Owner.PositionX;
241                     float childBottom = childLayout.MeasuredHeight.Size.AsDecimal() + childLayout.Owner.PositionY;
242
243                     if (maxWidth < childRight)
244                         maxWidth = childRight;
245
246                     if (maxHeight < childBottom)
247                         maxHeight = childBottom;
248
249                     if (childLayout.MeasuredWidth.State == MeasuredSize.StateType.MeasuredSizeTooSmall)
250                     {
251                         childWidthState = MeasuredSize.StateType.MeasuredSizeTooSmall;
252                     }
253                     if (childLayout.MeasuredHeight.State == MeasuredSize.StateType.MeasuredSizeTooSmall)
254                     {
255                         childHeightState = MeasuredSize.StateType.MeasuredSizeTooSmall;
256                     }
257                 }
258
259                 SetMeasuredDimensions(ResolveSizeAndState(new LayoutLength(maxWidth), widthMeasureSpec, childWidthState),
260                                       ResolveSizeAndState(new LayoutLength(maxHeight), heightMeasureSpec, childHeightState));
261             }
262
263             protected override void OnLayout(bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
264             {
265                 foreach (var childLayout in LayoutChildren)
266                 {
267                     if (!childLayout.SetPositionByLayout)
268                     {
269                         continue;
270                     }
271
272                     LayoutLength childWidth = childLayout.MeasuredWidth.Size;
273                     LayoutLength childHeight = childLayout.MeasuredHeight.Size;
274
275                     LayoutLength childLeft = new LayoutLength(childLayout.Owner.PositionX);
276                     LayoutLength childTop = new LayoutLength(childLayout.Owner.PositionY);
277
278                     var appBar = (Owner as ContentPage)?.AppBar;
279                     var content = (Owner as ContentPage)?.Content;
280
281                     if ((content != null) && (content == childLayout.Owner))
282                     {
283                         childTop = new LayoutLength(Padding.Top + content.Margin.Top + (appBar?.Layout.MeasuredHeight.Size.AsDecimal() ?? 0));
284                         childLayout.Layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight, false);
285                     }
286                     else
287                     {
288                         childLayout.Layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight, true);
289                     }
290                 }
291             }
292         }
293     }
294 }