Files
HawkeyeVision/framework/MaterialSkin.Core/Controls/MaterialContextMenuStrip.cs
2025-07-14 12:03:59 +02:00

196 lines
7.5 KiB
C#

using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Windows.Forms;
using MaterialSkin.Animations;
namespace MaterialSkin.Controls
{
public class MaterialContextMenuStrip : ContextMenuStrip, IMaterialControl
{
//Properties for managing the material design properties
[Browsable(false)]
public int Depth { get; set; }
[Browsable(false)]
public MaterialSkinManager SkinManager => MaterialSkinManager.Instance;
[Browsable(false)]
public MouseState 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.GetApplicationBackgroundColor();
}
protected override void OnMouseUp(MouseEventArgs mea)
{
base.OnMouseUp(mea);
AnimationSource = mea.Location;
}
private ToolStripItemClickedEventArgs _delayesArgs;
protected override void OnItemClicked(ToolStripItemClickedEventArgs e)
{
if (e.ClickedItem != null && !(e.ClickedItem is 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(120, 30);
}
protected override ToolStripDropDown CreateDefaultDropDown()
{
var baseDropDown = base.CreateDefaultDropDown();
if (DesignMode) return baseDropDown;
var defaultDropDown = new MaterialContextMenuStrip();
defaultDropDown.Items.AddRange(baseDropDown.Items);
return defaultDropDown;
}
}
internal class MaterialToolStripRender : ToolStripProfessionalRenderer, IMaterialControl
{
//Properties for managing the material design properties
public int Depth { get; set; }
public MaterialSkinManager SkinManager => MaterialSkinManager.Instance;
public MouseState MouseState { get; set; }
protected override void OnRenderItemText(ToolStripItemTextRenderEventArgs e)
{
var g = e.Graphics;
g.TextRenderingHint = TextRenderingHint.AntiAlias;
var itemRect = GetItemRect(e.Item);
var textRect = new Rectangle(24, itemRect.Y, itemRect.Width - (24 + 16), itemRect.Height);
g.DrawString(
e.Text,
SkinManager.ROBOTO_MEDIUM_10,
e.Item.Enabled ? SkinManager.GetPrimaryTextBrush() : SkinManager.GetDisabledOrHintBrush(),
textRect,
new StringFormat { LineAlignment = StringAlignment.Center });
}
protected override void OnRenderMenuItemBackground(ToolStripItemRenderEventArgs e)
{
var g = e.Graphics;
g.Clear(SkinManager.GetApplicationBackgroundColor());
//Draw background
var itemRect = GetItemRect(e.Item);
g.FillRectangle(e.Item.Selected && e.Item.Enabled ? SkinManager.GetCmsSelectedItemBrush() : new SolidBrush(SkinManager.GetApplicationBackgroundColor()), itemRect);
//Ripple animation
var toolStrip = e.ToolStrip as MaterialContextMenuStrip;
if (toolStrip != null)
{
var animationManager = toolStrip.AnimationManager;
var animationSource = toolStrip.AnimationSource;
if (toolStrip.AnimationManager.IsAnimating() && e.Item.Bounds.Contains(animationSource))
{
for (int i = 0; i < animationManager.GetAnimationCount(); i++)
{
var animationValue = animationManager.GetProgress(i);
var rippleBrush = new SolidBrush(Color.FromArgb((int)(51 - (animationValue * 50)), Color.Black));
var 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)
{
//base.OnRenderImageMargin(e);
}
protected override void OnRenderSeparator(ToolStripSeparatorRenderEventArgs e)
{
var g = e.Graphics;
g.FillRectangle(new SolidBrush(SkinManager.GetApplicationBackgroundColor()), e.Item.Bounds);
g.DrawLine(
new Pen(SkinManager.GetDividersColor()),
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)
{
var g = e.Graphics;
g.DrawRectangle(
new Pen(SkinManager.GetDividersColor()),
new Rectangle(e.AffectedBounds.X, e.AffectedBounds.Y, e.AffectedBounds.Width - 1, e.AffectedBounds.Height - 1));
}
protected override void OnRenderArrow(ToolStripArrowRenderEventArgs e)
{
var g = e.Graphics;
const int ARROW_SIZE = 4;
var arrowMiddle = new Point(e.ArrowRectangle.X + e.ArrowRectangle.Width / 2, e.ArrowRectangle.Y + e.ArrowRectangle.Height / 2);
var arrowBrush = e.Item.Enabled ? SkinManager.GetPrimaryTextBrush() : SkinManager.GetDisabledOrHintBrush();
using (var arrowPath = new GraphicsPath())
{
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 + 4, item.ContentRectangle.Height);
}
}
}