// 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.ComponentModel;
using System.Drawing;
using System.Drawing.Design;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
namespace AntdUI
{
///
/// Switch 开关
///
/// 开关选择器。
[Description("Switch 开关")]
[ToolboxItem(true)]
[DefaultProperty("Checked")]
[DefaultEvent("CheckedChanged")]
public class Switch : IControl
{
#region 属性
Color? fore;
///
/// 文字颜色
///
[Description("文字颜色"), Category("外观"), DefaultValue(null)]
[Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
public new Color? ForeColor
{
get => fore;
set
{
if (fore == value) fore = value;
fore = value;
Invalidate();
}
}
Color? fill;
///
/// 颜色
///
[Description("颜色"), Category("外观"), DefaultValue(null)]
[Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
public Color? Fill
{
get => fill;
set
{
if (fill == value) return;
fill = value;
Invalidate();
}
}
///
/// 悬停颜色
///
[Description("悬停颜色"), Category("外观"), DefaultValue(null)]
[Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
public Color? FillHover { get; set; }
bool AnimationCheck = false;
float AnimationCheckValue = 0;
bool _checked = false;
///
/// 选中状态
///
[Description("选中状态"), Category("数据"), DefaultValue(false)]
public bool Checked
{
get => _checked;
set
{
if (_checked == value) return;
_checked = value;
CheckedChanged?.Invoke(this, new BoolEventArgs(value));
ThreadCheck?.Dispose();
if (IsHandleCreated && Config.Animation)
{
AnimationCheck = true;
if (value)
{
ThreadCheck = new ITask(this, () =>
{
AnimationCheckValue = AnimationCheckValue.Calculate(0.1F);
if (AnimationCheckValue > 1) { AnimationCheckValue = 1F; return false; }
Invalidate();
return true;
}, 10, () =>
{
AnimationCheck = false;
Invalidate();
});
}
else
{
ThreadCheck = new ITask(this, () =>
{
AnimationCheckValue = AnimationCheckValue.Calculate(-0.1F);
if (AnimationCheckValue <= 0) { AnimationCheckValue = 0F; return false; }
Invalidate();
return true;
}, 10, () =>
{
AnimationCheck = false;
Invalidate();
});
}
}
else AnimationCheckValue = value ? 1F : 0F;
Invalidate();
}
}
///
/// 点击时自动改变选中状态
///
[Description("点击时自动改变选中状态"), Category("行为"), DefaultValue(false)]
public bool AutoCheck { get; set; } = true;
///
/// 波浪大小
///
[Description("波浪大小"), Category("外观"), DefaultValue(4)]
public int WaveSize { get; set; } = 4;
[Description("间距"), Category("外观"), DefaultValue(2)]
public int Gap { get; set; } = 2;
string? _checkedText = null, _unCheckedText = null;
[Description("选中时显示的文本"), Category("外观"), DefaultValue(null)]
public string? CheckedText
{
get => _checkedText;
set
{
if (_checkedText == value) return;
_checkedText = value;
if (_checked) Invalidate();
}
}
[Description("未选中时显示的文本"), Category("外观"), DefaultValue(null)]
public string? UnCheckedText
{
get => _unCheckedText;
set
{
if (_unCheckedText == value) return;
_unCheckedText = value;
if (!_checked) Invalidate();
}
}
#endregion
#region 事件
///
/// Checked 属性值更改时发生
///
[Description("Checked 属性值更改时发生"), Category("行为")]
public event BoolEventHandler? CheckedChanged = null;
#endregion
#region 渲染
protected override void OnPaint(PaintEventArgs e)
{
var rect = ClientRectangle;//绘制控件尺寸
var g = e.Graphics.High();
var rect_read = ReadRectangle;//读取控件尺寸
bool enabled = Enabled;
using (var path = rect_read.RoundPath(rect_read.Height))
{
Color _color = fill ?? Style.Db.Primary;
PaintClick(g, path, rect, rect_read, _color);
using (var brush = new SolidBrush(Style.Db.TextQuaternary))
{
g.FillPath(brush, path);
if (AnimationHover)
{
using (var brush2 = new SolidBrush(Helper.ToColorN(AnimationHoverValue, brush.Color)))
{
g.FillPath(brush2, path);
}
}
else if (ExtraMouseHover) g.FillPath(brush, path);
}
float gap = (int)(Gap * Config.Dpi), gap2 = gap * 2F;
if (AnimationCheck)
{
var alpha = 255 * AnimationCheckValue;
using (var brush = new SolidBrush(Helper.ToColor(alpha, _color)))
{
g.FillPath(brush, path);
}
var dot_rect = new RectangleF(rect_read.X + gap + (rect_read.Width - rect_read.Height) * AnimationCheckValue, rect_read.Y + gap, rect_read.Height - gap2, rect_read.Height - gap2);
using (var brush = new SolidBrush(enabled ? Style.Db.BgBase : Color.FromArgb(200, Style.Db.BgBase)))
{
g.FillEllipse(brush, dot_rect);
}
}
else if (_checked)
{
var colorhover = FillHover ?? Style.Db.PrimaryHover;
using (var brush = new SolidBrush(enabled ? _color : Color.FromArgb(200, _color)))
{
g.FillPath(brush, path);
}
if (AnimationHover)
{
using (var brush2 = new SolidBrush(Helper.ToColorN(AnimationHoverValue, colorhover)))
{
g.FillPath(brush2, path);
}
}
else if (ExtraMouseHover)
{
using (var brush = new SolidBrush(colorhover))
{
g.FillPath(brush, path);
}
}
var dot_rect = new RectangleF(rect_read.X + gap + rect_read.Width - rect_read.Height, rect_read.Y + gap, rect_read.Height - gap2, rect_read.Height - gap2);
using (var brush = new SolidBrush(enabled ? Style.Db.BgBase : Color.FromArgb(200, Style.Db.BgBase)))
{
g.FillEllipse(brush, dot_rect);
}
}
else
{
var dot_rect = new RectangleF(rect_read.X + gap, rect_read.Y + gap, rect_read.Height - gap2, rect_read.Height - gap2);
using (var brush = new SolidBrush(enabled ? Style.Db.BgBase : Color.FromArgb(200, Style.Db.BgBase)))
{
g.FillEllipse(brush, dot_rect);
}
}
// 绘制文本
string? textToRender = Checked ? CheckedText : UnCheckedText;
if (textToRender != null)
{
Color _fore = fore ?? Style.Db.PrimaryColor;
using (var brush = new SolidBrush(_fore))
{
var textSize = g.MeasureString(textToRender, Font);
var textRect = Checked
? new RectangleF(rect_read.X + (rect_read.Width - rect_read.Height + gap2) / 2 - textSize.Width / 2, rect_read.Y + rect_read.Height / 2 - textSize.Height / 2, textSize.Width, textSize.Height)
: new RectangleF(rect_read.X + (rect_read.Height - gap + (rect_read.Width - rect_read.Height + gap) / 2 - textSize.Width / 2), rect_read.Y + rect_read.Height / 2 - textSize.Height / 2, textSize.Width, textSize.Height);
g.DrawStr(textToRender, Font, brush, textRect);
}
}
}
this.PaintBadge(g);
base.OnPaint(e);
}
internal void PaintClick(Graphics g, GraphicsPath path, Rectangle rect, RectangleF rect_read, Color color)
{
if (AnimationClick)
{
float maxw = rect_read.Width + ((rect.Width - rect_read.Width) * AnimationClickValue), maxh = rect_read.Height + ((rect.Height - rect_read.Height) * AnimationClickValue),
alpha = 100 * (1F - AnimationClickValue);
using (var brush = new SolidBrush(Helper.ToColor(alpha, color)))
{
using (var path_click = new RectangleF((rect.Width - maxw) / 2F, (rect.Height - maxh) / 2F, maxw, maxh).RoundPath(maxh))
{
path_click.AddPath(path, false);
g.FillPath(brush, path_click);
}
}
}
}
public override Rectangle ReadRectangle
{
get => ClientRectangle.PaddingRect(Padding, WaveSize * Config.Dpi);
}
public override GraphicsPath RenderRegion
{
get
{
var rect_read = ReadRectangle;
return rect_read.RoundPath(rect_read.Height);
}
}
#endregion
#region 鼠标
protected override void OnClick(EventArgs e)
{
if (AutoCheck) Checked = !_checked;
base.OnClick(e);
}
protected override void OnMouseDown(MouseEventArgs e)
{
Focus();
base.OnMouseDown(e);
}
float AnimationHoverValue = 0;
bool AnimationHover = false;
bool _mouseHover = false;
bool ExtraMouseHover
{
get => _mouseHover;
set
{
if (_mouseHover == value) return;
_mouseHover = value;
var enabled = Enabled;
SetCursor(value && enabled);
if (enabled)
{
if (Config.Animation)
{
ThreadHover?.Dispose();
AnimationHover = true;
if (value)
{
ThreadHover = new ITask(this, () =>
{
AnimationHoverValue = AnimationHoverValue.Calculate(0.1F);
if (AnimationHoverValue > 1) { AnimationHoverValue = 1F; return false; }
Invalidate();
return true;
}, 10, () =>
{
AnimationHover = false;
Invalidate();
});
}
else
{
ThreadHover = new ITask(this, () =>
{
AnimationHoverValue = AnimationHoverValue.Calculate(-0.1F);
if (AnimationHoverValue <= 0) { AnimationHoverValue = 0F; return false; }
Invalidate();
return true;
}, 10, () =>
{
AnimationHover = false;
Invalidate();
});
}
}
else AnimationHoverValue = 255;
Invalidate();
}
}
}
#region 动画
protected override void Dispose(bool disposing)
{
ThreadClick?.Dispose();
ThreadCheck?.Dispose();
ThreadHover?.Dispose();
base.Dispose(disposing);
}
ITask? ThreadHover = null;
ITask? ThreadCheck = null;
ITask? ThreadClick = null;
bool AnimationClick = false;
float AnimationClickValue = 0;
protected override void OnMouseUp(MouseEventArgs e)
{
base.OnMouseUp(e);
if (Config.Animation && e.Button == MouseButtons.Left)
{
ThreadClick?.Dispose();
AnimationClickValue = 0;
AnimationClick = true;
ThreadClick = new ITask(this, () =>
{
if (AnimationClickValue > 0.6) AnimationClickValue = AnimationClickValue.Calculate(0.04F);
else AnimationClickValue = AnimationClickValue.Calculate(0.1F);
if (AnimationClickValue > 1) { AnimationClickValue = 0F; return false; }
Invalidate();
return true;
}, 50, () =>
{
AnimationClick = false;
Invalidate();
});
}
}
#endregion
protected override void OnMouseEnter(EventArgs e)
{
base.OnMouseEnter(e);
ExtraMouseHover = true;
}
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
ExtraMouseHover = false;
}
protected override void OnLeave(EventArgs e)
{
base.OnLeave(e);
ExtraMouseHover = false;
}
#endregion
}
}