BizHawk/BizHawk.Client.EmuHawk/tools/VirtualPads/controls/components/AnalogSticklPanel.cs

400 lines
9.2 KiB
C#

using System;
using System.Drawing;
using System.Windows.Forms;
using BizHawk.Client.Common;
using BizHawk.Emulation.Common;
//Just because this code was mostly rewritten, dont think it isnt still awful
namespace BizHawk.Client.EmuHawk
{
public sealed class AnalogStickPanel : Panel
{
private int _x = 0;
private int _y = 0;
public int X
{
get
{
return _x;
}
set
{
_x = value;
SetAnalog();
}
}
public int Y
{
get
{
return _y;
}
set
{
_y = value;
SetAnalog();
}
}
public bool HasValue = false;
public bool ReadOnly { get; set; }
public string XName = string.Empty;
public string YName = string.Empty;
private IController _previous = null;
float UserRangePercentageX = 100, UserRangePercentageY = 100;
public void SetUserRange(float rx, float ry)
{
UserRangePercentageX = rx;
UserRangePercentageY = ry;
Rerange();
Refresh();
}
public void SetRangeX(float[] range)
{
for (int i = 0; i < 3; i++) ActualRangeX[i] = range[i];
Rerange();
}
public void SetRangeY(float[] range)
{
for (int i = 0; i < 3; i++) ActualRangeY[i] = range[i];
Rerange();
}
public float[] RangeX = new float[] { -128f, 0.0f, 127f };
public float[] RangeY = new float[] { -128f, 0.0f, 127f };
public float[] ActualRangeX = new float[] { -128f, 0.0f, 127f };
public float[] ActualRangeY = new float[] { -128f, 0.0f, 127f };
float flipx = 1, flipy = 1;
void Rerange()
{
//baseline:
//Array.Copy(ActualRangeX, RangeX, 3);
//Array.Copy(ActualRangeY, RangeY, 3);
float rx = ActualRangeX[2] - ActualRangeX[0];
float ry = ActualRangeY[2] - ActualRangeY[0];
float midx = rx / 2 + ActualRangeX[0];
float midy = ry / 2 + ActualRangeY[0];
rx *= UserRangePercentageX / 100;
ry *= UserRangePercentageY / 100;
float minx = midx - rx / 2;
float maxx = minx + rx;
float miny = midy - ry / 2;
float maxy = miny + ry;
if (minx > maxx)
{
float temp = minx;
minx = maxx;
maxx = temp;
flipx = -1;
}
if (miny > maxy)
{
float temp = miny;
miny = maxy;
maxy = temp;
flipy = -1;
}
//Range?[1] isn't really used
RangeX[0] = minx;
RangeX[2] = maxx;
RangeY[0] = miny;
RangeY[2] = maxy;
Clamp();
}
//dont count on this working. it's never been tested.
//but it kind of must be, or else nothing here would work...
public float ScaleX = 0.5f;
public float ScaleY = 0.5f;
int MinX { get { return (int)(RangeX[0]); } }
int MinY { get { return (int)(RangeY[0]); } }
int MaxX { get { return (int)(RangeX[2]); } }
int MaxY { get { return (int)(RangeY[2]); } }
int RangeSizeX { get { return (int)(MaxX - MinX + 1); } }
int RangeSizeY { get { return (int)(MaxY - MinY + 1); } }
int PixelSizeX { get { return (int)(RangeSizeX * ScaleX); } }
int PixelSizeY { get { return (int)(RangeSizeY * ScaleY); } }
int PixelMinX { get { return (Size.Width - PixelSizeX) / 2; } }
int PixelMinY { get { return (Size.Height - PixelSizeY) / 2; } }
int PixelMidX { get { return PixelMinX + PixelSizeX / 2; } }
int PixelMidY { get { return PixelMinY + PixelSizeY / 2; } }
int PixelMaxX { get { return PixelMinX + PixelSizeX - 1; } }
int PixelMaxY { get { return PixelMinY + PixelSizeY - 1; } }
private int RealToGfxX(int val)
{
int v = val;
if (flipx == -1)
v = (MaxX - val) + MinX;
v = (int)(((float)v - MinX) * ScaleX);
v += PixelMinX;
return v;
}
private int RealToGfxY(int val)
{
int v = val;
if (flipy == -1)
v = (MaxY - val) + MinY;
v = (int)(((float)v - MinY) * ScaleY);
v += PixelMinY;
return v;
}
private int GfxToRealX(int val)
{
val -= PixelMinX;
float v = ((float)val / ScaleX + MinX);
if (v < MinX) v = MinX;
if (v > MaxX) v = MaxX;
if (flipx == -1)
v = (MaxX - v) + MinX;
return (int)v;
}
private int GfxToRealY(int val)
{
val -= PixelMinY;
float v;
v = ((float)val / ScaleY + MinY);
if (v < MinX) v = MinX;
if (v > MaxX) v = MaxX;
if(flipy == -1)
v = (MaxY - v) + MinY;
return (int)v;
}
private readonly Brush WhiteBrush = Brushes.White;
private readonly Brush GrayBrush = Brushes.LightGray;
private readonly Brush RedBrush = Brushes.Red;
private readonly Brush OffWhiteBrush = Brushes.Beige;
private readonly Pen BlackPen = new Pen(Brushes.Black);
private readonly Pen BluePen = new Pen(Brushes.Blue, 2);
private readonly Pen GrayPen = new Pen(Brushes.Gray, 2);
private readonly Bitmap Dot = new Bitmap(7, 7);
private readonly Bitmap GrayDot = new Bitmap(7, 7);
public Action ClearCallback { get; set; }
private void DoClearCallback()
{
if (ClearCallback != null)
{
ClearCallback();
}
}
public AnalogStickPanel()
{
Size = new Size(PixelSizeX + 1, PixelSizeY + 1);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
SetStyle(ControlStyles.Opaque, true);
BackColor = Color.Gray;
Paint += AnalogControlPanel_Paint;
BorderStyle = BorderStyle.Fixed3D;
// Draw the dot into a bitmap
using (var g = Graphics.FromImage(Dot))
{
g.Clear(Color.Transparent);
g.FillRectangle(RedBrush, 2, 0, 3, 7);
g.FillRectangle(RedBrush, 1, 1, 5, 5);
g.FillRectangle(RedBrush, 0, 2, 7, 3);
}
using (var gg = Graphics.FromImage(GrayDot))
{
gg.Clear(Color.Transparent);
gg.FillRectangle(Brushes.Gray, 2, 0, 3, 7);
gg.FillRectangle(Brushes.Gray, 1, 1, 5, 5);
gg.FillRectangle(Brushes.Gray, 0, 2, 7, 3);
}
}
private void SetAnalog()
{
var xn = HasValue ? X : (int?)null;
var yn = HasValue ? Y : (int?)null;
Global.StickyXORAdapter.SetFloat(XName, xn);
Global.StickyXORAdapter.SetFloat(YName, yn);
Refresh();
}
private void AnalogControlPanel_Paint(object sender, PaintEventArgs e)
{
unchecked
{
// Background
e.Graphics.Clear(Color.Black);
e.Graphics.FillRectangle(GrayBrush, PixelMinX, PixelMinY, PixelMaxX - PixelMinX, PixelMaxY- PixelMinY);
e.Graphics.FillEllipse(ReadOnly ? OffWhiteBrush : WhiteBrush, PixelMinX, PixelMinY, PixelMaxX - PixelMinX - 2, PixelMaxY - PixelMinY - 3);
e.Graphics.DrawEllipse(BlackPen, PixelMinX, PixelMinY, PixelMaxX - PixelMinX - 2, PixelMaxY - PixelMinY - 3);
e.Graphics.DrawLine(BlackPen, PixelMidX, 0, PixelMidX, PixelMaxY);
e.Graphics.DrawLine(BlackPen, 0, PixelMidY, PixelMaxX, PixelMidY);
// Previous frame
if (_previous != null)
{
var pX = (int)_previous.GetFloat(XName);
var pY = (int)_previous.GetFloat(YName);
e.Graphics.DrawLine(GrayPen, PixelMidX, PixelMidY, RealToGfxX(pX), RealToGfxY(pY));
e.Graphics.DrawImage(GrayDot, RealToGfxX(pX) - 3, RealToGfxY(MaxY) - RealToGfxY(pY) - 3);
}
// Line
if (HasValue)
{
e.Graphics.DrawLine(BluePen, PixelMidX, PixelMidY, RealToGfxX(X), RealToGfxY(Y));
e.Graphics.DrawImage(ReadOnly ? GrayDot : Dot, RealToGfxX(X) - 3, RealToGfxY(Y) - 3);
}
}
}
protected override void OnMouseMove(MouseEventArgs e)
{
if (!ReadOnly)
{
if (e.Button == MouseButtons.Left)
{
X = GfxToRealX(e.X);
Y = GfxToRealY(e.Y);
Clamp();
HasValue = true;
SetAnalog();
}
else if (e.Button == MouseButtons.Right)
{
Clear();
}
Refresh();
base.OnMouseMove(e);
}
}
protected override void OnMouseUp(MouseEventArgs e)
{
base.OnMouseUp(e);
Capture = false;
}
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x007B) // WM_CONTEXTMENU
{
// Don't let parent controls get this. We handle the right mouse button ourselves
return;
}
base.WndProc(ref m);
}
protected override void OnMouseDown(MouseEventArgs e)
{
if (!ReadOnly)
{
if (e.Button == MouseButtons.Left)
{
X = GfxToRealX(e.X);
Y = GfxToRealY(e.Y);
Clamp();
HasValue = true;
}
if (e.Button == MouseButtons.Right)
{
Clear();
}
Refresh();
}
}
public void Clear()
{
if (X != 0 || Y != 0 || HasValue)
{
X = Y = 0;
HasValue = false;
DoClearCallback();
Refresh();
}
}
public void Set(IController controller)
{
var newX = (int)controller.GetFloat(XName);
var newY = (int)controller.GetFloat(YName);
var changed = newX != X || newY != Y;
if (changed)
{
SetPosition(newX, newY);
}
}
public void SetPrevious(IController previous)
{
_previous = previous;
}
public void SetPosition(int xval, int yval)
{
X = xval;
Y = yval;
Clamp();
HasValue = true;
Refresh();
}
private void Clamp()
{
if (X > MaxX)
{
X = MaxX;
}
else if (X < MinX)
{
X = MinX;
}
if (Y > MaxY)
{
Y = MaxY;
}
else if (Y < MinY)
{
Y = MinY;
}
}
}
}