File: Microsoft\Windows\Controls\Ribbon\Primitives\RibbonTitlePanel.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\src\System.Windows.Controls.Ribbon\System.Windows.Controls.Ribbon_smvy2x3f_wpftmp.csproj (System.Windows.Controls.Ribbon)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
        
 
#if RIBBON_IN_FRAMEWORK
namespace System.Windows.Controls.Ribbon.Primitives
#else
namespace Microsoft.Windows.Controls.Ribbon.Primitives
#endif
{
    #region Using directives
 
    using MS.Internal;
    using System;
    using System.Diagnostics;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Controls.Primitives;
    using System.Windows.Media;
    
    #endregion
 
    /// <summary>
    ///   Implements the title panel of the Ribbon, this layouts out the Ribbon title
    ///   and RibbonContextualTabGroup separators around the QAT and system buttons.
    /// </summary>
    public class RibbonTitlePanel : Panel
    {
        #region Private Properties
 
        /// <summary>
        ///     DependencyProperty for Ribbon property.
        /// </summary>
        public static readonly DependencyProperty RibbonProperty =
            RibbonControlService.RibbonProperty.AddOwner(typeof(RibbonTitlePanel));
 
        /// <summary>
        ///     This property is used to access visual style brushes defined on the Ribbon class.
        /// </summary>
        public Ribbon Ribbon
        {
            get { return RibbonControlService.GetRibbon(this); }
        }
 
        #endregion
 
        #region Protected Methods
 
        /// <summary>
        ///   Measures the children of the RibbonTitlePanel.
        /// </summary>
        /// <param name="constraint">The space available for layout.</param>
        /// <returns>Returns the RibbonTitlePanel's desired size as determined by measurement of its children.</returns>
        protected override Size MeasureOverride(Size availableSize)
        {
            Size desiredSize = new Size();
            int count = Children.Count;
            if (count == 0)
            {
                return desiredSize;
            }
 
            double startContextualTabX = availableSize.Width;
            double endContextualTabX = 0.0;
 
            RibbonContextualTabGroupItemsControl groupHeaderItemsControl = Ribbon.ContextualTabGroupItemsControl;
            if (groupHeaderItemsControl != null && groupHeaderItemsControl.Visibility == Visibility.Visible)
            {
                // A dummy measure to ensure containers are generated.
                // We need to know FirstContextualTabHeader and LastContextualTabHeader
                if (groupHeaderItemsControl.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
                {
                    groupHeaderItemsControl.Measure(availableSize);
                }
 
                // Calculate start and end positions
                RibbonContextualTabGroup firstContextualTab = groupHeaderItemsControl.FirstContextualTabHeader;
                if (firstContextualTab != null)
                {
                    startContextualTabX = Math.Min(CalculateContextualTabGroupStartX(firstContextualTab), availableSize.Width); 
                }
 
                RibbonContextualTabGroup lastContextualTab = groupHeaderItemsControl.LastContextualTabHeader;
                if (lastContextualTab != null)
                {
                    endContextualTabX = Math.Min(CalculateContextualTabGroupEndX(lastContextualTab), availableSize.Width);
                }
 
                groupHeaderItemsControl.Measure(new Size(Math.Max(endContextualTabX - startContextualTabX, 0), availableSize.Height));
                desiredSize.Width += groupHeaderItemsControl.DesiredSize.Width;
                desiredSize.Height = Math.Max(desiredSize.Height, groupHeaderItemsControl.DesiredSize.Height);
                
            }
            
            FrameworkElement qat = Ribbon.QatTopHost as FrameworkElement;
            FrameworkElement titleHost = Ribbon.TitleHost as FrameworkElement;
 
            if (qat != null && titleHost != null)
            {
                double availableToQat = 0.0, availableToTitle;
                double leftSpace, rightSpace;
 
                // If ribbon is not collapsed and QAT is on top
                if (qat.Visibility == Visibility.Visible)
                {
                    rightSpace = availableSize.Width - endContextualTabX;
                    // 1) if there is no room on the right for Title
                    // 2) or when there are no contextual tabs (i.e. endContextualTabX==0), then rightSpace=entire width
                    //  want to ensure MinWidth for title if possible
                    if ((DoubleUtil.LessThan(rightSpace, titleHost.MinWidth) ||             
                        DoubleUtil.GreaterThan(startContextualTabX, endContextualTabX))         
                        && DoubleUtil.GreaterThan(startContextualTabX, titleHost.MinWidth))
                    {
                        // Leave titleHost.MinWidth at the left of ContextualTabGroupItemsControl for TitleHost
                        availableToQat = startContextualTabX - titleHost.MinWidth;
                    }
                    else
                    {
                        // Give QAT space from 0 to start of ContextualTabGroupItemsControl
                        availableToQat = startContextualTabX;
                    }
                    // The size computed could be negative because of invalid
                    // transforms due to invalid arrange. Assume that the space
                    // available is 0 in such cases and assume that validation of
                    // transforms at a later point of time will fix the situation.
                    availableToQat = Math.Max(availableToQat, 0);
 
                    // Measure QAT
                    qat.Measure(new Size(availableToQat, availableSize.Height));
                    availableToQat = qat.DesiredSize.Width;
 
                    desiredSize.Width += availableToQat;
                    desiredSize.Height = Math.Max(desiredSize.Height, qat.DesiredSize.Height);
                }
 
                // Measure TitleHost. 
                // We try to give it titleHost.MinWidth, but it may get less than that when QAT has already reached qat.MinWidth
                endContextualTabX = Math.Max(endContextualTabX, availableToQat);
                rightSpace = availableSize.Width - endContextualTabX;
                leftSpace = startContextualTabX - availableToQat;
 
                // first measure to title's full size and see if it can fit entirely to the left
                titleHost.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
                if (DoubleUtil.LessThanOrClose(titleHost.DesiredSize.Width, leftSpace))
                {
                    availableToTitle = leftSpace;
                }
                else
                {
                    // title takes best of right or left
                    availableToTitle = Math.Max(leftSpace, rightSpace);
                }
                // The size computed could be negative because of invalid
                // transforms due to invalid arrange. Assume that the space
                // available is 0 in such cases and assume that validation of
                // transforms at a later point of time will fix the situation.
                availableToTitle = Math.Max(availableToTitle, 0);
 
                titleHost.Measure(new Size(availableToTitle, availableSize.Height));
                desiredSize.Width += titleHost.DesiredSize.Width;
                desiredSize.Height = Math.Max(desiredSize.Height, titleHost.DesiredSize.Height);
            }
 
            desiredSize.Width = Math.Min(desiredSize.Width, availableSize.Width); // Prevent clipping
            return desiredSize;
        }
 
        /// <summary>
        ///   Arranges the children of the RibbonTitlePanel.
        /// </summary>
        /// <param name="arrangeSize">The arranged size of the TitlePanel.</param>
        /// <returns>The size at which the TitlePanel was arranged.</returns>
        protected override Size ArrangeOverride(Size finalSize)
        {
            double x = 0.0;
            double width;
            double height;
            
            double startContextualTabX = 0;
            double endContextualTabX = 0;
 
            // Arrange ContextualTabHeaders
            RibbonContextualTabGroupItemsControl tabGroups = Ribbon.ContextualTabGroupItemsControl;
            if (tabGroups != null && tabGroups.Visibility == Visibility.Visible)
            {
                RibbonContextualTabGroup firstContextualTab = tabGroups.FirstContextualTabHeader;
                if (firstContextualTab != null)
                {
                    startContextualTabX = Math.Min(CalculateContextualTabGroupStartX(firstContextualTab), finalSize.Width);
                }
 
                endContextualTabX = Math.Min(startContextualTabX + tabGroups.DesiredSize.Width, finalSize.Width);
                tabGroups.Arrange(new Rect(startContextualTabX, finalSize.Height - tabGroups.DesiredSize.Height, tabGroups.DesiredSize.Width, tabGroups.DesiredSize.Height));
            }
 
            // Arrange QuickAccessToolbar
            double qatDesiredWidth = 0.0;
            UIElement qat = Ribbon.QatTopHost;
            if (qat != null)
            {
                qatDesiredWidth = qat.DesiredSize.Width;
                qat.Arrange(new Rect(0, 0.0, qatDesiredWidth, qat.DesiredSize.Height));
            }
 
            endContextualTabX = Math.Max(endContextualTabX, qatDesiredWidth);
 
            // Arrange the title
            UIElement titleHost = Ribbon.TitleHost;
            if (titleHost != null)
            {
                x = 0.0;
                width = titleHost.DesiredSize.Width;
                height = titleHost.DesiredSize.Height;
                double leftSpace = startContextualTabX - qatDesiredWidth;
 
                // If title can fit entirely to the left, then arrange on the left
                // i.e. between QAT and CTGHeaders
                if ( DoubleUtil.LessThanOrClose(width, leftSpace) )
                { 
                    x = qatDesiredWidth;
                }
                else
                {
                    // Arrange to the right of CTGHeaders.
                    x = endContextualTabX; ;
                }
            
                titleHost.Arrange(new Rect(x, 0.0, width, height));
            }
            
            return finalSize;
        }
        
        #endregion
 
        #region Private Methods
 
        /// <summary>
        ///   Calculates the x-position at which the given ContextualTabGroup should be placed on the title panel.
        /// </summary>
        /// <param name="group">The RibbonContextualTabGroup to position.</param>
        /// <returns>The x-offset at which the ContextualTabGroup should be positioned.</returns>
        private double CalculateContextualTabGroupStartX(RibbonContextualTabGroup groupHeader)
        {
            double result = 0.0;
 
            // A visible CTG can have some of its tabs Collapsed. 
            // We should start from the first visible Tab in this CTG.
            RibbonTab firstTab = groupHeader.FirstVisibleTab;
            if (firstTab != null && firstTab.RibbonTabHeader != null && Ribbon != null)
            {
                RibbonTabHeader tabHeader = firstTab.RibbonTabHeader;
                GeneralTransform transformRibbonTabToRibbon = tabHeader.TransformToAncestor(Ribbon);
                GeneralTransform transformRibbonToRibbonTitlePanel = Ribbon.TransformToDescendant(this);
                Point point = new Point();
                point = transformRibbonTabToRibbon.Transform(point);
                point = transformRibbonToRibbonTitlePanel.Transform(point);
                result = point.X - tabHeader.Margin.Left; // Reduce RibbonTab Margin
            }
 
            return result;
        }
 
        private double CalculateContextualTabGroupEndX(RibbonContextualTabGroup groupHeader)
        {
            double endX = CalculateContextualTabGroupStartX(groupHeader);
 
            foreach (RibbonTab tab in groupHeader.Tabs)
            {
                if (tab.Visibility == Visibility.Visible && tab.RibbonTabHeader != null)
                {
                    endX += tab.RibbonTabHeader.DesiredSize.Width;
                }
            }
            return endX;
        }
        #endregion
    }
}