Faster box hover preview

Render it manually rather than let controls be goofy with draw calls.
This commit is contained in:
Kurt 2026-03-09 20:28:43 -05:00
parent 5b42ff746d
commit 662c3db7dc
3 changed files with 329 additions and 322 deletions

View File

@ -28,216 +28,13 @@ protected override void Dispose(bool disposing)
/// </summary>
private void InitializeComponent()
{
PAN_All = new System.Windows.Forms.Panel();
FLP_List = new System.Windows.Forms.FlowLayoutPanel();
L_LinesBeforeMoves = new System.Windows.Forms.Label();
FLP_Moves = new System.Windows.Forms.FlowLayoutPanel();
Move1 = new MoveDisplay();
Move2 = new MoveDisplay();
Move3 = new MoveDisplay();
Move4 = new MoveDisplay();
L_HintIllegal = new System.Windows.Forms.Label();
L_LinesAfterMoves = new System.Windows.Forms.Label();
PAN_Top = new System.Windows.Forms.Panel();
FLP_Top = new System.Windows.Forms.FlowLayoutPanel();
PB_Ball = new System.Windows.Forms.PictureBox();
L_Name = new System.Windows.Forms.Label();
PB_Gender = new System.Windows.Forms.PictureBox();
PAN_All.SuspendLayout();
FLP_List.SuspendLayout();
FLP_Moves.SuspendLayout();
PAN_Top.SuspendLayout();
FLP_Top.SuspendLayout();
((System.ComponentModel.ISupportInitialize)PB_Ball).BeginInit();
((System.ComponentModel.ISupportInitialize)PB_Gender).BeginInit();
SuspendLayout();
//
// PAN_All
//
PAN_All.AutoSize = true;
PAN_All.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
PAN_All.BackColor = System.Drawing.SystemColors.Window;
PAN_All.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
PAN_All.Controls.Add(FLP_List);
PAN_All.Controls.Add(PAN_Top);
PAN_All.Dock = System.Windows.Forms.DockStyle.Fill;
PAN_All.Location = new System.Drawing.Point(0, 0);
PAN_All.Margin = new System.Windows.Forms.Padding(0);
PAN_All.Name = "PAN_All";
PAN_All.Size = new System.Drawing.Size(148, 214);
PAN_All.TabIndex = 19;
//
// FLP_List
//
FLP_List.AutoSize = true;
FLP_List.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
FLP_List.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
FLP_List.Controls.Add(L_LinesBeforeMoves);
FLP_List.Controls.Add(FLP_Moves);
FLP_List.Controls.Add(L_HintIllegal);
FLP_List.Controls.Add(L_LinesAfterMoves);
FLP_List.Dock = System.Windows.Forms.DockStyle.Fill;
FLP_List.FlowDirection = System.Windows.Forms.FlowDirection.TopDown;
FLP_List.Location = new System.Drawing.Point(0, 34);
FLP_List.Margin = new System.Windows.Forms.Padding(0);
FLP_List.Name = "FLP_List";
FLP_List.Size = new System.Drawing.Size(146, 178);
FLP_List.TabIndex = 1;
FLP_List.WrapContents = false;
//
// L_LinesBeforeMoves
//
L_LinesBeforeMoves.AutoSize = true;
L_LinesBeforeMoves.Location = new System.Drawing.Point(2, 4);
L_LinesBeforeMoves.Margin = new System.Windows.Forms.Padding(2, 4, 0, 0);
L_LinesBeforeMoves.Name = "L_LinesBeforeMoves";
L_LinesBeforeMoves.Size = new System.Drawing.Size(36, 17);
L_LinesBeforeMoves.TabIndex = 5;
L_LinesBeforeMoves.Text = "Stats";
//
// FLP_Moves
//
FLP_Moves.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
FLP_Moves.AutoSize = true;
FLP_Moves.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
FLP_Moves.Controls.Add(Move1);
FLP_Moves.Controls.Add(Move2);
FLP_Moves.Controls.Add(Move3);
FLP_Moves.Controls.Add(Move4);
FLP_List.SetFlowBreak(FLP_Moves, true);
FLP_Moves.FlowDirection = System.Windows.Forms.FlowDirection.TopDown;
FLP_Moves.Location = new System.Drawing.Point(0, 25);
FLP_Moves.Margin = new System.Windows.Forms.Padding(0, 4, 0, 8);
FLP_Moves.Name = "FLP_Moves";
FLP_Moves.Size = new System.Drawing.Size(142, 96);
FLP_Moves.TabIndex = 7;
FLP_Moves.WrapContents = false;
//
// Move1
//
Move1.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
Move1.AutoSize = true;
Move1.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
Move1.Location = new System.Drawing.Point(4, 0);
Move1.Margin = new System.Windows.Forms.Padding(4, 0, 0, 0);
Move1.Name = "Move1";
Move1.Size = new System.Drawing.Size(138, 24);
Move1.TabIndex = 1;
//
// Move2
//
Move2.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
Move2.AutoSize = true;
Move2.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
Move2.Location = new System.Drawing.Point(4, 24);
Move2.Margin = new System.Windows.Forms.Padding(4, 0, 0, 0);
Move2.Name = "Move2";
Move2.Size = new System.Drawing.Size(138, 24);
Move2.TabIndex = 2;
//
// Move3
//
Move3.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
Move3.AutoSize = true;
Move3.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
Move3.Location = new System.Drawing.Point(4, 48);
Move3.Margin = new System.Windows.Forms.Padding(4, 0, 0, 0);
Move3.Name = "Move3";
Move3.Size = new System.Drawing.Size(138, 24);
Move3.TabIndex = 3;
//
// Move4
//
Move4.AutoSize = true;
Move4.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
Move4.Location = new System.Drawing.Point(4, 72);
Move4.Margin = new System.Windows.Forms.Padding(4, 0, 0, 0);
Move4.Name = "Move4";
Move4.Size = new System.Drawing.Size(138, 24);
Move4.TabIndex = 4;
//
// L_HintIllegal
//
L_HintIllegal.AutoSize = true;
L_HintIllegal.Location = new System.Drawing.Point(2, 133);
L_HintIllegal.Margin = new System.Windows.Forms.Padding(2, 4, 0, 4);
L_HintIllegal.Name = "L_HintIllegal";
L_HintIllegal.Size = new System.Drawing.Size(42, 17);
L_HintIllegal.TabIndex = 8;
L_HintIllegal.Text = "Illegal";
//
// L_LinesAfterMoves
//
L_LinesAfterMoves.AutoSize = true;
L_LinesAfterMoves.Location = new System.Drawing.Point(2, 158);
L_LinesAfterMoves.Margin = new System.Windows.Forms.Padding(2, 4, 0, 4);
L_LinesAfterMoves.Name = "L_LinesAfterMoves";
L_LinesAfterMoves.Size = new System.Drawing.Size(30, 17);
L_LinesAfterMoves.TabIndex = 6;
L_LinesAfterMoves.Text = "Info";
//
// PAN_Top
//
PAN_Top.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
PAN_Top.Controls.Add(FLP_Top);
PAN_Top.Dock = System.Windows.Forms.DockStyle.Top;
PAN_Top.Location = new System.Drawing.Point(0, 0);
PAN_Top.Margin = new System.Windows.Forms.Padding(0);
PAN_Top.Name = "PAN_Top";
PAN_Top.Size = new System.Drawing.Size(146, 34);
PAN_Top.TabIndex = 0;
//
// FLP_Top
//
FLP_Top.Controls.Add(PB_Ball);
FLP_Top.Controls.Add(L_Name);
FLP_Top.Controls.Add(PB_Gender);
FLP_Top.Dock = System.Windows.Forms.DockStyle.Fill;
FLP_Top.Location = new System.Drawing.Point(0, 0);
FLP_Top.Margin = new System.Windows.Forms.Padding(0);
FLP_Top.Name = "FLP_Top";
FLP_Top.Size = new System.Drawing.Size(144, 32);
FLP_Top.TabIndex = 71;
//
// PB_Ball
//
PB_Ball.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Center;
PB_Ball.Location = new System.Drawing.Point(4, 4);
PB_Ball.Margin = new System.Windows.Forms.Padding(4, 4, 0, 0);
PB_Ball.Name = "PB_Ball";
PB_Ball.Size = new System.Drawing.Size(24, 24);
PB_Ball.SizeMode = System.Windows.Forms.PictureBoxSizeMode.CenterImage;
PB_Ball.TabIndex = 68;
PB_Ball.TabStop = false;
//
// L_Name
//
L_Name.Location = new System.Drawing.Point(28, 4);
L_Name.Margin = new System.Windows.Forms.Padding(0, 4, 0, 0);
L_Name.Name = "L_Name";
L_Name.Size = new System.Drawing.Size(88, 24);
L_Name.TabIndex = 0;
L_Name.Text = "Species";
L_Name.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
//
// PB_Gender
//
PB_Gender.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Center;
PB_Gender.Location = new System.Drawing.Point(116, 4);
PB_Gender.Margin = new System.Windows.Forms.Padding(0, 4, 4, 0);
PB_Gender.Name = "PB_Gender";
PB_Gender.Size = new System.Drawing.Size(24, 24);
PB_Gender.SizeMode = System.Windows.Forms.PictureBoxSizeMode.CenterImage;
PB_Gender.TabIndex = 70;
PB_Gender.TabStop = false;
//
// PokePreview
//
AutoScaleMode = System.Windows.Forms.AutoScaleMode.Inherit;
AutoSize = true;
AutoSize = false;
AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
ClientSize = new System.Drawing.Size(148, 214);
Controls.Add(PAN_All);
FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
MaximizeBox = false;
MinimizeBox = false;
@ -245,36 +42,10 @@ private void InitializeComponent()
ShowIcon = false;
ShowInTaskbar = false;
Text = "PokePreview";
PAN_All.ResumeLayout(false);
PAN_All.PerformLayout();
FLP_List.ResumeLayout(false);
FLP_List.PerformLayout();
FLP_Moves.ResumeLayout(false);
FLP_Moves.PerformLayout();
PAN_Top.ResumeLayout(false);
FLP_Top.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)PB_Ball).EndInit();
((System.ComponentModel.ISupportInitialize)PB_Gender).EndInit();
ResumeLayout(false);
PerformLayout();
}
#endregion
private System.Windows.Forms.Panel PAN_All;
private System.Windows.Forms.FlowLayoutPanel FLP_List;
private System.Windows.Forms.Label L_LinesBeforeMoves;
private System.Windows.Forms.Panel PAN_Top;
private System.Windows.Forms.Label L_Name;
private System.Windows.Forms.PictureBox PB_Ball;
private System.Windows.Forms.PictureBox PB_Gender;
private MoveDisplay Move1;
private MoveDisplay Move2;
private MoveDisplay Move3;
private MoveDisplay Move4;
private System.Windows.Forms.Label L_LinesAfterMoves;
private System.Windows.Forms.FlowLayoutPanel FLP_Moves;
private System.Windows.Forms.FlowLayoutPanel FLP_Top;
private System.Windows.Forms.Label L_HintIllegal;
}
}

