Port InputRoll by adding GDI+ implementation

also available for Windows users via a toggle in TAStudio
This commit is contained in:
YoshiRulz 2019-10-25 19:17:50 +10:00
parent 881974b495
commit d0159ee565
No known key found for this signature in database
GPG Key ID: C4DE31C245353FB7
10 changed files with 1161 additions and 487 deletions

View File

@ -509,6 +509,8 @@ namespace BizHawk.Client.Common
// TAStudio
public TasStateManagerSettings DefaultTasProjSettings = new TasStateManagerSettings();
/// <remarks>defaults to 0 (GDI) - on linux this is forced to GDI+ later on</remarks>
public int TasStudioRenderer = 0;
// Macro Tool
public RecentFiles RecentMacros = new RecentFiles(8);

View File

@ -592,6 +592,14 @@
<DependentUpon>InputRoll.cs</DependentUpon>
<SubType>Component</SubType>
</Compile>
<Compile Include="CustomControls\InputRoll.Drawing.GDI.cs">
<DependentUpon>InputRoll.cs</DependentUpon>
<SubType>Component</SubType>
</Compile>
<Compile Include="CustomControls\InputRoll.Drawing.GDIP.cs">
<DependentUpon>InputRoll.cs</DependentUpon>
<SubType>Component</SubType>
</Compile>
<Compile Include="CustomControls\MenuButton.cs">
<SubType>Component</SubType>
</Compile>

View File

@ -0,0 +1,433 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using BizHawk.Client.EmuHawk.CustomControls;
namespace BizHawk.Client.EmuHawk
{
/// <remarks>GDI32.dll related methods are abstracted to here</remarks>
public partial class InputRoll
{
private readonly GDIRenderer _gdi;
private readonly IntPtr _rotatedFont;
private readonly IntPtr _normalFont;
private Size _charSize;
#region Initialization and Destruction
/// <summary>Initialises GDI-related stuff (called from constructor)</summary>
private void GDIConstruction()
{
using (var g = CreateGraphics()) using (_gdi.LockGraphics(g))
_charSize = _gdi.MeasureString("A", _commonFont); //TODO make this a property so changing it updates other values
}
private void GDIDispose()
{
_gdi.Dispose();
GDIRenderer.DestroyHFont(_normalFont);
GDIRenderer.DestroyHFont(_rotatedFont);
}
#endregion
#region Drawing Methods Using GDI
private void GDI_OnPaint(PaintEventArgs e)
{
using (_gdi.LockGraphics(e.Graphics))
{
_gdi.StartOffScreenBitmap(Width, Height);
// White Background
_gdi.SetBrush(Color.White);
_gdi.SetSolidPen(Color.White);
_gdi.FillRectangle(0, 0, Width, Height);
// Lag frame calculations
SetLagFramesArray();
var visibleColumns = _columns.VisibleColumns.ToList();
if (visibleColumns.Count != 0)
{
DrawColumnBg(e, visibleColumns);
DrawColumnText(e, visibleColumns);
}
// Background
DrawBg(e, visibleColumns);
// Foreground
DrawData(e, visibleColumns);
DrawColumnDrag(e);
DrawCellDrag(e);
_gdi.CopyToScreen();
_gdi.EndOffScreenBitmap();
}
}
private void GDI_DrawColumnDrag()
{
if (_columnDown != null && _columnDownMoved && _currentX.HasValue && _currentY.HasValue && IsHoveringOnColumnCell)
{
var x1 = _currentX.Value - _columnDown.Width.Value / 2;
var y1 = _currentY.Value - CellHeight / 2;
var x2 = x1 + _columnDown.Width.Value;
var y2 = y1 + CellHeight;
_gdi.SetSolidPen(_backColor);
_gdi.DrawRectangle(x1, y1, x2, y2);
_gdi.PrepDrawString(_normalFont, _foreColor);
_gdi.DrawString(_columnDown.Text, new Point(x1 + CellWidthPadding, y1 + CellHeightPadding));
}
}
private void GDI_DrawCellDrag()
{
if (_draggingCell == null) return;
var rowIndex = _draggingCell.RowIndex.Value;
var column = _draggingCell.Column;
var text = "";
var offsetX = 0;
var offsetY = 0;
QueryItemText?.Invoke(rowIndex, column, out text, ref offsetX, ref offsetY);
var bgColor = _backColor;
QueryItemBkColor?.Invoke(rowIndex, column, ref bgColor);
var draggedWidth = column.Width.Value;
var draggedHeight = CellHeight;
var x1 = _currentX.Value - draggedWidth / 2;
var y1 = _currentY.Value - draggedHeight / 2;
_gdi.SetBrush(bgColor);
_gdi.FillRectangle(x1, y1, draggedWidth, draggedHeight);
_gdi.PrepDrawString(_normalFont, _foreColor);
_gdi.DrawString(text, new Point(x1 + CellWidthPadding + offsetX, y1 + CellHeightPadding + offsetY));
}
private void GDI_DrawColumnText(IEnumerable<RollColumn> visibleColumns)
{
_gdi.PrepDrawString(_normalFont, _foreColor);
var isHoveringOnColumnCell = IsHoveringOnColumnCell;
if (HorizontalOrientation)
{
var start = -_vBar.Value;
foreach (var column in visibleColumns)
{
var point = new Point(CellWidthPadding, start + CellHeightPadding);
if (isHoveringOnColumnCell && column == CurrentCell.Column)
{
_gdi.PrepDrawString(_normalFont, SystemColors.HighlightText);
_gdi.DrawString(column.Text, point);
_gdi.PrepDrawString(_normalFont, _foreColor);
}
else
{
_gdi.DrawString(column.Text, point);
}
start += CellHeight;
}
}
else
{
var paddingX = 2 * CellWidthPadding - _hBar.Value; //TODO fix this CellPadding issue (2 * CellPadding vs just CellPadding)
foreach (var column in visibleColumns)
{
var point = new Point(column.Left.Value + paddingX, CellHeightPadding);
if (isHoveringOnColumnCell && column == CurrentCell.Column)
{
_gdi.PrepDrawString(_normalFont, SystemColors.HighlightText);
_gdi.DrawString(column.Text, point);
_gdi.PrepDrawString(_normalFont, _foreColor);
}
else
{
_gdi.DrawString(column.Text, point);
}
}
}
}
private void GDI_DrawData(IReadOnlyList<RollColumn> visibleColumns)
{
if (visibleColumns.Count == 0) return; // Prevent exceptions with small TAStudio windows
if (QueryItemText == null) return;
_gdi.PrepDrawString(_normalFont, _foreColor);
var startRow = FirstVisibleRow;
var range = Math.Min(LastVisibleRow, RowCount - 1) - startRow + 1;
int LastVisible;
if (HorizontalOrientation)
{
for (int i = 0, f = 0; f < range; i++, f++)
{
f += _lagFrames[i];
LastVisible = LastVisibleColumnIndex;
for (var j = FirstVisibleColumn; j <= LastVisible; j++)
{
Bitmap image = null;
var bitmapOffsetX = 0;
var bitmapOffsetY = 0;
QueryItemIcon?.Invoke(f + startRow, visibleColumns[j], ref image, ref bitmapOffsetX, ref bitmapOffsetY);
if (image != null)
{
_gdi.DrawBitmap(
image,
new Point(RowsToPixels(i) + CellWidthPadding + bitmapOffsetX, j * CellHeight + CellHeightPadding * 2 + bitmapOffsetY),
true
);
}
string text;
var strOffsetX = 0;
var strOffsetY = 0;
QueryItemText(f + startRow, visibleColumns[j], out text, ref strOffsetX, ref strOffsetY);
var rePrep = j == 1;
if (rePrep)
{
// 1. not sure about this; 2. repreps may be excess, but if we render one column at a time, we do need to change back after rendering the header
_gdi.PrepDrawString(
_rotatedFont,
_selectedItems.Contains(new Cell { Column = visibleColumns[j], RowIndex = i + startRow })
? SystemColors.HighlightText
: _foreColor
);
}
// Centre Text
var point = new Point(
RowsToPixels(i) + (CellWidth - text.Length * _charSize.Width) / 2 + strOffsetX,
j * CellHeight + CellHeightPadding - _vBar.Value + strOffsetY
);
if (!string.IsNullOrWhiteSpace(text)) _gdi.DrawString(text, point);
if (rePrep) _gdi.PrepDrawString(_normalFont, _foreColor);
}
}
}
else
{
var xPadding = CellWidthPadding + 1 - _hBar.Value;
for (int i = 0, f = 0; f < range; i++, f++) // Vertical
{
f += _lagFrames[i];
LastVisible = LastVisibleColumnIndex;
var y = RowsToPixels(i) + CellHeightPadding;
for (var j = FirstVisibleColumn; j <= LastVisible; j++) // Horizontal
{
var column = visibleColumns[j];
var x = column.Left.Value + xPadding;
Bitmap image = null;
var bitmapOffsetX = 0;
var bitmapOffsetY = 0;
QueryItemIcon?.Invoke(f + startRow, column, ref image, ref bitmapOffsetX, ref bitmapOffsetY);
if (image != null)
{
_gdi.DrawBitmap(
image,
new Point(x + bitmapOffsetX, y + bitmapOffsetY + CellHeightPadding),
true
);
}
string text;
var strOffsetX = 0;
var strOffsetY = 0;
QueryItemText(f + startRow, column, out text, ref strOffsetX, ref strOffsetY);
var rePrep = !_selectedItems.Contains(new Cell { Column = column, RowIndex = f + startRow });
if (rePrep) _gdi.PrepDrawString(_normalFont, SystemColors.HighlightText);
if (!string.IsNullOrWhiteSpace(text)) _gdi.DrawString(text, new Point(x + strOffsetX, y + strOffsetY));
if (rePrep) _gdi.PrepDrawString(_normalFont, _foreColor);
}
}
}
}
private void GDI_DrawColumnBg(IReadOnlyList<RollColumn> visibleColumns)
{
var columnCount = visibleColumns.Count;
_gdi.SetBrush(SystemColors.ControlLight);
_gdi.SetSolidPen(Color.Black);
if (HorizontalOrientation)
{
_gdi.FillRectangle(0, 0, ColumnWidth + 1, DrawHeight + 1);
_gdi.Line(0, 0, 0, columnCount * CellHeight + 1);
_gdi.Line(ColumnWidth, 0, ColumnWidth, columnCount * CellHeight + 1);
if (columnCount != 0) for (int i = 0, y = -_vBar.Value; i <= columnCount; i++, y += CellHeight)
{
_gdi.Line(1, y, ColumnWidth, y);
}
}
else
{
var bottomEdge = RowsToPixels(0);
// Gray column box and black line underneath
_gdi.FillRectangle(0, 0, Width + 1, bottomEdge + 1);
_gdi.Line(0, 0, TotalColWidth.Value + 1, 0);
_gdi.Line(0, bottomEdge, TotalColWidth.Value + 1, bottomEdge);
// Vertical black seperators
foreach (var column in visibleColumns)
{
var x = column.Left.Value - _hBar.Value;
_gdi.Line(x, 0, x, bottomEdge);
}
if (columnCount != 0)
{
var x = TotalColWidth.Value - _hBar.Value;
_gdi.Line(x, 0, x, bottomEdge);
}
}
// Emphasis
var columnHeight = ColumnHeight - 1;
if (HorizontalOrientation)
{
for (var i = 0; i < columnCount; i++)
{
if (!visibleColumns[i].Emphasis) continue; // only act on emphasised columns
_gdi.SetBrush(SystemColors.ActiveBorder);
_gdi.FillRectangle(1, i * CellHeight + 1, ColumnWidth - 1, columnHeight);
}
}
else
{
foreach (var column in visibleColumns)
{
if (!column.Emphasis) continue; // only act on emphasised columns
_gdi.SetBrush(SystemColors.ActiveBorder);
_gdi.FillRectangle(column.Left.Value + 1 - _hBar.Value, 1, column.Width.Value - 1, columnHeight);
}
}
// If the user is hovering over a column
if (IsHoveringOnColumnCell)
{
if (HorizontalOrientation)
{
for (var i = 0; i < columnCount; i++)
{
var column = visibleColumns[i];
if (column != CurrentCell.Column) continue; // only act on selected
_gdi.SetBrush(column.Emphasis ? Add(SystemColors.Highlight, 0x00222222) : SystemColors.Highlight);
_gdi.FillRectangle(1, i * CellHeight + 1, ColumnWidth - 1, columnHeight);
}
}
else
{
//TODO multiple selected columns
foreach (var column in visibleColumns)
{
if (column != CurrentCell.Column) continue; // only act on selected
if (column.Left.Value - _hBar.Value > Width || column.Right.Value - _hBar.Value < 0) continue; // Left of column is to the right of the viewable area, or right of column is to the left of the viewable area
_gdi.SetBrush(column.Emphasis ? Add(SystemColors.Highlight, 0x00550000) : SystemColors.Highlight);
var left = column.Left.Value + 1;
_gdi.FillRectangle(left - _hBar.Value, 1, column.Right.Value - left, columnHeight);
}
}
}
}
private void GDI_DrawBg(PaintEventArgs e, List<RollColumn> visibleColumns)
{
if (UseCustomBackground && QueryItemBkColor != null) DoBackGroundCallback(e, visibleColumns);
if (GridLines)
{
_gdi.SetSolidPen(SystemColors.ControlLight);
if (HorizontalOrientation)
{
// Columns
var iLimit = VisibleRows + 1;
for (var i = 1; i < iLimit; i++)
{
var x = RowsToPixels(i);
_gdi.Line(x, 1, x, DrawHeight);
}
// Rows
var x1 = RowsToPixels(0) + 1;
var jLimit = visibleColumns.Count + 1;
for (var j = 0; j < jLimit; j++) _gdi.Line(x1, j * CellHeight - _vBar.Value, DrawWidth, j * CellHeight - _vBar.Value);
}
else
{
// Columns
var y1 = ColumnHeight + 1;
var y2 = Height - 1;
foreach (var column in visibleColumns)
{
var x = column.Left.Value - _hBar.Value;
_gdi.Line(x, y1, x, y2);
}
if (visibleColumns.Count != 0)
{
var x = TotalColWidth.Value - _hBar.Value;
_gdi.Line(x, y1, x, y2);
}
// Rows
var x2 = Width + 1;
var iLimit = VisibleRows + 1;
for (var i = 1; i < iLimit; i++)
{
var y = RowsToPixels(i);
_gdi.Line(0, y, x2, y);
}
}
}
if (_selectedItems.Count != 0) DoSelectionBG(e, visibleColumns);
}
private void GDI_DrawCellBG(Color color, Cell cell, IList<RollColumn> visibleColumns)
{
// We can't draw without row and column, so assume they exist and fail catastrophically if they don't
int x, y, w;
if (HorizontalOrientation)
{
x = RowsToPixels(cell.RowIndex.Value) + 1;
if (x < ColumnWidth) return;
y = CellHeight * visibleColumns.IndexOf(cell.Column) + 1 - _vBar.Value;
w = CellWidth - 1;
}
else
{
y = RowsToPixels(cell.RowIndex.Value) + 1;
if (y < ColumnHeight) return;
x = cell.Column.Left.Value - _hBar.Value + 1;
w = cell.Column.Width.Value - 1;
}
if (x > DrawWidth || y > DrawHeight) return; // Don't draw if off-screen.
_gdi.SetBrush(color);
_gdi.FillRectangle(x, y, w, CellHeight - 1);
}
#endregion
}
}

