// 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.Drawing.Imaging; using System.Windows.Forms; namespace AntdUI { internal class LayeredFormFloatButton : ILayeredFormOpacity, IEventListener { FloatButton.Config config; int BadgeSize = 6, ShadowXY; public LayeredFormFloatButton(FloatButton.Config _config) { maxalpha = 255; config = _config; TopMost = config.TopMost; if (!config.TopMost) config.Form.SetTopMost(Handle); Font = config.Font == null ? config.Form.Font : config.Font; Helper.GDI(g => { var dpi = Config.Dpi; BadgeSize = (int)Math.Round(BadgeSize * dpi); _config.MarginX = (int)Math.Round(_config.MarginX * dpi); _config.MarginY = (int)Math.Round(_config.MarginY * dpi); _config.Size = (int)Math.Round(_config.Size * dpi); _config.Gap = (int)Math.Round(_config.Gap * dpi); ShadowXY = _config.Gap / 2; int size = _config.Size, t_size = size + _config.Gap, icon_size = (int)(size * 0.45F), xy = (size - icon_size) / 2; int hasx = 0, hasy = 0; if (_config.Vertical) { foreach (var it in _config.Btns) { it.PropertyChanged += Notify_PropertyChanged; it.rect = new Rectangle(hasx, hasy, t_size, t_size); it.rect_read = new Rectangle(hasx + ShadowXY, hasy + ShadowXY, size, size); SetIconSize(it, size, xy, icon_size, dpi); hasy += t_size; } SetSize(size + _config.Gap, hasy); } else { foreach (var it in _config.Btns) { it.PropertyChanged += Notify_PropertyChanged; it.rect = new Rectangle(hasx, hasy, t_size, t_size); it.rect_read = new Rectangle(hasx + ShadowXY, hasy + ShadowXY, size, size); SetIconSize(it, size, xy, icon_size, dpi); hasx += t_size; } SetSize(hasx, size + _config.Gap); } }); SetPoint(); config.Form.LocationChanged += Form_LSChanged; config.Form.SizeChanged += Form_LSChanged; } private void Notify_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e) { if (sender == null || e.PropertyName == null) return; Print(); } bool SetPoint() { if (config.Control == null) { var point = config.Form.Location; SetPoint(point.X, point.Y, config.Form.Width, config.Form.Height); } else { if (config.Control.IsDisposed) { IClose(); return false; } var point = config.Control.PointToScreen(Point.Empty); SetPoint(point.X, point.Y, config.Control.Width, config.Control.Height); } return true; } void SetPoint(int x, int y, int w, int h) { switch (config.Align) { case TAlign.TL: case TAlign.LT: SetLocation(new Point(x + config.MarginY, y + config.MarginY)); break; case TAlign.Top: SetLocation(new Point(x + (w - TargetRect.Width) / 2, y + config.MarginY)); break; case TAlign.TR: case TAlign.RT: SetLocation(new Point(x + w - config.MarginX - TargetRect.Width, y + config.MarginY)); break; case TAlign.Left: SetLocation(new Point(x + config.MarginY, y + (h - TargetRect.Height) / 2)); break; case TAlign.Right: SetLocation(new Point(x + w - config.MarginX - TargetRect.Width, y + (h - TargetRect.Height) / 2)); break; case TAlign.BL: case TAlign.LB: SetLocation(new Point(x + config.MarginY, y + h - config.MarginY - TargetRect.Height)); break; case TAlign.Bottom: SetLocation(new Point(x + (w - TargetRect.Width) / 2, y + h - config.MarginY - TargetRect.Height)); break; case TAlign.RB: case TAlign.BR: default: SetLocation(new Point(x + w - config.MarginX - TargetRect.Width, y + h - config.MarginY - TargetRect.Height)); break; } } void SetIconSize(FloatButton.ConfigBtn it, int size, int xy, int icon_size, float dpi) { if (it.IconSize.HasValue) { if (it.IconSize.Value.Width == it.IconSize.Value.Height) { int icon_size_div = (int)(it.IconSize.Value.Width * dpi); it.rect_icon = new Rectangle(it.rect_read.X + (size - icon_size_div) / 2, it.rect_read.Y + (size - icon_size_div) / 2, icon_size_div, icon_size_div); } else { int icon_size_w = (int)(it.IconSize.Value.Width * dpi), icon_size_h = (int)(it.IconSize.Value.Height * dpi); it.rect_icon = new Rectangle(it.rect_read.X + (size - icon_size_w) / 2, it.rect_read.Y + (size - icon_size_h) / 2, icon_size_w, icon_size_h); } } else it.rect_icon = new Rectangle(it.rect_read.X + xy, it.rect_read.Y + xy, icon_size, icon_size); } private void Form_LSChanged(object? sender, EventArgs e) { if (SetPoint()) Print(); } #region 渲染 readonly StringFormat stringCenter = Helper.SF_NoWrap(); public override Bitmap PrintBit() { var rect = TargetRectXY; Bitmap original_bmp = new Bitmap(rect.Width, rect.Height); using (var g = Graphics.FromImage(original_bmp).High()) { foreach (var it in config.Btns) { using (var path = DrawShadow(g, it)) { Color back, back_hover, fore; switch (it.Type) { case TTypeMini.Primary: back = Style.Db.Primary; back_hover = Style.Db.PrimaryHover; fore = Style.Db.PrimaryColor; break; case TTypeMini.Success: back = Style.Db.Success; back_hover = Style.Db.SuccessHover; fore = Style.Db.SuccessColor; break; case TTypeMini.Error: back = Style.Db.Error; back_hover = Style.Db.ErrorHover; fore = Style.Db.ErrorColor; break; case TTypeMini.Warn: back = Style.Db.Warning; back_hover = Style.Db.WarningHover; fore = Style.Db.WarningColor; break; case TTypeMini.Info: back = Style.Db.Info; back_hover = Style.Db.InfoHover; fore = Style.Db.InfoColor; break; default: back = Style.Db.BgElevated; back_hover = Style.Db.FillSecondary; fore = Style.Db.Text; break; } if (it.Fore.HasValue) fore = it.Fore.Value; using (var brush = new SolidBrush(back)) { g.FillPath(brush, path); } if (it.hover) { using (var brush = new SolidBrush(back_hover)) { g.FillPath(brush, path); } } if (it.IconSvg != null) g.GetImgExtend(it.IconSvg, it.rect_icon, fore); else if (it.Icon != null) g.DrawImage(it.Icon, it.rect_icon); else { using (var brush = new SolidBrush(fore)) { g.DrawStr(it.Text, Font, brush, it.rect_read, stringCenter); } } PrintBadge(g, it); } } } return original_bmp; } readonly StringFormat stringBadge = Helper.SF_NoWrap(); void PrintBadge(Graphics g, FloatButton.ConfigBtn it) { if (it.Badge != null) { var color = it.BadgeBack ?? Style.Db.Error; using (var brush_fore = new SolidBrush(Style.Db.ErrorColor)) { if (it.Badge == " ") { var rect_badge = new Rectangle(it.rect_read.Right - BadgeSize, it.rect_read.Y, BadgeSize, BadgeSize); using (var brush = new SolidBrush(color)) { g.FillEllipse(brush, rect_badge); using (var pen = new Pen(brush_fore.Color, 1F)) { g.DrawEllipse(pen, rect_badge); } } } else { using (var font = new Font(Font.FontFamily, it.BadgeSize)) { var size = g.MeasureString(it.Badge, font); float size_badge = size.Height * 1.2F, size_badge2 = size_badge * 0.4F; if (size.Height > size.Width) { var rect_badge = new RectangleF(it.rect_read.Right + size_badge2 - size_badge, it.rect_read.Y - size_badge2, size_badge, size_badge); using (var brush = new SolidBrush(color)) { g.FillEllipse(brush, rect_badge); using (var pen = new Pen(brush_fore.Color, 1F)) { g.DrawEllipse(pen, rect_badge); } } g.DrawStr(it.Badge, font, brush_fore, rect_badge, stringBadge); } else { var w_badge = size.Width * 1.2F; var rect_badge = new RectangleF(it.rect_read.Right + size_badge2 - w_badge, it.rect_read.Y - size_badge2, w_badge, size_badge); using (var brush = new SolidBrush(color)) { using (var path = rect_badge.RoundPath(rect_badge.Height)) { g.FillPath(brush, path); using (var pen = new Pen(brush_fore.Color, 1F)) { g.DrawPath(pen, path); } } } g.DrawStr(it.Badge, font, brush_fore, rect_badge, stringBadge); } } } } } } /// /// 绘制阴影 /// /// GDI GraphicsPath DrawShadow(Graphics g, FloatButton.ConfigBtn it) { bool round = it.Round; float radius = round ? it.rect_read.Height : it.Radius * Config.Dpi; var path = Helper.RoundPath(it.rect_read, radius, round); if (Config.ShadowEnabled) { if (it.shadow_temp == null || (it.shadow_temp.Width != it.rect.Width || it.shadow_temp.Height != it.rect.Height)) { it.shadow_temp?.Dispose(); using (var path2 = Helper.RoundPath(new Rectangle(ShadowXY, ShadowXY, it.rect_read.Width, it.rect_read.Height), radius, round)) { it.shadow_temp = path2.PaintShadow(it.rect.Width, it.rect.Height, 14); } } using (var attributes = new ImageAttributes()) { var matrix = new ColorMatrix { Matrix33 = 0.2F }; attributes.SetColorMatrix(matrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap); g.DrawImage(it.shadow_temp, new Rectangle(it.rect.X, it.rect.Y + 6, it.rect.Width, it.rect.Height), 0, 0, it.rect.Width, it.rect.Height, GraphicsUnit.Pixel, attributes); } } return path; } #endregion #region 鼠标 TooltipForm? tooltipForm = null; protected override void OnMouseMove(MouseEventArgs e) { int count = 0, hand = 0; foreach (var it in config.Btns) { if (it.rect.Contains(e.Location)) { hand++; if (!it.hover) { it.hover = true; count++; if (it.Tooltip != null) { var _rect = TargetRect; var rect = new Rectangle(_rect.X + it.rect.X, _rect.Y + it.rect.Y, it.rect.Width, it.rect.Height); if (tooltipForm == null) { tooltipForm = new TooltipForm(config.Form, rect, it.Tooltip, new TooltipConfig { Font = Font, ArrowAlign = config.Align.AlignMiniReverse(config.Vertical), }); tooltipForm.Show(this); } else tooltipForm.SetText(rect, it.Tooltip); } } } else { if (it.hover) { it.hover = false; count++; } } } SetCursor(hand > 0); if (count > 0) Print(); base.OnMouseMove(e); } protected override void OnMouseLeave(EventArgs e) { tooltipForm?.IClose(); tooltipForm = null; int count = 0; foreach (var it in config.Btns) { if (it.hover) { it.hover = false; count++; } } SetCursor(false); if (count > 0) Print(); base.OnMouseLeave(e); } protected override void OnMouseClick(MouseEventArgs e) { if (e.Button == MouseButtons.Left) { foreach (var it in config.Btns) { if (it.rect.Contains(e.Location)) { config.Call.Invoke(it); return; } } } base.OnMouseClick(e); } #endregion #region 主题 protected override void OnHandleCreated(EventArgs e) { base.OnHandleCreated(e); this.AddListener(); } protected override void Dispose(bool disposing) { config.Form.LocationChanged -= Form_LSChanged; config.Form.SizeChanged -= Form_LSChanged; foreach (var it in config.Btns) it.PropertyChanged -= Notify_PropertyChanged; base.Dispose(disposing); } public void HandleEvent(EventType id, object? tag) { switch (id) { case EventType.THEME: Print(); break; } } #endregion } }