Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
174 changes: 174 additions & 0 deletions MaterialSkin/Controls/MaterialFloatingActionButton.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Windows.Forms;
using MaterialSkin.Animations;

namespace MaterialSkin.Controls
{
public class MaterialFloatingActionButton : Button, IMaterialControl
{
[Browsable(false)]
public int Depth { get; set; }
[Browsable(false)]
public MaterialSkinManager SkinManager => MaterialSkinManager.Instance;
[Browsable(false)]
public MouseState MouseState { get; set; }

private const int FAB_SIZE = 56;
private const int FAB_MINI_SIZE = 40;
private const int FAB_ICON_MARGIN = 16;
private const int FAB_MINI_ICON_MARGIN = 8;
private const int FAB_ICON_SIZE = 24;

public bool Mini
{
get { return _mini; }
set { setSize(value); }
}
private bool _mini = false;

public bool AnimateShowHideButton
{
get { return _animateShowButton; }
set { _animateShowButton = value; }
}
private bool _animateShowButton;

public bool AnimateIcon
{
get { return _animateIcon; }
set { _animateIcon = value; }
}

private bool _animateIcon = false;

public Image Icon
{
get { return _icon; }
set { _icon = value; }
}
private Image _icon;

private bool _isHiding = false;

private readonly AnimationManager _animationManager;
private readonly AnimationManager _showAnimationManager;

public MaterialFloatingActionButton()
{
Size = new Size(FAB_SIZE, FAB_SIZE);
DoubleBuffered = true;
_animationManager = new AnimationManager(false)
{
Increment = 0.03,
AnimationType = AnimationType.EaseOut
};
_animationManager.OnAnimationProgress += sender => Invalidate();

_showAnimationManager = new AnimationManager(true)
{
Increment = 0.1,
AnimationType = AnimationType.EaseOut
};
_showAnimationManager.OnAnimationProgress += sender => Invalidate();
_showAnimationManager.OnAnimationFinished += _showAnimationManager_OnAnimationFinished;
}

private void setSize(bool mini)
{
Size = mini ? new Size(FAB_MINI_SIZE, FAB_MINI_SIZE) : new Size(FAB_SIZE, FAB_SIZE);
_mini = mini;
}

private void _showAnimationManager_OnAnimationFinished(object sender)
{
if(_isHiding)
{
Visible = false;
_isHiding = false;
}
}

protected override void OnInvalidated(InvalidateEventArgs e)
{
base.OnInvalidated(e);
// for some reason this is needed as Invalidate() does not trigger a repaint when animating.
InvokePaint(this, new PaintEventArgs(this.CreateGraphics(), this.ClientRectangle));
}

protected override void OnPaint(PaintEventArgs pevent)
{
var g = pevent.Graphics;
Rectangle bounds = new Rectangle(new Point(0, 0), Size);
GraphicsPath regionPath = new GraphicsPath();

g.FillEllipse(SkinManager.ColorScheme.AccentBrush, bounds);

if (_animationManager.IsAnimating())
{
for (int i = 0; i < _animationManager.GetAnimationCount(); i++)
{
var animationValue = _animationManager.GetProgress(i);
var animationSource = _animationManager.GetSource(i);
var rippleBrush = new SolidBrush(Color.FromArgb((int)(51 - (animationValue * 50)), Color.White));
var rippleSize = (int)(animationValue * Width * 2);
g.FillEllipse(rippleBrush, new Rectangle(animationSource.X - rippleSize / 2, animationSource.Y - rippleSize / 2, rippleSize, rippleSize));
}
}
if(_showAnimationManager.IsAnimating())
{
int target = Convert.ToInt32((_mini ? FAB_MINI_SIZE : FAB_SIZE) * _showAnimationManager.GetProgress());
bounds.Width = target == 0 ? 1 : target;
bounds.Height = target == 0 ? 1 : target;
bounds.X = Convert.ToInt32(((_mini ? FAB_MINI_SIZE : FAB_SIZE) / 2) - (((_mini ? FAB_MINI_SIZE : FAB_SIZE) / 2) * _showAnimationManager.GetProgress()));
bounds.Y = Convert.ToInt32(((_mini ? FAB_MINI_SIZE : FAB_SIZE) / 2) - (((_mini ? FAB_MINI_SIZE : FAB_SIZE) / 2) * _showAnimationManager.GetProgress()));

if(_animateIcon)
{
g.TranslateTransform((float) ((_mini ? FAB_MINI_SIZE : FAB_SIZE) / 2), (float) ((_mini ? FAB_MINI_SIZE : FAB_SIZE) / 2));
g.RotateTransform(-90.0f + (float) (90.0f * _showAnimationManager.GetProgress()));
g.TranslateTransform(-(float) ((_mini ? FAB_MINI_SIZE : FAB_SIZE) / 2), -(float) ((_mini ? FAB_MINI_SIZE : FAB_SIZE) / 2));
}
}

if(Icon != null)
{
Point iconPos = _mini ? new Point(FAB_MINI_ICON_MARGIN, FAB_MINI_ICON_MARGIN) : new Point(FAB_ICON_MARGIN, FAB_ICON_MARGIN);
g.DrawImage(Icon, new Rectangle(iconPos, new Size(24, 24)));
}

regionPath.AddEllipse(bounds);
Region = new Region(regionPath);
}

protected override void OnMouseUp(MouseEventArgs mevent)
{
base.OnMouseUp(mevent);
_animationManager.StartNewAnimation(AnimationDirection.In, mevent.Location);
}
private Point origin;

public new void Hide()
{
if(Visible)
{
_isHiding = true;
_showAnimationManager.StartNewAnimation(AnimationDirection.Out);
}
}

public new void Show()
{
if (!Visible)
{
origin = Location;
_showAnimationManager.StartNewAnimation(AnimationDirection.In);
Visible = true;
}
}
}
}
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ TabControl | Yes | N/A | N/A | Yes
ContextMenuStrip | Yes | Yes | Yes | Yes
ListView | Yes | Yes | No | No
ProgressBar | Yes | Yes | No | No
FloatingActionButton | No | No | No | No
FloatingActionButton | Yes | N/A | N/A | Yes
Dialogs | No | No | No | No
Switch | No | No | No | No
More... | No | No | No | No
Expand Down