// 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); } } }