// COPYRIGHT (C) Tom. ALL RIGHTS RESERVED.
// THE AntdUI PROJECT IS AN WINFORM LIBRARY LICENSED UNDER THE Apache-2.0 License.
// LICENSED UNDER THE Apache License, VERSION 2.0 (THE "License")
// YOU MAY NOT USE THIS FILE EXCEPT IN COMPLIANCE WITH THE License.
// YOU MAY OBTAIN A COPY OF THE LICENSE AT
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING, SOFTWARE
// DISTRIBUTED UNDER THE LICENSE IS DISTRIBUTED ON AN "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
// SEE THE LICENSE FOR THE SPECIFIC LANGUAGE GOVERNING PERMISSIONS AND
// LIMITATIONS UNDER THE License.
// GITCODE: https://gitcode.com/AntdUI/AntdUI
// GITEE: https://gitee.com/AntdUI/AntdUI
// GITHUB: https://github.com/AntdUI/AntdUI
// CSDN: https://blog.csdn.net/v_132
// QQ: 17379620

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Design;
using System.Windows.Forms;

namespace AntdUI
{
    partial class Tabs
    {
        /// <summary>
        /// 线条样式
        /// </summary>
        public class StyleLine : IStyle
        {
            Tabs? owner;
            public StyleLine() { }
            public StyleLine(Tabs tabs) { owner = tabs; }

            #region 属性

            int size = 3;
            /// <summary>
            /// 条大小
            /// </summary>
            [Description("条大小"), Category("样式"), DefaultValue(3)]
            public int Size
            {
                get => size;
                set
                {
                    if (size == value) return;
                    size = value;
                    owner?.LoadLayout(true);
                }
            }

            int padding = 8;
            /// <summary>
            /// 条边距
            /// </summary>
            [Description("条边距"), Category("样式"), DefaultValue(8)]
            public int Padding
            {
                get => padding;
                set
                {
                    if (padding == value) return;
                    padding = value;
                    owner?.LoadLayout(true);
                }
            }

            int radius = 0;
            /// <summary>
            /// 条圆角
            /// </summary>
            [Description("条圆角"), Category("样式"), DefaultValue(0)]
            public int Radius
            {
                get => radius;
                set
                {
                    if (radius == value) return;
                    radius = value;
                    owner?.Invalidate(true);
                }
            }

            bool closable = false;
            /// <summary>
            /// 可关闭
            /// </summary>
            [Description("可关闭"), Category("卡片"), DefaultValue(false)]
            public bool Closable
            {
                get => closable;
                set
                {
                    if (closable == value) return;
                    closable = value;
                    owner?.LoadLayout(true);
                }
            }

            int backsize = 1;
            /// <summary>
            /// 条背景大小
            /// </summary>
            [Description("条背景大小"), Category("样式"), DefaultValue(1)]
            public int BackSize
            {
                get => backsize;
                set
                {
                    if (backsize == value) return;
                    backsize = value;
                    owner?.LoadLayout(true);
                }
            }

