576 lines
20 KiB
C#
576 lines
20 KiB
C#
|
////////////////////////////////////////////////////////////////
|
||
|
// //
|
||
|
// Neoforce Controls //
|
||
|
// //
|
||
|
////////////////////////////////////////////////////////////////
|
||
|
// //
|
||
|
// File: ContextMenu.cs //
|
||
|
// //
|
||
|
// Version: 0.7 //
|
||
|
// //
|
||
|
// Date: 11/09/2010 //
|
||
|
// //
|
||
|
// Author: Tom Shane //
|
||
|
// //
|
||
|
////////////////////////////////////////////////////////////////
|
||
|
// //
|
||
|
// Copyright (c) by Tom Shane //
|
||
|
// //
|
||
|
////////////////////////////////////////////////////////////////
|
||
|
|
||
|
#region //// Using /////////////
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
using System;
|
||
|
using System.Collections.Generic;
|
||
|
using Microsoft.Xna.Framework;
|
||
|
using Microsoft.Xna.Framework.Graphics;
|
||
|
using Microsoft.Xna.Framework.Input;
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
namespace TomShane.Neoforce.Controls
|
||
|
{
|
||
|
|
||
|
public class ContextMenu: MenuBase
|
||
|
{
|
||
|
|
||
|
#region //// Fields ////////////
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
private long timer = 0;
|
||
|
private Control sender = null;
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
#region //// Properties ////////
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
protected internal Control Sender { get { return sender; } set { sender = value; } }
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
#region //// Construstors //////
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
public ContextMenu(Manager manager): base(manager)
|
||
|
{
|
||
|
Visible = false;
|
||
|
Detached = true;
|
||
|
StayOnBack = true;
|
||
|
|
||
|
Manager.Input.MouseDown += new MouseEventHandler(Input_MouseDown);
|
||
|
}
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
#region //// Destructors ///////
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
protected override void Dispose(bool disposing)
|
||
|
{
|
||
|
if (disposing)
|
||
|
{
|
||
|
Manager.Input.MouseDown -= Input_MouseDown;
|
||
|
}
|
||
|
base.Dispose(disposing);
|
||
|
}
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
#region //// Methods ///////////
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
public override void Init()
|
||
|
{
|
||
|
base.Init();
|
||
|
}
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
protected internal override void InitSkin()
|
||
|
{
|
||
|
base.InitSkin();
|
||
|
Skin = new SkinControl(Manager.Skin.Controls["ContextMenu"]);
|
||
|
}
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
protected override void DrawControl(Renderer renderer, Rectangle rect, GameTime gameTime)
|
||
|
{
|
||
|
base.DrawControl(renderer, rect, gameTime);
|
||
|
|
||
|
SkinLayer l1 = Skin.Layers["Control"];
|
||
|
SkinLayer l2 = Skin.Layers["Selection"];
|
||
|
|
||
|
int vsize = LineHeight();
|
||
|
Color col = Color.White;
|
||
|
|
||
|
for (int i = 0; i < Items.Count; i++)
|
||
|
{
|
||
|
int mod = i > 0 ? 2 : 0;
|
||
|
int left = rect.Left + l1.ContentMargins.Left + vsize;
|
||
|
int h = vsize - mod - (i < (Items.Count - 1) ? 1 : 0);
|
||
|
int top = rect.Top + l1.ContentMargins.Top + (i * vsize) + mod;
|
||
|
|
||
|
|
||
|
if (Items[i].Separated && i > 0)
|
||
|
{
|
||
|
Rectangle r = new Rectangle(left, rect.Top + l1.ContentMargins.Top + (i * vsize), LineWidth() - vsize + 4, 1);
|
||
|
renderer.Draw(Manager.Skin.Controls["Control"].Layers[0].Image.Resource, r, l1.Text.Colors.Enabled);
|
||
|
}
|
||
|
if (ItemIndex != i)
|
||
|
{
|
||
|
if (Items[i].Enabled)
|
||
|
{
|
||
|
Rectangle r = new Rectangle(left, top, LineWidth() - vsize, h);
|
||
|
renderer.DrawString(this, l1, Items[i].Text, r, false);
|
||
|
col = l1.Text.Colors.Enabled;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Rectangle r = new Rectangle(left + l1.Text.OffsetX,
|
||
|
top + l1.Text.OffsetY,
|
||
|
LineWidth() - vsize, h);
|
||
|
renderer.DrawString(l1.Text.Font.Resource, Items[i].Text, r, l1.Text.Colors.Disabled, l1.Text.Alignment);
|
||
|
col = l1.Text.Colors.Disabled;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (Items[i].Enabled)
|
||
|
{
|
||
|
Rectangle rs = new Rectangle(rect.Left + l1.ContentMargins.Left,
|
||
|
top,
|
||
|
Width - (l1.ContentMargins.Horizontal - Skin.OriginMargins.Horizontal),
|
||
|
h);
|
||
|
renderer.DrawLayer(this, l2, rs);
|
||
|
|
||
|
Rectangle r = new Rectangle(left,
|
||
|
top, LineWidth() - vsize, h);
|
||
|
|
||
|
renderer.DrawString(this, l2, Items[i].Text, r, false);
|
||
|
col = l2.Text.Colors.Enabled;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Rectangle rs = new Rectangle(rect.Left + l1.ContentMargins.Left,
|
||
|
top,
|
||
|
Width - (l1.ContentMargins.Horizontal - Skin.OriginMargins.Horizontal),
|
||
|
vsize);
|
||
|
renderer.DrawLayer(l2, rs, l2.States.Disabled.Color, l2.States.Disabled.Index);
|
||
|
|
||
|
Rectangle r = new Rectangle(left + l1.Text.OffsetX,
|
||
|
top + l1.Text.OffsetY,
|
||
|
LineWidth() - vsize, h);
|
||
|
renderer.DrawString(l2.Text.Font.Resource, Items[i].Text, r, l2.Text.Colors.Disabled, l2.Text.Alignment);
|
||
|
col = l2.Text.Colors.Disabled;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
if (Items[i].Image != null)
|
||
|
{
|
||
|
Rectangle r = new Rectangle(rect.Left + l1.ContentMargins.Left + 3,
|
||
|
rect.Top + top + 3,
|
||
|
LineHeight() - 6,
|
||
|
LineHeight() - 6);
|
||
|
renderer.Draw(Items[i].Image, r, Color.White);
|
||
|
}
|
||
|
|
||
|
if (Items[i].Items != null && Items[i].Items.Count > 0)
|
||
|
{
|
||
|
renderer.Draw(Manager.Skin.Images["Shared.ArrowRight"].Resource, rect.Left + LineWidth() - 4, rect.Top + l1.ContentMargins.Top + (i * vsize) + 8, col);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
private int LineHeight()
|
||
|
{
|
||
|
int h = 0;
|
||
|
if (Items.Count > 0)
|
||
|
{
|
||
|
SkinLayer l = Skin.Layers["Control"];
|
||
|
h = (int)l.Text.Font.Resource.LineSpacing + 9;
|
||
|
}
|
||
|
return h;
|
||
|
}
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
private int LineWidth()
|
||
|
{
|
||
|
int w = 0;
|
||
|
SkinFont font = Skin.Layers["Control"].Text.Font;
|
||
|
if (Items.Count > 0)
|
||
|
{
|
||
|
for (int i = 0; i < Items.Count; i++)
|
||
|
{
|
||
|
int wx = (int)font.Resource.MeasureString(Items[i].Text).X + 16;
|
||
|
if (wx > w) w = wx;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
w += 4 + LineHeight();
|
||
|
|
||
|
return w;
|
||
|
}
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
private void AutoSize()
|
||
|
{
|
||
|
SkinText font = Skin.Layers["Control"].Text;
|
||
|
if (Items != null && Items.Count > 0)
|
||
|
{
|
||
|
Height = (LineHeight() * Items.Count) + (Skin.Layers["Control"].ContentMargins.Vertical - Skin.OriginMargins.Vertical);
|
||
|
Width = LineWidth() + (Skin.Layers["Control"].ContentMargins.Horizontal - Skin.OriginMargins.Horizontal) + font.OffsetX;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Height = 16;
|
||
|
Width = 16;
|
||
|
}
|
||
|
}
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
private void TrackItem(int x, int y)
|
||
|
{
|
||
|
if (Items != null && Items.Count > 0)
|
||
|
{
|
||
|
SkinText font = Skin.Layers["Control"].Text;
|
||
|
int h = LineHeight();
|
||
|
y -= Skin.Layers["Control"].ContentMargins.Top;
|
||
|
int i = (int)((float)y / h);
|
||
|
if (i < Items.Count)
|
||
|
{
|
||
|
if (i != ItemIndex && Items[i].Enabled)
|
||
|
{
|
||
|
if (ChildMenu != null)
|
||
|
{
|
||
|
this.HideMenu(false);
|
||
|
}
|
||
|
|
||
|
if (i >= 0 && i != ItemIndex)
|
||
|
{
|
||
|
Items[i].SelectedInvoke(new EventArgs());
|
||
|
}
|
||
|
|
||
|
Focused = true;
|
||
|
ItemIndex = i;
|
||
|
timer = (long)TimeSpan.FromTicks(DateTime.Now.Ticks).TotalMilliseconds;
|
||
|
}
|
||
|
else if (!Items[i].Enabled && ChildMenu == null)
|
||
|
{
|
||
|
ItemIndex = -1;
|
||
|
}
|
||
|
}
|
||
|
Invalidate();
|
||
|
}
|
||
|
}
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
protected override void OnMouseMove(MouseEventArgs e)
|
||
|
{
|
||
|
base.OnMouseMove(e);
|
||
|
TrackItem(e.Position.X, e.Position.Y);
|
||
|
}
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
protected internal override void Update(GameTime gameTime)
|
||
|
{
|
||
|
base.Update(gameTime);
|
||
|
|
||
|
AutoSize();
|
||
|
|
||
|
long time = (long)TimeSpan.FromTicks(DateTime.Now.Ticks).TotalMilliseconds;
|
||
|
|
||
|
if (timer != 0 && time - timer >= Manager.MenuDelay && ItemIndex >= 0 && Items[ItemIndex].Items.Count > 0 && ChildMenu == null)
|
||
|
{
|
||
|
OnClick(new MouseEventArgs(new MouseState(), MouseButton.Left, Point.Zero));
|
||
|
}
|
||
|
}
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
protected override void OnMouseOut(MouseEventArgs e)
|
||
|
{
|
||
|
base.OnMouseOut(e);
|
||
|
|
||
|
if (!CheckArea(e.State.X, e.State.Y) && ChildMenu == null)
|
||
|
{
|
||
|
ItemIndex = -1;
|
||
|
}
|
||
|
}
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
protected override void OnClick(EventArgs e)
|
||
|
{
|
||
|
if (sender != null && !(sender is MenuBase)) sender.Focused = true;
|
||
|
base.OnClick(e);
|
||
|
timer = 0;
|
||
|
|
||
|
MouseEventArgs ex = (e is MouseEventArgs) ? (MouseEventArgs)e : new MouseEventArgs();
|
||
|
|
||
|
if (ex.Button == MouseButton.Left || ex.Button == MouseButton.None)
|
||
|
{
|
||
|
if (ItemIndex >= 0 && Items[ItemIndex].Enabled)
|
||
|
{
|
||
|
if (ItemIndex >= 0 && Items[ItemIndex].Items != null && Items[ItemIndex].Items.Count > 0)
|
||
|
{
|
||
|
if (ChildMenu == null)
|
||
|
{
|
||
|
ChildMenu = new ContextMenu(Manager);
|
||
|
(ChildMenu as ContextMenu).RootMenu = this.RootMenu;
|
||
|
(ChildMenu as ContextMenu).ParentMenu = this;
|
||
|
(ChildMenu as ContextMenu).sender = sender;
|
||
|
ChildMenu.Items.AddRange(Items[ItemIndex].Items);
|
||
|
(ChildMenu as ContextMenu).AutoSize();
|
||
|
}
|
||
|
int y = AbsoluteTop + Skin.Layers["Control"].ContentMargins.Top + (ItemIndex * LineHeight());
|
||
|
(ChildMenu as ContextMenu).Show(sender, AbsoluteLeft + Width - 1, y);
|
||
|
if (ex.Button == MouseButton.None) (ChildMenu as ContextMenu).ItemIndex = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (ItemIndex >= 0)
|
||
|
{
|
||
|
Items[ItemIndex].ClickInvoke(ex);
|
||
|
}
|
||
|
if (RootMenu is ContextMenu) (RootMenu as ContextMenu).HideMenu(true);
|
||
|
else if (RootMenu is MainMenu)
|
||
|
{
|
||
|
(RootMenu as MainMenu).HideMenu();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
protected override void OnKeyPress(KeyEventArgs e)
|
||
|
{
|
||
|
base.OnKeyPress(e);
|
||
|
|
||
|
timer = 0;
|
||
|
|
||
|
if (e.Key == Keys.Down || (e.Key == Keys.Tab && !e.Shift))
|
||
|
{
|
||
|
e.Handled = true;
|
||
|
ItemIndex += 1;
|
||
|
}
|
||
|
|
||
|
if (e.Key == Keys.Up || (e.Key == Keys.Tab && e.Shift))
|
||
|
{
|
||
|
e.Handled = true;
|
||
|
ItemIndex -=1;
|
||
|
}
|
||
|
|
||
|
if (ItemIndex > Items.Count - 1) ItemIndex = 0;
|
||
|
if (ItemIndex < 0) ItemIndex = Items.Count - 1;
|
||
|
|
||
|
if (e.Key == Keys.Right && Items[ItemIndex].Items.Count > 0)
|
||
|
{
|
||
|
e.Handled = true;
|
||
|
OnClick(new MouseEventArgs(new MouseState(), MouseButton.None, Point.Zero));
|
||
|
}
|
||
|
if (e.Key == Keys.Left)
|
||
|
{
|
||
|
e.Handled = true;
|
||
|
if (ParentMenu != null && ParentMenu is ContextMenu)
|
||
|
{
|
||
|
(ParentMenu as ContextMenu).Focused = true;
|
||
|
(ParentMenu as ContextMenu).HideMenu(false);
|
||
|
}
|
||
|
}
|
||
|
if (e.Key == Keys.Escape)
|
||
|
{
|
||
|
e.Handled = true;
|
||
|
if (ParentMenu != null) ParentMenu.Focused = true;
|
||
|
HideMenu(true);
|
||
|
}
|
||
|
}
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
protected override void OnGamePadPress(GamePadEventArgs e)
|
||
|
{
|
||
|
timer = 0;
|
||
|
|
||
|
if (e.Button == GamePadButton.None) return;
|
||
|
|
||
|
if (e.Button == GamePadActions.Down || e.Button == GamePadActions.NextControl)
|
||
|
{
|
||
|
e.Handled = true;
|
||
|
ItemIndex += 1;
|
||
|
}
|
||
|
else if (e.Button == GamePadActions.Up || e.Button == GamePadActions.PrevControl)
|
||
|
{
|
||
|
e.Handled = true;
|
||
|
ItemIndex -= 1;
|
||
|
}
|
||
|
|
||
|
if (ItemIndex > Items.Count - 1) ItemIndex = 0;
|
||
|
if (ItemIndex < 0) ItemIndex = Items.Count - 1;
|
||
|
|
||
|
if (e.Button == GamePadActions.Right && Items[ItemIndex].Items.Count > 0)
|
||
|
{
|
||
|
e.Handled = true;
|
||
|
OnClick(new MouseEventArgs(new MouseState(), MouseButton.None, Point.Zero));
|
||
|
}
|
||
|
if (e.Button == GamePadActions.Left)
|
||
|
{
|
||
|
e.Handled = true;
|
||
|
if (ParentMenu != null && ParentMenu is ContextMenu)
|
||
|
{
|
||
|
(ParentMenu as ContextMenu).Focused = true;
|
||
|
(ParentMenu as ContextMenu).HideMenu(false);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
base.OnGamePadPress(e);
|
||
|
}
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
public virtual void HideMenu(bool hideCurrent)
|
||
|
{
|
||
|
if (hideCurrent)
|
||
|
{
|
||
|
Visible = false;
|
||
|
ItemIndex = -1;
|
||
|
}
|
||
|
if (ChildMenu != null)
|
||
|
{
|
||
|
(ChildMenu as ContextMenu).HideMenu(true);
|
||
|
ChildMenu.Dispose();
|
||
|
ChildMenu = null;
|
||
|
}
|
||
|
}
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
public override void Show()
|
||
|
{
|
||
|
Show(null, Left, Top);
|
||
|
}
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
public virtual void Show(Control sender, int x, int y)
|
||
|
{
|
||
|
AutoSize();
|
||
|
base.Show();
|
||
|
if (!Initialized) Init();
|
||
|
if (sender != null && sender.Root != null && sender.Root is Container)
|
||
|
{
|
||
|
(sender.Root as Container).Add(this, false);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Manager.Add(this);
|
||
|
}
|
||
|
|
||
|
this.sender = sender;
|
||
|
|
||
|
if (sender != null && sender.Root != null && sender.Root is Container)
|
||
|
{
|
||
|
Left = x - Root.AbsoluteLeft;
|
||
|
Top = y - Root.AbsoluteTop;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Left = x;
|
||
|
Top = y;
|
||
|
}
|
||
|
|
||
|
if (AbsoluteLeft + Width > Manager.TargetWidth)
|
||
|
{
|
||
|
Left = Left - Width;
|
||
|
if (ParentMenu != null && ParentMenu is ContextMenu)
|
||
|
{
|
||
|
Left = Left - ParentMenu.Width + 2;
|
||
|
}
|
||
|
else if (ParentMenu != null)
|
||
|
{
|
||
|
Left = Manager.TargetWidth - (Parent != null ? Parent.AbsoluteLeft : 0) - Width - 2;
|
||
|
}
|
||
|
}
|
||
|
if (AbsoluteTop + Height > Manager.TargetHeight)
|
||
|
{
|
||
|
Top = Top - Height;
|
||
|
if (ParentMenu != null && ParentMenu is ContextMenu)
|
||
|
{
|
||
|
Top = Top + LineHeight();
|
||
|
}
|
||
|
else if (ParentMenu != null)
|
||
|
{
|
||
|
Top = ParentMenu.Top - Height - 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Focused = true;
|
||
|
}
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
private void Input_MouseDown(object sender, MouseEventArgs e)
|
||
|
{
|
||
|
if ((RootMenu is ContextMenu) && !(RootMenu as ContextMenu).CheckArea(e.Position.X, e.Position.Y) && Visible)
|
||
|
{
|
||
|
HideMenu(true);
|
||
|
}
|
||
|
else if ((RootMenu is MainMenu) && RootMenu.ChildMenu != null && !(RootMenu.ChildMenu as ContextMenu).CheckArea(e.Position.X, e.Position.Y) && Visible)
|
||
|
{
|
||
|
(RootMenu as MainMenu).HideMenu();
|
||
|
}
|
||
|
}
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
private bool CheckArea(int x, int y)
|
||
|
{
|
||
|
if (Visible)
|
||
|
{
|
||
|
if (x <= AbsoluteLeft ||
|
||
|
x >= AbsoluteLeft + Width ||
|
||
|
y <= AbsoluteTop ||
|
||
|
y >= AbsoluteTop + Height)
|
||
|
{
|
||
|
bool ret = false;
|
||
|
if (ChildMenu != null)
|
||
|
{
|
||
|
ret = (ChildMenu as ContextMenu).CheckArea(x, y);
|
||
|
}
|
||
|
return ret;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|