#region Imports using DPumpHydr.WinFrmUI.RLT.Manager; using DPumpHydr.WinFrmUI.RLT.Util; using System.ComponentModel; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Text; using System.Windows.Forms; using static DPumpHydr.WinFrmUI.RLT.Helper.MaterialDrawHelper; using static DPumpHydr.WinFrmUI.RLT.Util.MaterialAnimations; #endregion namespace DPumpHydr.WinFrmUI.RLT.Controls { #region MaterialContextMenuStrip [ToolboxItem(false)] public class MaterialContextMenuStrip : ContextMenuStrip, MaterialControlI { //Properties for managing the material design properties [Browsable(false)] public int Depth { get; set; } [Browsable(false)] public MaterialSkinManager SkinManager => MaterialSkinManager.Instance; [Browsable(false)] public MaterialMouseState MouseState { get; set; } internal AnimationManager AnimationManager; internal Point AnimationSource; public delegate void ItemClickStart(object sender, ToolStripItemClickedEventArgs e); public event ItemClickStart OnItemClickStart; public MaterialContextMenuStrip() { Renderer = new MaterialToolStripRender(); AnimationManager = new AnimationManager(false) { Increment = 0.07, AnimationType = AnimationType.Linear }; AnimationManager.OnAnimationProgress += sender => Invalidate(); AnimationManager.OnAnimationFinished += sender => OnItemClicked(_delayesArgs); BackColor = SkinManager.BackdropColor; } protected override void OnMouseUp(MouseEventArgs mea) { base.OnMouseUp(mea); AnimationSource = mea.Location; } private ToolStripItemClickedEventArgs _delayesArgs; protected override void OnItemClicked(ToolStripItemClickedEventArgs e) { if (e.ClickedItem is not null and not ToolStripSeparator) { if (e == _delayesArgs) { //The event has been fired manualy because the args are the ones we saved for delay base.OnItemClicked(e); } else { //Interrupt the default on click, saving the args for the delay which is needed to display the animaton _delayesArgs = e; //Fire custom event to trigger actions directly but keep cms open OnItemClickStart?.Invoke(this, e); //Start animation AnimationManager.StartNewAnimation(AnimationDirection.In); } } } } public class MaterialToolStripMenuItem : ToolStripMenuItem { public MaterialToolStripMenuItem() { AutoSize = false; Size = new Size(128, 32); } protected override ToolStripDropDown CreateDefaultDropDown() { ToolStripDropDown baseDropDown = base.CreateDefaultDropDown(); if (DesignMode) { return baseDropDown; } MaterialContextMenuStrip defaultDropDown = new(); defaultDropDown.Items.AddRange(baseDropDown.Items); return defaultDropDown; } } internal class MaterialToolStripRender : ToolStripProfessionalRenderer, MaterialControlI { private const int LEFT_PADDING = 16; private const int RIGHT_PADDING = 8; //Properties for managing the material design properties public int Depth { get; set; } public MaterialSkinManager SkinManager => MaterialSkinManager.Instance; public MaterialMouseState MouseState { get; set; } protected override void OnRenderItemText(ToolStripItemTextRenderEventArgs e) { Graphics g = e.Graphics; g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit; Rectangle itemRect = GetItemRect(e.Item); Rectangle textRect = new(LEFT_PADDING, itemRect.Y, itemRect.Width - (LEFT_PADDING + RIGHT_PADDING), itemRect.Height); using MaterialNativeTextRenderer NativeText = new(g); NativeText.DrawTransparentText(e.Text, SkinManager.GetLogFontByType(MaterialSkinManager.FontType.Body2), e.Item.Enabled ? SkinManager.TextHighEmphasisColor : SkinManager.TextDisabledOrHintColor, textRect.Location, textRect.Size, MaterialNativeTextRenderer.TextAlignFlags.Left | MaterialNativeTextRenderer.TextAlignFlags.Middle); } protected override void OnRenderMenuItemBackground(ToolStripItemRenderEventArgs e) { Graphics g = e.Graphics; g.Clear(SkinManager.BackgroundColor); //Draw background Rectangle itemRect = GetItemRect(e.Item); g.FillRectangle(e.Item.Selected && e.Item.Enabled ? SkinManager.BackgroundFocusBrush : SkinManager.BackgroundBrush, itemRect); //Ripple animation if (e.ToolStrip is MaterialContextMenuStrip toolStrip) { AnimationManager animationManager = toolStrip.AnimationManager; Point animationSource = toolStrip.AnimationSource; if (toolStrip.AnimationManager.IsAnimating() && e.Item.Bounds.Contains(animationSource)) { for (int i = 0; i < animationManager.GetAnimationCount(); i++) { double animationValue = animationManager.GetProgress(i); SolidBrush rippleBrush = new(Color.FromArgb((int)(51 - (animationValue * 50)), Color.Black)); int rippleSize = (int)(animationValue * itemRect.Width * 2.5); g.FillEllipse(rippleBrush, new Rectangle(animationSource.X - (rippleSize / 2), itemRect.Y - itemRect.Height, rippleSize, itemRect.Height * 3)); } } } } protected override void OnRenderImageMargin(ToolStripRenderEventArgs e) { } protected override void OnRenderSeparator(ToolStripSeparatorRenderEventArgs e) { Graphics g = e.Graphics; g.FillRectangle(SkinManager.BackgroundBrush, e.Item.Bounds); g.DrawLine( new Pen(SkinManager.DividersColor), new Point(e.Item.Bounds.Left, e.Item.Bounds.Height / 2), new Point(e.Item.Bounds.Right, e.Item.Bounds.Height / 2)); } protected override void OnRenderToolStripBorder(ToolStripRenderEventArgs e) { e.ToolStrip.BackColor = SkinManager.BackgroundColor; } protected override void OnRenderArrow(ToolStripArrowRenderEventArgs e) { Graphics g = e.Graphics; const int ARROW_SIZE = 4; Point arrowMiddle = new(e.ArrowRectangle.X + (e.ArrowRectangle.Width / 2), e.ArrowRectangle.Y + (e.ArrowRectangle.Height / 2)); Brush arrowBrush = e.Item.Enabled ? SkinManager.TextHighEmphasisBrush : SkinManager.TextDisabledOrHintBrush; using GraphicsPath arrowPath = new(); arrowPath.AddLines( new[] { new Point(arrowMiddle.X - ARROW_SIZE, arrowMiddle.Y - ARROW_SIZE), new Point(arrowMiddle.X, arrowMiddle.Y), new Point(arrowMiddle.X - ARROW_SIZE, arrowMiddle.Y + ARROW_SIZE) }); arrowPath.CloseFigure(); g.FillPath(arrowBrush, arrowPath); } private Rectangle GetItemRect(ToolStripItem item) { return new Rectangle(0, item.ContentRectangle.Y, item.ContentRectangle.Width, item.ContentRectangle.Height); } } #endregion }