#region Imports
|
|
using DPumpHydr.WinFrmUI.RLT.Manager;
|
using DPumpHydr.WinFrmUI.RLT.Util;
|
using System;
|
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 MaterialRadioButton
|
|
public class MaterialRadioButton : System.Windows.Forms.RadioButton, MaterialControlI
|
{
|
[Browsable(false)]
|
public int Depth { get; set; }
|
|
[Browsable(false)]
|
public MaterialSkinManager SkinManager => MaterialSkinManager.Instance;
|
|
[Browsable(false)]
|
public MaterialMouseState MouseState { get; set; }
|
|
[Browsable(false)]
|
public Point MouseLocation { get; set; }
|
|
private bool useAccentColor;
|
|
[Category("Material")]
|
public bool UseAccentColor
|
{
|
get => useAccentColor;
|
set
|
{
|
useAccentColor = value;
|
Invalidate();
|
}
|
}
|
|
private bool ripple;
|
|
[Category("Behavior")]
|
public bool Ripple
|
{
|
get => ripple;
|
set
|
{
|
ripple = value;
|
AutoSize = AutoSize; //Make AutoSize directly set the bounds.
|
|
if (value)
|
{
|
Margin = new Padding(0);
|
}
|
|
Invalidate();
|
}
|
}
|
|
// animation managers
|
private readonly AnimationManager _checkAM;
|
|
private readonly AnimationManager _rippleAM;
|
private readonly AnimationManager _hoverAM;
|
|
// size related variables which should be recalculated onsizechanged
|
private Rectangle _radioButtonBounds;
|
|
private int _boxOffset;
|
|
// size constants
|
private const int HEIGHT_RIPPLE = 37;
|
|
private const int HEIGHT_NO_RIPPLE = 20;
|
private const int RADIOBUTTON_SIZE = 18;
|
private const int RADIOBUTTON_SIZE_HALF = RADIOBUTTON_SIZE / 2;
|
private const int RADIOBUTTON_OUTER_CIRCLE_WIDTH = 2;
|
private const int RADIOBUTTON_INNER_CIRCLE_SIZE = RADIOBUTTON_SIZE - (2 * RADIOBUTTON_OUTER_CIRCLE_WIDTH);
|
private const int TEXT_OFFSET = 26;
|
|
public MaterialRadioButton()
|
{
|
SetStyle(ControlStyles.DoubleBuffer | ControlStyles.OptimizedDoubleBuffer, true);
|
|
_checkAM = new AnimationManager
|
{
|
AnimationType = AnimationType.EaseInOut,
|
Increment = 0.06
|
};
|
_hoverAM = new AnimationManager(true)
|
{
|
AnimationType = AnimationType.Linear,
|
Increment = 0.10
|
};
|
_rippleAM = new AnimationManager(false)
|
{
|
AnimationType = AnimationType.Linear,
|
Increment = 0.10,
|
SecondaryIncrement = 0.08
|
};
|
|
_checkAM.OnAnimationProgress += sender => Invalidate();
|
_hoverAM.OnAnimationProgress += sender => Invalidate();
|
_rippleAM.OnAnimationProgress += sender => Invalidate();
|
|
TabStopChanged += (sender, e) => TabStop = true;
|
|
CheckedChanged += (sender, args) =>
|
{
|
if (Ripple)
|
{
|
_checkAM.StartNewAnimation(Checked ? AnimationDirection.In : AnimationDirection.Out);
|
}
|
};
|
|
SizeChanged += OnSizeChanged;
|
|
Ripple = true;
|
MouseLocation = new Point(-1, -1);
|
}
|
|
private void OnSizeChanged(object sender, EventArgs eventArgs)
|
{
|
_boxOffset = (Height / 2) - (int)(RADIOBUTTON_SIZE / 2);
|
_radioButtonBounds = new Rectangle(_boxOffset, _boxOffset, RADIOBUTTON_SIZE, RADIOBUTTON_SIZE);
|
}
|
|
public override Size GetPreferredSize(Size proposedSize)
|
{
|
Size strSize;
|
|
using (MaterialNativeTextRenderer NativeText = new(CreateGraphics()))
|
{
|
strSize = NativeText.MeasureLogString(Text, SkinManager.GetLogFontByType(MaterialSkinManager.FontType.Body1));
|
}
|
|
int w = _boxOffset + TEXT_OFFSET + strSize.Width;
|
return Ripple ? new Size(w, HEIGHT_RIPPLE) : new Size(w, HEIGHT_NO_RIPPLE);
|
}
|
|
protected override void OnPaint(PaintEventArgs pevent)
|
{
|
Graphics g = pevent.Graphics;
|
g.SmoothingMode = SmoothingMode.AntiAlias;
|
g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
|
|
// clear the control
|
g.Clear(Parent.BackColor == Color.Transparent ? ((Parent.Parent == null || (Parent.Parent != null && Parent.Parent.BackColor == Color.Transparent)) ? SkinManager.BackgroundColor : Parent.Parent.BackColor) : Parent.BackColor);
|
|
int RADIOBUTTON_CENTER = _boxOffset + RADIOBUTTON_SIZE_HALF;
|
Point animationSource = new(RADIOBUTTON_CENTER, RADIOBUTTON_CENTER);
|
|
double animationProgress = _checkAM.GetProgress();
|
|
int colorAlpha = Enabled ? (int)(animationProgress * 255.0) : SkinManager.CheckBoxOffDisabledColor.A;
|
int backgroundAlpha = Enabled ? (int)(SkinManager.CheckboxOffColor.A * (1.0 - animationProgress)) : SkinManager.CheckBoxOffDisabledColor.A;
|
float animationSize = (float)(animationProgress * 9f);
|
float animationSizeHalf = animationSize / 2;
|
int rippleHeight = (HEIGHT_RIPPLE % 2 == 0) ? HEIGHT_RIPPLE - 3 : HEIGHT_RIPPLE - 2;
|
|
Color RadioColor = Color.FromArgb(colorAlpha, Enabled ? UseAccentColor ? SkinManager.ColorScheme.AccentColor : SkinManager.ColorScheme.PrimaryColor : SkinManager.CheckBoxOffDisabledColor);
|
|
// draw hover animation
|
if (Ripple)
|
{
|
double animationValue = _hoverAM.GetProgress();
|
int rippleSize = (int)(rippleHeight * (0.7 + (0.3 * animationValue)));
|
|
using SolidBrush rippleBrush = new(Color.FromArgb((int)(40 * animationValue),
|
!Checked ? (SkinManager.Theme == MaterialSkinManager.Themes.LIGHT ? Color.Black : Color.White) : RadioColor));
|
g.FillEllipse(rippleBrush, new Rectangle(animationSource.X - (rippleSize / 2), animationSource.Y - (rippleSize / 2), rippleSize - 1, rippleSize - 1));
|
}
|
|
// draw ripple animation
|
if (Ripple && _rippleAM.IsAnimating())
|
{
|
for (int i = 0; i < _rippleAM.GetAnimationCount(); i++)
|
{
|
double animationValue = _rippleAM.GetProgress(i);
|
int rippleSize = (_rippleAM.GetDirection(i) == AnimationDirection.InOutIn) ? (int)(rippleHeight * (0.7 + (0.3 * animationValue))) : rippleHeight;
|
|
using SolidBrush rippleBrush = new(Color.FromArgb((int)(animationValue * 40), !Checked ? (SkinManager.Theme == MaterialSkinManager.Themes.LIGHT ? Color.Black : Color.White) : RadioColor));
|
g.FillEllipse(rippleBrush, new Rectangle(animationSource.X - (rippleSize / 2), animationSource.Y - (rippleSize / 2), rippleSize - 1, rippleSize - 1));
|
}
|
}
|
|
// draw radiobutton circle
|
using (Pen pen = new(BlendColor(Parent.BackColor, Enabled ? SkinManager.CheckboxOffColor : SkinManager.CheckBoxOffDisabledColor, backgroundAlpha), 2))
|
{
|
g.DrawEllipse(pen, new Rectangle(_boxOffset, _boxOffset, RADIOBUTTON_SIZE, RADIOBUTTON_SIZE));
|
}
|
|
if (Enabled)
|
{
|
using Pen pen = new(RadioColor, 2);
|
g.DrawEllipse(pen, new Rectangle(_boxOffset, _boxOffset, RADIOBUTTON_SIZE, RADIOBUTTON_SIZE));
|
}
|
|
if (Checked)
|
{
|
using SolidBrush brush = new(RadioColor);
|
g.FillEllipse(brush, new RectangleF(RADIOBUTTON_CENTER - animationSizeHalf, RADIOBUTTON_CENTER - animationSizeHalf, animationSize, animationSize));
|
}
|
|
// Text
|
using MaterialNativeTextRenderer NativeText = new(g);
|
Rectangle textLocation = new(_boxOffset + TEXT_OFFSET, 0, Width, Height);
|
NativeText.DrawTransparentText(Text, SkinManager.GetLogFontByType(MaterialSkinManager.FontType.Body1),
|
Enabled ? SkinManager.TextHighEmphasisColor : SkinManager.TextDisabledOrHintColor,
|
textLocation.Location,
|
textLocation.Size,
|
MaterialNativeTextRenderer.TextAlignFlags.Left | MaterialNativeTextRenderer.TextAlignFlags.Middle);
|
}
|
|
private bool IsMouseInCheckArea()
|
{
|
return ClientRectangle.Contains(MouseLocation);
|
}
|
|
private bool hovered = false;
|
|
protected override void OnCreateControl()
|
{
|
base.OnCreateControl();
|
|
if (DesignMode)
|
{
|
return;
|
}
|
|
MouseState = MaterialMouseState.OUT;
|
|
GotFocus += (sender, AddingNewEventArgs) =>
|
{
|
if (Ripple && !hovered)
|
{
|
_hoverAM.StartNewAnimation(AnimationDirection.In, new object[] { Checked });
|
hovered = true;
|
}
|
};
|
|
LostFocus += (sender, args) =>
|
{
|
if (Ripple && hovered)
|
{
|
_hoverAM.StartNewAnimation(AnimationDirection.Out, new object[] { Checked });
|
hovered = false;
|
}
|
};
|
|
MouseEnter += (sender, args) =>
|
{
|
MouseState = MaterialMouseState.HOVER;
|
//if (Ripple && !hovered)
|
//{
|
// _hoverAM.StartNewAnimation(AnimationDirection.In, new object[] { Checked });
|
// hovered = true;
|
//}
|
};
|
|
MouseLeave += (sender, args) =>
|
{
|
MouseLocation = new Point(-1, -1);
|
MouseState = MaterialMouseState.OUT;
|
//if (Ripple && hovered)
|
//{
|
// _hoverAM.StartNewAnimation(AnimationDirection.Out, new object[] { Checked });
|
// hovered = false;
|
//}
|
};
|
|
MouseDown += (sender, args) =>
|
{
|
MouseState = MaterialMouseState.DOWN;
|
if (Ripple)
|
{
|
_rippleAM.SecondaryIncrement = 0;
|
_rippleAM.StartNewAnimation(AnimationDirection.InOutIn, new object[] { Checked });
|
}
|
};
|
|
KeyDown += (sender, args) =>
|
{
|
if (Ripple && (args.KeyCode == Keys.Space) && _rippleAM.GetAnimationCount() == 0)
|
{
|
_rippleAM.SecondaryIncrement = 0;
|
_rippleAM.StartNewAnimation(AnimationDirection.InOutIn, new object[] { Checked });
|
}
|
};
|
|
MouseUp += (sender, args) =>
|
{
|
if (Ripple)
|
{
|
MouseState = MaterialMouseState.HOVER;
|
_rippleAM.SecondaryIncrement = 0.08;
|
_hoverAM.StartNewAnimation(AnimationDirection.Out, new object[] { Checked });
|
hovered = false;
|
}
|
};
|
|
KeyUp += (sender, args) =>
|
{
|
if (Ripple && (args.KeyCode == Keys.Space))
|
{
|
MouseState = MaterialMouseState.HOVER;
|
_rippleAM.SecondaryIncrement = 0.08;
|
}
|
};
|
|
MouseMove += (sender, args) =>
|
{
|
MouseLocation = args.Location;
|
Cursor = IsMouseInCheckArea() ? Cursors.Hand : Cursors.Default;
|
};
|
}
|
}
|
|
#endregion
|
}
|