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 int layoutCount = 0;
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 //Release your own unmanaged resources here.
65 //You should not access any managed member here except static instance.
66 //because the execution order of Finalizes is non-deterministic.
68 if (SwigCPtr.Handle != global::System.IntPtr.Zero)
70 SwigCPtr = new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero);
73 transitionManager.Dispose();
79 /// Set or Get Layouting core animation override property.
80 /// Gives explicit control over the Layouting animation playback if set to True.
81 /// Reset to False if explicit control no longer required.
83 [EditorBrowsable(EditorBrowsableState.Never)]
84 public bool OverrideCoreAnimation
88 return transitionManager.OverrideCoreAnimation;
92 transitionManager.OverrideCoreAnimation = value;
97 /// Get the unique id of the LayoutController
105 /// Request relayout of a LayoutItem and it's parent tree.
107 /// <param name="layoutItem">LayoutItem that is required to be laid out again.</param>
108 public void RequestLayout(LayoutItem layoutItem)
110 // Go up the tree and mark all parents to relayout
111 ILayoutParent layoutParent = layoutItem.GetParent();
112 if (layoutParent != null)
114 LayoutGroup layoutGroup = layoutParent as LayoutGroup;
115 if (layoutGroup != null && !layoutGroup.LayoutRequested)
117 layoutGroup.RequestLayout();
123 /// Entry point into the C# Layouting that starts the Processing
125 public void Process(object source, EventArgs e)
127 window.LayersChildren?.ForEach(layer =>
129 if(layer?.LayoutCount > 0)
131 layer?.Children?.ForEach(view =>
135 FindRootLayouts(view, windowWidth, windowHeight);
141 transitionManager.SetupCoreAndPlayAnimation();
145 /// Get the Layouting animation object that transitions layouts and content.
146 /// Use OverrideCoreAnimation to explicitly control Playback.
148 /// <returns> The layouting core Animation. </returns>
149 [EditorBrowsable(EditorBrowsableState.Never)]
150 public Animation GetCoreAnimation()
152 return transitionManager.CoreAnimation;
156 /// Add transition data for a LayoutItem to the transition stack.
158 /// <param name="transitionDataEntry">Transition data for a LayoutItem.</param>
159 internal void AddTransitionDataEntry(LayoutData transitionDataEntry)
161 transitionManager.AddTransitionDataEntry(transitionDataEntry);
165 /// Add LayoutItem to a removal stack for removal after transitions finish.
167 /// <param name="itemToRemove">LayoutItem to remove.</param>
168 internal void AddToRemovalStack(LayoutItem itemToRemove)
170 transitionManager.AddToRemovalStack(itemToRemove);
174 /// The number of layouts.
175 /// This can be used to set/unset Process callback to calculate Layout.
177 internal int LayoutCount
186 if (layoutCount == value) return;
188 if (value < 0) throw new global::System.ArgumentOutOfRangeException(nameof(LayoutCount), "LayoutCount(" + LayoutCount + ") should not be less than zero");
190 if (layoutCount == 0)
192 ProcessorController.Instance.LayoutProcessorEvent += Process;
196 ProcessorController.Instance.LayoutProcessorEvent -= Process;
203 // Traverse the tree looking for a root node that is a layout.
204 // Once found, it's children can be assigned Layouts and the Measure process started.
205 private void FindRootLayouts(View rootNode, float parentWidth, float parentHeight)
207 if (rootNode.Layout != null)
209 NUILog.Debug("LayoutController Root found:" + rootNode.Name);
210 // rootNode has a layout, start measuring and layouting from here.
211 MeasureAndLayout(rootNode, parentWidth, parentHeight);
215 float rootWidth = rootNode.SizeWidth;
216 float rootHeight = rootNode.SizeHeight;
217 // Search children of supplied node for a layout.
218 for (uint i = 0; i < rootNode.ChildCount; i++)
220 FindRootLayouts(rootNode.GetChildAt(i), rootWidth, rootHeight);
225 // Starts of the actual measuring and layouting from the given root node.
226 // Can be called from multiple starting roots but in series.
227 // Get parent View's Size. If using Legacy size negotiation then should have been set already.
228 // Parent not a View so assume it's a Layer which is the size of the window.
229 private void MeasureAndLayout(View root, float parentWidth, float parentHeight)
231 var layout = root.Layout;
234 // Determine measure specification for root.
235 // The root layout policy could be an exact size, be match parent or wrap children.
236 // If wrap children then at most it can be the root parent size.
237 // If match parent then should be root parent size.
238 // If exact then should be that size limited by the root parent size.
239 float widthSize = GetLengthSize(parentWidth, root.WidthSpecification);
240 float heightSize = GetLengthSize(parentHeight, root.HeightSpecification);
241 var widthMode = GetMode(root.WidthSpecification);
242 var heightMode = GetMode(root.HeightSpecification);
244 if (layout.NeedsLayout(widthSize, heightSize, widthMode, heightMode))
246 var widthSpec = CreateMeasureSpecification(widthSize, widthMode);
247 var heightSpec = CreateMeasureSpecification(heightSize, heightMode);
249 // Start at root with it's parent's widthSpecification and heightSpecification
250 MeasureHierarchy(root, widthSpec, heightSpec);
253 float positionX = root.PositionX;
254 float positionY = root.PositionY;
255 // Start at root which was just measured.
256 PerformLayout(root, new LayoutLength(positionX),
257 new LayoutLength(positionY),
258 new LayoutLength(positionX) + layout.MeasuredWidth.Size,
259 new LayoutLength(positionY) + layout.MeasuredHeight.Size);
263 private float GetLengthSize(float size, int specification)
265 // exact size provided so match width exactly
266 return (specification >= 0) ? specification : size;
269 private MeasureSpecification.ModeType GetMode(int specification)
271 if (specification >= 0 || specification == LayoutParamPolicies.MatchParent)
273 return MeasureSpecification.ModeType.Exactly;
275 return MeasureSpecification.ModeType.Unspecified;
278 private MeasureSpecification CreateMeasureSpecification(float size, MeasureSpecification.ModeType mode)
280 return new MeasureSpecification(new LayoutLength(size), mode);
284 /// Starts measuring the tree, starting from the root layout.
286 private void MeasureHierarchy(View root, MeasureSpecification widthSpec, MeasureSpecification heightSpec)
288 // Does this View have a layout?
289 // Yes - measure the layout. It will call this method again for each of it's children.
290 // No - reached leaf or no layouts set
292 // If in a leaf View with no layout, it's natural size is bubbled back up.
293 root.Layout?.Measure(widthSpec, heightSpec);
297 /// Performs the layouting positioning
299 private void PerformLayout(View root, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
301 root.Layout?.Layout(left, top, right, bottom);
304 private void SetBaseWindowAndSize(Window window)
306 this.window = window;
307 this.window.Resized += OnWindowResized;
309 var windowSize = window.GetSize();
310 windowWidth = windowSize.Width;
311 windowHeight = windowSize.Height;
312 windowSize.Dispose();
316 private void OnWindowResized(object sender, Window.ResizedEventArgs e)
318 windowWidth = e.WindowSize.Width;
319 windowHeight = e.WindowSize.Height;
321 } // class LayoutController
322 } // namespace Tizen.NUI