View File

@ -1,27 +1,15 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using PKHeX.Core;
using PKHeX.Drawing.Misc;
using PKHeX.Drawing.PokeSprite;
namespace PKHeX.WinForms.Controls;
public partial class PokePreview : Form
public sealed partial class PokePreview : Form
{
/// <summary> Minimum width to display the form. </summary>
private readonly int InitialWidth;
private readonly int InitialNameWidth;
private const int interiorMargin = 4; // 2x pixel border on each side
public PokePreview()
{
InitializeComponent();
InitialWidth = Width;
InitialNameWidth = L_Name.Width;
L_HintIllegal.ForeColor = WinFormsUtil.ColorWarn;
}
private static readonly Image[] GenderImages =
[
Properties.Resources.gender_0,
@ -29,30 +17,323 @@ public PokePreview()
Properties.Resources.gender_2,
];
public void Populate(PKM pk, in BattleTemplateExportSettings settings, in LegalityLocalizationContext ctx)
private readonly List<RenderTextLine> TextLinesPre = [];
private readonly List<RenderMoveLine> MoveLines = [];
private readonly List<RenderTextLine> TextLinesHint = [];
private readonly List<RenderTextLine> TextLinesEncounter = [];
private string HeaderName = string.Empty;
private Image? HeaderBall;
private Image? HeaderGender;
private const int Border = 1;
private const int HeaderTopPadding = 4;
private const int HeaderBottomPadding = 4;
private const int HeaderLeftPadding = 4;
private const int HeaderRightPadding = 4;
private const int HeaderIconGap = 2;
private const int BodyTopPadding = 2;
private const int BodyBottomPadding = 2;
private const int BodyLeftPadding = 4;
private const int BodyRightPadding = 4;
private const int TextSectionTopPadding = 2;
private const int TextSectionBottomPadding = 2;
private const int TextLineSpacing = 1;
private const int MoveIconTextGap = 2;
private const int MoveSectionTopPadding = 4;
private const int MoveSectionBottomPadding = 8;
private const int IconSize = 24;
private static Color IllegalTextColor => WinFormsUtil.ColorWarn;
public PokePreview()
{
int width = PopulateHeader(pk, settings);
PopulateMoves(pk, ctx.Analysis, settings, ref width);
PopulateText(pk, ctx, settings, width);
InitializeComponent();
SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true);
UpdateStyles();
}
private int PopulateHeader(PKM pk, in BattleTemplateExportSettings settings)
public void Populate(PKM pk, in BattleTemplateExportSettings settings, in LegalityLocalizationContext ctx)
{
var name = GetNameTitle(pk, settings);
var size = MeasureSize(name, L_Name.Font);
L_Name.Width = Math.Max(InitialNameWidth, size.Width);
L_Name.Text = name;
var main = Main.Settings;
HeaderName = GetNameTitle(pk, settings);
PopulateBall(pk);
PopulateGender(pk);
if (pk.Format > 2)
HeaderBall = GetBallImage(pk);
else
HeaderBall = null;
var width = L_Name.Width + PB_Ball.Width + PB_Ball.Margin.Horizontal + PB_Gender.Width + PB_Gender.Margin.Horizontal + interiorMargin;
return Math.Max(InitialWidth, width);
if (pk.Format != 1 || main.EntityEditor.ShowGenderGen1)
HeaderGender = GetGenderImage(pk);
else
HeaderGender = null;
TextLinesPre.Clear();
MoveLines.Clear();
TextLinesHint.Clear();
TextLinesEncounter.Clear();
var hover = Main.Settings.Hover;
var (before, mid, after) = GetBeforeAndAfter(pk, ctx, settings);
AppendTextSection(TextLinesPre, before, hover.PreviewShowPaste, ForeColor);
BuildMoves(pk, ctx.Analysis, settings);
AppendTextSection(TextLinesHint, mid, hover.HoverSlotShowLegalityHint, IllegalTextColor);
AppendTextSection(TextLinesEncounter, after, hover.HoverSlotShowEncounter, ForeColor);
ApplySize();
Invalidate();
}
private void BuildMoves(PKM pk, LegalityAnalysis la, in BattleTemplateExportSettings settings)
{
if (pk.MoveCount == 0)
return;
var context = pk.Context;
var strings = settings.Localization.Strings;
var names = strings.movelist;
var checks = la.Info.Moves;
AppendMoveLine(pk, strings, names, context, pk.Move1, checks[0].Valid);
AppendMoveLine(pk, strings, names, context, pk.Move2, checks[1].Valid);
AppendMoveLine(pk, strings, names, context, pk.Move3, checks[2].Valid);
AppendMoveLine(pk, strings, names, context, pk.Move4, checks[3].Valid);
}
private void AppendMoveLine(PKM pk, GameStrings strings, ReadOnlySpan<string> names, EntityContext context, ushort move, bool valid)
{
if (move == 0 || move >= names.Length)
return;
byte type = MoveInfo.GetType(move, context);
var name = names[move];
if (move == (int)PKHeX.Core.Move.HiddenPower && pk.Context is not EntityContext.Gen8a)
{
if (HiddenPower.TryGetTypeIndex(pk.HPType, out type))
name = $"{name} ({strings.types[type]}) [{pk.HPPower}]";
}
var image = TypeSpriteUtil.GetTypeSpriteIconSmall(type);
var color = valid ? ForeColor : IllegalTextColor;
MoveLines.Add(new RenderMoveLine(name, image, color));
}
private static void AppendTextSection(List<RenderTextLine> list, ReadOnlySpan<char> text, bool visible, Color color)
{
if (!visible || text.Length == 0)
return;
int i = 0;
var lines = text.EnumerateLines();
foreach (var line in lines)
{
var topPadding = i == 0 ? TextSectionTopPadding : TextLineSpacing;
const int bottomPadding = 0;
list.Add(new RenderTextLine(line.ToString(), color, topPadding, bottomPadding));
i++;
}
var last = list[^1];
list[^1] = last with { BottomPadding = TextSectionBottomPadding };
}
private void ApplySize()
{
var width = GetPreferredWidth();
var height = GetPreferredHeight();
var size = new Size(width, height);
if (Size != size)
Size = size;
}
private int GetPreferredWidth()
{
var width = 0;
var nameSize = MeasureSize(HeaderName, Font);
var headerWidth = Border + HeaderLeftPadding + nameSize.Width + HeaderRightPadding + Border;
if (HeaderBall != null)
headerWidth += IconSize + HeaderIconGap;
if (HeaderGender != null)
headerWidth += IconSize + HeaderIconGap;
width = Math.Max(width, headerWidth);
foreach (var move in MoveLines)
{
var size = MeasureSize(move.Text, Font);
var lineWidth = Border + BodyLeftPadding + IconSize + MoveIconTextGap + size.Width + BodyRightPadding + Border;
width = Math.Max(width, lineWidth);
}
foreach (var line in TextLinesPre)
{
var size = MeasureSize(line.Text, Font);
var lineWidth = Border + BodyLeftPadding + size.Width + BodyRightPadding + Border;
width = Math.Max(width, lineWidth);
}
foreach (var line in TextLinesHint)
{
var size = MeasureSize(line.Text, Font);
var lineWidth = Border + BodyLeftPadding + size.Width + BodyRightPadding + Border;
width = Math.Max(width, lineWidth);
}
foreach (var line in TextLinesEncounter)
{
var size = MeasureSize(line.Text, Font);
var lineWidth = Border + BodyLeftPadding + size.Width + BodyRightPadding + Border;
width = Math.Max(width, lineWidth);
}
return width;
}
private int GetPreferredHeight()
{
var height = Border;
height += HeaderTopPadding + IconSize + HeaderBottomPadding;
height += Border;
height += BodyTopPadding;
if (MoveLines.Count != 0)
{
height += MoveSectionTopPadding;
height += MoveLines.Count * IconSize;
height += MoveSectionBottomPadding;
}
foreach (var line in TextLinesPre)
{
height += line.TopPadding;
var textHeight = Math.Max(Font.Height, MeasureSize(line.Text, Font).Height);
height += textHeight;
height += line.BottomPadding;
}
foreach (var line in TextLinesHint)
{
height += line.TopPadding;
var textHeight = Math.Max(Font.Height, MeasureSize(line.Text, Font).Height);
height += textHeight;
height += line.BottomPadding;
}
foreach (var line in TextLinesEncounter)
{
height += line.TopPadding;
var textHeight = Math.Max(Font.Height, MeasureSize(line.Text, Font).Height);
height += textHeight;
height += line.BottomPadding;
}
height += BodyBottomPadding;
height += Border;
return height;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
var g = e.Graphics;
g.Clear(BackColor);
var outer = new Rectangle(0, 0, Width - 1, Height - 1);
g.DrawRectangle(SystemPens.ControlDark, outer);
var headerTop = Border;
var headerHeight = HeaderTopPadding + IconSize + HeaderBottomPadding;
var headerBottom = headerTop + headerHeight;
g.DrawLine(SystemPens.ControlDark, Border, headerBottom, Width - Border - 1, headerBottom);
DrawHeader(g, headerTop);
var y = headerBottom + BodyTopPadding;
y = DrawTextLines(TextLinesPre, g, y);
y = DrawMoves(g, y);
y = DrawTextLines(TextLinesHint, g, y);
if (TextLinesEncounter.Count != 0)
{
g.DrawLine(SystemPens.ControlDark, Border, y, Width - Border - 1, y);
y += Border;
_ = DrawTextLines(TextLinesEncounter, g, y);
}
}
private void DrawHeader(Graphics g, int headerTop)
{
var y = headerTop + HeaderTopPadding;
var x = Border + HeaderLeftPadding;
if (HeaderBall is not null)
{
g.DrawImage(HeaderBall, new Rectangle(x, y, IconSize, IconSize));
x += IconSize + HeaderIconGap;
}
var textRect = new Rectangle(x, y, Math.Max(0, Width - x - Border - HeaderRightPadding - IconSize - HeaderIconGap), IconSize);
TextRenderer.DrawText(g, HeaderName, Font, textRect, ForeColor, TextFormatFlags.Left | TextFormatFlags.VerticalCenter | TextFormatFlags.EndEllipsis | TextFormatFlags.NoPadding);
// Gender displayed at right edge.
if (HeaderGender is not null)
{
var genderX = Width - Border - HeaderRightPadding - IconSize;
g.DrawImage(HeaderGender, new Rectangle(genderX, y, IconSize, IconSize));
}
}
private int DrawMoves(Graphics g, int y)
{
if (MoveLines.Count == 0)
return y;
const int x = Border + BodyLeftPadding;
y += MoveSectionTopPadding;
foreach (var line in MoveLines)
{
if (line.Icon is not null)
g.DrawImage(line.Icon, new Rectangle(x, y, IconSize, IconSize));
var textRect = new Rectangle(x + IconSize + MoveIconTextGap, y, Math.Max(0, Width - (x + IconSize + MoveIconTextGap) - Border - BodyRightPadding), IconSize);
TextRenderer.DrawText(g, line.Text, Font, textRect, line.Color, TextFormatFlags.Left | TextFormatFlags.VerticalCenter | TextFormatFlags.NoPadding);
y += IconSize;
}
y += MoveSectionBottomPadding;
return y;
}
private int DrawTextLines(List<RenderTextLine> list, Graphics g, int y)
{
const int x = Border + BodyLeftPadding;
var textWidth = Math.Max(0, Width - x - Border - BodyRightPadding);
foreach (var line in list)
{
y += line.TopPadding;
var height = Math.Max(Font.Height, MeasureSize(line.Text, Font).Height);
var rect = new Rectangle(x, y, textWidth, height);
TextRenderer.DrawText(g, line.Text, Font, rect, line.Color, TextFormatFlags.Left | TextFormatFlags.NoPadding);
y += height + line.BottomPadding;
}
return y;
}
public static Size MeasureSize(ReadOnlySpan<char> text, Font font)
{
const TextFormatFlags flags = TextFormatFlags.LeftAndRightPadding | TextFormatFlags.VerticalCenter;
return TextRenderer.MeasureText(text, font, new Size(), flags);
}
private static string GetNameTitle(PKM pk, in BattleTemplateExportSettings settings)
{
// Don't care about form; the user will be able to see the sprite next to the preview.
var nick = pk.Nickname;
var strings = settings.Localization.Strings;
var all = strings.Species;
@ -61,85 +342,30 @@ private static string GetNameTitle(PKM pk, in BattleTemplateExportSettings setti
return nick;
var expect = all[species];
if (settings.IsTokenInExport(BattleTemplateToken.Nickname))
return expect; // Nickname will be on another line.
return expect;
if (nick.Equals(expect, StringComparison.OrdinalIgnoreCase))
return nick;
return $"{nick} ({expect})";
}
private void PopulateBall(PKM pk)
private static Image GetBallImage(PKM pk)
{
var ball = (byte)Ball.Poke;
if (pk.Format >= 3)
ball = pk.Ball;
PB_Ball.Image = Drawing.PokeSprite.SpriteUtil.GetBallSprite(ball);
return SpriteUtil.GetBallSprite(ball);
}
private void PopulateGender(PKM pk)
private static Image? GetGenderImage(PKM pk)
{
if (pk.Format == 1)
{
PB_Gender.Image = null;
return;
}
return null;
var gender = pk.Gender;
if (gender >= GenderImages.Length)
gender = 2;
PB_Gender.Image = GenderImages[gender];
}
private void PopulateMoves(PKM pk, LegalityAnalysis la, in BattleTemplateExportSettings settings, ref int width)
{
var context = pk.Context;
var strings = settings.Localization.Strings;
var names = strings.movelist;
var check = la.Info.Moves;
var w1 = Move1.Populate(pk, strings, pk.Move1, context, names, check[0].Valid);
var w2 = Move2.Populate(pk, strings, pk.Move2, context, names, check[1].Valid);
var w3 = Move3.Populate(pk, strings, pk.Move3, context, names, check[2].Valid);
var w4 = Move4.Populate(pk, strings, pk.Move4, context, names, check[3].Valid);
var maxWidth = Math.Max(w1, Math.Max(w2, Math.Max(w3, w4)));
width = Math.Max(width, maxWidth + Move1.Margin.Horizontal + interiorMargin);
}
private void PopulateText(PKM pk, in LegalityLocalizationContext la, in BattleTemplateExportSettings settings, int width)
{
var (before, mid, after) = GetBeforeAndAfter(pk, la, settings);
var hover = Main.Settings.Hover;
bool hasMoves = pk.MoveCount != 0;
FLP_Moves.Visible = hasMoves;
var height = FLP_List.Top + interiorMargin;
if (hasMoves)
height += FLP_Moves.Height + FLP_Moves.Margin.Vertical;
ToggleLabel(L_LinesBeforeMoves, before, hover.PreviewShowPaste, ref width, ref height);
ToggleLabel(L_HintIllegal, mid, hover.HoverSlotShowLegalityHint, ref width, ref height);
ToggleLabel(L_LinesAfterMoves, after, hover.HoverSlotShowEncounter, ref width, ref height);
Size = new Size(width, height);
}
private static void ToggleLabel(Control display, string text, bool visible, ref int width, ref int height)
{
if (!visible || text.Length == 0)
{
display.Visible = false;
return;
}
var size = MeasureSize(text, display.Font);
width = Math.Max(width, display.Margin.Horizontal + size.Width);
height += size.Height + display.Margin.Vertical;
display.Text = text;
display.Visible = true;
}
public static Size MeasureSize(ReadOnlySpan<char> text, Font font)
{
const TextFormatFlags flags = TextFormatFlags.LeftAndRightPadding | TextFormatFlags.VerticalCenter;
return TextRenderer.MeasureText(text, font, new Size(), flags);
return GenderImages[gender];
}
private static (string Before, string Middle, string After) GetBeforeAndAfter(PKM pk, in LegalityLocalizationContext la, in BattleTemplateExportSettings settings)
@ -218,4 +444,7 @@ protected override CreateParams CreateParams
return createParams;
}
}
private readonly record struct RenderMoveLine(string Text, Image? Icon, Color Color);
private readonly record struct RenderTextLine(string Text, Color Color, int TopPadding, int BottomPadding);
}

View File

@ -38,6 +38,11 @@ public void Show(Control pb, PKM pk, StorageSlotType type = StorageSlotType.None
else if (Settings.HoverSlotShowText)
{
var text = GetPreviewText(pk, settings);
if (!settings.Order.Contains(BattleTemplateToken.FirstLine))
{
var insert = GetPreviewText(pk, settings with { Order = [BattleTemplateToken.FirstLine] });
text = insert + Environment.NewLine + text;
}
if (Settings.HoverSlotShowEncounter)
text = AppendEncounterInfo(ctx, text);
ShowSet.SetToolTip(pb, text);
@ -52,9 +57,11 @@ private void UpdatePreview(Control pb, PKM pk, in BattleTemplateExportSettings s
_source.Cancel();
_source.Dispose(); // Properly dispose the previous CancellationTokenSource
_source = new();
var wasVisible = Previewer.Visible;
UpdatePreviewPosition(new());
Previewer.Populate(pk, settings, ctx);
ShowInactiveTopmost(Previewer);
if (!wasVisible)
ShowInactiveTopmost(Previewer);
}
private const int SW_SHOWNOACTIVATE = 4;