// 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.Drawing;
using System.Drawing.Drawing2D;
using System.Threading;
using System.Windows.Forms;
namespace AntdUI
{
///
/// Notification 通知提醒框
///
/// 全局展示通知提醒信息。
public static class Notification
{
///
/// 成功通知
///
/// 窗口
/// 标题
/// 内容
/// 位置
/// 字体
/// 自动关闭时间(秒)0等于不关闭
public static void success(Form form, string title, string text, TAlignFrom align = TAlignFrom.TR, Font? font = null, int? autoClose = null)
{
open(new Config(form, title, text, TType.Success, align, font, autoClose));
}
///
/// 信息通知
///
/// 窗口
/// 标题
/// 内容
/// 位置
/// 字体
/// 自动关闭时间(秒)0等于不关闭
public static void info(Form form, string title, string text, TAlignFrom align = TAlignFrom.TR, Font? font = null, int? autoClose = null)
{
open(new Config(form, title, text, TType.Info, align, font, autoClose));
}
///
/// 警告通知
///
/// 窗口
/// 标题
/// 内容
/// 位置
/// 字体
/// 自动关闭时间(秒)0等于不关闭
public static void warn(Form form, string title, string text, TAlignFrom align = TAlignFrom.TR, Font? font = null, int? autoClose = null)
{
open(new Config(form, title, text, TType.Warn, align, font, autoClose));
}
///
/// 失败通知
///
/// 窗口
/// 标题
/// 内容
/// 位置
/// 字体
/// 自动关闭时间(秒)0等于不关闭
public static void error(Form form, string title, string text, TAlignFrom align = TAlignFrom.TR, Font? font = null, int? autoClose = null)
{
open(new Config(form, title, text, TType.Error, align, font, autoClose));
}
///
/// 普通通知
///
/// 窗口
/// 标题
/// 内容
/// 位置
/// 字体
/// 自动关闭时间(秒)0等于不关闭
public static void open(Form form, string title, string text, TAlignFrom align = TAlignFrom.TR, Font? font = null, int? autoClose = null)
{
open(new Config(form, title, text, TType.None, align, font, autoClose));
}
///
/// Notification 通知提醒框
///
/// 配置
public static void open(this Config config) => MsgQueue.Add(config);
///
/// 关闭全部
///
public static void close_all()
{
var close_list = new System.Collections.Generic.List(10);
foreach (var it in ILayeredFormAnimate.list)
{
foreach (var item in it.Value)
{
if (item is NotificationFrm notification) close_list.Add(notification);
}
}
if (close_list.Count == 0) return;
foreach (var it in close_list) it.CloseMe(false);
}
///
/// 关闭指定id
///
public static void close_id(string id)
{
MsgQueue.volley.Add("N" + id);
var close_list = new System.Collections.Generic.List();
foreach (var it in ILayeredFormAnimate.list)
{
foreach (var item in it.Value)
{
if (item is NotificationFrm notification && notification.config.ID == id) close_list.Add(notification);
}
}
if (close_list.Count == 0) return;
foreach (var it in close_list) it.CloseMe(false);
}
///
/// 配置
///
public class Config
{
public Config(Form _form, string _title, string _text, TType _icon, TAlignFrom _align)
{
Form = _form;
Title = _title;
Text = _text;
Align = _align;
Icon = _icon;
}
public Config(Form _form, string _title, string _text, TType _icon, TAlignFrom _align, Font? _font)
{
Form = _form;
Font = _font;
Title = _title;
Text = _text;
Align = _align;
Icon = _icon;
}
public Config(Form _form, string _title, string _text, TType _icon, TAlignFrom _align, Font? _font, int? autoClose)
{
Form = _form;
Font = _font;
Title = _title;
Text = _text;
Align = _align;
Icon = _icon;
if (autoClose.HasValue) AutoClose = autoClose.Value;
}
///
/// ID
///
public string? ID { get; set; }
///
/// 所属窗口
///
public Form Form { get; set; }
///
/// 标题
///
public string Title { get; set; }
///
/// 标题字体
///
public Font? FontTitle { get; set; }
///
/// 标题字体样式
///
public FontStyle? FontStyleTitle { get; set; }
///
/// 文本
///
public string Text { get; set; }
///
/// 图标
///
public TType Icon { get; set; }
///
/// 字体
///
public Font? Font { get; set; }
///
/// 方向
///
public TAlignFrom Align { get; set; }
///
/// 圆角
///
public int Radius { get; set; } = 10;
///
/// 自动关闭时间(秒)0等于不关闭
///
public int AutoClose { get; set; } = 6;
///
/// 是否显示关闭图标
///
public bool CloseIcon { get; set; } = true;
///
/// 是否置顶
///
public bool TopMost { get; set; }
///
/// 超链接回调
///
public ConfigLink? Link { get; set; }
///
/// 关闭回调
///
public Action? OnClose { get; set; }
///
/// 边距
///
public Size Padding { get; set; } = new Size(24, 20);
///
/// 弹出在窗口
///
public bool ShowInWindow { get; set; } = false;
}
public class ConfigLink
{
public ConfigLink(string text, Action call)
{
Text = text;
Call = call;
}
///
/// 连接文本
///
public string Text { get; set; }
///
/// 点击回调
///
public Action Call { get; set; }
}
}
internal class NotificationFrm : ILayeredFormAnimate
{
Font font_title;
internal Notification.Config config;
int shadow_size = 10;
public NotificationFrm(Notification.Config _config)
{
config = _config;
if (config.TopMost) Helper.SetTopMost(Handle);
else config.Form.SetTopMost(Handle);
shadow_size = (int)(shadow_size * Config.Dpi);
if (config.Font != null) Font = config.Font;
else if (Config.Font != null) Font = Config.Font;
else Font = config.Form.Font;
font_title = config.FontTitle ?? new Font(Font.FontFamily, Font.Size * 1.14F, config.FontStyleTitle ?? Font.Style);
Icon = config.Form.Icon;
Helper.GDI(g =>
{
SetSize(RenderMeasure(g, shadow_size));
});
close_button = new ITaskOpacity(this);
}
protected override void Dispose(bool disposing)
{
config.OnClose?.Invoke();
config.OnClose = null;
close_button.Dispose();
base.Dispose(disposing);
}
internal override TAlignFrom Align => config.Align;
internal override bool ActiveAnimation => false;
public bool IInit()
{
if (SetPosition(config.Form, config.ShowInWindow || Config.ShowInWindowByNotification)) return true;
if (config.AutoClose > 0)
{
ITask.Run(() =>
{
Thread.Sleep(config.AutoClose * 1000);
CloseMe(true);
});
}
PlayAnimation();
return false;
}
#region 渲染
readonly StringFormat s_f = Helper.SF_ALL(), s_f_left = Helper.SF_ALL(lr: StringAlignment.Near), s_f_left_left = Helper.SF(StringAlignment.Near, StringAlignment.Near);
public override Bitmap PrintBit()
{
var rect = TargetRectXY;
var rect_read = rect.PaddingRect(Padding, shadow_size);
Bitmap original_bmp = new Bitmap(rect.Width, rect.Height);
using (var g = Graphics.FromImage(original_bmp).High())
{
using (var path = DrawShadow(g, rect, rect_read))
{
using (var brush = new SolidBrush(Style.Db.BgElevated))
{
g.FillPath(brush, path);
}
}
if (config.Icon != TType.None) g.PaintIcons(config.Icon, rect_icon);
if (config.CloseIcon)
{
if (close_button.Animation)
{
using (var brush = new SolidBrush(Helper.ToColor(close_button.Value, Style.Db.FillSecondary)))
{
using (var path = rect_close.RoundPath((int)(4 * Config.Dpi)))
{
g.FillPath(brush, path);
}
}
g.PaintIconClose(rect_close, Style.Db.Text, .6F);
}
else if (close_button.Switch)
{
using (var brush = new SolidBrush(Style.Db.FillSecondary))
{
using (var path = rect_close.RoundPath((int)(4 * Config.Dpi)))
{
g.FillPath(brush, path);
}
}
g.PaintIconClose(rect_close, Style.Db.Text, .6F);
}
else g.PaintIconClose(rect_close, Style.Db.TextTertiary, .6F);
}
using (var brush = new SolidBrush(Style.Db.TextBase))
{
g.DrawStr(config.Title, font_title, brush, rect_title, s_f_left);
g.DrawStr(config.Text, Font, brush, rect_txt, s_f_left_left);
}
if (config.Link != null)
{
using (var brush = new SolidBrush(Style.Db.Primary))
using (var pen = new Pen(Style.Db.Primary, 1F * Config.Dpi))
{
g.DrawStr(config.Link.Text, Font, brush, rect_link_text, s_f);
g.DrawLines(pen, TAlignMini.Right.TriangleLines(rect_links));
}
}
}
return original_bmp;
}
Bitmap? shadow_temp = null;
///
/// 绘制阴影
///
/// GDI
/// 客户区域
/// 真实区域
GraphicsPath DrawShadow(Graphics g, Rectangle rect_client, Rectangle rect_read)
{
var path = rect_read.RoundPath((int)(config.Radius * Config.Dpi));
if (Config.ShadowEnabled)
{
if (shadow_temp == null || (shadow_temp.Width != rect_client.Width || shadow_temp.Height != rect_client.Height))
{
shadow_temp?.Dispose();
shadow_temp = path.PaintShadow(rect_client.Width, rect_client.Height);
}
g.DrawImage(shadow_temp, rect_client, 0.2F);
}
return path;
}
Rectangle rect_icon, rect_title, rect_txt, rect_close;
Rectangle rect_link_text, rect_links;
Size RenderMeasure(Graphics g, int shadow)
{
int shadow2 = shadow * 2;
float dpi = Config.Dpi;
var size_title = g.MeasureString(config.Title, font_title, 10000, s_f_left).Size();
int paddingx = (int)(config.Padding.Width * dpi), paddingy = (int)(config.Padding.Height * dpi), t_max_width = (int)Math.Ceiling(360 * dpi);
int sp = (int)(8 * dpi), close_size = (int)Math.Ceiling(22F * dpi);
if (size_title.Width > t_max_width)
{
t_max_width = size_title.Width;
if (config.CloseIcon) t_max_width += close_size + sp;
}
var size_desc = g.MeasureString(config.Text, Font, t_max_width, s_f_left).Size();
int width_title = (config.CloseIcon ? size_title.Width + close_size + sp : size_title.Width), width_desc = size_desc.Width;
int max_width = width_desc > width_title ? width_desc : width_title;
if (config.Icon == TType.None)
{
rect_title = new Rectangle(shadow + paddingx, shadow + paddingy, max_width, size_title.Height);
int h = size_title.Height;
if (config.CloseIcon) rect_close = new Rectangle(rect_title.Right - close_size, rect_title.Y, close_size, close_size);
rect_txt = new Rectangle(shadow + paddingx, rect_title.Bottom + sp, rect_title.Width, size_desc.Height);
if (size_desc.Height > 0) h += size_desc.Height + sp;
if (config.Link != null)
{
var size_link = g.MeasureString(config.Link.Text, Font, 10000, s_f).Size();
rect_link_text = new Rectangle(rect_title.X, rect_txt.Bottom + sp, size_link.Width, size_link.Height);
rect_links = new Rectangle(rect_link_text.Right, rect_link_text.Y, rect_link_text.Height, rect_link_text.Height);
h += size_link.Height + sp;
}
return new Size(max_width + paddingx * 2 + shadow2, h + paddingy * 2 + shadow2);
}
else
{
int icon_size = (int)Math.Ceiling(size_title.Height * 1.14F), icon_sp = icon_size / 2;
rect_icon = new Rectangle(shadow + paddingx, shadow + paddingy, icon_size, icon_size);
rect_title = new Rectangle(rect_icon.X + rect_icon.Width + icon_sp, shadow + paddingy, max_width, icon_size);
int h = icon_size;
rect_txt = new Rectangle(rect_title.X, rect_title.Bottom + sp, rect_title.Width, size_desc.Height);
if (config.CloseIcon) rect_close = new Rectangle(rect_title.Right - close_size, rect_title.Y, close_size, close_size);
if (size_desc.Height > 0) h += size_desc.Height + sp;
if (config.Link != null)
{
var size_link = g.MeasureString(config.Link.Text, Font, 10000, s_f).Size();
rect_link_text = new Rectangle(rect_title.X, rect_txt.Bottom + sp, size_link.Width, size_link.Height);
rect_links = new Rectangle(rect_link_text.Right, rect_link_text.Y, rect_link_text.Height, rect_link_text.Height);
h += size_link.Height + sp;
}
return new Size(max_width + icon_size + icon_sp + paddingx * 2 + shadow2, h + paddingy * 2 + shadow2);
}
}
#endregion
#region 鼠标
ITaskOpacity close_button;
protected override void OnMouseMove(MouseEventArgs e)
{
if (config.CloseIcon)
{
close_button.MaxValue = Style.Db.FillSecondary.A;
close_button.Switch = rect_close.Contains(e.Location);
SetCursor(close_button.Switch);
if (close_button.Switch)
{
base.OnMouseMove(e);
return;
}
}
if (config.Link != null) SetCursor(rect_link_text.Contains(e.Location));
base.OnMouseMove(e);
}
protected override void OnMouseClick(MouseEventArgs e)
{
if (config.Link != null && rect_link_text.Contains(e.Location)) config.Link.Call();
CloseMe(false);
base.OnMouseClick(e);
}
#endregion
}
}