You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
SCJMapper-V2/Layout/ShapeKey.cs

171 lines
6.0 KiB
C#

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SCJMapper_V2.Common;
using SCJMapper_V2.Devices.Keyboard;
namespace SCJMapper_V2.Layout
{
/// <summary>
/// Key Input
/// contains a text to display at a position within a rectangle
/// </summary>
class ShapeKey : ShapeItem
{
/// <summary>
/// The SCGameKey for this Command
/// Only a single one - NO modifiers here
/// </summary>
public string SCGameKey { get; set; }
/// <summary>
/// The DX GameKey for this Command
/// </summary>
public SharpDX.DirectInput.Key DXGameKey
{
get {
return KeyboardCls.FromSCKeyboardCmd( SCGameKey );
}
}
/// <summary>
/// The Windows Virtual GameKey for this Command
/// </summary>
public VirtualKey WinVirtualKey
{
get {
return (VirtualKey)WinApi.MapVirtualKeyEx( (uint)DXGameKey, WinApi.VirtualKeyMapType.MAPVK_VSC_TO_VK_EX, IntPtr.Zero );
}
}
/// <summary>
/// Indicates that the Key symbol needs to be drawn
/// </summary>
public bool IsSymbolShape { get; set; } = false; // default
/// <summary>
/// GetRoundRectPath
/// Credit: licensed under The Code Project Open License (CPOL)
/// https://www.codeproject.com/Articles/27228/A-class-for-creating-round-rectangles-in-GDI-with
/// This function uses the AddArc method for defining the rounded rectangle path.
/// The first workaround handles the special case where the radius is 10.
/// It offsets the arc's rectangle and increases its size at a strategic point.
/// I dont have a good theory for why this works or why it is only needed for a radius of 10.
/// </summary>
private void GetRoundRectPath( ref GraphicsPath pPath, Rectangle r, int dia )
{
// diameter can't exceed width or height
if ( dia > r.Width ) dia = r.Width;
if ( dia > r.Height ) dia = r.Height;
// define a corner
var Corner = new Rectangle( r.X, r.Y, dia, dia );
pPath.Reset( ); // begin path
// top left
pPath.AddArc( Corner, 180, 90 );
// tweak needed for radius of 10 (dia of 20)
if ( dia == 20 ) {
Corner.Width += 1;
Corner.Height += 1;
r.Width -= 1; r.Height -= 1;
}
// top right
Corner.X += ( r.Width - dia - 1 );
pPath.AddArc( Corner, 270, 90 );
// bottom right
Corner.Y += ( r.Height - dia - 1 );
pPath.AddArc( Corner, 0, 90 );
// bottom left
Corner.X -= ( r.Width - dia - 1 );
pPath.AddArc( Corner, 90, 90 );
// end path
pPath.CloseFigure( );
}
/// <summary>
/// DrawRoundRect
/// Credit: licensed under The Code Project Open License (CPOL)
/// https://www.codeproject.com/Articles/27228/A-class-for-creating-round-rectangles-in-GDI-with
/// This function draws a rounded rectangle using the passed rectangle, radius, pen color, and pen width.
/// The second workaround involves using a pen width of 1 and drawing “width” number of rectangles,
/// decrementing the size of the rect each time.That alone is insufficient, because it will leave
/// holes at the corners. Instead, this deflates only the x, draws the rect, then deflates the y, and draws again.
/// </summary>
private void DrawRoundRect( Graphics pGraphics, Rectangle r, Color color, int radius, int width )
{
int dia = 2 * radius;
// set to pixel mode
var oldPageUnit = pGraphics.PageUnit;
pGraphics.PageUnit = GraphicsUnit.Pixel;
// define the pen
var pen = new Pen( color, 1 );
pen.Alignment = System.Drawing.Drawing2D.PenAlignment.Center;
// get the corner path
var path = new GraphicsPath( );
// get path
GetRoundRectPath( ref path, r, dia );
// draw the round rect
pGraphics.DrawPath( pen, path );
// if width > 1
for ( int i = 1; i < width; i++ ) {
r.Inflate( -1, 0 ); // left stroke
GetRoundRectPath( ref path, r, dia ); // get the path
pGraphics.DrawPath( pen, path ); // draw the round rect
r.Inflate( 0, -1 ); // up stroke
GetRoundRectPath( ref path, r, dia ); // get the path
pGraphics.DrawPath( pen, path ); // draw the round rect
}
// restore page unit
pGraphics.PageUnit = oldPageUnit;
}
/// <summary>
/// Draw a key
/// </summary>
/// <param name="g"></param>
/// <param name="location"></param>
/// <param name="key"></param>
private void DrawKey( Graphics g, Rectangle drawRect, string key )
{
var printSize =Size.Add( Size.Ceiling( g.MeasureString( key, MapProps.MapFont ) ), new Size(18,18)); // get the surounding box for the Text
var rect = new Rectangle( drawRect.Location, printSize );
rect.Offset( 0, ( drawRect.Height - printSize.Height ) / 2 ); // try to find the middle by shifting the drawing
if ( rect.Width < rect.Height ) rect.Width = rect.Height; // minimum with
DrawRoundRect( g, rect, MapProps.KbdSymbolPen.Color, 7, 3 );
rect.Inflate( -5, -5 );
DrawRoundRect( g, rect, MapProps.KbdSymbolPen.Color, 7, 3 );
rect.Inflate( -2, -2 );
g.DrawString( key, MapProps.MapFont, MapProps.KbdSymbolBrush, rect ); // write into the rectangle
}
#region IShape Implementation
/// <summary>
/// Draws the shape
/// </summary>
public override void DrawShape( Graphics g )
{
// Key Symbol left of the Text Location
if ( IsValid ) {
var symbolRect = Rectangle;
symbolRect.Offset( -120, 0 ); // TODO get a proper left offset rather than static (Should be left aligned though..)
symbolRect.Width = 120;
string key = WinApi.KbdScanCodeToVK((uint)DXGameKey); // might work....
if (IsSymbolShape) DrawKey( g, symbolRect, key );
}
// draw the text
base.DrawShape( g );
}
#endregion
}
}