            /// <summary>
            /// 条背景
            /// </summary>
            [Description("条背景"), Category("样式"), DefaultValue(null)]
            [Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
            public Color? Back { get; set; }

            #endregion

            #region 布局

            Rectangle rect_ful, rect_line_top;
            TabPageRect[] rects = new TabPageRect[0];
            public void LoadLayout(Tabs tabs, Rectangle rect, TabCollection items)
            {
                rect_ful = rect;
                owner = tabs;
                rects = Helper.GDI(g =>
                {
                    int gap = (int)(tabs.Gap * tabs.Dpi), gap2 = gap * 2, xy = 0;
                    int barSize = (int)(Size * tabs.Dpi), barPadding = (int)(Padding * tabs.Dpi), barPadding2 = barPadding * 2;
                    var rect_list = new List<TabPageRect>(items.Count);
                    var rect_dir = GetDir(tabs, g, items, gap, gap2, out int ico_size, out int ico_gap, out int close_size, out int xy2, out int rw);
                    int? rw_tmp = tabs.TextCenter ? rw : null;
                    switch (tabs.Alignment)
                    {
                        case TabAlignment.Bottom:
                            int y = rect.Bottom - xy2;
                            foreach (var it in rect_dir)
                            {
                                if (it.Key.Visible)
                                {
                                    bool close = closable && !it.Key.ReadOnly;
                                    Rectangle rect_it;
                                    if (close)
                                    {
                                        if (it.Key.HasIcon)
                                        {
                                            rect_it = new Rectangle(rect.X + xy, y, it.Value.Width + gap2 + ico_size + close_size + ico_gap * 2, xy2);
                                            rect_list.Add(new TabPageRect(rect_it, it.Value, gap, ico_size, close_size, ico_gap, true).SetLine(barPadding, barPadding2, barSize));
                                        }
                                        else
                                        {
                                            rect_it = new Rectangle(rect.X + xy, y, it.Value.Width + gap2 + close_size + ico_gap, xy2);
                                            rect_list.Add(new TabPageRect(rect_it, false, it.Value, gap, close_size, ico_gap, true).SetLine(barPadding, barPadding2, barSize));
                                        }
                                    }
                                    else
                                    {
                                        if (it.Key.HasIcon)
                                        {
                                            rect_it = new Rectangle(rect.X + xy, y, it.Value.Width + gap2 + ico_size + ico_gap, xy2);
                                            rect_list.Add(new TabPageRect(rect_it, it.Value, gap, ico_size, ico_gap, true).SetLine(barPadding, barPadding2, barSize));
                                        }
                                        else
                                        {
                                            rect_it = new Rectangle(rect.X + xy, y, it.Value.Width + gap2, xy2);
                                            rect_list.Add(new TabPageRect(rect_it, it.Value, gap, true).SetLine(barPadding, barPadding2, barSize));
                                        }
                                    }
                                    it.Key.SetRect(rect_it);
                                    xy += rect_it.Width;
                                }
                                else rect_list.Add(new TabPageRect());
                            }
                            tabs.SetPadding(0, 0, 0, xy2 + tabs.Margin.Horizontal);
                            if (BackSize > 0)
                            {
                                int barBackSize = (int)(BackSize * tabs.Dpi);
                                rect_line_top = new Rectangle(0, rect.Bottom - xy2, rect.Width + tabs.Margin.Horizontal, barBackSize);
                            }
                            owner.scroll_max = xy - rect.Width;
                            owner.scroll_show = xy > rect.Width;
                            break;
                        case TabAlignment.Left:
                            foreach (var it in rect_dir)
                            {
                                if (it.Key.Visible)
                                {
                                    bool close = closable && !it.Key.ReadOnly;
                                    Rectangle rect_it = new Rectangle(rect.X, rect.Y + xy, xy2, it.Value.Height + gap), rect_line = new Rectangle(rect_it.X + xy2 - barSize, rect_it.Y + barPadding, barSize, rect_it.Height - barPadding2);
                                    if (close)
                                    {
                                        if (it.Key.HasIcon) rect_list.Add(new TabPageRect(rect_it, it.Value, gap, ico_size, close_size, ico_gap, true, rw_tmp, rw).SetLine(rect_line));
                                        else rect_list.Add(new TabPageRect(rect_it, false, it.Value, gap, close_size, ico_gap, true, rw_tmp, rw).SetLine(rect_line));
                                    }
                                    else
                                    {
                                        if (it.Key.HasIcon) rect_list.Add(new TabPageRect(rect_it, it.Value, gap, ico_size, ico_gap, true, rw_tmp).SetLine(rect_line));
                                        else rect_list.Add(new TabPageRect(rect_it, it.Value, gap, true, rw_tmp).SetLine(rect_line));
                                    }
                                    it.Key.SetRect(rect_it);
                                    xy += rect_it.Height;
                                }
                                else rect_list.Add(new TabPageRect());
                            }
                            tabs.SetPadding(xy2 + tabs.Margin.Vertical, 0, 0, 0);
                            if (BackSize > 0)
                            {
                                int barBackSize = (int)(BackSize * tabs.Dpi);
                                rect_line_top = new Rectangle(rect.X + xy2 - barBackSize, 0, barBackSize, rect.Height + tabs.Margin.Vertical);
                            }
                            owner.scroll_max = xy - rect.Height;
                            owner.scroll_show = xy > rect.Height;
                            break;
                        case TabAlignment.Right:
                            int x = rect.Right - xy2;
                            foreach (var it in rect_dir)
                            {
                                if (it.Key.Visible)
                                {
                                    bool close = closable && !it.Key.ReadOnly;
                                    Rectangle rect_it = new Rectangle(x, rect.Y + xy, xy2, it.Value.Height + gap), rect_line = new Rectangle(rect_it.X, rect_it.Y + barPadding, barSize, rect_it.Height - barPadding2);
                                    if (close)
                                    {
                                        if (it.Key.HasIcon) rect_list.Add(new TabPageRect(rect_it, it.Value, gap, ico_size, close_size, ico_gap, false, rw_tmp, rw).SetLine(rect_line));
                                        else rect_list.Add(new TabPageRect(rect_it, false, it.Value, gap, close_size, ico_gap, false, rw_tmp, rw).SetLine(rect_line));
                                    }
                                    else
                                    {
                                        if (it.Key.HasIcon) rect_list.Add(new TabPageRect(rect_it, it.Value, gap, ico_size, ico_gap, false, rw_tmp).SetLine(rect_line));
                                        else rect_list.Add(new TabPageRect(rect_it, it.Value, gap, false, rw_tmp).SetLine(rect_line));
                                    }
                                    it.Key.SetRect(rect_it);
                                    xy += rect_it.Height;
                                }
                                else rect_list.Add(new TabPageRect());
                            }
                            tabs.SetPadding(0, 0, xy2 + tabs.Margin.Vertical, 0);
                            if (BackSize > 0)
                            {
                                int barBackSize = (int)(BackSize * tabs.Dpi);
                                rect_line_top = new Rectangle(x, 0, barBackSize, rect.Height + tabs.Margin.Vertical);
                            }
                            owner.scroll_max = xy - rect.Height;
                            owner.scroll_show = xy > rect.Height;
                            break;
                        case TabAlignment.Top:
                        default:
                            foreach (var it in rect_dir)
                            {
                                if (it.Key.Visible)
                                {
                                    bool close = closable && !it.Key.ReadOnly;
                                    Rectangle rect_it;
                                    if (close)
                                    {
                                        if (it.Key.HasIcon)
                                        {
                                            rect_it = new Rectangle(rect.X + xy, rect.Y, it.Value.Width + gap2 + ico_size + close_size + ico_gap * 2, xy2);
                                            rect_list.Add(new TabPageRect(rect_it, it.Value, gap, ico_size, close_size, ico_gap, true).SetLineB(barPadding, barPadding2, barSize));
                                        }
                                        else
                                        {
                                            rect_it = new Rectangle(rect.X + xy, rect.Y, it.Value.Width + gap2 + close_size + ico_gap, xy2);
                                            rect_list.Add(new TabPageRect(rect_it, false, it.Value, gap, close_size, ico_gap, true).SetLineB(barPadding, barPadding2, barSize));
                                        }
                                    }
                                    else
                                    {
                                        if (it.Key.HasIcon)
                                        {
                                            rect_it = new Rectangle(rect.X + xy, rect.Y, it.Value.Width + gap2 + ico_size + ico_gap, xy2);
                                            rect_list.Add(new TabPageRect(rect_it, it.Value, gap, ico_size, ico_gap, true).SetLineB(barPadding, barPadding2, barSize));
                                        }
                                        else
                                        {
                                            rect_it = new Rectangle(rect.X + xy, rect.Y, it.Value.Width + gap2, xy2);
                                            rect_list.Add(new TabPageRect(rect_it, it.Value, gap, true).SetLineB(barPadding, barPadding2, barSize));
                                        }
                                    }
                                    it.Key.SetRect(rect_it);
                                    xy += rect_it.Width;
                                }
                                else rect_list.Add(new TabPageRect());
                            }
                            tabs.SetPadding(0, xy2 + tabs.Margin.Horizontal, 0, 0);
                            if (BackSize > 0)
                            {
                                int barBackSize = (int)(BackSize * tabs.Dpi);
                                rect_line_top = new Rectangle(0, rect.Y + xy2 - barBackSize, rect.Width + tabs.Margin.Horizontal, barBackSize);
                            }
                            owner.scroll_max = xy - rect.Width;
                            owner.scroll_show = xy > rect.Width;
                            break;
                    }
                    if (owner.scroll_show) owner.scroll_max += owner.SizeExceed(rect, rect_list[0].Rect, rect_list[rect_list.Count - 1].Rect);
                    else
                    {
                        owner.scroll_x = owner.scroll_y = 0;
                        if (tabs.centered)
                        {
                            switch (tabs.Alignment)
                            {
                                case TabAlignment.Left:
                                case TabAlignment.Right:
                                    int oy = (rect.Height - xy) / 2;
                                    foreach (var item in rect_dir) item.Key.SetOffset(0, oy);
                                    foreach (var item in rect_list)
                                    {
                                        item.Rect.Offset(0, oy);
                                        item.Rect_Text.Offset(0, oy);
                                        item.Rect_Ico.Offset(0, oy);
                                        item.Rect_Line.Offset(0, oy);
                                    }
                                    break;
                                case TabAlignment.Top:
                                case TabAlignment.Bottom:
                                default:
                                    int ox = (rect.Width - xy) / 2;
                                    foreach (var item in rect_dir) item.Key.SetOffset(ox, 0);
                                    foreach (var item in rect_list)
                                    {
                                        item.Rect.Offset(ox, 0);
                                        item.Rect_Text.Offset(ox, 0);
                                        item.Rect_Ico.Offset(ox, 0);
                                        item.Rect_Line.Offset(ox, 0);
                                    }
                                    break;
                            }
                        }
                    }
                    return rect_list.ToArray();
                });
            }
            int onetmp = 0;
            Dictionary<TabPage, Size> GetDir(Tabs owner, Canvas g, TabCollection items, int gap, int gap2, out int ico_size, out int ico_gap, out int close_size, out int sizewh, out int rw)
            {
                sizewh = rw = 0;
                var size_t = g.MeasureString(Config.NullText, owner.Font);
                var rect_dir = new Dictionary<TabPage, Size>(items.Count);
                int i = 0;
                int? tmp = null;
                foreach (var item in items)
                {
                    var size = g.MeasureString(item.Text, owner.Font);
                    rect_dir.Add(item, size);
                    if (rw < size.Width) rw = size.Width;
                    if (item.Visible && tmp == null) tmp = i;
                    i++;
                }
                onetmp = tmp ?? 0;
                ico_size = (int)(size_t.Height * owner.iconratio);
                ico_gap = (int)(size_t.Height * owner.icongap);
                close_size = (int)(size_t.Height * (owner.iconratio * .8F));
                switch (owner.Alignment)
                {
                    case TabAlignment.Left:
                    case TabAlignment.Right:
                        if (closable)
                        {
                            foreach (var it in rect_dir)
                            {
                                if (it.Key.Visible)
                                {
                                    int w;
                                    if (it.Key.HasIcon) w = it.Value.Width + gap2 + ico_size + ico_gap;
                                    else w = it.Value.Width + gap2;
                                    w += close_size + ico_gap;
                                    if (sizewh < w) sizewh = w;
                                }
                            }
                        }
                        else
                        {
                            foreach (var it in rect_dir)
                            {
                                if (it.Key.Visible)
                                {
                                    int w;
                                    if (it.Key.HasIcon) w = it.Value.Width + gap2 + ico_size + ico_gap;
                                    else w = it.Value.Width + gap2;
                                    if (sizewh < w) sizewh = w;
                                }
                            }
                        }
                        break;
                    case TabAlignment.Top:
                    case TabAlignment.Bottom:
                    default:
                        foreach (var it in rect_dir)
                        {
                            if (it.Key.Visible)
                            {
                                int h = it.Value.Height + gap;
                                if (sizewh < h) sizewh = h;
                            }
                        }
                        break;
                }

                return owner.HandItemSize(rect_dir, ref sizewh);
            }

            #endregion

            #region 渲染

            public void Paint(Tabs owner, Canvas g, TabCollection items)
            {
                if (rects.Length > 0)
                {
                    if (BackSize > 0)
                    {
                        using (var brush = new SolidBrush(Back ?? Colour.BorderSecondary.Get(nameof(Tabs), owner.ColorScheme)))
                        {
                            g.Fill(brush, rect_line_top);
                        }
                    }
                    if (owner.scroll_show) g.SetClip(owner.PaintExceedPre(rect_ful, rects[rects.Length - 1].Rect.Height));
                    else g.SetClip(rect_ful);
                    using (var brush_fore = new SolidBrush(owner.ForeColor ?? Colour.Text.Get(nameof(Tabs), owner.ColorScheme)))
                    using (var brush_fill = new SolidBrush(owner.Fill ?? Colour.Primary.Get(nameof(Tabs), owner.ColorScheme)))
                    using (var brush_active = new SolidBrush(owner.FillActive ?? Colour.PrimaryActive.Get(nameof(Tabs), owner.ColorScheme)))
                    using (var brush_hover = new SolidBrush(owner.FillHover ?? Colour.PrimaryHover.Get(nameof(Tabs), owner.ColorScheme)))
                    {
                        if (owner.scroll_show) g.TranslateTransform(-owner.scroll_x, -owner.scroll_y);
                        if (AnimationBar)
                        {
                            PaintBar(g, AnimationBarValue, brush_fill);
                            int i = 0;
                            foreach (var page in items)
                            {
                                if (page.Visible)
                                {
                                    if (owner.SelectedIndex == i) PaintText(g, rects[i], owner, page, brush_fill);
                                    else if (owner.hover_i == i) PaintText(g, rects[i], owner, page, brush_hover);
                                    else PaintText(g, rects[i], owner, page, brush_fore);
                                }
                                i++;
                            }
                        }
                        else
                        {
                            int i = 0;
                            if (owner.pageMove == null)
                            {
                                foreach (var page in items)
                                {
                                    if (page.Visible)
                                    {
                                        if (owner.SelectedIndex == i)//是否选中
                                        {
                                            PaintBar(g, rects[i].Rect_Line, brush_fill);
                                            PaintText(g, rects[i], owner, page, brush_fill);
                                        }
                                        else if (owner.hover_i == i) PaintText(g, rects[i], owner, page, owner.pageDown == page ? brush_active : brush_hover);
                                        else PaintText(g, rects[i], owner, page, brush_fore);
                                    }
                                    i++;
                                }
                            }
                            else
                            {
                                int sel = 0;
                                foreach (var page in items)
                                {
                                    if (page.Visible)
                                    {
                                        if (owner.pageMove == page) sel = i;
                                        else PaintItem(owner, g, i, page, brush_fore, brush_fill, brush_hover, brush_active);
                                    }
                                    i++;
                                }
                                var state = g.Save();
                                g.TranslateTransform(-owner.offsetx, -owner.offsety);
                                PaintItem(owner, g, sel, owner.pageMove, brush_fore, brush_fill, brush_hover, brush_active);
                                g.Restore(state);
                            }
                        }

                        if (owner.scroll_show) owner.PaintExceed(g, brush_fore.Color, (int)(radius * owner.Dpi), rect_ful, rects[onetmp].Rect, rects[rects.Length - 1].Rect, false);
                    }
                }
            }

            void PaintItem(Tabs owner, Canvas g, int i, TabPage page, SolidBrush brush_fore, SolidBrush brush_fill, SolidBrush brush_hover, SolidBrush brush_active)
            {
                if (owner.SelectedIndex == i)
                {
                    PaintBar(g, rects[i].Rect_Line, brush_fill);
                    PaintText(g, rects[i], owner, page, brush_fill);
                }
                else if (owner.hover_i == i) PaintText(g, rects[i], owner, page, owner.pageDown == page ? brush_active : brush_hover);
                else PaintText(g, rects[i], owner, page, brush_fore);
            }
            void PaintText(Canvas g, TabPageRect rects, Tabs owner, TabPage page, SolidBrush brush)
            {
                if (page.Enabled)
                {
                    if (page.HasIcon)
                    {
                        if (page.Icon != null) g.Image(page.Icon, rects.Rect_Ico);
                        if (page.IconSvg != null) g.GetImgExtend(page.IconSvg, rects.Rect_Ico, brush.Color);
                    }
                    if (closable)
                    {
                        if (rects.hover_close == null) g.PaintIconClose(rects.Rect_Close, Colour.TextQuaternary.Get(nameof(Tabs), owner.ColorScheme));
                        else if (rects.hover_close.Animation) g.PaintIconClose(rects.Rect_Close, Helper.ToColor(rects.hover_close.Value + Colour.TextQuaternary.Get(nameof(Tabs), owner.ColorScheme).A, Colour.Text.Get(nameof(Tabs), owner.ColorScheme)));
                        else if (rects.hover_close.Switch) g.PaintIconClose(rects.Rect_Close, Colour.Text.Get(nameof(Tabs), owner.ColorScheme));
                        else g.PaintIconClose(rects.Rect_Close, Colour.TextQuaternary.Get(nameof(Tabs), owner.ColorScheme));
                    }
                    g.String(page.Text, owner.Font, brush, rects.Rect_Text, owner.s_c);
                }
                else
                {
                    if (page.HasIcon)
                    {
                        if (page.Icon != null) g.Image(page.Icon, rects.Rect_Ico);
                        if (page.IconSvg != null) g.GetImgExtend(page.IconSvg, rects.Rect_Ico, Colour.TextQuaternary.Get(nameof(Tabs), owner.ColorScheme));
                    }
                    if (closable)
                    {
                        if (rects.hover_close == null) g.PaintIconClose(rects.Rect_Close, Colour.TextQuaternary.Get(nameof(Tabs), owner.ColorScheme));
                        else if (rects.hover_close.Animation) g.PaintIconClose(rects.Rect_Close, Helper.ToColor(rects.hover_close.Value + Colour.TextQuaternary.Get(nameof(Tabs), owner.ColorScheme).A, Colour.Text.Get(nameof(Tabs), owner.ColorScheme)));
                        else if (rects.hover_close.Switch) g.PaintIconClose(rects.Rect_Close, Colour.Text.Get(nameof(Tabs), owner.ColorScheme));
                        else g.PaintIconClose(rects.Rect_Close, Colour.TextQuaternary.Get(nameof(Tabs), owner.ColorScheme));
                    }
                    g.String(page.Text, owner.Font, Colour.TextQuaternary.Get(nameof(Tabs), owner.ColorScheme), rects.Rect_Text, owner.s_c);
                }
                owner.PaintBadge(g, page, rects.Rect_Text);
            }
            void PaintBar(Canvas g, RectangleF rect, SolidBrush brush)
            {
                if (radius > 0)
                {
                    using (var path = rect.RoundPath(radius * g.Dpi))
                    {
                        g.Fill(brush, path);
                    }
                }
                else g.Fill(brush, rect);
            }
            void PaintBar(Canvas g, Rectangle rect, SolidBrush brush)
            {
                if (radius > 0)
                {
                    using (var path = rect.RoundPath(radius * g.Dpi))
                    {
                        g.Fill(brush, path);
                    }
                }
                else g.Fill(brush, rect);
            }

            #endregion

            public TabPageRect GetTabRect(int i) => rects[i];

            #region 动画

            bool AnimationBar = false;
            RectangleF AnimationBarValue;
            AnimationTask? ThreadBar;

            void SetRect(int old, int value)
            {
                if (owner == null || owner.items == null || rects.Length == 0) return;
                if (owner.items.ListExceed(value)) return;
                if (owner.items.ListExceed(old))
                {
                    AnimationBarValue = rects[value].Rect_Line;
                    return;
                }
                ThreadBar?.Dispose();
                TabPageRect oldTab = rects[old], newTab = rects[value];
                owner.TabFocusMove(oldTab, newTab, value, rects.Length);
                Helper.GDI(g =>
                {
                    RectangleF OldValue = oldTab.Rect_Line, NewValue = newTab.Rect_Line;
                    if (AnimationBarValue.Height == 0) AnimationBarValue = OldValue;
                    if (Config.HasAnimation(nameof(Tabs)))
                    {
                        if (owner.alignment == TabAlignment.Left || owner.alignment == TabAlignment.Right)
                        {
                            if (OldValue.X == NewValue.X)
                            {
                                AnimationBarValue.X = OldValue.X;
                                AnimationBar = true;
                                float p_val = Math.Abs(NewValue.Y - AnimationBarValue.Y) * 0.09F, p_w_val = Math.Abs(NewValue.Height - AnimationBarValue.Height) * 0.1F, p_val2 = (NewValue.Y - AnimationBarValue.Y) * 0.5F;
                                ThreadBar = new AnimationTask(new AnimationLoopConfig(owner, () =>
                                {
                                    if (AnimationBarValue.Height != NewValue.Height)
                                    {
                                        if (NewValue.Height > OldValue.Height)
                                        {
                                            AnimationBarValue.Height += p_w_val;
                                            if (AnimationBarValue.Height > NewValue.Height) AnimationBarValue.Height = NewValue.Height;
                                        }
                                        else
                                        {
                                            AnimationBarValue.Height -= p_w_val;
                                            if (AnimationBarValue.Height < NewValue.Height) AnimationBarValue.Height = NewValue.Height;
                                        }
                                    }
                                    if (NewValue.Y > OldValue.Y)
                                    {
                                        if (AnimationBarValue.Y > p_val2) AnimationBarValue.Y += p_val / 2F;
                                        else AnimationBarValue.Y += p_val;
                                        if (AnimationBarValue.Y > NewValue.Y)
                                        {
                                            AnimationBarValue.Y = NewValue.Y;
                                            owner.Invalidate();
                                            return false;
                                        }
                                    }
                                    else
                                    {
                                        AnimationBarValue.Y -= p_val;
                                        if (AnimationBarValue.Y < NewValue.Y)
                                        {
                                            AnimationBarValue.Y = NewValue.Y;
                                            owner.Invalidate();
                                            return false;
                                        }
                                    }
                                    owner.Invalidate();
                                    return true;
                                }, 10).SetEnd(() =>
                                {
                                    AnimationBarValue = NewValue;
                                    AnimationBar = false;
                                    owner.Invalidate();
                                }));
                                return;
                            }
                        }
                        else
                        {
                            if (OldValue.Y == NewValue.Y)
                            {
                                AnimationBarValue.Y = OldValue.Y;
                                AnimationBar = true;
                                float p_val = Math.Abs(NewValue.X - AnimationBarValue.X) * 0.09F, p_w_val = Math.Abs(NewValue.Width - AnimationBarValue.Width) * 0.1F, p_val2 = (NewValue.X - AnimationBarValue.X) * 0.5F;
                                ThreadBar = new AnimationTask(new AnimationLoopConfig(owner, () =>
                                {
                                    if (AnimationBarValue.Width != NewValue.Width)
                                    {
                                        if (NewValue.Width > OldValue.Width)
                                        {
                                            AnimationBarValue.Width += p_w_val;
                                            if (AnimationBarValue.Width > NewValue.Width) AnimationBarValue.Width = NewValue.Width;
                                        }
                                        else
                                        {
                                            AnimationBarValue.Width -= p_w_val;
                                            if (AnimationBarValue.Width < NewValue.Width) AnimationBarValue.Width = NewValue.Width;
                                        }
                                    }
                                    if (NewValue.X > OldValue.X)
                                    {
                                        if (AnimationBarValue.X > p_val2) AnimationBarValue.X += p_val / 2F;
                                        else AnimationBarValue.X += p_val;
                                        if (AnimationBarValue.X > NewValue.X)
                                        {
                                            AnimationBarValue.X = NewValue.X;
                                            owner.Invalidate();
                                            return false;
                                        }
                                    }
                                    else
                                    {
                                        AnimationBarValue.X -= p_val;
                                        if (AnimationBarValue.X < NewValue.X)
                                        {
                                            AnimationBarValue.X = NewValue.X;
                                            owner.Invalidate();
                                            return false;
                                        }
                                    }
                                    owner.Invalidate();
                                    return true;
                                }, 10).SetEnd(() =>
                                {
                                    AnimationBarValue = NewValue;
                                    AnimationBar = false;
                                    owner.Invalidate();
                                }));
                                return;
                            }
                        }
                    }
                    AnimationBarValue = NewValue;
                    owner.Invalidate();
                });
            }

            #endregion

            public void SelectedIndexChanged(int i, int old) => SetRect(old, i);
            public void Dispose() => ThreadBar?.Dispose();

            public bool MouseClick(TabPage page, int i, int x, int y)
            {
                if (owner == null) return false;
                if (closable && !page.ReadOnly)
                {
                    if (rects[i].Rect_Close.Contains(x, y))
                    {
                        bool flag = true;
                        if (owner.ClosingPage != null) flag = owner.ClosingPage.Invoke(owner, new ClosingPageEventArgs(page));
                        if (flag) owner.Pages.Remove(page);
                        return true;
                    }
                }
                return false;
            }
            public void MouseMove(int x, int y)
            {
                if (owner == null) return;
                if (closable)
                {
                    int i = 0;
                    foreach (var item in rects)
                    {
                        item.hover_close ??= new ITaskOpacity(nameof(Tabs), owner);
                        if (i == owner.hover_i)
                        {
                            item.hover_close.MaxValue = Colour.Text.Get(nameof(Tabs), owner.ColorScheme).A - Colour.TextQuaternary.Get(nameof(Tabs), owner.ColorScheme).A;
                            item.hover_close.Switch = item.Rect_Close.Contains(x, y);
                        }
                        else item.hover_close.Switch = false;
                        i++;
                    }
                }
            }
            public void MouseLeave()
            {
                if (closable)
                {
                    foreach (var item in rects)
                    {
                        if (item.hover_close == null) continue;
                        item.hover_close.Switch = false;
                    }
                }
            }
        }

