// 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
{
///
/// Spin 加载中
///
/// 用于页面和区块的加载中状态。
[Description("Spin 加载中")]
[ToolboxItem(true)]
public class Spin : IControl
{
#region 属性
[Description("颜色"), Category("外观"), DefaultValue(null)]
[Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
public Color? Fill { get; set; }
string? text = null;
///
/// 文本
///
[Description("文本"), Category("外观"), DefaultValue(null)]
public override string? Text
{
get => text;
set
{
if (text == value) return;
text = value;
spin_core.Clear();
Invalidate();
OnTextChanged(EventArgs.Empty);
}
}
#endregion
#region 动画
SpinCore spin_core = new SpinCore();
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
spin_core.Start(this);
}
protected override void OnFontChanged(EventArgs e)
{
spin_core.Clear();
base.OnFontChanged(e);
}
#endregion
protected override void OnPaint(PaintEventArgs e)
{
var rect = ClientRectangle.PaddingRect(Padding);
if (rect.Width == 0 || rect.Height == 0) return;
spin_core.Paint(e.Graphics.High(), rect, text, Fill ?? Style.Db.Primary, null, this);
}
protected override void Dispose(bool disposing)
{
spin_core.Dispose();
base.Dispose(disposing);
}
///
/// 配置
///
public class Config
{
///
/// 文本
///
public string? Text { get; set; }
///
/// 背景颜色
///
public Color? Back { get; set; }
///
/// 颜色
///
public Color? Color { get; set; }
///
/// 字体
///
public Font? Font { get; set; }
///
/// 圆角
///
public int? Radius { get; set; }
}
#region 静态方法
///
/// Spin 加载中
///
/// 控件主体
/// 需要等待的委托
/// 运行结束后的回调
public static void open(Control control, Action action, Action? end = null)
{
open(control, new Config(), action, end);
}
///
/// Spin 加载中
///
/// 控件主体
/// 加载文本
/// 需要等待的委托
/// 运行结束后的回调
public static void open(Control control, string text, Action action, Action? end = null)
{
open(control, new Config { Text = text }, action, end);
}
///
/// Spin 加载中
///
/// 控件主体
/// 自定义配置
/// 需要等待的委托
/// 运行结束后的回调
public static void open(Control control, Config config, Action action, Action? end = null)
{
var parent = control.FindPARENT();
if (parent is LayeredFormModal model)
{
model.Load += (a, b) =>
{
control.BeginInvoke(new Action(() =>
{
open_core(control, parent, config, action, end);
}));
};
return;
}
else if (parent is LayeredFormDrawer drawer)
{
drawer.LoadOK = () =>
{
control.BeginInvoke(new Action(() =>
{
open_core(control, parent, config, action, end);
}));
};
return;
}
else if (control.InvokeRequired)
{
control.BeginInvoke(new Action(() =>
{
open_core(control, parent, config, action, end);
}));
return;
}
open_core(control, parent, config, action, end);
}
static void open_core(Control control, Form? parent, Config config, Action action, Action? end = null)
{
var frm = new SpinForm(control, parent, config);
frm.Show(control);
ITask.Run(() =>
{
try
{
action();
}
catch { }
frm.Invoke(new Action(() =>
{
frm.Dispose();
}));
}, end);
}
#endregion
}
internal class SpinCore : IDisposable
{
ITask? thread = null;
float LineWidth = 6, LineAngle = 0;
float prog_size = 0;
public void Clear()
{
prog_size = 0;
}
public void Start(IControl control)
{
bool ProgState = false;
thread = new ITask(control, () =>
{
if (ProgState)
{
LineAngle = LineAngle.Calculate(9F);
LineWidth = LineWidth.Calculate(0.6F);
if (LineWidth > 75) ProgState = false;
}
else
{
LineAngle = LineAngle.Calculate(9.6F);
LineWidth = LineWidth.Calculate(-0.6F);
if (LineWidth < 6) ProgState = true;
}
if (LineAngle >= 360) LineAngle = 0;
control.Invalidate();
return true;
}, 10);
}
public void Start(ILayeredForm control)
{
bool ProgState = false;
thread = new ITask(control, () =>
{
if (ProgState)
{
LineAngle = LineAngle.Calculate(9F);
LineWidth = LineWidth.Calculate(0.6F);
if (LineWidth > 75) ProgState = false;
}
else
{
LineAngle = LineAngle.Calculate(9.6F);
LineWidth = LineWidth.Calculate(-0.6F);
if (LineWidth < 6) ProgState = true;
}
if (LineAngle >= 360) LineAngle = 0;
control.Print();
return true;
}, 10);
}
readonly StringFormat s_f = Helper.SF_ALL();
public void Paint(Graphics g, Rectangle rect, string? text, Color color, Font? font, Control control)
{
if (prog_size == 0) prog_size = g.MeasureString(text ?? Config.NullText, font ?? control.Font).Height;
float rprog_size = prog_size * 1.4F, size = prog_size * .1F, size2 = prog_size / 2F;
var rect_prog = new RectangleF(rect.X + (rect.Width - rprog_size) / 2, rect.Y + (rect.Height - rprog_size) / 2, rprog_size, rprog_size);
if (text != null)
{
var y = rect_prog.Bottom;
rect_prog.Offset(0, -size2);
using (var brush = new SolidBrush(control.ForeColor))
{
g.DrawStr(text, font ?? control.Font, brush, new RectangleF(rect.X, y, rect.Width, prog_size), s_f);
}
}
using (var brush = new Pen(color, size))
{
brush.StartCap = brush.EndCap = LineCap.Round;
try
{
g.DrawArc(brush, rect_prog, LineAngle, LineWidth * 3.6F);
}
catch { }
}
}
public void Dispose()
{
thread?.Dispose();
}
}
internal class SpinForm : ILayeredFormOpacity
{
Control control;
Form? parent = null;
Spin.Config config;
public SpinForm(Control _control, Form? _parent, Spin.Config _config)
{
control = _control;
parent = _parent;
Font = _control.Font;
config = _config;
_control.SetTopMost(Handle);
SetSize(_control.Size);
SetLocation(_control.PointToScreen(Point.Empty));
if (_config.Radius.HasValue) Radius = _config.Radius.Value;
else if (_control is IControl icontrol) gpath = icontrol.RenderRegion;
}
GraphicsPath? gpath = null;
int Radius = 0;
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
spin_core.Start(this);
if (parent != null)
{
parent.LocationChanged += Parent_LocationChanged;
parent.SizeChanged += Parent_SizeChanged;
}
}
private void Parent_LocationChanged(object? sender, EventArgs e)
{
SetLocation(control.PointToScreen(Point.Empty));
}
private void Parent_SizeChanged(object? sender, EventArgs e)
{
SetLocation(control.PointToScreen(Point.Empty));
SetSize(control.Size);
if (!config.Radius.HasValue && control is IControl icontrol)
{
gpath?.Dispose();
gpath = icontrol.RenderRegion;
}
}
#region 渲染
SpinCore spin_core = new SpinCore();
public override Bitmap PrintBit()
{
var rect = TargetRectXY;
var original_bmp = new Bitmap(rect.Width, rect.Height);
using (var g = Graphics.FromImage(original_bmp).HighLay())
{
try
{
using (var brush = new SolidBrush(config.Back ?? Color.FromArgb(100, Style.Db.TextBase)))
{
if (gpath != null) g.FillPath(brush, gpath);
else if (Radius > 0)
{
using (var path = rect.RoundPath(Radius)) { g.FillPath(brush, path); }
}
else g.FillRectangle(brush, rect);
}
}
catch { }
spin_core.Paint(g, rect, config.Text, config.Color ?? Style.Db.Primary, config.Font, this);
}
return original_bmp;
}
#endregion
protected override void Dispose(bool disposing)
{
spin_core.Dispose();
if (parent != null)
{
parent.LocationChanged -= Parent_LocationChanged;
parent.SizeChanged -= Parent_SizeChanged;
}
gpath?.Dispose();
base.Dispose(disposing);
if (control == null) return;
}
}
}