// 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
{
///
/// WindowBar 窗口栏
///
/// 窗口栏。
[Description("WindowBar 窗口栏")]
[ToolboxItem(true)]
[Designer(typeof(IControlDesigner))]
public class WindowBar : IControl, IEventListener
{
#region 属性
TAMode mode = TAMode.Auto;
[Description("色彩模式"), Category("外观"), DefaultValue(TAMode.Auto)]
public TAMode Mode
{
get => mode;
set
{
if (mode == value) return;
mode = value;
DisposeBmp();
Invalidate();
}
}
string? text = null;
[Description("文字"), Category("外观"), DefaultValue(null)]
public override string? Text
{
get => text;
set
{
if (text == value) return;
text = value;
Invalidate();
OnTextChanged(EventArgs.Empty);
}
}
string? desc = null;
[Description("副标题"), Category("外观"), DefaultValue(null)]
public string? SubText
{
get => desc;
set
{
if (desc == value) return;
desc = value;
Invalidate();
}
}
int useLeft = 0;
[Description("左侧使用"), Category("外观"), DefaultValue(0)]
public int UseLeft
{
get => useLeft;
set
{
if (useLeft == value) return;
useLeft = value;
Invalidate();
}
}
bool useSystemStyleColor = false;
[Description("使用系统颜色"), Category("外观"), DefaultValue(false)]
public bool UseSystemStyleColor
{
get => useSystemStyleColor;
set
{
if (useSystemStyleColor == value) return;
useSystemStyleColor = value;
DisposeBmp();
Invalidate();
}
}
[Description("点击退出关闭"), Category("行为"), DefaultValue(false)]
public bool CancelButton { get; set; } = false;
#region 图标
bool showicon = true;
[Description("是否显示图标"), Category("外观"), DefaultValue(true)]
public bool ShowIcon
{
get => showicon;
set
{
if (showicon == value) return;
showicon = value;
Invalidate();
}
}
Image? icon = null;
[Description("图标"), Category("外观"), DefaultValue(null)]
public Image? Icon
{
get => icon;
set
{
if (icon == value) return;
icon = value;
Invalidate();
}
}
string? iconSvg = null;
[Description("图标SVG"), Category("外观"), DefaultValue(null)]
public string? IconSvg
{
get => iconSvg;
set
{
if (iconSvg == value) return;
iconSvg = value;
Invalidate();
}
}
#endregion
#region 加载动画
bool loading = false;
int AnimationLoadingValue = 0;
[Description("加载状态"), Category("外观"), DefaultValue(false)]
public bool Loading
{
get => loading;
set
{
if (loading == value) return;
loading = value;
ThreadLoading?.Dispose();
if (loading)
{
ThreadLoading = new ITask(this, () =>
{
AnimationLoadingValue += 6;
if (AnimationLoadingValue > 360) AnimationLoadingValue = 0;
Invalidate();
return loading;
}, 10, () =>
{
Invalidate();
});
}
else Invalidate();
}
}
protected override void Dispose(bool disposing)
{
hove_close.Dispose();
hove_max.Dispose();
hove_min.Dispose();
ThreadLoading?.Dispose();
temp_logo?.Dispose();
temp_min?.Dispose();
temp_max?.Dispose();
temp_restore?.Dispose();
temp_close?.Dispose();
temp_close_hover?.Dispose();
base.Dispose(disposing);
}
ITask? ThreadLoading = null;
#endregion
#region 按钮
bool maximizeBox = true;
///
/// 是否显示最大化按钮
///
[Description("是否显示最大化按钮"), Category("外观"), DefaultValue(true)]
public bool MaximizeBox
{
get => maximizeBox;
set
{
if (maximizeBox == value) return;
maximizeBox = value;
OnSizeChanged(EventArgs.Empty);
Invalidate();
}
}
bool minimizeBox = true;
///
/// 是否显示最小化按钮
///
[Description("是否显示最小化按钮"), Category("外观"), DefaultValue(true)]
public bool MinimizeBox
{
get => minimizeBox;
set
{
if (minimizeBox == value) return;
minimizeBox = value;
OnSizeChanged(EventArgs.Empty);
Invalidate();
}
}
bool isMax = false;
public bool IsMax
{
get => isMax;
set
{
if (isMax == value) return;
isMax = value;
Invalidate();
}
}
#endregion
///
/// 是否可以拖动位置
///
[Description("是否可以拖动位置"), Category("行为"), DefaultValue(true)]
public bool DragMove { get; set; } = true;
[Description("关闭按钮大小"), Category("行为"), DefaultValue(48)]
public int CloseSize { get; set; } = 48;
#region 线条
bool showDivider = false;
///
/// 显示线
///
[Description("显示线"), Category("线"), DefaultValue(false)]
public bool DividerShow
{
get => showDivider;
set
{
if (showDivider == value) return;
showDivider = value;
Invalidate();
}
}
Color? dividerColor;
///
/// 线颜色
///
[Description("线颜色"), Category("线"), DefaultValue(null)]
[Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
public Color? DividerColor
{
get => dividerColor;
set
{
if (dividerColor == value) return;
dividerColor = value;
if (showDivider) Invalidate();
}
}
float dividerthickness = 1F;
///
/// 线厚度
///
[Description("线厚度"), Category("线"), DefaultValue(1F)]
public float DividerThickness
{
get => dividerthickness;
set
{
if (dividerthickness == value) return;
dividerthickness = value;
if (showDivider) Invalidate();
}
}
int dividerMargin = 0;
///
/// 线边距
///
[Description("线边距"), Category("线"), DefaultValue(0)]
public int DividerMargin
{
get => dividerMargin;
set
{
if (dividerMargin == value) return;
dividerMargin = value;
if (showDivider) Invalidate();
}
}
#endregion
#endregion
public override Rectangle DisplayRectangle
{
get => ClientRectangle.PaddingRect(Padding, 0, 0, hasr, 0);
}
readonly StringFormat stringLeft = Helper.SF_ALL(lr: StringAlignment.Near);
#region 渲染
protected override void OnPaint(PaintEventArgs e)
{
var rect_ = ClientRectangle;
var rect = rect_.PaddingRect(Padding, UseLeft, 0, 0, 0);
var g = e.Graphics.High();
var size = g.MeasureString(text ?? Config.NullText, Font);
bool showLeft = false;
Color fore = Style.Db.Text, forebase = Style.Db.TextBase, foreSecondary = Style.Db.TextSecondary,
fillsecondary = Style.Db.FillSecondary;
if (useSystemStyleColor)
{
forebase = ForeColor;
if (mode == TAMode.Dark)
{
fore = Style.rgba(forebase, 0.85F);
foreSecondary = Style.rgba(forebase, 0.65F);
fillsecondary = Style.rgba(forebase, 0.12F);
}
else
{
fore = Style.rgba(forebase, 0.88F);
foreSecondary = Style.rgba(forebase, 0.65F);
fillsecondary = Style.rgba(forebase, 0.06F);
}
}
else if (mode == TAMode.Light)
{
forebase = Color.Black;
fore = Style.rgba(forebase, 0.88F);
foreSecondary = Style.rgba(forebase, 0.65F);
fillsecondary = Style.rgba(forebase, 0.06F);
}
else if (mode == TAMode.Dark)
{
forebase = Color.White;
fore = Style.rgba(forebase, 0.85F);
foreSecondary = Style.rgba(forebase, 0.65F);
fillsecondary = Style.rgba(forebase, 0.12F);
}
int icon_size = (int)size.Height, iocn_xy = (rect.Height - icon_size) / 2;
if (loading || iconSvg != null || icon != null || showicon)
{
var rect_icon = new Rectangle(rect.X + iocn_xy, rect.Y + iocn_xy, icon_size, icon_size);
if (loading)
{
using (var brush = new Pen(Color.FromArgb(170, fore), 3f))
{
brush.StartCap = brush.EndCap = LineCap.Round;
g.DrawArc(brush, rect_icon, AnimationLoadingValue, 100);
}
showLeft = true;
}
else if (iconSvg != null)
{
if (PrintLogo(g, iconSvg, fore, new Rectangle(rect.X + iocn_xy, rect.Y + iocn_xy, icon_size, icon_size))) showLeft = true;
}
if (!showLeft)
{
if (icon != null)
{
g.DrawImage(icon, rect_icon);
showLeft = true;
}
else if (showicon)
{
var form = Parent.FindPARENT();
if (form != null && form.Icon != null)
{
g.DrawIcon(form.Icon, rect_icon);
showLeft = true;
}
}
}
if (showLeft)
{
int use = iocn_xy * 2 + icon_size;
rect = new Rectangle(rect.X + use, rect.Y, rect.Width - use, rect.Height);
}
else if (rect.X == 0) rect = new Rectangle(rect.X + iocn_xy, rect.Y, rect.Width - iocn_xy, rect.Height);
}
else if (rect.X == 0) rect = new Rectangle(rect.X + iocn_xy, rect.Y, rect.Width - iocn_xy, rect.Height);
using (var brush = new SolidBrush(forebase))
{
g.DrawStr(text, Font, brush, rect, stringLeft);
if (desc != null)
{
using (var brushsub = new SolidBrush(foreSecondary))
{
g.DrawStr(desc, Font, brushsub, new RectangleF(rect.X + size.Width, rect.Y, rect.Width - size.Width, rect.Height), stringLeft);
}
}
}
#region 按钮
int btn_size = (int)(size.Height * 1.2F), btn_x = (rect_close.Width - btn_size) / 2, btn_y = (rect_close.Height - btn_size) / 2;
var rect_close_icon = new Rectangle(rect_close.X + btn_x, rect_close.Y + btn_y, btn_size, btn_size);
if (hove_close.Down)
{
using (var brush = new SolidBrush(Style.Db.ErrorActive))
{
g.FillRectangle(brush, rect_close);
}
PrintCloseHover(g, rect_close_icon);
}
else if (hove_close.Animation)
{
using (var brush = new SolidBrush(Helper.ToColor(hove_close.Value, Style.Db.Error)))
{
g.FillRectangle(brush, rect_close);
}
PrintClose(g, fore, rect_close_icon);
g.GetImgExtend(SvgDb.IcoAppClose, rect_close_icon, Helper.ToColor(hove_close.Value, Style.Db.ErrorColor));
}
else if (hove_close.Switch)
{
using (var brush = new SolidBrush(Style.Db.Error))
{
g.FillRectangle(brush, rect_close);
}
PrintCloseHover(g, rect_close_icon);
}
else PrintClose(g, fore, rect_close_icon);
if (maximizeBox)
{
var rect_max_icon = new Rectangle(rect_max.X + btn_x, rect_max.Y + btn_y, btn_size, btn_size);
if (hove_max.Animation)
{
using (var brush = new SolidBrush(Helper.ToColor(hove_max.Value, fillsecondary)))
{
g.FillRectangle(brush, rect_max);
}
}
else if (hove_max.Switch)
{
using (var brush = new SolidBrush(fillsecondary))
{
g.FillRectangle(brush, rect_max);
}
}
if (hove_max.Down)
{
using (var brush = new SolidBrush(fillsecondary))
{
g.FillRectangle(brush, rect_max);
}
}
if (IsMax) PrintRestore(g, fore, rect_max_icon);
else PrintMax(g, fore, rect_max_icon);
}
if (minimizeBox)
{
var rect_min_icon = new Rectangle(rect_min.X + btn_x, rect_min.Y + btn_y, btn_size, btn_size);
if (hove_min.Animation)
{
using (var brush = new SolidBrush(Helper.ToColor(hove_min.Value, fillsecondary)))
{
g.FillRectangle(brush, rect_min);
}
}
else if (hove_min.Switch)
{
using (var brush = new SolidBrush(fillsecondary))
{
g.FillRectangle(brush, rect_min);
}
}
if (hove_min.Down)
{
using (var brush = new SolidBrush(fillsecondary))
{
g.FillRectangle(brush, rect_min);
}
}
PrintMin(g, fore, rect_min_icon);
}
#endregion
if (showDivider)
{
float thickness = dividerthickness * Config.Dpi;
int margin = (int)(dividerMargin * Config.Dpi);
using (var brush = dividerColor.Brush(Style.Db.Split))
{
g.FillRectangle(brush, new RectangleF(rect_.X + margin, rect_.Bottom - thickness, rect_.Width - margin * 2, thickness));
}
}
base.OnPaint(e);
}
#region 渲染帮助
Bitmap? temp_logo = null, temp_min = null, temp_max = null, temp_restore = null, temp_close = null, temp_close_hover = null;
void PrintClose(Graphics g, Color color, Rectangle rect_icon)
{
if (temp_close == null || temp_close.Width != rect_icon.Width)
{
temp_close?.Dispose();
temp_close = SvgExtend.GetImgExtend(SvgDb.IcoAppClose, rect_icon, color);
}
if (temp_close != null) g.DrawImage(temp_close, rect_icon);
}
void PrintCloseHover(Graphics g, Rectangle rect_icon)
{
if (temp_close_hover == null || temp_close_hover.Width != rect_icon.Width)
{
temp_close_hover?.Dispose();
temp_close_hover = SvgExtend.GetImgExtend(SvgDb.IcoAppClose, rect_icon, Style.Db.ErrorColor);
}
if (temp_close_hover != null) g.DrawImage(temp_close_hover, rect_icon);
}
void PrintMax(Graphics g, Color color, Rectangle rect_icon)
{
if (temp_max == null || temp_max.Width != rect_icon.Width)
{
temp_max?.Dispose();
temp_max = SvgExtend.GetImgExtend(SvgDb.IcoAppMax, rect_icon, color);
}
if (temp_max != null) g.DrawImage(temp_max, rect_icon);
}
void PrintRestore(Graphics g, Color color, Rectangle rect_icon)
{
if (temp_restore == null || temp_restore.Width != rect_icon.Width)
{
temp_restore?.Dispose();
temp_restore = SvgExtend.GetImgExtend(SvgDb.IcoAppRestore, rect_icon, color);
}
if (temp_restore != null) g.DrawImage(temp_restore, rect_icon);
}
void PrintMin(Graphics g, Color color, Rectangle rect_icon)
{
if (temp_min == null || temp_min.Width != rect_icon.Width)
{
temp_min?.Dispose();
temp_min = SvgExtend.GetImgExtend(SvgDb.IcoAppMin, rect_icon, color);
}
if (temp_min != null) g.DrawImage(temp_min, rect_icon);
}
bool PrintLogo(Graphics g, string svg, Color color, Rectangle rect_icon)
{
if (temp_logo == null || temp_logo.Width != rect_icon.Width)
{
temp_logo?.Dispose();
temp_logo = SvgExtend.GetImgExtend(svg, rect_icon, color);
}
if (temp_logo != null) { g.DrawImage(temp_logo, rect_icon); return true; }
return false;
}
void DisposeBmp()
{
temp_logo?.Dispose();
temp_min?.Dispose();
temp_max?.Dispose();
temp_restore?.Dispose();
temp_close?.Dispose();
temp_logo = null;
temp_min = null;
temp_max = null;
temp_restore = null;
temp_close = null;
}
#endregion
#endregion
int hasr = 0;
protected override void OnSizeChanged(EventArgs e)
{
var rect = ClientRectangle.PaddingRect(Padding);
if (CloseSize > 0)
{
int btn_size = (maximizeBox || minimizeBox) ? (int)Math.Round(CloseSize * Config.Dpi) : (int)Math.Round((CloseSize - 8) * Config.Dpi);
rect_close = new Rectangle(rect.Right - btn_size, rect.Y, btn_size, rect.Height);
hasr = btn_size;
int left = rect_close.Left;
if (maximizeBox)
{
rect_max = new Rectangle(left - btn_size, rect.Y, btn_size, rect.Height);
left -= btn_size;
hasr += btn_size;
}
if (minimizeBox)
{
rect_min = new Rectangle(left - btn_size, rect.Y, btn_size, rect.Height);
hasr += btn_size;
}
}
else hasr = 0;
if (DragMove)
{
var form = Parent.FindPARENT();
if (form != null)
{
if (form is BaseForm form_win) IsMax = form_win.IsMax;
else IsMax = form.WindowState == FormWindowState.Maximized;
}
}
base.OnSizeChanged(e);
}
#region 动画
ITaskOpacity hove_close, hove_max, hove_min;
public WindowBar() { hove_close = new ITaskOpacity(this); hove_max = new ITaskOpacity(this); hove_min = new ITaskOpacity(this); }
#endregion
#region 鼠标
Rectangle rect_close, rect_max, rect_min;
protected override void OnMouseMove(MouseEventArgs e)
{
bool _close = rect_close.Contains(e.Location), _max = rect_max.Contains(e.Location), _min = rect_min.Contains(e.Location);
if (_close != hove_close.Switch || _max != hove_max.Switch || _min != hove_min.Switch)
{
Color fillsecondary = Style.Db.FillSecondary;
if (mode == TAMode.Light) fillsecondary = Style.rgba(0, 0, 0, 0.06F);
else if (mode == TAMode.Dark) fillsecondary = Style.rgba(255, 255, 255, 0.12F);
hove_max.MaxValue = hove_min.MaxValue = fillsecondary.A;
hove_close.Switch = _close;
hove_max.Switch = _max;
hove_min.Switch = _min;
}
base.OnMouseMove(e);
}
protected override void OnMouseLeave(EventArgs e)
{
hove_close.Switch = hove_max.Switch = hove_min.Switch = false;
base.OnMouseLeave(e);
}
protected override void OnMouseDown(MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
hove_close.Down = rect_close.Contains(e.Location);
hove_max.Down = rect_max.Contains(e.Location);
hove_min.Down = rect_min.Contains(e.Location);
if (hove_close.Down || hove_max.Down || hove_min.Down) return;
if (DragMove)
{
var form = Parent.FindPARENT();
if (form != null)
{
if (e.Clicks > 1)
{
if (maximizeBox)
{
if (form is BaseForm form_win) IsMax = form_win.MaxRestore();
else
{
if (form.WindowState == FormWindowState.Maximized)
{
IsMax = false;
form.WindowState = FormWindowState.Normal;
}
else
{
IsMax = true;
form.WindowState = FormWindowState.Maximized;
}
}
return;
}
}
else
{
if (form is BaseForm form_win) form_win.DraggableMouseDown();
else
{
Vanara.PInvoke.User32.ReleaseCapture();
Vanara.PInvoke.User32.SendMessage(form.Handle, 0x0112, 61456 | 2, IntPtr.Zero);
}
}
}
}
}
base.OnMouseDown(e);
}
protected override void OnMouseUp(MouseEventArgs e)
{
if (hove_close.Down && rect_close.Contains(e.Location)) Parent.FindPARENT()?.Close();
else if (hove_max.Down && rect_max.Contains(e.Location))
{
var form = Parent.FindPARENT();
if (form != null)
{
if (form is BaseForm form_win) IsMax = form_win.MaxRestore();
else
{
if (form.WindowState == FormWindowState.Maximized)
{
IsMax = false;
form.WindowState = FormWindowState.Normal;
}
else
{
IsMax = true;
form.WindowState = FormWindowState.Maximized;
}
}
}
}
else if (hove_min.Down && rect_min.Contains(e.Location))
{
var form = Parent.FindPARENT();
if (form != null) form.WindowState = FormWindowState.Minimized;
}
hove_close.Down = hove_max.Down = hove_min.Down = false;
base.OnMouseUp(e);
}
#region 主题变化
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
this.AddListener();
}
public void HandleEvent(EventType id, object? tag)
{
switch (id)
{
case EventType.THEME:
DisposeBmp();
Invalidate();
break;
case EventType.WINDOW_STATE:
if (tag is bool state) IsMax = state;
break;
}
}
#endregion
#endregion
#region 按钮点击
protected override bool ProcessDialogKey(Keys keyData)
{
if (CancelButton && (keyData & (Keys.Alt | Keys.Control)) == Keys.None)
{
Keys keyCode = keyData & Keys.KeyCode;
switch (keyCode)
{
case Keys.Escape:
Parent.FindPARENT()?.Close();
return true;
}
}
return base.ProcessDialogKey(keyData);
}
#endregion
}
}