2 * Copyright (c) 2021 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 using Tizen.NUI.BaseComponents;
19 using System.ComponentModel;
25 /// [Draft] The class that initiates the Measuring and Layouting of the tree,
26 /// It sets a callback that becomes the entry point into the C# Layouting.
28 internal class LayoutController : Disposable
30 private static int layoutControllerID = 1;
33 private Window window;
34 private float windowWidth;
35 private float windowHeight;
36 private LayoutTransitionManager transitionManager;
38 private bool subscribed;
41 /// Constructs a LayoutController which controls the measuring and layouting.<br />
42 /// <param name="window">Window attach this LayoutController to.</param>
44 public LayoutController(Window window)
46 transitionManager = new LayoutTransitionManager();
47 id = layoutControllerID++;
49 SetBaseWindowAndSize(window);
53 /// Dispose Explicit or Implicit
55 protected override void Dispose(DisposeTypes type)
64 ProcessorController.Instance.LayoutProcessorEvent -= Process;
68 //Release your own unmanaged resources here.
69 //You should not access any managed member here except static instance.
70 //because the execution order of Finalizes is non-deterministic.
72 if (SwigCPtr.Handle != global::System.IntPtr.Zero)
74 SwigCPtr = new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero);
77 transitionManager.Dispose();
83 /// Set or Get Layouting core animation override property.
84 /// Gives explicit control over the Layouting animation playback if set to True.
85 /// Reset to False if explicit control no longer required.
87 [EditorBrowsable(EditorBrowsableState.Never)]
88 public bool OverrideCoreAnimation
92 return transitionManager.OverrideCoreAnimation;
96 transitionManager.OverrideCoreAnimation = value;
101 /// Get the unique id of the LayoutController
109 /// Request relayout of a LayoutItem and it's parent tree.
111 /// <param name="layoutItem">LayoutItem that is required to be laid out again.</param>
112 public void RequestLayout(LayoutItem layoutItem)
114 // Go up the tree and mark all parents to relayout
115 ILayoutParent layoutParent = layoutItem.GetParent();
116 if (layoutParent != null)
118 LayoutGroup layoutGroup = layoutParent as LayoutGroup;
119 if (layoutGroup != null && !layoutGroup.LayoutRequested)
121 layoutGroup.RequestLayout();
127 /// Entry point into the C# Layouting that starts the Processing
129 public void Process(object source, EventArgs e)
131 window.LayersChildren?.ForEach(layer =>
133 layer?.Children?.ForEach(view =>
137 FindRootLayouts(view, windowWidth, windowHeight);
142 transitionManager.SetupCoreAndPlayAnimation();
146 /// Get the Layouting animation object that transitions layouts and content.
147 /// Use OverrideCoreAnimation to explicitly control Playback.
149 /// <returns> The layouting core Animation. </returns>
150 [EditorBrowsable(EditorBrowsableState.Never)]
151 public Animation GetCoreAnimation()
153 return transitionManager.CoreAnimation;
157 /// Create and set process callback
159 internal void CreateProcessCallback()
163 ProcessorController.Instance.LayoutProcessorEvent += Process;
169 /// Add transition data for a LayoutItem to the transition stack.
171 /// <param name="transitionDataEntry">Transition data for a LayoutItem.</param>
172 internal void AddTransitionDataEntry(LayoutData transitionDataEntry)
174 transitionManager.AddTransitionDataEntry(transitionDataEntry);
178 /// Add LayoutItem to a removal stack for removal after transitions finish.
180 /// <param name="itemToRemove">LayoutItem to remove.</param>
181 internal void AddToRemovalStack(LayoutItem itemToRemove)
183 transitionManager.AddToRemovalStack(itemToRemove);
186 // Traverse the tree looking for a root node that is a layout.
187 // Once found, it's children can be assigned Layouts and the Measure process started.
188 private void FindRootLayouts(View rootNode, float parentWidth, float parentHeight)
190 if (rootNode.Layout != null)
192 NUILog.Debug("LayoutController Root found:" + rootNode.Name);
193 // rootNode has a layout, start measuring and layouting from here.
194 MeasureAndLayout(rootNode, parentWidth, parentHeight);
198 float rootWidth = rootNode.SizeWidth;
199 float rootHeight = rootNode.SizeHeight;
200 // Search children of supplied node for a layout.
201 for (uint i = 0; i < rootNode.ChildCount; i++)
203 FindRootLayouts(rootNode.GetChildAt(i), rootWidth, rootHeight);
208 // Starts of the actual measuring and layouting from the given root node.
209 // Can be called from multiple starting roots but in series.
210 // Get parent View's Size. If using Legacy size negotiation then should have been set already.
211 // Parent not a View so assume it's a Layer which is the size of the window.
212 private void MeasureAndLayout(View root, float parentWidth, float parentHeight)
214 if (root.Layout != null)
216 // Determine measure specification for root.
217 // The root layout policy could be an exact size, be match parent or wrap children.
218 // If wrap children then at most it can be the root parent size.
219 // If match parent then should be root parent size.
220 // If exact then should be that size limited by the root parent size.
221 float widthSize = GetLengthSize(parentWidth, root.WidthSpecification);
222 float heightSize = GetLengthSize(parentHeight, root.HeightSpecification);
223 var widthMode = GetMode(root.WidthSpecification);
224 var heightMode = GetMode(root.HeightSpecification);
226 if (root.Layout.NeedsLayout(widthSize, heightSize, widthMode, heightMode))
228 var widthSpec = CreateMeasureSpecification(widthSize, widthMode);
229 var heightSpec = CreateMeasureSpecification(heightSize, heightMode);
231 // Start at root with it's parent's widthSpecification and heightSpecification
232 MeasureHierarchy(root, widthSpec, heightSpec);
235 float positionX = root.PositionX;
236 float positionY = root.PositionY;
237 // Start at root which was just measured.
238 PerformLayout(root, new LayoutLength(positionX),
239 new LayoutLength(positionY),
240 new LayoutLength(positionX) + root.Layout.MeasuredWidth.Size,
241 new LayoutLength(positionY) + root.Layout.MeasuredHeight.Size);
245 private float GetLengthSize(float size, int specification)
247 // exact size provided so match width exactly
248 return (specification >= 0) ? specification : size;
251 private MeasureSpecification.ModeType GetMode(int specification)
253 if (specification >= 0 || specification == LayoutParamPolicies.MatchParent)
255 return MeasureSpecification.ModeType.Exactly;
257 return MeasureSpecification.ModeType.Unspecified;
260 private MeasureSpecification CreateMeasureSpecification(float size, MeasureSpecification.ModeType mode)
262 return new MeasureSpecification(new LayoutLength(size), mode);
266 /// Starts measuring the tree, starting from the root layout.
268 private void MeasureHierarchy(View root, MeasureSpecification widthSpec, MeasureSpecification heightSpec)
270 // Does this View have a layout?
271 // Yes - measure the layout. It will call this method again for each of it's children.
272 // No - reached leaf or no layouts set
274 // If in a leaf View with no layout, it's natural size is bubbled back up.
275 root.Layout?.Measure(widthSpec, heightSpec);
279 /// Performs the layouting positioning
281 private void PerformLayout(View root, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
283 root.Layout?.Layout(left, top, right, bottom);
286 private void SetBaseWindowAndSize(Window window)
288 this.window = window;
289 this.window.Resized += OnWindowResized;
291 var windowSize = window.GetSize();
292 windowWidth = windowSize.Width;
293 windowHeight = windowSize.Height;
294 windowSize.Dispose();
298 private void OnWindowResized(object sender, Window.ResizedEventArgs e)
300 windowWidth = e.WindowSize.Width;
301 windowHeight = e.WindowSize.Height;
303 } // class LayoutController
304 } // namespace Tizen.NUI