|
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using System.Threading;
using System.Collections;
using System.Diagnostics;
namespace Bornander.IconMenu
{
public delegate void IconDeletedHandler(Icon icon);
public partial class IconMenu : UserControl
{
private enum State
{
Launch,
Edit
}
private Image offscreenImage = null;
private Graphics offscreen = null;
private int slotWidth = 48;
private int slotHeight = 48;
private List<Slot> slots = new List<Slot>();
private List<Icon> icons = new List<Icon>();
private bool mouseIsDown = false;
private Vector mouseDownPosition = new Vector();
private DateTime mouseDownTimestamp = DateTime.Now;
private Icon selectedIcon = null;
private Image deleteMarker = null;
private State state = State.Launch;
private TimeSpan clickDelay = new TimeSpan(0, 0, 0, 0, 500);
public event IconDeletedHandler Deleted;
public IconMenu()
{
InitializeComponent();
animationTimer.Enabled = true;
this.MouseDown += new MouseEventHandler(HandleMouseDown);
this.MouseMove += new MouseEventHandler(HandleMouseMove);
this.MouseUp += new MouseEventHandler(HandleMouseUp);
}
private void OnDeleted(Icon icon)
{
if (Deleted != null)
{
Deleted(icon);
}
}
private void HandleMouseUp(object sender, MouseEventArgs e)
{
mouseIsDown = false;
if (selectedIcon != null)
{
if (DateTime.Now - mouseDownTimestamp < clickDelay)
{
switch (state)
{
case State.Launch:
selectedIcon.OnLaunch();
break;
case State.Edit:
Slot slot = GetSlotAtPoint((Point)mouseDownPosition);
if (deleteMarker != null && slot != null && !slot.IsEmpty && !slot.Icon.IsReadOnly)
{
Vector slotPosition = (Vector)slot.Icon.Location;
Vector positionInSlot = mouseDownPosition - slotPosition;
if (positionInSlot.X < deleteMarker.Width && positionInSlot.Y < deleteMarker.Height)
{
DialogResult result = MessageBox.Show("Are you sure you want to delete this?", "Delete", MessageBoxButtons.OKCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2);
if (result == DialogResult.OK)
{
icons.Remove(slot.Icon);
OnDeleted(slot.Icon);
slot.Icon = null;
Relayout();
}
break;
}
}
state = State.Launch;
selectedIcon.IsMouseControlled = false;
break;
}
}
else
{
int selectedIndex = slots.IndexOf(selectedIcon.Slot);
if (selectedIndex > icons.Count - 1)
{
selectedIcon.Slot.Icon = null;
Slot slot = slots[icons.Count - 1];
slot.Icon = selectedIcon;
selectedIcon.Slot = slot;
}
}
selectedIcon.IsMouseControlled = false;
selectedIcon = null;
}
else
{
if (state == State.Edit)
{
state = State.Launch;
}
}
}
void HandleMouseMove(object sender, MouseEventArgs e)
{
if (selectedIcon != null)
{
if (state == State.Edit)
{
Point mouseLocation = new Point(e.X, e.Y);
selectedIcon.UpdateOnMouseLocation(mouseLocation);
Slot slot = GetSlotAtPoint(mouseLocation);
if (slot != null)
{
int selectedIndex = slots.IndexOf(selectedIcon.Slot);
int currentIndex = slots.IndexOf(slot);
if (selectedIndex != currentIndex)
{
for (int i = selectedIndex; i != currentIndex; i += Math.Sign(currentIndex - selectedIndex))
{
Slot slotA = slots[i];
Slot slotB = slots[i + Math.Sign(currentIndex - selectedIndex)];
slotA.Icon = slotB.Icon;
if (slotA.Icon != null)
{
slotA.Icon.Slot = slotA;
}
}
slot.Icon = selectedIcon;
selectedIcon.Slot = slot;
}
}
}
else
{
if (DateTime.Now - mouseDownTimestamp >= clickDelay)
{
state = State.Edit;
selectedIcon.IsMouseControlled = true;
}
}
}
}
private void HandleMouseDown(object sender, MouseEventArgs e)
{
mouseIsDown = true;
mouseDownTimestamp = DateTime.Now;
mouseDownPosition = new Vector(e.X, e.Y);
Icon icon = GetIconAtPoint((Point)mouseDownPosition);
if (icon != null)
{
selectedIcon = icon;
}
}
private Slot GetSlotAtPoint(Point point)
{
foreach(Slot slot in slots)
{
if (slot.Bounds.Contains(point))
{
return slot;
}
}
return null;
}
private Icon GetIconAtPoint(Point point)
{
foreach (Icon icon in icons)
{
if (icon.Slot.Bounds.Contains(point))
{
return icon;
}
}
return null;
}
private void AssignSlotToIcons()
{
foreach (Slot slot in slots)
{
slot.Icon = null;
}
foreach (Icon icon in icons)
{
icon.Slot = null;
foreach (Slot slot in slots)
{
if (slot.IsEmpty)
{
icon.Slot = slot;
slot.Icon = icon;
break;
}
}
}
}
private void CreateOffscreen()
{
offscreenImage = new Bitmap(Math.Max(Width, 1), Math.Max(Height, 1));
offscreen = Graphics.FromImage(offscreenImage);
}
private void Relayout()
{
offscreen = null;
int numberOfHorizontalSlots = Width / slotWidth;
int numberOfVerticalSlots = Height / slotHeight;
int horizontalPadding = (Width % slotWidth) / 2;
int verticalPadding = (Height % slotHeight) / 2;
icons.Sort(delegate(Icon left, Icon right)
{
if (left.Slot == null)
return 1;
else
{
if (right.Slot == null)
return -1;
}
return slots.IndexOf(left.Slot) - slots.IndexOf(right.Slot);
});
slots = new List<Slot>();
for (int i = 0; verticalPadding + (i + 1) * slotHeight < Height; ++i)
{
for (int j = 0; j < numberOfHorizontalSlots; ++j)
{
slots.Add(new Slot(new Rectangle(horizontalPadding + j * slotWidth, verticalPadding + i * slotHeight, slotWidth, slotHeight)));
}
}
AssignSlotToIcons();
Invalidate();
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
Relayout();
}
protected override void OnPaintBackground(PaintEventArgs e)
{
if (offscreen == null)
{
CreateOffscreen();
}
offscreen.FillRectangle(new SolidBrush(BackColor), ClientRectangle);
foreach (Icon icon in icons)
{
if (!icon.IsMouseControlled)
{
icon.Paint(offscreen, state == State.Edit, deleteMarker);
}
}
if (selectedIcon != null)
{
selectedIcon.Paint(offscreen, state == State.Edit, deleteMarker);
}
e.Graphics.DrawImage(offscreenImage, 0, 0);
}
public void AddIcon(Icon icon)
{
icons.Add(icon);
foreach (Slot slot in slots)
{
if (slot.IsEmpty)
{
icon.Slot = slot;
slot.Icon = icon;
break;
}
}
}
private void OnAnimationStep(object sender, EventArgs e)
{
if (mouseIsDown && selectedIcon != null && (DateTime.Now - mouseDownTimestamp >= clickDelay))
{
state = State.Edit;
selectedIcon.IsMouseControlled = true;
}
foreach (Icon icon in icons)
{
icon.Animate(0.2f, state == State.Edit);
}
Refresh();
}
#region Public properties
public Image DeleteMarker
{
set { deleteMarker = value; }
get { return deleteMarker; }
}
public int SlotWidth
{
set { slotWidth = value; }
get { return slotWidth; }
}
public int SlotHeight
{
set { slotHeight = value; }
get { return slotHeight; }
}
#endregion
}
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.
Article videos
Oakmead Apps Android Games
21 Feb 2014: Best VB.NET Article of January 2014 - Second Prize
18 Oct 2013: Best VB.NET article of September 2013
23 Jun 2012: Best C++ article of May 2012
20 Apr 2012: Best VB.NET article of March 2012
22 Feb 2010: Best overall article of January 2010
22 Feb 2010: Best C# article of January 2010