// 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
{
///
/// ColorPicker 颜色选择器
///
/// 提供颜色选取的组件。
[Description("ColorPicker 颜色选择器")]
[ToolboxItem(true)]
[DefaultProperty("Value")]
[DefaultEvent("ValueChanged")]
public class ColorPicker : IControl, SubLayeredForm
{
public ColorPicker()
{
base.BackColor = Color.Transparent;
}
#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();
}
}
#region 背景
internal Color? back;
///
/// 背景颜色
///
[Description("背景颜色"), Category("外观"), DefaultValue(null)]
[Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
public new Color? BackColor
{
get => back;
set
{
if (back == value) return;
back = value;
Invalidate();
}
}
#endregion
#region 边框
internal float borderWidth = 1F;
///
/// 边框宽度
///
[Description("边框宽度"), Category("边框"), DefaultValue(1F)]
public float BorderWidth
{
get => borderWidth;
set
{
if (borderWidth == value) return;
borderWidth = value;
Invalidate();
}
}
///
/// 边框颜色
///
internal Color? borderColor;
[Description("边框颜色"), Category("边框"), DefaultValue(null)]
[Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
public Color? BorderColor
{
get => borderColor;
set
{
if (borderColor == value) return;
borderColor = value;
Invalidate();
}
}
///
/// 悬停边框颜色
///
[Description("悬停边框颜色"), Category("边框"), DefaultValue(null)]
[Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
public Color? BorderHover { get; set; }
///
/// 激活边框颜色
///
[Description("激活边框颜色"), Category("边框"), DefaultValue(null)]
[Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
public Color? BorderActive { get; set; }
#endregion
///
/// 波浪大小
///
[Description("波浪大小"), Category("外观"), DefaultValue(4)]
public int WaveSize { get; set; } = 4;
internal int radius = 6;
///
/// 圆角
///
[Description("圆角"), Category("外观"), DefaultValue(6)]
public int Radius
{
get => radius;
set
{
if (radius == value) return;
radius = value;
Invalidate();
}
}
bool round = false;
///
/// 圆角样式
///
[Description("圆角样式"), Category("外观"), DefaultValue(false)]
public bool Round
{
get => round;
set
{
if (round == value) return;
round = value;
Invalidate();
}
}
Color _value = Style.Db.Primary;
[Description("颜色的值"), Category("值"), DefaultValue(typeof(Color), "Transparent")]
public Color Value
{
get => _value;
set
{
if (value == _value) return;
_value = value;
ValueChanged?.Invoke(this, new ColorEventArgs(value));
if (BeforeAutoSize()) Invalidate();
}
}
bool showText = false;
///
/// 显示Hex文字
///
[Description("显示Hex文字"), Category("值"), DefaultValue(false)]
public bool ShowText
{
get => showText;
set
{
if (showText == value) return;
showText = value;
if (BeforeAutoSize()) Invalidate();
}
}
///
/// 禁用透明度
///
[Description("禁用透明度"), Category("行为"), DefaultValue(false)]
public bool DisabledAlpha { get; set; } = false;
TColorMode mode = TColorMode.Hex;
///
/// 颜色模式
///
[Description("颜色模式"), Category("行为"), DefaultValue(TColorMode.Hex)]
public TColorMode Mode
{
get => mode;
set
{
if (mode == value) return;
mode = value;
if (BeforeAutoSize()) Invalidate();
}
}
///
/// 触发下拉的行为
///
[Description("触发下拉的行为"), Category("行为"), DefaultValue(Trigger.Click)]
public Trigger Trigger { get; set; } = Trigger.Click;
///
/// 菜单弹出位置
///
[Description("菜单弹出位置"), Category("行为"), DefaultValue(TAlignFrom.BL)]
public TAlignFrom Placement { get; set; } = TAlignFrom.BL;
///
/// 下拉箭头是否显示
///
[Description("下拉箭头是否显示"), Category("外观"), DefaultValue(true)]
public bool DropDownArrow { get; set; } = true;
bool joinLeft = false;
///
/// 连接左边
///
[Description("连接左边"), Category("外观"), DefaultValue(false)]
public bool JoinLeft
{
get => joinLeft;
set
{
if (joinLeft == value) return;
joinLeft = value;
if (BeforeAutoSize()) Invalidate();
}
}
bool joinRight = false;
///
/// 连接右边
///
[Description("连接右边"), Category("外观"), DefaultValue(false)]
public bool JoinRight
{
get => joinRight;
set
{
if (joinRight == value) return;
joinRight = value;
if (BeforeAutoSize()) Invalidate();
}
}
#endregion
#region 事件
///
/// Value 属性值更改时发生
///
[Description("Value 属性值更改时发生"), Category("行为")]
public event ColorEventHandler? ValueChanged = null;
///
/// Value格式化时发生
///
[Description("Value格式化时发生"), Category("行为")]
public event ColorFormatEventHandler? ValueFormatChanged;
#endregion
#region 渲染
internal StringFormat stringLeft = Helper.SF_NoWrap(lr: StringAlignment.Near);
protected override void OnPaint(PaintEventArgs e)
{
var rect = ClientRectangle.PaddingRect(Padding);
var g = e.Graphics.High();
var rect_read = ReadRectangle;
float _radius = round ? rect_read.Height : radius * Config.Dpi;
bool enabled = Enabled;
using (var path = Path(rect_read, _radius))
{
Color _fore = fore ?? Style.Db.Text, _back = back ?? Style.Db.BgContainer,
_border = borderColor ?? Style.Db.BorderColor,
_borderHover = BorderHover ?? Style.Db.PrimaryHover,
_borderActive = BorderActive ?? Style.Db.Primary;
PaintClick(g, path, rect, _borderActive, radius);
int size_color = (int)(rect_read.Height * 0.75F);
if (enabled)
{
using (var brush = new SolidBrush(_back))
{
g.FillPath(brush, path);
}
if (borderWidth > 0)
{
if (AnimationHover)
{
using (var brush = new Pen(_border, borderWidth))
{
g.DrawPath(brush, path);
}
using (var brush = new Pen(Helper.ToColor(AnimationHoverValue, _borderHover), borderWidth))
{
g.DrawPath(brush, path);
}
}
else if (ExtraMouseDown)
{
using (var brush = new Pen(_borderActive, borderWidth))
{
g.DrawPath(brush, path);
}
}
else if (ExtraMouseHover)
{
using (var brush = new Pen(_borderHover, borderWidth))
{
g.DrawPath(brush, path);
}
}
else
{
using (var brush = new Pen(_border, borderWidth))
{
g.DrawPath(brush, path);
}
}
}
}
else
{
using (var brush = new SolidBrush(Style.Db.FillTertiary))
{
g.FillPath(brush, path);
}
if (borderWidth > 0)
{
using (var brush = new Pen(_border, borderWidth))
{
g.DrawPath(brush, path);
}
}
}
var r = _radius * 0.75F;
if (showText)
{
int gap = (rect_read.Height - size_color) / 2;
var rect_color = new Rectangle(rect_read.X + gap, rect_read.Y + gap, size_color, size_color);
using (var path_color = rect_color.RoundPath(r))
{
PaintAlpha(g, r, rect_color);
using (var brush = new SolidBrush(_value))
{
g.FillPath(brush, path_color);
}
}
using (var brush = new SolidBrush(_fore))
{
var wi = gap * 2 + size_color;
if (ValueFormatChanged == null)
{
switch (mode)
{
case TColorMode.Hex:
g.DrawStr("#" + _value.ToHex(), Font, brush, new Rectangle(rect_read.X + wi, rect_read.Y, rect_read.Width - wi, rect_read.Height), stringLeft);
break;
case TColorMode.Rgb:
if (_value.A == 255) g.DrawStr(string.Format("rgb({0},{1},{2})", _value.R, _value.G, _value.B), Font, brush, new Rectangle(rect_read.X + wi, rect_read.Y, rect_read.Width - wi, rect_read.Height), stringLeft);
else g.DrawStr(string.Format("rgba({0},{1},{2},{3})", _value.R, _value.G, _value.B, Math.Round(_value.A / 255D, 2)), Font, brush, new Rectangle(rect_read.X + wi, rect_read.Y, rect_read.Width - wi, rect_read.Height), stringLeft);
break;
}
}
else g.DrawStr(ValueFormatChanged(this, new ColorEventArgs(_value)), Font, brush, new Rectangle(rect_read.X + wi, rect_read.Y, rect_read.Width - wi, rect_read.Height), stringLeft);
}
}
else
{
int size_colorw = (int)(rect_read.Width * 0.75F);
var rect_color = new Rectangle(rect_read.X + (rect_read.Width - size_colorw) / 2, rect_read.Y + (rect_read.Height - size_color) / 2, size_colorw, size_color);
using (var path_color = rect_color.RoundPath(r))
{
PaintAlpha(g, r, rect_color);
using (var brush = new SolidBrush(_value))
{
g.FillPath(brush, path_color);
}
}
}
}
this.PaintBadge(g);
base.OnPaint(e);
}
Bitmap? bmp_alpha = null;
void PaintAlpha(Graphics g, float radius, Rectangle rect)
{
if (bmp_alpha == null || bmp_alpha.Width != rect.Width || bmp_alpha.Height != rect.Height)
{
bmp_alpha?.Dispose();
bmp_alpha = new Bitmap(rect.Width, rect.Height);
using (var tmp = new Bitmap(rect.Width, rect.Height))
{
using (var g2 = Graphics.FromImage(tmp).High())
{
PaintAlpha(g2, rect);
}
using (var g2 = Graphics.FromImage(bmp_alpha).High())
{
Helper.PaintImg(g2, new Rectangle(0, 0, rect.Width, rect.Height), tmp, TFit.Fill, radius, false);
}
}
}
g.DrawImage(bmp_alpha, rect);
}
void PaintAlpha(Graphics g, Rectangle rect)
{
int u_y = 0, size = rect.Height / 4;
bool ad = false;
using (var brush = new SolidBrush(Style.Db.FillSecondary))
{
while (u_y < rect.Height)
{
int u_x = 0;
bool adsub = ad;
while (u_x < rect.Width)
{
if (adsub) g.FillRectangle(brush, new Rectangle(u_x, u_y, size, size));
u_x += size;
adsub = !adsub;
}
u_y += size;
ad = !ad;
}
}
}
#region 渲染帮助
internal GraphicsPath Path(Rectangle rect_read, float _radius)
{
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);
}
#region 点击动画
internal void PaintClick(Graphics g, GraphicsPath path, Rectangle rect, Color color, float radius)
{
if (AnimationFocus)
{
if (AnimationFocusValue > 0)
{
using (var brush = new SolidBrush(Helper.ToColor(AnimationFocusValue, color)))
{
using (var path_click = rect.RoundPath(radius, round))
{
path_click.AddPath(path, false);
g.FillPath(brush, path_click);
}
}
}
}
else if (ExtraMouseDown && WaveSize > 0)
{
using (var brush = new SolidBrush(Color.FromArgb(30, color)))
{
using (var path_click = rect.RoundPath(radius, round))
{
path_click.AddPath(path, false);
g.FillPath(brush, path_click);
}
}
}
}
#endregion
#endregion
public override Rectangle ReadRectangle
{
get => ClientRectangle.PaddingRect(Padding).ReadRect((WaveSize + borderWidth / 2F) * Config.Dpi, JoinLeft, JoinRight);
}
public override GraphicsPath RenderRegion
{
get
{
var rect_read = ReadRectangle;
float _radius = round ? rect_read.Height : radius;
return Path(rect_read, _radius);
}
}
#endregion
#region 事件/焦点
bool AnimationFocus = false;
int AnimationFocusValue = 0;
#endregion
#region 鼠标
internal bool _mouseDown = false;
internal bool ExtraMouseDown
{
get => _mouseDown;
set
{
if (_mouseDown == value) return;
_mouseDown = value;
if (Config.Animation && WaveSize > 0)
{
ThreadFocus?.Dispose();
AnimationFocus = true;
if (value)
{
ThreadFocus = new ITask(this, () =>
{
AnimationFocusValue += 4;
if (AnimationFocusValue > 30) return false;
Invalidate();
return true;
}, 20, () =>
{
AnimationFocus = false;
Invalidate();
});
}
else
{
ThreadFocus = new ITask(this, () =>
{
AnimationFocusValue -= 4;
if (AnimationFocusValue < 1) return false;
Invalidate();
return true;
}, 20, () =>
{
AnimationFocus = false;
Invalidate();
});
}
}
else Invalidate();
}
}
internal int AnimationHoverValue = 0;
internal bool AnimationHover = false;
internal bool _mouseHover = false;
internal bool ExtraMouseHover
{
get => _mouseHover;
set
{
if (_mouseHover == value) return;
_mouseHover = value;
if (Enabled)
{
if (Config.Animation && !ExtraMouseDown)
{
ThreadHover?.Dispose();
AnimationHover = true;
if (value)
{
ThreadHover = new ITask(this, () =>
{
AnimationHoverValue += 20;
if (AnimationHoverValue > 255) { AnimationHoverValue = 255; return false; }
Invalidate();
return true;
}, 10, () =>
{
AnimationHover = false;
Invalidate();
});
}
else
{
ThreadHover = new ITask(this, () =>
{
AnimationHoverValue -= 20;
if (AnimationHoverValue < 1) { AnimationHoverValue = 0; return false; }
Invalidate();
return true;
}, 10, () =>
{
AnimationHover = false;
Invalidate();
});
}
}
else AnimationHoverValue = 255;
Invalidate();
}
}
}
protected override void OnMouseEnter(EventArgs e)
{
SetCursor(true);
base.OnMouseEnter(e);
ExtraMouseHover = true;
if (Trigger == Trigger.Hover && subForm == null) ClickDown();
}
protected override void OnMouseLeave(EventArgs e)
{
SetCursor(false);
base.OnMouseLeave(e);
ExtraMouseHover = false;
}
protected override void OnLeave(EventArgs e)
{
SetCursor(false);
base.OnLeave(e);
ExtraMouseHover = false;
}
LayeredFormColorPicker? subForm = null;
public ILayeredForm? SubForm() => subForm;
protected override void OnMouseClick(MouseEventArgs e)
{
if (e.Button == MouseButtons.Left && Trigger == Trigger.Click)
{
ImeMode = ImeMode.Disable;
Focus();
ClickDown();
}
base.OnMouseClick(e);
}
void ClickDown()
{
ExtraMouseDown = true;
if (subForm == null)
{
subForm = new LayeredFormColorPicker(this, ReadRectangle, color =>
{
Value = color;
});
subForm.Disposed += (a, b) =>
{
ExtraMouseDown = false;
subForm = null;
};
subForm.Show(this);
}
}
#endregion
#region 释放/动画
protected override void Dispose(bool disposing)
{
ThreadFocus?.Dispose();
ThreadHover?.Dispose();
base.Dispose(disposing);
}
ITask? ThreadHover = null;
ITask? ThreadFocus = null;
#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 =>
{
Size font_size;
if (ValueFormatChanged == null)
{
switch (mode)
{
case TColorMode.Rgb:
font_size = g.MeasureString(_value.A == 255 ? "rgb(255,255,255)" : "rgba(255,255,255,0.99)", Font).Size();
break;
case TColorMode.Hex:
default:
font_size = g.MeasureString(_value.A == 255 ? "#DDDCCC" : "#DDDDCCCC", Font).Size();
break;
}
}
else font_size = g.MeasureString(ValueFormatChanged(this, new ColorEventArgs(_value)), Font).Size();
int gap = (int)((20 + WaveSize) * Config.Dpi);
if (showText)
{
int s = font_size.Height + gap;
return new Size(s + font_size.Width, s);
}
else
{
int s = font_size.Height + gap;
return new Size(s, s);
}
});
}
}
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
protected override bool ProcessCmdKey(ref System.Windows.Forms.Message msg, Keys keyData)
{
if (subForm != null) return subForm.IProcessCmdKey(ref msg, keyData);
return base.ProcessCmdKey(ref msg, keyData);
}
protected override void OnKeyPress(KeyPressEventArgs e)
{
if (subForm != null) subForm.IKeyPress(e);
base.OnKeyPress(e);
}
}
}