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#

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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
}
}