397a4046699e4624b50ebe0ba2b125965397986f
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / internal / Layouting / LayoutController.cs
1 /*
2  * Copyright (c) 2019 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;
20 using Tizen.NUI.BaseComponents;
21 using System.Runtime.InteropServices;
22
23 namespace Tizen.NUI
24 {
25     /// <summary>
26     /// [Draft] The class that initiates the Measuring and Layouting of the tree,
27     ///         It sets a callback that becomes the entry point into the C# Layouting.
28     /// </summary>
29     internal class LayoutController : global::System.IDisposable
30     {
31         private global::System.Runtime.InteropServices.HandleRef unmanagedLayoutController;
32
33         [UnmanagedFunctionPointer(CallingConvention.StdCall)]
34         internal delegate void Callback(int id);
35
36         event Callback mInstance;
37
38         //A Flat to check if it is already disposed.
39         protected bool disposed = false;
40
41         private Window _window;
42         /// <summary>
43         /// Constructs a LayoutController which controls the measuring and layouting.<br />
44         /// <param name="window">Window attach this LayoutController to.</param>
45         /// </summary>
46         public LayoutController(Window window)
47         {
48             global::System.IntPtr cPtr = Interop.LayoutController.LayoutController_New();
49             _window = window;
50             // Wrap cPtr in a managed handle.
51             unmanagedLayoutController = new global::System.Runtime.InteropServices.HandleRef(this, cPtr);
52
53             mInstance = new Callback(Process);
54             Interop.LayoutController.LayoutController_SetCallback(unmanagedLayoutController, mInstance);
55         }
56
57         /// <summary>
58         /// Get the unique id of the LayoutController
59         /// </summary>
60         public int GetId()
61         {
62             return Interop.LayoutController.LayoutController_GetId(unmanagedLayoutController);
63         }
64
65         /// <summary>
66         /// Request relayout of a LayoutItem and it's parent tree.
67         /// </summary>
68         /// <param name="layoutItem">LayoutItem that is required to be laid out again.</param>
69         public void RequestLayout(LayoutItem layoutItem)
70         {
71             // Go up the tree and mark all parents to relayout
72             ILayoutParent layoutParent = layoutItem.GetParent();
73             if (layoutParent != null)
74             {
75                  LayoutGroup layoutGroup =  layoutParent as LayoutGroup;
76                  if(! layoutGroup.LayoutRequested)
77                  {
78                     layoutGroup.RequestLayout();
79                  }
80             }
81         }
82
83
84         /// <summary>
85         /// Destructor which adds LayoutController to the Dispose queue.
86         /// </summary>
87         ~LayoutController()
88         {
89             Dispose(DisposeTypes.Explicit);
90         }
91
92         /// <summary>
93         /// Explict Dispose.
94         /// </summary>
95         public void Dispose()
96         {
97            Dispose(DisposeTypes.Explicit);
98            System.GC.SuppressFinalize(this);
99         }
100
101         /// <summary>
102         /// Dispose Explict or Implicit
103         /// </summary>
104         protected virtual void Dispose(DisposeTypes type)
105         {
106             if (disposed)
107             {
108                 return;
109             }
110
111             if (type == DisposeTypes.Explicit)
112             {
113                 //Called by User code
114                 //Release your own managed resources here.
115                 //You should release all of your own disposable objects here.
116             }
117
118             //Release your own unmanaged resources here.
119             //You should not access any managed member here except static instance.
120             //because the execution order of Finalizes is non-deterministic.
121
122             if (unmanagedLayoutController.Handle != global::System.IntPtr.Zero)
123             {
124                 Interop.LayoutController.delete_LayoutController(unmanagedLayoutController);
125                 unmanagedLayoutController = new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero);
126             }
127
128             disposed = true;
129         }
130
131         // Traverse through children from the provided root.
132         // If a child has no layout but is a pure View then assign a default layout and continue traversal.
133         // If child has no layout and not a pure View then assign a LayoutItem that terminates the layouting (leaf),
134         private void AutomaticallyAssignLayouts(View rootNode)
135         {
136             for (uint i = 0; i < rootNode.ChildCount; i++)
137             {
138                 View view = rootNode.GetChildAt(i);
139                 if (rootNode.Layout == null )
140                 {
141                     if (rootNode.GetType() == typeof(View))
142                     {
143                         rootNode.Layout = new LayoutGroup();
144                         AutomaticallyAssignLayouts(rootNode);
145                     }
146                     else
147                     {
148                         rootNode.Layout = new LayoutItem();
149                     }
150                 }
151             }
152         }
153
154         // Traverse the tree looking for a root node that is a layout.
155         // Once found, it's children can be assigned Layouts and the Measure process started.
156         private void FindRootLayouts(View rootNode)
157         {
158             if (rootNode.Layout != null)
159             {
160                 Log.Info("NUI", "Found root:" + rootNode.Name + "\n");
161                 // rootNode has a layout, ensure all children have default layouts or layout items.
162                 AutomaticallyAssignLayouts(rootNode);
163                 // rootNode has a layout, start measuring and layouting from here.
164                 MeasureAndLayout(rootNode);
165             }
166             else
167             {
168                 // Search children of supplied node for a layout.
169                 for (uint i = 0; i < rootNode.ChildCount; i++)
170                 {
171                     View view = rootNode.GetChildAt(i);
172                     FindRootLayouts(view);
173                 }
174             }
175         }
176
177         // Starts of the actual measuring and layouting from the given root node.
178         // Can be called from multiple starting roots but in series.
179         void MeasureAndLayout(View root)
180         {
181             if (root !=null)
182             {
183                 // Get parent MeasureSpecification, this could be the Window or View with an exact size.
184                 Container parentNode = root.GetParent();
185                 Size2D rootSize;
186                 Position2D rootPosition = root.Position2D;
187                 View parentView = parentNode as View;
188                 if (parentView)
189                 {
190                     // Get parent View's Size.  If using Legacy size negotiation then should have been set already.
191                     rootSize = new Size2D(parentView.Size2D.Width, parentView.Size2D.Height);
192                 }
193                 else
194                 {
195                     // Parent not a View so assume it's a Layer which is the size of the window.
196                     rootSize = new Size2D(_window.Size.Width, _window.Size.Height);
197                 }
198
199                 // Determine measure specification for root.
200                 // The root layout policy could be an exact size, be match parent or wrap children.
201                 // If wrap children then at most it can be the root parent size.
202                 // If match parent then should be root parent size.
203                 // If exact then should be that size limited by the root parent size.
204
205                 LayoutLength width = new LayoutLength(rootSize.Width);
206                 LayoutLength height = new LayoutLength(rootSize.Height);
207                 MeasureSpecification.ModeType widthMode = MeasureSpecification.ModeType.AtMost;
208                 MeasureSpecification.ModeType heightMode = MeasureSpecification.ModeType.AtMost;
209
210                 if (root.WidthSpecification >= 0 )
211                 {
212                     // exact size provided so match width exactly
213                     width = new LayoutLength(root.WidthSpecification);
214                     widthMode = MeasureSpecification.ModeType.Exactly;
215                 }
216                 else if (root.WidthSpecification == LayoutParamPolicies.MatchParent)
217                 {
218                     widthMode = MeasureSpecification.ModeType.Exactly;
219                 }
220
221                 if (root.HeightSpecification >= 0 )
222                 {
223                     // exact size provided so match height exactly
224                     height = new LayoutLength(root.HeightSpecification);
225                     heightMode = MeasureSpecification.ModeType.Exactly;
226                 }
227                 else if (root.HeightSpecification == LayoutParamPolicies.MatchParent)
228                 {
229                     heightMode = MeasureSpecification.ModeType.Exactly;
230                 }
231
232                 MeasureSpecification parentWidthSpecification =
233                     new MeasureSpecification( width, widthMode);
234
235                 MeasureSpecification parentHeightSpecification =
236                     new MeasureSpecification( height, heightMode);
237
238                 // Start at root with it's parent's widthSpecification and heightSpecification
239                 MeasureHierarchy(root, parentWidthSpecification, parentHeightSpecification);
240
241                 // Start at root which was just measured.
242                 PerformLayout( root, new LayoutLength(rootPosition.X),
243                                      new LayoutLength(rootPosition.Y),
244                                      new LayoutLength(rootPosition.X) + root.Layout.MeasuredWidth.Size,
245                                      new LayoutLength(rootPosition.Y) + root.Layout.MeasuredHeight.Size );
246             }
247         }
248
249         /// <summary>
250         /// Entry point into the C# Layouting that starts the Processing
251         /// </summary>
252         private void Process(int id)
253         {
254             Layer defaultLayer = _window.GetDefaultLayer();
255             for (uint i = 0; i < defaultLayer.ChildCount; i++)
256             {
257                 View view = defaultLayer.GetChildAt(i);
258                 FindRootLayouts(view);
259             }
260
261         }
262
263         /// <summary>
264         /// Starts measuring the tree, starting from the root layout.
265         /// </summary>
266         private void MeasureHierarchy(View root, MeasureSpecification widthSpec, MeasureSpecification heightSpec)
267         {
268             // Does this View have a layout?
269             // Yes - measure the layout. It will call this method again for each of it's children.
270             // No -  reached leaf or no layouts set
271             //
272             // If in a leaf View with no layout, it's natural size is bubbled back up.
273
274             if (root.Layout != null)
275             {
276                 root.Layout.Measure(widthSpec, heightSpec);
277             }
278         }
279
280         /// <summary>
281         /// Performs the layouting positioning
282         /// </summary>
283         private void PerformLayout(View root, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
284         {
285             if(root.Layout != null)
286             {
287                 root.Layout.Layout(left, top, right, bottom);
288             }
289         }
290     } // class LayoutController
291
292 } // namespace Tizen.NUI