View File

@ -0,0 +1,477 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
namespace BizHawk.Client.EmuHawk
{
/// <remarks>New GDI+ methods live here</remarks>
public partial class InputRoll
{
/// <remarks>instance field to mirror GDI implementation</remarks>
private Pen sPen;
/// <remarks>instance field to mirror GDI implementation</remarks>
private Brush sBrush;
/// <remarks>GDI+ uses floats to measure strings</remarks>
private SizeF _charSizeF;
#region Initialization and Destruction
/// <summary>Initialises GDI+-related stuff (called from constructor)</summary>
private void GDIPConstruction()
{
// HFont?
// Rotated HFont?
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
SetStyle(ControlStyles.Opaque, true);
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
using (var g = CreateGraphics())
{
_charSizeF = g.MeasureString("A", _commonFont);
// _charSize = Size.Round(sizeF);
}
}
#endregion
#region Drawing Methods Using GDI+
private void GDIP_OnPaint(PaintEventArgs e)
{
// white background
sBrush = new SolidBrush(Color.White);
sPen = new Pen(Color.White);
e.Graphics.FillRectangle(sBrush, e.ClipRectangle);
e.Graphics.Flush();
// Lag frame calculations
SetLagFramesArray();
var visibleColumns = _columns.VisibleColumns.ToList();
if (visibleColumns.Count != 0)
{
DrawColumnBg(e, visibleColumns);
DrawColumnText(e, visibleColumns);
}
// Background
DrawBg(e, visibleColumns);
// Foreground
DrawData(e, visibleColumns);
DrawColumnDrag(e);
DrawCellDrag(e);
}
private void GDIP_DrawColumnDrag(PaintEventArgs e)
{
if (_columnDown != null && _columnDownMoved && _currentX.HasValue && _currentY.HasValue && IsHoveringOnColumnCell)
{
var x1 = _currentX.Value - _columnDown.Width.Value / 2;
var y1 = _currentY.Value - CellHeight / 2;
var x2 = x1 + _columnDown.Width.Value;
var y2 = y1 + CellHeight;
sPen = new Pen(_backColor);
e.Graphics.DrawRectangle(sPen, x1, y1, x2, y2);
sBrush = new SolidBrush(_foreColor);
// e.Graphics.DrawString(_columnDown.Text, _commonFont, sBrush, (PointF)(new Point(x1 + CellWidthPadding, y1 + CellHeightPadding)));
GDIP_DrawString(e, _columnDown.Text, _commonFont, new Point(x1 + CellWidthPadding, y1 + CellHeightPadding), _foreColor);
}
}
private void GDIP_DrawCellDrag(PaintEventArgs e)
{
if (_draggingCell == null) return;
var text = "";
var offsetX = 0;
var offsetY = 0;
QueryItemText?.Invoke(_draggingCell.RowIndex.Value, _draggingCell.Column, out text, ref offsetX, ref offsetY);
var bgColor = _backColor;
QueryItemBkColor?.Invoke(_draggingCell.RowIndex.Value, _draggingCell.Column, ref bgColor);
var x1 = _currentX.Value - _draggingCell.Column.Width.Value / 2;
var y1 = _currentY.Value - CellHeight / 2;
var x2 = x1 + _draggingCell.Column.Width.Value;
var y2 = y1 + CellHeight;
sBrush = new SolidBrush(bgColor);
e.Graphics.FillRectangle(sBrush, x1, y1, x2 - x1, y2 - y1);
sBrush = new SolidBrush(_foreColor);
// e.Graphics.DrawString(text, _commonFont, sBrush, (PointF)(new Point(x1 + CellWidthPadding + offsetX, y1 + CellHeightPadding + offsetY)));
GDIP_DrawString(e, text, _commonFont, new Point(x1 + CellWidthPadding + offsetX, y1 + CellHeightPadding + offsetY), _foreColor);
}
private void GDIP_DrawColumnText(PaintEventArgs e, IReadOnlyCollection<RollColumn> visibleColumns)
{
sBrush = new SolidBrush(_foreColor);
if (HorizontalOrientation)
{
var start = -_vBar.Value;
foreach (var column in visibleColumns)
{
var point = new Point(CellWidthPadding, start + CellHeightPadding);
if (IsHoveringOnColumnCell && column == CurrentCell.Column)
{
var temp = sBrush;
sBrush = new SolidBrush(SystemColors.HighlightText);
// e.Graphics.DrawString(column.Text, _commonFont, sBrush, (PointF)(point));
GDIP_DrawString(e, column.Text, _commonFont, point, SystemColors.HighlightText);
sBrush = temp;
}
else
{
// e.Graphics.DrawString(column.Text, _commonFont, sBrush, (PointF)(point));
GDIP_DrawString(e, column.Text, _commonFont, point, _foreColor);
}
start += CellHeight;
}
}
else
{
var xPadding = CellWidthPadding + 1 - _hBar.Value;
foreach (var column in visibleColumns)
{
var point = new Point(column.Left.Value + xPadding, CellHeightPadding);
if (IsHoveringOnColumnCell && column == CurrentCell.Column)
{
var temp = sBrush;
sBrush = new SolidBrush(SystemColors.HighlightText);
// e.Graphics.DrawString(column.Text, _commonFont, sBrush, (PointF)(point));
GDIP_DrawString(e, column.Text, _commonFont, point, SystemColors.HighlightText);
sBrush = temp;
}
else
{
// e.Graphics.DrawString(column.Text, _commonFont, sBrush, (PointF)(point));
GDIP_DrawString(e, column.Text, _commonFont, point, _foreColor);
}
}
}
}
private void GDIP_DrawData(PaintEventArgs e, IReadOnlyList<RollColumn> visibleColumns)
{
if (visibleColumns.Count == 0) return; // Prevent exceptions with small TAStudio windows
if (QueryItemText == null) return;
var startRow = FirstVisibleRow;
int LastVisible;
if (HorizontalOrientation)
{
var isRotated = false;
var fLimit = Math.Min(LastVisibleRow, RowCount - 1) - startRow + 1;
sBrush = new SolidBrush(_foreColor);
for (int i = 0, f = 0; f < fLimit; i++, f++)
{
f += _lagFrames[i];
LastVisible = LastVisibleColumnIndex;
for (var j = FirstVisibleColumn; j <= LastVisible; j++)
{
Bitmap image = null;
var bitmapOffsetX = 0;
var bitmapOffsetY = 0;
QueryItemIcon?.Invoke(f + startRow, visibleColumns[j], ref image, ref bitmapOffsetX, ref bitmapOffsetY);
if (image != null)
{
var x1 = RowsToPixels(i) + CellWidthPadding + bitmapOffsetX;
var y1 = j * CellHeight + CellHeightPadding * 2 + bitmapOffsetY;
e.Graphics.DrawImage(image, new Point(x1, y1));
}
string text;
var strOffsetX = 0;
var strOffsetY = 0;
QueryItemText(f + startRow, visibleColumns[j], out text, ref strOffsetX, ref strOffsetY);
// Centre Text
var point = new Point(
RowsToPixels(i) + (CellWidth - (int)Math.Round(text.Length * _charSizeF.Width)) / 2 + strOffsetX,
j * CellHeight + CellHeightPadding - _vBar.Value + strOffsetY
);
var rePrep = false;
if (j == 1)
{
if (_selectedItems.Contains(new Cell { Column = visibleColumns[j], RowIndex = i + startRow }))
{
isRotated = true;
sBrush = new SolidBrush(SystemColors.HighlightText);
rePrep = true;
}
else if (j == 1)
{
// 1. not sure about this; 2. repreps may be excess, but if we render one column at a time, we do need to change back after rendering the header
rePrep = true;
isRotated = true;
sBrush = new SolidBrush(SystemColors.HighlightText);
}
}
if (!string.IsNullOrWhiteSpace(text))
{
// _gdi.DrawString(text, point);
if (isRotated)
{
var sz = e.Graphics.VisibleClipBounds.Size;
e.Graphics.TranslateTransform(sz.Width / 2, sz.Height / 2);
e.Graphics.RotateTransform(90);
sz = e.Graphics.MeasureString(text, _commonFont);
e.Graphics.DrawString(text, _commonFont, sBrush, -(sz.Width / 2), -(sz.Height / 2));
}
else
{
// e.Graphics.DrawString(text, _commonFont, sBrush, (PointF)point);
GDIP_DrawString(e, text, _commonFont, point, new Pen(sBrush).Color);
}
}
if (rePrep)
{
isRotated = false;
sBrush = new SolidBrush(_foreColor);
}
}
}
}
else
{
var range = Math.Min(LastVisibleRow, RowCount - 1) - startRow + 1;
var xPadding = CellWidthPadding + 1 - _hBar.Value;
sBrush = new SolidBrush(_foreColor);
for (int i = 0, f = 0; f < range; i++, f++) // Vertical
{
f += _lagFrames[i];
LastVisible = LastVisibleColumnIndex;
for (var j = FirstVisibleColumn; j <= LastVisible; j++) // Horizontal
{
var col = visibleColumns[j];
Bitmap image = null;
var bitmapOffsetX = 0;
var bitmapOffsetY = 0;
QueryItemIcon?.Invoke(f + startRow, visibleColumns[j], ref image, ref bitmapOffsetX, ref bitmapOffsetY);
var point = new Point(col.Left.Value + xPadding, RowsToPixels(i) + CellHeightPadding);
if (image != null) e.Graphics.DrawImage(image, new Point(point.X + bitmapOffsetX, point.Y + bitmapOffsetY + CellHeightPadding));
string text;
var strOffsetX = 0;
var strOffsetY = 0;
QueryItemText(f + startRow, visibleColumns[j], out text, ref strOffsetX, ref strOffsetY);
var rePrep = false;
if (_selectedItems.Contains(new Cell { Column = visibleColumns[j], RowIndex = f + startRow }))
{
sBrush = new SolidBrush(SystemColors.HighlightText);
rePrep = true;
}
if (!string.IsNullOrWhiteSpace(text))
{
// e.Graphics.DrawString(text, _commonFont, sBrush, (PointF)(new Point(point.X + strOffsetX, point.Y + strOffsetY)));
GDIP_DrawString(e, text, _commonFont, new Point(point.X + strOffsetX, point.Y + strOffsetY), new Pen(sBrush).Color);
}
if (rePrep) sBrush = new SolidBrush(_foreColor);
}
}
}
}
private void GDIP_DrawColumnBg(PaintEventArgs e, IReadOnlyList<RollColumn> visibleColumns)
{
var columnCount = visibleColumns.Count;
sBrush = new SolidBrush(SystemColors.ControlLight);
sPen = new Pen(Color.Black);
if (HorizontalOrientation)
{
e.Graphics.FillRectangle(sBrush, 0, 0, ColumnWidth + 1, DrawHeight + 1);
e.Graphics.DrawLine(sPen, 0, 0, 0, columnCount * CellHeight + 1);
e.Graphics.DrawLine(sPen, ColumnWidth, 0, ColumnWidth, columnCount * CellHeight + 1);
var iLimit = columnCount + 1;
if (iLimit > 1)
{
for (int i = 0, y = -_vBar.Value; i < iLimit; i++, y += CellHeight) e.Graphics.DrawLine(sPen, 1, y, ColumnWidth, y);
}
}
else
{
var bottomEdge = RowsToPixels(0);
// Gray column box and black line underneath
e.Graphics.FillRectangle(sBrush, 0, 0, Width + 1, bottomEdge + 1);
e.Graphics.DrawLine(sPen, 0, 0, TotalColWidth.Value + 1, 0);
e.Graphics.DrawLine(sPen, 0, bottomEdge, TotalColWidth.Value + 1, bottomEdge);
// Vertical black seperators
foreach (var column in visibleColumns)
{
var pos = column.Left.Value - _hBar.Value;
e.Graphics.DrawLine(sPen, pos, 0, pos, bottomEdge);
}
if (columnCount != 0)
{
var right = TotalColWidth.Value - _hBar.Value;
e.Graphics.DrawLine(sPen, right, 0, right, bottomEdge);
}
}
// Emphasis
for (var i = 0; i < columnCount; i++)
{
var column = visibleColumns[i];
if (!column.Emphasis) continue; // only act on emphasised columns
sBrush = new SolidBrush(SystemColors.ActiveBorder);
if (HorizontalOrientation) e.Graphics.FillRectangle(sBrush, 1, i * CellHeight + 1, ColumnWidth - 1, ColumnHeight - 1);
else e.Graphics.FillRectangle(sBrush, column.Left.Value + 1 - _hBar.Value, 1, column.Width.Value - 1, ColumnHeight - 1);
}
// If the user is hovering over a column
if (IsHoveringOnColumnCell)
{
if (HorizontalOrientation)
{
for (var i = 0; i < columnCount; i++)
{
var column = visibleColumns[i];
if (column != CurrentCell.Column) continue; // only act on selected
sBrush = new SolidBrush(column.Emphasis
? Color.FromArgb(SystemColors.Highlight.ToArgb() + 0x00222222) //TODO should be bitwise or?
: SystemColors.Highlight);
e.Graphics.FillRectangle(sBrush, 1, i * CellHeight + 1, ColumnWidth - 1, ColumnHeight - 1);
}
}
else
{
//TODO multiple selected columns
foreach (var column in visibleColumns)
{
if (column != CurrentCell.Column) continue; // only act on selected
if (column.Left.Value - _hBar.Value > Width || column.Right.Value - _hBar.Value < 0) continue; // Left of column is to the right of the viewable area, or right of column is to the left of the viewable area
var left = column.Left.Value - _hBar.Value;
var width = column.Right.Value - _hBar.Value - left;
sBrush = new SolidBrush(column.Emphasis
? Color.FromArgb(SystemColors.Highlight.ToArgb() + 0x00550000) //TODO should be bitwise or?
: SystemColors.Highlight);
e.Graphics.FillRectangle(sBrush, left + 1, 1, width - 1, ColumnHeight - 1);
}
}
}
}
private void GDIP_DrawBg(PaintEventArgs e, List<RollColumn> visibleColumns)
{
if (UseCustomBackground && QueryItemBkColor != null) DoBackGroundCallback(e, visibleColumns);
if (GridLines)
{
sPen = new Pen(SystemColors.ControlLight);
if (HorizontalOrientation)
{
// Columns
var iLimit = VisibleRows + 1;
for (var i = 1; i < iLimit; i++)
{
var x = RowsToPixels(i);
e.Graphics.DrawLine(sPen, x, 1, x, DrawHeight);
}
// Rows
var x1 = RowsToPixels(0) + 1;
var jLimit = visibleColumns.Count + 1;
for (var j = 0; j < jLimit; j++)
{
var y = j * CellHeight - _vBar.Value;
e.Graphics.DrawLine(sPen, x1, y, DrawWidth, y);
}
}
else
{
// Columns
var y1 = ColumnHeight + 1;
var y2 = Height - 1;
foreach (var column in visibleColumns)
{
var x = column.Left.Value - _hBar.Value;
e.Graphics.DrawLine(sPen, x, y1, x, y2);
}
if (visibleColumns.Count != 0)
{
var x = TotalColWidth.Value - _hBar.Value;
e.Graphics.DrawLine(sPen, x, y1, x, y2);
}
// Rows
var x2 = Width + 1;
var iLimit = VisibleRows + 1;
for (var i = 1; i < iLimit; i++)
{
var y = RowsToPixels(i);
e.Graphics.DrawLine(sPen, 0, y, x2, y);
}
}
}
if (_selectedItems.Count != 0) DoSelectionBG(e, visibleColumns);
}
private void GDIP_DrawCellBG(PaintEventArgs e, Color color, Cell cell, IList<RollColumn> visibleColumns)
{
// We can't draw without row and column, so assume they exist and fail catastrophically if they don't
int x, y, w;
if (HorizontalOrientation)
{
x = RowsToPixels(cell.RowIndex.Value) + 1;
if (x < ColumnWidth) return;
y = CellHeight * visibleColumns.IndexOf(cell.Column) + 1 - _vBar.Value;
w = CellWidth - 1;
}
else
{
y = RowsToPixels(cell.RowIndex.Value) + 1;
if (y < ColumnHeight) return;
x = cell.Column.Left.Value - _hBar.Value + 1;
w = cell.Column.Width.Value - 1;
}
if (x > DrawWidth || y > DrawHeight) return; // Don't draw if off-screen.
sBrush = new SolidBrush(color.A == 0 ? Color.FromArgb(255, color) : color);
e.Graphics.FillRectangle(sBrush, x, y, w, CellHeight - 1);
}
private void GDIP_DrawString(PaintEventArgs e, string text, Font font, Point point, Color color)
{
// TextRenderer.DrawText(e.Graphics, text, font, point, color);
e.Graphics.DrawString(text, font, new SolidBrush(color), (PointF)point);
}
#endregion
}
}