        /// <summary>
        /// 卡片样式
        /// </summary>
        public class StyleCard : IStyle
        {
            Tabs? owner;
            public StyleCard() { }
            public StyleCard(Tabs tabs) { owner = tabs; }

            #region 属性

            int radius = 6;
            /// <summary>
            /// 卡片圆角
            /// </summary>
            [Description("卡片圆角"), Category("卡片"), DefaultValue(6)]
            public int Radius
            {
                get => radius;
                set
                {
                    if (radius == value) return;
                    radius = value;
                    owner?.Invalidate();
                }
            }

            int bordersize = 1;
            /// <summary>
            /// 边框大小
            /// </summary>
            [Description("边框大小"), Category("卡片"), DefaultValue(1)]
            public int Border
            {
                get => bordersize;
                set
                {
                    if (bordersize == value) return;
                    bordersize = value;
                    owner?.LoadLayout(true);
                }
            }

            Color? border;
            /// <summary>
            /// 卡片边框颜色
            /// </summary>
            [Description("卡片边框颜色"), Category("卡片"), DefaultValue(null)]
            [Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
            public Color? BorderColor
            {
                get => border;
                set
                {
                    if (border == value) return;
                    border = value;
                    owner?.Invalidate();
                }
            }

            /// <summary>
            /// 卡片边框激活颜色
            /// </summary>
            [Description("卡片边框激活颜色"), Category("卡片"), DefaultValue(null)]
            [Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
            public Color? BorderActive { get; set; }

            Color? fill;
            /// <summary>
            /// 卡片颜色
            /// </summary>
            [Description("卡片颜色"), Category("卡片"), DefaultValue(null)]
            [Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
            public Color? Fill
            {
                get => fill;
                set
                {
                    if (fill == value) return;
                    fill = value;
                    owner?.Invalidate();
                }
            }

            /// <summary>
            /// 卡片悬停颜色
            /// </summary>
            [Description("卡片悬停颜色"), Category("卡片"), DefaultValue(null)]
            [Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
            public Color? FillHover { get; set; }

            /// <summary>
            /// 卡片激活颜色
            /// </summary>
            [Description("卡片激活颜色"), Category("卡片"), DefaultValue(null)]
            [Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
            public Color? FillActive { get; set; }

            int gap = 2;
            /// <summary>
            /// 卡片间距
            /// </summary>
            [Description("卡片间距"), Category("卡片"), DefaultValue(2)]
            public int Gap
            {
                get => gap;
                set
                {
                    if (gap == value) return;
                    gap = value;
                    owner?.LoadLayout(true);
                }
            }

            bool closable = false;
            /// <summary>
            /// 可关闭
            /// </summary>
            [Description("可关闭"), Category("卡片"), DefaultValue(false)]
            public bool Closable
            {
                get => closable;
                set
                {
                    if (closable == value) return;
                    closable = value;
                    owner?.LoadLayout(true);
                }
            }

            #endregion

            #region 布局

            TabPageRect[] rects = new TabPageRect[0];
            public void LoadLayout(Tabs tabs, Rectangle rect, TabCollection items)
            {
                owner = tabs;
                rects = Helper.GDI(g =>
                {
                    int gap = (int)(tabs.Gap * owner.Dpi), gap2 = gap * 2, xy = 0, cardgap = (int)(Gap * owner.Dpi);

                    var rect_list = new List<TabPageRect>(items.Count);
                    var rect_dir = GetDir(tabs, g, items, gap, gap2, out int ico_size, out int ico_gap, out int close_size, out int xy2, out int rw);
                    int? rw_tmp = tabs.TextCenter ? rw : null;
                    switch (tabs.Alignment)
                    {
                        case TabAlignment.Bottom:
                            int y = rect.Bottom - xy2;
                            foreach (var it in rect_dir)
                            {
                                if (it.Key.Visible)
                                {
                                    bool close = closable && !it.Key.ReadOnly;
                                    Rectangle rect_it;
                                    if (close)
                                    {
                                        if (it.Key.HasIcon)
                                        {
                                            rect_it = new Rectangle(rect.X + xy, y, it.Value.Width + gap2 + ico_size + close_size + ico_gap * 2, xy2);
                                            rect_list.Add(new TabPageRect(rect_it, it.Value, gap, ico_size, close_size, ico_gap, true));
                                        }
                                        else
                                        {
                                            rect_it = new Rectangle(rect.X + xy, y, it.Value.Width + gap2 + close_size + ico_gap, xy2);
                                            rect_list.Add(new TabPageRect(rect_it, false, it.Value, gap, close_size, ico_gap, true));
                                        }
                                    }
                                    else
                                    {
                                        if (it.Key.HasIcon)
                                        {
                                            rect_it = new Rectangle(rect.X + xy, y, it.Value.Width + gap2 + ico_size + ico_gap, xy2);
                                            rect_list.Add(new TabPageRect(rect_it, it.Value, gap, ico_size, ico_gap, true));
                                        }
                                        else
                                        {
                                            rect_it = new Rectangle(rect.X + xy, y, it.Value.Width + gap2, xy2);
                                            rect_list.Add(new TabPageRect(rect_it, it.Value, gap, true));
                                        }
                                    }
                                    it.Key.SetRect(rect_it);
                                    xy += rect_it.Width + cardgap;
                                }
                                else rect_list.Add(new TabPageRect());
                            }
                            xy -= cardgap;
                            tabs.SetPadding(0, 0, 0, xy2 + tabs.Margin.Top);
                            owner.scroll_max = xy - rect.Width;
                            owner.scroll_show = xy > rect.Width;
                            break;
                        case TabAlignment.Left:
                            foreach (var it in rect_dir)
                            {
                                if (it.Key.Visible)
                                {
                                    bool close = closable && !it.Key.ReadOnly;
                                    Rectangle rect_it = new Rectangle(rect.X, rect.Y + xy, xy2, it.Value.Height + gap);
                                    if (close)
                                    {
                                        if (it.Key.HasIcon) rect_list.Add(new TabPageRect(rect_it, it.Value, gap, ico_size, close_size, ico_gap, true, rw_tmp, rw));
                                        else rect_list.Add(new TabPageRect(rect_it, false, it.Value, gap, close_size, ico_gap, true, rw_tmp, rw));
                                    }
                                    else
                                    {
                                        if (it.Key.HasIcon) rect_list.Add(new TabPageRect(rect_it, it.Value, gap, ico_size, ico_gap, true, rw_tmp));
                                        else rect_list.Add(new TabPageRect(rect_it, it.Value, gap, true, rw_tmp));
                                    }
                                    it.Key.SetRect(rect_it);
                                    xy += rect_it.Height + cardgap;
                                }
                                else rect_list.Add(new TabPageRect());
                            }
                            xy -= cardgap;
                            tabs.SetPadding(xy2 + tabs.Margin.Left, 0, 0, 0);
                            owner.scroll_max = xy - rect.Height;
                            owner.scroll_show = xy > rect.Height;
                            break;
                        case TabAlignment.Right:
                            int x = rect.Right - xy2;
                            foreach (var it in rect_dir)
                            {
                                if (it.Key.Visible)
                                {
                                    bool close = closable && !it.Key.ReadOnly;
                                    Rectangle rect_it = new Rectangle(x, rect.Y + xy, xy2, it.Value.Height + gap);
                                    if (close)
                                    {
                                        if (it.Key.HasIcon) rect_list.Add(new TabPageRect(rect_it, it.Value, gap, ico_size, close_size, ico_gap, false, rw_tmp, rw));
                                        else rect_list.Add(new TabPageRect(rect_it, false, it.Value, gap, close_size, ico_gap, false, rw_tmp, rw));
                                    }
                                    else
                                    {
                                        if (it.Key.HasIcon) rect_list.Add(new TabPageRect(rect_it, it.Value, gap, ico_size, ico_gap, false, rw_tmp));
                                        else rect_list.Add(new TabPageRect(rect_it, it.Value, gap, false, rw_tmp));
                                    }
                                    it.Key.SetRect(rect_it);
                                    xy += rect_it.Height + cardgap;
                                }
                                else rect_list.Add(new TabPageRect());
                            }
                            xy -= cardgap;
                            tabs.SetPadding(0, 0, xy2 + tabs.Margin.Left, 0);
                            owner.scroll_max = xy - rect.Height;
                            owner.scroll_show = xy > rect.Height;
                            break;
                        case TabAlignment.Top:
                        default:
                            foreach (var it in rect_dir)
                            {
                                if (it.Key.Visible)
                                {
                                    bool close = closable && !it.Key.ReadOnly;
                                    Rectangle rect_it;
                                    if (close)
                                    {
                                        if (it.Key.HasIcon)
                                        {
                                            rect_it = new Rectangle(rect.X + xy, rect.Y, it.Value.Width + gap2 + ico_size + close_size + ico_gap * 2, xy2);
                                            rect_list.Add(new TabPageRect(rect_it, it.Value, gap, ico_size, close_size, ico_gap, true));
                                        }
                                        else
                                        {
                                            rect_it = new Rectangle(rect.X + xy, rect.Y, it.Value.Width + gap2 + close_size + ico_gap, xy2);
                                            rect_list.Add(new TabPageRect(rect_it, false, it.Value, gap, close_size, ico_gap, true));
                                        }
                                    }
                                    else
                                    {
                                        if (it.Key.HasIcon)
                                        {
                                            rect_it = new Rectangle(rect.X + xy, rect.Y, it.Value.Width + gap2 + ico_size + ico_gap, xy2);
                                            rect_list.Add(new TabPageRect(rect_it, it.Value, gap, ico_size, ico_gap, true));
                                        }
                                        else
                                        {
                                            rect_it = new Rectangle(rect.X + xy, rect.Y, it.Value.Width + gap2, xy2);
                                            rect_list.Add(new TabPageRect(rect_it, it.Value, gap, true));
                                        }
                                    }
                                    it.Key.SetRect(rect_it);
                                    xy += rect_it.Width + cardgap;
                                }
                                else rect_list.Add(new TabPageRect());
                            }
                            xy -= cardgap;
                            tabs.SetPadding(0, xy2 + tabs.Margin.Top, 0, 0);
                            owner.scroll_max = xy - rect.Width;
                            owner.scroll_show = xy > rect.Width;
                            break;
                    }
                    if (owner.scroll_show) owner.scroll_max += owner.SizeExceed(owner.ClientRectangle, rect_list[0].Rect, rect_list[rect_list.Count - 1].Rect);
                    else
                    {
                        owner.scroll_x = owner.scroll_y = 0;
                        if (tabs.centered)
                        {
                            switch (tabs.Alignment)
                            {
                                case TabAlignment.Left:
                                case TabAlignment.Right:
                                    int oy = (rect.Height - xy) / 2;
                                    foreach (var item in rect_dir) item.Key.SetOffset(0, oy);
                                    foreach (var item in rect_list)
                                    {
                                        item.Rect.Offset(0, oy);
                                        item.Rect_Text.Offset(0, oy);
                                        item.Rect_Ico.Offset(0, oy);
                                        item.Rect_Close.Offset(0, oy);
                                    }
                                    break;
                                case TabAlignment.Top:
                                case TabAlignment.Bottom:
                                default:
                                    int ox = (rect.Width - xy) / 2;
                                    foreach (var item in rect_dir) item.Key.SetOffset(ox, 0);
                                    foreach (var item in rect_list)
                                    {
                                        item.Rect.Offset(ox, 0);
                                        item.Rect_Text.Offset(ox, 0);
                                        item.Rect_Ico.Offset(ox, 0);
                                        item.Rect_Close.Offset(ox, 0);
                                    }
                                    break;
                            }
                        }
                    }
                    return rect_list.ToArray();
                });
            }
            int onetmp = 0;
            Dictionary<TabPage, Size> GetDir(Tabs owner, Canvas g, TabCollection items, int gap, int gap2, out int ico_size, out int ico_gap, out int close_size, out int sizewh, out int rw)
            {
                sizewh = rw = 0;
                var size_t = g.MeasureString(Config.NullText, owner.Font);
                var rect_dir = new Dictionary<TabPage, Size>(items.Count);
                int i = 0;
                int? tmp = null;
                foreach (var item in items)
                {
                    var size = g.MeasureString(item.Text, owner.Font);
                    rect_dir.Add(item, size);
                    if (rw < size.Width) rw = size.Width;
                    if (item.Visible && tmp == null) tmp = i;
                    i++;
                }
                onetmp = tmp ?? 0;
                ico_size = (int)(size_t.Height * owner.iconratio);
                ico_gap = (int)(size_t.Height * owner.icongap);
                close_size = (int)(size_t.Height * (owner.iconratio * .8F));
                switch (owner.Alignment)
                {
                    case TabAlignment.Left:
                    case TabAlignment.Right:
                        if (closable)
                        {
                            foreach (var it in rect_dir)
                            {
                                if (it.Key.Visible)
                                {
                                    int w;
                                    if (it.Key.HasIcon) w = it.Value.Width + gap2 + ico_size + ico_gap;
                                    else w = it.Value.Width + gap2;
                                    w += close_size + ico_gap;
                                    if (sizewh < w) sizewh = w;
                                }
                            }
                        }
                        else
                        {
                            foreach (var it in rect_dir)
                            {
                                if (it.Key.Visible)
                                {
                                    int w;
                                    if (it.Key.HasIcon) w = it.Value.Width + gap2 + ico_size + ico_gap;
                                    else w = it.Value.Width + gap2;
                                    if (sizewh < w) sizewh = w;
                                }
                            }
                        }
                        break;
                    case TabAlignment.Top:
                    case TabAlignment.Bottom:
                    default:
                        foreach (var it in rect_dir)
                        {
                            if (it.Key.Visible)
                            {
                                int h = it.Value.Height + gap;
                                if (sizewh < h) sizewh = h;
                            }
                        }
                        break;
                }

                return owner.HandItemSize(rect_dir, ref sizewh);
            }

            #endregion

            #region 渲染

            public void Paint(Tabs owner, Canvas g, TabCollection items)
            {
                if (rects.Length == items.Count)
                {
                    using (var brush_fore = new SolidBrush(owner.ForeColor ?? Colour.Text.Get(nameof(Tabs), owner.ColorScheme)))
                    using (var brush_fill = new SolidBrush(owner.Fill ?? Colour.Primary.Get(nameof(Tabs), owner.ColorScheme)))
                    {
                        var rect_t = owner.ClientRectangle;
                        int radius = (int)(Radius * owner.Dpi), bor = (int)(bordersize * owner.Dpi), bor2 = bor * 6, bor22 = bor2 * 2, borb2 = bor / 2;
                        TabPage? sel = null;
                        int select = owner.SelectedIndex;
                        var rect_one = rects[onetmp].Rect;
                        switch (owner.Alignment)
                        {
                            case TabAlignment.Bottom:
                                int read_b_h = rect_one.Height + rect_one.X;
                                var rect_s_b = new Rectangle(rect_t.X, rect_t.Bottom - read_b_h, rect_t.Width, read_b_h);
                                if (owner.scroll_show)
                                {
                                    g.SetClip(owner.PaintExceedPre(rect_s_b, rect_one.Height));
                                    g.TranslateTransform(-owner.scroll_x, -owner.scroll_y);
                                }
                                else g.SetClip(rect_s_b);
                                sel = PaintTABS(owner, g, items, radius, brush_fore, bor, false, false, true, true);
                                g.ResetTransform();
                                if (sel != null)//是否选中
                                {
                                    var state = g.Save();
                                    if (owner.pageMove == sel) g.TranslateTransform(-owner.offsetx, -owner.offsety);

                                    var rect_page = sel.Rect;
                                    if (bor > 0)
                                    {
                                        float ly = rect_page.Y + borb2;
                                        var rect_card = new RectangleF(rect_page.X + borb2, rect_page.Y - borb2, rect_page.Width - bor, rect_page.Height + borb2);
                                        var rect_line = new Rectangle(rect_page.X - bor, rect_page.Y + bor, rect_page.Width + bor2, rect_page.Height + bor);
                                        PaintTABS(owner, g, rect_t, rect_page, radius, rect_card, rect_line, rect_t.X, ly, rect_t.Right, ly, bor, false, false, true, true);
                                        if (owner.scroll_show) g.TranslateTransform(-owner.scroll_x, -owner.scroll_y);
                                    }
                                    else
                                    {
                                        if (owner.scroll_show) g.TranslateTransform(-owner.scroll_x, -owner.scroll_y);
                                        using (var path = Helper.RoundPath(rect_page, radius, false, false, true, true))
                                        using (var brush_bg_active = new SolidBrush(FillActive ?? Colour.BgContainer.Get(nameof(Tabs), owner.ColorScheme)))
                                        {
                                            g.Fill(brush_bg_active, path);
                                        }
                                    }
                                    PaintText(g, rects[select], owner, sel, brush_fill);
                                    g.Restore(state);
                                }
                                break;
                            case TabAlignment.Left:
                                var rect_s_l = new Rectangle(rect_t.X, rect_t.Y, rect_one.Right, rect_t.Height);
                                if (owner.scroll_show)
                                {
                                    g.SetClip(owner.PaintExceedPre(rect_s_l, rect_one.Height));
                                    g.TranslateTransform(-owner.scroll_x, -owner.scroll_y);
                                }
                                else g.SetClip(rect_s_l);
                                sel = PaintTABS(owner, g, items, radius, brush_fore, bor, true, false, false, true);
                                g.ResetTransform();
                                if (sel != null)//是否选中
                                {
                                    var state = g.Save();
                                    if (owner.pageMove == sel) g.TranslateTransform(-owner.offsetx, -owner.offsety);

                                    var rect_page = sel.Rect;
                                    if (bor > 0)
                                    {
                                        float lx = rect_page.Right - borb2;
                                        var rect_card = new RectangleF(rect_page.X - borb2, rect_page.Y + borb2, rect_page.Width + borb2, rect_page.Height - bor);
                                        var rect_line = new Rectangle(rect_page.X - borb2, rect_page.Y - bor, rect_page.Width, rect_page.Height + bor2);
                                        PaintTABS(owner, g, rect_t, rect_page, radius, rect_card, rect_line, lx, rect_t.Y, lx, rect_t.Bottom, bor, true, false, false, true);
                                        if (owner.scroll_show) g.TranslateTransform(-owner.scroll_x, -owner.scroll_y);
                                    }
                                    else
                                    {
                                        if (owner.scroll_show) g.TranslateTransform(-owner.scroll_x, -owner.scroll_y);
                                        using (var path = Helper.RoundPath(rect_page, radius, true, false, false, true))
                                        using (var brush_bg_active = new SolidBrush(FillActive ?? Colour.BgContainer.Get(nameof(Tabs), owner.ColorScheme)))
                                        {
                                            g.Fill(brush_bg_active, path);
                                        }
                                    }
                                    PaintText(g, rects[select], owner, sel, brush_fill);
                                    g.Restore(state);
                                }
                                break;
                            case TabAlignment.Right:
                                int read_r_w = rect_one.Width + rect_one.Y;
                                var rect_s_r = new Rectangle(rect_t.Right - read_r_w, rect_t.Y, read_r_w, rect_t.Height);
                                if (owner.scroll_show)
                                {
                                    g.SetClip(owner.PaintExceedPre(rect_s_r, rect_one.Height));
                                    g.TranslateTransform(-owner.scroll_x, -owner.scroll_y);
                                }
                                else g.SetClip(rect_s_r);
                                sel = PaintTABS(owner, g, items, radius, brush_fore, bor, false, true, true, false);
                                g.ResetTransform();
                                if (sel != null)//是否选中
                                {
                                    var state = g.Save();
                                    if (owner.pageMove == sel) g.TranslateTransform(-owner.offsetx, -owner.offsety);

                                    var rect_page = sel.Rect;
                                    if (bor > 0)
                                    {
                                        float lx = rect_page.X + borb2;
                                        var rect_card = new RectangleF(rect_page.X - borb2, rect_page.Y + borb2, rect_page.Width + borb2, rect_page.Height - bor);
                                        var rect_line = new Rectangle(rect_page.X + bor, rect_page.Y - bor, rect_page.Width + bor, rect_page.Height + bor2);
                                        PaintTABS(owner, g, rect_t, rect_page, radius, rect_card, rect_line, lx, rect_t.Y, lx, rect_t.Bottom, bor, false, true, true, false);
                                        if (owner.scroll_show) g.TranslateTransform(-owner.scroll_x, -owner.scroll_y);
                                    }
                                    else
                                    {
                                        if (owner.scroll_show) g.TranslateTransform(-owner.scroll_x, -owner.scroll_y);
                                        using (var path = Helper.RoundPath(rect_page, radius, false, true, true, false))
                                        using (var brush_bg_active = new SolidBrush(FillActive ?? Colour.BgContainer.Get(nameof(Tabs), owner.ColorScheme)))
                                        {
                                            g.Fill(brush_bg_active, path);
                                        }
                                    }
                                    PaintText(g, rects[select], owner, sel, brush_fill);
                                    g.Restore(state);
                                }
                                break;
                            case TabAlignment.Top:
                            default:
                                var rect_s_t = new Rectangle(rect_t.X, rect_t.Y, rect_t.Width, rect_one.Bottom);
                                if (owner.scroll_show)
                                {
                                    g.SetClip(owner.PaintExceedPre(rect_s_t, rect_one.Height));
                                    g.TranslateTransform(-owner.scroll_x, -owner.scroll_y);
                                }
                                else g.SetClip(rect_s_t);
                                sel = PaintTABS(owner, g, items, radius, brush_fore, bor, true, true, false, false);
                                g.ResetTransform();
                                if (sel != null)//是否选中
                                {
                                    var state = g.Save();
                                    if (owner.pageMove == sel) g.TranslateTransform(-owner.offsetx, -owner.offsety);

                                    var rect_page = sel.Rect;
                                    if (bor > 0)
                                    {
                                        float ly = rect_page.Bottom - borb2;
                                        var rect_card = new RectangleF(rect_page.X + borb2, rect_page.Y - borb2, rect_page.Width - bor, rect_page.Height + borb2);
                                        var rect_line = new Rectangle(rect_page.X - bor, rect_page.Y - bor, rect_page.Width + bor2, rect_page.Height);
                                        PaintTABS(owner, g, rect_t, rect_page, radius, rect_card, rect_line, rect_t.X, ly, rect_t.Right, ly, bor, true, true, false, false);
                                        if (owner.scroll_show) g.TranslateTransform(-owner.scroll_x, -owner.scroll_y);
                                    }
                                    else
                                    {
                                        if (owner.scroll_show) g.TranslateTransform(-owner.scroll_x, -owner.scroll_y);
                                        using (var path = Helper.RoundPath(rect_page, radius, true, true, false, false))
                                        using (var brush_bg_active = new SolidBrush(FillActive ?? Colour.BgContainer.Get(nameof(Tabs), owner.ColorScheme)))
                                        {
                                            g.Fill(brush_bg_active, path);
                                        }
                                    }
                                    PaintText(g, rects[select], owner, sel, brush_fill);
                                    g.Restore(state);
                                }
                                break;
                        }
                        g.ResetClip();
                        if (owner.scroll_show) owner.PaintExceed(g, brush_fore.Color, radius, rect_t, rect_one, rects[rects.Length - 1].Rect, true);
                    }
                }
            }

            TabPage? PaintTABS(Tabs owner, Canvas g, TabCollection items, int radius, SolidBrush brush_fore, int bor, bool TL, bool TR, bool BR, bool BL)
            {
                int i = 0;
                TabPage? sel = null;
                using (var brush_active = new SolidBrush(owner.FillActive ?? Colour.PrimaryActive.Get(nameof(Tabs), owner.ColorScheme)))
                using (var brush_hover = new SolidBrush(owner.FillHover ?? Colour.PrimaryHover.Get(nameof(Tabs), owner.ColorScheme)))
                using (var brush_bg = new SolidBrush(Fill ?? Colour.FillQuaternary.Get(nameof(Tabs), owner.ColorScheme)))
                using (var brush_bg_hover = new SolidBrush(FillHover ?? Colour.FillQuaternary.Get(nameof(Tabs), owner.ColorScheme)))
                {
                    foreach (var page in items)
                    {
                        if (page.Visible)
                        {
                            if (owner.SelectedIndex == i) sel = page;
                            else
                            {
                                var state = g.Save();
                                if (owner.pageMove == page) g.TranslateTransform(-owner.offsetx, -owner.offsety);

                                using (var path = Helper.RoundPath(page.Rect, radius, TL, TR, BR, BL))
                                {
                                    g.Fill(owner.hover_i == i ? brush_bg_hover : brush_bg, path);
                                    if (bor > 0) g.Draw(border ?? Colour.BorderSecondary.Get(nameof(Tabs), owner.ColorScheme), bor, path);
                                    if (owner.hover_i == i) PaintText(g, rects[i], owner, page, owner.pageDown == page ? brush_active : brush_hover);
                                    else PaintText(g, rects[i], owner, page, brush_fore);
                                }
                                g.Restore(state);
                            }
                        }
                        i++;
                    }
                }
                return sel;
            }
            void PaintTABS(Tabs owner, Canvas gg, Rectangle rect_t, Rectangle rect_page, int radius, RectangleF rect_card, Rectangle rect_line, float x, float y, float x2, float y2, int bor, bool TL, bool TR, bool BR, bool BL)
            {
                try
                {
                    using (var bmp = new Bitmap(rect_t.Width, rect_t.Height))
                    {
                        using (var g = Graphics.FromImage(bmp).High())
                        {
                            using (var path = Helper.RoundPath(rect_page, radius, TL, TR, BR, BL))
                            using (var pen_bg = new Pen(BorderActive ?? Colour.BorderColor.Get(nameof(Tabs), owner.ColorScheme), bor))
                            using (var brush_bg_active = new SolidBrush(FillActive ?? Colour.BgContainer.Get(nameof(Tabs), owner.ColorScheme)))
                            {
                                g.DrawLine(pen_bg, x, y, x2, y2);
                                if (owner.scroll_show) g.TranslateTransform(-owner.scroll_x, -owner.scroll_y);
                                using (var path_card = Helper.RoundPath(rect_card, radius, TL, TR, BR, BL))
                                {
                                    g.Fill(brush_bg_active, path_card);
                                }
                                g.SetClip(rect_line);
                                g.Draw(pen_bg, path);
                            }
                        }

                        gg.Image(bmp, rect_t);
                    }
                }
                catch { }
            }
            void PaintText(Canvas g, TabPageRect rects, Tabs owner, TabPage page, SolidBrush brush)
            {
                if (page.Enabled)
                {
                    if (page.HasIcon)
                    {
                        if (page.Icon != null) g.Image(page.Icon, rects.Rect_Ico);
                        if (page.IconSvg != null) g.GetImgExtend(page.IconSvg, rects.Rect_Ico, brush.Color);
                    }
                    if (closable)
                    {
                        if (rects.hover_close == null) g.PaintIconClose(rects.Rect_Close, Colour.TextQuaternary.Get(nameof(Tabs), owner.ColorScheme));
                        else if (rects.hover_close.Animation) g.PaintIconClose(rects.Rect_Close, Helper.ToColor(rects.hover_close.Value + Colour.TextQuaternary.Get(nameof(Tabs), owner.ColorScheme).A, Colour.Text.Get(nameof(Tabs), owner.ColorScheme)));
                        else if (rects.hover_close.Switch) g.PaintIconClose(rects.Rect_Close, Colour.Text.Get(nameof(Tabs), owner.ColorScheme));
                        else g.PaintIconClose(rects.Rect_Close, Colour.TextQuaternary.Get(nameof(Tabs), owner.ColorScheme));
                    }
                    g.String(page.Text, owner.Font, brush, rects.Rect_Text, owner.s_c);
                }
                else
                {
                    if (page.HasIcon)
                    {
                        if (page.Icon != null) g.Image(page.Icon, rects.Rect_Ico);
                        if (page.IconSvg != null) g.GetImgExtend(page.IconSvg, rects.Rect_Ico, Colour.TextQuaternary.Get(nameof(Tabs), owner.ColorScheme));
                    }
                    if (closable)
                    {
                        if (rects.hover_close == null) g.PaintIconClose(rects.Rect_Close, Colour.TextQuaternary.Get(nameof(Tabs), owner.ColorScheme));
                        else if (rects.hover_close.Animation) g.PaintIconClose(rects.Rect_Close, Helper.ToColor(rects.hover_close.Value + Colour.TextQuaternary.Get(nameof(Tabs), owner.ColorScheme).A, Colour.Text.Get(nameof(Tabs), owner.ColorScheme)));
                        else if (rects.hover_close.Switch) g.PaintIconClose(rects.Rect_Close, Colour.Text.Get(nameof(Tabs), owner.ColorScheme));
                        else g.PaintIconClose(rects.Rect_Close, Colour.TextQuaternary.Get(nameof(Tabs), owner.ColorScheme));
                    }
                    g.String(page.Text, owner.Font, Colour.TextQuaternary.Get(nameof(Tabs), owner.ColorScheme), rects.Rect_Text, owner.s_c);
                }
                owner.PaintBadge(g, page, rects.Rect_Text);
            }

            #endregion

            public TabPageRect GetTabRect(int i) => rects[i];

            public void SelectedIndexChanged(int i, int old)
            {
                if (owner == null) return;
                if (old > -1 && i > -1)
                {
                    TabPageRect oldTab = rects[old], newTab = rects[i];
                    if (owner.TabFocusMove(oldTab, newTab, i, rects.Length)) return;
                }
                owner.Invalidate();
            }

            public void Dispose()
            {
                foreach (var item in rects)
                {
                    item.hover_close?.Dispose();
                }
            }

            #region 鼠标

            public bool MouseClick(TabPage page, int i, int x, int y)
            {
                if (owner == null) return false;
                if (closable && !page.ReadOnly)
                {
                    if (rects[i].Rect_Close.Contains(x, y))
                    {
                        bool flag = true;
                        if (owner.ClosingPage != null) flag = owner.ClosingPage.Invoke(owner, new ClosingPageEventArgs(page));
                        if (flag) owner.Pages.Remove(page);
                        return true;
                    }
                }
                return false;
            }
            public void MouseMove(int x, int y)
            {
                if (owner == null) return;
                if (closable)
                {
                    int i = 0;
                    foreach (var item in rects)
                    {
                        item.hover_close ??= new ITaskOpacity(nameof(Tabs), owner);
                        if (i == owner.hover_i)
                        {
                            item.hover_close.MaxValue = Colour.Text.Get(nameof(Tabs), owner.ColorScheme).A - Colour.TextQuaternary.Get(nameof(Tabs), owner.ColorScheme).A;
                            item.hover_close.Switch = item.Rect_Close.Contains(x, y);
                        }
                        else item.hover_close.Switch = false;
                        i++;
                    }
                }
            }

            public void MouseLeave()
            {
                if (closable)
                {
                    foreach (var item in rects)
                    {
                        if (item.hover_close == null) continue;
                        item.hover_close.Switch = false;
                    }
                }
            }

            #endregion
        }

        /// <summary>
        /// 卡片样式2
        /// </summary>
        public class StyleCard2 : IStyle
        {
            Tabs? owner;
            public StyleCard2() { }
            public StyleCard2(Tabs tabs) { owner = tabs; }

            #region 属性

            int radius = 6;
            /// <summary>
            /// 卡片圆角
            /// </summary>
            [Description("卡片圆角"), Category("卡片"), DefaultValue(6)]
            public int Radius
            {
                get => radius;
                set
                {
                    if (radius == value) return;
                    radius = value;
                    owner?.Invalidate();
                }
            }

            int bordersize = 1;
            /// <summary>
            /// 边框大小
            /// </summary>
            [Description("边框大小"), Category("卡片"), DefaultValue(1)]
            public int Border
            {
                get => bordersize;
                set
                {
                    if (bordersize == value) return;
                    bordersize = value;
                    owner?.LoadLayout(true);
                }
            }

            Color? border;
            /// <summary>
            /// 卡片边框颜色
            /// </summary>
            [Description("卡片边框颜色"), Category("卡片"), DefaultValue(null)]
            [Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
            public Color? BorderColor
            {
                get => border;
                set
                {
                    if (border == value) return;
                    border = value;
                    owner?.Invalidate();
                }
            }

            /// <summary>
            /// 卡片边框激活颜色
            /// </summary>
            [Description("卡片边框激活颜色"), Category("卡片"), DefaultValue(null)]
            [Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
            public Color? BorderActive { get; set; }


            Color? fill;
            /// <summary>
            /// 卡片颜色
            /// </summary>
            [Description("卡片颜色"), Category("卡片"), DefaultValue(null)]
            [Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
            public Color? Fill
            {
                get => fill;
                set
                {
                    if (fill == value) return;
                    fill = value;
                    owner?.Invalidate();
                }
            }

            /// <summary>
            /// 卡片悬停颜色
            /// </summary>
            [Description("卡片悬停颜色"), Category("卡片"), DefaultValue(null)]
            [Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
            public Color? FillHover { get; set; }

            /// <summary>
            /// 卡片激活颜色
            /// </summary>
            [Description("卡片激活颜色"), Category("卡片"), DefaultValue(null)]
            [Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
            public Color? FillActive { get; set; }

            int gap = 2;
            /// <summary>
            /// 卡片间距
            /// </summary>
            [Description("卡片间距"), Category("卡片"), DefaultValue(2)]
            public int Gap
            {
                get => gap;
                set
                {
                    if (gap == value) return;
                    gap = value;
                    owner?.LoadLayout(true);
                }
            }
            public enum CloseType { none, always, activate }

            CloseType closable = CloseType.none;
            /// <summary>
            /// 可关闭
            /// </summary>
            [Description("可关闭"), Category("卡片"), DefaultValue(false)]
            public CloseType Closable
            {
                get => closable;
                set
                {
                    if (closable == value) return;
                    closable = value;
                    owner?.LoadLayout(true);
                }
            }

            #endregion

            #region 布局

            TabPageRect[] rects = new TabPageRect[0];
            public void LoadLayout(Tabs tabs, Rectangle rect, TabCollection items)
            {
                owner = tabs;
                rects = Helper.GDI(g =>
                {
                    int gap = (int)(tabs.Gap * tabs.Dpi), gap2 = gap * 2, xy = 0, cardgap = (int)(Gap * tabs.Dpi);
                    var rect_list = new List<TabPageRect>(items.Count);
                    var rect_dir = GetDir(tabs, g, items, gap, gap2, out int ico_size, out int ico_gap, out int close_size, out int xy2, out int rw);
                    int? rw_tmp = tabs.TextCenter ? rw : null;
                    if (closable != CloseType.none)
                    {
                        switch (tabs.Alignment)
                        {
                            case TabAlignment.Bottom:
                                int y = rect.Bottom - xy2;
                                foreach (var it in rect_dir)
                                {
                                    if (it.Key.Visible)
                                    {
                                        Rectangle rect_it = new Rectangle();
                                        if (it.Key.ReadOnly)
                                        {
                                            if (it.Key.HasIcon)
                                            {
                                                rect_it = new Rectangle(rect.X + xy, y, it.Value.Width + gap2 + ico_size + ico_gap, xy2);
                                                rect_list.Add(new TabPageRect(rect_it, it.Value, gap, ico_size, ico_gap, true));
                                            }
                                            else
                                            {
                                                rect_it = new Rectangle(rect.X + xy, y, it.Value.Width + gap2, xy2);
                                                rect_list.Add(new TabPageRect(rect_it, it.Value, gap, true));
                                            }
                                        }
                                        else
                                        {
                                            if (it.Key.HasIcon)
                                            {
                                                rect_it = new Rectangle(rect.X + xy, y, it.Value.Width + gap2 + ico_size + close_size + ico_gap * 2, xy2);
                                                rect_list.Add(new TabPageRect(rect_it, it.Value, gap, ico_size, close_size, ico_gap, true));
                                            }
                                            else
                                            {
                                                rect_it = new Rectangle(rect.X + xy, y, it.Value.Width + gap2 + close_size + ico_gap, xy2);
                                                rect_list.Add(new TabPageRect(rect_it, false, it.Value, gap, close_size, ico_gap, true));
                                            }
                                        }
                                        it.Key.SetRect(rect_it);
                                        xy += rect_it.Width + cardgap;
                                    }
                                    else rect_list.Add(new TabPageRect());
                                }
                                xy -= cardgap;
                                tabs.SetPadding(0, 0, 0, xy2 + 2 + bordersize + tabs.Margin.Top);
                                owner.scroll_max = xy - rect.Width;
                                owner.scroll_show = xy > rect.Width;
                                break;
                            case TabAlignment.Left:
                                foreach (var it in rect_dir)
                                {
                                    if (it.Key.Visible)
                                    {
                                        Rectangle rect_it = new Rectangle(rect.X, rect.Y + xy, xy2, it.Value.Height + gap);
                                        if (it.Key.ReadOnly)
                                        {
                                            if (it.Key.HasIcon) rect_list.Add(new TabPageRect(rect_it, it.Value, gap, ico_size, ico_gap, true, rw_tmp));
                                            else rect_list.Add(new TabPageRect(rect_it, it.Value, gap, true, rw_tmp));
                                        }
                                        else
                                        {
                                            if (it.Key.HasIcon) rect_list.Add(new TabPageRect(rect_it, it.Value, gap, ico_size, close_size, ico_gap, true, rw_tmp, rw));
                                            else rect_list.Add(new TabPageRect(rect_it, false, it.Value, gap, close_size, ico_gap, true, rw_tmp, rw));
                                        }
                                        it.Key.SetRect(rect_it);
                                        xy += rect_it.Height + cardgap;
                                    }
                                    else rect_list.Add(new TabPageRect());
                                }
                                xy -= cardgap;
                                tabs.SetPadding(xy2 + 2 + bordersize + tabs.Margin.Left, 0, 0, 0);
                                owner.scroll_max = xy - rect.Height;
                                owner.scroll_show = xy > rect.Height;
                                break;
                            case TabAlignment.Right:
                                int x = rect.Right - xy2;
                                foreach (var it in rect_dir)
                                {
                                    if (it.Key.Visible)
                                    {
                                        Rectangle rect_it = new Rectangle(x, rect.Y + xy, xy2, it.Value.Height + gap);
                                        if (it.Key.ReadOnly)
                                        {
                                            if (it.Key.HasIcon) rect_list.Add(new TabPageRect(rect_it, it.Value, gap, ico_size, ico_gap, false, rw_tmp));
                                            else rect_list.Add(new TabPageRect(rect_it, it.Value, gap, false, rw_tmp));
                                        }
                                        else
                                        {
                                            if (it.Key.HasIcon) rect_list.Add(new TabPageRect(rect_it, it.Value, gap, ico_size, close_size, ico_gap, false, rw_tmp, rw));
                                            else rect_list.Add(new TabPageRect(rect_it, false, it.Value, gap, close_size, ico_gap, false, rw_tmp, rw));
                                        }
                                        it.Key.SetRect(rect_it);
                                        xy += rect_it.Height + cardgap;
                                    }
                                    else rect_list.Add(new TabPageRect());
                                }
                                xy -= cardgap;
                                tabs.SetPadding(0, 0, xy2 + 2 + bordersize + tabs.Margin.Left, 0);
                                owner.scroll_max = xy - rect.Height;
                                owner.scroll_show = xy > rect.Height;
                                break;
                            case TabAlignment.Top:
                            default:
                                foreach (var it in rect_dir)
                                {
                                    if (it.Key.Visible)
                                    {
                                        Rectangle rect_it = new Rectangle();
                                        if (it.Key.ReadOnly)
                                        {
                                            if (it.Key.HasIcon)
                                            {
                                                rect_it = new Rectangle(rect.X + xy, rect.Y, it.Value.Width + gap2 + ico_size + ico_gap, xy2);
                                                rect_list.Add(new TabPageRect(rect_it, it.Value, gap, ico_size, ico_gap, true));
                                            }
                                            else
                                            {
                                                rect_it = new Rectangle(rect.X + xy, rect.Y, it.Value.Width + gap2, xy2);
                                                rect_list.Add(new TabPageRect(rect_it, it.Value, gap, true));
                                            }
                                        }
                                        else
                                        {
                                            if (it.Key.HasIcon)
                                            {
                                                rect_it = new Rectangle(rect.X + xy, rect.Y, it.Value.Width + gap2 + ico_size + close_size + ico_gap * 2, xy2);
                                                rect_list.Add(new TabPageRect(rect_it, it.Value, gap, ico_size, close_size, ico_gap, true));
                                            }
                                            else
                                            {
                                                rect_it = new Rectangle(rect.X + xy, rect.Y, it.Value.Width + gap2 + close_size + ico_gap, xy2);
                                                rect_list.Add(new TabPageRect(rect_it, false, it.Value, gap, close_size, ico_gap, true));
                                            }
                                        }
                                        it.Key.SetRect(rect_it);
                                        xy += rect_it.Width + cardgap;
                                    }
                                    else rect_list.Add(new TabPageRect());
                                }
                                xy -= cardgap;
                                tabs.SetPadding(0, xy2 + 2 + bordersize + tabs.Margin.Top, 0, 0);
                                owner.scroll_max = xy - rect.Width;
                                owner.scroll_show = xy > rect.Width;
                                break;
                        }
                    }
                    else
                    {
                        switch (tabs.Alignment)
                        {
                            case TabAlignment.Bottom:
                                int y = rect.Bottom - xy2;
                                foreach (var it in rect_dir)
                                {
                                    if (it.Key.Visible)
                                    {
                                        Rectangle rect_it;
                                        if (it.Key.HasIcon)
                                        {
                                            rect_it = new Rectangle(rect.X + xy, y, it.Value.Width + gap2 + ico_size + ico_gap, xy2);
                                            rect_list.Add(new TabPageRect(rect_it, it.Value, gap, ico_size, ico_gap, true, rw_tmp));
                                        }
                                        else
                                        {
                                            rect_it = new Rectangle(rect.X + xy, y, it.Value.Width + gap2, xy2);
                                            rect_list.Add(new TabPageRect(rect_it, it.Value, gap, true, rw_tmp));
                                        }
                                        it.Key.SetRect(rect_it);
                                        xy += rect_it.Width + cardgap;
                                    }
                                    else rect_list.Add(new TabPageRect());
                                }
                                xy -= cardgap;
                                tabs.SetPadding(0, 0, 0, xy2 + 2 + bordersize + tabs.Margin.Top);
                                owner.scroll_max = xy - rect.Width;
                                owner.scroll_show = xy > rect.Width;
                                break;
                            case TabAlignment.Left:
                                foreach (var it in rect_dir)
                                {
                                    if (it.Key.Visible)
                                    {
                                        Rectangle rect_it = new Rectangle(rect.X, rect.Y + xy, xy2, it.Value.Height + gap);
                                        if (it.Key.HasIcon) rect_list.Add(new TabPageRect(rect_it, it.Value, gap, ico_size, ico_gap, true, rw_tmp));
                                        else rect_list.Add(new TabPageRect(rect_it, it.Value, gap, true, rw_tmp));
                                        it.Key.SetRect(rect_it);
                                        xy += rect_it.Height + cardgap;
                                    }
                                    else rect_list.Add(new TabPageRect());
                                }
                                xy -= cardgap;
                                tabs.SetPadding(xy2 + 2 + bordersize + tabs.Margin.Left, 0, 0, 0);
                                owner.scroll_max = xy - rect.Height;
                                owner.scroll_show = xy > rect.Height;
                                break;
                            case TabAlignment.Right:
                                int x = rect.Right - xy2;
                                foreach (var it in rect_dir)
                                {
                                    if (it.Key.Visible)
                                    {
                                        Rectangle rect_it = new Rectangle(x, rect.Y + xy, xy2, it.Value.Height + gap);
                                        if (it.Key.HasIcon) rect_list.Add(new TabPageRect(rect_it, it.Value, gap, ico_size, ico_gap, false, rw_tmp));
                                        else rect_list.Add(new TabPageRect(rect_it, it.Value, gap, false, rw_tmp));
                                        it.Key.SetRect(rect_it);
                                        xy += rect_it.Height + cardgap;
                                    }
                                    else rect_list.Add(new TabPageRect());
                                }
                                xy -= cardgap;
                                tabs.SetPadding(0, 0, xy2 + 2 + bordersize + tabs.Margin.Left, 0);
                                owner.scroll_max = xy - rect.Height;
                                owner.scroll_show = xy > rect.Height;
                                break;
                            case TabAlignment.Top:
                            default:
                                foreach (var it in rect_dir)
                                {
                                    if (it.Key.Visible)
                                    {
                                        Rectangle rect_it;
                                        if (it.Key.HasIcon)
                                        {
                                            rect_it = new Rectangle(rect.X + xy, rect.Y, it.Value.Width + gap2 + ico_size + ico_gap, xy2);
                                            rect_list.Add(new TabPageRect(rect_it, it.Value, gap, ico_size, ico_gap, true, rw_tmp));
                                        }
                                        else
                                        {
                                            rect_it = new Rectangle(rect.X + xy, rect.Y, it.Value.Width + gap2, xy2);
                                            rect_list.Add(new TabPageRect(rect_it, it.Value, gap, true, rw_tmp));
                                        }
                                        it.Key.SetRect(rect_it);
                                        xy += rect_it.Width + cardgap;
                                    }
                                    else rect_list.Add(new TabPageRect());
                                }
                                xy -= cardgap;
                                tabs.SetPadding(0, xy2 + 2 + bordersize + tabs.Margin.Top, 0, 0);
                                owner.scroll_max = xy - rect.Width;
                                owner.scroll_show = xy > rect.Width;
                                break;
                        }
                    }

                    if (owner.scroll_show) owner.scroll_max += owner.SizeExceed(owner.ClientRectangle, rect_list[0].Rect, rect_list[rect_list.Count - 1].Rect);
                    else
                    {
                        owner.scroll_x = owner.scroll_y = 0;
                        if (tabs.centered)
                        {
                            switch (tabs.Alignment)
                            {
                                case TabAlignment.Left:
                                case TabAlignment.Right:
                                    int oy = (rect.Height - xy) / 2;
                                    foreach (var item in rect_dir) item.Key.SetOffset(0, oy);
                                    foreach (var item in rect_list)
                                    {
                                        item.Rect.Offset(0, oy);
                                        item.Rect_Text.Offset(0, oy);
                                        item.Rect_Ico.Offset(0, oy);
                                        item.Rect_Close.Offset(0, oy);
                                    }
                                    break;
                                case TabAlignment.Top:
                                case TabAlignment.Bottom:
                                default:
                                    int ox = (rect.Width - xy) / 2;
                                    foreach (var item in rect_dir) item.Key.SetOffset(ox, 0);
                                    foreach (var item in rect_list)
                                    {
                                        item.Rect.Offset(ox, 0);
                                        item.Rect_Text.Offset(ox, 0);
                                        item.Rect_Ico.Offset(ox, 0);
                                        item.Rect_Close.Offset(ox, 0);
                                    }
                                    break;
                            }
                        }
                    }
                    return rect_list.ToArray();
                });
            }
            int onetmp = 0;
            Dictionary<TabPage, Size> GetDir(Tabs owner, Canvas g, TabCollection items, int gap, int gap2, out int ico_size, out int ico_gap, out int close_size, out int sizewh, out int rw)
            {
                sizewh = rw = 0;
                var size_t = g.MeasureString(Config.NullText, owner.Font);
                var rect_dir = new Dictionary<TabPage, Size>(items.Count);
                int i = 0;
                int? tmp = null;
                foreach (var item in items)
                {
                    var size = g.MeasureString(item.Text, owner.Font);
                    rect_dir.Add(item, size);
                    if (rw < size.Width) rw = size.Width;
                    if (item.Visible && tmp == null) tmp = i;
                    i++;
                }
                onetmp = tmp ?? 0;
                ico_size = (int)(size_t.Height * owner.iconratio);
                ico_gap = (int)(size_t.Height * owner.icongap);
                close_size = (int)(size_t.Height * (owner.iconratio * .8F));
                switch (owner.Alignment)
                {
                    case TabAlignment.Left:
                    case TabAlignment.Right:
                        if (closable != CloseType.none)
                        {
                            foreach (var it in rect_dir)
                            {
                                if (it.Key.Visible)
                                {
                                    int w;
                                    if (it.Key.HasIcon) w = it.Value.Width + gap2 + ico_size + ico_gap;
                                    else w = it.Value.Width + gap2;
                                    w += close_size + ico_gap;
                                    if (sizewh < w) sizewh = w;
                                }
                            }
                        }
                        else
                        {
                            foreach (var it in rect_dir)
                            {
                                if (it.Key.Visible)
                                {
                                    int w;
                                    if (it.Key.HasIcon) w = it.Value.Width + gap2 + ico_size + ico_gap;
                                    else w = it.Value.Width + gap2;
                                    if (sizewh < w) sizewh = w;
                                }
                            }
                        }
                        break;
                    case TabAlignment.Top:
                    case TabAlignment.Bottom:
                    default:
                        foreach (var it in rect_dir)
                        {
                            if (it.Key.Visible)
                            {
                                int h = it.Value.Height + gap;
                                if (sizewh < h) sizewh = h;
                            }
                        }
                        break;
                }

                return owner.HandItemSize(rect_dir, ref sizewh);
            }

            #endregion

            #region 渲染

            public void Paint(Tabs owner, Canvas g, TabCollection items)
            {
                if (rects.Length == items.Count)
                {
                    using (var brush_fore = new SolidBrush(owner.ForeColor ?? Colour.Text.Get(nameof(Tabs), owner.ColorScheme)))
                    using (var brush_fill = new SolidBrush(owner.Fill ?? Colour.Primary.Get(nameof(Tabs), owner.ColorScheme)))
                    using (var brush_active = new SolidBrush(owner.FillActive ?? Colour.PrimaryActive.Get(nameof(Tabs), owner.ColorScheme)))
                    using (var brush_hover = new SolidBrush(owner.FillHover ?? Colour.PrimaryHover.Get(nameof(Tabs), owner.ColorScheme)))
                    using (var brush_bg = new SolidBrush(Fill ?? Colour.FillQuaternary.Get(nameof(Tabs), owner.ColorScheme)))
                    using (var brush_bg_hover = new SolidBrush(FillHover ?? Colour.FillQuaternary.Get(nameof(Tabs), owner.ColorScheme)))
                    using (var brush_bg_active = new SolidBrush(FillActive ?? Colour.BgContainer.Get(nameof(Tabs), owner.ColorScheme)))
                    {
                        var rect_t = owner.ClientRectangle;
                        int radius = (int)(Radius * owner.Dpi), bor = (int)(bordersize * owner.Dpi), bor2 = bor * 6, bor22 = bor2 * 2;
                        float borb2 = bor / 2F;
                        TabPage? sel = null;
                        int i = 0, select = owner.SelectedIndex;
                        var rect_one = rects[onetmp].Rect;
                        switch (owner.Alignment)
                        {
                            case TabAlignment.Bottom:
                                int read_b_h = rect_one.Height + rect_one.X;
                                var rect_s_b = new Rectangle(rect_t.X, rect_t.Bottom - read_b_h - bor, rect_t.Width, read_b_h + bor);
                                if (owner.scroll_show)
                                {
                                    g.SetClip(owner.PaintExceedPre(rect_s_b, rect_one.Height));
                                    g.TranslateTransform(-owner.scroll_x, -owner.scroll_y);
                                }
                                else g.SetClip(rect_s_b);
                                foreach (var page in items)
                                {
                                    if (page.Visible)
                                    {
                                        if (select == i) sel = page;
                                        else
                                        {
                                            var state = g.Save();
                                            if (owner.pageMove == page) g.TranslateTransform(-owner.offsetx, -owner.offsety);

                                            using (var path = Helper.RoundPath(page.Rect, radius, true, true, true, true))
                                            {
                                                g.Fill(owner.hover_i == i ? brush_bg_hover : brush_bg, path);
                                                if (bor > 0) g.Draw(border ?? Colour.BorderSecondary.Get(nameof(Tabs), owner.ColorScheme), bor, path);
                                                if (owner.hover_i == i) PaintText(g, rects[i], owner, page, owner.pageDown == page ? brush_active : brush_hover, true);
                                                else PaintText(g, rects[i], owner, page, brush_fore, closable == CloseType.always);
                                            }
                                            g.Restore(state);
                                        }
                                    }
                                    i++;
                                }
                                g.ResetTransform();
                                if (sel != null)//是否选中
                                {
                                    var state = g.Save();
                                    if (owner.pageMove == sel) g.TranslateTransform(-owner.offsetx, -owner.offsety);

                                    var rect_page = sel.Rect;
                                    using (var path = Helper.RoundPath(rect_page, radius, true, true, true, true))
                                    {
                                        if (bor > 0)
                                        {
                                            float ly = rect_page.Y + borb2;
                                            if (owner.scroll_show) g.TranslateTransform(-owner.scroll_x, -owner.scroll_y);
                                            using (var path2 = Helper.RoundPath(new RectangleF(rect_page.X + borb2, rect_page.Y - borb2, rect_page.Width - bor, rect_page.Height + borb2), radius, true, true, true, true))
                                            {
                                                g.Fill(brush_bg_active, path2);
                                            }
                                            g.Draw(BorderActive ?? Colour.BorderColor.Get(nameof(Tabs), owner.ColorScheme), bor, path);
                                        }
                                        else
                                        {
                                            if (owner.scroll_show) g.TranslateTransform(-owner.scroll_x, -owner.scroll_y);
                                            g.Fill(brush_bg_active, path);
                                        }
                                        PaintText(g, rects[select], owner, sel, brush_fill, true);
                                    }
                                    g.Restore(state);
                                }
                                break;
                            case TabAlignment.Left:
                                var rect_s_l = new Rectangle(rect_t.X, rect_t.Y, rect_one.Right + bor, rect_t.Height);
                                if (owner.scroll_show)
                                {
                                    g.SetClip(owner.PaintExceedPre(rect_s_l, rect_one.Height));
                                    g.TranslateTransform(-owner.scroll_x, -owner.scroll_y);
                                }
                                else g.SetClip(rect_s_l);
                                foreach (var page in items)
                                {
                                    if (page.Visible)
                                    {
                                        if (owner.SelectedIndex == i) sel = page;
                                        else
                                        {
                                            var state = g.Save();
                                            if (owner.pageMove == page) g.TranslateTransform(-owner.offsetx, -owner.offsety);

                                            using (var path = Helper.RoundPath(page.Rect, radius, true, true, true, true))
                                            {
                                                g.Fill(owner.hover_i == i ? brush_bg_hover : brush_bg, path);
                                                if (bor > 0) g.Draw(border ?? Colour.BorderSecondary.Get(nameof(Tabs), owner.ColorScheme), bor, path);
                                                if (owner.hover_i == i) PaintText(g, rects[i], owner, page, owner.pageDown == page ? brush_active : brush_hover, true);
                                                else PaintText(g, rects[i], owner, page, brush_fore, closable == CloseType.always);
                                            }
                                            g.Restore(state);
                                        }
                                    }
                                    i++;
                                }
                                g.ResetTransform();
                                if (sel != null)//是否选中
                                {
                                    var state = g.Save();
                                    if (owner.pageMove == sel) g.TranslateTransform(-owner.offsetx, -owner.offsety);

                                    var rect_page = sel.Rect;
                                    using (var path = Helper.RoundPath(rect_page, radius, true, true, true, true))
                                    {
                                        if (bor > 0)
                                        {
                                            float lx = rect_page.Right - borb2;
                                            if (owner.scroll_show) g.TranslateTransform(-owner.scroll_x, -owner.scroll_y);
                                            using (var path2 = Helper.RoundPath(new RectangleF(rect_page.X - borb2, rect_page.Y + borb2, rect_page.Width + borb2, rect_page.Height - bor), radius, true, true, true, true))
                                            {
                                                g.Fill(brush_bg_active, path2);
                                            }
                                            g.Draw(BorderActive ?? Colour.BorderColor.Get(nameof(Tabs), owner.ColorScheme), bor, path);
                                        }
                                        else
                                        {
                                            if (owner.scroll_show) g.TranslateTransform(-owner.scroll_x, -owner.scroll_y);
                                            g.Fill(brush_bg_active, path);
                                        }
                                        PaintText(g, rects[select], owner, sel, brush_fill, true);
                                    }
                                    g.Restore(state);
                                }
                                break;
                            case TabAlignment.Right:
                                int read_r_w = rect_one.Width + rect_one.Y;
                                var rect_s_r = new Rectangle(rect_t.Right - read_r_w - bor, rect_t.Y, read_r_w + bor, rect_t.Height);
                                if (owner.scroll_show)
                                {
                                    g.SetClip(owner.PaintExceedPre(rect_s_r, rect_one.Height));
                                    g.TranslateTransform(-owner.scroll_x, -owner.scroll_y);
                                }
                                else g.SetClip(rect_s_r);
                                foreach (var page in items)
                                {
                                    if (page.Visible)
                                    {
                                        if (owner.SelectedIndex == i) sel = page;
                                        else
                                        {
                                            var state = g.Save();
                                            if (owner.pageMove == page) g.TranslateTransform(-owner.offsetx, -owner.offsety);

                                            using (var path = Helper.RoundPath(page.Rect, radius, true, true, true, true))
                                            {
                                                g.Fill(owner.hover_i == i ? brush_bg_hover : brush_bg, path);
                                                if (bor > 0) g.Draw(border ?? Colour.BorderSecondary.Get(nameof(Tabs), owner.ColorScheme), bor, path);
                                                if (owner.hover_i == i) PaintText(g, rects[i], owner, page, owner.pageDown == page ? brush_active : brush_hover, true);
                                                else PaintText(g, rects[i], owner, page, brush_fore, closable == CloseType.always);
                                            }
                                            g.Restore(state);
                                        }
                                    }
                                    i++;
                                }
                                g.ResetTransform();
                                if (sel != null)//是否选中
                                {
                                    var state = g.Save();
                                    if (owner.pageMove == sel) g.TranslateTransform(-owner.offsetx, -owner.offsety);

                                    var rect_page = sel.Rect;
                                    using (var path = Helper.RoundPath(rect_page, radius, true, true, true, true))
                                    {
                                        if (bor > 0)
                                        {
                                            float lx = rect_page.X + borb2;
                                            if (owner.scroll_show) g.TranslateTransform(-owner.scroll_x, -owner.scroll_y);
                                            using (var path2 = Helper.RoundPath(new RectangleF(rect_page.X - borb2, rect_page.Y + borb2, rect_page.Width + borb2, rect_page.Height - bor), radius, true, true, true, true))
                                            {
                                                g.Fill(brush_bg_active, path2);
                                            }
                                            g.Draw(BorderActive ?? Colour.BorderColor.Get(nameof(Tabs), owner.ColorScheme), bor, path);
                                        }
                                        else
                                        {
                                            if (owner.scroll_show) g.TranslateTransform(-owner.scroll_x, -owner.scroll_y);
                                            g.Fill(brush_bg_active, path);
                                        }
                                        PaintText(g, rects[select], owner, sel, brush_fill, true);
                                    }
                                    g.Restore(state);
                                }
                                break;
                            case TabAlignment.Top:
                            default:
                                var rect_s_t = new Rectangle(rect_t.X, rect_t.Y, rect_t.Width, rect_one.Bottom + bor);
                                if (owner.scroll_show)
                                {
                                    g.SetClip(owner.PaintExceedPre(rect_s_t, rect_one.Height));
                                    g.TranslateTransform(-owner.scroll_x, -owner.scroll_y);
                                }
                                else g.SetClip(rect_s_t);
                                foreach (var page in items)
                                {
                                    if (page.Visible)
                                    {
                                        if (owner.SelectedIndex == i) sel = page;
                                        else
                                        {
                                            var state = g.Save();
                                            if (owner.pageMove == page) g.TranslateTransform(-owner.offsetx, -owner.offsety);

                                            using (var path = Helper.RoundPath(page.Rect, radius, true, true, true, true))
                                            {
                                                g.Fill(owner.hover_i == i ? brush_bg_hover : brush_bg, path);
                                                if (bor > 0) g.Draw(border ?? Colour.BorderSecondary.Get(nameof(Tabs), owner.ColorScheme), bor, path);
                                                if (owner.hover_i == i) PaintText(g, rects[i], owner, page, owner.pageDown == page ? brush_active : brush_hover, true);
                                                else PaintText(g, rects[i], owner, page, brush_fore, (closable == CloseType.always && !page.ReadOnly));
                                            }
                                            g.Restore(state);
                                        }
                                    }
                                    i++;
                                }
                                g.ResetTransform();
                                if (sel != null)//是否选中
                                {
                                    var state = g.Save();
                                    if (owner.pageMove == sel) g.TranslateTransform(-owner.offsetx, -owner.offsety);

                                    var rect_page = sel.Rect;
                                    using (var path = Helper.RoundPath(rect_page, radius, true, true, true, true))
                                    {
                                        if (bor > 0)
                                        {
                                            float ly = rect_page.Bottom - borb2;
                                            if (owner.scroll_show) g.TranslateTransform(-owner.scroll_x, -owner.scroll_y);
                                            using (var path2 = Helper.RoundPath(new RectangleF(rect_page.X + borb2, rect_page.Y - borb2, rect_page.Width - bor, rect_page.Height + borb2), radius, true, true, true, true))
                                            {
                                                g.Fill(brush_bg_active, path2);
                                            }
                                            g.Draw(BorderActive ?? Colour.BorderColor.Get(nameof(Tabs), owner.ColorScheme), bor, path);
                                        }
                                        else
                                        {
                                            if (owner.scroll_show) g.TranslateTransform(-owner.scroll_x, -owner.scroll_y);
                                            g.Fill(brush_bg_active, path);
                                        }
                                        PaintText(g, rects[select], owner, sel, brush_fill, !sel.ReadOnly);
                                    }
                                    g.Restore(state);
                                }
                                break;
                        }

                        if (owner.scroll_show) owner.PaintExceed(g, brush_fore.Color, radius, rect_t, rect_one, rects[rects.Length - 1].Rect, true);
                    }
                }
            }
            void PaintText(Canvas g, TabPageRect rects, Tabs owner, TabPage page, SolidBrush brush, bool closshow = false)
            {
                if (page.Enabled)
                {
                    if (page.HasIcon)
                    {
                        if (page.Icon != null) g.Image(page.Icon, rects.Rect_Ico);
                        if (page.IconSvg != null) g.GetImgExtend(page.IconSvg, rects.Rect_Ico, brush.Color);
                    }
                    if (closable != CloseType.none && closshow)
                    {
                        if (rects.hover_close == null) g.PaintIconClose(rects.Rect_Close, Colour.TextQuaternary.Get(nameof(Tabs), owner.ColorScheme));
                        else if (rects.hover_close.Animation) g.PaintIconClose(rects.Rect_Close, Helper.ToColor(rects.hover_close.Value + Colour.TextQuaternary.Get(nameof(Tabs), owner.ColorScheme).A, Colour.Text.Get(nameof(Tabs), owner.ColorScheme)));
                        else if (rects.hover_close.Switch) g.PaintIconClose(rects.Rect_Close, Colour.Text.Get(nameof(Tabs), owner.ColorScheme));
                        else g.PaintIconClose(rects.Rect_Close, Colour.TextQuaternary.Get(nameof(Tabs), owner.ColorScheme));
                    }
                    g.String(page.Text, owner.Font, brush, rects.Rect_Text, owner.s_c);
                }
                else
                {
                    if (page.HasIcon)
                    {
                        if (page.Icon != null) g.Image(page.Icon, rects.Rect_Ico);
                        if (page.IconSvg != null) g.GetImgExtend(page.IconSvg, rects.Rect_Ico, Colour.TextQuaternary.Get(nameof(Tabs), owner.ColorScheme));
                    }
                    if (closable != CloseType.none && closshow)
                    {
                        if (rects.hover_close == null) g.PaintIconClose(rects.Rect_Close, Colour.TextQuaternary.Get(nameof(Tabs), owner.ColorScheme));
                        else if (rects.hover_close.Animation) g.PaintIconClose(rects.Rect_Close, Helper.ToColor(rects.hover_close.Value + Colour.TextQuaternary.Get(nameof(Tabs), owner.ColorScheme).A, Colour.Text.Get(nameof(Tabs), owner.ColorScheme)));
                        else if (rects.hover_close.Switch) g.PaintIconClose(rects.Rect_Close, Colour.Text.Get(nameof(Tabs), owner.ColorScheme));
                        else g.PaintIconClose(rects.Rect_Close, Colour.TextQuaternary.Get(nameof(Tabs), owner.ColorScheme));
                    }
                    g.String(page.Text, owner.Font, Colour.TextQuaternary.Get(nameof(Tabs), owner.ColorScheme), rects.Rect_Text, owner.s_c);
                }
                owner.PaintBadge(g, page, rects.Rect_Text);
            }

            #endregion
            public TabPageRect GetTabRect(int i) => rects[i];

            public void SelectedIndexChanged(int i, int old)
            {
                if (owner == null) return;
                if (old > -1 && i > -1)
                {
                    TabPageRect oldTab = rects[old], newTab = rects[i];
                    if (owner.TabFocusMove(oldTab, newTab, i, rects.Length)) return;
                }
                owner.Invalidate();
            }

            public void Dispose()
            {
                foreach (var item in rects) item.hover_close?.Dispose();
            }

            #region 鼠标

            public bool MouseClick(TabPage page, int i, int x, int y)
            {
                if (owner == null) return false;
                if (closable != CloseType.none)
                {
                    if (rects[i].Rect_Close.Contains(x, y))
                    {
                        bool flag = true;
                        if (owner.ClosingPage != null) flag = owner.ClosingPage.Invoke(owner, new ClosingPageEventArgs(page));
                        if (flag) owner.Pages.Remove(page);
                        return true;
                    }
                }
                return false;
            }
            public void MouseMove(int x, int y)
            {
                if (owner == null) return;
                if (closable != CloseType.none)
                {
                    int i = 0;
                    foreach (var item in rects)
                    {
                        item.hover_close ??= new ITaskOpacity(nameof(Tabs), owner);
                        if (i == owner.hover_i)
                        {
                            item.hover_close.MaxValue = Colour.Text.Get(nameof(Tabs), owner.ColorScheme).A - Colour.TextQuaternary.Get(nameof(Tabs), owner.ColorScheme).A;
                            item.hover_close.Switch = item.Rect_Close.Contains(x, y);
                        }
                        else item.hover_close.Switch = false;
                        i++;
                    }
                }
            }

            public bool MouseMovePre(int x, int y) => false;

            public void MouseLeave()
            {
                if (closable != CloseType.none)
                {
                    foreach (var item in rects)
                    {
                        if (item.hover_close == null) continue;
                        item.hover_close.Switch = false;
                    }
                }
            }

            #endregion
        }

        [TypeConverter(typeof(ExpandableObjectConverter))]
        public interface IStyle
        {
            void LoadLayout(Tabs owner, Rectangle rect, TabCollection items);
            void Paint(Tabs owner, Canvas g, TabCollection items);
            void SelectedIndexChanged(int i, int old);
            bool MouseClick(TabPage page, int i, int x, int y);
            void MouseMove(int x, int y);
            void MouseLeave();
            void Dispose();

            TabPageRect GetTabRect(int i);
        }
    }

    public class TabPageRect
    {
        public TabPageRect() { }
        public TabPageRect(Rectangle rect, Size size, int gap, bool left, int? rw = null)
        {
            Rect = rect;
            if (left) Rect_Text = new Rectangle(rect.X + gap, rect.Y, rw ?? size.Width, rect.Height);
            else
            {
                int w = rw ?? size.Width;
                Rect_Text = new Rectangle(rect.Right - gap - w, rect.Y, w, rect.Height);
            }
        }

        #region Line

        public TabPageRect SetLine(int barPadding, int barPadding2, int barSize)
        {
            Rect_Line = new Rectangle(Rect.X + barPadding, Rect.Y, Rect.Width - barPadding2, barSize);
            return this;
        }
        public TabPageRect SetLineB(int barPadding, int barPadding2, int barSize)
        {
            Rect_Line = new Rectangle(Rect.X + barPadding, Rect.Bottom - barSize, Rect.Width - barPadding2, barSize);
            return this;
        }
        public TabPageRect SetLine(Rectangle rect)
        {
            Rect_Line = rect;
            return this;
        }

        #endregion

        #region Card

        /// <summary>
        /// 布局（图标+文本）
        /// </summary>
        /// <param name="rect">项区域</param>
        /// <param name="size">文字大小</param>
        /// <param name="gap">边距</param>
        /// <param name="ico_size">图标大小</param>
        /// <param name="ico_gap">图标边距</param>
        public TabPageRect(Rectangle rect, Size size, int gap, int ico_size, int ico_gap, bool left, int? rw = null)
        {
            Rect = rect;
            int ux = rect.X + gap;
            Rect_Ico = new Rectangle(ux, rect.Y + (rect.Height - ico_size) / 2, ico_size, ico_size);
            if (left) Rect_Text = new Rectangle(ux + ico_size + ico_gap, rect.Y, rw ?? size.Width, rect.Height);
            else
            {
                int w = rw ?? size.Width;
                Rect_Text = new Rectangle(rect.Right - gap - w, rect.Y, w, rect.Height);
            }
        }

        /// <summary>
        /// 布局（图标+文本+关闭）
        /// </summary>
        /// <param name="rect">项区域</param>
        /// <param name="size">文字大小</param>
        /// <param name="gap">边距</param>
        /// <param name="ico_size">图标大小</param>
        /// <param name="close_size">关闭大小</param>
        /// <param name="ico_gap">图标边距</param>
        public TabPageRect(Rectangle rect, Size size, int gap, int ico_size, int close_size, int ico_gap, bool left, int? rw = null, int? tw = null)
        {
            Rect = rect;
            int ux = rect.X + gap;
            Rect_Ico = new Rectangle(ux, rect.Y + (rect.Height - ico_size) / 2, ico_size, ico_size);
            ux += ico_size + ico_gap;
            if (left) Rect_Text = new Rectangle(ux, rect.Y, rw ?? size.Width, rect.Height);
            else
            {
                int w = rw ?? size.Width;
                Rect_Text = new Rectangle(rect.Right - gap - close_size - ico_gap - w, rect.Y, w, rect.Height);
            }
            Rect_Close = new Rectangle(ux + (tw ?? size.Width) + ico_gap, rect.Y + (rect.Height - close_size) / 2, close_size, close_size);
        }

        /// <summary>
        /// 布局（图标+关闭）
        /// </summary>
        /// <param name="rect">项区域</param>
        /// <param name="size">文字大小</param>
        /// <param name="gap">边距</param>
        /// <param name="close_size">关闭大小</param>
        /// <param name="ico_gap">图标边距</param>
        public TabPageRect(Rectangle rect, bool test, Size size, int gap, int close_size, int ico_gap, bool left, int? rw = null, int? tw = null)
        {
            Rect = rect;
            int ux = rect.X + gap;
            if (left) Rect_Text = new Rectangle(ux, rect.Y, rw ?? size.Width, rect.Height);
            else
            {
                int w = rw ?? size.Width;
                Rect_Text = new Rectangle(rect.Right - gap - close_size - ico_gap - w, rect.Y, w, rect.Height);
            }
            Rect_Close = new Rectangle(ux + (tw ?? size.Width) + ico_gap, rect.Y + (rect.Height - close_size) / 2, close_size, close_size);
        }

        #endregion

        /// <summary>
        /// 总区域
        /// </summary>
        public Rectangle Rect;
        /// <summary>
        /// 线条区域
        /// </summary>
        public Rectangle Rect_Line;
        /// <summary>
        /// 文本区域
        /// </summary>
        public Rectangle Rect_Text;
        /// <summary>
        /// 图标区域
        /// </summary>
        public Rectangle Rect_Ico;
        /// <summary>
        /// 关闭按钮区域
        /// </summary>
        public Rectangle Rect_Close;

        internal ITaskOpacity? hover_close;
    }
}