From 662c3db7dc68898e9c6cdd27dde93356044ad33c Mon Sep 17 00:00:00 2001 From: Kurt Date: Mon, 9 Mar 2026 20:28:43 -0500 Subject: [PATCH] Faster box hover preview Render it manually rather than let controls be goofy with draw calls. --- .../Controls/Slots/PokePreview.Designer.cs | 231 +--------- PKHeX.WinForms/Controls/Slots/PokePreview.cs | 411 ++++++++++++++---- .../Controls/Slots/SummaryPreviewer.cs | 9 +- 3 files changed, 329 insertions(+), 322 deletions(-) diff --git a/PKHeX.WinForms/Controls/Slots/PokePreview.Designer.cs b/PKHeX.WinForms/Controls/Slots/PokePreview.Designer.cs index ca2e09d95..a31d235fc 100644 --- a/PKHeX.WinForms/Controls/Slots/PokePreview.Designer.cs +++ b/PKHeX.WinForms/Controls/Slots/PokePreview.Designer.cs @@ -28,216 +28,13 @@ protected override void Dispose(bool disposing) /// 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; } } diff --git a/PKHeX.WinForms/Controls/Slots/PokePreview.cs b/PKHeX.WinForms/Controls/Slots/PokePreview.cs index ba5684f05..f3eed0e70 100644 --- a/PKHeX.WinForms/Controls/Slots/PokePreview.cs +++ b/PKHeX.WinForms/Controls/Slots/PokePreview.cs @@ -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 { - /// Minimum width to display the form. - 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 TextLinesPre = []; + private readonly List MoveLines = []; + private readonly List TextLinesHint = []; + private readonly List 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 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 list, ReadOnlySpan 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 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 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 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); } diff --git a/PKHeX.WinForms/Controls/Slots/SummaryPreviewer.cs b/PKHeX.WinForms/Controls/Slots/SummaryPreviewer.cs index b21724301..c44289b8c 100644 --- a/PKHeX.WinForms/Controls/Slots/SummaryPreviewer.cs +++ b/PKHeX.WinForms/Controls/Slots/SummaryPreviewer.cs @@ -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;