84d8d850bdbdf6fbb41bb42fcd6d3ab078a67943
[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         }
90
91         /// <summary>
92         /// Explict Dispose.
93         /// </summary>
94         public void Dispose()
95         {
96            Dispose(DisposeTypes.Explicit);
97         }
98
99         /// <summary>
100         /// Dispose Explict or Implicit
101         /// </summary>
102         protected virtual void Dispose(DisposeTypes type)
103         {
104             if (disposed)
105             {
106                 return;
107             }
108
109             if (type == DisposeTypes.Explicit)
110             {
111                 //Called by User code
112                 //Release your own managed resources here.
113                 //You should release all of your own disposable objects here.
114             }
115
116             //Release your own unmanaged resources here.
117             //You should not access any managed member here except static instance.
118             //because the execution order of Finalizes is non-deterministic.
119
120             if (unmanagedLayoutController.Handle != global::System.IntPtr.Zero)
121             {
122                 Interop.LayoutController.delete_LayoutController(unmanagedLayoutController);
123                 unmanagedLayoutController = new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero);
124             }
125
126             disposed = true;
127         }
128
129         // Traverse through children from the provided root.
130         // If a child has no layout but is a pure View then assign a default layout and continue traversal.
131         // If child has no layout and not a pure View then assign a LayoutItem that terminates the layouting (leaf),
132         private void AutomaticallyAssignLayouts(View rootNode)
133         {
134             for (uint i = 0; i < rootNode.ChildCount; i++)
135             {
136                 View view = rootNode.GetChildAt(i);
137                 if (rootNode.Layout == null )
138                 {
139                     if (rootNode.GetType() == typeof(View))
140                     {
141                         rootNode.Layout = new LayoutGroup();
142                         AutomaticallyAssignLayouts(rootNode);
143                     }
144                     else
145                     {
146                         rootNode.Layout = new LayoutItem();
147                     }
148                 }
149             }
150         }
151
152         // Traverse the tree looking for a root node that is a layout.
153         // Once found, it's children can be assigned Layouts and the Measure process started.
154         private void FindRootLayouts(View rootNode)
155         {
156             if (rootNode.Layout != null)
157             {
158                 // rootNode has a layout, ensure all children have default layouts or layout items.
159                 AutomaticallyAssignLayouts(rootNode);
160                 // rootNode has a layout, start measuring and layouting from here.
161                 MeasureAndLayout(rootNode);
162             }
163             else
164             {
165                 // Search children of supplied node for a layout.
166                 for (uint i = 0; i < rootNode.ChildCount; i++)
167                 {
168                     View view = rootNode.GetChildAt(i);
169                     FindRootLayouts(view);
170                 }
171             }
172         }
173
174         // Starts of the actual measuring and layouting from the given root node.
175         // Can be called from multiple starting roots but in series.
176         void MeasureAndLayout(View root)
177         {
178             if (root !=null)
179             {
180                 // Get parent MeasureSpecification, this could be the Window or View with an exact size.
181                 Container parentNode = root.GetParent();
182                 Size2D rootSize;
183                 Position2D rootPosition = root.Position2D;
184                 View parentView = parentNode as View;
185                 if (parentView)
186                 {
187                     // Get parent View's Size.  If using Legacy size negotiation then should have been set already.
188                     rootSize = new Size2D(parentView.Size2D.Width, parentView.Size2D.Height);
189                 }
190                 else
191                 {
192                     // Parent not a View so assume it's a Layer which is the size of the window.
193                     rootSize = new Size2D(_window.Size.Width, _window.Size.Height);
194                 }
195
196                 // Determine measure specification for root.
197                 // The root layout policy could be an exact size, be match parent or wrap children.
198                 // If wrap children then at most it can be the root parent size.
199                 // If match parent then should be root parent size.
200                 // If exact then should be that size limited by the root parent size.
201
202                 LayoutLength width = new LayoutLength(rootSize.Width);
203                 LayoutLength height = new LayoutLength(rootSize.Height);
204                 MeasureSpecification.ModeType widthMode = MeasureSpecification.ModeType.AtMost;
205                 MeasureSpecification.ModeType heightMode = MeasureSpecification.ModeType.AtMost;
206
207                 if ( root.WidthSpecification >= 0 )
208                 {
209                     // exact size provided so match width exactly
210                     width = new LayoutLength(root.WidthSpecification);
211                     widthMode = MeasureSpecification.ModeType.Exactly;
212                 }
213                 else if (root.WidthSpecification == LayoutParamPolicies.MatchParent)
214                 {
215
216                     widthMode = MeasureSpecification.ModeType.Exactly;
217                 }
218
219                 if (root.HeightSpecification >= 0 )
220                 {
221                     // exact size provided so match height exactly
222                     height = new LayoutLength(root.HeightSpecification);
223                     heightMode = MeasureSpecification.ModeType.Exactly;
224                 }
225                 else if (root.HeightSpecification == LayoutParamPolicies.MatchParent)
226                 {
227                     heightMode = MeasureSpecification.ModeType.Exactly;
228                 }
229
230                 MeasureSpecification parentWidthSpecification =
231                     new MeasureSpecification( width, widthMode);
232
233                 MeasureSpecification parentHeightSpecification =
234                     new MeasureSpecification( height, heightMode);
235
236                 // Start at root with it's parent's widthSpecification and heightSpecification
237                 MeasureHierarchy(root, parentWidthSpecification, parentHeightSpecification);
238
239                 // Start at root which was just measured.
240                 PerformLayout( root, new LayoutLength(rootPosition.X),
241                                      new LayoutLength(rootPosition.Y),
242                                      new LayoutLength(rootPosition.X) + root.Layout.MeasuredWidth.Size,
243                                      new LayoutLength(rootPosition.Y) + root.Layout.MeasuredHeight.Size );
244             }
245         }
246
247         /// <summary>
248         /// Entry point into the C# Layouting that starts the Processing
249         /// </summary>
250         private void Process(int id)
251         {
252             // First layer in the Window should be the default layer (index 0 )
253             uint numberOfLayers = _window.LayerCount;
254             for (uint layerIndex = 0; layerIndex < numberOfLayers; layerIndex++)
255             {
256                 Layer layer = _window.GetLayer(layerIndex);
257                 for (uint i = 0; i < layer.ChildCount; i++)
258                 {
259                     View view = layer.GetChildAt(i);
260                     FindRootLayouts(view);
261                 }
262             }
263
264         }
265
266         /// <summary>
267         /// Starts measuring the tree, starting from the root layout.
268         /// </summary>
269         private void MeasureHierarchy(View root, MeasureSpecification widthSpec, MeasureSpecification heightSpec)
270         {
271             // Does this View have a layout?
272             // Yes - measure the layout. It will call this method again for each of it's children.
273             // No -  reached leaf or no layouts set
274             //
275             // If in a leaf View with no layout, it's natural size is bubbled back up.
276
277             if (root.Layout != null)
278             {
279                 root.Layout.Measure(widthSpec, heightSpec);
280             }
281         }
282
283         /// <summary>
284         /// Performs the layouting positioning
285         /// </summary>
286         private void PerformLayout(View root, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
287         {
288             if(root.Layout != null)
289             {
290                 root.Layout.Layout(left, top, right, bottom);
291             }
292         }
293     } // class LayoutController
294
295 } // namespace Tizen.NUI