/* Copyright (c) 2007 Ben Howell
* This software is licensed under the MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using Decal.Adapter;
using Decal.Adapter.Wrappers;
using DecalTimer = Decal.Interop.Input.TimerClass;
using System.Xml;
namespace GoArrow.Huds
{
abstract class WindowHud : IManagedHud
{
[DllImport("user32.dll")]
private static extern bool ScreenToClient(IntPtr hWnd, ref Point lpPoint);
#region WindowMessage Constants
const short WM_MOUSEMOVE = 0x0200;
const short WM_LBUTTONDOWN = 0x0201;
const short WM_LBUTTONUP = 0x0202;
const short WM_RBUTTONDOWN = 0x0204;
const short WM_RBUTTONUP = 0x0205;
const short WM_MBUTTONDOWN = 0x0207;
const short WM_MBUTTONUP = 0x0208;
const short WM_MOUSEWHEEL = 0x020A;
// Range of WM_MOUSE* events
const short WM_MOUSEFIRST = 0x0200;
const short WM_MOUSELAST = 0x020A;
#endregion
const int BorderWidth = 4, BorderPadding = 2, BorderPaddedWidth = BorderWidth + BorderPadding;
const int TitleBarHeight = 18, TitleBarPaddedHeight = TitleBarHeight - BorderPadding;
const int ControlSize = 14, ControlPadding = 2, ControlPaddedSize = ControlSize + ControlPadding;
const int NumControlBoxes = 3;
const int MinimizedWidth = 200, MinimizedHeight = 2 * BorderWidth + TitleBarHeight;
private static readonly Size msDblClickRect = SystemInformation.DoubleClickSize;
private static readonly long msDblClickTicks = SystemInformation.DoubleClickTime * 10000L;
private const int GoIcon = 0x06001F80;
protected static readonly Color Clear = Color.FromArgb(0);
#region Private Fields
private HudManager mManager;
private Hud mHud = null;
private Bitmap mClientImage;
private volatile bool mWindowNeedsRepaint = false, mBordersNeedRepaint = false;
private volatile bool mClientNeedsRepaint = false, mClientImageLost = true;
private Rectangle mRegion; // Absolute position on screen
private Rectangle mClientRegion; // Absolute position on screen; entirely contained in mRegion
// Alpha fading
private DecalTimer mFaderTimer;
private DateTime mFadeBeginTime;
private long mFadeDurationMillis;
private bool mFadeInitialDelay;
private int mFadeStartAlphaFrame, mFadeEndAlphaFrame;
private int mAlphaFrame = 255;
// Mouse stuff
private Point mMouseLocation;
private Point mMouseDownLocation;
private Point mMouseLastClickLocation;
private long mMouseLastClickTicks = long.MinValue;
private MouseButtons mMouseLastClickButton = MouseButtons.None;
private MouseButtons mMouseButtons = MouseButtons.None;
private MouseButtons mClientMouseButtons = MouseButtons.None;
// Used to make sure that if a mouse-down event is eaten, its corresponding mouse-up event is eaten as well
private MouseButtons mMouseDownEaten = MouseButtons.None;
private bool mMouseOnClose = false, mMouseOnMinRestore = false, mMouseOnSticky = false;
[Flags]
private enum MoveResizeMode
{
Idle = 0x0,
Moving = 0x1,
ResizeN = 0x10,
ResizeS = 0x20,
ResizeE = 0x40,
ResizeW = 0x80,
Resizing = ResizeN | ResizeS | ResizeE | ResizeW,
}
private Rectangle mOriginalRegion; // Used when moving or resizing
private MoveResizeMode mMoveResizeMode = MoveResizeMode.Idle;
[Flags]
private enum Border
{
None = 0x0,
North = 0x1,
East = 0x2,
South = 0x4,
West = 0x8,
Title = 0x10,
All = North | East | South | West | Title,
}
private Border mHighlightedBorder = Border.None;
// Private fields with public accessors
private bool mDisposed = false;
private bool mVisible = false, mMinimized = false, mSticky = false;
private bool mMouseOnWindow = false, mMouseOnClient = false;
private string mTitle;
private int mAlphaFrameActive = 255;
private int mAlphaFrameInactive = 255;
private Size mMinSize = new Size(MinimizedWidth, 50);
private Size mMaxSize = new Size(800, 600);
private Color mBorderColor1 = Color.FromArgb(unchecked((int)0xFFA27B42));
private Color mBorderColor2 = Color.FromArgb(unchecked((int)0xFF67553B));
private Color mBorderColorHighlight = Color.FromArgb(unchecked((int)0xFFC9B169));
private Color mBackgroundColor = Color.FromArgb(0xC0, Color.Black);
private HudResizeDrawMode mResizeDrawMode = HudResizeDrawMode.Crop;
#endregion
#region Public Events
///
/// Occurs once each frame before repainting.
///
public event EventHandler Heartbeat;
///
/// Occurs when the Visible property is changed either
/// programatically or by the user.
///
public event EventHandler VisibleChanged;
///
/// Occurs when the Minimized property is changed either
/// programatically or by the user.
///
public event EventHandler MinimizedChaged;
///
/// Occurs when the Sticky property is changed either
/// programatically or by the user.
///
public event EventHandler StickyChaged;
///
/// Occurs repeatedly while the window is being resized by the user.
///
public event EventHandler Resizing;
///
/// Occurs when a window resize is complete or when the window size is
/// changed programatically.
///
public event EventHandler ResizeEnd;
///
/// Occurs repeatedly while the window is being moved by the user.
///
public event EventHandler Moving;
///
/// Occurs when a window move is complete or when the window position
/// is changed programatically.
///
public event EventHandler MoveEnd;
///
/// Occurs when the mouse moves while over the window.
/// Mouse coordinates are relative to the top left corner of the window.
///
public event EventHandler MouseMove;
///
/// Occurs when a mouse button is pressed while over the window.
/// Mouse coordinates are relative to the top left corner of the window.
///
public event EventHandler MouseDown;
///
/// Occurs when a mouse button is released while over the window.
/// Mouse coordinates are relative to the top left corner of the window.
/// The message can only be eaten if the cooresponding MouseDown
/// message was also eaten.
///
public event EventHandler MouseUp;
///
/// Occurs when a mouse button is double clicked while over the window.
/// Mouse coordinates are relative to the top left corner of the window.
///
public event EventHandler MouseDoubleClick;
///
/// Occurs when the mouse wheel is rotated while over the window.
/// Mouse coordinates are relative to the top left corner of the window.
///
public event EventHandler MouseWheel;
///
/// Occurs when the mouse enters the window.
/// Mouse coordinates are an absolute position on the screen.
///
public event EventHandler MouseEnter;
///
/// Occurs when the mouse leaves the window.
/// Mouse coordinates are an absolute position on the screen.
///
public event EventHandler MouseLeave;
#endregion
#region Protected Events
///
/// Occurs when the client area is either hidden or shown.
///
protected event EventHandler ClientVisibleChanged;
///
/// Occurs when the mouse is moved over the client area.
/// Mouse coordinates are relative to the client area.
///
protected event EventHandler ClientMouseMove;
///
/// Occurs when a mouse button is pressed over the client area.
/// Mouse coordinates are relative to the client area.
///
protected event EventHandler ClientMouseDown;
///
/// Occurs when a mouse button is released over the client area.
/// Mouse coordinates are relative to the client area. The message
/// can only be eaten if the cooresponding MouseDown message was
/// also eaten.
///
protected event EventHandler ClientMouseUp;
///
/// Occurs when a mouse button is clicked twice over the client area.
/// Mouse coordinates are relative to the client area.
///
protected event EventHandler ClientMouseDoubleClick;
///
/// Occurs when the mouse wheel is rotated over the client area.
/// Mouse coordinates are relative to the client area.
///
protected event EventHandler ClientMouseWheel;
///
/// Occurs when the mouse is moved while one or more mouse buttons is
/// held down. The events will continue to occur until the button
/// is released, even if the mouse is dragged outside the client area.
///
protected event EventHandler ClientMouseDrag;
///
/// Occurs when the mouse enters the client area.
/// Mouse coordinates are relative to the client area.
///
protected event EventHandler ClientMouseEnter;
///
/// Occurs when the mouse exits the client area.
/// Mouse coordinates are relative to the client area.
///
protected event EventHandler ClientMouseLeave;
///
/// Lets the client handle window messages.
///
protected event EventHandler WindowMessage;
///
/// The Alpha value has changed
///
protected event EventHandler AlphaChanged;
#endregion
#region Creation and Disposal
/// Creates a new instance of a WindowHud.
/// The size and location of the entire window,
/// including the title bar.
/// The title of the window.
/// The manager for this window.
public WindowHud(Rectangle region, string title, HudManager manager)
{
mRegion = ConstrainRegion(region);
mClientRegion = CalculateClientRegion(mRegion);
mClientImage = new Bitmap(mClientRegion.Width, mClientRegion.Height);
mTitle = title;
mManager = manager;
mFaderTimer = new DecalTimer();
mFaderTimer.Timeout += new Decal.Interop.Input.ITimerEvents_TimeoutEventHandler(FaderTimer_Timeout);
// For fading
MouseEnter += new EventHandler(FadeIn);
MouseLeave += new EventHandler(FadeOut);
// This will call RecreateHud()
Manager.RegisterHud(this, false);
}
///
/// Performs cleanup operations like unregistering events and disabling
/// the HUD. To dispose all active windows at once, the manager's
/// Dispose() method.
///
public virtual void Dispose()
{
if (mDisposed)
return;
MouseEnter -= FadeIn;
MouseLeave -= FadeOut;
if (mFaderTimer.Running) { mFaderTimer.Stop(); }
mFaderTimer.Timeout -= FaderTimer_Timeout;
Manager.UnregisterHud(this);
DisposeHudsInternal();
mVisible = false;
mDisposed = true;
}
#endregion
#region Public Accessors and Methods
/// Gets the HudManager for this window.
public HudManager Manager
{
get { return mManager; }
}
/// Gets or sets whether the window is visible to the user.
public bool Visible
{
get { return mVisible; }
set
{
if (mVisible != value)
{
bool client = ClientVisible;
mVisible = value;
if (VisibleChanged != null)
{
VisibleChanged(this, EventArgs.Empty);
}
if (ClientVisibleChanged != null && client != ClientVisible)
{
ClientVisibleChanged(this, EventArgs.Empty);
}
RepaintWindow();
Repaint();
if (mVisible)
{
if (MouseOnWindow || Sticky)
{
AlphaFrame = AlphaFrameActive;
}
else
{
AlphaFrame = AlphaFrameInactive;
}
Manager.BringToFront(this, false);
}
else
{
mMouseOnWindow = false;
mMouseOnClient = false;
mMouseOnClose = false;
mMouseOnMinRestore = false;
mMouseOnSticky = false;
mMouseButtons = MouseButtons.None;
mMouseLastClickButton = MouseButtons.None;
DisposeHudsInternal();
}
}
if (mVisible)
{
Minimized = false;
}
}
}
/// Gets or sets whether the window is minimized.
public bool Minimized
{
get { return mMinimized; }
set
{
if (mMinimized != value)
{
bool client = ClientVisible;
mMinimized = value;
if (MinimizedChaged != null)
{
MinimizedChaged(this, EventArgs.Empty);
}
if (ClientVisibleChanged != null && client != ClientVisible)
{
ClientVisibleChanged(this, EventArgs.Empty);
}
CalculateHighlightedBorder();
RepaintWindow();
}
}
}
///
/// Gets or sets whether the window is sticky. If the window is sticky,
/// it will not fade out when the mouse leaves the window. This only
/// has an effect when has a different
/// value from and/or
/// has a different value from
/// .
///
public bool Sticky
{
get { return mSticky; }
set
{
if (mSticky != value)
{
mSticky = value;
if (StickyChaged != null)
{
StickyChaged(this, EventArgs.Empty);
}
if (mSticky)
{
AlphaFrame = AlphaFrameActive;
}
else if (!MouseOnWindow)
{
FadeOut(null, null);
}
DrawControlBoxes(false);
}
}
}
///
/// Gets or sets whether this Hud is always on top of other Huds
/// managed by the same HudManager.
///
public bool AlwaysOnTop
{
get { return Manager.IsAlwaysOnTop(this); }
set { Manager.SetAlwaysOnTop(this, value); }
}
/// Gets whether Dispose() has been called for this window.
public bool Disposed
{
get { return mDisposed; }
}
/// Gets whether the the user is moving the HUD.
public bool IsMoving
{
get { return mMoveResizeMode == MoveResizeMode.Moving; }
}
/// Gets whether the user is resizing the HUD.
public bool IsResizing
{
get { return (mMoveResizeMode & MoveResizeMode.Resizing) != 0; }
}
/// Gets whether the mouse is over the window.
public bool MouseOnWindow
{
get { return mMouseOnWindow; }
}
/// Gets or sets the window's size and position.
public Rectangle Region
{
get { return mRegion; }
set
{
bool moved = mRegion.Location != value.Location;
bool resized = mRegion.Size != value.Size;
mRegion = ConstrainRegion(value);
mClientRegion = CalculateClientRegion(mRegion);
if (moved)
{
if (HudsAreCreated)
{
mHud.Region = new Rectangle(mRegion.Location, mHud.Region.Size);
}
if (MoveEnd != null)
{
MoveEnd(this, EventArgs.Empty);
}
}
if (resized)
{
if (ResizeEnd != null)
{
ResizeEnd(this, EventArgs.Empty);
}
Repaint();
RepaintWindow();
}
}
}
///
/// Gets or sets the minimum size of the entire window, including
/// the title bar. Must be >= 24.
///
public Size MinSize
{
get { return mMinSize; }
set
{
mMinSize = new Size(Math.Max(value.Width, MinimizedWidth), Math.Max(value.Height, 24));
if (Width < mMinSize.Width || Height < mMinSize.Height)
{
int newWidth = Width, newHeight = Height;
if (newWidth < mMinSize.Width)
newWidth = mMinSize.Width;
if (newHeight < mMinSize.Height)
newHeight = mMinSize.Height;
Size = new Size(newWidth, newHeight);
}
}
}
///
/// Gets or sets the maximum size of the entire window, including
/// the title bar. Must be <= 1600.
///
public Size MaxSize
{
get { return mMaxSize; }
set
{
mMaxSize = new Size(Math.Min(value.Width, 1600), Math.Min(value.Height, 1600));
if (Width > mMaxSize.Width || Height > mMaxSize.Height)
{
int newWidth = Width, newHeight = Height;
if (newWidth > mMaxSize.Width)
newWidth = mMaxSize.Width;
if (newHeight > mMaxSize.Height)
newHeight = mMaxSize.Height;
Size = new Size(newWidth, newHeight);
}
Manager.RecreateHud(this);
}
}
///
/// Gets the mouse buttons that were pressed while over the client area
/// and not yet released. This can be used to determine if a drag is
/// in progress.
///
protected MouseButtons ClientMouseButtons
{
get { return mClientMouseButtons; }
}
#region Region Accessors
/// Gets or sets the window's position.
public Point Location
{
get { return mRegion.Location; }
set { Region = new Rectangle(value, Region.Size); }
}
/// Gets or sets the window's size.
public Size Size
{
get { return mRegion.Size; }
set { Region = new Rectangle(Region.Location, value); }
}
/// Gets or sets the window's x-coordinate.
public int X
{
get { return mRegion.X; }
set { Region = new Rectangle(value, Region.Y, Region.Width, Region.Height); }
}
/// Gets or sets the window's y-coordinate.
public int Y
{
get { return mRegion.Y; }
set { Region = new Rectangle(Region.X, value, Region.Width, Region.Height); }
}
/// Gets or sets the window's width.
public int Width
{
get { return mRegion.Width; }
set { Region = new Rectangle(Region.X, Region.Y, value, Region.Height); }
}
/// Gets or sets the window's height.
public int Height
{
get { return mRegion.Height; }
set { Region = new Rectangle(Region.X, Region.Y, Region.Width, value); }
}
/// Gets the x-coordinate of the window's left edge.
public int Left { get { return mRegion.Left; } }
/// Gets the x-coordinate of the window's right edge.
public int Right { get { return mRegion.Right; } }
/// Gets the y-coordinate of the window's top edge.
public int Top { get { return mRegion.Top; } }
/// Gets the y-coordinate of the window's bottom edge.
public int Bottom { get { return mRegion.Bottom; } }
///
/// Gets or sets the minimum width of the entire window. Must be >= 24.
///
public int MinWidth
{
get { return MinSize.Width; }
set { MinSize = new Size(value, MinSize.Height); }
}
///
/// Gets or sets the minimum height of the entire window, including
/// the title bar. Must be >= 24.
///
public int MinHeight
{
get { return MinSize.Height; }
set { MinSize = new Size(MinSize.Width, value); }
}
///
/// Gets or sets the maximum width of the entire window. Must be
/// <= 1600.
///
public int MaxWidth
{
get { return MaxSize.Width; }
set { MaxSize = new Size(value, MaxSize.Height); }
}
///
/// Gets or sets the maximum height of the entire window, including
/// the title bar. Must be <= 1600.
///
public int MaxHeight
{
get { return MaxSize.Height; }
set { MaxSize = new Size(MaxSize.Width, value); }
}
///
/// Gets or sets the region of the AC window that the client part of
/// the window occupies. Setting the client region will move/resize
/// the entire window.
///
protected Rectangle ClientRegion
{
get { return mClientRegion; }
set
{
Region = new Rectangle(
value.X + mRegion.X - mClientRegion.X,
value.Y + mRegion.Y - mClientRegion.Y,
value.Width + mRegion.Width - mClientRegion.Width,
value.Height + mRegion.Height - mClientRegion.Height
);
}
}
///
/// Gets or sets the size of the client region. Setting the client
/// size will resize the entire window.
///
protected Size ClientSize
{
get { return mClientRegion.Size; }
set { ClientRegion = new Rectangle(mClientRegion.Location, value); }
}
///
/// Gets or sets the position of the client region in the AC window.
/// Setting the client position will move the entire window.
///
protected Point ClientLocation
{
get { return mClientRegion.Location; }
set { ClientRegion = new Rectangle(value, mClientRegion.Size); }
}
private int DisplayedWidth
{
get { return Minimized ? MinimizedWidth : mRegion.Width; }
}
private int DisplayedHeight
{
get { return Minimized ? MinimizedHeight : mRegion.Height; }
}
#endregion
/// Gets or sets the title of this window.
public string Title
{
get { return mTitle; }
set
{
if (mTitle != value)
{
mTitle = value;
RepaintWindow();
}
}
}
///
/// Sets the alpha transparency of both the frame and content of the
/// window, and disables fading out when the mouse is not over the window.
/// Alpha must be between 0-255; 0 is transparent and 255 is opaque.
///
/// The alpha value for AlphaFrameActive and
/// AlphaFrameInactive.
public void SetAlpha(int alphaFrame)
{
this.AlphaFrameActive = alphaFrame;
this.AlphaFrameInactive = alphaFrame;
}
///
/// Sets the alpha transparencies for when the mouse is over and off
/// of the window.
///
/// The alpha value for AlphaFrameActive and
/// AlphaClientActive.
/// The alpha value for AlphaFrameInactive
/// and AlphaClientInactive.
public void SetAlphaFading(int activeAlpha, int inactiveAlpha)
{
this.AlphaFrameActive = activeAlpha;
this.AlphaFrameInactive = inactiveAlpha;
}
///
/// Gets or sets the alpha transparency of the border and title bar of
/// the window when the mouse is hovering over the window. Alpha must
/// be between 0-255; 0 is transparent and 255 is opaque.
///
///
///
public int AlphaFrameActive
{
get { return mAlphaFrameActive; }
set
{
if (value < 0) { mAlphaFrameActive = 0; }
else if (value > 255) { mAlphaFrameActive = 255; }
else { mAlphaFrameActive = value; }
if (MouseOnWindow || Sticky)
AlphaFrame = mAlphaFrameActive;
RepaintWindow();
}
}
///
/// Gets or sets the alpha transparency of the content of the window
/// when the mouse is not over the window. Alpha must be between
/// 0-255; 0 is transparent and 255 is opaque.
///
///
///
public int AlphaFrameInactive
{
get { return mAlphaFrameInactive; }
set
{
if (value < 0) { mAlphaFrameInactive = 0; }
else if (value > 255) { mAlphaFrameInactive = 255; }
else { mAlphaFrameInactive = value; }
if (!MouseOnWindow && !Sticky)
AlphaFrame = mAlphaFrameInactive;
RepaintWindow();
}
}
///
/// Gets or sets the outer color for the border.
///
public Color BorderColor1
{
get { return mBorderColor1; }
set { mBorderColor1 = value; RepaintWindow(); }
}
///
/// Gets or sets the inner color for the border.
///
public Color BorderColor2
{
get { return mBorderColor2; }
set { mBorderColor2 = value; RepaintWindow(); }
}
///
/// Gets or sets the color of the border when the user mouses over the border.
///
public Color BorderColorHighlight
{
get { return mBorderColorHighlight; }
set { mBorderColorHighlight = value; RepaintWindow(); }
}
///
/// Gets or sets the background color of the entire window.
///
public Color BackgroundColor
{
get { return mBackgroundColor; }
set { mBackgroundColor = value; RepaintWindow(); }
}
///
/// Gets or sets the method that the window will use to redraw the
/// client area as the window is resized.
///
public HudResizeDrawMode ResizeDrawMode
{
get { return mResizeDrawMode; }
set { mResizeDrawMode = value; }
}
#endregion
#region Protected Accesors and Methods
protected PluginHost Host
{
get { return Manager.Host; }
}
protected CoreManager Core
{
get { return Manager.Core; }
}
/// Gets the image used for drawing the client.
protected Bitmap ClientImage
{
get { return mClientImage; }
}
/// Gets the mouse's absolute position in the AC window.
protected Point MouseLocation
{
get { return mMouseLocation; }
}
/// Gets the mouse's position relative to the client window.
protected Point MouseLocationClient
{
get { return new Point(mMouseLocation.X - mClientRegion.X, mMouseLocation.Y - mClientRegion.Y); }
}
/// Gets whether the mouse is over the client region.
protected bool MouseOnClient
{
get { return mMouseOnClient; }
}
/// Gets whether the client area is visible.
protected bool ClientVisible
{
get { return Visible && !Minimized; }
}
///
/// Converts absolute screen coordinates into coordinates relative to
/// the client's location.
///
/// The absolute screen coordinates.
/// Coordinates relative to the client's location.
protected Point ScreenToClient(Point p)
{
return new Point(p.X - ClientLocation.X, p.Y - ClientLocation.Y);
}
///
/// Converts coordinates relative to the client's location into
/// absolute screen coordinates.
///
/// The coordinates relative to the client's location.
/// Absolute screen coordinates.
protected Point ClientToScreen(Point p)
{
return new Point(p.X + ClientLocation.X, p.Y + ClientLocation.Y);
}
///
/// Causes the function to be called
/// on the next frame. Multiple calls to this function before
/// the next frame will result in only one call to PaintClient().
///
protected void Repaint()
{
mClientNeedsRepaint = true;
// The msRepaintHeartbeat timer will call RepaintHeartbeat() next frame
}
///
/// This is where the client should do all of its painting onto the
/// GDI+ surface.
/// You cannot call this function directly; instead call
/// , which will indirectly call this.
///
/// The GDI+ surface to draw on.
/// Indicates whether the bitmap has been
/// cleared since the last time this function was called (e.g.
/// because the image was resized).
protected abstract void PaintClient(Graphics g, bool imageDataLost);
///
/// Shrinks (or grows) a rectangle by the specified number in every
/// direction. That is, it increases the left and top, and decreases
/// the right and bottom of the rectangle by the specified amount.
///
/// The rectangle to shrink or grow
/// The amout to shrink. Use a negative number
/// to grow.
/// A new rectangle that is a shrunken/grown version of the
/// original.
protected Rectangle ShrinkRect(Rectangle r, int shrinkBy)
{
return new Rectangle(r.X + shrinkBy, r.Y + shrinkBy,
r.Width - 2 * shrinkBy, r.Height - 2 * shrinkBy);
}
#endregion
#region Utility Functions
private bool CalcMouseOnClient()
{
return Visible && mMouseOnWindow && !Minimized &&
mClientRegion.Contains(mMouseLocation);
}
private bool CalcMouseOnWindow()
{
return Visible && mRegion.Contains(mMouseLocation) &&
(!Minimized || mMouseLocation.Y <= (mRegion.Y + TitleBarHeight + BorderWidth));
}
private Rectangle CalculateClientRegion(Rectangle region)
{
return new Rectangle(
region.X + BorderPaddedWidth,
region.Y + TitleBarPaddedHeight + 2 * BorderPaddedWidth,
region.Width - 2 * BorderPaddedWidth,
region.Height - TitleBarPaddedHeight - 3 * BorderPaddedWidth);
}
private Rectangle ConstrainRegion(Rectangle region)
{
if (region.Width < MinWidth) { region.Width = MinWidth; }
else if (region.Width > MaxWidth) { region.Width = MaxWidth; }
if (region.Height < MinHeight) { region.Height = MinHeight; }
else if (region.Height > MaxHeight) { region.Height = MaxHeight; }
if (region.X < MinWidth - BorderPaddedWidth - region.Width)
{
region.X = MinWidth - BorderPaddedWidth - region.Width;
}
if (region.Y < 24 - TitleBarPaddedHeight)
{
region.Y = 24 - TitleBarPaddedHeight;
}
return region;
}
private Rectangle CloseBoxRect
{
get
{
return new Rectangle(
mRegion.X + DisplayedWidth - BorderPaddedWidth - ControlSize,
mRegion.Top + BorderPaddedWidth, ControlSize, ControlSize);
}
}
private Rectangle MinRestoreBoxRect
{
get
{
return new Rectangle(
mRegion.X + DisplayedWidth - BorderPaddedWidth - ControlSize - ControlPaddedSize,
mRegion.Top + BorderPaddedWidth, ControlSize, ControlSize);
}
}
private Rectangle StickyBoxRect
{
get
{
return new Rectangle(
mRegion.X + DisplayedWidth - BorderPaddedWidth - ControlSize - 2 * ControlPaddedSize,
mRegion.Top + BorderPaddedWidth, ControlSize, ControlSize);
}
}
private Rectangle TitleBarRect
{
get
{
return new Rectangle(mRegion.Left + BorderWidth, mRegion.Top + BorderWidth,
DisplayedWidth - 2 * BorderWidth, TitleBarHeight + BorderWidth);
}
}
private Rectangle RelToAbs(Rectangle r)
{
return new Rectangle(r.X + mRegion.X, r.Y + mRegion.Y, r.Width, r.Height);
}
private Rectangle AbsToRel(Rectangle r)
{
return new Rectangle(r.X - mRegion.X, r.Y - mRegion.Y, r.Width, r.Height);
}
private Border HighlightedBorder
{
get { return mHighlightedBorder; }
set
{
if (mHighlightedBorder != value)
{
mHighlightedBorder = value;
RepaintBorders();
}
}
}
private Point ClientOffset
{
get { return new Point(mClientRegion.X - mRegion.X, mClientRegion.Y - mRegion.Y); }
}
private Rectangle RelativeClientRegion
{
get { return new Rectangle(ClientOffset, ClientSize); }
}
#endregion
#region Painting
/// This is called by the manager each frame.
void IManagedHud.RepaintHeartbeat()
{
if (Heartbeat != null)
Heartbeat(this, EventArgs.Empty);
if (!Visible)
{
DisposeHudsInternal();
}
else
{
if (!HudsAreCreated)
{
Manager.RecreateHud(this);
}
else if (mHud.Region.Location != mRegion.Location)
{
mHud.Region = new Rectangle(mRegion.Location, mHud.Region.Size);
}
if (mWindowNeedsRepaint)
{
PaintWindowInternal();
}
else
{
if (mBordersNeedRepaint)
{
PaintBorders(false, false);
}
if (mClientNeedsRepaint)
{
DrawClientImage(false, true);
}
}
mHud.Enabled = Visible;
}
}
///
/// Deletes and recreates the window huds, causing this window to be
/// the topmost window. Do not use this function if this hud is
/// managed by a HudManager. Instead, use
/// .
///
public virtual void RecreateHud()
{
DisposeHudsInternal();
if (Visible)
{
mHud = Host.Render.CreateHud(new Rectangle(mRegion.Location, MaxSize));
mHud.Enabled = true;
mHud.Alpha = AlphaFrame;
PaintWindowInternal();
//DrawClientImage();
}
}
private bool HudsAreCreated
{
get { return mHud != null && !mHud.Lost; }
}
private void DisposeHudsInternal()
{
if (mHud != null)
{
mHud.Enabled = false;
Host.Render.RemoveHud(mHud);
mHud.Dispose();
mHud = null;
}
}
private void RepaintWindow()
{
mWindowNeedsRepaint = true;
// The repaint will happen in RepaintHeartbeat(), next frame
}
private void RepaintBorders()
{
mBordersNeedRepaint = true;
}
// mHud must not be lost or null
private void PaintWindowInternal()
{
mWindowNeedsRepaint = false;
mHud.Clear();
mHud.Fill(new Rectangle(BorderWidth, BorderWidth, DisplayedWidth - 2 * BorderWidth,
DisplayedHeight - 2 * BorderWidth), BackgroundColor);
PaintBorders(false, true);
// Draw title
Rectangle titleRect = new Rectangle(BorderPaddedWidth, BorderPaddedWidth,
DisplayedWidth - 2 * BorderPaddedWidth - NumControlBoxes * ControlPaddedSize,
TitleBarPaddedHeight);
mHud.BeginText("Times New Roman", 14);
mHud.WriteText(Title, Color.White, WriteTextFormats.Center, titleRect);
mHud.EndText();
DrawControlBoxes(true);
DrawClientImage(true, false);
mHud.EndRender();
//if (ResizeDrawMode != HudResizeDrawMode.Repaint && IsResizing)
//{
// DrawClientImage();
//}
}
private void DrawClientImage(bool inRender, bool paintBackground)
{
if (!ClientVisible)
return;
bool startInRender = inRender;
bool endInRender = inRender;
if (mClientNeedsRepaint)
{
if (mClientImage.Size != mClientRegion.Size)
{
mClientImage = new Bitmap(mClientRegion.Width, mClientRegion.Height);
mClientImageLost = true;
}
using (Graphics gClient = Graphics.FromImage(mClientImage))
{
PaintClient(gClient, mClientImageLost);
}
mClientNeedsRepaint = false;
mClientImageLost = false;
}
Rectangle drawRect = RelativeClientRegion;
if (paintBackground)
{
if (startInRender)
{
mHud.EndRender();
startInRender = false;
}
mHud.Fill(drawRect, BackgroundColor);
}
Bitmap drawImage = mClientImage;
if (mClientRegion.Size != mClientImage.Size)
{
if (ResizeDrawMode == HudResizeDrawMode.Scale)
{
drawImage = new Bitmap(mClientImage, mClientRegion.Size);
}
else if (ResizeDrawMode == HudResizeDrawMode.Crop)
{
if (mClientImage.Width < mClientRegion.Width
&& mClientImage.Height < mClientRegion.Height)
{
drawRect.Size = mClientImage.Size;
}
else
{
Size sz = new Size(Math.Min(mClientRegion.Width, mClientImage.Width),
Math.Min(mClientRegion.Height, mClientImage.Height));
drawImage = new Bitmap(sz.Width, sz.Height);
drawRect.Size = sz;
Graphics.FromImage(drawImage).DrawImageUnscaled(mClientImage, 0, 0);
}
}
else if (ResizeDrawMode == HudResizeDrawMode.CropCenter)
{
if (mClientImage.Width < mClientRegion.Width
&& mClientImage.Height < mClientRegion.Height)
{
drawRect.X += (mClientRegion.Width - mClientImage.Width) / 2;
drawRect.Y += (mClientRegion.Height - mClientImage.Height) / 2;
drawRect.Size = mClientImage.Size;
}
else
{
drawImage = new Bitmap(mClientRegion.Width, mClientRegion.Height);
drawRect.Size = mClientRegion.Size;
Graphics.FromImage(drawImage).DrawImageUnscaled(mClientImage,
(mClientRegion.Width - mClientImage.Width) / 2,
(mClientRegion.Height - mClientImage.Height) / 2);
}
}
}
if (!startInRender)
mHud.BeginRender();
mHud.DrawImage(drawImage, drawRect);
if (!endInRender)
mHud.EndRender();
}
private void PaintBorders(bool startInRender, bool endInRender)
{
if (!HudsAreCreated)
return;
mBordersNeedRepaint = false;
if (startInRender) { mHud.EndRender(); }
Color north = ((HighlightedBorder & Border.North) == 0) ? BorderColor1 : BorderColorHighlight;
mHud.Fill(new Rectangle(1, 1, DisplayedWidth - 2, 3), north);
mHud.Fill(new Rectangle(1, 2, DisplayedWidth - 2, 1), BorderColor2);
Color south = ((HighlightedBorder & Border.South) == 0) ? BorderColor1 : BorderColorHighlight;
mHud.Fill(new Rectangle(1, DisplayedHeight - 4, DisplayedWidth - 2, 3), south);
mHud.Fill(new Rectangle(1, DisplayedHeight - 3, DisplayedWidth - 2, 1), BorderColor2);
Color west = ((HighlightedBorder & Border.West) == 0) ? BorderColor1 : BorderColorHighlight;
mHud.Fill(new Rectangle(1, 1, 3, DisplayedHeight - 2), west);
mHud.Fill(new Rectangle(2, 1, 1, DisplayedHeight - 2), BorderColor2);
Color east = ((HighlightedBorder & Border.East) == 0) ? BorderColor1 : BorderColorHighlight;
mHud.Fill(new Rectangle(DisplayedWidth - 4, 1, 3, DisplayedHeight - 2), east);
mHud.Fill(new Rectangle(DisplayedWidth - 3, 1, 1, DisplayedHeight - 2), BorderColor2);
if (!Minimized)
{
Color title = ((HighlightedBorder & Border.Title) == 0) ? BorderColor1 : BorderColorHighlight;
mHud.Fill(new Rectangle(BorderWidth, BorderWidth + TitleBarHeight,
DisplayedWidth - 2 * BorderWidth, 3), title);
mHud.Fill(new Rectangle(BorderWidth - 1, BorderWidth + TitleBarHeight + 1,
DisplayedWidth - 2 * BorderWidth + 2, 1), BorderColor2);
}
mHud.BeginRender();
// Draw corners
Rectangle cornerRect = new Rectangle(0, 0, 5, 5);
mHud.DrawImage(Icons.Window.BorderCorner, cornerRect);
cornerRect.X = DisplayedWidth - 5;
mHud.DrawImage(Icons.Window.BorderCorner, cornerRect);
cornerRect.Y = DisplayedHeight - 5;
mHud.DrawImage(Icons.Window.BorderCorner, cornerRect);
cornerRect.X = 0;
mHud.DrawImage(Icons.Window.BorderCorner, cornerRect);
if (!endInRender) { mHud.EndRender(); }
}
private void DrawControlBoxes(bool inRender)
{
if (!HudsAreCreated)
return;
if (!inRender) { mHud.BeginRender(); }
if (mMouseOnClose && CloseBoxRect.Contains(mMouseLocation))
{
mHud.DrawImage(Icons.Window.CloseBox_pressed, AbsToRel(CloseBoxRect));
}
else
{
mHud.DrawImage(Icons.Window.CloseBox, AbsToRel(CloseBoxRect));
}
if (mMouseOnMinRestore && MinRestoreBoxRect.Contains(mMouseLocation))
{
if (Minimized)
mHud.DrawImage(Icons.Window.RestoreBox_pressed, AbsToRel(MinRestoreBoxRect));
else
mHud.DrawImage(Icons.Window.MinimizeBox_pressed, AbsToRel(MinRestoreBoxRect));
}
else
{
if (Minimized)
mHud.DrawImage(Icons.Window.RestoreBox, AbsToRel(MinRestoreBoxRect));
else
mHud.DrawImage(Icons.Window.MinimizeBox, AbsToRel(MinRestoreBoxRect));
}
if (AlphaFadingEnabled)
{
if (Sticky || (mMouseOnSticky && StickyBoxRect.Contains(mMouseLocation)))
{
mHud.DrawImage(Icons.Window.StickyBox_pressed, AbsToRel(StickyBoxRect));
}
else
{
mHud.DrawImage(Icons.Window.StickyBox, AbsToRel(StickyBoxRect));
}
}
if (!inRender) { mHud.EndRender(); }
}
#endregion
#region Alpha Fading
private bool AlphaFadingEnabled
{
get { return AlphaFrameActive != AlphaFrameInactive; }
}
// Handles the MouseEnter event
private void FadeIn(object sender, HudMouseEventArgs e)
{
FadeAlpha(AlphaFrameActive, 300, 0);
}
// Handles the MouseLeave event
private void FadeOut(object sender, HudMouseEventArgs e)
{
if (!Sticky)
{
FadeAlpha(AlphaFrameInactive, 500, 0);
}
}
protected int AlphaFrame
{
get { return mAlphaFrame; }
set
{
if (mAlphaFrame != value)
{
if (value < 0) { mAlphaFrame = 0; }
else if (value > 255) { mAlphaFrame = 255; }
else { mAlphaFrame = value; }
if (AlphaChanged != null)
AlphaChanged(this, new AlphaChangedEventArgs(mAlphaFrame));
}
if (HudsAreCreated)
mHud.Alpha = mAlphaFrame;
}
}
/// Fades the window's opacity to the specified value.
/// The target alpha value for the
/// border and title bar. Must be between 0-255.
/// The target alpha value for the
/// content of the window. Must be between 0-255.
/// The duration of the fade in
/// milliseconds. Must be >= 0.
/// The initial delay before
/// fading.
/// If any of the arguments are
/// not within valid ranges.
private void FadeAlpha(int finalAlphaFrame, long durationMillis, int initialDelayMillis)
{
if (finalAlphaFrame < 0) { finalAlphaFrame = 0; }
if (finalAlphaFrame > 255) { finalAlphaFrame = 255; }
if (mFaderTimer.Running)
{
mFaderTimer.Stop();
int desiredDist = Math.Abs(finalAlphaFrame - mFadeEndAlphaFrame);
int actualDist = Math.Abs(finalAlphaFrame - AlphaFrame);
if (desiredDist == 0)
mFadeDurationMillis = durationMillis;
else
mFadeDurationMillis = durationMillis * actualDist / desiredDist;
}
else
{
mFadeDurationMillis = durationMillis;
}
if (!Visible || mFadeDurationMillis <= 0)
{
AlphaFrame = finalAlphaFrame;
return;
}
if (AlphaFrame == finalAlphaFrame)
return;
mFadeEndAlphaFrame = finalAlphaFrame;
mFadeStartAlphaFrame = AlphaFrame;
mFadeBeginTime = DateTime.Now;
if (initialDelayMillis <= 0)
{
mFadeInitialDelay = false;
mFaderTimer.Start(1);
}
else
{
mFadeInitialDelay = true;
mFaderTimer.Start(initialDelayMillis);
}
}
private void FaderTimer_Timeout(Decal.Interop.Input.Timer Source)
{
try
{
if (mFadeInitialDelay)
{
mFadeInitialDelay = false;
mFaderTimer.Stop();
mFaderTimer.Start(1);
mFadeBeginTime = DateTime.Now;
return;
}
long elapsedMillis = ((TimeSpan)DateTime.Now.Subtract(mFadeBeginTime)).Milliseconds;
int newAlphaFrame = (int)(mFadeStartAlphaFrame +
(mFadeEndAlphaFrame - mFadeStartAlphaFrame) * elapsedMillis / mFadeDurationMillis);
bool fadingUpFrame = mFadeEndAlphaFrame > mFadeStartAlphaFrame;
bool done = true;
if (fadingUpFrame && newAlphaFrame >= mFadeEndAlphaFrame ||
!fadingUpFrame && newAlphaFrame <= mFadeEndAlphaFrame)
{
AlphaFrame = mFadeEndAlphaFrame;
}
else
{
AlphaFrame = newAlphaFrame;
done = false;
}
if (done) { mFaderTimer.Stop(); }
}
catch (Exception ex) { Manager.HandleException(ex); }
}
#endregion
#region WindowMessage Handling
void IManagedHud.WindowMessage(WindowMessageEventArgs e)
{
if (WindowMessage != null)
WindowMessage(this, e);
// Only process mouse messages when the window is visible
if (!Visible || e.Msg < WM_MOUSEFIRST || e.Msg > WM_MOUSELAST)
return;
Point prevLocation = mMouseLocation;
bool prevOnWindow = mMouseOnWindow;
bool prevOnClient = mMouseOnClient;
mMouseLocation = new Point(e.LParam);
if (e.Msg == WM_MOUSEWHEEL)
{
// WM_MOUSEWHEEL messages are apparently absolute on the screen,
// not relative to the AC window; fix with ScreenToClient
ScreenToClient(Host.Decal.Hwnd, ref mMouseLocation);
}
mMouseOnWindow = CalcMouseOnWindow();
mMouseOnWindow = mMouseOnWindow && Manager.MouseHoveringOnHud(this);
mMouseOnClient = CalcMouseOnClient();
// Calculate relative coordinates
int wX = mMouseLocation.X - mRegion.X;
int wY = mMouseLocation.Y - mRegion.Y;
int cX = mMouseLocation.X - mClientRegion.X;
int cY = mMouseLocation.Y - mClientRegion.Y;
short fwKeys = (short)(e.WParam & 0xFFFF);
MouseButtons button = MouseButtons.None;
if (e.Msg == WM_MOUSEMOVE) { }
else if (e.Msg == WM_LBUTTONDOWN || e.Msg == WM_LBUTTONUP)
button = MouseButtons.Left;
else if (e.Msg == WM_RBUTTONDOWN || e.Msg == WM_RBUTTONUP)
button = MouseButtons.Right;
else if (e.Msg == WM_MBUTTONDOWN || e.Msg == WM_MBUTTONUP)
button = MouseButtons.Middle;
bool dblClick = false;
if (e.Msg == WM_LBUTTONDOWN || e.Msg == WM_RBUTTONDOWN || e.Msg == WM_MBUTTONDOWN)
{
dblClick = (mMouseLastClickButton == button)
&& Math.Abs(mMouseLocation.X - mMouseLastClickLocation.X) <= msDblClickRect.Width
&& Math.Abs(mMouseLocation.Y - mMouseLastClickLocation.Y) <= msDblClickRect.Height
&& Math.Abs(DateTime.Now.Ticks - mMouseLastClickTicks) <= msDblClickTicks;
mMouseLastClickTicks = dblClick ? long.MinValue : DateTime.Now.Ticks;
mMouseLastClickLocation = mMouseLocation;
mMouseLastClickButton = button;
}
#region Handle Moving, Resizing, and Control Button Clicks
if (e.Msg == WM_MOUSEMOVE)
{
// Handle moving the window
if (IsMoving)
{
mRegion.X = mOriginalRegion.X + mMouseLocation.X - mMouseDownLocation.X;
mRegion.Y = mOriginalRegion.Y + mMouseLocation.Y - mMouseDownLocation.Y;
mRegion = ConstrainRegion(mRegion);
mClientRegion = CalculateClientRegion(mRegion);
// Recalculate whether the mouse is on the window and
// client, since the regions have changed
mMouseOnWindow = CalcMouseOnWindow();
mMouseOnClient = CalcMouseOnClient();
if (HudsAreCreated)
{
mHud.Region = new Rectangle(mRegion.Location, mHud.Region.Size);
}
if (Moving != null)
{
Moving(this, EventArgs.Empty);
}
}
// Handle resizing the window
else if (IsResizing)
{
if ((mMoveResizeMode & MoveResizeMode.ResizeN) != 0)
{
int oldBottom = mRegion.Bottom;
mRegion.Y = mOriginalRegion.Y + mMouseLocation.Y - mMouseDownLocation.Y;
mRegion.Height = oldBottom - mRegion.Y;
if (mRegion.Height < MinHeight)
{
mRegion.Y = oldBottom - MinHeight;
mRegion.Height = MinHeight;
}
else if (mRegion.Height > MaxHeight)
{
mRegion.Y = oldBottom - MaxHeight;
mRegion.Height = MaxHeight;
}
}
else if ((mMoveResizeMode & MoveResizeMode.ResizeS) != 0)
{
mRegion.Height = mOriginalRegion.Height + mMouseLocation.Y - mMouseDownLocation.Y;
}
if ((mMoveResizeMode & MoveResizeMode.ResizeW) != 0)
{
int oldRight = mRegion.Right;
mRegion.X = mOriginalRegion.X + mMouseLocation.X - mMouseDownLocation.X;
mRegion.Width = oldRight - mRegion.X;
if (mRegion.Width < MinWidth)
{
mRegion.X = oldRight - MinWidth;
mRegion.Width = MinWidth;
}
else if (mRegion.Width > MaxWidth)
{
mRegion.X = oldRight - MaxWidth;
mRegion.Width = MaxWidth;
}
}
else if ((mMoveResizeMode & MoveResizeMode.ResizeE) != 0)
{
mRegion.Width = mOriginalRegion.Width + mMouseLocation.X - mMouseDownLocation.X;
}
mRegion = ConstrainRegion(mRegion);
mClientRegion = CalculateClientRegion(mRegion);
// Recalculate whether the mouse is on the window and
// client, since the regions have changed
mMouseOnWindow = CalcMouseOnWindow();
mMouseOnClient = CalcMouseOnClient();
if (Resizing != null)
{
Resizing(this, EventArgs.Empty);
}
RepaintWindow();
if (ResizeDrawMode == HudResizeDrawMode.Repaint)
Repaint();
}
CalculateHighlightedBorder();
// Handle the mouse entering/leaving the control boxes
if (mMouseOnClose || mMouseOnMinRestore || mMouseOnSticky)
{
DrawControlBoxes(false);
}
} // if (e.Msg == WM_MOUSEMOVE)
else if (e.Msg == WM_LBUTTONDOWN && mMouseOnWindow)
{
// Put this window on top of other windows
Manager.BringToFront(this, false);
if (CloseBoxRect.Contains(mMouseLocation))
{
mMouseOnClose = true;
DrawControlBoxes(false);
e.Eat = true;
}
else if (MinRestoreBoxRect.Contains(mMouseLocation))
{
mMouseOnMinRestore = true;
DrawControlBoxes(false);
e.Eat = true;
}
else if (AlphaFadingEnabled && StickyBoxRect.Contains(mMouseLocation))
{
mMouseOnSticky = true;
DrawControlBoxes(false);
e.Eat = true;
}
else if (TitleBarRect.Contains(mMouseLocation))
{
if (dblClick)
{
Minimized = !Minimized;
}
else
{
mMoveResizeMode = MoveResizeMode.Moving;
mMouseDownLocation = mMouseLocation;
mOriginalRegion = mRegion;
}
e.Eat = true;
}
else
{
mMoveResizeMode = MoveResizeMode.Idle;
if (!Minimized)
{
if (mMouseLocation.X <= mRegion.Left + BorderPaddedWidth)
mMoveResizeMode |= MoveResizeMode.ResizeW;
else if (mMouseLocation.X >= mRegion.Right - BorderPaddedWidth)
mMoveResizeMode |= MoveResizeMode.ResizeE;
if (mMouseLocation.Y <= mRegion.Top + BorderPaddedWidth)
mMoveResizeMode |= MoveResizeMode.ResizeN;
else if (mMouseLocation.Y >= mRegion.Bottom - BorderPaddedWidth)
mMoveResizeMode |= MoveResizeMode.ResizeS;
if (mMoveResizeMode != MoveResizeMode.Idle)
{
mMouseDownLocation = mMouseLocation;
mOriginalRegion = mRegion;
e.Eat = true;
}
}
}
} // else if (e.Msg == WM_LBUTTONDOWN && mMouseOnWindow)
else if (e.Msg == WM_LBUTTONUP)
{
if (MoveEnd != null && mMoveResizeMode == MoveResizeMode.Moving)
{
MoveEnd(this, EventArgs.Empty);
}
else if ((mMoveResizeMode & MoveResizeMode.Resizing) != 0)
{
if (ResizeEnd != null)
{
ResizeEnd(this, EventArgs.Empty);
}
// If the ResizeDrawMode is something other than Repaint,
// we need to repaint it now that we're done resizing.
if (ResizeDrawMode != HudResizeDrawMode.Repaint)
{
Repaint();
}
if (!mMouseOnWindow)
{
MouseEvent(MouseLeave, e, mMouseButtons, 0, wX, wY, 0, fwKeys);
}
}
mMoveResizeMode = MoveResizeMode.Idle;
if (mMouseOnClose || mMouseOnMinRestore || mMouseOnSticky)
{
if (mMouseOnClose && CloseBoxRect.Contains(mMouseLocation))
{
Visible = false;
}
else if (mMouseOnMinRestore && MinRestoreBoxRect.Contains(mMouseLocation))
{
Minimized = !Minimized;
}
else if (mMouseOnSticky && StickyBoxRect.Contains(mMouseLocation))
{
Sticky = !Sticky;
}
mMouseOnClose = false;
mMouseOnMinRestore = false;
mMouseOnSticky = false;
DrawControlBoxes(false);
}
} // else if (e.Msg == WM_LBUTTONUP)
#endregion
#region Generate Mouse Events
if (mMouseOnWindow)
{
switch (e.Msg)
{
case WM_MOUSEMOVE:
MouseEvent(MouseMove, e, mMouseButtons, 0, wX, wY, 0, fwKeys);
if (mMouseOnClient)
{
MouseEvent(ClientMouseMove, e, mClientMouseButtons, 0, cX, cY, 0, fwKeys);
}
break;
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
mMouseButtons |= button;
if (dblClick)
{
MouseEvent(MouseDoubleClick, e, button, 2, wX, wY, 0, fwKeys);
}
else
{
MouseEvent(MouseDown, e, button, 1, wX, wY, 0, fwKeys);
}
if (mMouseOnClient)
{
if (mClientMouseButtons == MouseButtons.None)
{
mMouseDownLocation = mMouseLocation;
}
mClientMouseButtons |= button;
if (dblClick)
{
MouseEvent(ClientMouseDoubleClick, e, button, 2, cX, cY, 0, fwKeys);
}
else
{
MouseEvent(ClientMouseDown, e, button, 1, cX, cY, 0, fwKeys);
}
}
break;
case WM_LBUTTONUP:
case WM_RBUTTONUP:
case WM_MBUTTONUP:
MouseEvent(MouseUp, e, button, 1, wX, wY, 0, fwKeys, false);
if (mMouseOnClient)
{
MouseEvent(ClientMouseUp, e, button, 1, cX, cY, 0, fwKeys, false);
}
break;
case WM_MOUSEWHEEL:
int zDelta = e.WParam >> 16;
MouseEvent(MouseWheel, e, button, 0, wX, wY, zDelta, fwKeys);
if (mMouseOnClient)
{
MouseEvent(ClientMouseWheel, e, button, 0, cX, cY, zDelta, fwKeys);
}
break;
} // switch (e.Msg)
} // if (mMouseOnWindow)
// Additional WM_MOUSEMOVE messages that don't require the mouse to be on the window
if (e.Msg == WM_MOUSEMOVE)
{
// Alert the client about dragging
if (ClientMouseDrag != null && mClientMouseButtons != MouseButtons.None)
{
int x = mMouseLocation.X - mClientRegion.X;
int y = mMouseLocation.Y - mClientRegion.Y;
int dX = mMouseLocation.X - prevLocation.X;
int dY = mMouseLocation.Y - prevLocation.Y;
HudMouseDragEventArgs args = new HudMouseDragEventArgs(mClientMouseButtons,
0, x, y, 0, fwKeys, mMouseDownLocation.X, mMouseDownLocation.Y, dX, dY);
ClientMouseDrag(this, args);
if (args.Eat)
e.Eat = true;
}
// Handle mouse entering/leaving the window or client
// This must be done after moving/resizing
if (!IsResizing)
{
// Mouse enter window
if (mMouseOnWindow && !prevOnWindow)
{
MouseEvent(MouseEnter, e, mMouseButtons, 0, wX, wY, 0, fwKeys);
}
// Mouse leave window
else if (!mMouseOnWindow && prevOnWindow)
{
MouseEvent(MouseLeave, e, mMouseButtons, 0, wX, wY, 0, fwKeys);
}
}
// Mouse enter client
if (mMouseOnClient && !prevOnClient)
{
MouseEvent(ClientMouseEnter, e, mClientMouseButtons, 0, cX, cY, 0, fwKeys);
}
// Mouse leave client
else if (!mMouseOnClient && prevOnClient)
{
MouseEvent(ClientMouseLeave, e, mClientMouseButtons, 0, cX, cY, 0, fwKeys);
}
}
#endregion
if (e.Eat && (e.Msg == WM_LBUTTONDOWN || e.Msg == WM_RBUTTONDOWN || e.Msg == WM_MBUTTONDOWN))
{
mMouseDownEaten |= button;
}
// Update which buttons are on the window and client
// This must be done after mouse events are generated
if (e.Msg == WM_LBUTTONUP || e.Msg == WM_RBUTTONUP || e.Msg == WM_MBUTTONUP)
{
mMouseButtons &= ~button;
mClientMouseButtons &= ~button;
if ((mMouseDownEaten & button) != 0) { e.Eat = true; }
mMouseDownEaten &= ~button;
}
}
private void CalculateHighlightedBorder()
{
// Highlight the border if the mouse is on it
if (!mMouseOnWindow)
{
HighlightedBorder = Border.None;
}
else if (TitleBarRect.Contains(mMouseLocation))
{
HighlightedBorder = Border.All;
}
else if (mMouseOnWindow && !Minimized)
{
Border b = Border.None;
if (mMouseLocation.Y <= mRegion.Top + BorderPaddedWidth)
b |= Border.North;
else if (mMouseLocation.Y >= mRegion.Bottom - BorderPaddedWidth)
b |= Border.South;
if (mMouseLocation.X >= mRegion.Right - BorderPaddedWidth)
b |= Border.East;
else if (mMouseLocation.X <= mRegion.Left + BorderPaddedWidth)
b |= Border.West;
HighlightedBorder = b;
}
else
{
HighlightedBorder = Border.None;
}
}
private void MouseEvent(EventHandler EventToFire, WindowMessageEventArgs e,
MouseButtons button, int clicks, int x, int y, int delta, short fwKeys)
{
MouseEvent(EventToFire, e, button, clicks, x, y, delta, fwKeys, true);
}
private void MouseEvent(EventHandler EventToFire, WindowMessageEventArgs e,
MouseButtons button, int clicks, int x, int y, int delta, short fwKeys, bool allowEat)
{
if (EventToFire != null)
{
HudMouseEventArgs args = new HudMouseEventArgs(button, clicks, x, y, delta, fwKeys);
EventToFire(this, args);
if (allowEat && args.Eat)
e.Eat = true;
}
}
bool IManagedHud.MouseHoveringObscuresOther
{
get { return mMouseOnWindow; }
}
#endregion
}
#region Helper Classes
public class HudMouseEventArgs : MouseEventArgs
{
///
/// One detent of the mouse wheel will produce a Delta equal to this.
/// A detent is one notch of the mouse wheel.
///
public const int WHEEL_DELTA = 120;
private short mModifierKeys;
private bool mEat = false;
public HudMouseEventArgs(MouseButtons button, int clicks, int x, int y, int delta, short fwKeys)
: base(button, clicks, x, y, delta)
{
mModifierKeys = fwKeys;
}
///
/// Indicates whether the Control Key was held down while this event
/// was generated.
///
public bool Control
{
get { return (mModifierKeys & KeyModifiers.Control) != 0; }
}
///
/// Indicates whether the Shift Key was held down while this event
/// was generated.
///
public bool Shift
{
get { return (mModifierKeys & KeyModifiers.Shift) != 0; }
}
///
/// Indicates whether the Left Mouse Button was held down while this
/// event was generated.
///
public bool LeftButton
{
get { return (mModifierKeys & KeyModifiers.LeftButton) != 0; }
}
///
/// Indicates whether the Right Mouse Button was held down while this
/// event was generated.
///
public bool RightButton
{
get { return (mModifierKeys & KeyModifiers.RightButton) != 0; }
}
///
/// Indicates whether the Middle Mouse Button was held down while this
/// event was generated.
///
public bool MiddleButton
{
get { return (mModifierKeys & KeyModifiers.MiddleButton) != 0; }
}
///
/// Indicates whether the First X Mouse Button was held down while this
/// event was generated.
///
public bool XButton1
{
get { return (mModifierKeys & KeyModifiers.XButton1) != 0; }
}
///
/// Indicates whether the Second X Mouse Button was held down while this
/// event was generated.
///
public bool XButton2
{
get { return (mModifierKeys & KeyModifiers.XButton2) != 0; }
}
///
/// Gets the modifier key bitmask for this event.
///
public short ModifierKeys
{
get { return mModifierKeys; }
}
///
/// Set this to True if your code handles the event, which will cause
/// the event not to be passed to AC or Decal. Leave at its default
/// value if your code does not handle the event (explicitly setting
/// this to false may have unexpeted results when the event is handled
/// by other handlers).
///
public bool Eat
{
get { return mEat; }
set { mEat = value; }
}
}
public class HudMouseDragEventArgs : HudMouseEventArgs
{
int mMouseDownX, mMouseDownY, mDeltaX, mDeltaY;
///
/// Gets the X coordinate of where the mouse drag started.
///
public int MouseDownX
{
get { return mMouseDownX; }
}
///
/// Gets the Y coordinate of where the mouse drag started.
///
public int MouseDownY
{
get { return mMouseDownY; }
}
///
/// Gets the coordinates of where the mouse drag started.
///
public Point MouseDownLocation
{
get { return new Point(mMouseDownX, mMouseDownY); }
}
///
/// Gets the amount that the X coordinate has changed since the last
/// MouseDrag event. Or, if this is the first MouseDrag event for
/// this drag, gets the amount that the X coordinate has changed
/// since the MouseDown event prior to this event.
///
public int DeltaX
{
get { return mDeltaX; }
}
///
/// Gets the amount that the Y coordinate has changed since the last
/// MouseDrag event. Or, if this is the first MouseDrag event for
/// this drag, gets the amount that the Y coordinate has changed
/// since the MouseDown event prior to this event.
///
public int DeltaY
{
get { return mDeltaY; }
}
public HudMouseDragEventArgs(MouseButtons button, int clicks, int x, int y, int delta, short fwKeys,
int mouseDownX, int mouseDownY, int deltaX, int deltaY)
: base(button, clicks, x, y, delta, fwKeys)
{
mMouseDownX = mouseDownX;
mMouseDownY = mouseDownY;
mDeltaX = deltaX;
mDeltaY = deltaY;
}
}
public enum HudResizeDrawMode
{
///
/// The client's image will be kept the same as the window is resized,
/// and repainted when the resize is done. This is the fastest option.
///
Crop,
///
/// Same as Crop, but the image will remain centered instead of
/// attached to the top-left corner.
///
CropCenter,
///
/// The client's image will be scaled as the window is resized, and
/// repainted when the resize is done.
///
Scale,
///
/// The client will be repainted continuously as the window is resized.
/// This is the slowest option.
///
Repaint,
}
public class MouseButtonsEx
{
private MouseButtons mButtons;
private short mModifierKeys;
public MouseButtonsEx(MouseButtons buttons)
{
mButtons = buttons;
mModifierKeys = 0;
}
public MouseButtonsEx(MouseButtons buttons, short modifierKeys)
{
mButtons = buttons;
mModifierKeys = modifierKeys;
}
public MouseButtons Buttons
{
get { return mButtons; }
set { mButtons = value; }
}
public short ModifierKeys
{
get { return mModifierKeys; }
set { mModifierKeys = value; }
}
public bool Matches(HudMouseEventArgs e)
{
return ((mButtons & e.Button) != 0) && ((e.ModifierKeys & mModifierKeys) == mModifierKeys);
}
public void AddModifierKey(short keysToAdd)
{
mModifierKeys |= keysToAdd;
}
public void RemoveModifierKey(short keysToRemove)
{
mModifierKeys &= (short)~keysToRemove;
}
public override string ToString()
{
return Buttons + "|" + ModifierKeys;
}
public static bool TryParse(string str, out MouseButtonsEx value)
{
string[] parts = str.Split('|');
short modifierKeys;
try
{
if (parts.Length == 2 && short.TryParse(parts[1], out modifierKeys))
{
MouseButtons buttons = (MouseButtons)Enum.Parse(typeof(MouseButtons), parts[0]);
value = new MouseButtonsEx(buttons, modifierKeys);
return true;
}
}
catch (ArgumentException) { /* Failed to parse enum */ }
value = null;
return false;
}
}
public class AlphaChangedEventArgs : EventArgs
{
private readonly int mAlpha;
public AlphaChangedEventArgs(int alpha)
{
mAlpha = alpha;
}
public int Alpha { get { return mAlpha; } }
}
#endregion
}