372 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			372 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
using System;
 | 
						|
using System.Collections.Generic;
 | 
						|
using System.Text;
 | 
						|
using System.Drawing;
 | 
						|
 | 
						|
namespace DevComponents.DotNetBar.Layout
 | 
						|
{
 | 
						|
    internal class ItemLayout
 | 
						|
    {
 | 
						|
        public void Layout(LayoutGroup group, Rectangle clientBounds, LayoutContext context)
 | 
						|
        {
 | 
						|
            if (clientBounds.IsEmpty) return;
 | 
						|
            List<LayoutItemStrip> strips = SliceItemsInStrips(group, clientBounds, context);
 | 
						|
            ResizeStrips(strips, clientBounds);
 | 
						|
            Rectangle totalBounds = FinalizeItemSize(strips, clientBounds, context);
 | 
						|
            group.ActualBounds = totalBounds;
 | 
						|
            //Console.WriteLine("{0} -> {1}", clientBounds, totalBounds);
 | 
						|
        }
 | 
						|
 | 
						|
        private LayoutContext CreateLayoutContext(LayoutContext context, int startAbsoluteIndex)
 | 
						|
        {
 | 
						|
            LayoutContext newContext = new LayoutContext(context.LayoutControl, context.Graphics, context.Font);
 | 
						|
            newContext.RightToLeft = context.RightToLeft;
 | 
						|
            newContext.AbsoluteIndex = startAbsoluteIndex;
 | 
						|
            return newContext;
 | 
						|
        }
 | 
						|
        private Rectangle FinalizeItemSize(List<LayoutItemStrip> strips, Rectangle clientBounds, LayoutContext context)
 | 
						|
        {
 | 
						|
            int sizeLimit = GetSizeLimit(clientBounds);
 | 
						|
            int stripSizeLimit = GetStripsSizeLimit(clientBounds);
 | 
						|
            Rectangle totalBounds = new Rectangle(clientBounds.Location, Size.Empty);
 | 
						|
            Point loc = clientBounds.Location;
 | 
						|
            Rectangle fullClientBounds = clientBounds;
 | 
						|
 | 
						|
            int absIndex = context.AbsoluteIndex;
 | 
						|
 | 
						|
            foreach (LayoutItemStrip strip in strips)
 | 
						|
            {
 | 
						|
                LayoutItemBase layoutItem = strip.Items[0];
 | 
						|
                int clientBoundsWidth = clientBounds.Width;
 | 
						|
                if (strip.IsLeftSpanStrip)
 | 
						|
                {
 | 
						|
                    Size size = new Size(Math.Max(MinItemSize(layoutItem), ActualItemSize(layoutItem, fullClientBounds.Width)),
 | 
						|
                        Math.Max(strip.MinStripHeightAbsolute, clientBounds.Height));
 | 
						|
                    Rectangle r = new Rectangle(loc, size);
 | 
						|
                    clientBounds.X += size.Width;
 | 
						|
                    clientBounds.Width -= size.Width;
 | 
						|
                    loc = clientBounds.Location;
 | 
						|
                    layoutItem.SetBounds(r, (layoutItem.SharedTextSizeEnabled ? context.LargestTextSize : layoutItem.RealTextSize));
 | 
						|
                    if (layoutItem is LayoutGroup)
 | 
						|
                    {
 | 
						|
                        LayoutContext groupContext = CreateLayoutContext(context, absIndex++);
 | 
						|
                        ((LayoutGroup)layoutItem).Layout(groupContext);
 | 
						|
                        absIndex = groupContext.AbsoluteIndex;
 | 
						|
                    }
 | 
						|
                    totalBounds = Rectangle.Union(r, totalBounds);
 | 
						|
                    layoutItem.AbsoluteIndex = absIndex++;
 | 
						|
                    continue;
 | 
						|
                }
 | 
						|
                else if (strip.IsRightSpanStrip)
 | 
						|
                {
 | 
						|
                    Size size = new Size(Math.Max(MinItemSize(layoutItem), ActualItemSize(layoutItem, fullClientBounds.Width)),
 | 
						|
                        Math.Max(strip.MinStripHeightAbsolute, clientBounds.Height));
 | 
						|
                    Rectangle r = new Rectangle(clientBounds.Right - size.Width, clientBounds.Y, size.Width, size.Height);
 | 
						|
                    clientBounds.Width -= size.Width;
 | 
						|
                    layoutItem.SetBounds(r, layoutItem.SharedTextSizeEnabled ? context.LargestTextSize : layoutItem.RealTextSize);
 | 
						|
                    if (layoutItem is LayoutGroup)
 | 
						|
                    {
 | 
						|
                        LayoutContext groupContext = CreateLayoutContext(context, 10000/*absIndex++*/); // Pushing the tab index for right span strip to the right most
 | 
						|
                        ((LayoutGroup)layoutItem).Layout(groupContext);
 | 
						|
                        //absIndex = groupContext.AbsoluteIndex;
 | 
						|
                    }
 | 
						|
                    totalBounds = Rectangle.Union(r, totalBounds);
 | 
						|
                    layoutItem.AbsoluteIndex = absIndex++;
 | 
						|
                    //layoutItem.ActualTextSize = layoutItem.SharedTextSizeEnabled ? context.LargestTextSize : layoutItem.RealTextSize;
 | 
						|
                    continue;
 | 
						|
                }
 | 
						|
 | 
						|
                bool repeat = false;
 | 
						|
                int repeatCount = 0;
 | 
						|
                int startAbsIndex = absIndex;
 | 
						|
                do
 | 
						|
                {
 | 
						|
                    loc.X = clientBounds.X;
 | 
						|
                    repeatCount++;
 | 
						|
                    absIndex = startAbsIndex;
 | 
						|
                    int totalPercentWidth = 0;
 | 
						|
                    foreach (LayoutItemBase item in strip.Items)
 | 
						|
                    {
 | 
						|
                        Size size = new Size(Math.Max(MinItemSize(item), ActualItemSize(item, clientBoundsWidth - strip.TotalFixedWidth)),
 | 
						|
                            strip.StripHeightAbsolute);
 | 
						|
                        if (item.WidthType == eLayoutSizeType.Percent)
 | 
						|
                            totalPercentWidth += item.Width;
 | 
						|
                        if (strip.Items.Count == 1 && size.Width < clientBoundsWidth && clientBoundsWidth >= MinItemSize(item) && item.WidthType == eLayoutSizeType.Percent)
 | 
						|
                            size.Width = clientBoundsWidth;
 | 
						|
                        else if (loc.X + size.Width > clientBounds.X + clientBoundsWidth && strip.Items.Count > 1)
 | 
						|
                        {
 | 
						|
                            if (strip.Items[strip.Items.Count - 1] == item && MinItemSize(item) <= clientBoundsWidth - (loc.X - clientBounds.X))
 | 
						|
                                size.Width = clientBoundsWidth - (loc.X - clientBounds.X);
 | 
						|
                            else
 | 
						|
                            {
 | 
						|
                                clientBoundsWidth -= (loc.X + size.Width) - (clientBounds.X + clientBoundsWidth);
 | 
						|
                                repeat = true;
 | 
						|
                                break;
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
                        else if(strip.TotalFixedWidth == 0 && totalPercentWidth == 100 && strip.Items[strip.Items.Count - 1] == item && size.Width<clientBoundsWidth - (loc.X - clientBounds.X))
 | 
						|
                            size.Width = clientBoundsWidth - (loc.X - clientBounds.X);
 | 
						|
                        Rectangle r = new Rectangle(loc, size);
 | 
						|
                        item.StripTextBaseline = strip.LargestTextBaseline;
 | 
						|
                        item.SetBounds(r, item.SharedTextSizeEnabled ? context.LargestTextSize : item.RealTextSize);
 | 
						|
                        if (item is LayoutGroup)
 | 
						|
                        {
 | 
						|
                            LayoutContext groupContext = CreateLayoutContext(context, absIndex++);
 | 
						|
                            ((LayoutGroup)item).Layout(groupContext);
 | 
						|
                            absIndex = groupContext.AbsoluteIndex;
 | 
						|
                        }
 | 
						|
                        item.AbsoluteIndex = absIndex++;
 | 
						|
                        totalBounds = Rectangle.Union(r, totalBounds);
 | 
						|
                        loc.X += size.Width;
 | 
						|
                    }
 | 
						|
                } while (repeat && repeatCount < 2);
 | 
						|
 | 
						|
                loc.Y += strip.StripHeightAbsolute;
 | 
						|
                loc.X = clientBounds.X;
 | 
						|
            }
 | 
						|
            context.AbsoluteIndex = absIndex;
 | 
						|
            return totalBounds;
 | 
						|
        }
 | 
						|
        private void ResizeStrips(List<LayoutItemStrip> strips, Rectangle clientBounds)
 | 
						|
        {
 | 
						|
            int totalFixedSize = 0;
 | 
						|
            List<LayoutItemStrip> percenageStrips = new List<LayoutItemStrip>();
 | 
						|
            foreach (LayoutItemStrip strip in strips)
 | 
						|
            {
 | 
						|
                if (strip.StripHeightPercentage == 0)
 | 
						|
                    totalFixedSize += strip.StripHeightAbsolute;
 | 
						|
                else
 | 
						|
                    percenageStrips.Add(strip);
 | 
						|
            }
 | 
						|
            if (percenageStrips.Count > 0)
 | 
						|
            {
 | 
						|
                int sizeLimit = GetStripsSizeLimit(clientBounds);
 | 
						|
                if (totalFixedSize < sizeLimit)
 | 
						|
                    sizeLimit -= totalFixedSize;
 | 
						|
                foreach (LayoutItemStrip strip in percenageStrips)
 | 
						|
                {
 | 
						|
                    strip.StripHeightAbsolute = Math.Max(strip.StripHeightAbsolute, (sizeLimit * strip.StripHeightPercentage) / 100);
 | 
						|
                    strip.StripHeightAbsolute = Math.Max(strip.StripHeightAbsolute, strip.MinStripHeightAbsolute);
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        private List<LayoutItemStrip> SliceItemsInStrips(LayoutGroup group, Rectangle clientBounds, LayoutContext context)
 | 
						|
        {
 | 
						|
            LayoutItemCollection items = group.Items;
 | 
						|
            List<LayoutItemStrip> strips = new List<LayoutItemStrip>();
 | 
						|
 | 
						|
            if (items.Count == 0) return strips;
 | 
						|
 | 
						|
            int sizeLimit = GetSizeLimit(clientBounds);
 | 
						|
            int currentPercent = 0;
 | 
						|
            int currentFixedSize = 0;
 | 
						|
            int currentTotalMinSize = 0;
 | 
						|
            int start = 0, end = items.Count;
 | 
						|
            Size largestTextSize = Size.Empty;
 | 
						|
 | 
						|
            LayoutItemStrip currentStrip = new LayoutItemStrip();
 | 
						|
 | 
						|
            // Check whether first or last item spans total client bounds effectively reducing the client width
 | 
						|
            int firstVisibleIndex = GetFirstVisibleItemIndex(items);
 | 
						|
            int lastVisibleIndex = GetLastVisibleItemIndex(items);
 | 
						|
            LayoutItemBase spanItem = null;
 | 
						|
            if (firstVisibleIndex >= 0) spanItem = items[firstVisibleIndex];
 | 
						|
            if (spanItem != null && !IsFixedStripSize(spanItem) && ItemStripSize(spanItem) >= 100)
 | 
						|
            {
 | 
						|
                if (spanItem.IsTextSizeShared)
 | 
						|
                    largestTextSize = Helpers.Max(largestTextSize, spanItem.MeasureText(context));
 | 
						|
                else
 | 
						|
                    spanItem.MeasureText(context);
 | 
						|
                sizeLimit -= ActualItemSize(spanItem, clientBounds.Width);
 | 
						|
                currentStrip.IsLeftSpanStrip = true;
 | 
						|
                currentStrip.StripHeightPercentage = 100;
 | 
						|
                currentStrip.MinStripHeightAbsolute = spanItem.MinSize.Height;
 | 
						|
                currentStrip.Items.Add(spanItem);
 | 
						|
                strips.Add(currentStrip);
 | 
						|
                currentStrip = new LayoutItemStrip();
 | 
						|
                start = firstVisibleIndex + 1;
 | 
						|
            }
 | 
						|
            if (firstVisibleIndex != lastVisibleIndex && lastVisibleIndex > 0)
 | 
						|
            {
 | 
						|
                spanItem = items[lastVisibleIndex];
 | 
						|
                if (!IsFixedStripSize(spanItem) && ItemStripSize(spanItem) >= 100 && !(!IsFixedSize(spanItem) && ItemSize(spanItem) >= 100))
 | 
						|
                {
 | 
						|
                    if (spanItem.IsTextSizeShared)
 | 
						|
                        largestTextSize = Helpers.Max(largestTextSize, spanItem.MeasureText(context));
 | 
						|
                    else
 | 
						|
                        spanItem.MeasureText(context);
 | 
						|
                    sizeLimit -= ActualItemSize(spanItem, clientBounds.Width);
 | 
						|
                    currentStrip.IsRightSpanStrip = true;
 | 
						|
                    currentStrip.StripHeightPercentage = 100;
 | 
						|
                    currentStrip.MinStripHeightAbsolute = spanItem.MinSize.Height;
 | 
						|
                    currentStrip.Items.Add(spanItem);
 | 
						|
                    strips.Add(currentStrip);
 | 
						|
                    currentStrip = new LayoutItemStrip();
 | 
						|
                    end = lastVisibleIndex;
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            for (int i = start; i < end; i++)
 | 
						|
            {
 | 
						|
                LayoutItemBase item = items[i];
 | 
						|
                if (!item.Visible) continue;
 | 
						|
                int itemSize = ItemSize(item);
 | 
						|
 | 
						|
                if (item.IsTextSizeShared)
 | 
						|
                    largestTextSize = Helpers.Max(largestTextSize, item.MeasureText(context));
 | 
						|
                else
 | 
						|
                    item.MeasureText(context);
 | 
						|
 | 
						|
                if (IsFixedSize(item))
 | 
						|
                {
 | 
						|
                    if (currentTotalMinSize + itemSize > sizeLimit && currentTotalMinSize > 0 || currentPercent >= 100)
 | 
						|
                    {
 | 
						|
                        // Trigger new line
 | 
						|
                        currentStrip.TotalFixedWidth = currentFixedSize;
 | 
						|
                        strips.Add(currentStrip);
 | 
						|
                        currentStrip = new LayoutItemStrip();
 | 
						|
                        currentFixedSize = Math.Max(itemSize, MinItemSize(item)); ;
 | 
						|
                        currentTotalMinSize = itemSize;
 | 
						|
                        currentPercent = 0;
 | 
						|
                    }
 | 
						|
                    else
 | 
						|
                    {
 | 
						|
                        currentFixedSize += Math.Max(itemSize, MinItemSize(item));
 | 
						|
                        currentTotalMinSize += itemSize;
 | 
						|
                    }
 | 
						|
                    if (item.HeightType == eLayoutSizeType.Percent)
 | 
						|
                        currentStrip.StripHeightPercentage = Math.Max(currentStrip.StripHeightPercentage, item.Height);
 | 
						|
                    else
 | 
						|
                        currentStrip.StripHeightAbsolute = Math.Max(currentStrip.StripHeightAbsolute, item.Height);
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    int minItemSize = MinItemSize(item);
 | 
						|
                    if (currentPercent + itemSize > 100 && currentPercent > 0 || (currentTotalMinSize + minItemSize > sizeLimit || itemSize > 100) && currentTotalMinSize > 0)
 | 
						|
                    {
 | 
						|
                        // Trigger new line
 | 
						|
                        currentStrip.TotalFixedWidth = currentFixedSize;
 | 
						|
                        strips.Add(currentStrip);
 | 
						|
                        currentStrip = new LayoutItemStrip();
 | 
						|
                        currentFixedSize = 0;
 | 
						|
                        currentTotalMinSize = 0;
 | 
						|
                        currentPercent = 0;
 | 
						|
                    }
 | 
						|
                    //else
 | 
						|
                    //{
 | 
						|
                    currentPercent += itemSize;
 | 
						|
                    currentTotalMinSize += minItemSize;
 | 
						|
                    //}
 | 
						|
                    if (item.HeightType == eLayoutSizeType.Percent)
 | 
						|
                        currentStrip.StripHeightPercentage = Math.Max(currentStrip.StripHeightPercentage, item.Height);
 | 
						|
                    else
 | 
						|
                        currentStrip.StripHeightAbsolute = Math.Max(currentStrip.StripHeightAbsolute, item.Height);
 | 
						|
                }
 | 
						|
                currentStrip.MinStripHeightAbsolute = Math.Max(currentStrip.MinStripHeightAbsolute, item.MinSize.Height);
 | 
						|
                if (item.IsTextBaselineShared)
 | 
						|
                    currentStrip.LargestTextBaseline = Math.Max(currentStrip.LargestTextBaseline, item.TextBaseline);
 | 
						|
                currentStrip.Items.Add(item);
 | 
						|
            }
 | 
						|
            if (currentStrip.Items.Count > 0)
 | 
						|
            {
 | 
						|
                strips.Add(currentStrip);
 | 
						|
                currentStrip.TotalFixedWidth = currentFixedSize;
 | 
						|
            }
 | 
						|
 | 
						|
            context.LargestTextSize = largestTextSize;
 | 
						|
 | 
						|
            return strips;
 | 
						|
        }
 | 
						|
 | 
						|
        private int GetFirstVisibleItemIndex(LayoutItemCollection items)
 | 
						|
        {
 | 
						|
            for (int i = 0; i < items.Count; i++)
 | 
						|
            {
 | 
						|
                if (items[i].Visible) return i;
 | 
						|
            }
 | 
						|
            return -1;
 | 
						|
        }
 | 
						|
        private int GetLastVisibleItemIndex(LayoutItemCollection items)
 | 
						|
        {
 | 
						|
            for (int i = items.Count - 1; i >= 0; i--)
 | 
						|
            {
 | 
						|
                if (items[i].Visible) return i;
 | 
						|
            }
 | 
						|
            return -1;
 | 
						|
        }
 | 
						|
 | 
						|
        private int ActualItemSize(LayoutItemBase item, int clientSize)
 | 
						|
        {
 | 
						|
            if (item.WidthType == eLayoutSizeType.Absolute)
 | 
						|
                return item.Width;
 | 
						|
            else
 | 
						|
                return (Math.Min(100, (item.Width == 99 ? 100 : item.Width)) * clientSize) / 100; // 99% is special case where we have relative size item inside of fixed size items on single line
 | 
						|
        }
 | 
						|
        private int MinItemSize(LayoutItemBase item)
 | 
						|
        {
 | 
						|
            return item.MinSize.Width;
 | 
						|
        }
 | 
						|
        private int GetSizeLimit(Rectangle clientBounds)
 | 
						|
        {
 | 
						|
            return clientBounds.Width;
 | 
						|
        }
 | 
						|
        private int GetStripsSizeLimit(Rectangle clientBounds)
 | 
						|
        {
 | 
						|
            return clientBounds.Height;
 | 
						|
        }
 | 
						|
        private int ItemSize(LayoutItemBase item)
 | 
						|
        {
 | 
						|
            return item.Width;
 | 
						|
        }
 | 
						|
        private int ItemStripSize(LayoutItemBase item)
 | 
						|
        {
 | 
						|
            return item.Height;
 | 
						|
        }
 | 
						|
        private bool IsFixedSize(LayoutItemBase item)
 | 
						|
        {
 | 
						|
            return item.IsWidthFixed;
 | 
						|
        }
 | 
						|
        private bool IsFixedStripSize(LayoutItemBase item)
 | 
						|
        {
 | 
						|
            return item.IsHeightFixed;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    internal class LayoutItemStrip
 | 
						|
    {
 | 
						|
        /// <summary>
 | 
						|
        /// Collection of items inside of strip.
 | 
						|
        /// </summary>
 | 
						|
        public List<LayoutItemBase> Items = new List<LayoutItemBase>();
 | 
						|
        /// <summary>
 | 
						|
        /// Total width of all items with fixed width in the strip.
 | 
						|
        /// </summary>
 | 
						|
        public int TotalFixedWidth = 0;
 | 
						|
        /// <summary>
 | 
						|
        /// True if this is left most strip in group which spans whole group height.
 | 
						|
        /// </summary>
 | 
						|
        public bool IsLeftSpanStrip = false;
 | 
						|
        /// <summary>
 | 
						|
        /// True if this is right most strip in group which spans whole group height.
 | 
						|
        /// </summary>
 | 
						|
        public bool IsRightSpanStrip = false;
 | 
						|
        /// <summary>
 | 
						|
        /// Strip height determined as maximum height of items in it.
 | 
						|
        /// </summary>
 | 
						|
        public int StripHeightAbsolute = 0;
 | 
						|
        /// <summary>
 | 
						|
        /// Minimum strip height according to the larges MinSize of contained items.
 | 
						|
        /// </summary>
 | 
						|
        public int MinStripHeightAbsolute = 0;
 | 
						|
        /// <summary>
 | 
						|
        /// Maximum percentage strip height for all items in it.
 | 
						|
        /// </summary>
 | 
						|
        public int StripHeightPercentage = 0;
 | 
						|
        /// <summary>
 | 
						|
        /// The largest text-baseline for all items in strip.
 | 
						|
        /// </summary>
 | 
						|
        public int LargestTextBaseline = 0;
 | 
						|
    }
 | 
						|
}
 |