/// <summary> /// Calculate target state for each element based on layout algorithm. /// </summary> /// <param name="layoutId">Identifier of the layout to be used.</param> /// <param name="metaPanel">Reference to owning panel instance.</param> /// <param name="stateDict">Dictionary of per-element state.</param> /// <param name="elements">Collection of elements to be arranged.</param> /// <param name="finalSize">Size that layout should use to arrange child elements.</param> public override void TargetChildren(string layoutId, MetaPanelBase metaPanel, MetaElementStateDict stateDict, ICollection elements, Size finalSize) { if (_count > 0) { // Each child is an equal angle around the radius double angleCurrent = StartAngle; double diff = (EndAngle - StartAngle); double angleDelta = diff / ((diff == (double)360) ? _count : _count - 1); // Calculate the radius separately for each dimension double radiusX = (finalSize.Width - _maxLength * 2) / 2; double radiusY = (finalSize.Height - _maxLength * 2) / 2; // Do we force into being a circle? if (Circle) { // Always reduce to using the smallest direction radiusX = Math.Min(radiusX, radiusY); radiusY = radiusX; } // Rotate around the center point Point center = new Point(finalSize.Width / 2, finalSize.Height / 2); // Calculate the target rectangle for each element foreach (UIElement element in elements) { // We ignore items being removed if (stateDict[element].Status != MetaElementStatus.Removing) { // We ignore items that are collapsed if (element.Visibility != Visibility.Collapsed) { // Rotate around the center point using the accumulated angle double childX = center.X + Math.Cos(2 * Math.PI * angleCurrent / 360) * radiusX; double childY = center.Y + Math.Sin(2 * Math.PI * angleCurrent / 360) * radiusY; angleCurrent += angleDelta; // Position the element at Size desiredSize = element.DesiredSize; Rect newTargetRect = new Rect(childX - desiredSize.Width / 2, childY - desiredSize.Height / 2, desiredSize.Width, desiredSize.Height); // Store the new target rectangle if (!stateDict[element].TargetRect.Equals(newTargetRect)) { stateDict[element].TargetChanged = true; stateDict[element].TargetRect = newTargetRect; } } } } } }
/// <summary> /// Perform animation effects on the set of children. /// </summary> /// <param name="animateId">Identifier of the animate to be used.</param> /// <param name="metaPanel">Reference to owning panel instance.</param> /// <param name="stateDict">Dictionary of per-element state.</param> /// <param name="elements">Collection of elements to be animated.</param> /// <param name="elapsedMilliseconds">Elapsed milliseconds since last animation cycle.</param> public override void ApplyAnimation(string animateId, MetaPanelBase metaPanel, MetaElementStateDict stateDict, ICollection elements, double elapsedMilliseconds) { // Only apply if we match the incoming animate identifier if (string.IsNullOrEmpty(Id) || Id.Equals(animateId)) { foreach (UIElement element in elements) { MetaElementState elementState = stateDict[element]; // If this is a new element that has not had its current rectangle set if ((elementState.Status == MetaElementStatus.New) && elementState.NewCalculating) { elementState.CurrentRect = RectFromSize(Size, RectFromLocation(Location, metaPanel, elementState), elementState); elementState.TargetChanged = true; elementState.AnimateComplete = false; } } } // Let base class take care of easing animations base.ApplyAnimation(animateId, metaPanel, stateDict, elements, elapsedMilliseconds); }
/// <summary> /// Perform animation effects on the set of children. /// </summary> /// <param name="animateId">Identifier of the animate to be used.</param> /// <param name="metaPanel">Reference to owning panel instance.</param> /// <param name="stateDict">Dictionary of per-element state.</param> /// <param name="elements">Collection of elements to be animated.</param> /// <param name="elapsedMilliseconds">Elapsed milliseconds since last animation cycle.</param> public override void ApplyAnimation(string animateId, MetaPanelBase metaPanel, MetaElementStateDict stateDict, ICollection elements, double elapsedMilliseconds) { // Only apply if we match the incoming animate identifier if (string.IsNullOrEmpty(Id) || Id.Equals(animateId)) { foreach (UIElement element in elements) { MetaElementState elementState = stateDict[element]; // Only interested in elements being removed... if (elementState.Status == MetaElementStatus.Removing) { // ...and having the final target rectangle calculated just the once... if (!elementState.RemoveCalculated) { // Calculate the correct target rectangle Rect positionRect = RectFromSize(Size, RectFromLocation(Location, metaPanel, elementState), elementState); // Update the final target value elementState.TargetRect = positionRect; elementState.TargetChanged = true; elementState.AnimateComplete = false; } } } } // Let base class take care of easing animations base.ApplyAnimation(animateId, metaPanel, stateDict, elements, elapsedMilliseconds); }
/// <summary> /// Invoked when a property change requires a measure to occur. /// </summary> /// <param name="d">Owning object.</param> /// <param name="e">Details of property that has changed.</param> protected static void OnNeedMeasureOnChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { MetaPanelBase sender = (MetaPanelBase)d; sender.OnNeedMeasure(sender, EventArgs.Empty); }
/// <summary> /// Calculate target state for each element based on layout algorithm. /// </summary> /// <param name="layoutId">Identifier of the layout to be used.</param> /// <param name="metaPanel">Reference to owning panel instance.</param> /// <param name="stateDict">Dictionary of per-element state.</param> /// <param name="elements">Collection of elements to be arranged.</param> /// <param name="finalSize">Size that layout should use to arrange child elements.</param> public override void TargetChildren(string layoutId, MetaPanelBase metaPanel, MetaElementStateDict stateDict, ICollection elements, Size finalSize) { // Only apply if we match the incoming layout identifier if (string.IsNullOrEmpty(Id) || Id.Equals(layoutId)) { Rect newTargetRect = new Rect(Utility.PointZero, finalSize); foreach (UIElement element in elements) { // We ignore items being removed if (stateDict[element].Status != MetaElementStatus.Removing) { // Store the new target rectangle if (!stateDict[element].TargetRect.Equals(newTargetRect)) { stateDict[element].TargetChanged = true; stateDict[element].TargetRect = newTargetRect; } } } } }
private static void OnIsAnimatingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { MetaPanelBase metaPanelBase = (MetaPanelBase)d; // By always requesting a panel measure when the Rendering event is // fired we ensure that the panel invokes the animation instances and // the child elements are updated until the animation ends. if ((bool)e.NewValue) { // Reset the last ticks as we do not want to use the duration since the // last time animation finished, which might have been a long time ago metaPanelBase._lastTicks = -1; // This event is fired just before rendering of each display frame CompositionTarget.Rendering += new EventHandler(metaPanelBase.OnRendering); } else { CompositionTarget.Rendering -= new EventHandler(metaPanelBase.OnRendering); } // Raise the changed event EventHandler handler = metaPanelBase.IsAnimatingChanged; if (handler != null) { handler(metaPanelBase, EventArgs.Empty); } }
/// <summary> /// Perform animation effects on the set of children. /// </summary> /// <param name="animateId">Identifier of the animate to be used.</param> /// <param name="metaPanel">Reference to owning panel instance.</param> /// <param name="stateDict">Dictionary of per-element state.</param> /// <param name="elements">Collection of elements to be animated.</param> /// <param name="elapsedMilliseconds">Elapsed milliseconds since last animation cycle.</param> public override void ApplyAnimation(string animateId, MetaPanelBase metaPanel, MetaElementStateDict stateDict, ICollection elements, double elapsedMilliseconds) { ApplyAnimation(animateId, metaPanel, stateDict, elements, elapsedMilliseconds, Start, End); }
/// <summary> /// Calculate target state for each element based on layout algorithm. /// </summary> /// <param name="layoutId">Identifier of the layout to be used.</param> /// <param name="metaPanel">Reference to owning panel instance.</param> /// <param name="stateDict">Dictionary of per-element state.</param> /// <param name="elements">Collection of elements to be arranged.</param> /// <param name="finalSize">Size that layout should use to arrange child elements.</param> public override void TargetChildren(string layoutId, MetaPanelBase metaPanel, MetaElementStateDict stateDict, ICollection elements, Size finalSize) { // Only apply if we match the incoming layout identifier if (string.IsNullOrEmpty(Id) || Id.Equals(layoutId)) { // Calculate the target rectangle for each element foreach (UIElement element in elements) { // We ignore items being removed if (stateDict[element].Status != MetaElementStatus.Removing) { // Default to being the desired size but at position zero,zero Rect newTargetRect = new Rect(0, 0, element.DesiredSize.Width, element.DesiredSize.Height); // Use the Left or Right value provided as attached properties of the element double left = CanvasLayout.GetLeft(element); if (!double.IsNaN(left)) { newTargetRect.X = left; } else { double right = CanvasLayout.GetRight(element); if (!double.IsNaN(right)) { newTargetRect.X = (finalSize.Width - element.DesiredSize.Width) - right; } } // Use the Top or Bottom value provided as attached properties of the element double top = CanvasLayout.GetTop(element); if (!double.IsNaN(top)) { newTargetRect.Y = top; } else { double bottom = CanvasLayout.GetBottom(element); if (!double.IsNaN(bottom)) { newTargetRect.Y = (finalSize.Height - element.DesiredSize.Height) - bottom; } } // Store the new target rectangle if (!stateDict[element].TargetRect.Equals(newTargetRect)) { stateDict[element].TargetChanged = true; stateDict[element].TargetRect = newTargetRect; } } } } }
/// <summary> /// Calculate target state for each element based on layout algorithm. /// </summary> /// <param name="layoutId">Identifier of the layout to be used.</param> /// <param name="metaPanel">Reference to owning panel instance.</param> /// <param name="stateDict">Dictionary of per-element state.</param> /// <param name="elements">Collection of elements to be arranged.</param> /// <param name="finalSize">Size that layout should use to arrange child elements.</param> public void TargetChildren(string layoutId, MetaPanelBase metaPanel, MetaElementStateDict stateDict, ICollection elements, Size finalSize) { foreach (Layout layout in this) { layout.TargetChildren(layoutId, metaPanel, stateDict, elements, finalSize); } }
/// <summary> /// Perform animation effects on the set of children. /// </summary> /// <param name="animateId">Identifier of the animate to be used.</param> /// <param name="metaPanel">Reference to owning panel instance.</param> /// <param name="stateDict">Dictionary of per-element state.</param> /// <param name="elements">Collection of elements to be animated.</param> /// <param name="elapsedMilliseconds">Elapsed milliseconds since last animation cycle.</param> /// <param name="startOpacity">Opacity for start of animation.</param> /// <param name="endOpacity">Opacity for end of animation.</param> public void ApplyAnimation(string animateId, MetaPanelBase metaPanel, MetaElementStateDict stateDict, ICollection elements, double elapsedMilliseconds, double startOpacity, double endOpacity) { // Only apply if we match the incoming animate identifier if (string.IsNullOrEmpty(Id) || Id.Equals(animateId)) { // Only grab the dependancy properties once, for improved perf double duration = Duration; foreach (UIElement element in elements) { // Only apply animation to element of required target state MetaElementState elementState = stateDict[element]; if (elementState.Status == Target) { // If the element is the correct target and correct status for starting animation if ((!elementState.RemoveCalculated && (Target == MetaElementStatus.Removing)) || (elementState.NewCalculating && (Target == MetaElementStatus.New))) { // Use the starting opacity element.Opacity = startOpacity; elementState.ElapsedOpacityTime = 0; } else { // Only perform animation if some time has actually ellapsed and not already at target if ((elapsedMilliseconds > 0) && (element.Opacity != (double)endOpacity)) { // Add new elapsed time to the animation running time elementState.ElapsedOpacityTime += elapsedMilliseconds; // Does elapsed time indicate animation should have completed? if (elementState.ElapsedOpacityTime >= duration) { element.Opacity = endOpacity; } else { element.Opacity = EasingCalculation.Calculate(elementState.ElapsedOpacityTime, startOpacity, endOpacity - startOpacity, duration); } } } // If not yet at target opacity then not finished with animating elementState.AnimateComplete &= (element.Opacity == (double)endOpacity); } } } }
/// <summary> /// Perform animation effects on the set of children. /// </summary> /// <param name="animateId">Identifier of the animate to be used.</param> /// <param name="metaPanel">Reference to owning panel instance.</param> /// <param name="stateDict">Dictionary of per-element state.</param> /// <param name="elements">Collection of elements to be animated.</param> /// <param name="elapsedMilliseconds">Elapsed milliseconds since last animation cycle.</param> public void ApplyAnimation(string animateId, MetaPanelBase metaPanel, MetaElementStateDict stateDict, ICollection elements, double elapsedMilliseconds) { foreach (Animate animate in this) { animate.ApplyAnimation(animateId, metaPanel, stateDict, elements, elapsedMilliseconds); } }
/// <summary> /// Measure the layout size required to arrange all elements. /// </summary> /// <param name="layoutId">Identifier of the layout to be used.</param> /// <param name="metaPanel">Reference to owning panel instance.</param> /// <param name="stateDict">Dictionary of per-element state.</param> /// <param name="elements">Collection of elements to be measured.</param> /// <param name="availableSize">Available size that can be given to elements.</param> /// <returns>Size the layout determines it needs based on child element sizes.</returns> public override Size MeasureChildren(string layoutId, MetaPanelBase metaPanel, MetaElementStateDict stateDict, ICollection elements, Size availableSize) { // Only apply if we match the incoming layout identifier if (string.IsNullOrEmpty(Id) || Id.Equals(layoutId)) { // Measure each element in turn _validCount = 0; Size usedSize = new Size(); Size maxSize = new Size(); foreach (UIElement element in elements) { // Allow element to have all the remainder size available Size elementSize = new Size(Math.Max(0.0, (double)(availableSize.Width - usedSize.Width)), Math.Max(0.0, (double)(availableSize.Height - usedSize.Height))); element.Measure(elementSize); // We ignore items being removed if (stateDict[element].Status != MetaElementStatus.Removing) { // Discover the used size and the maximum required size for opposite direction Size desiredSize = element.DesiredSize; switch (GetDock(element)) { case Dock.Left: case Dock.Right: maxSize.Height = Math.Max(maxSize.Height, usedSize.Height + desiredSize.Height); usedSize.Width += desiredSize.Width; break; case Dock.Top: case Dock.Bottom: maxSize.Width = Math.Max(maxSize.Width, usedSize.Width + desiredSize.Width); usedSize.Height += desiredSize.Height; break; } _validCount++; } } return(new Size(Math.Max(maxSize.Width, usedSize.Width), Math.Max(maxSize.Height, usedSize.Height))); } else { return(Size.Empty); } }
/// <summary> /// Measure the layout size required to arrange all elements. /// </summary> /// <param name="layoutId">Identifier of the layout to be used.</param> /// <param name="metaPanel">Reference to owning panel instance.</param> /// <param name="stateDict">Dictionary of per-element state.</param> /// <param name="elements">Collection of elements to be measured.</param> /// <param name="availableSize">Available size that can be given to elements.</param> /// <returns>Size the layout determines it needs based on child element sizes.</returns> public override Size MeasureChildren(string layoutId, MetaPanelBase metaPanel, MetaElementStateDict stateDict, ICollection elements, Size availableSize) { // Only apply if we match the incoming layout identifier if (string.IsNullOrEmpty(Id) || Id.Equals(layoutId)) { // Cache the dependancy properties for faster perf Orientation orientation = Orientation; // Give children as much space as they want in the opposite direction to the stacking if (orientation == Orientation.Horizontal) { availableSize.Width = double.PositiveInfinity; } else { availableSize.Height = double.PositiveInfinity; } // Measure each element in turn Size measureSize = new Size(); foreach (UIElement element in elements) { element.Measure(availableSize); // We ignore items being removed if (stateDict[element].Status != MetaElementStatus.Removing) { // Accumulate in the orientation direction, but track maximum value in the opposite if (orientation == Orientation.Horizontal) { measureSize.Width += element.DesiredSize.Width; measureSize.Height = Math.Max(measureSize.Height, element.DesiredSize.Height); } else { measureSize.Width = Math.Max(measureSize.Width, element.DesiredSize.Width); measureSize.Height += element.DesiredSize.Height; } } } return(measureSize); } else { return(Size.Empty); } }
/// <summary> /// Calculate target state for each element based on layout algorithm. /// </summary> /// <param name="layoutId">Identifier of the layout to be used.</param> /// <param name="metaPanel">Reference to owning panel instance.</param> /// <param name="stateDict">Dictionary of per-element state.</param> /// <param name="elements">Collection of elements to be arranged.</param> /// <param name="finalSize">Size that layout should use to arrange child elements.</param> public override void TargetChildren(string layoutId, MetaPanelBase metaPanel, MetaElementStateDict stateDict, ICollection elements, Size finalSize) { // Only apply if we match the incoming layout identifier if (string.IsNullOrEmpty(Id) || Id.Equals(layoutId)) { // TODO target elements that are not in tree drawing TargetTreeNodes(Utility.PointZero, StartNode, stateDict, false); } }
/// <summary> /// Calculate target state for each element based on layout algorithm. /// </summary> /// <param name="layoutId">Identifier of the layout to be used.</param> /// <param name="metaPanel">Reference to owning panel instance.</param> /// <param name="stateDict">Dictionary of per-element state.</param> /// <param name="elements">Collection of elements to be arranged.</param> /// <param name="finalSize">Size that layout should use to arrange child elements.</param> public override void TargetChildren(string layoutId, MetaPanelBase metaPanel, MetaElementStateDict stateDict, ICollection elements, Size finalSize) { // Only apply if we match the incoming layout identifier if (string.IsNullOrEmpty(Id) || Id.Equals(layoutId)) { // Find the size of each size and start at the top left cell Rect cellRect = new Rect(0, 0, finalSize.Width / (double)_columns, finalSize.Height / (double)_rows); // Move across to the defined starting column cellRect.X += cellRect.Width * FirstColumn; // Calculate the right hand side where we need to start a new row double finalSizeRight = finalSize.Width - 1.0; // Calculate the target rectangle for each element foreach (UIElement element in elements) { // We ignore items being removed if (stateDict[element].Status != MetaElementStatus.Removing) { // We ignore items that are collapsed from view if (element.Visibility != Visibility.Collapsed) { // The new target is the current cell rect Rect newTargetRect = cellRect; // Move across to next column cellRect.X += cellRect.Width; // Do we start a line row? if (cellRect.X >= finalSizeRight) { cellRect.X = 0; cellRect.Y += cellRect.Height; } // Store the new target rectangle if (!stateDict[element].TargetRect.Equals(newTargetRect)) { stateDict[element].TargetChanged = true; stateDict[element].TargetRect = newTargetRect; } } } } } }
/// <summary> /// Perform animation effects on the set of children. /// </summary> /// <param name="animateId">Identifier of the animate to be used.</param> /// <param name="metaPanel">Reference to owning panel instance.</param> /// <param name="stateDict">Dictionary of per-element state.</param> /// <param name="elements">Collection of elements to be animated.</param> /// <param name="elapsedMilliseconds">Elapsed milliseconds since last animation cycle.</param> /// <param name="startOpacity">Opacity for start of animation.</param> /// <param name="endOpacity">Opacity for end of animation.</param> public void ApplyAnimation(string animateId, MetaPanelBase metaPanel, MetaElementStateDict stateDict, ICollection elements, double elapsedMilliseconds, double startOpacity, double endOpacity) { // Only apply if we match the incoming animate identifier if (string.IsNullOrEmpty(Id) || Id.Equals(animateId)) { // Only grab the dependancy properties once, for improved perf double duration = Duration; foreach (UIElement element in elements) { // Only apply animation to element of required target state MetaElementState elementState = stateDict[element]; if (elementState.Status == Target) { // If the element is the correct target and correct status for starting animation if ((!elementState.RemoveCalculated && (Target == MetaElementStatus.Removing)) || (elementState.NewCalculating && (Target == MetaElementStatus.New))) { // Use the starting opacity element.Opacity = startOpacity; elementState.ElapsedOpacityTime = 0; } else { // Only perform animation if some time has actually ellapsed and not already at target if ((elapsedMilliseconds > 0) && (element.Opacity != (double)endOpacity)) { // Add new elapsed time to the animation running time elementState.ElapsedOpacityTime += elapsedMilliseconds; // Does elapsed time indicate animation should have completed? if (elementState.ElapsedOpacityTime >= duration) element.Opacity = endOpacity; else element.Opacity = EasingCalculation.Calculate(elementState.ElapsedOpacityTime, startOpacity, endOpacity - startOpacity, duration); } } // If not yet at target opacity then not finished with animating elementState.AnimateComplete &= (element.Opacity == (double)endOpacity); } } } }
private static void OnWrapChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { UIElement element = d as UIElement; if (element != null) { MetaPanelBase parent = VisualTreeHelper.GetParent(element) as MetaPanelBase; if (parent != null) { parent.InvalidateMeasure(); } } }
/// <summary> /// Position child elements according to already calculated target state. /// </summary> /// <param name="layoutId">Identifier of the layout to be used.</param> /// <param name="metaPanel">Reference to owning panel instance.</param> /// <param name="stateDict">Dictionary of per-element state.</param> /// <param name="elements">Collection of elements to be arranged.</param> /// <param name="finalSize">Size that layout should use to arrange child elements.</param> /// <returns>Size used by the layout panel.</returns> public Size ArrangeChildren(string layoutId, MetaPanelBase metaPanel, MetaElementStateDict stateDict, ICollection elements, Size finalSize) { foreach (UIElement element in elements) { MetaElementState elementState = stateDict[element]; elementState.Element.Arrange(elementState.CurrentRect); } // Layout panel takes all the provided size return(finalSize); }
/// <summary> /// Measure the layout size required to arrange all elements. /// </summary> /// <param name="layoutId">Identifier of the layout to be used.</param> /// <param name="metaPanel">Reference to owning panel instance.</param> /// <param name="stateDict">Dictionary of per-element state.</param> /// <param name="elements">Collection of elements to be measured.</param> /// <param name="availableSize">Available size that can be given to elements.</param> /// <returns>Size the layout determines it needs based on child element sizes.</returns> public override Size MeasureChildren(string layoutId, MetaPanelBase metaPanel, MetaElementStateDict stateDict, ICollection elements, Size availableSize) { // Only apply if we match the incoming layout identifier if (string.IsNullOrEmpty(Id) || Id.Equals(layoutId)) { // TODO measure elements that are not in tree drawing return MeasureTreeNodes(StartNode, stateDict, false); } return Size.Empty; }
/// <summary> /// Measure the layout size required to arrange all elements. /// </summary> /// <param name="layoutId">Identifier of the layout to be used.</param> /// <param name="metaPanel">Reference to owning panel instance.</param> /// <param name="stateDict">Dictionary of per-element state.</param> /// <param name="elements">Collection of elements to be measured.</param> /// <param name="availableSize">Available size that can be given to elements.</param> /// <returns>Size the layout determines it needs based on child element sizes.</returns> public override Size MeasureChildren(string layoutId, MetaPanelBase metaPanel, MetaElementStateDict stateDict, ICollection elements, Size availableSize) { // Only apply if we match the incoming layout identifier if (string.IsNullOrEmpty(Id) || Id.Equals(layoutId)) { // TODO measure elements that are not in tree drawing return(MeasureTreeNodes(StartNode, stateDict, false)); } return(Size.Empty); }
/// <summary> /// Perform animation effects on the set of children. /// </summary> /// <param name="animateId">Identifier of the animate to be used.</param> /// <param name="metaPanel">Reference to owning panel instance.</param> /// <param name="stateDict">Dictionary of per-element state.</param> /// <param name="elements">Collection of elements to be animated.</param> /// <param name="elapsedMilliseconds">Elapsed milliseconds since last animation cycle.</param> public override void ApplyAnimation(string animateId, MetaPanelBase metaPanel, MetaElementStateDict stateDict, ICollection elements, double elapsedMilliseconds) { // Only apply if we match the incoming animate identifier if (string.IsNullOrEmpty(Id) || Id.Equals(animateId)) { foreach (UIElement element in elements) { // Immediately move to the target rectangle MetaElementState elementState = stateDict[element]; elementState.CurrentRect = elementState.TargetRect; } } }
/// <summary> /// Measure the layout size required to arrange all elements. /// </summary> /// <param name="layoutId">Identifier of the layout to be used.</param> /// <param name="metaPanel">Reference to owning panel instance.</param> /// <param name="stateDict">Dictionary of per-element state.</param> /// <param name="elements">Collection of elements to be measured.</param> /// <param name="availableSize">Available size that can be given to elements.</param> /// <returns>Size the layout determines it needs based on child element sizes.</returns> public override Size MeasureChildren(string layoutId, MetaPanelBase metaPanel, MetaElementStateDict stateDict, ICollection elements, Size availableSize) { // Only apply if we match the incoming layout identifier if (string.IsNullOrEmpty(Id) || Id.Equals(layoutId)) { // Cache the dependancy properties for faster perf Orientation orientation = Orientation; // Give children as much space as they want in the opposite direction to the stacking if (orientation == Orientation.Horizontal) availableSize.Width = double.PositiveInfinity; else availableSize.Height = double.PositiveInfinity; // Measure each element in turn Size measureSize = new Size(); foreach (UIElement element in elements) { element.Measure(availableSize); // We ignore items being removed if (stateDict[element].Status != MetaElementStatus.Removing) { // Accumulate in the orientation direction, but track maximum value in the opposite if (orientation == Orientation.Horizontal) { measureSize.Width += element.DesiredSize.Width; measureSize.Height = Math.Max(measureSize.Height, element.DesiredSize.Height); } else { measureSize.Width = Math.Max(measureSize.Width, element.DesiredSize.Width); measureSize.Height += element.DesiredSize.Height; } } } return measureSize; } else return Size.Empty; }
/// <summary> /// Measure the layout size required to arrange all elements. /// </summary> /// <param name="layoutId">Identifier of the layout to be used.</param> /// <param name="metaPanel">Reference to owning panel instance.</param> /// <param name="stateDict">Dictionary of per-element state.</param> /// <param name="elements">Collection of elements to be measured.</param> /// <param name="availableSize">Available size that can be given to elements.</param> /// <returns>Size the layout determines it needs based on child element sizes.</returns> public override Size MeasureChildren(string layoutId, MetaPanelBase metaPanel, MetaElementStateDict stateDict, ICollection elements, Size availableSize) { // Only apply if we match the incoming layout identifier if (string.IsNullOrEmpty(Id) || Id.Equals(layoutId)) { // Measure each element in turn foreach (UIElement element in elements) { stateDict[element].Element.Measure(Utility.SizeInfinity); } } // Return an empty size return(Size.Empty); }
/// <summary> /// Measure the layout size required to arrange all elements. /// </summary> /// <param name="layoutId">Identifier of the layout to be used.</param> /// <param name="metaPanel">Reference to owning panel instance.</param> /// <param name="stateDict">Dictionary of per-element state.</param> /// <param name="elements">Collection of elements to be measured.</param> /// <param name="availableSize">Available size that can be given to elements.</param> /// <returns>Size the layout determines it needs based on child element sizes.</returns> public Size MeasureChildren(string layoutId, MetaPanelBase metaPanel, MetaElementStateDict stateDict, ICollection elements, Size availableSize) { Size retSize = new Size(); foreach (Layout layout in this) { Size layoutSize = layout.MeasureChildren(layoutId, metaPanel, stateDict, elements, availableSize); // Find the largest requested size retSize.Width = Math.Max(retSize.Width, layoutSize.Width); retSize.Height = Math.Max(retSize.Height, layoutSize.Height); } return(retSize); }
/// <summary> /// Calculate target state for each element based on layout algorithm. /// </summary> /// <param name="layoutId">Identifier of the layout to be used.</param> /// <param name="metaPanel">Reference to owning panel instance.</param> /// <param name="stateDict">Dictionary of per-element state.</param> /// <param name="elements">Collection of elements to be arranged.</param> /// <param name="finalSize">Size that layout should use to arrange child elements.</param> public override void TargetChildren(string layoutId, MetaPanelBase metaPanel, MetaElementStateDict stateDict, ICollection elements, Size finalSize) { // Only apply if we match the incoming layout identifier if (string.IsNullOrEmpty(Id) || Id.Equals(layoutId)) { // Cache the dependancy properties for faster perf Orientation orientation = Orientation; // Calculate the target rectangle for each element double offset = 0; foreach (UIElement element in elements) { // We ignore items being removed if (stateDict[element].Status != MetaElementStatus.Removing) { Rect newTargetRect; // Updating the elements target rectangle depends on orientation if (orientation == Orientation.Horizontal) { newTargetRect = new Rect(offset, 0, element.DesiredSize.Width, Math.Max(finalSize.Height, element.DesiredSize.Height)); offset += element.DesiredSize.Width; } else { newTargetRect = new Rect(0, offset, Math.Max(finalSize.Width, element.DesiredSize.Width), element.DesiredSize.Height); offset += element.DesiredSize.Height; } // Store the new target rectangle if (!stateDict[element].TargetRect.Equals(newTargetRect)) { stateDict[element].TargetChanged = true; stateDict[element].TargetRect = newTargetRect; } } } } }
/// <summary> /// Measure the layout size required to arrange all elements. /// </summary> /// <param name="layoutId">Identifier of the layout to be used.</param> /// <param name="metaPanel">Reference to owning panel instance.</param> /// <param name="stateDict">Dictionary of per-element state.</param> /// <param name="elements">Collection of elements to be measured.</param> /// <param name="availableSize">Available size that can be given to elements.</param> /// <returns>Size the layout determines it needs based on child element sizes.</returns> public override Size MeasureChildren(string layoutId, MetaPanelBase metaPanel, MetaElementStateDict stateDict, ICollection elements, Size availableSize) { // Only apply if we match the incoming layout identifier if (string.IsNullOrEmpty(Id) || Id.Equals(layoutId)) { // Calculate number of rows and columns needed to show all children UpdateRowsColumns(stateDict, elements); // Each element should be equal sized according to grid cell size Size elementSize = new Size(availableSize.Width / (double)_columns, availableSize.Height / (double)_rows); // Measure each element in turn double widest = 0; double tallest = 0; foreach (UIElement element in elements) { element.Measure(elementSize); if (element.Visibility != Visibility.Collapsed) { // We ignore items being removed if (stateDict[element].Status != MetaElementStatus.Removing) { // Track widest/tallest for calculating cell size later widest = Math.Max(widest, element.DesiredSize.Width); tallest = Math.Max(tallest, element.DesiredSize.Height); } } } // We would like a size that ensures each cell is big enough for the biggest child return(new Size(_columns * widest, _rows * tallest)); } else { return(Size.Empty); } }
/// <summary> /// Position child elements according to already calculated target state. /// </summary> /// <param name="layoutId">Identifier of the layout to be used.</param> /// <param name="metaPanel">Reference to owning panel instance.</param> /// <param name="stateDict">Dictionary of per-element state.</param> /// <param name="elements">Collection of elements to be arranged.</param> /// <param name="finalSize">Size that layout should use to arrange child elements.</param> /// <returns>Size used by the layout panel.</returns> public virtual Size ArrangeChildren(string layoutId, MetaPanelBase metaPanel, MetaElementStateDict stateDict, ICollection elements, Size finalSize) { // Only apply if we match the incoming layout identifier if (string.IsNullOrEmpty(Id) || Id.Equals(layoutId)) { // Position each element foreach (UIElement element in elements) { MetaElementState elementState = stateDict[element]; elementState.Element.Arrange(elementState.CurrentRect); } } // Layout panel takes all the provided size return(finalSize); }
/// <summary> /// Position child elements according to already calculated target state. /// </summary> /// <param name="layoutId">Identifier of the layout to be used.</param> /// <param name="metaPanel">Reference to owning panel instance.</param> /// <param name="stateDict">Dictionary of per-element state.</param> /// <param name="elements">Collection of elements to be arranged.</param> /// <param name="finalSize">Size that layout should use to arrange child elements.</param> /// <returns>Size used by the layout panel.</returns> public virtual Size ArrangeChildren(string layoutId, MetaPanelBase metaPanel, MetaElementStateDict stateDict, ICollection elements, Size finalSize) { // Only apply if we match the incoming layout identifier if (string.IsNullOrEmpty(Id) || Id.Equals(layoutId)) { // Position each element foreach (UIElement element in elements) { MetaElementState elementState = stateDict[element]; elementState.Element.Arrange(elementState.CurrentRect); } } // Layout panel takes all the provided size return finalSize; }
/// <summary> /// Measure the layout size required to arrange all elements. /// </summary> /// <param name="layoutId">Identifier of the layout to be used.</param> /// <param name="metaPanel">Reference to owning panel instance.</param> /// <param name="stateDict">Dictionary of per-element state.</param> /// <param name="elements">Collection of elements to be measured.</param> /// <param name="availableSize">Available size that can be given to elements.</param> /// <returns>Size the layout determines it needs based on child element sizes.</returns> public override Size MeasureChildren(string layoutId, MetaPanelBase metaPanel, MetaElementStateDict stateDict, ICollection elements, Size availableSize) { Size retSize = new Size(); // Only apply if we match the incoming layout identifier if (string.IsNullOrEmpty(Id) || Id.Equals(layoutId)) { foreach (UIElement element in elements) { stateDict[element].Element.Measure(availableSize); retSize.Width = Math.Max(retSize.Width, stateDict[element].Element.DesiredSize.Width); retSize.Height = Math.Max(retSize.Height, stateDict[element].Element.DesiredSize.Height); } } return(retSize); }
/// <summary> /// Measure the layout size required to arrange all elements. /// </summary> /// <param name="layoutId">Identifier of the layout to be used.</param> /// <param name="metaPanel">Reference to owning panel instance.</param> /// <param name="stateDict">Dictionary of per-element state.</param> /// <param name="elements">Collection of elements to be measured.</param> /// <param name="availableSize">Available size that can be given to elements.</param> /// <returns>Size the layout determines it needs based on child element sizes.</returns> public override Size MeasureChildren(string layoutId, MetaPanelBase metaPanel, MetaElementStateDict stateDict, ICollection elements, Size availableSize) { Size retSize = new Size(); // Only apply if we match the incoming layout identifier if (string.IsNullOrEmpty(Id) || Id.Equals(layoutId)) { foreach (UIElement element in elements) { stateDict[element].Element.Measure(availableSize); retSize.Width = Math.Max(retSize.Width, stateDict[element].Element.DesiredSize.Width); retSize.Height = Math.Max(retSize.Height, stateDict[element].Element.DesiredSize.Height); } } return retSize; }
/// <summary> /// Measure the layout size required to arrange all elements. /// </summary> /// <param name="layoutId">Identifier of the layout to be used.</param> /// <param name="metaPanel">Reference to owning panel instance.</param> /// <param name="stateDict">Dictionary of per-element state.</param> /// <param name="elements">Collection of elements to be measured.</param> /// <param name="availableSize">Available size that can be given to elements.</param> /// <returns>Size the layout determines it needs based on child element sizes.</returns> public override Size MeasureChildren(string layoutId, MetaPanelBase metaPanel, MetaElementStateDict stateDict, ICollection elements, Size availableSize) { // Measure each element in turn _count = 0; Size maxSize = new Size(); foreach (UIElement element in elements) { if (element != null) { element.Measure(Utility.SizeInfinity); // We ignore items being removed if (stateDict[element].Status != MetaElementStatus.Removing) { // We ignore items that are collapsed if (element.Visibility != Visibility.Collapsed) { // Find the widest and tallest element maxSize.Width = Math.Max(maxSize.Width, element.DesiredSize.Width); maxSize.Height = Math.Max(maxSize.Height, element.DesiredSize.Height); // Count number of valid elements _count++; } } } } // Max length is the diagonal length of biggest element _maxLength = Math.Sqrt((maxSize.Width * maxSize.Width) + (maxSize.Height * maxSize.Height)); // Always use the provided size return(availableSize); }
/// <summary> /// Measure the layout size required to arrange all elements. /// </summary> /// <param name="layoutId">Identifier of the layout to be used.</param> /// <param name="metaPanel">Reference to owning panel instance.</param> /// <param name="stateDict">Dictionary of per-element state.</param> /// <param name="elements">Collection of elements to be measured.</param> /// <param name="availableSize">Available size that can be given to elements.</param> /// <returns>Size the layout determines it needs based on child element sizes.</returns> public override Size MeasureChildren(string layoutId, MetaPanelBase metaPanel, MetaElementStateDict stateDict, ICollection elements, Size availableSize) { Size retSize = new Size(); // Only apply if we match the incoming layout identifier if (string.IsNullOrEmpty(Id) || Id.Equals(layoutId)) { // If there are no column/row definitions then size to available area if ((_columns.Count == 0) && (_rows.Count == 0)) { foreach (UIElement element in elements) { stateDict[element].Element.Measure(availableSize); retSize.Width = Math.Max(retSize.Width, stateDict[element].Element.DesiredSize.Width); retSize.Height = Math.Max(retSize.Height, stateDict[element].Element.DesiredSize.Height); } } else { BuildProxyColumns(); BuildProxyRows(); PreMeasure(availableSize); MeasureElements(stateDict, elements); UpdateDefinitions(stateDict, elements, false); UpdateDefinitions(stateDict, elements, true); CalculateOffsetsAndTotals(); retSize = CalculateDesiredSize(stateDict, elements, availableSize); } } return(retSize); }
/// <summary> /// Calculate target state for each element based on layout algorithm. /// </summary> /// <param name="layoutId">Identifier of the layout to be used.</param> /// <param name="metaPanel">Reference to owning panel instance.</param> /// <param name="stateDict">Dictionary of per-element state.</param> /// <param name="elements">Collection of elements to be arranged.</param> /// <param name="finalSize">Size that layout should use to arrange child elements.</param> public override void TargetChildren(string layoutId, MetaPanelBase metaPanel, MetaElementStateDict stateDict, ICollection elements, Size finalSize) { // Only apply if we match the incoming layout identifier if (string.IsNullOrEmpty(Id) || Id.Equals(layoutId)) { // Calculate the target rectangle for each element foreach (UIElement element in elements) { // We ignore items being removed if (stateDict[element].Status != MetaElementStatus.Removing) { // Default to being the desired size but at position zero,zero Rect newTargetRect = new Rect(0, 0, element.DesiredSize.Width, element.DesiredSize.Height); // Use the Left or Right value provided as attached properties of the element double left = CanvasLayout.GetLeft(element); if (!double.IsNaN(left)) newTargetRect.X = left; else { double right = CanvasLayout.GetRight(element); if (!double.IsNaN(right)) newTargetRect.X = (finalSize.Width - element.DesiredSize.Width) - right; } // Use the Top or Bottom value provided as attached properties of the element double top = CanvasLayout.GetTop(element); if (!double.IsNaN(top)) newTargetRect.Y = top; else { double bottom = CanvasLayout.GetBottom(element); if (!double.IsNaN(bottom)) newTargetRect.Y = (finalSize.Height - element.DesiredSize.Height) - bottom; } // Store the new target rectangle if (!stateDict[element].TargetRect.Equals(newTargetRect)) { stateDict[element].TargetChanged = true; stateDict[element].TargetRect = newTargetRect; } } } } }
/// <summary> /// Calculate appropriate rectangle from given current state and target location. /// </summary> /// <param name="location">Location enumeration.</param> /// <param name="metaPanel">Reference to owning panel instance.</param> /// <param name="elementState">Animation state of element.</param> /// <returns>Calculated rectangle using provided location.</returns> protected Rect RectFromLocation(AnimateLocation location, MetaPanelBase metaPanel, MetaElementState elementState) { // Nearest edge is converted into a particular edge switch (location) { case AnimateLocation.NearestEdge: case AnimateLocation.NearestEdgePaged: bool paged = (location == AnimateLocation.NearestEdgePaged); // Find distance from each edge double left = Math.Abs(elementState.TargetRect.Left); double top = Math.Abs(elementState.TargetRect.Top); double right = Math.Abs(metaPanel.ActualWidth - elementState.TargetRect.Right); double bottom = Math.Abs(metaPanel.ActualHeight - elementState.TargetRect.Bottom); // Find nearest distance for vertical and horizontal double horz = (left < right ? left : right); double vert = (top < bottom ? top : bottom); // Is horizontal nearest? if (horz <= vert) { // Is the left the nearest? if (horz == left) location = (paged ? AnimateLocation.LeftPaged : AnimateLocation.Left); else location = (paged ? AnimateLocation.RightPaged : AnimateLocation.Right); } else { // Is the top the nearest? if (vert == top) location = (paged ? AnimateLocation.TopPaged : AnimateLocation.Top); else location = (paged ? AnimateLocation.BottomPaged : AnimateLocation.Bottom); } break; } switch (location) { case AnimateLocation.Target: return elementState.TargetRect; case AnimateLocation.Center: return new Rect((metaPanel.ActualWidth / 2) - (elementState.TargetRect.Width - 2), (metaPanel.ActualHeight / 2) - (elementState.TargetRect.Height - 2), elementState.TargetRect.Width, elementState.TargetRect.Height); case AnimateLocation.Top: return new Rect(elementState.TargetRect.X, -elementState.TargetRect.Height, elementState.TargetRect.Width, elementState.TargetRect.Height); case AnimateLocation.TopPaged: return new Rect(elementState.TargetRect.X, -metaPanel.ActualHeight + elementState.TargetRect.Y, elementState.TargetRect.Width, elementState.TargetRect.Height); case AnimateLocation.Bottom: return new Rect(elementState.TargetRect.X, metaPanel.ActualHeight, elementState.TargetRect.Width, elementState.TargetRect.Height); case AnimateLocation.BottomPaged: return new Rect(elementState.TargetRect.X, metaPanel.ActualHeight + elementState.TargetRect.Y, elementState.TargetRect.Width, elementState.TargetRect.Height); case AnimateLocation.Left: return new Rect(-elementState.TargetRect.Width, elementState.TargetRect.Y, elementState.TargetRect.Width, elementState.TargetRect.Height); case AnimateLocation.LeftPaged: return new Rect(-metaPanel.ActualWidth + elementState.TargetRect.X, elementState.TargetRect.Y, elementState.TargetRect.Width, elementState.TargetRect.Height); case AnimateLocation.Right: return new Rect(metaPanel.ActualWidth, elementState.TargetRect.Y, elementState.TargetRect.Width, elementState.TargetRect.Height); case AnimateLocation.RightPaged: return new Rect(metaPanel.ActualWidth + elementState.TargetRect.X, elementState.TargetRect.Y, elementState.TargetRect.Width, elementState.TargetRect.Height); case AnimateLocation.TopLeft: return new Rect(-elementState.TargetRect.Width, -elementState.TargetRect.Height, elementState.TargetRect.Width, elementState.TargetRect.Height); case AnimateLocation.TopRight: return new Rect(metaPanel.ActualWidth, -elementState.TargetRect.Height, elementState.TargetRect.Width, elementState.TargetRect.Height); case AnimateLocation.BottomLeft: return new Rect(-elementState.TargetRect.Width, metaPanel.ActualHeight, elementState.TargetRect.Width, elementState.TargetRect.Height); case AnimateLocation.BottomRight: return new Rect(metaPanel.ActualWidth, metaPanel.ActualHeight, elementState.TargetRect.Width, elementState.TargetRect.Height); default: // Should never happen! Debug.Assert(false); return new Rect(); } }
/// <summary> /// Measure the layout size required to arrange all elements. /// </summary> /// <param name="layoutId">Identifier of the layout to be used.</param> /// <param name="metaPanel">Reference to owning panel instance.</param> /// <param name="stateDict">Dictionary of per-element state.</param> /// <param name="elements">Collection of elements to be measured.</param> /// <param name="availableSize">Available size that can be given to elements.</param> /// <returns>Size the layout determines it needs based on child element sizes.</returns> public override Size MeasureChildren(string layoutId, MetaPanelBase metaPanel, MetaElementStateDict stateDict, ICollection elements, Size availableSize) { // Only apply if we match the incoming layout identifier if (string.IsNullOrEmpty(Id) || Id.Equals(layoutId)) { // Cache the dependancy properties for faster perf double itemWidth = ItemWidth; double itemHeight = ItemHeight; Orientation orientation = Orientation; // Cache if the defined sizes are valid bool itemWidthDefined = !double.IsNaN(ItemWidth); bool itemHeightDefined = !double.IsNaN(itemHeight); // Find size we provide to each child for measuring against, we use item sizes if defined Size elementSize = new Size(itemWidthDefined ? itemWidth : double.PositiveInfinity, itemHeightDefined ? itemHeight : double.PositiveInfinity); // Measure each element in turn bool breakAfter = false; double lineMax = 0; double lineOffset = 0; Size measureSize = new Size(); foreach (UIElement element in elements) { element.Measure(elementSize); // We ignore items being removed if (stateDict[element].Status != MetaElementStatus.Removing) { // Decide on the actual size we will allocate to the element Size childSize = new Size(itemWidthDefined ? itemWidth : element.DesiredSize.Width, itemHeightDefined ? itemHeight : element.DesiredSize.Height); // Calculation depends on orientation if (orientation == Orientation.Horizontal) { // Does this element overflow the line? if (breakAfter || (((lineOffset + childSize.Width) > availableSize.Width) && !measureSize.IsEmpty)) { // Total measured size is equal to the widest line and tallest item per line measureSize.Width = Math.Max(measureSize.Width, lineOffset); measureSize.Height += lineMax; // Start at the left edge of the next line lineOffset = 0; lineMax = 0; } // Position the child on the current line lineOffset += childSize.Width; lineMax = Math.Max(lineMax, childSize.Height); } else { // Does this element overflow the line? if (breakAfter || (((lineOffset + childSize.Height) > availableSize.Height) && !measureSize.IsEmpty)) { // Total measured size is equal to the tallest line and widest item per line measureSize.Height = Math.Max(measureSize.Height, lineOffset); measureSize.Width += lineMax; // Start at the top edge of the next line lineOffset = 0; lineMax = 0; } // Position the child on the current line lineOffset += childSize.Height; lineMax = Math.Max(lineMax, childSize.Width); } // Do we perform a line break after this element breakAfter = WrapLayout.GetBreakAfter(element); } } // Remember to take into account the last line if (orientation == Orientation.Horizontal) { // Total measured size is equal to the widest line and tallest item per line measureSize.Width = Math.Max(measureSize.Width, lineOffset); measureSize.Height += lineMax; } else { // Total measured size is equal to the tallest line and widest item per line measureSize.Height = Math.Max(measureSize.Height, lineOffset); measureSize.Width += lineMax; } // Return minimum size needed to contain all the elements according to their desired sizes return measureSize; } else return Size.Empty; }
/// <summary> /// Measure the layout size required to arrange all elements. /// </summary> /// <param name="layoutId">Identifier of the layout to be used.</param> /// <param name="metaPanel">Reference to owning panel instance.</param> /// <param name="stateDict">Dictionary of per-element state.</param> /// <param name="elements">Collection of elements to be measured.</param> /// <param name="availableSize">Available size that can be given to elements.</param> /// <returns>Size the layout determines it needs based on child element sizes.</returns> public override Size MeasureChildren(string layoutId, MetaPanelBase metaPanel, MetaElementStateDict stateDict, ICollection elements, Size availableSize) { // Only apply if we match the incoming layout identifier if (string.IsNullOrEmpty(Id) || Id.Equals(layoutId)) { // Measure each element in turn foreach (UIElement element in elements) stateDict[element].Element.Measure(Utility.SizeInfinity); } // Return an empty size return Size.Empty; }
/// <summary> /// Measure the layout size required to arrange all elements. /// </summary> /// <param name="layoutId">Identifier of the layout to be used.</param> /// <param name="metaPanel">Reference to owning panel instance.</param> /// <param name="stateDict">Dictionary of per-element state.</param> /// <param name="elements">Collection of elements to be measured.</param> /// <param name="availableSize">Available size that can be given to elements.</param> /// <returns>Size the layout determines it needs based on child element sizes.</returns> public abstract Size MeasureChildren(string layoutId, MetaPanelBase metaPanel, MetaElementStateDict stateDict, ICollection elements, Size availableSize);
/// <summary> /// Calculate target state for each element based on layout algorithm. /// </summary> /// <param name="layoutId">Identifier of the layout to be used.</param> /// <param name="metaPanel">Reference to owning panel instance.</param> /// <param name="stateDict">Dictionary of per-element state.</param> /// <param name="elements">Collection of elements to be arranged.</param> /// <param name="finalSize">Size that layout should use to arrange child elements.</param> public override void TargetChildren(string layoutId, MetaPanelBase metaPanel, MetaElementStateDict stateDict, ICollection elements, Size finalSize) { // Only apply if we match the incoming layout identifier if (string.IsNullOrEmpty(Id) || Id.Equals(layoutId)) { // Cache the dependancy properties for faster perf double itemWidth = ItemWidth; double itemHeight = ItemHeight; Orientation orientation = Orientation; // Cache if the defined sizes are valid bool itemWidthDefined = !double.IsNaN(ItemWidth); bool itemHeightDefined = !double.IsNaN(itemHeight); // Find size we provide to each child for measuring against, we use item sizes if defined Size elementSize = new Size(itemWidthDefined ? itemWidth : double.PositiveInfinity, itemHeightDefined ? itemHeight : double.PositiveInfinity); // Measure each element in turn int current = 0; double lineOffset = 0; double lineMax = 0; double positionOffset = 0; bool breakAfter = false; List<UIElement> targets = new List<UIElement>(); foreach (UIElement element in elements) { // We ignore items being removed if (stateDict[element].Status != MetaElementStatus.Removing) { // Decide on the actual size we will allocate to the element Size childSize = new Size(itemWidthDefined ? itemWidth : element.DesiredSize.Width, itemHeightDefined ? itemHeight : element.DesiredSize.Height); // Calculation depends on orientation if (orientation == Orientation.Horizontal) { // Does this element overflow the line? if (breakAfter || (((lineOffset + childSize.Width) > finalSize.Width) && (current > 0))) { // Create position targets for items on this line TargetLine(stateDict, targets, positionOffset, lineMax, itemWidthDefined, itemHeightDefined, itemWidth, itemHeight, finalSize, orientation); // Move positioning down by height of the line positionOffset += lineMax; // Start at the left edge of the next line lineOffset = 0; lineMax = 0; targets.Clear(); } // Position the child on the current line lineOffset += childSize.Width; lineMax = Math.Max(lineMax, childSize.Height); } else { // Does this element overflow the line? if (breakAfter || (((lineOffset + childSize.Height) > finalSize.Height) && (current > 0))) { // Create position targets for items on this line TargetLine(stateDict, targets, positionOffset, lineMax, itemWidthDefined, itemHeightDefined, itemWidth, itemHeight, finalSize, orientation); // Move positioning down by height of the line positionOffset += lineMax; // Start at the left edge of the next line lineOffset = 0; lineMax = 0; targets.Clear(); } // Position the child on the current line lineOffset += childSize.Height; lineMax = Math.Max(lineMax, childSize.Width); } targets.Add(element); current++; // Do we perform a line break after this element breakAfter = WrapLayout.GetBreakAfter(element); } } // Remember to take into account the last line TargetLine(stateDict, targets, positionOffset, lineMax, itemWidthDefined, itemHeightDefined, itemWidth, itemHeight, finalSize, orientation); } }
/// <summary> /// Measure the layout size required to arrange all elements. /// </summary> /// <param name="layoutId">Identifier of the layout to be used.</param> /// <param name="metaPanel">Reference to owning panel instance.</param> /// <param name="stateDict">Dictionary of per-element state.</param> /// <param name="elements">Collection of elements to be measured.</param> /// <param name="availableSize">Available size that can be given to elements.</param> /// <returns>Size the layout determines it needs based on child element sizes.</returns> public override Size MeasureChildren(string layoutId, MetaPanelBase metaPanel, MetaElementStateDict stateDict, ICollection elements, Size availableSize) { // Only apply if we match the incoming layout identifier if (string.IsNullOrEmpty(Id) || Id.Equals(layoutId)) { // Cache the dependancy properties for faster perf double itemWidth = ItemWidth; double itemHeight = ItemHeight; Orientation orientation = Orientation; // Cache if the defined sizes are valid bool itemWidthDefined = !double.IsNaN(ItemWidth); bool itemHeightDefined = !double.IsNaN(itemHeight); // Find size we provide to each child for measuring against, we use item sizes if defined Size elementSize = new Size(itemWidthDefined ? itemWidth : double.PositiveInfinity, itemHeightDefined ? itemHeight : double.PositiveInfinity); // Measure each element in turn bool breakAfter = false; double lineMax = 0; double lineOffset = 0; Size measureSize = new Size(); foreach (UIElement element in elements) { element.Measure(elementSize); // We ignore items being removed if (stateDict[element].Status != MetaElementStatus.Removing) { // Decide on the actual size we will allocate to the element Size childSize = new Size(itemWidthDefined ? itemWidth : element.DesiredSize.Width, itemHeightDefined ? itemHeight : element.DesiredSize.Height); // Calculation depends on orientation if (orientation == Orientation.Horizontal) { // Does this element overflow the line? if (breakAfter || (((lineOffset + childSize.Width) > availableSize.Width) && !measureSize.IsEmpty)) { // Total measured size is equal to the widest line and tallest item per line measureSize.Width = Math.Max(measureSize.Width, lineOffset); measureSize.Height += lineMax; // Start at the left edge of the next line lineOffset = 0; lineMax = 0; } // Position the child on the current line lineOffset += childSize.Width; lineMax = Math.Max(lineMax, childSize.Height); } else { // Does this element overflow the line? if (breakAfter || (((lineOffset + childSize.Height) > availableSize.Height) && !measureSize.IsEmpty)) { // Total measured size is equal to the tallest line and widest item per line measureSize.Height = Math.Max(measureSize.Height, lineOffset); measureSize.Width += lineMax; // Start at the top edge of the next line lineOffset = 0; lineMax = 0; } // Position the child on the current line lineOffset += childSize.Height; lineMax = Math.Max(lineMax, childSize.Width); } // Do we perform a line break after this element breakAfter = WrapLayout.GetBreakAfter(element); } } // Remember to take into account the last line if (orientation == Orientation.Horizontal) { // Total measured size is equal to the widest line and tallest item per line measureSize.Width = Math.Max(measureSize.Width, lineOffset); measureSize.Height += lineMax; } else { // Total measured size is equal to the tallest line and widest item per line measureSize.Height = Math.Max(measureSize.Height, lineOffset); measureSize.Width += lineMax; } // Return minimum size needed to contain all the elements according to their desired sizes return(measureSize); } else { return(Size.Empty); } }
/// <summary> /// Measure the layout size required to arrange all elements. /// </summary> /// <param name="layoutId">Identifier of the layout to be used.</param> /// <param name="metaPanel">Reference to owning panel instance.</param> /// <param name="stateDict">Dictionary of per-element state.</param> /// <param name="elements">Collection of elements to be measured.</param> /// <param name="availableSize">Available size that can be given to elements.</param> /// <returns>Size the layout determines it needs based on child element sizes.</returns> public override Size MeasureChildren(string layoutId, MetaPanelBase metaPanel, MetaElementStateDict stateDict, ICollection elements, Size availableSize) { // Measure each element in turn _count = 0; Size maxSize = new Size(); foreach (UIElement element in elements) if (element != null) { element.Measure(Utility.SizeInfinity); // We ignore items being removed if (stateDict[element].Status != MetaElementStatus.Removing) { // We ignore items that are collapsed if (element.Visibility != Visibility.Collapsed) { // Find the widest and tallest element maxSize.Width = Math.Max(maxSize.Width, element.DesiredSize.Width); maxSize.Height = Math.Max(maxSize.Height, element.DesiredSize.Height); // Count number of valid elements _count++; } } } // Max length is the diagonal length of biggest element _maxLength = Math.Sqrt((maxSize.Width * maxSize.Width) + (maxSize.Height * maxSize.Height)); // Always use the provided size return availableSize; }
/// <summary> /// Perform animation effects on the set of children. /// </summary> /// <param name="animateId">Identifier of the animate to be used.</param> /// <param name="metaPanel">Reference to owning panel instance.</param> /// <param name="stateDict">Dictionary of per-element state.</param> /// <param name="elements">Collection of elements to be animated.</param> /// <param name="elapsedMilliseconds">Elapsed milliseconds since last animation cycle.</param> public abstract void ApplyAnimation(string animateId, MetaPanelBase metaPanel, MetaElementStateDict stateDict, ICollection elements, double elapsedMilliseconds);
/// <summary> /// Perform animation effects on the set of children. /// </summary> /// <param name="animateId">Identifier of the animate to be used.</param> /// <param name="metaPanel">Reference to owning panel instance.</param> /// <param name="stateDict">Dictionary of per-element state.</param> /// <param name="elements">Collection of elements to be animated.</param> /// <param name="elapsedMilliseconds">Elapsed milliseconds since last animation cycle.</param> public override void ApplyAnimation(string animateId, MetaPanelBase metaPanel, MetaElementStateDict stateDict, ICollection elements, double elapsedMilliseconds) { // Only apply if we match the incoming animate identifier if (string.IsNullOrEmpty(Id) || Id.Equals(animateId)) { // Update the easing equation with latest value EasingCalculation.Easing = Easing; // Cache dependancy properties for faster perf double duration = Math.Max((double)1, Duration); foreach (UIElement element in elements) { // Only apply animation to element of required target state MetaElementState elementState = stateDict[element]; if (elementState.Status == Target) { Rect targetRect = elementState.TargetRect; Rect currentRect = (elementState.CurrentRect.IsEmpty ? targetRect : elementState.CurrentRect); // If start of animation.... if (elementState.TargetChanged) { // Cache starting information elementState.StartRect = currentRect; elementState.ElapsedBoundsTime = 0; } else { // Only perform animation if some time has actually ellapsed and not already at target if ((elapsedMilliseconds > 0) && !currentRect.Equals(targetRect)) { // Add new elapsed time to the animation running time elementState.ElapsedBoundsTime += elapsedMilliseconds; // Does elapsed time indicate animation should have completed? if (elementState.ElapsedBoundsTime >= duration) currentRect = targetRect; else { Rect startRect = elementState.StartRect; double elapsedTime = elementState.ElapsedBoundsTime; // Using animation easing to discover new target rectangle corners double left = EasingCalculation.Calculate(elapsedTime, startRect.X, targetRect.X - startRect.X, duration); double top = EasingCalculation.Calculate(elapsedTime, startRect.Y, targetRect.Y - startRect.Y, duration); double bottom = EasingCalculation.Calculate(elapsedTime, startRect.Bottom, targetRect.Bottom - startRect.Bottom, duration); double right = EasingCalculation.Calculate(elapsedTime, startRect.Right, targetRect.Right - startRect.Right, duration); // Normalize edges left/right edges if (left > right) { elapsedTime = left; left = right; right = elapsedTime; } // Normalize edges top/bottom edges if (top > bottom) { elapsedTime = top; top = bottom; bottom = elapsedTime; } currentRect = new Rect(left, top, right - left, bottom - top); } } } // Put back the updated rectangle and decide if more animation is needed elementState.CurrentRect = currentRect; elementState.AnimateComplete &= (currentRect.Equals(targetRect)); } } } }
/// <summary> /// Measure the layout size required to arrange all elements. /// </summary> /// <param name="layoutId">Identifier of the layout to be used.</param> /// <param name="metaPanel">Reference to owning panel instance.</param> /// <param name="stateDict">Dictionary of per-element state.</param> /// <param name="elements">Collection of elements to be measured.</param> /// <param name="availableSize">Available size that can be given to elements.</param> /// <returns>Size the layout determines it needs based on child element sizes.</returns> public override Size MeasureChildren(string layoutId, MetaPanelBase metaPanel, MetaElementStateDict stateDict, ICollection elements, Size availableSize) { // Only apply if we match the incoming layout identifier if (string.IsNullOrEmpty(Id) || Id.Equals(layoutId)) { // Measure each element in turn _validCount = 0; Size usedSize = new Size(); Size maxSize = new Size(); foreach (UIElement element in elements) { // Allow element to have all the remainder size available Size elementSize = new Size(Math.Max(0.0, (double)(availableSize.Width - usedSize.Width)), Math.Max(0.0, (double)(availableSize.Height - usedSize.Height))); element.Measure(elementSize); // We ignore items being removed if (stateDict[element].Status != MetaElementStatus.Removing) { // Discover the used size and the maximum required size for opposite direction Size desiredSize = element.DesiredSize; switch (GetDock(element)) { case Dock.Left: case Dock.Right: maxSize.Height = Math.Max(maxSize.Height, usedSize.Height + desiredSize.Height); usedSize.Width += desiredSize.Width; break; case Dock.Top: case Dock.Bottom: maxSize.Width = Math.Max(maxSize.Width, usedSize.Width + desiredSize.Width); usedSize.Height += desiredSize.Height; break; } _validCount++; } } return new Size(Math.Max(maxSize.Width, usedSize.Width), Math.Max(maxSize.Height, usedSize.Height)); } else return Size.Empty; }
/// <summary> /// Calculate target state for each element based on layout algorithm. /// </summary> /// <param name="layoutId">Identifier of the layout to be used.</param> /// <param name="metaPanel">Reference to owning panel instance.</param> /// <param name="stateDict">Dictionary of per-element state.</param> /// <param name="elements">Collection of elements to be arranged.</param> /// <param name="finalSize">Size that layout should use to arrange child elements.</param> public override void TargetChildren(string layoutId, MetaPanelBase metaPanel, MetaElementStateDict stateDict, ICollection elements, Size finalSize) { // Only apply if we match the incoming layout identifier if (string.IsNullOrEmpty(Id) || Id.Equals(layoutId)) { double x = 0.0; double y = 0.0; double right = 0.0; double bottom = 0.0; // If using LastChildFill then the last element takes up all the remainder space int lastDockElement = (LastChildFill ? _validCount - 1 : _validCount); // Calculate the target rectangle for each element int i = 0; foreach (UIElement element in elements) { // We ignore items being removed if (stateDict[element].Status != MetaElementStatus.Removing) { // Calculate the maximum possible rect for the element Rect newTargetRect = new Rect(x, y, Math.Max(0.0, (double)(finalSize.Width - (x + right))), Math.Max(0.0, (double)(finalSize.Height - (y + bottom)))); if (i < lastDockElement) { // Constrain target rect by the dock edge Size desiredSize = element.DesiredSize; switch (GetDock(element)) { case Dock.Left: x += desiredSize.Width; newTargetRect.Width = desiredSize.Width; break; case Dock.Top: y += desiredSize.Height; newTargetRect.Height = desiredSize.Height; break; case Dock.Right: right += desiredSize.Width; newTargetRect.X = Math.Max(0.0, (double)(finalSize.Width - right)); newTargetRect.Width = desiredSize.Width; break; case Dock.Bottom: bottom += desiredSize.Height; newTargetRect.Y = Math.Max(0.0, (double)(finalSize.Height - bottom)); newTargetRect.Height = desiredSize.Height; break; } } // Store the new target rectangle if (!stateDict[element].TargetRect.Equals(newTargetRect)) { stateDict[element].TargetChanged = true; stateDict[element].TargetRect = newTargetRect; } i++; } } } }
/// <summary> /// Measure the layout size required to arrange all elements. /// </summary> /// <param name="layoutId">Identifier of the layout to be used.</param> /// <param name="metaPanel">Reference to owning panel instance.</param> /// <param name="stateDict">Dictionary of per-element state.</param> /// <param name="elements">Collection of elements to be measured.</param> /// <param name="availableSize">Available size that can be given to elements.</param> /// <returns>Size the layout determines it needs based on child element sizes.</returns> public override Size MeasureChildren(string layoutId, MetaPanelBase metaPanel, MetaElementStateDict stateDict, ICollection elements, Size availableSize) { Size retSize = new Size(); // Only apply if we match the incoming layout identifier if (string.IsNullOrEmpty(Id) || Id.Equals(layoutId)) { // If there are no column/row definitions then size to available area if ((_columns.Count == 0) && (_rows.Count == 0)) { foreach (UIElement element in elements) { stateDict[element].Element.Measure(availableSize); retSize.Width = Math.Max(retSize.Width, stateDict[element].Element.DesiredSize.Width); retSize.Height = Math.Max(retSize.Height, stateDict[element].Element.DesiredSize.Height); } } else { BuildProxyColumns(); BuildProxyRows(); PreMeasure(availableSize); MeasureElements(stateDict, elements); UpdateDefinitions(stateDict, elements, false); UpdateDefinitions(stateDict, elements, true); CalculateOffsetsAndTotals(); retSize = CalculateDesiredSize(stateDict, elements, availableSize); } } return retSize; }
/// <summary> /// Calculate target state for each element based on layout algorithm. /// </summary> /// <param name="layoutId">Identifier of the layout to be used.</param> /// <param name="metaPanel">Reference to owning panel instance.</param> /// <param name="stateDict">Dictionary of per-element state.</param> /// <param name="elements">Collection of elements to be arranged.</param> /// <param name="finalSize">Size that layout should use to arrange child elements.</param> public override void TargetChildren(string layoutId, MetaPanelBase metaPanel, MetaElementStateDict stateDict, ICollection elements, Size finalSize) { // Only apply if we match the incoming layout identifier if (string.IsNullOrEmpty(Id) || Id.Equals(layoutId)) { // If there are no column/row definitions then arrange to entire final size if ((_columns.Count == 0) && (_rows.Count == 0)) { Rect newTargetRect = new Rect(Utility.PointZero, finalSize); foreach (UIElement element in elements) { // We ignore items being removed if (stateDict[element].Status != MetaElementStatus.Removing) { // Store the new target rectangle if (!stateDict[element].TargetRect.Equals(newTargetRect)) { stateDict[element].TargetChanged = true; stateDict[element].TargetRect = newTargetRect; } } } } else { // Calculate final width/height of col/row definitions PreTarget(finalSize); // Update new target rectangle for each non-removing element foreach (UIElement element in elements) { // We ignore items being removed if (stateDict[element].Status != MetaElementStatus.Removing) { // Find the arrange size for the element Rect newTargetRect = TargetArrangeSize(element); // Store the new target rectangle if (!stateDict[element].TargetRect.Equals(newTargetRect)) { stateDict[element].TargetChanged = true; stateDict[element].TargetRect = newTargetRect; } } } } } // Update rows and columns with actual widths/heights for (int i = 0; i < ColumnDefinitions.Count; i++) ColumnDefinitions[i].ActualWidth = _proxyColumns[i].MinSize; for (int i = 0; i < RowDefinitions.Count; i++) RowDefinitions[i].ActualHeight = _proxyRows[i].MinSize; }
/// <summary> /// Calculate target state for each element based on layout algorithm. /// </summary> /// <param name="layoutId">Identifier of the layout to be used.</param> /// <param name="metaPanel">Reference to owning panel instance.</param> /// <param name="stateDict">Dictionary of per-element state.</param> /// <param name="elements">Collection of elements to be arranged.</param> /// <param name="finalSize">Size that layout should use to arrange child elements.</param> public override void TargetChildren(string layoutId, MetaPanelBase metaPanel, MetaElementStateDict stateDict, ICollection elements, Size finalSize) { // Only apply if we match the incoming layout identifier if (string.IsNullOrEmpty(Id) || Id.Equals(layoutId)) { // Cache the dependancy properties for faster perf double itemWidth = ItemWidth; double itemHeight = ItemHeight; Orientation orientation = Orientation; // Cache if the defined sizes are valid bool itemWidthDefined = !double.IsNaN(ItemWidth); bool itemHeightDefined = !double.IsNaN(itemHeight); // Find size we provide to each child for measuring against, we use item sizes if defined Size elementSize = new Size(itemWidthDefined ? itemWidth : double.PositiveInfinity, itemHeightDefined ? itemHeight : double.PositiveInfinity); // Measure each element in turn int current = 0; double lineOffset = 0; double lineMax = 0; double positionOffset = 0; bool breakAfter = false; List <UIElement> targets = new List <UIElement>(); foreach (UIElement element in elements) { // We ignore items being removed if (stateDict[element].Status != MetaElementStatus.Removing) { // Decide on the actual size we will allocate to the element Size childSize = new Size(itemWidthDefined ? itemWidth : element.DesiredSize.Width, itemHeightDefined ? itemHeight : element.DesiredSize.Height); // Calculation depends on orientation if (orientation == Orientation.Horizontal) { // Does this element overflow the line? if (breakAfter || (((lineOffset + childSize.Width) > finalSize.Width) && (current > 0))) { // Create position targets for items on this line TargetLine(stateDict, targets, positionOffset, lineMax, itemWidthDefined, itemHeightDefined, itemWidth, itemHeight, finalSize, orientation); // Move positioning down by height of the line positionOffset += lineMax; // Start at the left edge of the next line lineOffset = 0; lineMax = 0; targets.Clear(); } // Position the child on the current line lineOffset += childSize.Width; lineMax = Math.Max(lineMax, childSize.Height); } else { // Does this element overflow the line? if (breakAfter || (((lineOffset + childSize.Height) > finalSize.Height) && (current > 0))) { // Create position targets for items on this line TargetLine(stateDict, targets, positionOffset, lineMax, itemWidthDefined, itemHeightDefined, itemWidth, itemHeight, finalSize, orientation); // Move positioning down by height of the line positionOffset += lineMax; // Start at the left edge of the next line lineOffset = 0; lineMax = 0; targets.Clear(); } // Position the child on the current line lineOffset += childSize.Height; lineMax = Math.Max(lineMax, childSize.Width); } targets.Add(element); current++; // Do we perform a line break after this element breakAfter = WrapLayout.GetBreakAfter(element); } } // Remember to take into account the last line TargetLine(stateDict, targets, positionOffset, lineMax, itemWidthDefined, itemHeightDefined, itemWidth, itemHeight, finalSize, orientation); } }
/// <summary> /// Calculate target state for each element based on layout algorithm. /// </summary> /// <param name="layoutId">Identifier of the layout to be used.</param> /// <param name="metaPanel">Reference to owning panel instance.</param> /// <param name="stateDict">Dictionary of per-element state.</param> /// <param name="elements">Collection of elements to be arranged.</param> /// <param name="finalSize">Size that layout should use to arrange child elements.</param> public abstract void TargetChildren(string layoutId, MetaPanelBase metaPanel, MetaElementStateDict stateDict, ICollection elements, Size finalSize);