// 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.
// 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
{
///
/// 线条样式
///
public class StyleLine : IStyle
{
Tabs? owner;
public StyleLine() { }
public StyleLine(Tabs tabs) { owner = tabs; }
int size = 3;
///
/// 条大小
///
[Description("条大小"), Category("样式"), DefaultValue(3)]
public int Size
{
get => size;
set
{
if (size == value) return;
size = value;
owner?.LoadLayout();
}
}
int padding = 8;
///
/// 条边距
///
[Description("条边距"), Category("样式"), DefaultValue(8)]
public int Padding
{
get => padding;
set
{
if (padding == value) return;
padding = value;
owner?.LoadLayout();
}
}
int radius = 0;
///
/// 条圆角
///
[Description("条圆角"), Category("样式"), DefaultValue(0)]
public int Radius
{
get => radius;
set
{
if (radius == value) return;
radius = value;
owner?.Invalidate();
}
}
int backsize = 1;
///
/// 条背景大小
///
[Description("条背景大小"), Category("样式"), DefaultValue(1)]
public int BackSize
{
get => backsize;
set
{
if (backsize == value) return;
backsize = value;
owner?.LoadLayout();
}
}
///
/// 条背景
///
[Description("条背景"), Category("样式"), DefaultValue(null)]
[Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
public Color? Back { get; set; }
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 * Config.Dpi), gapI = gap / 2, xy = 0, xy2 = 0;
int barSize = (int)(Size * Config.Dpi), barPadding = (int)(Padding * Config.Dpi), barPadding2 = barPadding * 2;
var rect_list = new List(items.Count);
var rect_dir = GetDir(tabs, g, items, gap, gapI, out int ico_size, out xy2);
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 + gap + ico_size + gapI, xy2);
rect_list.Add(new TabPageRect(rect_it, new Rectangle(rect_it.X + barPadding, rect_it.Y, rect_it.Width - barPadding2, barSize), it.Value, ico_size, gap, gapI));
}
else
{
rect_it = new Rectangle(rect.X + xy, y, it.Value.Width + gap, xy2);
rect_list.Add(new TabPageRect(rect_it, new Rectangle(rect_it.X + barPadding, rect_it.Y, rect_it.Width - barPadding2, barSize)));
}
it.Key.SetRect(rect_it);
xy += rect_it.Width;
}
else rect_list.Add(new TabPageRect());
}
tabs.SetPadding(0, 0, 0, xy2);
if (BackSize > 0)
{
int barBackSize = (int)(BackSize * Config.Dpi);
rect_line_top = new Rectangle(rect.X, rect.Bottom - xy2, rect.Width, 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)
{
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, new Rectangle(rect_it.X + xy2 - barSize, rect_it.Y + barPadding, barSize, rect_it.Height - barPadding2), it.Value, ico_size, gap, gapI));
else rect_list.Add(new TabPageRect(rect_it, new Rectangle(rect_it.X + xy2 - barSize, rect_it.Y + barPadding, barSize, rect_it.Height - barPadding2)));
it.Key.SetRect(rect_it);
xy += rect_it.Height;
}
else rect_list.Add(new TabPageRect());
}
tabs.SetPadding(xy2, 0, 0, 0);
if (BackSize > 0)
{
int barBackSize = (int)(BackSize * Config.Dpi);
rect_line_top = new Rectangle(rect.X + xy2 - barBackSize, rect.Y, barBackSize, rect.Height);
}
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, new Rectangle(rect_it.X, rect_it.Y + barPadding, barSize, rect_it.Height - barPadding2), it.Value, ico_size, gap, gapI));
else rect_list.Add(new TabPageRect(rect_it, new Rectangle(rect_it.X, rect_it.Y + barPadding, barSize, rect_it.Height - barPadding2)));
it.Key.SetRect(rect_it);
xy += rect_it.Height;
}
else rect_list.Add(new TabPageRect());
}
tabs.SetPadding(0, 0, xy2, 0);
if (BackSize > 0)
{
int barBackSize = (int)(BackSize * Config.Dpi);
rect_line_top = new Rectangle(x, rect.Y, barBackSize, rect.Height);
}
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 + gap + ico_size + gapI, xy2);
rect_list.Add(new TabPageRect(rect_it, new Rectangle(rect_it.X + barPadding, rect_it.Bottom - barSize, rect_it.Width - barPadding2, barSize), it.Value, ico_size, gap, gapI));
}
else
{
rect_it = new Rectangle(rect.X + xy, rect.Y, it.Value.Width + gap, xy2);
rect_list.Add(new TabPageRect(rect_it, new Rectangle(rect_it.X + barPadding, rect_it.Bottom - barSize, rect_it.Width - barPadding2, barSize)));
}
it.Key.SetRect(rect_it);
xy += rect_it.Width;
}
else rect_list.Add(new TabPageRect());
}
tabs.SetPadding(0, xy2, 0, 0);
if (BackSize > 0)
{
int barBackSize = (int)(BackSize * Config.Dpi);
rect_line_top = new Rectangle(rect.Left, rect.Y + xy2 - barBackSize, rect.Width, 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();
});
}
public void Paint(Tabs owner, Graphics g, TabCollection items)
{
if (rects.Length > 0)
{
if (BackSize > 0)
{
using (var brush = new SolidBrush(Back ?? AntdUI.Style.Db.BorderSecondary))
{
g.FillRectangle(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 ?? AntdUI.Style.Db.Text))
using (var brush_fill = new SolidBrush(owner.Fill ?? AntdUI.Style.Db.Primary))
using (var brush_active = new SolidBrush(owner.FillActive ?? AntdUI.Style.Db.PrimaryActive))
using (var brush_hover = new SolidBrush(owner.FillHover ?? AntdUI.Style.Db.PrimaryHover))
{
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;
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, page.MDown ? brush_active : brush_hover);
else PaintText(g, rects[i], owner, page, brush_fore);
}
i++;
}
}
if (owner.scroll_show) owner.PaintExceed(g, brush_fore.Color, (int)(radius * Config.Dpi), rect_ful, rects[0].Rect, rects[rects.Length - 1].Rect, false);
}
}
}
#region 辅助
Dictionary GetDir(Tabs owner, Graphics g, TabCollection items, int gap, int gapI, out int ico_size, out int sizewh)
{
sizewh = 0;
var size_t = g.MeasureString(Config.NullText, owner.Font).Size();
var rect_dir = new Dictionary(items.Count);
foreach (var item in items)
{
var size = g.MeasureString(item.Text, owner.Font).Size();
rect_dir.Add(item, size);
}
ico_size = (int)(size_t.Height * owner.IconRatio);
switch (owner.Alignment)
{
case TabAlignment.Left:
case TabAlignment.Right:
foreach (var it in rect_dir)
{
if (it.Key.Visible)
{
int w;
if (it.Key.HasIcon) w = it.Value.Width + ico_size + gap + gapI;
else w = it.Value.Width + gap;
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);
}
void PaintText(Graphics g, TabPageRect rects, Tabs owner, TabPage page, SolidBrush brush)
{
if (page.HasIcon)
{
if (page.Icon != null) g.DrawImage(page.Icon, rects.Rect_Ico);
else if (page.IconSvg != null) g.GetImgExtend(page.IconSvg, rects.Rect_Ico, brush.Color);
}
g.DrawStr(page.Text, owner.Font, brush, rects.Rect_Text, owner.s_c);
owner.PaintBadge(g, page, rects.Rect_Text);
}
void PaintBar(Graphics g, RectangleF rect, SolidBrush brush)
{
if (radius > 0)
{
using (var path = rect.RoundPath(radius * Config.Dpi))
{
g.FillPath(brush, path);
}
}
else g.FillRectangle(brush, rect);
}
void PaintBar(Graphics g, Rectangle rect, SolidBrush brush)
{
if (radius > 0)
{
using (var path = rect.RoundPath(radius * Config.Dpi))
{
g.FillPath(brush, path);
}
}
else g.FillRectangle(brush, rect);
}
internal class TabPageRect
{
public TabPageRect() { }
public TabPageRect(Rectangle rect, Rectangle rect_line)
{
Rect = Rect_Text = rect;
Rect_Line = rect_line;
}
public TabPageRect(Rectangle rect_it, Rectangle rect_line, Size size, int ico_size, int gap, int gapI)
{
Rect = rect_it;
Rect_Line = rect_line;
Rect_Text = new Rectangle(rect_it.X + ico_size + gapI, rect_it.Y, size.Width + gap, rect_it.Height);
Rect_Ico = new Rectangle(rect_it.X + gapI, rect_it.Y + (rect_it.Height - ico_size) / 2, ico_size, ico_size);
}
public Rectangle Rect;
public Rectangle Rect_Line;
public Rectangle Rect_Text;
public Rectangle Rect_Ico;
}
#endregion
#region 动画
bool AnimationBar = false;
RectangleF AnimationBarValue;
ITask? ThreadBar = null;
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();
Helper.GDI(g =>
{
RectangleF OldValue = rects[old].Rect_Line, NewValue = rects[value].Rect_Line;
if (AnimationBarValue.Height == 0) AnimationBarValue = OldValue;
if (Config.Animation)
{
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 ITask(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, () =>
{
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 ITask(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, () =>
{
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 void MouseWheel(int delta)
{
if (owner != null && owner.scroll_show)
{
switch (owner.Alignment)
{
case TabAlignment.Left:
case TabAlignment.Right:
owner.scroll_x = 0;
owner.scroll_y -= delta;
break;
case TabAlignment.Top:
case TabAlignment.Bottom:
default:
owner.scroll_y = 0;
owner.scroll_x -= delta;
break;
}
}
}
public void MouseMove(int x, int y) { }
public bool MouseClick(TabPage page, int i, int x, int y) => false;
public void MouseLeave() { }
}
///
/// 卡片样式
///
public class StyleCard : IStyle
{
Tabs? owner;
public StyleCard() { }
public StyleCard(Tabs tabs) { owner = tabs; }
int radius = 6;
///
/// 卡片圆角
///
[Description("卡片圆角"), Category("卡片"), DefaultValue(6)]
public int Radius
{
get => radius;
set
{
if (radius == value) return;
radius = value;
owner?.Invalidate();
}
}
int bordersize = 1;
///
/// 边框大小
///
[Description("边框大小"), Category("卡片"), DefaultValue(1)]
public int Border
{
get => bordersize;
set
{
if (bordersize == value) return;
bordersize = value;
owner?.LoadLayout();
}
}
Color? border;
///
/// 卡片边框颜色
///
[Description("卡片边框颜色"), Category("卡片"), DefaultValue(null)]
[Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
public Color? BorderColor
{
get => border;
set
{
if (border == value) return;
border = value;
owner?.Invalidate();
}
}
///
/// 卡片边框激活颜色
///
[Description("卡片边框激活颜色"), Category("卡片"), DefaultValue(null)]
[Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
public Color? BorderActive { get; set; }
Color? fill;
///
/// 卡片颜色
///
[Description("卡片颜色"), Category("卡片"), DefaultValue(null)]
[Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
public Color? Fill
{
get => fill;
set
{
if (fill == value) return;
fill = value;
owner?.Invalidate();
}
}
///
/// 卡片悬停颜色
///
[Description("卡片悬停颜色"), Category("卡片"), DefaultValue(null)]
[Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
public Color? FillHover { get; set; }
///
/// 卡片激活颜色
///
[Description("卡片激活颜色"), Category("卡片"), DefaultValue(null)]
[Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
public Color? FillActive { get; set; }
int gap = 2;
///
/// 卡片间距
///
[Description("卡片间距"), Category("卡片"), DefaultValue(2)]
public int Gap
{
get => gap;
set
{
if (gap == value) return;
gap = value;
owner?.LoadLayout();
}
}
bool closable = false;
///
/// 可关闭
///
[Description("可关闭"), Category("卡片"), DefaultValue(false)]
public bool Closable
{
get => closable;
set
{
if (closable == value) return;
closable = value;
owner?.LoadLayout();
}
}
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 * Config.Dpi), gapI = gap / 2, xy = 0, xy2 = 0;
int cardgap = (int)(Gap * Config.Dpi);
var rect_list = new List(items.Count);
var rect_dir = GetDir(tabs, g, items, gap, out int ico_size, out int close_size, out xy2);
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 + gap + ico_size + close_size + gap * 2, xy2);
rect_list.Add(new TabPageRect(rect_it, it.Value, ico_size, close_size, gap, gapI));
}
else
{
rect_it = new Rectangle(rect.X + xy, y, it.Value.Width + gap + close_size + gap, xy2);
rect_list.Add(new TabPageRect(rect_it, false, it.Value, close_size, gap, gapI));
}
}
else
{
if (it.Key.HasIcon)
{
rect_it = new Rectangle(rect.X + xy, y, it.Value.Width + gap + ico_size + gap, xy2);
rect_list.Add(new TabPageRect(rect_it, it.Value, ico_size, gap, gapI));
}
else
{
rect_it = new Rectangle(rect.X + xy, y, it.Value.Width + gap, xy2);
rect_list.Add(new TabPageRect(rect_it));
}
}
it.Key.SetRect(rect_it);
xy += rect_it.Width + cardgap;
}
else rect_list.Add(new TabPageRect());
}
xy -= cardgap;
tabs.SetPadding(0, 0, 0, xy2);
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;
if (close)
{
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, ico_size, close_size, gap, gapI));
else rect_list.Add(new TabPageRect(rect_it, false, it.Value, close_size, gap, gapI));
}
else
{
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, ico_size, gap, gapI));
else rect_list.Add(new TabPageRect(rect_it));
}
it.Key.SetRect(rect_it);
xy += rect_it.Height + cardgap;
}
else rect_list.Add(new TabPageRect());
}
xy -= cardgap;
tabs.SetPadding(xy2, 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;
if (close)
{
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, ico_size, close_size, gap, gapI));
else rect_list.Add(new TabPageRect(rect_it, false, it.Value, close_size, gap, gapI));
}
else
{
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, ico_size, gap, gapI));
else rect_list.Add(new TabPageRect(rect_it));
}
it.Key.SetRect(rect_it);
xy += rect_it.Height + cardgap;
}
else rect_list.Add(new TabPageRect());
}
xy -= cardgap;
tabs.SetPadding(0, 0, xy2, 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 + gap + ico_size + close_size + gap * 2, xy2);
rect_list.Add(new TabPageRect(rect_it, it.Value, ico_size, close_size, gap, gapI));
}
else
{
rect_it = new Rectangle(rect.X + xy, rect.Y, it.Value.Width + gap + close_size + gap, xy2);
rect_list.Add(new TabPageRect(rect_it, false, it.Value, close_size, gap, gapI));
}
}
else
{
if (it.Key.HasIcon)
{
rect_it = new Rectangle(rect.X + xy, rect.Y, it.Value.Width + gap + ico_size + gap, xy2);
rect_list.Add(new TabPageRect(rect_it, it.Value, ico_size, gap, gapI));
}
else
{
rect_it = new Rectangle(rect.X + xy, rect.Y, it.Value.Width + gap, xy2);
rect_list.Add(new TabPageRect(rect_it));
}
}
it.Key.SetRect(rect_it);
xy += rect_it.Width + cardgap;
}
else rect_list.Add(new TabPageRect());
}
xy -= cardgap;
tabs.SetPadding(0, xy2, 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();
});
}
public void Paint(Tabs owner, Graphics g, TabCollection items)
{
if (rects.Length == items.Count)
{
using (var brush_fore = new SolidBrush(owner.ForeColor ?? AntdUI.Style.Db.Text))
using (var brush_fill = new SolidBrush(owner.Fill ?? AntdUI.Style.Db.Primary))
using (var brush_active = new SolidBrush(owner.FillActive ?? AntdUI.Style.Db.PrimaryActive))
using (var brush_hover = new SolidBrush(owner.FillHover ?? AntdUI.Style.Db.PrimaryHover))
using (var brush_bg = new SolidBrush(Fill ?? AntdUI.Style.Db.FillQuaternary))
using (var brush_bg_hover = new SolidBrush(FillHover ?? AntdUI.Style.Db.FillQuaternary))
using (var brush_bg_active = new SolidBrush(FillActive ?? AntdUI.Style.Db.BgContainer))
{
var rect_t = owner.ClientRectangle;
int radius = (int)(Radius * Config.Dpi), bor = (int)(bordersize * Config.Dpi), bor2 = bor * 6, bor22 = bor2 * 2;
float borb2 = bor / 2F;
TabPage? sel = null;
int i = 0, select = owner.SelectedIndex;
switch (owner.Alignment)
{
case TabAlignment.Bottom:
int read_b_h = rects[0].Rect.Height + rects[0].Rect.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, rects[0].Rect.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
{
using (var path = Helper.RoundPath(page.Rect, radius, false, false, true, true))
{
g.FillPath(owner.hover_i == i ? brush_bg_hover : brush_bg, path);
if (bor > 0)
{
using (var pen_bg = new Pen(border ?? AntdUI.Style.Db.BorderSecondary, bor))
{
g.DrawPath(pen_bg, path);
}
}
if (owner.hover_i == i) PaintText(g, rects[i], owner, page, page.MDown ? brush_active : brush_hover);
else PaintText(g, rects[i], owner, page, brush_fore);
}
}
}
i++;
}
g.ResetClip();
g.ResetTransform();
if (sel != null)//是否选中
{
var rect_page = sel.Rect;
using (var path = Helper.RoundPath(rect_page, radius, false, false, true, true))
{
if (bor > 0)
{
using (var pen_bg = new Pen(BorderActive ?? AntdUI.Style.Db.BorderColor, bor))
{
float ly = rect_page.Y + borb2;
g.DrawLine(pen_bg, rect_t.X, ly, rect_t.Right, ly);
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, false, false, true, true))
{
g.FillPath(brush_bg_active, path2);
}
g.SetClip(new Rectangle(rect_page.X - bor, rect_page.Y + bor, rect_page.Width + bor2, rect_page.Height + bor));
g.DrawPath(pen_bg, path);
g.ResetClip();
}
}
else
{
if (owner.scroll_show) g.TranslateTransform(-owner.scroll_x, -owner.scroll_y);
g.FillPath(brush_bg_active, path);
}
PaintText(g, rects[select], owner, sel, brush_fill);
}
}
break;
case TabAlignment.Left:
var rect_s_l = new Rectangle(rect_t.X, rect_t.Y, rects[0].Rect.Right, rect_t.Height);
if (owner.scroll_show)
{
g.SetClip(owner.PaintExceedPre(rect_s_l, rects[0].Rect.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
{
using (var path = Helper.RoundPath(page.Rect, radius, true, false, false, true))
{
g.FillPath(owner.hover_i == i ? brush_bg_hover : brush_bg, path);
if (bor > 0)
{
using (var pen_bg = new Pen(border ?? AntdUI.Style.Db.BorderSecondary, bor))
{
g.DrawPath(pen_bg, path);
}
}
if (owner.hover_i == i) PaintText(g, rects[i], owner, page, page.MDown ? brush_active : brush_hover);
else PaintText(g, rects[i], owner, page, brush_fore);
}
}
}
i++;
}
g.ResetClip();
g.ResetTransform();
if (sel != null)//是否选中
{
var rect_page = sel.Rect;
using (var path = Helper.RoundPath(rect_page, radius, true, false, false, true))
{
if (bor > 0)
{
using (var pen_bg = new Pen(BorderActive ?? AntdUI.Style.Db.BorderColor, bor))
{
float lx = rect_page.Right - borb2;
g.DrawLine(pen_bg, lx, rect_t.Y, lx, rect_t.Bottom);
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, false, false, true))
{
g.FillPath(brush_bg_active, path2);
}
g.SetClip(new RectangleF(rect_page.X - borb2, rect_page.Y - bor, rect_page.Width, rect_page.Height + bor2));
g.DrawPath(pen_bg, path);
g.ResetClip();
}
}
else
{
if (owner.scroll_show) g.TranslateTransform(-owner.scroll_x, -owner.scroll_y);
g.FillPath(brush_bg_active, path);
}
PaintText(g, rects[select], owner, sel, brush_fill);
}
}
break;
case TabAlignment.Right:
int read_r_w = rects[0].Rect.Width + rects[0].Rect.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, rects[0].Rect.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
{
using (var path = Helper.RoundPath(page.Rect, radius, false, true, true, false))
{
g.FillPath(owner.hover_i == i ? brush_bg_hover : brush_bg, path);
if (bor > 0)
{
using (var pen_bg = new Pen(border ?? AntdUI.Style.Db.BorderSecondary, bor))
{
g.DrawPath(pen_bg, path);
}
}
if (owner.hover_i == i) PaintText(g, rects[i], owner, page, page.MDown ? brush_active : brush_hover);
else PaintText(g, rects[i], owner, page, brush_fore);
}
}
}
i++;
}
g.ResetClip();
g.ResetTransform();
if (sel != null)//是否选中
{
var rect_page = sel.Rect;
using (var path = Helper.RoundPath(rect_page, radius, false, true, true, false))
{
if (bor > 0)
{
using (var pen_bg = new Pen(BorderActive ?? AntdUI.Style.Db.BorderColor, bor))
{
float lx = rect_page.X + borb2;
g.DrawLine(pen_bg, lx, rect_t.Y, lx, rect_t.Bottom);
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, false, true, true, false))
{
g.FillPath(brush_bg_active, path2);
}
g.SetClip(new Rectangle(rect_page.X + bor, rect_page.Y - bor, rect_page.Width + bor, rect_page.Height + bor2));
g.DrawPath(pen_bg, path);
g.ResetClip();
}
}
else
{
if (owner.scroll_show) g.TranslateTransform(-owner.scroll_x, -owner.scroll_y);
g.FillPath(brush_bg_active, path);
}
PaintText(g, rects[select], owner, sel, brush_fill);
}
}
break;
case TabAlignment.Top:
default:
var rect_s_t = new Rectangle(rect_t.X, rect_t.Y, rect_t.Width, rects[0].Rect.Bottom);
if (owner.scroll_show)
{
g.SetClip(owner.PaintExceedPre(rect_s_t, rects[0].Rect.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
{
using (var path = Helper.RoundPath(page.Rect, radius, true, true, false, false))
{
g.FillPath(owner.hover_i == i ? brush_bg_hover : brush_bg, path);
if (bor > 0)
{
using (var pen_bg = new Pen(border ?? AntdUI.Style.Db.BorderSecondary, bor))
{
g.DrawPath(pen_bg, path);
}
}
if (owner.hover_i == i) PaintText(g, rects[i], owner, page, page.MDown ? brush_active : brush_hover);
else PaintText(g, rects[i], owner, page, brush_fore);
}
}
}
i++;
}
g.ResetClip();
g.ResetTransform();
if (sel != null)//是否选中
{
var rect_page = sel.Rect;
using (var path = Helper.RoundPath(rect_page, radius, true, true, false, false))
{
if (bor > 0)
{
using (var pen_bg = new Pen(BorderActive ?? AntdUI.Style.Db.BorderColor, bor))
{
float ly = rect_page.Bottom - borb2;
g.DrawLine(pen_bg, rect_t.X, ly, rect_t.Right, ly);
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, false, false))
{
g.FillPath(brush_bg_active, path2);
}
g.SetClip(new Rectangle(rect_page.X - bor, rect_page.Y - bor, rect_page.Width + bor2, rect_page.Height));
g.DrawPath(pen_bg, path);
g.ResetClip();
}
}
else
{
if (owner.scroll_show) g.TranslateTransform(-owner.scroll_x, -owner.scroll_y);
g.FillPath(brush_bg_active, path);
}
PaintText(g, rects[select], owner, sel, brush_fill);
}
}
break;
}
if (owner.scroll_show) owner.PaintExceed(g, brush_fore.Color, radius, rect_t, rects[0].Rect, rects[rects.Length - 1].Rect, true);
}
}
}
#region 辅助
Dictionary GetDir(Tabs owner, Graphics g, TabCollection items, int gap, out int ico_size, out int close_size, out int sizewh)
{
sizewh = 0;
var size_t = g.MeasureString(Config.NullText, owner.Font).Size();
var rect_dir = new Dictionary(items.Count);
foreach (var item in items)
{
var size = g.MeasureString(item.Text, owner.Font).Size();
rect_dir.Add(item, size);
}
ico_size = (int)(size_t.Height * owner.IconRatio);
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 + ico_size + gap * 2;
else w = it.Value.Width + gap;
w += ico_size + 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 + ico_size + gap * 2;
else w = it.Value.Width + gap;
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);
}
void PaintText(Graphics g, TabPageRect rects, Tabs owner, TabPage page, SolidBrush brush)
{
if (page.HasIcon)
{
if (page.Icon != null) g.DrawImage(page.Icon, rects.Rect_Ico);
else 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, AntdUI.Style.Db.TextQuaternary);
else if (rects.hover_close.Animation) g.PaintIconClose(rects.Rect_Close, Helper.ToColor(rects.hover_close.Value + AntdUI.Style.Db.TextQuaternary.A, AntdUI.Style.Db.Text));
else if (rects.hover_close.Switch) g.PaintIconClose(rects.Rect_Close, AntdUI.Style.Db.Text);
else g.PaintIconClose(rects.Rect_Close, AntdUI.Style.Db.TextQuaternary);
}
g.DrawStr(page.Text, owner.Font, brush, rects.Rect_Text, owner.s_c);
owner.PaintBadge(g, page, rects.Rect_Text);
}
internal class TabPageRect
{
public TabPageRect() { }
public TabPageRect(Rectangle rect)
{
Rect = Rect_Text = rect;
}
public TabPageRect(Rectangle rect_it, Size size, int ico_size, int gap, int gapI)
{
Rect = rect_it;
Rect_Text = new Rectangle(rect_it.X + ico_size + gap, rect_it.Y + gapI, size.Width + gap, rect_it.Height - gap);
Rect_Ico = new Rectangle(rect_it.X + gap, rect_it.Y + (rect_it.Height - ico_size) / 2, ico_size, ico_size);
}
public TabPageRect(Rectangle rect_it, Size size, int ico_size, int close_size, int gap, int gapI)
{
Rect = rect_it;
Rect_Text = new Rectangle(rect_it.X + ico_size + gap, rect_it.Y + gapI, size.Width + gap, rect_it.Height - gap);
Rect_Ico = new Rectangle(rect_it.X + gap, rect_it.Y + (rect_it.Height - ico_size) / 2, ico_size, ico_size);
Rect_Close = new Rectangle(rect_it.Right - gap - close_size, rect_it.Y + (rect_it.Height - close_size) / 2, close_size, close_size);
}
public TabPageRect(Rectangle rect_it, bool test, Size size, int close_size, int gap, int gapI)
{
Rect = rect_it;
int y = rect_it.Y + (rect_it.Height - close_size) / 2;
Rect_Text = new Rectangle(rect_it.X, rect_it.Y + gapI, size.Width + gap, rect_it.Height - gap);
Rect_Close = new Rectangle(rect_it.Right - gap - close_size, y, close_size, close_size);
}
public Rectangle Rect;
public Rectangle Rect_Text;
public Rectangle Rect_Ico;
public Rectangle Rect_Close;
public ITaskOpacity? hover_close;
}
#endregion
public void SelectedIndexChanged(int i, int old)
{
owner?.Invalidate();
}
public void Dispose()
{
foreach (var item in rects)
{
item.hover_close?.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)
{
if (item.hover_close == null) item.hover_close = new ITaskOpacity(owner);
if (i == owner.hover_i)
{
item.hover_close.MaxValue = AntdUI.Style.Db.Text.A - AntdUI.Style.Db.TextQuaternary.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;
}
}
}
public void MouseWheel(int delta)
{
if (owner != null && owner.scroll_show)
{
switch (owner.Alignment)
{
case TabAlignment.Left:
case TabAlignment.Right:
owner.scroll_x = 0;
owner.scroll_y -= delta;
break;
case TabAlignment.Top:
case TabAlignment.Bottom:
default:
owner.scroll_y = 0;
owner.scroll_x -= delta;
break;
}
}
}
}
///
/// 卡片样式2
///
public class StyleCard2 : IStyle
{
Tabs? owner;
public StyleCard2() { }
public StyleCard2(Tabs tabs) { owner = tabs; }
int radius = 6;
///
/// 卡片圆角
///
[Description("卡片圆角"), Category("卡片"), DefaultValue(6)]
public int Radius
{
get => radius;
set
{
if (radius == value) return;
radius = value;
owner?.Invalidate();
}
}
int bordersize = 1;
///
/// 边框大小
///
[Description("边框大小"), Category("卡片"), DefaultValue(1)]
public int Border
{
get => bordersize;
set
{
if (bordersize == value) return;
bordersize = value;
owner?.LoadLayout();
}
}
Color? border;
///
/// 卡片边框颜色
///
[Description("卡片边框颜色"), Category("卡片"), DefaultValue(null)]
[Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
public Color? BorderColor
{
get => border;
set
{
if (border == value) return;
border = value;
owner?.Invalidate();
}
}
///
/// 卡片边框激活颜色
///
[Description("卡片边框激活颜色"), Category("卡片"), DefaultValue(null)]
[Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
public Color? BorderActive { get; set; }
Color? fill;
///
/// 卡片颜色
///
[Description("卡片颜色"), Category("卡片"), DefaultValue(null)]
[Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
public Color? Fill
{
get => fill;
set
{
if (fill == value) return;
fill = value;
owner?.Invalidate();
}
}
///
/// 卡片悬停颜色
///
[Description("卡片悬停颜色"), Category("卡片"), DefaultValue(null)]
[Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
public Color? FillHover { get; set; }
///
/// 卡片激活颜色
///
[Description("卡片激活颜色"), Category("卡片"), DefaultValue(null)]
[Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
public Color? FillActive { get; set; }
int gap = 2;
///
/// 卡片间距
///
[Description("卡片间距"), Category("卡片"), DefaultValue(2)]
public int Gap
{
get => gap;
set
{
if (gap == value) return;
gap = value;
owner?.LoadLayout();
}
}
public enum CloseType { none, always, activate }
CloseType closable = CloseType.none;
///
/// 可关闭
///
[Description("可关闭"), Category("卡片"), DefaultValue(false)]
public CloseType Closable
{
get => closable;
set
{
if (closable == value) return;
closable = value;
owner?.LoadLayout();
}
}
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 * Config.Dpi), gapI = gap / 2, xy = 0, xy2 = 0;
int cardgap = (int)(Gap * Config.Dpi);
var rect_list = new List(items.Count);
var rect_dir = GetDir(tabs, g, items, gap, out int ico_size, out int close_size, out xy2);
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;
if (it.Key.HasIcon)
{
rect_it = new Rectangle(rect.X + xy, y + 2 + bordersize, it.Value.Width + gap + ico_size + close_size + gap * 2, xy2 - 2 - bordersize);
rect_list.Add(new TabPageRect(rect_it, it.Value, ico_size, close_size, gap, gapI));
}
else
{
rect_it = new Rectangle(rect.X + xy, y + 2 + bordersize, it.Value.Width + gap + close_size + gap, xy2 - 2 - bordersize);
rect_list.Add(new TabPageRect(rect_it, false, it.Value, close_size, gap, gapI));
}
it.Key.SetRect(rect_it);
xy += rect_it.Width + cardgap;
}
else rect_list.Add(new TabPageRect());
}
xy -= cardgap;
tabs.SetPadding(0, 0, 0, xy2);
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 - 2 - bordersize, it.Value.Height + gap);
if (it.Key.HasIcon)
rect_list.Add(new TabPageRect(rect_it, it.Value, ico_size, close_size, gap, gapI));
else
rect_list.Add(new TabPageRect(rect_it, false, it.Value, close_size, gap, gapI));
it.Key.SetRect(rect_it);
xy += rect_it.Height + cardgap;
}
else rect_list.Add(new TabPageRect());
}
xy -= cardgap;
tabs.SetPadding(xy2, 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 + 2 + bordersize, rect.Y + xy, xy2 - 2 - bordersize, it.Value.Height + gap);
if (it.Key.HasIcon)
rect_list.Add(new TabPageRect(rect_it, it.Value, ico_size, close_size, gap, gapI));
else
rect_list.Add(new TabPageRect(rect_it, false, it.Value, close_size, gap, gapI));
it.Key.SetRect(rect_it);
xy += rect_it.Height + cardgap;
}
else rect_list.Add(new TabPageRect());
}
xy -= cardgap;
tabs.SetPadding(0, 0, xy2, 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 + gap + ico_size + close_size + gap * 2, xy2 - 2 - bordersize);
rect_list.Add(new TabPageRect(rect_it, it.Value, ico_size, close_size, gap, gapI));
}
else
{
rect_it = new Rectangle(rect.X + xy, rect.Y, it.Value.Width + gap + close_size + gap, xy2 - 2 - bordersize);
rect_list.Add(new TabPageRect(rect_it, false, it.Value, close_size, gap, gapI));
}
it.Key.SetRect(rect_it);
xy += rect_it.Width + cardgap;
}
else rect_list.Add(new TabPageRect());
}
xy -= cardgap;
tabs.SetPadding(0, xy2, 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 + 2 + bordersize, it.Value.Width + gap + ico_size + gap, xy2 - 2 - bordersize);
rect_list.Add(new TabPageRect(rect_it, it.Value, ico_size, gap, gapI));
}
else
{
rect_it = new Rectangle(rect.X + xy, y + 2 + bordersize, it.Value.Width + gap, xy2 - 2 - bordersize);
rect_list.Add(new TabPageRect(rect_it));
}
it.Key.SetRect(rect_it);
xy += rect_it.Width + cardgap;
}
else rect_list.Add(new TabPageRect());
}
xy -= cardgap;
tabs.SetPadding(0, 0, 0, xy2);
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 - 2 - bordersize, it.Value.Height + gap);
if (it.Key.HasIcon) rect_list.Add(new TabPageRect(rect_it, it.Value, ico_size, gap, gapI));
else rect_list.Add(new TabPageRect(rect_it));
it.Key.SetRect(rect_it);
xy += rect_it.Height + cardgap;
}
else rect_list.Add(new TabPageRect());
}
xy -= cardgap;
tabs.SetPadding(xy2, 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 + 2 + bordersize, rect.Y + xy, xy2 - 2 - bordersize, it.Value.Height + gap);
if (it.Key.HasIcon)
rect_list.Add(new TabPageRect(rect_it, it.Value, ico_size, gap, gapI));
else
rect_list.Add(new TabPageRect(rect_it));
it.Key.SetRect(rect_it);
xy += rect_it.Height + cardgap;
}
else rect_list.Add(new TabPageRect());
}
xy -= cardgap;
tabs.SetPadding(0, 0, xy2, 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 + gap + ico_size + gap, xy2 - 2 - bordersize);
rect_list.Add(new TabPageRect(rect_it, it.Value, ico_size, gap, gapI));
}
else
{
rect_it = new Rectangle(rect.X + xy, rect.Y, it.Value.Width + gap, xy2 - 2 - bordersize);
rect_list.Add(new TabPageRect(rect_it));
}
it.Key.SetRect(rect_it);
xy += rect_it.Width + cardgap;
}
else rect_list.Add(new TabPageRect());
}
xy -= cardgap;
tabs.SetPadding(0, xy2, 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();
});
}
public void Paint(Tabs owner, Graphics g, TabCollection items)
{
if (rects.Length == items.Count)
{
using (var brush_fore = new SolidBrush(owner.ForeColor ?? AntdUI.Style.Db.Text))
using (var brush_fill = new SolidBrush(owner.Fill ?? AntdUI.Style.Db.Primary))
using (var brush_active = new SolidBrush(owner.FillActive ?? AntdUI.Style.Db.PrimaryActive))
using (var brush_hover = new SolidBrush(owner.FillHover ?? AntdUI.Style.Db.PrimaryHover))
using (var brush_bg = new SolidBrush(Fill ?? AntdUI.Style.Db.FillQuaternary))
using (var brush_bg_hover = new SolidBrush(FillHover ?? AntdUI.Style.Db.FillQuaternary))
using (var brush_bg_active = new SolidBrush(FillActive ?? AntdUI.Style.Db.BgContainer))
{
var rect_t = owner.ClientRectangle;
int radius = (int)(Radius * Config.Dpi), bor = (int)(bordersize * Config.Dpi), bor2 = bor * 6, bor22 = bor2 * 2;
float borb2 = bor / 2F;
TabPage? sel = null;
int i = 0, select = owner.SelectedIndex;
switch (owner.Alignment)
{
case TabAlignment.Bottom:
int read_b_h = rects[0].Rect.Height + rects[0].Rect.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, rects[0].Rect.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
{
using (var path = Helper.RoundPath(page.Rect, radius, true, true, true, true))
{
g.FillPath(owner.hover_i == i ? brush_bg_hover : brush_bg, path);
if (bor > 0)
{
using (var pen_bg = new Pen(border ?? AntdUI.Style.Db.BorderSecondary, bor))
{
g.DrawPath(pen_bg, path);
}
}
if (owner.hover_i == i) PaintText(g, rects[i], owner, page, page.MDown ? brush_active : brush_hover, true);
else PaintText(g, rects[i], owner, page, brush_fore, closable == CloseType.always ? true : false);
}
}
}
i++;
}
g.ResetTransform();
if (sel != null)//是否选中
{
var rect_page = sel.Rect;
using (var path = Helper.RoundPath(rect_page, radius, true, true, true, true))
{
if (bor > 0)
{
using (var pen_bg = new Pen(BorderActive ?? AntdUI.Style.Db.BorderColor, bor))
{
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.FillPath(brush_bg_active, path2);
}
g.DrawPath(pen_bg, path);
}
}
else
{
if (owner.scroll_show) g.TranslateTransform(-owner.scroll_x, -owner.scroll_y);
g.FillPath(brush_bg_active, path);
}
PaintText(g, rects[select], owner, sel, brush_fill, true);
}
}
break;
case TabAlignment.Left:
var rect_s_l = new Rectangle(rect_t.X, rect_t.Y, rects[0].Rect.Right, rect_t.Height);
if (owner.scroll_show)
{
g.SetClip(owner.PaintExceedPre(rect_s_l, rects[0].Rect.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
{
using (var path = Helper.RoundPath(page.Rect, radius, true, true, true, true))
{
g.FillPath(owner.hover_i == i ? brush_bg_hover : brush_bg, path);
if (bor > 0)
{
using (var pen_bg = new Pen(border ?? AntdUI.Style.Db.BorderSecondary, bor))
{
g.DrawPath(pen_bg, path);
}
}
if (owner.hover_i == i) PaintText(g, rects[i], owner, page, page.MDown ? brush_active : brush_hover, true);
else PaintText(g, rects[i], owner, page, brush_fore, closable == CloseType.always ? true : false);
}
}
}
i++;
}
g.ResetTransform();
if (sel != null)//是否选中
{
var rect_page = sel.Rect;
using (var path = Helper.RoundPath(rect_page, radius, true, true, true, true))
{
if (bor > 0)
{
using (var pen_bg = new Pen(BorderActive ?? AntdUI.Style.Db.BorderColor, bor))
{
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.FillPath(brush_bg_active, path2);
}
g.DrawPath(pen_bg, path);
}
}
else
{
if (owner.scroll_show) g.TranslateTransform(-owner.scroll_x, -owner.scroll_y);
g.FillPath(brush_bg_active, path);
}
PaintText(g, rects[select], owner, sel, brush_fill, true);
}
}
break;
case TabAlignment.Right:
int read_r_w = rects[0].Rect.Width + rects[0].Rect.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, rects[0].Rect.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
{
using (var path = Helper.RoundPath(page.Rect, radius, true, true, true, true))
{
g.FillPath(owner.hover_i == i ? brush_bg_hover : brush_bg, path);
if (bor > 0)
{
using (var pen_bg = new Pen(border ?? AntdUI.Style.Db.BorderSecondary, bor))
{
g.DrawPath(pen_bg, path);
}
}
if (owner.hover_i == i) PaintText(g, rects[i], owner, page, page.MDown ? brush_active : brush_hover, true);
else PaintText(g, rects[i], owner, page, brush_fore, closable == CloseType.always ? true : false);
}
}
}
i++;
}
g.ResetTransform();
if (sel != null)//是否选中
{
var rect_page = sel.Rect;
using (var path = Helper.RoundPath(rect_page, radius, true, true, true, true))
{
if (bor > 0)
{
using (var pen_bg = new Pen(BorderActive ?? AntdUI.Style.Db.BorderColor, bor))
{
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.FillPath(brush_bg_active, path2);
}
g.DrawPath(pen_bg, path);
}
}
else
{
if (owner.scroll_show) g.TranslateTransform(-owner.scroll_x, -owner.scroll_y);
g.FillPath(brush_bg_active, path);
}
PaintText(g, rects[select], owner, sel, brush_fill, true);
}
}
break;
case TabAlignment.Top:
default:
var rect_s_t = new Rectangle(rect_t.X, rect_t.Y, rect_t.Width, rects[0].Rect.Bottom);
if (owner.scroll_show)
{
g.SetClip(owner.PaintExceedPre(rect_s_t, rects[0].Rect.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
{
using (var path = Helper.RoundPath(page.Rect, radius, true, true, true, true))
{
g.FillPath(owner.hover_i == i ? brush_bg_hover : brush_bg, path);
if (bor > 0)
{
using (var pen_bg = new Pen(border ?? AntdUI.Style.Db.BorderSecondary, bor))
{
g.DrawPath(pen_bg, path);
}
}
if (owner.hover_i == i) PaintText(g, rects[i], owner, page, page.MDown ? brush_active : brush_hover, true);
else PaintText(g, rects[i], owner, page, brush_fore, closable == CloseType.always ? true : false);
}
}
}
i++;
}
g.ResetTransform();
if (sel != null)//是否选中
{
var rect_page = sel.Rect;
using (var path = Helper.RoundPath(rect_page, radius, true, true, true, true))
{
if (bor > 0)
{
using (var pen_bg = new Pen(BorderActive ?? AntdUI.Style.Db.BorderColor, bor))
{
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.FillPath(brush_bg_active, path2);
}
g.DrawPath(pen_bg, path);
}
}
else
{
if (owner.scroll_show) g.TranslateTransform(-owner.scroll_x, -owner.scroll_y);
g.FillPath(brush_bg_active, path);
}
PaintText(g, rects[select], owner, sel, brush_fill, true);
}
}
break;
}
if (owner.scroll_show) owner.PaintExceed(g, brush_fore.Color, radius, rect_t, rects[0].Rect, rects[rects.Length - 1].Rect, true);
}
}
}
#region 辅助
Dictionary GetDir(Tabs owner, Graphics g, TabCollection items, int gap, out int ico_size, out int close_size, out int sizewh)
{
sizewh = 0;
var size_t = g.MeasureString(Config.NullText, owner.Font).Size();
var rect_dir = new Dictionary(items.Count);
foreach (var item in items)
{
var size = g.MeasureString(item.Text, owner.Font).Size();
rect_dir.Add(item, size);
}
ico_size = (int)(size_t.Height * owner.IconRatio);
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 + ico_size + gap * 2;
else w = it.Value.Width + gap;
w += ico_size + 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 + ico_size + gap * 2;
else w = it.Value.Width + gap;
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);
}
void PaintText(Graphics g, TabPageRect rects, Tabs owner, TabPage page, SolidBrush brush, bool closshow = false)
{
if (page.HasIcon)
{
if (page.Icon != null) g.DrawImage(page.Icon, rects.Rect_Ico);
else 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, AntdUI.Style.Db.TextQuaternary);
else if (rects.hover_close.Animation)
g.PaintIconClose(rects.Rect_Close, Helper.ToColor(rects.hover_close.Value + AntdUI.Style.Db.TextQuaternary.A, AntdUI.Style.Db.Text));
else if (rects.hover_close.Switch)
g.PaintIconClose(rects.Rect_Close, AntdUI.Style.Db.Text);
else
g.PaintIconClose(rects.Rect_Close, AntdUI.Style.Db.TextQuaternary);
}
g.DrawStr(page.Text, owner.Font, brush, rects.Rect_Text, owner.s_c);
owner.PaintBadge(g, page, rects.Rect_Text);
}
internal class TabPageRect
{
public TabPageRect() { }
public TabPageRect(Rectangle rect)
{
Rect = Rect_Text = rect;
}
public TabPageRect(Rectangle rect_it, Size size, int ico_size, int gap, int gapI)
{
Rect = rect_it;
Rect_Text = new Rectangle(rect_it.X + ico_size + gap, rect_it.Y + gapI, size.Width + gap, rect_it.Height - gap);
Rect_Ico = new Rectangle(rect_it.X + gap, rect_it.Y + (rect_it.Height - ico_size) / 2, ico_size, ico_size);
}
public TabPageRect(Rectangle rect_it, Size size, int ico_size, int close_size, int gap, int gapI)
{
Rect = rect_it;
Rect_Text = new Rectangle(rect_it.X + ico_size + gap, rect_it.Y + gapI, size.Width + gap, rect_it.Height - gap);
Rect_Ico = new Rectangle(rect_it.X + gap, rect_it.Y + (rect_it.Height - ico_size) / 2, ico_size, ico_size);
Rect_Close = new Rectangle(rect_it.Right - gap - close_size, rect_it.Y + (rect_it.Height - close_size) / 2, close_size, close_size);
}
public TabPageRect(Rectangle rect_it, bool test, Size size, int close_size, int gap, int gapI)
{
Rect = rect_it;
int y = rect_it.Y + (rect_it.Height - close_size) / 2;
Rect_Text = new Rectangle(rect_it.X, rect_it.Y + gapI, size.Width + gap, rect_it.Height - gap);
Rect_Close = new Rectangle(rect_it.Right - gap - close_size, y, close_size, close_size);
}
public Rectangle Rect;
public Rectangle Rect_Text;
public Rectangle Rect_Ico;
public Rectangle Rect_Close;
public ITaskOpacity? hover_close;
}
#endregion
public void SelectedIndexChanged(int i, int old)
{
owner?.Invalidate();
}
public void Dispose()
{
foreach (var item in rects)
{
item.hover_close?.Dispose();
}
}
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)
{
if (item.hover_close == null) item.hover_close = new ITaskOpacity(owner);
if (i == owner.hover_i)
{
item.hover_close.MaxValue = AntdUI.Style.Db.Text.A - AntdUI.Style.Db.TextQuaternary.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;
}
}
}
public void MouseWheel(int delta)
{
if (owner != null && owner.scroll_show)
{
switch (owner.Alignment)
{
case TabAlignment.Left:
case TabAlignment.Right:
owner.scroll_x = 0;
owner.scroll_y -= delta;
break;
case TabAlignment.Top:
case TabAlignment.Bottom:
default:
owner.scroll_y = 0;
owner.scroll_x -= delta;
break;
}
}
}
}
[TypeConverter(typeof(ExpandableObjectConverter))]
public interface IStyle
{
void LoadLayout(Tabs owner, Rectangle rect, TabCollection items);
void Paint(Tabs owner, Graphics 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 MouseWheel(int delta);
void Dispose();
}
}
}