View File

@ -6,380 +6,59 @@ using System.Windows.Forms;
namespace BizHawk.Client.EmuHawk
{
/// <summary>
/// This in the most part now contains renderer selection logic
/// </summary>
public partial class InputRoll
{
#region Renderer-Based Logic Methods
protected override void OnPaint(PaintEventArgs e)
{
using (_gdi.LockGraphics(e.Graphics))
{
_gdi.StartOffScreenBitmap(Width, Height);
// White Background
_gdi.SetBrush(Color.White);
_gdi.SetSolidPen(Color.White);
_gdi.FillRectangle(0, 0, Width, Height);
// Lag frame calculations
SetLagFramesArray();
var visibleColumns = _columns.VisibleColumns.ToList();
if (visibleColumns.Any())
{
DrawColumnBg(e, visibleColumns);
DrawColumnText(e, visibleColumns);
}
// Background
DrawBg(e, visibleColumns);
// Foreground
DrawData(e, visibleColumns);
DrawColumnDrag(e);
DrawCellDrag(e);
_gdi.CopyToScreen();
_gdi.EndOffScreenBitmap();
}
}
protected override void OnPaintBackground(PaintEventArgs pevent)
{
// Do nothing, and this should never be called
if (Renderer == RollRenderer.GDIPlus)
GDIP_OnPaint(e);
else if (Renderer == RollRenderer.GDI)
GDI_OnPaint(e);
}
private void DrawColumnDrag(PaintEventArgs e)
{
if (_columnDown != null && _columnDownMoved && _currentX.HasValue && _currentY.HasValue && IsHoveringOnColumnCell)
{
int x1 = _currentX.Value - (_columnDown.Width.Value / 2);
int y1 = _currentY.Value - (CellHeight / 2);
int x2 = x1 + _columnDown.Width.Value;
int y2 = y1 + CellHeight;
_gdi.SetSolidPen(_backColor);
_gdi.DrawRectangle(x1, y1, x2, y2);
_gdi.PrepDrawString(_normalFont, _foreColor);
_gdi.DrawString(_columnDown.Text, new Point(x1 + CellWidthPadding, y1 + CellHeightPadding));
}
if (Renderer == RollRenderer.GDIPlus)
GDIP_DrawColumnDrag(e);
else if (Renderer == RollRenderer.GDI)
GDI_DrawColumnDrag();
}
private void DrawCellDrag(PaintEventArgs e)
{
if (_draggingCell != null)
{
var text = "";
int offsetX = 0;
int offsetY = 0;
QueryItemText?.Invoke(_draggingCell.RowIndex.Value, _draggingCell.Column, out text, ref offsetX, ref offsetY);
Color bgColor = _backColor;
QueryItemBkColor?.Invoke(_draggingCell.RowIndex.Value, _draggingCell.Column, ref bgColor);
int x1 = _currentX.Value - (_draggingCell.Column.Width.Value / 2);
int y1 = _currentY.Value - (CellHeight / 2);
int x2 = x1 + _draggingCell.Column.Width.Value;
int y2 = y1 + CellHeight;
_gdi.SetBrush(bgColor);
_gdi.FillRectangle(x1, y1, x2 - x1, y2 - y1);
_gdi.PrepDrawString(_normalFont, _foreColor);
_gdi.DrawString(text, new Point(x1 + CellWidthPadding + offsetX, y1 + CellHeightPadding + offsetY));
}
if (Renderer == RollRenderer.GDIPlus)
GDIP_DrawCellDrag(e);
else if (Renderer == RollRenderer.GDI)
GDIP_DrawCellDrag(e);
}
private void DrawColumnText(PaintEventArgs e, List<RollColumn> visibleColumns)
{
if (HorizontalOrientation)
{
int start = -_vBar.Value;
_gdi.PrepDrawString(_normalFont, _foreColor);
foreach (var column in visibleColumns)
{
var point = new Point(CellWidthPadding, start + CellHeightPadding);
if (IsHoveringOnColumnCell && column == CurrentCell.Column)
{
_gdi.PrepDrawString(_normalFont, SystemColors.HighlightText);
_gdi.DrawString(column.Text, point);
_gdi.PrepDrawString(_normalFont, _foreColor);
}
else
{
_gdi.DrawString(column.Text, point);
}
start += CellHeight;
}
}
else
{
_gdi.PrepDrawString(_normalFont, _foreColor);
foreach (var column in visibleColumns)
{
var point = new Point(column.Left.Value + 2 * CellWidthPadding - _hBar.Value, CellHeightPadding); // TODO: fix this CellPadding issue (2 * CellPadding vs just CellPadding)
if (IsHoveringOnColumnCell && column == CurrentCell.Column)
{
_gdi.PrepDrawString(_normalFont, SystemColors.HighlightText);
_gdi.DrawString(column.Text, point);
_gdi.PrepDrawString(_normalFont, _foreColor);
}
else
{
_gdi.DrawString(column.Text, point);
}
}
}
if (Renderer == RollRenderer.GDIPlus)
GDIP_DrawColumnText(e, visibleColumns);
else if (Renderer == RollRenderer.GDI)
GDI_DrawColumnText(visibleColumns);
}
private void DrawData(PaintEventArgs e, List<RollColumn> visibleColumns)
{
// Prevent exceptions with small TAStudio windows
if (visibleColumns.Count == 0)
{
return;
}
if (QueryItemText != null)
{
if (HorizontalOrientation)
{
int startRow = FirstVisibleRow;
int range = Math.Min(LastVisibleRow, RowCount - 1) - startRow + 1;
_gdi.PrepDrawString(_normalFont, _foreColor);
for (int i = 0, f = 0; f < range; i++, f++)
{
f += _lagFrames[i];
int LastVisible = LastVisibleColumnIndex;
for (int j = FirstVisibleColumn; j <= LastVisible; j++)
{
Bitmap image = null;
int x = 0;
int y = 0;
int bitmapOffsetX = 0;
int bitmapOffsetY = 0;
QueryItemIcon?.Invoke(f + startRow, visibleColumns[j], ref image, ref bitmapOffsetX, ref bitmapOffsetY);
if (image != null)
{
x = RowsToPixels(i) + CellWidthPadding + bitmapOffsetX;
y = (j * CellHeight) + (CellHeightPadding * 2) + bitmapOffsetY;
_gdi.DrawBitmap(image, new Point(x, y), true);
}
string text;
int strOffsetX = 0;
int strOffsetY = 0;
QueryItemText(f + startRow, visibleColumns[j], out text, ref strOffsetX, ref strOffsetY);
// Center Text
x = RowsToPixels(i) + ((CellWidth - (text.Length * _charSize.Width)) / 2);
y = (j * CellHeight) + CellHeightPadding - _vBar.Value;
var point = new Point(x + strOffsetX, y + strOffsetY);
var rePrep = false;
if (j == 1)
if (_selectedItems.Contains(new Cell { Column = visibleColumns[j], RowIndex = i + startRow }))
{
_gdi.PrepDrawString(_rotatedFont, SystemColors.HighlightText);
rePrep = true;
}
else if (j == 1)
{
// 1. not sure about this; 2. repreps may be excess, but if we render one column at a time, we do need to change back after rendering the header
rePrep = true;
_gdi.PrepDrawString(_rotatedFont, _foreColor);
}
if (!string.IsNullOrWhiteSpace(text))
{
_gdi.DrawString(text, point);
}
if (rePrep)
{
_gdi.PrepDrawString(_normalFont, _foreColor);
}
}
}
}
else
{
int startRow = FirstVisibleRow;
int range = Math.Min(LastVisibleRow, RowCount - 1) - startRow + 1;
_gdi.PrepDrawString(_normalFont, _foreColor);
int xPadding = CellWidthPadding + 1 - _hBar.Value;
for (int i = 0, f = 0; f < range; i++, f++) // Vertical
{
f += _lagFrames[i];
int LastVisible = LastVisibleColumnIndex;
for (int j = FirstVisibleColumn; j <= LastVisible; j++) // Horizontal
{
RollColumn col = visibleColumns[j];
string text;
int strOffsetX = 0;
int strOffsetY = 0;
Point point = new Point(col.Left.Value + xPadding, RowsToPixels(i) + CellHeightPadding);
Bitmap image = null;
int bitmapOffsetX = 0;
int bitmapOffsetY = 0;
QueryItemIcon?.Invoke(f + startRow, visibleColumns[j], ref image, ref bitmapOffsetX, ref bitmapOffsetY);
if (image != null)
{
_gdi.DrawBitmap(image, new Point(point.X + bitmapOffsetX, point.Y + bitmapOffsetY + CellHeightPadding), true);
}
QueryItemText(f + startRow, visibleColumns[j], out text, ref strOffsetX, ref strOffsetY);
bool rePrep = false;
if (_selectedItems.Contains(new Cell { Column = visibleColumns[j], RowIndex = f + startRow }))
{
_gdi.PrepDrawString(_normalFont, SystemColors.HighlightText);
rePrep = true;
}
if (!string.IsNullOrWhiteSpace(text))
{
_gdi.DrawString(text, new Point(point.X + strOffsetX, point.Y + strOffsetY));
}
if (rePrep)
{
_gdi.PrepDrawString(_normalFont, _foreColor);
}
}
}
}
}
if (Renderer == RollRenderer.GDIPlus)
GDIP_DrawData(e, visibleColumns);
else if (Renderer == RollRenderer.GDI)
GDI_DrawData(visibleColumns);
}
private void DrawColumnBg(PaintEventArgs e, List<RollColumn> visibleColumns)
{
_gdi.SetBrush(SystemColors.ControlLight);
_gdi.SetSolidPen(Color.Black);
if (HorizontalOrientation)
{
_gdi.FillRectangle(0, 0, ColumnWidth + 1, DrawHeight + 1);
_gdi.Line(0, 0, 0, visibleColumns.Count * CellHeight + 1);
_gdi.Line(ColumnWidth, 0, ColumnWidth, visibleColumns.Count * CellHeight + 1);
int start = -_vBar.Value;
foreach (var column in visibleColumns)
{
_gdi.Line(1, start, ColumnWidth, start);
start += CellHeight;
}
if (visibleColumns.Any())
{
_gdi.Line(1, start, ColumnWidth, start);
}
}
else
{
int bottomEdge = RowsToPixels(0);
// Gray column box and black line underneath
_gdi.FillRectangle(0, 0, Width + 1, bottomEdge + 1);
_gdi.Line(0, 0, TotalColWidth.Value + 1, 0);
_gdi.Line(0, bottomEdge, TotalColWidth.Value + 1, bottomEdge);
// Vertical black seperators
for (int i = 0; i < visibleColumns.Count; i++)
{
int pos = visibleColumns[i].Left.Value - _hBar.Value;
_gdi.Line(pos, 0, pos, bottomEdge);
}
// Draw right most line
if (visibleColumns.Any())
{
int right = TotalColWidth.Value - _hBar.Value;
_gdi.Line(right, 0, right, bottomEdge);
}
}
// Emphasis
foreach (var column in visibleColumns.Where(c => c.Emphasis))
{
_gdi.SetBrush(SystemColors.ActiveBorder);
if (HorizontalOrientation)
{
_gdi.FillRectangle(1, visibleColumns.IndexOf(column) * CellHeight + 1, ColumnWidth - 1, ColumnHeight - 1);
}
else
{
_gdi.FillRectangle(column.Left.Value + 1 - _hBar.Value, 1, column.Width.Value - 1, ColumnHeight - 1);
}
}
// If the user is hovering over a column
if (IsHoveringOnColumnCell)
{
if (HorizontalOrientation)
{
for (int i = 0; i < visibleColumns.Count; i++)
{
if (visibleColumns[i] != CurrentCell.Column)
{
continue;
}
if (CurrentCell.Column.Emphasis)
{
_gdi.SetBrush(Add(SystemColors.Highlight, 0x00222222));
}
else
{
_gdi.SetBrush(SystemColors.Highlight);
}
_gdi.FillRectangle(1, i * CellHeight + 1, ColumnWidth - 1, ColumnHeight - 1);
}
}
else
{
// TODO multiple selected columns
for (int i = 0; i < visibleColumns.Count; i++)
{
if (visibleColumns[i] == CurrentCell.Column)
{
// Left of column is to the right of the viewable area or right of column is to the left of the viewable area
if (visibleColumns[i].Left.Value - _hBar.Value > Width || visibleColumns[i].Right.Value - _hBar.Value < 0)
{
continue;
}
int left = visibleColumns[i].Left.Value - _hBar.Value;
int width = visibleColumns[i].Right.Value - _hBar.Value - left;
if (CurrentCell.Column.Emphasis)
{
_gdi.SetBrush(Add(SystemColors.Highlight, 0x00550000));
}
else
{
_gdi.SetBrush(SystemColors.Highlight);
}
_gdi.FillRectangle(left + 1, 1, width - 1, ColumnHeight - 1);
}
}
}
}
if (Renderer == RollRenderer.GDIPlus)
GDIP_DrawColumnBg(e, visibleColumns);
else if (Renderer == RollRenderer.GDI)
GDI_DrawColumnBg(visibleColumns);
}
// TODO refactor this and DoBackGroundCallback functions.
@ -388,57 +67,30 @@ namespace BizHawk.Client.EmuHawk
/// </summary>
private void DrawBg(PaintEventArgs e, List<RollColumn> visibleColumns)
{
if (UseCustomBackground && QueryItemBkColor != null)
{
DoBackGroundCallback(e, visibleColumns);
}
if (Renderer == RollRenderer.GDIPlus)
GDIP_DrawBg(e, visibleColumns);
else if (Renderer == RollRenderer.GDI)
GDI_DrawBg(e, visibleColumns);
}
if (GridLines)
{
_gdi.SetSolidPen(SystemColors.ControlLight);
if (HorizontalOrientation)
{
// Columns
for (int i = 1; i < VisibleRows + 1; i++)
{
int x = RowsToPixels(i);
_gdi.Line(x, 1, x, DrawHeight);
}
/// <summary>
/// Given a cell with rowindex inbetween 0 and VisibleRows, it draws the background color specified. Do not call with absolute rowindices.
/// </summary>
private void DrawCellBG(PaintEventArgs e, Color color, Cell cell, List<RollColumn> visibleColumns)
{
if (Renderer == RollRenderer.GDIPlus)
GDIP_DrawCellBG(e, color, cell, visibleColumns);
else if (Renderer == RollRenderer.GDI)
GDI_DrawCellBG(color, cell, visibleColumns);
}
// Rows
for (int i = 0; i < visibleColumns.Count + 1; i++)
{
_gdi.Line(RowsToPixels(0) + 1, i * CellHeight - _vBar.Value, DrawWidth, i * CellHeight - _vBar.Value);
}
}
else
{
// Columns
int y = ColumnHeight + 1;
int? totalColWidth = TotalColWidth;
foreach (var column in visibleColumns)
{
int x = column.Left.Value - _hBar.Value;
_gdi.Line(x, y, x, Height - 1);
}
#endregion
if (visibleColumns.Any())
{
_gdi.Line(totalColWidth.Value - _hBar.Value, y, totalColWidth.Value - _hBar.Value, Height - 1);
}
#region Non-Renderer-Specific Methods
// Rows
for (int i = 1; i < VisibleRows + 1; i++)
{
_gdi.Line(0, RowsToPixels(i), Width + 1, RowsToPixels(i));
}
}
}
if (_selectedItems.Any())
{
DoSelectionBG(e, visibleColumns);
}
protected override void OnPaintBackground(PaintEventArgs pevent)
{
// Do nothing, and this should never be called
}
private void DoSelectionBG(PaintEventArgs e, List<RollColumn> visibleColumns)
@ -484,49 +136,10 @@ namespace BizHawk.Client.EmuHawk
cellColor = Color.FromArgb(cellColor.R - (int)((cellColor.R - SystemColors.Highlight.R) * alpha),
cellColor.G - (int)((cellColor.G - SystemColors.Highlight.G) * alpha),
cellColor.B - (int)((cellColor.B - SystemColors.Highlight.B) * alpha));
DrawCellBG(cellColor, relativeCell, visibleColumns);
DrawCellBG(e, cellColor, relativeCell, visibleColumns);
}
}
/// <summary>
/// Given a cell with rowindex inbetween 0 and VisibleRows, it draws the background color specified. Do not call with absolute rowindices.
/// </summary>
private void DrawCellBG(Color color, Cell cell, List<RollColumn> visibleColumns)
{
int x, y, w, h;
if (HorizontalOrientation)
{
x = RowsToPixels(cell.RowIndex.Value) + 1;
w = CellWidth - 1;
y = (CellHeight * visibleColumns.IndexOf(cell.Column)) + 1 - _vBar.Value; // We can't draw without row and column, so assume they exist and fail catastrophically if they don't
h = CellHeight - 1;
if (x < ColumnWidth)
{
return;
}
}
else
{
w = cell.Column.Width.Value - 1;
x = cell.Column.Left.Value - _hBar.Value + 1;
y = RowsToPixels(cell.RowIndex.Value) + 1; // We can't draw without row and column, so assume they exist and fail catastrophically if they don't
h = CellHeight - 1;
if (y < ColumnHeight)
{
return;
}
}
if (x > DrawWidth || y > DrawHeight)
{
return;
} // Don't draw if off screen.
_gdi.SetBrush(color);
_gdi.FillRectangle(x, y, w, h);
}
/// <summary>
/// Calls QueryItemBkColor callback for all visible cells and fills in the background of those cells.
/// </summary>
@ -546,7 +159,7 @@ namespace BizHawk.Client.EmuHawk
for (int i = 0, f = 0; f < range; i++, f++)
{
f += _lagFrames[i];
Color rowColor = Color.White;
QueryRowBkColor?.Invoke(f + startIndex, ref rowColor);
@ -573,7 +186,7 @@ namespace BizHawk.Client.EmuHawk
Column = visibleColumns[j],
RowIndex = i
};
DrawCellBG(itemColor, cell, visibleColumns);
DrawCellBG(e, itemColor, cell, visibleColumns);
}
}
}
@ -583,7 +196,7 @@ namespace BizHawk.Client.EmuHawk
for (int i = 0, f = 0; f < range; i++, f++) // Vertical
{
f += _lagFrames[i];
Color rowColor = Color.White;
QueryRowBkColor?.Invoke(f + startIndex, ref rowColor);
@ -610,11 +223,13 @@ namespace BizHawk.Client.EmuHawk
Column = visibleColumns[j],
RowIndex = i
};
DrawCellBG(itemColor, cell, visibleColumns);
DrawCellBG(e, itemColor, cell, visibleColumns);
}
}
}
}
}
#endregion
}
}

