/// <summary> /// Initialize a new instance of the MetaPanelBase class. /// </summary> public MetaPanelBase() { // List used to collect entries that are to be removed from panel _removeList = new MetaElementStateList(); // Use dictionary to associate each element with its animation state _stateDict = new MetaElementStateDict(); // Create and monitor changes in the layout collection _layoutCollection = new LayoutCollection(this); _layoutCollection.NeedMeasure += new EventHandler(OnNeedMeasure); _layoutCollection.CollectionChanged += new EventHandler(OnNeedMeasure); MonitorExtendElement(_layoutCollection); // Create and monitor changes in the animate collection _animateCollection = new AnimateDefinitions(this); _animateCollection.NeedMeasure += new EventHandler(OnNeedMeasure); MonitorExtendElement(_animateCollection); // Default layout/animate to be applied as the default in case collections are empty or not applied _defaultLayout = new StretchLayout(); _defaultAnimate = new NullAnimate(); // Let the Silverlight/WPF specific construction take place PlatformConstructor(); }
/// <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 void MeasureElements(MetaElementStateDict stateDict, ICollection elements) { foreach (UIElement element in elements) { // Find the row/col definitions for this element int col = Math.Max(0, Math.Min(_columns.Count - 1, GetColumn(element))); int row = Math.Max(0, Math.Min(_rows.Count - 1, GetRow(element))); // Find the row/col spanning definitions for this element (convert to zero based spanning) int colSpan = Math.Max(Math.Min(_columns.Count - col, GetColumnSpan(element)), 1) - 1; int rowSpan = Math.Max(Math.Min(_rows.Count - row, GetRowSpan(element)), 1) - 1; // Find column width to use for measuring int index = col; double measureWidth = 0; do { measureWidth += _proxyColumns[index].MeasureSize; } while ((measureWidth < double.PositiveInfinity) && (index++ < (col + colSpan))); // Find row height to use for measuring index = row; double measureHeight = 0; do { measureHeight += _proxyRows[index].MeasureSize; } while ((measureWidth < double.PositiveInfinity) && (index++ < (row + rowSpan))); stateDict[element].Element.Measure(new Size(measureWidth, measureHeight)); } }
/// <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> /// 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> /// 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) { 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> 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> /// 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> /// 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); } }
private Size TargetTreeNodes(Point location, ITreeNode node, MetaElementStateDict stateDict, bool downwards) { Size totalSize = Utility.SizeZero; UIElement element = node as UIElement; if ((element != null) && (stateDict.ContainsKey(element))) { // Do not measure children of items being removed if (stateDict[element].Status != MetaElementStatus.Removing) { // Starting size covers only the node totalSize = element.DesiredSize; // Process each sub tree Point childLocation = new Point(location.X + element.DesiredSize.Width, location.Y); Size childTotalSize = Utility.SizeZero; foreach (ITreeNode child in node.ChildNodes()) { Size childSize = TargetTreeNodes(childLocation, child, stateDict, downwards); // Stack all the sub trees vertically in sizing childTotalSize.Width = Math.Max(childTotalSize.Width, childSize.Width); childTotalSize.Height += childSize.Height; childLocation.Y += childSize.Height; } // Place the set of sub trees to the right of this node totalSize.Width += childTotalSize.Width; totalSize.Height = Math.Max(totalSize.Height, childTotalSize.Height); } // Position the node vertically centered Rect newTargetRect = new Rect(location.X, location.Y + (totalSize.Height - element.DesiredSize.Height) / 2, element.DesiredSize.Width, element.DesiredSize.Height); // Store the new target rectangle if (!stateDict[element].TargetRect.Equals(newTargetRect)) { stateDict[element].TargetChanged = true; stateDict[element].TargetRect = newTargetRect; } } return(totalSize); }
/// <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); } } } }
/// <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> /// 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> /// 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> /// 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)) { // 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> /// 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); } }
private void ResyncStateDictionary() { MetaElementStateDict newStateDict = new MetaElementStateDict(); // Transfer existing state to new dictionary and create new state for new elements foreach (UIElement element in Children) { if (_stateDict.ContainsKey(element)) { newStateDict.Add(element, _stateDict[element]); } else { newStateDict.Add(element, new MetaElementState(element)); } } // Any removed elements will not have state in new dictionary _stateDict = newStateDict; }
/// <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); }
private void TargetLine(MetaElementStateDict stateDict, List <UIElement> targets, double positionOffset, double lineMax, bool itemWidthDefined, bool itemHeightDefined, double itemWidth, double itemHeight, Size finalSize, Orientation orientation) { double lineOffset = 0; foreach (UIElement element in targets) { // 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); Rect newTargetRect; // Updating the elements target rectangle depends on orientation if (orientation == Orientation.Horizontal) { newTargetRect = new Rect(lineOffset, positionOffset, childSize.Width, lineMax); lineOffset += childSize.Width; } else { newTargetRect = new Rect(positionOffset, lineOffset, lineMax, childSize.Height); lineOffset += childSize.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) { 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); }
private Size MeasureTreeNodes(ITreeNode node, MetaElementStateDict stateDict, bool downwards) { Size totalSize = Utility.SizeZero; UIElement element = node as UIElement; if ((element != null) && (stateDict.ContainsKey(element))) { // Use element size as the starting total size element.Measure(Utility.SizeInfinity); // Do not measure children of items being removed if (stateDict[element].Status != MetaElementStatus.Removing) { // Starting size covers only the node totalSize = element.DesiredSize; // Process each sub tree Size childTotalSize = Utility.SizeZero; foreach (ITreeNode child in node.ChildNodes()) { Size childSize = MeasureTreeNodes(child, stateDict, downwards); // Stack all the sub trees vertically in sizing childTotalSize.Width = Math.Max(childTotalSize.Width, childSize.Width); childTotalSize.Height += childSize.Height; } // Place the set of sub trees to the right of this node totalSize.Width += childTotalSize.Width; totalSize.Height = Math.Max(totalSize.Height, childTotalSize.Height); } } return(totalSize); }
private Size CalculateDesiredSize(MetaElementStateDict stateDict, ICollection elements, Size availableSize) { double width = _proxyColumns[_proxyColumns.Length - 1].MinTotal + _proxyColumns[_proxyColumns.Length - 1].MinSize; double height = _proxyRows[_proxyRows.Length - 1].MinTotal + _proxyRows[_proxyRows.Length - 1].MinSize; // If any of the columns is marked as Star if (HasColumnStars) { // If we have infinite space then return the measured width needed for all columns, otherwise we just take all the space if (availableSize.Width == double.PositiveInfinity) { width = _proxyColumns[_proxyColumns.Length - 1].DesiredTotal + _proxyColumns[_proxyColumns.Length - 1].DesiredSize; } else { width = availableSize.Width; } } // If any of the rows is marked as Star if (HasRowStars) { // If we have infinite space then return the measured height needed for all rows, otherwise we just take all the space if (availableSize.Height == double.PositiveInfinity) { height = _proxyRows[_proxyRows.Length - 1].DesiredTotal + _proxyRows[_proxyRows.Length - 1].DesiredSize; } else { height = availableSize.Height; } } return(new Size(width, height)); }
private Size MeasureTreeNodes(ITreeNode node, MetaElementStateDict stateDict, bool downwards) { Size totalSize = Utility.SizeZero; UIElement element = node as UIElement; if ((element != null) && (stateDict.ContainsKey(element))) { // Use element size as the starting total size element.Measure(Utility.SizeInfinity); // Do not measure children of items being removed if (stateDict[element].Status != MetaElementStatus.Removing) { // Starting size covers only the node totalSize = element.DesiredSize; // Process each sub tree Size childTotalSize = Utility.SizeZero; foreach (ITreeNode child in node.ChildNodes()) { Size childSize = MeasureTreeNodes(child, stateDict, downwards); // Stack all the sub trees vertically in sizing childTotalSize.Width = Math.Max(childTotalSize.Width, childSize.Width); childTotalSize.Height += childSize.Height; } // Place the set of sub trees to the right of this node totalSize.Width += childTotalSize.Width; totalSize.Height = Math.Max(totalSize.Height, childTotalSize.Height); } } return totalSize; }
private Size CalculateDesiredSize(MetaElementStateDict stateDict, ICollection elements, Size availableSize) { double width = _proxyColumns[_proxyColumns.Length - 1].MinTotal + _proxyColumns[_proxyColumns.Length - 1].MinSize; double height = _proxyRows[_proxyRows.Length - 1].MinTotal + _proxyRows[_proxyRows.Length - 1].MinSize; // If any of the columns is marked as Star if (HasColumnStars) { // If we have infinite space then return the measured width needed for all columns, otherwise we just take all the space if (availableSize.Width == double.PositiveInfinity) width = _proxyColumns[_proxyColumns.Length - 1].DesiredTotal + _proxyColumns[_proxyColumns.Length - 1].DesiredSize; else width = availableSize.Width; } // If any of the rows is marked as Star if (HasRowStars) { // If we have infinite space then return the measured height needed for all rows, otherwise we just take all the space if (availableSize.Height == double.PositiveInfinity) height = _proxyRows[_proxyRows.Length - 1].DesiredTotal + _proxyRows[_proxyRows.Length - 1].DesiredSize; else height = availableSize.Height; } return new Size(width, height); }
/// <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)) { // 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)) { // 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; } } } } }
private void TargetLine(MetaElementStateDict stateDict, List<UIElement> targets, double positionOffset, double lineMax, bool itemWidthDefined, bool itemHeightDefined, double itemWidth, double itemHeight, Size finalSize, Orientation orientation) { double lineOffset = 0; foreach (UIElement element in targets) { // 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); Rect newTargetRect; // Updating the elements target rectangle depends on orientation if (orientation == Orientation.Horizontal) { newTargetRect = new Rect(lineOffset, positionOffset, childSize.Width, lineMax); lineOffset += childSize.Width; } else { newTargetRect = new Rect(positionOffset, lineOffset, lineMax, childSize.Height); lineOffset += childSize.Height; } // Store the new target rectangle if (!stateDict[element].TargetRect.Equals(newTargetRect)) { stateDict[element].TargetChanged = true; stateDict[element].TargetRect = newTargetRect; } } }
private Size TargetTreeNodes(Point location, ITreeNode node, MetaElementStateDict stateDict, bool downwards) { Size totalSize = Utility.SizeZero; UIElement element = node as UIElement; if ((element != null) && (stateDict.ContainsKey(element))) { // Do not measure children of items being removed if (stateDict[element].Status != MetaElementStatus.Removing) { // Starting size covers only the node totalSize = element.DesiredSize; // Process each sub tree Point childLocation = new Point(location.X + element.DesiredSize.Width, location.Y); Size childTotalSize = Utility.SizeZero; foreach (ITreeNode child in node.ChildNodes()) { Size childSize = TargetTreeNodes(childLocation, child, stateDict, downwards); // Stack all the sub trees vertically in sizing childTotalSize.Width = Math.Max(childTotalSize.Width, childSize.Width); childTotalSize.Height += childSize.Height; childLocation.Y += childSize.Height; } // Place the set of sub trees to the right of this node totalSize.Width += childTotalSize.Width; totalSize.Height = Math.Max(totalSize.Height, childTotalSize.Height); } // Position the node vertically centered Rect newTargetRect = new Rect(location.X, location.Y + (totalSize.Height - element.DesiredSize.Height) / 2, element.DesiredSize.Width, element.DesiredSize.Height); // Store the new target rectangle if (!stateDict[element].TargetRect.Equals(newTargetRect)) { stateDict[element].TargetChanged = true; stateDict[element].TargetRect = newTargetRect; } } return totalSize; }
/// <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> /// 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);
/// <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);
private void ResyncStateDictionary() { MetaElementStateDict newStateDict = new MetaElementStateDict(); // Transfer existing state to new dictionary and create new state for new elements foreach (UIElement element in Children) { if (_stateDict.ContainsKey(element)) newStateDict.Add(element, _stateDict[element]); else newStateDict.Add(element, new MetaElementState(element)); } // Any removed elements will not have state in new dictionary _stateDict = newStateDict; }
private void UpdateDefinitions(MetaElementStateDict stateDict, ICollection elements, bool spanning) { foreach (UIElement element in elements) { // Find the row/col definitions for this element int col = Math.Max(0, Math.Min(_columns.Count - 1, GetColumn(element))); int row = Math.Max(0, Math.Min(_rows.Count - 1, GetRow(element))); // Find the row/col spanning definitions for this element (convert to zero based spanning) int colSpan = Math.Max(Math.Min(_columns.Count - col, GetColumnSpan(element)), 1) - 1; int rowSpan = Math.Max(Math.Min(_rows.Count - row, GetRowSpan(element)), 1) - 1; // If cell covers a single column then update the minimum size of column with desired width if (!spanning && (colSpan == 0)) _proxyColumns[col].UpdateFromMeasure(stateDict[element].Element.DesiredSize.Width); if (spanning && (colSpan > 0)) { // Quantity of desired width to be allocated double desiredWidth = stateDict[element].Element.DesiredSize.Width; // Scan all spanning columns and remove the already allocated space from desired width int index = col; int autoColumns = 0; do { desiredWidth -= _proxyColumns[index].MinSize; if (_proxyColumns[index].UserGridUnitType == GridUnitType.Auto) autoColumns++; } while (index++ < (col + colSpan)); // If there is still some width left to allocate and something to allocate into if ((desiredWidth > 0) && (autoColumns > 0)) { index = col; do { if (_proxyColumns[index].UserGridUnitType == GridUnitType.Auto) { double extraSpace = desiredWidth / autoColumns--; desiredWidth -= extraSpace; _proxyColumns[index].UpdateFromMeasure(_proxyColumns[index].MinSize + extraSpace); } } while (index++ < (col + colSpan)); } } // If cell covers a single row then update the minimum size of column with desired height if (!spanning & (rowSpan == 0)) _proxyRows[row].UpdateFromMeasure(stateDict[element].Element.DesiredSize.Height); if (spanning && (rowSpan > 0)) { // Quantity of desired height to be allocated double desiredHeight = stateDict[element].Element.DesiredSize.Height; // Scan all spanning columns and remove the already allocated space from desired height int index = col; int autoRows = 0; do { desiredHeight -= _proxyColumns[index].MinSize; if (_proxyColumns[index].UserGridUnitType == GridUnitType.Auto) autoRows++; } while (index++ < (col + colSpan)); // If there is still some height left to allocate and something to allocate into if ((desiredHeight > 0) && (autoRows > 0)) { index = col; do { if (_proxyColumns[index].UserGridUnitType == GridUnitType.Auto) { double extraSpace = desiredHeight / autoRows--; desiredHeight -= extraSpace; _proxyColumns[index].UpdateFromMeasure(_proxyColumns[index].MinSize + extraSpace); } } while (index++ < (col + colSpan)); } } } }
/// <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> /// 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++; } } } }
private bool AnimateChildren(MetaElementStateDict stateDict, ICollection elements) { // Find number of milliseconds elapsed since last animation calculation, note that if not // currently animating then the elapsed time since the last animation cycle must be 0. double elapsedMilliseconds = (!IsAnimating || (_lastTicks == -1) ? 0 : (_nextTicks - _lastTicks) / (double)(10000)); if (IsAnimationAllowed) { // Ask the chain of animation classes to apply any required movement changes Animates.ApplyAnimation(AnimateId, this, stateDict, elements, elapsedMilliseconds); } // Post-process on animation state bool moreAnimation = false; foreach (MetaElementState elementState in stateDict.Values) { // At end of animation cycle the new/remove values must have been calculated elementState.NewCalculating = false; if (elementState.Status == MetaElementStatus.Removing) elementState.RemoveCalculated = true; // Has the element finished being removed? if (elementState.AnimateComplete && (elementState.Status == MetaElementStatus.Removing)) _removeList.Add(elementState); else { // If the element has not finished being animated if (!elementState.AnimateComplete) { // Always reset to being completed, ready for next cycle elementState.AnimateComplete = true; moreAnimation = true; } else { // Has element finished being 'new'? if (elementState.Status == MetaElementStatus.New) elementState.Status = MetaElementStatus.Existing; // Animation is completed so ensure the current rect is same as the target // rect. This ensures that if there are no animation classes actually doing // size/position animation then the child elements actually do get positioned. elementState.CurrentRect = elementState.TargetRect; } } // Reset the target changed flag elementState.TargetChanged = false; // Never allow the current rect to be empty if (elementState.CurrentRect.IsEmpty) elementState.CurrentRect = elementState.TargetRect; } // Process the removal list foreach (MetaElementState elementState in _removeList) { RemoveChildElement(elementState.Element); stateDict.Remove(elementState.Element); moreAnimation = true; } // Must clear list to prevent dangling references to the removed UIElement instances _removeList.Clear(); return moreAnimation; }
/// <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; }