using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using System.Drawing; using System.Drawing.Drawing2D; namespace BizHawk.Client.EmuHawk { /// /// New GDI+ methods live here /// public partial class InputRoll { // single instance to mirror GDI implementation private Pen sPen = null; // single instance to mirror GDI implementation private Brush sBrush = null; // GDI+ uses floats for measure string private SizeF _charSizeF; #region Initialization and Destruction /// /// Initializes GDI+ related stuff /// (called from constructor) /// 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); } } private void GDIPDispose() { } #endregion #region Drawing Methods Using GDI+ private void GDIP_OnPaint(PaintEventArgs e) { // white background sBrush = new SolidBrush(Color.White); sPen = new Pen(Color.White); Rectangle rect = e.ClipRectangle; e.Graphics.FillRectangle(sBrush, rect); e.Graphics.Flush(); // 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); } private void GDIP_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; 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) { 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; 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, List visibleColumns) { if (HorizontalOrientation) { int start = -_vBar.Value; sBrush = new SolidBrush(_foreColor); foreach (var column in visibleColumns) { var point = new Point(CellWidthPadding, start + CellHeightPadding); if (IsHoveringOnColumnCell && column == CurrentCell.Column) { sBrush = new SolidBrush(SystemColors.HighlightText); //e.Graphics.DrawString(column.Text, _commonFont, sBrush, (PointF)(point)); GDIP_DrawString(e, column.Text, _commonFont, point, SystemColors.HighlightText); sBrush = new SolidBrush(_foreColor); } else { //e.Graphics.DrawString(column.Text, _commonFont, sBrush, (PointF)(point)); GDIP_DrawString(e, column.Text, _commonFont, point, _foreColor); } start += CellHeight; } } else { sBrush = new SolidBrush(_foreColor); foreach (var column in visibleColumns) { int xPadding = CellWidthPadding + 1 - _hBar.Value; var point = new Point(column.Left.Value + xPadding, CellHeightPadding); if (IsHoveringOnColumnCell && column == CurrentCell.Column) { sBrush = new SolidBrush(SystemColors.HighlightText); //e.Graphics.DrawString(column.Text, _commonFont, sBrush, (PointF)(point)); GDIP_DrawString(e, column.Text, _commonFont, point, SystemColors.HighlightText); sBrush = new SolidBrush(_foreColor); } else { //e.Graphics.DrawString(column.Text, _commonFont, sBrush, (PointF)(point)); GDIP_DrawString(e, column.Text, _commonFont, point, _foreColor); } } } } private void GDIP_DrawData(PaintEventArgs e, List visibleColumns) { // Prevent exceptions with small TAStudio windows if (visibleColumns.Count == 0) { return; } bool isRotated = false; if (QueryItemText != null) { if (HorizontalOrientation) { int startRow = FirstVisibleRow; int range = Math.Min(LastVisibleRow, RowCount - 1) - startRow + 1; sBrush = new SolidBrush(_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; e.Graphics.DrawImage(image, new Point(x, y)); } 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 - (int)Math.Round((text.Length * _charSizeF.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 })) { 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) { SizeF 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 { int startRow = FirstVisibleRow; int range = Math.Min(LastVisibleRow, RowCount - 1) - startRow + 1; sBrush = new SolidBrush(_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) { e.Graphics.DrawImage(image, new Point(point.X + bitmapOffsetX, point.Y + bitmapOffsetY + CellHeightPadding)); } 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 })) { sBrush = new SolidBrush(SystemColors.HighlightText); isRotated = false; 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) { isRotated = false; sBrush = new SolidBrush(_foreColor); } } } } } } private void GDIP_DrawColumnBg(PaintEventArgs e, List visibleColumns) { 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, visibleColumns.Count * CellHeight + 1); e.Graphics.DrawLine(sPen, ColumnWidth, 0, ColumnWidth, visibleColumns.Count * CellHeight + 1); int start = -_vBar.Value; foreach (var column in visibleColumns) { e.Graphics.DrawLine(sPen, 1, start, ColumnWidth, start); start += CellHeight; } if (visibleColumns.Any()) { e.Graphics.DrawLine(sPen, 1, start, ColumnWidth, start); } } else { int 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 for (int i = 0; i < visibleColumns.Count; i++) { int pos = visibleColumns[i].Left.Value - _hBar.Value; e.Graphics.DrawLine(sPen, pos, 0, pos, bottomEdge); } // Draw right most line if (visibleColumns.Any()) { int right = TotalColWidth.Value - _hBar.Value; e.Graphics.DrawLine(sPen, right, 0, right, bottomEdge); } } // Emphasis foreach (var column in visibleColumns.Where(c => c.Emphasis)) { sBrush = new SolidBrush(SystemColors.ActiveBorder); if (HorizontalOrientation) { e.Graphics.FillRectangle(sBrush, 1, visibleColumns.IndexOf(column) * 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 (int i = 0; i < visibleColumns.Count; i++) { if (visibleColumns[i] != CurrentCell.Column) { continue; } if (CurrentCell.Column.Emphasis) { sBrush = new SolidBrush(Color.FromArgb(SystemColors.Highlight.ToArgb() + 0x00222222)); } else { sBrush = new SolidBrush(SystemColors.Highlight); } e.Graphics.FillRectangle(sBrush, 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) { sBrush = new SolidBrush(Color.FromArgb(SystemColors.Highlight.ToArgb() + 0x00550000)); } else { sBrush = new SolidBrush(SystemColors.Highlight); } e.Graphics.FillRectangle(sBrush, left + 1, 1, width - 1, ColumnHeight - 1); } } } } } private void GDIP_DrawBg(PaintEventArgs e, List visibleColumns) { if (UseCustomBackground && QueryItemBkColor != null) { DoBackGroundCallback(e, visibleColumns); } if (GridLines) { sPen = new Pen(SystemColors.ControlLight); if (HorizontalOrientation) { // Columns for (int i = 1; i < VisibleRows + 1; i++) { int x = RowsToPixels(i); e.Graphics.DrawLine(sPen, x, 1, x, DrawHeight); } // Rows for (int i = 0; i < visibleColumns.Count + 1; i++) { e.Graphics.DrawLine(sPen, 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; e.Graphics.DrawLine(sPen, x, y, x, Height - 1); } if (visibleColumns.Any()) { e.Graphics.DrawLine(sPen, totalColWidth.Value - _hBar.Value, y, totalColWidth.Value - _hBar.Value, Height - 1); } // Rows for (int i = 1; i < VisibleRows + 1; i++) { e.Graphics.DrawLine(sPen, 0, RowsToPixels(i), Width + 1, RowsToPixels(i)); } } } if (_selectedItems.Any()) { DoSelectionBG(e, visibleColumns); } } private void GDIP_DrawCellBG(PaintEventArgs e, Color color, Cell cell, List 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. var col = cell.Column.Name; if (color.A == 0) { sBrush = new SolidBrush(Color.FromArgb(255, color)); } else { sBrush = new SolidBrush(color); } e.Graphics.FillRectangle(sBrush, x, y, w, h); } 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 } }