// 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.Collections.Generic; using System.Drawing; using System.Drawing.Drawing2D; using System.Windows.Forms; namespace AntdUI { internal class LayeredFormMenuDown : ILayeredFormOpacityDown { internal float Radius = 0; bool isauto = true, isdark = false; List Items; public LayeredFormMenuDown(Menu control, int radius, Rectangle rect_read, MenuItemCollection items) { MessageCloseMouseLeave = true; isauto = control.Theme == TAMode.Auto; isdark = Config.IsDark || control.Theme == TAMode.Dark; control.Parent.SetTopMost(Handle); PARENT = control; select_x = 0; Font = control.Font; Radius = (int)(radius * Config.Dpi); Items = new List(items.Count); Init(control, rect_read, items); } public LayeredFormMenuDown(Menu parent, int sx, LayeredFormMenuDown control, float radius, Rectangle rect_read, MenuItemCollection items) { isauto = parent.Theme == TAMode.Auto; isdark = Config.IsDark || parent.Theme == TAMode.Dark; parent.Parent.SetTopMost(Handle); select_x = sx; PARENT = parent; Font = control.Font; Radius = radius; control.Disposed += (a, b) => { Dispose(); }; Items = new List(items.Count); Init(control, rect_read, items); } internal LayeredFormMenuDown? SubForm = null; void Init(Control control, Rectangle rect_read, MenuItemCollection items) { int y = 10, w = rect_read.Width; Helper.GDI(g => { var size = g.MeasureString(Config.NullText, Font).Size(); int gap_y = (int)(5 * Config.Dpi), gap_x = (int)(12 * Config.Dpi), gap_x2 = gap_x * 2, gap_y2 = gap_y * 2; int font_size = size.Height + gap_y2; y += gap_y; #region AutoWidth int b_w = size.Width + gap_x2; bool ui_icon = false, ui_arrow = false; foreach (MenuItem obj in items) { if (obj.Text != null) { var size3 = g.MeasureString(obj.Text, Font).Size(); if (size3.Width > b_w) b_w = size3.Width; } if (obj.HasIcon) ui_icon = true; if (obj.CanExpand) ui_arrow = true; } if (ui_icon) b_w += font_size; if (ui_arrow) b_w += (int)Math.Ceiling(font_size * 0.6F); w = b_w + gap_x2; #endregion int item_count = 0, divider_count = 0; int text_height = font_size - gap_y2; foreach (MenuItem it in items) { item_count++; Rectangle rect_bg = new Rectangle(10 + gap_y, y, w - gap_y2, font_size), rect_text = new Rectangle(rect_bg.X + gap_x, rect_bg.Y + gap_y, rect_bg.Width - gap_x2, text_height); Items.Add(new OMenuItem(it, rect_bg, gap_y, rect_text)); y += font_size; } var vr = (font_size * item_count) + (gap_y * divider_count); y = 10 + gap_y2 + vr; }); SetSizeW(w + 20); EndHeight = y + 10; if (control is LayeredFormMenuDown) { var point = control.PointToScreen(Point.Empty); SetLocation(point.X + rect_read.Width, point.Y + rect_read.Y - 10); } else { if (control is Menu menu && menu.Mode == TMenuMode.Horizontal) SetLocation(rect_read.X + (rect_read.Width - (w + 20)) / 2, rect_read.Bottom); else SetLocation(rect_read.Right, rect_read.Y); } KeyCall = keys => { int _select_x = -1; if (PARENT is Menu manu) _select_x = manu.select_x; if (select_x == _select_x) { if (keys == Keys.Escape) { Dispose(); return true; } if (nodata) return false; if (keys == Keys.Enter) { if (hoveindex > -1) { var it = Items[hoveindex]; if (OnClick(it)) return true; } } else if (keys == Keys.Up) { hoveindex--; if (hoveindex < 0) hoveindex = Items.Count - 1; foreach (var it in Items) it.Hover = false; FocusItem(Items[hoveindex]); return true; } else if (keys == Keys.Down) { if (hoveindex == -1) hoveindex = 0; else { hoveindex++; if (hoveindex > Items.Count - 1) hoveindex = 0; } foreach (var it in Items) it.Hover = false; FocusItem(Items[hoveindex]); return true; } else if (keys == Keys.Left) { if (_select_x > 0) { if (PARENT is Menu menu2) menu2.select_x--; IClose(); } return true; } else if (keys == Keys.Right) { if (hoveindex > -1) { var it = Items[hoveindex]; if (it.Sub != null && it.Sub.Count > 0) { SubForm?.IClose(); SubForm = null; OpenDown(it); if (PARENT is Menu menu2) menu2.select_x++; } } return true; } } return false; }; } StringFormat stringFormatLeft = Helper.SF(lr: StringAlignment.Near); public void FocusItem(OMenuItem item) { if (item.SetHover(true)) Print(); } /// /// 是否显示暂无数据 /// bool nodata = false; #region 鼠标 internal int select_x = 0; int hoveindex = -1; protected override void OnMouseUp(MouseEventArgs e) { foreach (var it in Items) { if (it.Show && it.Contains(e.Location, out _)) { if (OnClick(it)) return; } } base.OnMouseUp(e); } bool OnClick(OMenuItem it) { if (it.Sub == null || it.Sub.Count == 0) { if (PARENT is Menu menu) menu.DropDownChange(it.Val); IClose(); return true; } else { if (SubForm == null) OpenDown(it); else { SubForm?.IClose(); SubForm = null; } } return false; } void OpenDown(OMenuItem it) { if (PARENT is Menu menu) { SubForm = new LayeredFormMenuDown(menu, select_x + 1, this, Radius, new Rectangle(it.Rect.X, it.Rect.Y - 0, it.Rect.Width, it.Rect.Height), it.Sub); SubForm.Show(this); } } int hoveindexold = -1; protected override void OnMouseMove(MouseEventArgs e) { hoveindex = -1; int count = 0; for (int i = 0; i < Items.Count; i++) { var it = Items[i]; if (it.Contains(e.Location, out var change)) hoveindex = i; if (change) count++; } if (count > 0) Print(); base.OnMouseMove(e); if (hoveindexold == hoveindex) return; hoveindexold = hoveindex; SubForm?.IClose(); SubForm = null; if (hoveindex > -1) { if (PARENT is Menu menu) menu.select_x = select_x; var it = Items[hoveindex]; if (it.Sub != null && it.Sub.Count > 0 && PARENT != null) OpenDown(it); } } #endregion readonly StringFormat s_f = Helper.SF_NoWrap(); public override Bitmap PrintBit() { var rect = TargetRectXY; var rect_read = new Rectangle(10, 10, rect.Width - 20, rect.Height - 20); Bitmap original_bmp = new Bitmap(rect.Width, rect.Height); using (var g = Graphics.FromImage(original_bmp).High()) { using (var path = rect_read.RoundPath(Radius)) { DrawShadow(g, rect, rect.Width, EndHeight); if (isauto) { using (var brush = new SolidBrush(Style.Db.BgElevated)) { g.FillPath(brush, path); } } else if (isdark) { using (var brush = new SolidBrush("#1F1F1F".ToColor())) { g.FillPath(brush, path); } } else { using (var brush = new SolidBrush(Color.White)) { g.FillPath(brush, path); } } } if (nodata) { string emptytext = Localization.Provider?.GetLocalizedString("NoData") ?? "暂无数据"; using (var brush = new SolidBrush(Color.FromArgb(180, Style.Db.Text))) { g.DrawStr(emptytext, Font, brush, rect_read, s_f); } } else { if (isauto) { using (var brush = new SolidBrush(Style.Db.Text)) { foreach (var it in Items) { if (it.Show) DrawItem(g, brush, it); } } } else if (isdark) { using (var brush = new SolidBrush(Color.White)) { foreach (var it in Items) { if (it.Show) DrawItem(g, brush, it); } } } else { using (var brush = new SolidBrush(Color.Black)) { foreach (var it in Items) { if (it.Show) DrawItem(g, brush, it); } } } } } return original_bmp; } void DrawItem(Graphics g, SolidBrush brush, OMenuItem it) { if (isauto) { if (isdark) { if (it.Val.Select) { using (var brush_back = new SolidBrush(Style.Db.Primary)) { using (var path = it.Rect.RoundPath(Radius)) { g.FillPath(brush_back, path); } } using (var brush_select = new SolidBrush(Style.Db.TextBase)) { g.DrawStr(it.Val.Text, it.Val.Font ?? Font, brush_select, it.RectText, stringFormatLeft); } PaintIcon(g, it, brush.Color); } else { if (it.Hover) { using (var brush_back = new SolidBrush(Style.Db.FillTertiary)) { using (var path = it.Rect.RoundPath(Radius)) { g.FillPath(brush_back, path); } } } g.DrawStr(it.Val.Text, it.Val.Font ?? Font, brush, it.RectText, stringFormatLeft); PaintIcon(g, it, brush.Color); } } else { if (it.Val.Select) { using (var brush_back = new SolidBrush(Style.Db.PrimaryBg)) { using (var path = it.Rect.RoundPath(Radius)) { g.FillPath(brush_back, path); } } using (var brush_select = new SolidBrush(Style.Db.TextBase)) { g.DrawStr(it.Val.Text, it.Val.Font ?? Font, brush_select, it.RectText, stringFormatLeft); } } else { if (it.Hover) { using (var brush_back = new SolidBrush(Style.Db.FillTertiary)) { using (var path = it.Rect.RoundPath(Radius)) { g.FillPath(brush_back, path); } } } g.DrawStr(it.Val.Text, it.Val.Font ?? Font, brush, it.RectText, stringFormatLeft); } PaintIcon(g, it, brush.Color); } } else { if (isdark) { if (it.Val.Select) { using (var brush_back = new SolidBrush("#1668DC".ToColor())) { using (var path = it.Rect.RoundPath(Radius)) { g.FillPath(brush_back, path); } } using (var brush_select = new SolidBrush(Color.White)) { g.DrawStr(it.Val.Text, it.Val.Font ?? Font, brush_select, it.RectText, stringFormatLeft); } PaintIcon(g, it, brush.Color); } else { if (it.Hover) { using (var brush_back = new SolidBrush(Style.rgba(255, 255, 255, 0.08F))) { using (var path = it.Rect.RoundPath(Radius)) { g.FillPath(brush_back, path); } } } g.DrawStr(it.Val.Text, it.Val.Font ?? Font, brush, it.RectText, stringFormatLeft); PaintIcon(g, it, brush.Color); } } else { if (it.Val.Select) { using (var brush_back = new SolidBrush(Style.Db.PrimaryBg)) { using (var path = it.Rect.RoundPath(Radius)) { g.FillPath(brush_back, path); } } using (var brush_select = new SolidBrush(Style.Db.TextBase)) { g.DrawStr(it.Val.Text, it.Val.Font ?? Font, brush_select, it.RectText, stringFormatLeft); } } else { if (it.Hover) { using (var brush_back = new SolidBrush(Style.Db.FillTertiary)) { using (var path = it.Rect.RoundPath(Radius)) { g.FillPath(brush_back, path); } } } g.DrawStr(it.Val.Text, it.Val.Font ?? Font, brush, it.RectText, stringFormatLeft); } PaintIcon(g, it, brush.Color); } } if (it.has_sub) PaintArrow(g, it, brush.Color); } void PaintIcon(Graphics g, OMenuItem it, Color fore) { if (it.Val.Icon != null) g.DrawImage(it.Val.Icon, it.RectIcon); else if (it.Val.IconSvg != null) g.GetImgExtend(it.Val.IconSvg, it.RectIcon, fore); } void PaintArrow(Graphics g, OMenuItem item, Color color) { int size = item.arr_rect.Width, size_arrow = size / 2; g.TranslateTransform(item.arr_rect.X + size_arrow, item.arr_rect.Y + size_arrow); g.RotateTransform(-90F); using (var pen = new Pen(color, 2F)) { pen.StartCap = pen.EndCap = LineCap.Round; g.DrawLines(pen, new Rectangle(-size_arrow, -size_arrow, item.arr_rect.Width, item.arr_rect.Height).TriangleLines(-1, .2F)); } g.ResetTransform(); } Bitmap? shadow_temp = null; /// /// 绘制阴影 /// /// GDI /// 客户区域 /// 最终阴影宽度 /// 最终阴影高度 void DrawShadow(Graphics g, Rectangle rect_client, int shadow_width, int shadow_height) { if (Config.ShadowEnabled) { if (shadow_temp == null || (shadow_temp.Width != shadow_width || shadow_temp.Height != shadow_height)) { shadow_temp?.Dispose(); using (var path = new Rectangle(10, 10, shadow_width - 20, shadow_height - 20).RoundPath(Radius)) { shadow_temp = path.PaintShadow(shadow_width, shadow_height); } } g.DrawImage(shadow_temp, rect_client, 0.2F); } } #region 列模型 internal class OMenuItem { public OMenuItem(MenuItem _val, Rectangle rect, int gap_y, Rectangle rect_text) { Sub = _val.Sub; if (_val.CanExpand) has_sub = true; Rect = rect; if (_val.HasIcon) { RectIcon = new Rectangle(rect_text.X, rect_text.Y, rect_text.Height, rect_text.Height); RectText = new Rectangle(rect_text.X + gap_y + rect_text.Height, rect_text.Y, rect_text.Width - rect_text.Height - gap_y, rect_text.Height); } else RectText = rect_text; arr_rect = new Rectangle(Rect.Right - Rect.Height - gap_y, Rect.Y, Rect.Height, Rect.Height); Show = true; Val = _val; } public MenuItem Val { get; set; } /// /// 子选项 /// public MenuItemCollection Sub { get; set; } internal bool has_sub { get; set; } public Rectangle RectIcon { get; set; } public bool Hover { get; set; } public bool Show { get; set; } internal Rectangle arr_rect { get; set; } public Rectangle Rect { get; set; } internal bool SetHover(bool val) { bool change = false; if (val) { if (!Hover) change = true; Hover = true; } else { if (Hover) change = true; Hover = false; } return change; } internal bool Contains(Point point, out bool change) { if (Rect.Contains(point)) { change = SetHover(true); return true; } else { change = SetHover(false); return false; } } public Rectangle RectText { get; set; } } #endregion } }