// 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.Drawing.Drawing2D;
using System.Windows.Forms;
namespace AntdUI
{
///
/// Button 按钮
///
/// 按钮用于开始一个即时操作。
[Description("Button 按钮")]
[ToolboxItem(true)]
[DefaultProperty("Text")]
public class Button : IControl, IButtonControl
{
IButton button;
public Button()
{
SetStyle(ControlStyles.StandardClick | ControlStyles.StandardDoubleClick, false);
base.BackColor = Color.Transparent;
button = new IButton(this, () => BeforeAutoSize());
}
#region 属性
///
/// 文字颜色
///
[Description("文字颜色"), Category("外观"), DefaultValue(null)]
[Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
public new Color? ForeColor
{
get => button.Fore;
set => button.Fore = value;
}
#region 背景
///
/// 背景颜色
///
[Description("背景颜色"), Category("外观"), DefaultValue(null)]
[Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
public new Color? BackColor
{
get => button.Back;
set => button.Back = value;
}
///
/// 背景渐变色
///
[Description("背景渐变色"), Category("外观"), DefaultValue(null)]
public string? BackExtend
{
get => button.BackExtend;
set => button.BackExtend = value;
}
///
/// 悬停背景颜色
///
[Description("悬停背景颜色"), Category("外观"), DefaultValue(null)]
[Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
public Color? BackHover
{
get => button.BackHover;
set => button.BackHover = value;
}
///
/// 激活背景颜色
///
[Description("激活背景颜色"), Category("外观"), DefaultValue(null)]
[Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
public Color? BackActive
{
get => button.BackActive;
set => button.BackActive = value;
}
///
/// 背景图片
///
[Description("背景图片"), Category("外观"), DefaultValue(null)]
public new Image? BackgroundImage
{
get => button.BackgroundImage;
set => button.BackgroundImage = value;
}
///
/// 背景图片布局
///
[Description("背景图片布局"), Category("外观"), DefaultValue(TFit.Fill)]
public new TFit BackgroundImageLayout
{
get => button.BackgroundImageLayout;
set => button.BackgroundImageLayout = value;
}
#endregion
#region 默认样式
///
/// Default模式背景颜色
///
[Description("Default模式背景颜色"), Category("外观"), DefaultValue(null)]
[Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
public Color? DefaultBack
{
get => button.DefaultBack;
set => button.DefaultBack = value;
}
///
/// Default模式边框颜色
///
[Description("Default模式边框颜色"), Category("外观"), DefaultValue(null)]
[Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
public Color? DefaultBorderColor
{
get => button.DefaultBorderColor;
set => button.DefaultBorderColor = value;
}
#endregion
#region 边框
///
/// 边框宽度
///
[Description("边框宽度"), Category("边框"), DefaultValue(0F)]
public float BorderWidth
{
get => button.BorderWidth;
set => button.BorderWidth = value;
}
#endregion
///
/// 波浪大小
///
[Description("波浪大小"), Category("外观"), DefaultValue(4)]
public int WaveSize
{
get => button.WaveSize;
set => button.WaveSize = value;
}
///
/// 圆角
///
[Description("圆角"), Category("外观"), DefaultValue(6)]
public int Radius
{
get => button.Radius;
set => button.Radius = value;
}
///
/// 形状
///
[Description("形状"), Category("外观"), DefaultValue(TShape.Default)]
public TShape Shape
{
get => button.Shape;
set => button.Shape = value;
}
///
/// 类型
///
[Description("类型"), Category("外观"), DefaultValue(TTypeMini.Default)]
public TTypeMini Type
{
get => button.Type;
set => button.Type = value;
}
///
/// 幽灵属性,使按钮背景透明
///
[Description("幽灵属性,使按钮背景透明"), Category("外观"), DefaultValue(false)]
public bool Ghost
{
get => button.Ghost;
set => button.Ghost = value;
}
///
/// 响应真实区域
///
[Description("响应真实区域"), Category("行为"), DefaultValue(false)]
public bool RespondRealAreas { get; set; }
///
/// 显示箭头
///
[Description("显示箭头"), Category("行为"), DefaultValue(false)]
public bool ShowArrow
{
get => button.ShowArrow;
set => button.ShowArrow = value;
}
///
/// 箭头链接样式
///
[Description("箭头链接样式"), Category("行为"), DefaultValue(false)]
public bool IsLink
{
get => button.IsLink;
set => button.IsLink = value;
}
///
/// 箭头角度
///
[Browsable(false), Description("箭头角度"), Category("外观"), DefaultValue(-1F)]
public float ArrowProg
{
get => button.ArrowProg;
set => button.ArrowProg = value;
}
#region 文本
///
/// 文本
///
[Editor(typeof(System.ComponentModel.Design.MultilineStringEditor), typeof(UITypeEditor))]
[Description("文本"), Category("外观"), DefaultValue(null)]
public override string? Text
{
get => button.Text;
set
{
var old = button.Text;
button.Text = value;
if (old != value) OnTextChanged(EventArgs.Empty);
}
}
///
/// 文本位置
///
[Description("文本位置"), Category("外观"), DefaultValue(ContentAlignment.MiddleCenter)]
public ContentAlignment TextAlign
{
get => button.TextAlign;
set => button.TextAlign = value;
}
///
/// 文本超出自动处理
///
[Description("文本超出自动处理"), Category("行为"), DefaultValue(false)]
public bool AutoEllipsis
{
get => button.AutoEllipsis;
set => button.AutoEllipsis = value;
}
///
/// 是否多行
///
[Description("是否多行"), Category("行为"), DefaultValue(false)]
public bool TextMultiLine
{
get => button.TextMultiLine;
set => button.TextMultiLine = value;
}
#endregion
#region 图标
///
/// 图标比例
///
[Description("图标比例"), Category("外观"), DefaultValue(.7F)]
public float IconRatio
{
get => button.IconRatio;
set => button.IconRatio = value;
}
///
/// 图标
///
[Description("图标"), Category("外观"), DefaultValue(null)]
public Image? Icon
{
get => button.Icon;
set => button.Icon = value;
}
///
/// 图标SVG
///
[Description("图标SVG"), Category("外观"), DefaultValue(null)]
public string? IconSvg
{
get => button.IconSvg;
set => button.IconSvg = value;
}
///
/// 是否包含图标
///
public bool HasIcon => button.HasIcon;
///
/// 图标大小
///
[Description("图标大小"), Category("外观"), DefaultValue(typeof(Size), "0, 0")]
public Size IconSize
{
get => button.IconSize;
set => button.IconSize = value;
}
///
/// 悬停图标
///
[Description("悬停图标"), Category("外观"), DefaultValue(null)]
public Image? IconHover
{
get => button.IconHover;
set => button.IconHover = value;
}
///
/// 悬停图标SVG
///
[Description("悬停图标SVG"), Category("外观"), DefaultValue(null)]
public string? IconHoverSvg
{
get => button.IconHoverSvg;
set => button.IconHoverSvg = value;
}
///
/// 悬停图标动画时长
///
[Description("悬停图标动画时长"), Category("外观"), DefaultValue(200)]
public int IconHoverAnimation
{
get => button.IconHoverAnimation;
set => button.IconHoverAnimation = value;
}
///
/// 按钮图标组件的位置
///
[Description("按钮图标组件的位置"), Category("外观"), DefaultValue(TAlignMini.Left)]
public TAlignMini IconPosition
{
get => button.IconPosition;
set => button.IconPosition = value;
}
#endregion
#region 切换
///
/// 选中状态
///
[Description("选中状态"), Category("切换"), DefaultValue(false)]
public bool Toggle
{
get => button.Toggle;
set => button.Toggle = value;
}
///
/// 切换图标
///
[Description("切换图标"), Category("切换"), DefaultValue(null)]
public Image? ToggleIcon
{
get => button.ToggleIcon;
set => button.ToggleIcon = value;
}
///
/// 切换图标SVG
///
[Description("切换图标SVG"), Category("切换"), DefaultValue(null)]
public string? ToggleIconSvg
{
get => button.ToggleIconSvg;
set => button.ToggleIconSvg = value;
}
///
/// 是否包含切换图标
///
public bool HasToggleIcon => button.HasToggleIcon;
///
/// 切换悬停图标
///
[Description("切换悬停图标"), Category("切换"), DefaultValue(null)]
public Image? ToggleIconHover
{
get => button.ToggleIconHover;
set => button.ToggleIconHover = value;
}
///
/// 切换悬停图标SVG
///
[Description("切换悬停图标SVG"), Category("切换"), DefaultValue(null)]
public string? ToggleIconHoverSvg
{
get => button.ToggleIconHoverSvg;
set => button.ToggleIconHoverSvg = value;
}
///
/// 图标切换动画时长
///
[Description("图标切换动画时长"), Category("切换"), DefaultValue(200)]
public int IconToggleAnimation
{
get => button.IconToggleAnimation;
set => button.IconToggleAnimation = value;
}
///
/// 文字颜色
///
[Description("切换文字颜色"), Category("切换"), DefaultValue(null)]
[Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
public Color? ToggleFore
{
get => button.ToggleFore;
set => button.ToggleFore = value;
}
///
/// 切换类型
///
[Description("切换类型"), Category("切换"), DefaultValue(null)]
public TTypeMini? ToggleType
{
get => button.ToggleType;
set => button.ToggleType = value;
}
#region 背景
///
/// 切换背景颜色
///
[Description("切换背景颜色"), Category("切换"), DefaultValue(null)]
[Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
public Color? ToggleBack
{
get => button.ToggleBack;
set => button.ToggleBack = value;
}
///
/// 切换背景渐变色
///
[Description("切换背景渐变色"), Category("切换"), DefaultValue(null)]
public string? ToggleBackExtend
{
get => button.ToggleBackExtend;
set => button.ToggleBackExtend = value;
}
///
/// 切换悬停背景颜色
///
[Description("切换悬停背景颜色"), Category("切换"), DefaultValue(null)]
[Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
public Color? ToggleBackHover
{
get => button.ToggleBackHover;
set => button.ToggleBackHover = value;
}
///
/// 切换激活背景颜色
///
[Description("切换激活背景颜色"), Category("切换"), DefaultValue(null)]
[Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
public Color? ToggleBackActive
{
get => button.ToggleBackActive;
set => button.ToggleBackActive = value;
}
#endregion
#endregion
#region 加载动画
///
/// 加载状态
///
[Description("加载状态"), Category("外观"), DefaultValue(false)]
public bool Loading
{
get => button.Loading;
set => button.Loading = value;
}
///
/// 加载进度
///
[Description("加载进度"), Category("加载"), DefaultValue(0.3F)]
public float LoadingValue
{
get => button.LoadingValue;
set => button.LoadingValue = value;
}
#region 水波进度
///
/// 水波进度
///
[Description("水波进度"), Category("加载"), DefaultValue(0F)]
public float LoadingWaveValue
{
get => button.LoadingWaveValue;
set => button.LoadingWaveValue = value;
}
///
/// 水波颜色
///
[Description("水波颜色"), Category("加载"), DefaultValue(null)]
[Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
public Color? LoadingWaveColor
{
get => button.LoadingWaveColor;
set => button.LoadingWaveColor = value;
}
///
/// 水波是否垂直
///
[Description("水波是否垂直"), Category("加载"), DefaultValue(false)]
public bool LoadingWaveVertical
{
get => button.LoadingWaveVertical;
set => button.LoadingWaveVertical = value;
}
///
/// 水波大小
///
[Description("水波大小"), Category("加载"), DefaultValue(2)]
public int LoadingWaveSize
{
get => button.LoadingWaveSize;
set => button.LoadingWaveSize = value;
}
///
/// 水波数量
///
[Description("水波数量"), Category("加载"), DefaultValue(1)]
public int LoadingWaveCount
{
get => button.LoadingWaveCount;
set => button.LoadingWaveCount = value;
}
#endregion
#endregion
///
/// 连接左边
///
[Description("连接左边"), Category("外观"), DefaultValue(false)]
public bool JoinLeft
{
get => button.JoinLeft;
set => button.JoinLeft = value;
}
///
/// 连接右边
///
[Description("连接右边"), Category("外观"), DefaultValue(false)]
public bool JoinRight
{
get => button.JoinRight;
set => button.JoinRight = value;
}
#endregion
#region 渲染
protected override void OnPaint(PaintEventArgs e)
{
var g = e.Graphics.High();
button.Paint(g, ClientRectangle.PaddingRect(Padding), ReadRectangle);
this.PaintBadge(g);
base.OnPaint(e);
}
public override Rectangle ReadRectangle
{
get => ClientRectangle.PaddingRect(Padding).ReadRect((WaveSize + button.BorderWidth / 2F) * Config.Dpi, button.Shape, button.JoinLeft, button.JoinRight);
}
public override GraphicsPath RenderRegion
{
get
{
var rect_read = ReadRectangle;
float _radius = (button.Shape == TShape.Round || button.Shape == TShape.Circle) ? rect_read.Height : button.Radius * Config.Dpi;
return button.Path(rect_read, _radius);
}
}
#endregion
#region 鼠标
protected override void OnMouseMove(MouseEventArgs e)
{
if (RespondRealAreas)
{
var rect_read = ReadRectangle;
using (var path = button.Path(rect_read, button.Radius * Config.Dpi))
{
button.MouseHover = path.IsVisible(e.Location);
}
}
base.OnMouseMove(e);
}
protected override void OnMouseEnter(EventArgs e)
{
base.OnMouseEnter(e);
if (RespondRealAreas) return;
button.MouseHover = true;
}
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
button.MouseHover = false;
}
protected override void OnLeave(EventArgs e)
{
base.OnLeave(e);
button.MouseHover = false;
}
protected override void OnMouseDown(MouseEventArgs e)
{
if (CanClick(e.Location))
{
Focus();
base.OnMouseDown(e);
button.MouseDown = true;
}
}
protected override void OnMouseUp(MouseEventArgs e)
{
if (button.MouseDown)
{
if (CanClick(e.Location))
{
base.OnMouseUp(e);
if (e.Button == MouseButtons.Left)
{
button.OnClick();
OnClick(e);
}
OnMouseClick(e);
}
button.MouseDown = false;
}
else base.OnMouseUp(e);
}
#endregion
#region 自动大小
///
/// 自动大小
///
[Browsable(true)]
[Description("自动大小"), Category("外观"), DefaultValue(false)]
public override bool AutoSize
{
get => base.AutoSize;
set
{
if (base.AutoSize == value) return;
base.AutoSize = value;
if (value)
{
if (autoSize == TAutoSize.None) autoSize = TAutoSize.Auto;
}
else autoSize = TAutoSize.None;
BeforeAutoSize();
}
}
TAutoSize autoSize = TAutoSize.None;
///
/// 自动大小模式
///
[Description("自动大小模式"), Category("外观"), DefaultValue(TAutoSize.None)]
public TAutoSize AutoSizeMode
{
get => autoSize;
set
{
if (autoSize == value) return;
autoSize = value;
base.AutoSize = autoSize != TAutoSize.None;
BeforeAutoSize();
}
}
protected override void OnFontChanged(EventArgs e)
{
BeforeAutoSize();
base.OnFontChanged(e);
}
public override Size GetPreferredSize(Size proposedSize)
{
if (autoSize == TAutoSize.None) return base.GetPreferredSize(proposedSize);
else if (autoSize == TAutoSize.Width) return new Size(PSize.Width, base.GetPreferredSize(proposedSize).Height);
else if (autoSize == TAutoSize.Height) return new Size(base.GetPreferredSize(proposedSize).Width, PSize.Height);
return PSize;
}
internal Size PSize
{
get
{
return Helper.GDI(g =>
{
var font_size = g.MeasureString(button.Text ?? Config.NullText, Font).Size();
int gap = (int)(20 * Config.Dpi), wave = (int)(WaveSize * Config.Dpi);
if (button.Shape == TShape.Circle || string.IsNullOrEmpty(button.Text))
{
int s = font_size.Height + wave + gap;
return new Size(s, s);
}
else
{
int m = wave * 2;
if (button.JoinLeft || button.JoinRight) m = 0;
bool has_icon = button.Loading || HasIcon;
if (has_icon || button.ShowArrow)
{
if (has_icon && (IconPosition == TAlignMini.Top || IconPosition == TAlignMini.Bottom))
{
int size = (int)Math.Ceiling(font_size.Height * 1.2F);
return new Size(font_size.Width + m + gap + size, font_size.Height + wave + gap + size);
}
int height = font_size.Height + wave + gap;
if (has_icon && button.ShowArrow) return new Size(font_size.Width + m + gap + font_size.Height * 2, height);
else if (has_icon) return new Size(font_size.Width + m + gap + (int)Math.Ceiling(font_size.Height * 1.2F), height);
else return new Size(font_size.Width + m + gap + (int)Math.Ceiling(font_size.Height * .8F), height);
}
else return new Size(font_size.Width + m + gap, font_size.Height + wave + gap);
}
});
}
}
protected override void OnResize(EventArgs e)
{
BeforeAutoSize();
base.OnResize(e);
}
internal bool BeforeAutoSize()
{
if (autoSize == TAutoSize.None) return true;
if (InvokeRequired)
{
bool flag = false;
Invoke(new Action(() =>
{
flag = BeforeAutoSize();
}));
return flag;
}
var PS = PSize;
switch (autoSize)
{
case TAutoSize.Width:
if (Width == PS.Width) return true;
Width = PS.Width;
break;
case TAutoSize.Height:
if (Height == PS.Height) return true;
Height = PS.Height;
break;
case TAutoSize.Auto:
default:
if (Width == PS.Width && Height == PS.Height) return true;
Size = PS;
break;
}
return false;
}
#endregion
#region 按钮点击
protected override void OnKeyUp(KeyEventArgs e)
{
if (e.KeyCode is Keys.Enter or Keys.Space)
{
button.OnClick();
OnClick(EventArgs.Empty);
e.Handled = true;
}
base.OnKeyUp(e);
}
[DefaultValue(DialogResult.None)]
public DialogResult DialogResult { get; set; } = DialogResult.None;
///
/// 是否默认按钮
///
public void NotifyDefault(bool value)
{
}
public void PerformClick()
{
button.OnClick();
OnClick(EventArgs.Empty);
}
bool CanClick()
{
if (button.Loading) return false;
else
{
if (RespondRealAreas)
{
var e = PointToClient(MousePosition);
var rect_read = ReadRectangle;
using (var path = button.Path(rect_read, button.Radius * Config.Dpi))
{
return path.IsVisible(e);
}
}
else return true;
}
}
bool CanClick(Point e)
{
if (button.Loading) return false;
else
{
if (RespondRealAreas)
{
var rect_read = ReadRectangle;
using (var path = button.Path(rect_read, button.Radius * Config.Dpi))
{
return path.IsVisible(e);
}
}
else return true;
}
}
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Advanced)]
public new event EventHandler? DoubleClick
{
add => base.DoubleClick += value;
remove => base.DoubleClick -= value;
}
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Advanced)]
public new event MouseEventHandler? MouseDoubleClick
{
add => base.MouseDoubleClick += value;
remove => base.MouseDoubleClick -= value;
}
#endregion
protected override void OnEnabledChanged(EventArgs e)
{
button.Enabled = Enabled;
base.OnEnabledChanged(e);
}
protected override void Dispose(bool disposing)
{
button.Dispose();
base.Dispose(disposing);
}
}
class IButton : IDisposable
{
Func Font;
Func BAutoSize;
Action Invalidate;
IControl control;
public IButton(IControl _control, Func autoSize)
{
control = _control;
Invalidate = () => control.Invalidate();
Font = () => control.Font;
BAutoSize = autoSize;
}
#region 属性
bool enabled = true;
public bool Enabled
{
get => enabled;
set
{
if (enabled == value) return;
enabled = value;
Invalidate();
}
}
Color? fore;
///
/// 文字颜色
///
public Color? Fore
{
get => fore;
set
{
if (fore == value) fore = value;
fore = value;
Invalidate();
}
}
#region 背景
Color? back;
///
/// 背景颜色
///
public Color? Back
{
get => back;
set
{
if (back == value) return;
back = value;
Invalidate();
}
}
string? backExtend = null;
///
/// 背景渐变色
///
public string? BackExtend
{
get => backExtend;
set
{
if (backExtend == value) return;
backExtend = value;
Invalidate();
}
}
///
/// 悬停背景颜色
///
public Color? BackHover { get; set; }
///
/// 激活背景颜色
///
public Color? BackActive { get; set; }
Image? backImage = null;
///
/// 背景图片
///
public Image? BackgroundImage
{
get => backImage;
set
{
if (backImage == value) return;
backImage = value;
Invalidate();
}
}
TFit backFit = TFit.Fill;
///
/// 背景图片布局
///
public TFit BackgroundImageLayout
{
get => backFit;
set
{
if (backFit == value) return;
backFit = value;
Invalidate();
}
}
#endregion
#region 默认样式
Color? defaultback;
///
/// Default模式背景颜色
///
public Color? DefaultBack
{
get => defaultback;
set
{
if (defaultback == value) return;
defaultback = value;
if (type == TTypeMini.Default) Invalidate();
}
}
Color? defaultbordercolor;
///
/// Default模式边框颜色
///
public Color? DefaultBorderColor
{
get => defaultbordercolor;
set
{
if (defaultbordercolor == value) return;
defaultbordercolor = value;
if (type == TTypeMini.Default) Invalidate();
}
}
#endregion
#region 边框
float borderWidth = 0;
///
/// 边框宽度
///
public float BorderWidth
{
get => borderWidth;
set
{
if (borderWidth == value) return;
borderWidth = value;
Invalidate();
}
}
#endregion
///
/// 波浪大小
///
public int WaveSize { get; set; } = 4;
int radius = 6;
///
/// 圆角
///
public int Radius
{
get => radius;
set
{
if (radius == value) return;
radius = value;
if (BAutoSize()) Invalidate();
}
}
TShape shape = TShape.Default;
///
/// 形状
///
public TShape Shape
{
get => shape;
set
{
if (shape == value) return;
shape = value;
if (BAutoSize()) Invalidate();
}
}
TTypeMini type = TTypeMini.Default;
///
/// 类型
///
public TTypeMini Type
{
get => type;
set
{
if (type == value) return;
type = value;
Invalidate();
}
}
bool ghost = false;
///
/// 幽灵属性,使按钮背景透明
///
public bool Ghost
{
get => ghost;
set
{
if (ghost == value) return;
ghost = value;
Invalidate();
}
}
public float ArrowProg = -1F;
bool showArrow = false;
///
/// 显示箭头
///
public bool ShowArrow
{
get => showArrow;
set
{
if (showArrow == value) return;
showArrow = value;
if (BAutoSize()) Invalidate();
}
}
bool isLink = false;
///
/// 箭头链接样式
///
public bool IsLink
{
get => isLink;
set
{
if (isLink == value) return;
isLink = value;
Invalidate();
}
}
#region 文本
bool textLine = false;
string? text = null;
///
/// 文本
///
public string? Text
{
get => text;
set
{
if (string.IsNullOrEmpty(value)) value = null;
if (text == value) return;
text = value;
if (text == null) textLine = false;
else textLine = text.Contains(Environment.NewLine);
if (BAutoSize()) Invalidate();
}
}
StringFormat stringFormat = Helper.SF_NoWrap();
ContentAlignment textAlign = ContentAlignment.MiddleCenter;
///
/// 文本位置
///
[Description("文本位置"), Category("外观"), DefaultValue(ContentAlignment.MiddleCenter)]
public ContentAlignment TextAlign
{
get => textAlign;
set
{
if (textAlign == value) return;
if (loading || HasIcon || showArrow)
{
value = ContentAlignment.MiddleCenter;
if (textAlign == value) return;
}
textAlign = value;
textAlign.SetAlignment(ref stringFormat);
Invalidate();
}
}
bool autoEllipsis = false;
///
/// 文本超出自动处理
///
public bool AutoEllipsis
{
get => autoEllipsis;
set
{
if (autoEllipsis == value) return;
autoEllipsis = value;
stringFormat.Trimming = value ? StringTrimming.EllipsisCharacter : StringTrimming.None;
}
}
bool textMultiLine = false;
///
/// 是否多行
///
public bool TextMultiLine
{
get => textMultiLine;
set
{
if (textMultiLine == value) return;
textMultiLine = value;
stringFormat.FormatFlags = value ? 0 : StringFormatFlags.NoWrap;
Invalidate();
}
}
#endregion
#region 图标
float iconratio = .7F;
///
/// 图标比例
///
[Description("图标比例"), Category("外观"), DefaultValue(.7F)]
public float IconRatio
{
get => iconratio;
set
{
if (iconratio == value) return;
iconratio = value;
if (BAutoSize()) Invalidate();
}
}
Image? icon = null;
///
/// 图标
///
[Description("图标"), Category("外观"), DefaultValue(null)]
public Image? Icon
{
get => icon;
set
{
if (icon == value) return;
icon = value;
if (BAutoSize()) Invalidate();
}
}
string? iconSvg = null;
///
/// 图标SVG
///
[Description("图标SVG"), Category("外观"), DefaultValue(null)]
public string? IconSvg
{
get => iconSvg;
set
{
if (iconSvg == value) return;
iconSvg = value;
if (BAutoSize()) Invalidate();
}
}
///
/// 是否包含图标
///
public bool HasIcon
{
get => iconSvg != null || icon != null;
}
///
/// 图标大小
///
[Description("图标大小"), Category("外观"), DefaultValue(typeof(Size), "0, 0")]
public Size IconSize { get; set; } = new Size(0, 0);
///
/// 悬停图标
///
[Description("悬停图标"), Category("外观"), DefaultValue(null)]
public Image? IconHover { get; set; } = null;
///
/// 悬停图标SVG
///
[Description("悬停图标SVG"), Category("外观"), DefaultValue(null)]
public string? IconHoverSvg { get; set; } = null;
///
/// 悬停图标动画时长
///
[Description("悬停图标动画时长"), Category("外观"), DefaultValue(200)]
public int IconHoverAnimation { get; set; } = 200;
TAlignMini iconPosition = TAlignMini.Left;
///
/// 按钮图标组件的位置
///
[Description("按钮图标组件的位置"), Category("外观"), DefaultValue(TAlignMini.Left)]
public TAlignMini IconPosition
{
get => iconPosition;
set
{
if (iconPosition == value) return;
iconPosition = value;
if (BAutoSize()) Invalidate();
}
}
#endregion
#region 切换
#region 动画
bool AnimationIconToggle = false;
float AnimationIconToggleValue = 0F;
#endregion
bool toggle = false;
///
/// 选中状态
///
[Description("选中状态"), Category("切换"), DefaultValue(false)]
public bool Toggle
{
get => toggle;
set
{
if (value == toggle) return;
toggle = value;
if (Config.Animation)
{
if (IconToggleAnimation > 0 && HasIcon && HasToggleIcon)
{
ThreadIconHover?.Dispose();
ThreadIconHover = null;
AnimationIconHover = false;
ThreadIconToggle?.Dispose();
AnimationIconToggle = true;
var t = Animation.TotalFrames(10, IconToggleAnimation);
if (value)
{
ThreadIconToggle = new ITask((i) =>
{
AnimationIconToggleValue = Animation.Animate(i, t, 1F, AnimationType.Ball);
Invalidate();
return true;
}, 10, t, () =>
{
AnimationIconToggleValue = 1F;
AnimationIconToggle = false;
Invalidate();
});
}
else
{
ThreadIconToggle = new ITask((i) =>
{
AnimationIconToggleValue = 1F - Animation.Animate(i, t, 1F, AnimationType.Ball);
Invalidate();
return true;
}, 10, t, () =>
{
AnimationIconToggleValue = 0F;
AnimationIconToggle = false;
Invalidate();
});
}
}
else Invalidate();
}
else Invalidate();
}
}
Image? iconToggle = null;
///
/// 切换图标
///
[Description("切换图标"), Category("切换"), DefaultValue(null)]
public Image? ToggleIcon
{
get => iconToggle;
set
{
if (iconToggle == value) return;
iconToggle = value;
if (toggle) Invalidate();
}
}
string? iconSvgToggle = null;
///
/// 切换图标SVG
///
[Description("切换图标SVG"), Category("切换"), DefaultValue(null)]
public string? ToggleIconSvg
{
get => iconSvgToggle;
set
{
if (iconSvgToggle == value) return;
iconSvgToggle = value;
if (toggle) Invalidate();
}
}
///
/// 是否包含切换图标
///
public bool HasToggleIcon
{
get => iconSvgToggle != null || iconToggle != null;
}
///
/// 切换悬停图标
///
[Description("切换悬停图标"), Category("切换"), DefaultValue(null)]
public Image? ToggleIconHover { get; set; } = null;
///
/// 切换悬停图标SVG
///
[Description("切换悬停图标SVG"), Category("切换"), DefaultValue(null)]
public string? ToggleIconHoverSvg { get; set; } = null;
///
/// 图标切换动画时长
///
[Description("图标切换动画时长"), Category("切换"), DefaultValue(200)]
public int IconToggleAnimation { get; set; } = 200;
Color? foreToggle;
///
/// 文字颜色
///
public Color? ToggleFore
{
get => foreToggle;
set
{
if (foreToggle == value) foreToggle = value;
foreToggle = value;
if (toggle) Invalidate();
}
}
TTypeMini? typeToggle = null;
///
/// 切换类型
///
public TTypeMini? ToggleType
{
get => typeToggle;
set
{
if (typeToggle == value) return;
typeToggle = value;
if (toggle) Invalidate();
}
}
#region 背景
Color? backToggle;
///
/// 切换背景颜色
///
public Color? ToggleBack
{
get => backToggle;
set
{
if (backToggle == value) return;
backToggle = value;
if (toggle) Invalidate();
}
}
string? backExtendToggle = null;
///
/// 切换背景渐变色
///
public string? ToggleBackExtend
{
get => backExtendToggle;
set
{
if (backExtendToggle == value) return;
backExtendToggle = value;
if (toggle) Invalidate();
}
}
///
/// 切换悬停背景颜色
///
public Color? ToggleBackHover { get; set; }
///
/// 切换激活背景颜色
///
public Color? ToggleBackActive { get; set; }
#endregion
#endregion
#region 加载动画
bool loading = false;
int AnimationLoadingValue = 0;
int AnimationLoadingWaveValue = 0;
///
/// 加载状态
///
[Description("加载状态"), Category("外观"), DefaultValue(false)]
public bool Loading
{
get => loading;
set
{
if (loading == value) return;
loading = value;
control.SetCursor(_mouseHover && enabled && !value);
BAutoSize();
ThreadLoading?.Dispose();
if (loading)
{
AnimationClickValue = 0;
ThreadLoading = new ITask(control, i =>
{
AnimationLoadingWaveValue += 1;
if (AnimationLoadingWaveValue > 100) AnimationLoadingWaveValue = 0;
AnimationLoadingValue = i;
Invalidate();
return loading;
}, 10, 360, 6, () =>
{
Invalidate();
});
}
else Invalidate();
}
}
///
/// 加载进度
///
public float LoadingValue { get; set; } = 0.3F;
#region 水波进度
///
/// 水波进度
///
public float LoadingWaveValue { get; set; } = 0;
///
/// 水波颜色
///
public Color? LoadingWaveColor { get; set; }
///
/// 水波是否垂直
///
public bool LoadingWaveVertical { get; set; }
///
/// 水波大小
///
public int LoadingWaveSize { get; set; } = 2;
///
/// 水波数量
///
public int LoadingWaveCount { get; set; } = 1;
#endregion
#endregion
bool joinLeft = false;
///
/// 连接左边
///
[Description("连接左边"), Category("外观"), DefaultValue(false)]
public bool JoinLeft
{
get => joinLeft;
set
{
if (joinLeft == value) return;
joinLeft = value;
if (BAutoSize()) Invalidate();
}
}
bool joinRight = false;
///
/// 连接右边
///
[Description("连接右边"), Category("外观"), DefaultValue(false)]
public bool JoinRight
{
get => joinRight;
set
{
if (joinRight == value) return;
joinRight = value;
if (BAutoSize()) Invalidate();
}
}
public void Dispose()
{
ThreadClick?.Dispose();
ThreadHover?.Dispose();
ThreadIconHover?.Dispose();
ThreadIconToggle?.Dispose();
ThreadLoading?.Dispose();
}
ITask? ThreadHover = null;
ITask? ThreadIconHover = null;
ITask? ThreadIconToggle = null;
ITask? ThreadClick = null;
ITask? ThreadLoading = null;
#region 点击动画
bool AnimationClick = false;
float AnimationClickValue = 0;
public void OnClick()
{
if (WaveSize > 0 && Config.Animation)
{
ThreadClick?.Dispose();
AnimationClickValue = 0;
AnimationClick = true;
ThreadClick = new ITask(control, () =>
{
if (AnimationClickValue > 0.6) AnimationClickValue = AnimationClickValue.Calculate(0.04F);
else AnimationClickValue += AnimationClickValue = AnimationClickValue.Calculate(0.1F);
if (AnimationClickValue > 1) { AnimationClickValue = 0F; return false; }
Invalidate();
return true;
}, 50, () =>
{
AnimationClick = false;
Invalidate();
});
}
}
#endregion
#region 悬停动画
bool _mouseDown = false;
public bool MouseDown
{
get => _mouseDown;
set
{
if (_mouseDown == value) return;
_mouseDown = value;
Invalidate();
}
}
int AnimationHoverValue = 0;
bool AnimationHover = false;
bool AnimationIconHover = false;
float AnimationIconHoverValue = 0F;
bool _mouseHover = false;
public bool MouseHover
{
get => _mouseHover;
set
{
if (_mouseHover == value) return;
_mouseHover = value;
control.SetCursor(value && enabled && !loading);
if (enabled)
{
var backHover = GetColorO();
int alpha = backHover.A;
if (Config.Animation)
{
if (IconHoverAnimation > 0 && ((toggle && HasToggleIcon && (ToggleIconHoverSvg != null || ToggleIconHover != null)) || (HasIcon && (IconHoverSvg != null || IconHover != null))))
{
ThreadIconHover?.Dispose();
AnimationIconHover = true;
var t = Animation.TotalFrames(10, IconHoverAnimation);
if (value)
{
ThreadIconHover = new ITask((i) =>
{
AnimationIconHoverValue = Animation.Animate(i, t, 1F, AnimationType.Ball);
Invalidate();
return true;
}, 10, t, () =>
{
AnimationIconHoverValue = 1F;
AnimationIconHover = false;
Invalidate();
});
}
else
{
ThreadIconHover = new ITask((i) =>
{
AnimationIconHoverValue = 1F - Animation.Animate(i, t, 1F, AnimationType.Ball);
Invalidate();
return true;
}, 10, t, () =>
{
AnimationIconHoverValue = 0F;
AnimationIconHover = false;
Invalidate();
});
}
}
if (alpha > 0)
{
int addvalue = alpha / 12;
ThreadHover?.Dispose();
AnimationHover = true;
if (value)
{
ThreadHover = new ITask(control, () =>
{
AnimationHoverValue += addvalue;
if (AnimationHoverValue > alpha) { AnimationHoverValue = alpha; return false; }
Invalidate();
return true;
}, 10, () =>
{
AnimationHover = false;
Invalidate();
});
}
else
{
ThreadHover = new ITask(control, () =>
{
if (AnimationHoverValue > alpha) AnimationHoverValue = alpha;
else AnimationHoverValue -= addvalue;
if (AnimationHoverValue < 1) { AnimationHoverValue = 0; return false; }
Invalidate();
return true;
}, 10, () =>
{
AnimationHover = false;
Invalidate();
});
}
}
else
{
AnimationHoverValue = alpha;
Invalidate();
}
}
else AnimationHoverValue = alpha;
Invalidate();
}
}
}
#endregion
#endregion
#region 渲染
public void Paint(Graphics g, Rectangle rect, Rectangle rect_read)
{
float _radius = (shape == TShape.Round || shape == TShape.Circle) ? rect_read.Height : radius * Config.Dpi;
if (backImage != null) g.PaintImg(rect_read, backImage, backFit, _radius, shape);
bool is_default = type == TTypeMini.Default;
if (toggle && typeToggle.HasValue) is_default = typeToggle.Value == TTypeMini.Default;
if (is_default)
{
GetDefaultColorConfig(out var _fore, out var _color, out var _back_hover, out var _back_active);
using (var path = Path(rect_read, _radius))
{
#region 动画
if (AnimationClick)
{
float maxw = rect_read.Width + ((rect.Width - rect_read.Width) * AnimationClickValue), maxh = rect_read.Height + ((rect.Height - rect_read.Height) * AnimationClickValue);
if (shape == TShape.Circle)
{
if (maxw > maxh) maxw = maxh;
else maxh = maxw;
}
float alpha = 100 * (1F - AnimationClickValue);
using (var brush = new SolidBrush(Helper.ToColor(alpha, _color)))
{
using (var path_click = new RectangleF(rect.X + (rect.Width - maxw) / 2F, rect.Y + (rect.Height - maxh) / 2F, maxw, maxh).RoundPath(_radius, shape))
{
path_click.AddPath(path, false);
g.FillPath(brush, path_click);
}
}
}
#endregion
if (enabled)
{
if (!ghost)
{
#region 绘制阴影
if (WaveSize > 0)
{
using (var path_shadow = new RectangleF(rect_read.X, rect_read.Y + 3, rect_read.Width, rect_read.Height).RoundPath(_radius))
{
path_shadow.AddPath(path, false);
using (var brush = new SolidBrush(Style.Db.FillQuaternary))
{
g.FillPath(brush, path_shadow);
}
}
}
#endregion
using (var brush = new SolidBrush(defaultback ?? Style.Db.DefaultBg))
{
g.FillPath(brush, path);
}
}
if (borderWidth > 0)
{
PaintLoadingWave(g, path, rect_read);
float border = borderWidth * Config.Dpi;
if (MouseDown)
{
using (var brush = new Pen(_back_active, border))
{
g.DrawPath(brush, path);
}
PaintTextLoading(g, text, _back_active, rect_read, enabled);
}
else if (AnimationHover)
{
var colorHover = Helper.ToColor(AnimationHoverValue, _back_hover);
using (var brush = new Pen(Style.Db.DefaultBorder, border))
{
g.DrawPath(brush, path);
}
using (var brush = new Pen(colorHover, border))
{
g.DrawPath(brush, path);
}
PaintTextLoading(g, text, _fore, colorHover, rect_read);
}
else if (MouseHover)
{
using (var brush = new Pen(_back_hover, border))
{
g.DrawPath(brush, path);
}
PaintTextLoading(g, text, _back_hover, rect_read, enabled);
}
else
{
using (var brush = new Pen(defaultbordercolor ?? Style.Db.DefaultBorder, border))
{
g.DrawPath(brush, path);
}
PaintTextLoading(g, text, _fore, rect_read, enabled);
}
}
else
{
if (MouseDown)
{
using (var brush = new SolidBrush(_back_active))
{
g.FillPath(brush, path);
}
}
else if (AnimationHover)
{
using (var brush = new SolidBrush(Helper.ToColor(AnimationHoverValue, _back_hover)))
{
g.FillPath(brush, path);
}
}
else if (MouseHover)
{
using (var brush = new SolidBrush(_back_hover))
{
g.FillPath(brush, path);
}
}
PaintLoadingWave(g, path, rect_read);
PaintTextLoading(g, text, _fore, rect_read, enabled);
}
}
else
{
PaintLoadingWave(g, path, rect_read);
if (borderWidth > 0)
{
using (var brush = new SolidBrush(Style.Db.FillTertiary))
{
g.FillPath(brush, path);
}
}
PaintTextLoading(g, text, Style.Db.TextQuaternary, rect_read, enabled);
}
}
}
else
{
GetColorConfig(out var _fore, out var _back, out var _back_hover, out var _back_active);
using (var path = Path(rect_read, _radius))
{
#region 动画
if (AnimationClick)
{
float maxw = rect_read.Width + ((rect.Width - rect_read.Width) * AnimationClickValue), maxh = rect_read.Height + ((rect.Height - rect_read.Height) * AnimationClickValue);
if (shape == TShape.Circle)
{
if (maxw > maxh) maxw = maxh;
else maxh = maxw;
}
float alpha = 100 * (1F - AnimationClickValue);
using (var brush = new SolidBrush(Helper.ToColor(alpha, _back)))
{
using (var path_click = new RectangleF(rect.X + (rect.Width - maxw) / 2F, rect.Y + (rect.Height - maxh) / 2F, maxw, maxh).RoundPath(_radius, shape))
{
path_click.AddPath(path, false);
g.FillPath(brush, path_click);
}
}
}
#endregion
if (ghost)
{
PaintLoadingWave(g, path, rect_read);
#region 绘制背景
if (borderWidth > 0)
{
float border = borderWidth * Config.Dpi;
if (MouseDown)
{
using (var brush = new Pen(_back_active, border))
{
g.DrawPath(brush, path);
}
PaintTextLoading(g, text, _back_active, rect_read, enabled);
}
else if (AnimationHover)
{
var colorHover = Helper.ToColor(AnimationHoverValue, _back_hover);
using (var brush = new Pen(enabled ? _back : Style.Db.FillTertiary, border))
{
g.DrawPath(brush, path);
}
using (var brush = new Pen(colorHover, border))
{
g.DrawPath(brush, path);
}
PaintTextLoading(g, text, _back, colorHover, rect_read);
}
else if (MouseHover)
{
using (var brush = new Pen(_back_hover, border))
{
g.DrawPath(brush, path);
}
PaintTextLoading(g, text, _back_hover, rect_read, enabled);
}
else
{
if (enabled)
{
if (toggle)
{
using (var brushback = backExtendToggle.BrushEx(rect_read, _back))
{
using (var brush = new Pen(brushback, border))
{
g.DrawPath(brush, path);
}
}
}
else
{
using (var brushback = backExtend.BrushEx(rect_read, _back))
{
using (var brush = new Pen(brushback, border))
{
g.DrawPath(brush, path);
}
}
}
}
else
{
using (var brush = new Pen(Style.Db.FillTertiary, border))
{
g.DrawPath(brush, path);
}
}
PaintTextLoading(g, text, enabled ? _back : Style.Db.TextQuaternary, rect_read, enabled);
}
}
else PaintTextLoading(g, text, enabled ? _back : Style.Db.TextQuaternary, rect_read, enabled);
#endregion
}
else
{
#region 绘制阴影
if (enabled && WaveSize > 0)
{
using (var path_shadow = new RectangleF(rect_read.X, rect_read.Y + 3, rect_read.Width, rect_read.Height).RoundPath(_radius))
{
path_shadow.AddPath(path, false);
using (var brush = new SolidBrush(_back.rgba(Config.Mode == TMode.Dark ? 0.15F : 0.1F)))
{
g.FillPath(brush, path_shadow);
}
}
}
#endregion
#region 绘制背景
if (enabled)
{
if (toggle)
{
using (var brush = backExtendToggle.BrushEx(rect_read, _back))
{
g.FillPath(brush, path);
}
}
else
{
using (var brush = backExtend.BrushEx(rect_read, _back))
{
g.FillPath(brush, path);
}
}
}
else
{
using (var brush = new SolidBrush(Style.Db.FillTertiary))
{
g.FillPath(brush, path);
}
}
if (MouseDown)
{
using (var brush = new SolidBrush(_back_active))
{
g.FillPath(brush, path);
}
}
else if (AnimationHover)
{
var colorHover = Helper.ToColor(AnimationHoverValue, _back_hover);
using (var brush = new SolidBrush(colorHover))
{
g.FillPath(brush, path);
}
}
else if (MouseHover)
{
using (var brush = new SolidBrush(_back_hover))
{
g.FillPath(brush, path);
}
}
#endregion
PaintLoadingWave(g, path, rect_read);
PaintTextLoading(g, text, enabled ? _fore : Style.Db.TextQuaternary, rect_read, enabled);
}
}
}
}
#region 渲染帮助
void PaintLoadingWave(Graphics g, GraphicsPath path, Rectangle rect)
{
if (loading && LoadingWaveValue > 0)
{
using (var brush = new SolidBrush(LoadingWaveColor ?? Style.Db.Fill))
{
if (LoadingWaveValue >= 1) g.FillPath(brush, path);
else if (LoadingWaveCount > 0)
{
var state = g.Save();
g.SetClip(path);
g.ResetTransform();
int len = (int)(LoadingWaveSize * Config.Dpi), count = LoadingWaveCount * 2 + 2;
if (count < 6) count = 6;
if (LoadingWaveVertical)
{
int pvalue = (int)(rect.Height * LoadingWaveValue);
if (pvalue > 0)
{
pvalue = rect.Height - pvalue + rect.Y;
int wd = rect.Width / LoadingWaveCount, wd2 = wd * 2, pvalue2 = pvalue - len, rr = rect.X + wd * count;
using (var path_line = new GraphicsPath())
{
g.TranslateTransform(-(wd + wd2 * (AnimationLoadingWaveValue / 100F)), 0);
path_line.AddLine(rr, pvalue, rr, rect.Bottom);
path_line.AddLine(rr, rect.Bottom, rect.X, rect.Bottom);
path_line.AddLine(rect.X, rect.Bottom, rect.X, pvalue);
bool to = true;
var line = new List(count);
for (int i = 0; i < count + 1; i++)
{
line.Add(new PointF(rect.X + wd * i, to ? pvalue : pvalue2));
to = !to;
}
path_line.AddCurve(line.ToArray());
g.FillPath(brush, path_line);
}
}
}
else
{
int pvalue = (int)(rect.Width * LoadingWaveValue);
if (pvalue > 0)
{
pvalue += rect.X;
int wd = rect.Height / LoadingWaveCount, wd2 = wd * 2, pvalue2 = pvalue + len, rb = rect.Y + wd * count;
using (var path_line = new GraphicsPath())
{
g.TranslateTransform(0, -(wd + wd2 * (AnimationLoadingWaveValue / 100F)));
path_line.AddLine(pvalue, rb, rect.X, rb);
path_line.AddLine(rect.X, rb, rect.X, rect.Y);
path_line.AddLine(rect.X, rect.Y, pvalue, rect.Y);
bool to = true;
var line = new List(count);
for (int i = 0; i < count + 1; i++)
{
line.Add(new PointF(to ? pvalue : pvalue2, rect.Y + wd * i));
to = !to;
}
path_line.AddCurve(line.ToArray());
g.FillPath(brush, path_line);
}
}
}
g.Restore(state);
}
else
{
if (LoadingWaveVertical)
{
int pvalue = (int)(rect.Height * LoadingWaveValue);
if (pvalue > 0)
{
var state = g.Save();
g.SetClip(new Rectangle(rect.X, rect.Y + rect.Height - pvalue, rect.Width, pvalue));
g.FillPath(brush, path);
g.Restore(state);
}
}
else
{
int pvalue = (int)(rect.Width * LoadingWaveValue);
if (pvalue > 0)
{
var state = g.Save();
g.SetClip(new Rectangle(rect.X, rect.Y, pvalue, rect.Height));
g.FillPath(brush, path);
g.Restore(state);
}
}
}
}
}
}
void PaintTextLoading(Graphics g, string? text, Color color, Rectangle rect_read, bool enabled)
{
var font_size = g.MeasureString(text ?? Config.NullText, Font()).Size();
if (text == null)
{
//没有文字
var rect = GetIconRectCenter(font_size, rect_read);
if (loading)
{
float loading_size = rect_read.Height * 0.06F;
using (var brush = new Pen(color, loading_size))
{
brush.StartCap = brush.EndCap = LineCap.Round;
g.DrawArc(brush, rect, AnimationLoadingValue, LoadingValue * 360F);
}
}
else
{
if (PaintIcon(g, color, rect, false, enabled) && showArrow)
{
int size = (int)(font_size.Height * IconRatio);
var rect_arrow = new Rectangle(rect_read.X + (rect_read.Width - size) / 2, rect_read.Y + (rect_read.Height - size) / 2, size, size);
using (var pen = new Pen(color, 2F * Config.Dpi))
{
pen.StartCap = pen.EndCap = LineCap.Round;
if (isLink)
{
int size_arrow = rect_arrow.Width / 2;
g.TranslateTransform(rect_arrow.X + size_arrow, rect_arrow.Y + size_arrow);
g.RotateTransform(-90F);
g.DrawLines(pen, new Rectangle(-size_arrow, -size_arrow, rect_arrow.Width, rect_arrow.Height).TriangleLines(ArrowProg));
g.ResetTransform();
}
else g.DrawLines(pen, rect_arrow.TriangleLines(ArrowProg));
}
}
}
}
else
{
bool has_left = loading || HasIcon, has_right = showArrow;
Rectangle rect_text;
if (has_left || has_right)
{
if (has_left && has_right)
{
rect_text = RectAlignLR(g, textLine, Font(), iconPosition, iconratio, font_size, rect_read, out var rect_l, out var rect_r);
if (loading)
{
float loading_size = rect_l.Height * .14F;
using (var brush = new Pen(color, loading_size))
{
brush.StartCap = brush.EndCap = LineCap.Round;
g.DrawArc(brush, rect_l, AnimationLoadingValue, LoadingValue * 360F);
}
}
else PaintIcon(g, color, rect_l, true, enabled);
#region ARROW
using (var pen = new Pen(color, 2F * Config.Dpi))
{
pen.StartCap = pen.EndCap = LineCap.Round;
if (isLink)
{
int size_arrow = rect_r.Width / 2;
g.TranslateTransform(rect_r.X + size_arrow, rect_r.Y + size_arrow);
g.RotateTransform(-90F);
g.DrawLines(pen, new Rectangle(-size_arrow, -size_arrow, rect_r.Width, rect_r.Height).TriangleLines(ArrowProg));
g.ResetTransform();
}
else g.DrawLines(pen, rect_r.TriangleLines(ArrowProg));
}
#endregion
}
else if (has_left)
{
rect_text = RectAlignL(g, textLine, Font(), iconPosition, iconratio, font_size, rect_read, out var rect_l);
if (loading)
{
float loading_size = rect_l.Height * .14F;
using (var brush = new Pen(color, loading_size))
{
brush.StartCap = brush.EndCap = LineCap.Round;
g.DrawArc(brush, rect_l, AnimationLoadingValue, LoadingValue * 360F);
}
}
else PaintIcon(g, color, rect_l, true, enabled);
}
else
{
rect_text = RectAlignR(g, textLine, Font(), iconPosition, iconratio, font_size, rect_read, out var rect_r);
#region ARROW
using (var pen = new Pen(color, 2F * Config.Dpi))
{
pen.StartCap = pen.EndCap = LineCap.Round;
if (isLink)
{
int size_arrow = rect_r.Width / 2;
g.TranslateTransform(rect_r.X + size_arrow, rect_r.Y + size_arrow);
g.RotateTransform(-90F);
g.DrawLines(pen, new Rectangle(-size_arrow, -size_arrow, rect_r.Width, rect_r.Height).TriangleLines(ArrowProg));
g.ResetTransform();
}
else g.DrawLines(pen, rect_r.TriangleLines(ArrowProg));
}
#endregion
}
}
else
{
int sps = (int)(font_size.Height * .4F), sps2 = sps * 2;
rect_text = new Rectangle(rect_read.X + sps, rect_read.Y + sps, rect_read.Width - sps2, rect_read.Height - sps2);
PaintTextAlign(rect_read, ref rect_text);
}
using (var brush = new SolidBrush(color))
{
g.DrawStr(text, Font(), brush, rect_text, stringFormat);
}
}
}
void PaintTextLoading(Graphics g, string? text, Color color, Color colorHover, Rectangle rect_read)
{
var font_size = g.MeasureString(text ?? Config.NullText, Font()).Size();
if (text == null)
{
var rect = GetIconRectCenter(font_size, rect_read);
if (loading)
{
float loading_size = rect_read.Height * 0.06F;
using (var brush = new Pen(color, loading_size))
{
brush.StartCap = brush.EndCap = LineCap.Round;
g.DrawArc(brush, rect, AnimationLoadingValue, LoadingValue * 360F);
}
}
else
{
if (PaintIcon(g, color, rect, false, true))
{
if (showArrow)
{
int size = (int)(font_size.Height * IconRatio);
var rect_arrow = new Rectangle(rect_read.X + (rect_read.Width - size) / 2, rect_read.Y + (rect_read.Height - size) / 2, size, size);
using (var pen = new Pen(color, 2F * Config.Dpi))
using (var penHover = new Pen(colorHover, pen.Width))
{
pen.StartCap = pen.EndCap = LineCap.Round;
if (isLink)
{
int size_arrow = rect_arrow.Width / 2;
g.TranslateTransform(rect_arrow.X + size_arrow, rect_arrow.Y + size_arrow);
g.RotateTransform(-90F);
var rect_arrow_lines = new Rectangle(-size_arrow, -size_arrow, rect_arrow.Width, rect_arrow.Height).TriangleLines(ArrowProg);
g.DrawLines(pen, rect_arrow_lines);
g.DrawLines(penHover, rect_arrow_lines);
g.ResetTransform();
}
else
{
var rect_arrow_lines = rect_arrow.TriangleLines(ArrowProg);
g.DrawLines(pen, rect_arrow_lines);
g.DrawLines(penHover, rect_arrow_lines);
}
}
}
}
else PaintIcon(g, colorHover, rect, false, true);
}
}
else
{
bool has_left = loading || HasIcon, has_right = showArrow;
Rectangle rect_text;
if (has_left || has_right)
{
if (has_left && has_right)
{
rect_text = RectAlignLR(g, textLine, Font(), iconPosition, iconratio, font_size, rect_read, out var rect_l, out var rect_r);
if (loading)
{
float loading_size = rect_l.Height * .14F;
using (var brush = new Pen(color, loading_size))
{
brush.StartCap = brush.EndCap = LineCap.Round;
g.DrawArc(brush, rect_l, AnimationLoadingValue, LoadingValue * 360F);
}
}
else
{
PaintIcon(g, color, rect_l, true, true);
PaintIcon(g, colorHover, rect_l, true, true);
}
#region ARROW
using (var pen = new Pen(color, 2F * Config.Dpi))
using (var penHover = new Pen(colorHover, pen.Width))
{
penHover.StartCap = penHover.EndCap = pen.StartCap = pen.EndCap = LineCap.Round;
if (isLink)
{
int size_arrow = rect_r.Width / 2;
g.TranslateTransform(rect_r.X + size_arrow, rect_r.Y + size_arrow);
g.RotateTransform(-90F);
var rect_arrow = new Rectangle(-size_arrow, -size_arrow, rect_r.Width, rect_r.Height).TriangleLines(ArrowProg);
g.DrawLines(pen, rect_arrow);
g.DrawLines(penHover, rect_arrow);
g.ResetTransform();
}
else
{
var rect_arrow = rect_r.TriangleLines(ArrowProg);
g.DrawLines(pen, rect_arrow);
g.DrawLines(penHover, rect_arrow);
}
}
#endregion
}
else if (has_left)
{
rect_text = RectAlignL(g, textLine, Font(), iconPosition, iconratio, font_size, rect_read, out var rect_l);
if (loading)
{
float loading_size = rect_l.Height * .14F;
using (var brush = new Pen(color, loading_size))
{
brush.StartCap = brush.EndCap = LineCap.Round;
g.DrawArc(brush, rect_l, AnimationLoadingValue, LoadingValue * 360F);
}
}
else
{
PaintIcon(g, color, rect_l, true, true);
PaintIcon(g, colorHover, rect_l, true, true);
}
}
else
{
rect_text = RectAlignR(g, textLine, Font(), iconPosition, iconratio, font_size, rect_read, out var rect_r);
#region ARROW
using (var pen = new Pen(color, 2F * Config.Dpi))
using (var penHover = new Pen(colorHover, pen.Width))
{
penHover.StartCap = penHover.EndCap = pen.StartCap = pen.EndCap = LineCap.Round;
if (isLink)
{
int size_arrow = rect_r.Width / 2;
g.TranslateTransform(rect_r.X + size_arrow, rect_r.Y + size_arrow);
g.RotateTransform(-90F);
var rect_arrow = new Rectangle(-size_arrow, -size_arrow, rect_r.Width, rect_r.Height).TriangleLines(ArrowProg);
g.DrawLines(pen, rect_arrow);
g.DrawLines(penHover, rect_arrow);
g.ResetTransform();
}
else
{
var rect_arrow = rect_r.TriangleLines(ArrowProg);
g.DrawLines(pen, rect_arrow);
g.DrawLines(penHover, rect_arrow);
}
}
#endregion
}
}
else
{
int sps = (int)(font_size.Height * .4F), sps2 = sps * 2;
rect_text = new Rectangle(rect_read.X + sps, rect_read.Y + sps, rect_read.Width - sps2, rect_read.Height - sps2);
PaintTextAlign(rect_read, ref rect_text);
}
using (var brush = new SolidBrush(color))
using (var brushHover = new SolidBrush(colorHover))
{
g.DrawStr(text, Font(), brush, rect_text, stringFormat);
g.DrawStr(text, Font(), brushHover, rect_text, stringFormat);
}
}
}
internal static Rectangle RectAlignL(Graphics g, bool textLine, Font font, TAlignMini iconPosition, float iconratio, Size font_size, Rectangle rect_read, out Rectangle rect_l)
{
int font_Height = font_size.Height;
if (textLine && (iconPosition == TAlignMini.Top || iconPosition == TAlignMini.Bottom)) font_Height = g.MeasureString(Config.NullText, font).Size().Height;
int icon_size = (int)(font_Height * iconratio), sp = (int)(font_Height * .25F);
Rectangle rect_text;
switch (iconPosition)
{
case TAlignMini.Top:
int t_x = rect_read.Y + ((rect_read.Height - (font_size.Height + icon_size + sp)) / 2);
rect_text = new Rectangle(rect_read.X, t_x + icon_size + sp, rect_read.Width, font_size.Height);
rect_l = new Rectangle(rect_read.X + (rect_read.Width - icon_size) / 2, t_x, icon_size, icon_size);
break;
case TAlignMini.Bottom:
int b_x = rect_read.Y + ((rect_read.Height - (font_size.Height + icon_size + sp)) / 2);
rect_text = new Rectangle(rect_read.X, b_x, rect_read.Width, font_size.Height);
rect_l = new Rectangle(rect_read.X + (rect_read.Width - icon_size) / 2, b_x + font_size.Height + sp, icon_size, icon_size);
break;
case TAlignMini.Right:
int r_x = rect_read.X + ((rect_read.Width - (font_size.Width + icon_size + sp)) / 2);
rect_text = new Rectangle(r_x, rect_read.Y, font_size.Width, rect_read.Height);
rect_l = new Rectangle(r_x + font_size.Width + sp, rect_read.Y + (rect_read.Height - icon_size) / 2, icon_size, icon_size);
break;
case TAlignMini.Left:
default:
int l_x = rect_read.X + ((rect_read.Width - (font_size.Width + icon_size + sp)) / 2);
rect_text = new Rectangle(l_x + icon_size + sp, rect_read.Y, font_size.Width, rect_read.Height);
rect_l = new Rectangle(l_x, rect_read.Y + (rect_read.Height - icon_size) / 2, icon_size, icon_size);
break;
}
return rect_text;
}
internal static Rectangle RectAlignLR(Graphics g, bool textLine, Font font, TAlignMini iconPosition, float iconratio, Size font_size, Rectangle rect_read, out Rectangle rect_l, out Rectangle rect_r)
{
int font_Height = font_size.Height;
if (textLine && (iconPosition == TAlignMini.Top || iconPosition == TAlignMini.Bottom)) font_Height = g.MeasureString(Config.NullText, font).Size().Height;
int icon_size = (int)(font_Height * iconratio), sp = (int)(font_Height * .25F), sps = (int)(font_size.Height * .4F);
Rectangle rect_text;
switch (iconPosition)
{
case TAlignMini.Top:
int t_x = rect_read.Y + ((rect_read.Height - (font_size.Height + icon_size + sp)) / 2);
rect_text = new Rectangle(rect_read.X, t_x + icon_size + sp, rect_read.Width, font_size.Height);
rect_l = new Rectangle(rect_read.X + (rect_read.Width - icon_size) / 2, t_x, icon_size, icon_size);
rect_r = new Rectangle(rect_read.Right - icon_size - sps, rect_text.Y + (rect_text.Height - icon_size) / 2, icon_size, icon_size);
break;
case TAlignMini.Bottom:
int b_x = rect_read.Y + ((rect_read.Height - (font_size.Height + icon_size + sp)) / 2);
rect_text = new Rectangle(rect_read.X, b_x, rect_read.Width, font_size.Height);
rect_l = new Rectangle(rect_read.X + (rect_read.Width - icon_size) / 2, b_x + font_size.Height + sp, icon_size, icon_size);
rect_r = new Rectangle(rect_read.Right - icon_size - sps, rect_text.Y + (rect_text.Height - icon_size) / 2, icon_size, icon_size);
break;
case TAlignMini.Right:
int r_x = rect_read.X + ((rect_read.Width - (font_size.Width + icon_size + sp + sps)) / 2), r_y = rect_read.Y + (rect_read.Height - icon_size) / 2;
rect_text = new Rectangle(r_x, rect_read.Y, font_size.Width, rect_read.Height);
rect_l = new Rectangle(r_x + font_size.Width + sp, r_y, icon_size, icon_size);
rect_r = new Rectangle(rect_read.X + sps, r_y, icon_size, icon_size);
break;
case TAlignMini.Left:
default:
int l_x = rect_read.X + ((rect_read.Width - (font_size.Width + icon_size + sp + sps)) / 2), l_y = rect_read.Y + (rect_read.Height - icon_size) / 2;
rect_text = new Rectangle(l_x + icon_size + sp, rect_read.Y, font_size.Width, rect_read.Height);
rect_l = new Rectangle(l_x, l_y, icon_size, icon_size);
rect_r = new Rectangle(rect_read.Right - icon_size - sps, l_y, icon_size, icon_size);
break;
}
return rect_text;
}
internal static Rectangle RectAlignR(Graphics g, bool textLine, Font font, TAlignMini iconPosition, float iconratio, Size font_size, Rectangle rect_read, out Rectangle rect_r)
{
int font_Height = font_size.Height;
if (textLine && (iconPosition == TAlignMini.Top || iconPosition == TAlignMini.Bottom)) font_Height = g.MeasureString(Config.NullText, font).Size().Height;
int icon_size = (int)(font_Height * iconratio), sp = (int)(font_Height * .25F), sps = (int)(font_size.Height * .4F), rsps = icon_size + sp;
Rectangle rect_text;
switch (iconPosition)
{
case TAlignMini.Bottom:
case TAlignMini.Right:
rect_text = new Rectangle(rect_read.X + rsps, rect_read.Y, rect_read.Width - rsps, rect_read.Height);
rect_r = new Rectangle(rect_read.X + sps, rect_read.Y + (rect_read.Height - icon_size) / 2, icon_size, icon_size);
break;
case TAlignMini.Top:
case TAlignMini.Left:
default:
rect_text = new Rectangle(rect_read.X, rect_read.Y, rect_read.Width - rsps, rect_read.Height);
rect_r = new Rectangle(rect_read.Right - icon_size - sps, rect_read.Y + (rect_read.Height - icon_size) / 2, icon_size, icon_size);
break;
}
return rect_text;
}
void PaintTextAlign(Rectangle rect_read, ref Rectangle rect_text)
{
switch (textAlign)
{
case ContentAlignment.MiddleCenter:
case ContentAlignment.MiddleLeft:
case ContentAlignment.MiddleRight:
rect_text.Y = rect_read.Y;
rect_text.Height = rect_read.Height;
break;
case ContentAlignment.TopLeft:
case ContentAlignment.TopRight:
case ContentAlignment.TopCenter:
rect_text.Height = rect_read.Height - rect_text.Y;
break;
}
}
#region 渲染图标
///
/// 居中的图标绘制区域
///
/// 字体大小
/// 客户区域
Rectangle GetIconRectCenter(Size font_size, Rectangle rect_read)
{
if (IconSize.Width > 0 && IconSize.Height > 0)
{
int w = (int)(IconSize.Width * Config.Dpi), h = (int)(IconSize.Height * Config.Dpi);
return new Rectangle(rect_read.X + (rect_read.Width - w) / 2, rect_read.Y + (rect_read.Height - h) / 2, w, h);
}
else
{
int w = (int)(font_size.Height * IconRatio * 1.125F);
return new Rectangle(rect_read.X + (rect_read.Width - w) / 2, rect_read.Y + (rect_read.Height - w) / 2, w, w);
}
}
///
/// 渲染图标
///
/// GDI
/// 颜色
/// 区域
/// 包含文本
/// 使能
bool PaintIcon(Graphics g, Color? color, Rectangle rect_o, bool hastxt, bool enabled)
{
var rect = hastxt ? GetIconRect(rect_o) : rect_o;
if (AnimationIconHover)
{
PaintCoreIcon(g, rect, color, 1F - AnimationIconHoverValue);
PaintCoreIconHover(g, rect, color, AnimationIconHoverValue);
return false;
}
else if (AnimationIconToggle)
{
float d = 1F - AnimationIconToggleValue;
if (MouseHover)
{
if (!PaintCoreIcon(g, IconHover, IconHoverSvg, rect, color, d)) PaintCoreIcon(g, icon, iconSvg, rect, color, d);
if (!PaintCoreIcon(g, ToggleIconHover, ToggleIconHoverSvg, rect, color, AnimationIconToggleValue)) PaintCoreIcon(g, ToggleIcon, ToggleIconSvg, rect, color, AnimationIconToggleValue);
}
else
{
PaintCoreIcon(g, icon, iconSvg, rect, color, d);
PaintCoreIcon(g, iconToggle, iconSvgToggle, rect, color, AnimationIconToggleValue);
}
return false;
}
else
{
if (enabled)
{
if (MouseHover)
{
if (PaintCoreIconHover(g, rect, color)) return false;
}
if (PaintCoreIcon(g, rect, color)) return false;
}
else
{
if (MouseHover)
{
if (PaintCoreIconHover(g, rect, color, .3F)) return false;
}
if (PaintCoreIcon(g, rect, color, .3F)) return false;
}
}
return true;
}
///
/// 图标绘制区域
///
/// 图标区域
Rectangle GetIconRect(Rectangle rectl)
{
if (IconSize.Width > 0 && IconSize.Height > 0)
{
int w = (int)(IconSize.Width * Config.Dpi), h = (int)(IconSize.Height * Config.Dpi);
return new Rectangle(rectl.X + (rectl.Width - w) / 2, rectl.Y + (rectl.Height - h) / 2, w, h);
}
else return rectl;
}
bool PaintCoreIcon(Graphics g, Rectangle rect, Color? color, float opacity = 1F) => toggle ? PaintCoreIcon(g, iconToggle, iconSvgToggle, rect, color, opacity) : PaintCoreIcon(g, icon, iconSvg, rect, color, opacity);
bool PaintCoreIconHover(Graphics g, Rectangle rect, Color? color, float opacity = 1F) => toggle ? PaintCoreIcon(g, ToggleIconHover, ToggleIconHoverSvg, rect, color, opacity) : PaintCoreIcon(g, IconHover, IconHoverSvg, rect, color, opacity);
bool PaintCoreIcon(Graphics g, Image? icon, string? iconSvg, Rectangle rect, Color? color, float opacity = 1F)
{
if (iconSvg != null)
{
using (var _bmp = SvgExtend.GetImgExtend(iconSvg, rect, color))
{
if (_bmp != null)
{
g.DrawImage(_bmp, rect, opacity);
return true;
}
}
}
else if (icon != null)
{
g.DrawImage(icon, rect, opacity);
return true;
}
return false;
}
#endregion
public GraphicsPath Path(RectangleF rect_read, float _radius)
{
if (shape == TShape.Circle)
{
var path = new GraphicsPath();
path.AddEllipse(rect_read);
return path;
}
if (joinLeft && joinRight) return rect_read.RoundPath(0);
else if (joinRight) return rect_read.RoundPath(_radius, true, false, false, true);
else if (joinLeft) return rect_read.RoundPath(_radius, false, true, true, false);
return rect_read.RoundPath(_radius);
}
#endregion
#region 帮助
Color GetColorO()
{
if (toggle)
{
if (typeToggle.HasValue) return GetColorO(typeToggle.Value);
else return GetColorO(type);
}
return GetColorO(type);
}
Color GetColorO(TTypeMini type)
{
Color color;
switch (type)
{
case TTypeMini.Default:
if (borderWidth > 0) color = Style.Db.PrimaryHover;
else color = Style.Db.FillSecondary;
break;
case TTypeMini.Success:
color = Style.Db.SuccessHover;
break;
case TTypeMini.Error:
color = Style.Db.ErrorHover;
break;
case TTypeMini.Info:
color = Style.Db.InfoHover;
break;
case TTypeMini.Warn:
color = Style.Db.WarningHover;
break;
case TTypeMini.Primary:
default:
color = Style.Db.PrimaryHover;
break;
}
if (BackHover.HasValue) color = BackHover.Value;
return color;
}
void GetDefaultColorConfig(out Color Fore, out Color Color, out Color backHover, out Color backActive)
{
Fore = Style.Db.DefaultColor;
Color = Style.Db.Primary;
if (borderWidth > 0)
{
backHover = Style.Db.PrimaryHover;
backActive = Style.Db.PrimaryActive;
}
else
{
backHover = Style.Db.FillSecondary;
backActive = Style.Db.Fill;
}
if (toggle)
{
if (foreToggle.HasValue) Fore = foreToggle.Value;
if (ToggleBackHover.HasValue) backHover = ToggleBackHover.Value;
if (ToggleBackActive.HasValue) backActive = ToggleBackActive.Value;
}
else
{
if (fore.HasValue) Fore = fore.Value;
if (BackHover.HasValue) backHover = BackHover.Value;
if (BackActive.HasValue) backActive = BackActive.Value;
}
if (loading)
{
Fore = Color.FromArgb(165, Fore);
Color = Color.FromArgb(165, Color);
}
}
void GetColorConfig(out Color Fore, out Color Back, out Color backHover, out Color backActive)
{
if (toggle)
{
if (typeToggle.HasValue) GetColorConfig(typeToggle.Value, out Fore, out Back, out backHover, out backActive);
else GetColorConfig(type, out Fore, out Back, out backHover, out backActive);
if (foreToggle.HasValue) Fore = foreToggle.Value;
if (backToggle.HasValue) Back = backToggle.Value;
if (ToggleBackHover.HasValue) backHover = ToggleBackHover.Value;
if (ToggleBackActive.HasValue) backActive = ToggleBackActive.Value;
if (loading) Back = Color.FromArgb(165, Back);
return;
}
GetColorConfig(type, out Fore, out Back, out backHover, out backActive);
if (fore.HasValue) Fore = fore.Value;
if (back.HasValue) Back = back.Value;
if (BackHover.HasValue) backHover = BackHover.Value;
if (BackActive.HasValue) backActive = BackActive.Value;
if (loading) Back = Color.FromArgb(165, Back);
}
void GetColorConfig(TTypeMini type, out Color Fore, out Color Back, out Color backHover, out Color backActive)
{
switch (type)
{
case TTypeMini.Error:
Back = Style.Db.Error;
Fore = Style.Db.ErrorColor;
backHover = Style.Db.ErrorHover;
backActive = Style.Db.ErrorActive;
break;
case TTypeMini.Success:
Back = Style.Db.Success;
Fore = Style.Db.SuccessColor;
backHover = Style.Db.SuccessHover;
backActive = Style.Db.SuccessActive;
break;
case TTypeMini.Info:
Back = Style.Db.Info;
Fore = Style.Db.InfoColor;
backHover = Style.Db.InfoHover;
backActive = Style.Db.InfoActive;
break;
case TTypeMini.Warn:
Back = Style.Db.Warning;
Fore = Style.Db.WarningColor;
backHover = Style.Db.WarningHover;
backActive = Style.Db.WarningActive;
break;
case TTypeMini.Primary:
default:
Back = Style.Db.Primary;
Fore = Style.Db.PrimaryColor;
backHover = Style.Db.PrimaryHover;
backActive = Style.Db.PrimaryActive;
break;
}
}
#endregion
#endregion
}
}