View File

@ -15,7 +15,10 @@ namespace BizHawk.Client.EmuHawk
// Row width is specified for horizontal orientation
public partial class InputRoll : Control
{
private readonly GDIRenderer _gdi;
private RollRenderer Renderer = RollRenderer.GDIPlus;
private Font _commonFont;
private readonly SortedSet<Cell> _selectedItems = new SortedSet<Cell>(new SortCell());
private readonly VScrollBar _vBar;
@ -24,8 +27,6 @@ namespace BizHawk.Client.EmuHawk
private readonly Timer _hoverTimer = new Timer();
private readonly byte[] _lagFrames = new byte[256]; // Large enough value that it shouldn't ever need resizing. // apparently not large enough for 4K
private readonly IntPtr _rotatedFont;
private readonly IntPtr _normalFont;
private readonly Color _foreColor;
private readonly Color _backColor;
@ -35,7 +36,6 @@ namespace BizHawk.Client.EmuHawk
private int _maxCharactersInHorizontal = 1;
private int _rowCount;
private Size _charSize;
private RollColumn _columnDown;
@ -52,6 +52,9 @@ namespace BizHawk.Client.EmuHawk
public InputRoll()
{
// set renderer once at InputRoll instantiation
Renderer = (RollRenderer)TAStudio.InputRollRenderer;
UseCustomBackground = true;
GridLines = true;
CellWidthPadding = 3;
@ -59,24 +62,29 @@ namespace BizHawk.Client.EmuHawk
CurrentCell = null;
ScrollMethod = "near";
var commonFont = new Font("Arial", 8, FontStyle.Bold);
_normalFont = GDIRenderer.CreateNormalHFont(commonFont, 6);
// PrepDrawString doesn't actually set the font, so this is rather useless.
// I'm leaving this stuff as-is so it will be a bit easier to fix up with another rendering method.
_rotatedFont = GDIRenderer.CreateRotatedHFont(commonFont, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
SetStyle(ControlStyles.Opaque, true);
_gdi = new GDIRenderer();
using (var g = CreateGraphics())
using (_gdi.LockGraphics(g))
switch (Renderer)
{
_charSize = _gdi.MeasureString("A", commonFont); // TODO make this a property so changing it updates other values.
case RollRenderer.GDI:
_commonFont = new Font("Arial", 8, FontStyle.Bold);
_normalFont = GDIRenderer.CreateNormalHFont(_commonFont, 6);
// PrepDrawString doesn't actually set the font, so this is rather useless.
// I'm leaving this stuff as-is so it will be a bit easier to fix up with another rendering method.
_rotatedFont = GDIRenderer.CreateRotatedHFont(_commonFont, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
SetStyle(ControlStyles.Opaque, true);
_gdi = new GDIRenderer();
GDIConstruction();
break;
case RollRenderer.GDIPlus:
// _commonFont = new Font("Courier New", 8, FontStyle.Bold);
_commonFont = new Font("Arial", 8, FontStyle.Bold);
GDIPConstruction();
break;
}
UpdateCellSize();
@ -126,11 +134,7 @@ namespace BizHawk.Client.EmuHawk
protected override void Dispose(bool disposing)
{
_gdi.Dispose();
GDIRenderer.DestroyHFont(_normalFont);
GDIRenderer.DestroyHFont(_rotatedFont);
if (Renderer == RollRenderer.GDI) GDIDispose();
base.Dispose(disposing);
}
@ -193,7 +197,7 @@ namespace BizHawk.Client.EmuHawk
{
return _hBar.SmallChange / CellWidth;
}
if (CellHeight == 0) CellHeight++;
return _vBar.SmallChange / CellHeight;
}
@ -643,7 +647,7 @@ namespace BizHawk.Client.EmuHawk
{
return _hBar.Value / CellWidth;
}
if (CellHeight == 0) CellHeight++;
return _vBar.Value / CellHeight;
}
@ -771,6 +775,7 @@ namespace BizHawk.Client.EmuHawk
{
get
{
if (CellHeight == 0) CellHeight++;
if (HorizontalOrientation)
{
return (DrawWidth - ColumnWidth) / CellWidth;
@ -789,6 +794,7 @@ namespace BizHawk.Client.EmuHawk
{
get
{
if (CellHeight == 0) CellHeight++;
if (HorizontalOrientation)
{
return _vBar.Value / CellHeight;
@ -805,6 +811,7 @@ namespace BizHawk.Client.EmuHawk
{
get
{
if (CellHeight == 0) CellHeight++;
List<RollColumn> columnList = VisibleColumns.ToList();
int ret;
if (HorizontalOrientation)
@ -1009,7 +1016,7 @@ namespace BizHawk.Client.EmuHawk
Refresh();
}
}
else if (_columnDown != null) // Kind of silly feeling to have this check twice, but the only alternative I can think of has it refreshing twice when pointed column changes with column down, and speed matters
else if (_columnDown != null) // Kind of silly feeling to have this check twice, but the only alternative I can think of has it refreshing twice when pointed column changes with column down, and speed matters
{
Refresh();
}
@ -1579,10 +1586,13 @@ namespace BizHawk.Client.EmuHawk
// See MSDN Page for more information on the dumb ScrollBar.Maximum Property
private void RecalculateScrollBars()
{
if (_vBar == null || _hBar == null) return;
UpdateDrawSize();
var columns = _columns.VisibleColumns.ToList();
if (CellHeight == 0) CellHeight++;
if (HorizontalOrientation)
{
NeedsVScrollbar = columns.Count > DrawHeight / CellHeight;
@ -1763,6 +1773,8 @@ namespace BizHawk.Client.EmuHawk
{
newCell.RowIndex = PixelsToRows(x);
if (CellHeight == 0) CellHeight++;
int colIndex = (y + _vBar.Value) / CellHeight;
if (colIndex >= 0 && colIndex < columns.Count)
{
@ -1798,7 +1810,15 @@ namespace BizHawk.Client.EmuHawk
/// <returns>The new width of the RollColumn object.</returns>
private int UpdateWidth(RollColumn col)
{
col.Width = (col.Text.Length * _charSize.Width) + (CellWidthPadding * 4);
switch (Renderer)
{
case RollRenderer.GDI:
col.Width = (col.Text.Length * _charSize.Width) + (CellWidthPadding * 4);
break;
case RollRenderer.GDIPlus:
col.Width = (int)Math.Round((col.Text.Length * _charSizeF.Width) + (CellWidthPadding * 4));
break;
}
return col.Width.Value;
}
@ -1863,6 +1883,7 @@ namespace BizHawk.Client.EmuHawk
{
return (int)Math.Floor((float)(pixels - ColumnWidth) / CellWidth);
}
if (CellHeight == 0) CellHeight++;
return (int)Math.Floor((float)(pixels - ColumnHeight) / CellHeight);
}
@ -1889,8 +1910,17 @@ namespace BizHawk.Client.EmuHawk
/// </summary>
private void UpdateCellSize()
{
CellHeight = _charSize.Height + (CellHeightPadding * 2);
CellWidth = (_charSize.Width * MaxCharactersInHorizontal) + (CellWidthPadding * 4); // Double the padding for horizontal because it looks better
switch (Renderer)
{
case RollRenderer.GDI:
CellHeight = _charSize.Height + (CellHeightPadding * 2);
CellWidth = (_charSize.Width * MaxCharactersInHorizontal) + (CellWidthPadding * 4); // Double the padding for horizontal because it looks better
break;
case RollRenderer.GDIPlus:
CellHeight = (int)Math.Round(_charSizeF.Height + (CellHeightPadding * 2) + 1); // needed for GDI+ to match GDI cell height
CellWidth = (int)Math.Round((_charSizeF.Width * MaxCharactersInHorizontal) + (CellWidthPadding * 4)); // Double the padding for horizontal because it looks better
break;
}
}
// SuuperW: Count lag frames between FirstDisplayed and given display position
@ -2260,6 +2290,12 @@ namespace BizHawk.Client.EmuHawk
}
}
public enum RollRenderer
{
GDI = 0,
GDIPlus = 1
}
#endregion
}
}

View File

@ -125,6 +125,9 @@ namespace BizHawk.Client.EmuHawk
this.DefaultStateSettingsMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.SettingsSubMenu = new System.Windows.Forms.ToolStripMenuItem();
this.RotateMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.RendererOptionsMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.SetRenderer0 = new System.Windows.Forms.ToolStripMenuItem();
this.SetRenderer1 = new System.Windows.Forms.ToolStripMenuItem();
this.HideLagFramesSubMenu = new System.Windows.Forms.ToolStripMenuItem();
this.HideLagFrames0 = new System.Windows.Forms.ToolStripMenuItem();
this.HideLagFrames1 = new System.Windows.Forms.ToolStripMenuItem();
@ -964,6 +967,7 @@ namespace BizHawk.Client.EmuHawk
//
this.SettingsSubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.RotateMenuItem,
this.RendererOptionsMenuItem,
this.HideLagFramesSubMenu,
this.iconsToolStripMenuItem,
this.toolStripSeparator23,
@ -982,6 +986,34 @@ namespace BizHawk.Client.EmuHawk
this.RotateMenuItem.Text = "Rotate";
this.RotateMenuItem.Click += new System.EventHandler(this.RotateMenuItem_Click);
//
// RendererOptionsMenuItem
//
this.RendererOptionsMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.SetRenderer0,
this.SetRenderer1});
this.RendererOptionsMenuItem.Name = "RendererOptionsMenuItem";
this.RendererOptionsMenuItem.Size = new System.Drawing.Size(183, 22);
this.RendererOptionsMenuItem.Text = "Renderer";
this.RendererOptionsMenuItem.DropDownOpened += new System.EventHandler(this.SelectedRendererSubMenu_DropDownOpened);
//
// SetRenderer0
//
this.SetRenderer0.CheckOnClick = true;
this.SetRenderer0.Name = "SetRenderer0";
this.SetRenderer0.Size = new System.Drawing.Size(180, 22);
this.SetRenderer0.Tag = 0;
this.SetRenderer0.Text = "GDI";
this.SetRenderer0.Click += new System.EventHandler(this.SetRenderer_Click);
//
// SetRenderer1
//
this.SetRenderer1.CheckOnClick = true;
this.SetRenderer1.Name = "SetRenderer1";
this.SetRenderer1.Size = new System.Drawing.Size(180, 22);
this.SetRenderer1.Tag = 1;
this.SetRenderer1.Text = "GDI+ (Experimental)";
this.SetRenderer1.Click += new System.EventHandler(this.SetRenderer_Click);
//
// HideLagFramesSubMenu
//
this.HideLagFramesSubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
@ -1816,5 +1848,8 @@ namespace BizHawk.Client.EmuHawk
private System.Windows.Forms.ToolStripMenuItem AutoRestoreOnMouseUpOnlyMenuItem;
private System.Windows.Forms.ToolStripMenuItem SingleClickFloatEditMenuItem;
private System.Windows.Forms.ToolStripMenuItem LoadBranchOnDoubleclickMenuItem;
private System.Windows.Forms.ToolStripMenuItem RendererOptionsMenuItem;
private System.Windows.Forms.ToolStripMenuItem SetRenderer0;
private System.Windows.Forms.ToolStripMenuItem SetRenderer1;
}
}
}

View File

@ -9,6 +9,7 @@ using BizHawk.Client.Common;
using BizHawk.Client.Common.MovieConversionExtensions;
using BizHawk.Client.EmuHawk.ToolExtensions;
using BizHawk.Client.EmuHawk.WinFormExtensions;
using BizHawk.Common;
namespace BizHawk.Client.EmuHawk
{
@ -1115,6 +1116,51 @@ namespace BizHawk.Client.EmuHawk
RotateMenuItem.ShortcutKeyDisplayString = TasView.RotateHotkeyStr;
}
private void SelectedRendererSubMenu_DropDownOpened(object sender, EventArgs e)
{
if (OSTailoredCode.CurrentOS == OSTailoredCode.DistinctOS.Windows)
{
if (Global.Config.TasStudioRenderer != InputRollRenderer)
{
switch (Global.Config.TasStudioRenderer)
{
case 0:
SetRenderer0.Text = "GDI (Pending TAStudio Restart)";
SetRenderer0.Enabled = false;
SetRenderer0.Checked = true;
SetRenderer1.Text = "GDI+ (Experimental)";
SetRenderer1.Checked = false;
break;
case 1:
SetRenderer1.Text = "GDI+ (Pending TAStudio Restart)";
SetRenderer1.Enabled = false;
SetRenderer1.Checked = true;
SetRenderer0.Text = "GDI";
SetRenderer0.Checked = false;
break;
}
}
else
{
SetRenderer0.Text = "GDI";
SetRenderer0.Enabled = true;
SetRenderer1.Text = "GDI+ (Experimental)";
SetRenderer1.Enabled = true;
SetRenderer0.Checked = Global.Config.TasStudioRenderer == 0;
SetRenderer1.Checked = Global.Config.TasStudioRenderer == 1;
}
}
else
{
SetRenderer0.Checked = false;
SetRenderer0.Enabled = false;
SetRenderer0.Visible = false;
SetRenderer1.Checked = true;
SetRenderer1.Enabled = false;
SetRenderer1.Visible = true;
}
}
private void HideLagFramesSubMenu_DropDownOpened(object sender, EventArgs e)
{
HideLagFrames0.Checked = TasView.LagFramesToHide == 0;
@ -1163,6 +1209,16 @@ namespace BizHawk.Client.EmuHawk
CurrentTasMovie.FlagChanges();
}
private void SetRenderer_Click(object sender, EventArgs e)
{
var incoming = (int)(sender as ToolStripMenuItem).Tag;
if (incoming != InputRollRenderer)
{
MessageBox.Show("Changing the input roll renderer requires a\nmanual restart of TAStudio", "Renderer Change Warning", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
Global.Config.TasStudioRenderer = incoming;
}
private void HideLagFramesX_Click(object sender, EventArgs e)
{
TasView.LagFramesToHide = (int)((ToolStripMenuItem)sender).Tag;

View File

@ -15,6 +15,7 @@ using BizHawk.Client.Common.MovieConversionExtensions;
using BizHawk.Client.EmuHawk.WinFormExtensions;
using BizHawk.Client.EmuHawk.ToolExtensions;
using BizHawk.Common;
using BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES;
namespace BizHawk.Client.EmuHawk
@ -25,6 +26,13 @@ namespace BizHawk.Client.EmuHawk
public TasMovie CurrentTasMovie => Global.MovieSession.Movie as TasMovie;
private MainForm Mainform => GlobalWin.MainForm;
/// <summary>
/// Static settings so that InputRoll.cs can determine its renderer ahead of instantiation
/// 0: GDI
/// 1: GDI+
/// </summary>
public static int InputRollRenderer = OSTailoredCode.CurrentOS == OSTailoredCode.DistinctOS.Windows ? 0 : 1;
public bool IsInMenuLoop { get; private set; }
public string StatesPath => PathManager.MakeAbsolutePath(Global.Config.PathEntries["Global", "TAStudio states"].Path, null);
@ -113,6 +121,10 @@ namespace BizHawk.Client.EmuHawk
public TAStudio()
{
Settings = new TAStudioSettings();
// input roll renderer must be set before InputRoll initialisation
InputRollRenderer = OSTailoredCode.CurrentOS == OSTailoredCode.DistinctOS.Windows ? Global.Config.TasStudioRenderer : 1;
InitializeComponent();
InitializeSeekWorker();

View File

@ -123,19 +123,19 @@
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="RecentSubMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAlwSFlzAAALAQAA
CwEBNnBPWwAAAk1JREFUOE+VkktvUlEUhVsfsdHoREcOHJr4C/RPONHoXzBxoOlAJybOlAZtkdSa2piY
NsjjFiiX8qY8ChYL+ECB2lKB8rq0k5bEmiiRbtc+6b2B6MTBR/ZZa+919j3tEBH9RbudHD6E63/2qAwc
treT58BVRVnWl8vBbLEg7wNC/QPaMrwb4GT/jFa024mzQLe56c9GwjM7klXXlcw6ksyPSbLoKByc/lUq
+TbQMwrODARAGAF3SxtexSMbf8vOCVp9ZyK+/euaW9TO+SfksOlprSjvoteAjU5rAYqSuFyvR1PR8Ewv
GJii8rcAoYFSb+d4gDAgNI/8jGTHOFUroT3410QAHuk4Am4Vi/KOzz2JGxfFcLMZI3wK5T7ZqaXEhcYb
WU2PKJM2H7Ra8XE14AQO91dTpk4k9JLq9YgYHghoxcWZPa/bSCH/C2o0orPaBo1GbDQee9VJxF+zoYFP
wtpGWgpN0/uMRWgcyiG1WsSkBhxFwG0E7AV8z2lrKyxuYvgBs2kLr4z1XcLj4SA2gD+nBhxB8p1sxtKZ
t4xR/otTDNdqS1oQw7ezx2/AfxVok1oAmh+WSt7v/MKLLgOtr3tEQD+sseeyPyX0dqHdVAOGq9XQPazX
/JyzH9itY+SQ9LSSnKV8fkHANWvsoYc/JYaZERHAPzicBw9AoZBf+BnwTZEN/4G2N4egZg1eDz05cIHn
tACmUgmeAtdhRsvlwH6x6Dr4+EESoO5B68JLo+eSOjMQwKDpGLgCJtDoBysgBXzQDOBifz8zcPh/aOgP
7nYTiVA2JaoAAAAASUVORK5CYII=
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6
JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAACXBIWXMAAAsBAAALAQE2cE9bAAACTUlE
QVQ4T5WSS29SURSFWx+x0ehERw4cmvgL9E840ehfMHGg6UAnJs6UBm2R1JramJg2yOMWKJfypjwKFgv4
QIHaUoHyurSTlsSaKJFu1z7pvYHoxMFH9llr73X2Pe0QEf1Fu50cPoTrf/aoDBy2t5PnwFVFWdaXy8Fs
sSDvA0L9A9oyvBvgZP+MVrTbibNAt7npz0bCMzuSVdeVzDqSzI9JsugoHJz+VSr5NtAzCs4MBEAYAXdL
G17FIxt/y84JWn1nIr7965pb1M75J+Sw6WmtKO+i14CNTmsBipK4XK9HU9HwTC8YmKLytwChgVJv53iA
MCA0j/yMZMc4VSuhPfjXRAAe6TgCbhWL8o7PPYkbF8VwsxkjfArlPtmppcSFxhtZTY8okzYftFrxcTXg
BA73V1OmTiT0kur1iBgeCGjFxZk9r9tIIf8LajSis9oGjUZsNB571UnEX7OhgU/C2kZaCk3T+4xFaBzK
IbVaxKQGHEXAbQTsBXzPaWsrLG5i+AGzaQuvjPVdwuPhIDaAP6cGHEHynWzG0pm3jFH+i1MM12pLWhDD
t7PHb8B/FWiTWgCaH5ZK3u/8wosuA62ve0RAP6yx57I/JfR2od1UA4ar1dA9rNf8nLMf2K1j5JD0tJKc
pXx+QcA1a+yhhz8lhpkREcA/OJwHD0ChkF/4GfBNkQ3/gbY3h6BmDV4PPTlwgee0AKZSCZ4C12FGy+XA
frHoOvj4QRKg7kHrwkuj55I6MxDAoOkYuAIm0OgHKyAFfNAM4GJ/PzNw+H9o6A/udhOJUDYlqgAAAABJ
RU5ErkJggg==
</value>
</data>
<metadata name="TasStatusStrip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">