PKHeX/PKHeX.WinForms/Controls/Slots/SummaryPreviewer.cs
Kurt 9792455f34 Refactor to use Context over Generation
Generation was always more weak; am I paranoid about potential VC3? maybe
Better indicates the move source for LGPE exclusive moves, etc.
2026-02-15 02:15:50 -06:00

204 lines
6.9 KiB
C#

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using PKHeX.Core;
namespace PKHeX.WinForms.Controls;
public sealed class SummaryPreviewer
{
private readonly ToolTip ShowSet = new() { InitialDelay = 200, IsBalloon = false, AutoPopDelay = 32_767 };
private readonly CryPlayer Cry = new();
private readonly PokePreview Previewer = new();
private CancellationTokenSource _source = new();
private static HoverSettings Settings => Main.Settings.Hover;
public void Show(Control pb, PKM pk, StorageSlotType type = StorageSlotType.None)
{
if (pk.Species == 0)
{
Clear();
return;
}
var programLanguage = Language.GetLanguageValue(Main.Settings.Startup.Language);
var cfg = Main.Settings.BattleTemplate;
var settings = cfg.Hover.GetSettings(programLanguage, pk.Context);
var localize = LegalityLocalizationSet.GetLocalization(programLanguage);
var la = new LegalityAnalysis(pk, type);
var ctx = LegalityLocalizationContext.Create(la, localize);
if (Settings.HoverSlotShowPreview && Control.ModifierKeys != Keys.Alt)
{
UpdatePreview(pb, pk, settings, ctx);
}
else if (Settings.HoverSlotShowText)
{
var text = GetPreviewText(pk, settings);
if (Settings.HoverSlotShowEncounter)
text = AppendEncounterInfo(ctx, text);
ShowSet.SetToolTip(pb, text);
}
if (Settings.HoverSlotPlayCry)
Cry.PlayCry(pk, pk.Context);
}
private void UpdatePreview(Control pb, PKM pk, in BattleTemplateExportSettings settings, in LegalityLocalizationContext ctx)
{
_source.Cancel();
_source.Dispose(); // Properly dispose the previous CancellationTokenSource
_source = new();
UpdatePreviewPosition(new());
Previewer.Populate(pk, settings, ctx);
ShowInactiveTopmost(Previewer);
}
private const int SW_SHOWNOACTIVATE = 4;
private const int HWND_TOPMOST = -1;
private const uint SWP_NOACTIVATE = 0x0010;
#pragma warning disable SYSLIB1054 // Use 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time
[System.Runtime.InteropServices.DllImport("user32.dll", EntryPoint = "SetWindowPos")]
private static extern bool SetWindowPos(
int hWnd, // Window handle
int hWndInsertAfter, // Placement-order handle
int X, // Horizontal position
int Y, // Vertical position
int cx, // Width
int cy, // Height
uint uFlags); // Window positioning flags
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern bool ShowWindow(nint hWnd, int nCmdShow);
#pragma warning restore SYSLIB1054 // Use 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time
public static void ShowInactiveTopmost(Form frm)
{
try
{
ShowWindow(frm.Handle, SW_SHOWNOACTIVATE);
SetWindowPos(frm.Handle.ToInt32(), HWND_TOPMOST,
frm.Left, frm.Top, frm.Width, frm.Height,
SWP_NOACTIVATE);
}
catch
{
// error handling
}
}
public void UpdatePreviewPosition(Point location)
{
var cLoc = Cursor.Position;
var shift = Settings.PreviewCursorShift;
cLoc.Offset(shift);
Previewer.Location = cLoc;
}
public void Show(Control pb, IEncounterInfo enc)
{
if (enc.Species == 0)
{
Clear();
return;
}
if (Settings.HoverSlotShowText)
ShowSet.SetToolTip(pb, GetPreviewText(enc, Settings.HoverSlotShowEncounterVerbose));
if (Settings.HoverSlotPlayCry)
Cry.PlayCry(enc, enc.Context);
}
public void Clear()
{
try
{
var token = _source.Token;
Task.Run(async () =>
{
if (!Previewer.IsHandleCreated)
return; // not shown ever
// Give a little bit of fade-out delay
await Task.Delay(50, CancellationToken.None).ConfigureAwait(false);
if (!token.IsCancellationRequested)
await Previewer.InvokeAsync(Previewer.Hide, CancellationToken.None).ConfigureAwait(false);
}, CancellationToken.None).ConfigureAwait(false);
}
catch
{
// Ignore.
}
ShowSet.RemoveAll();
Cry.Stop();
}
public static string GetPreviewText(PKM pk, BattleTemplateExportSettings settings) => ShowdownParsing.GetLocalizedPreviewText(pk, settings);
public static string AppendEncounterInfo(LegalityLocalizationContext la, string text)
{
var result = new List<string>(8);
if (text.Length != 0) // add a blank line between the set and the encounter info if isn't already a blank line
{
result.Add(text);
result.Add(string.Empty);
}
LegalityFormatting.AddEncounterInfo(la, result);
return string.Join(Environment.NewLine, result);
}
private static string GetPreviewText(IEncounterInfo enc, bool verbose = false)
{
var lines = enc.GetTextLines(verbose, Main.CurrentLanguage);
return string.Join(Environment.NewLine, lines);
}
public static string AppendLegalityHint(in LegalityLocalizationContext la, string line)
{
// Get the first illegal check result, and append the localization of it as the hint.
// If all legal, return the input string unchanged.
var analysis = la.Analysis;
if (analysis.Valid)
return line;
foreach (var chk in analysis.Results)
{
if (chk.Valid)
continue;
var hint = la.Humanize(chk, verbose: true);
return Join(line, hint);
}
for (var i = 0; i < analysis.Info.Moves.Length; i++)
{
var chk = analysis.Info.Moves[i];
if (chk.Valid)
continue;
var hint = la.FormatMove(chk, i, la.Analysis.Info.Entity.Context);
return Join(line, hint);
}
for (var i = 0; i < analysis.Info.Relearn.Length; i++)
{
var chk = analysis.Info.Relearn[i];
if (chk.Valid)
continue;
var hint = la.FormatMove(chk, i, la.Analysis.Info.Entity.Context);
return Join(line, hint);
}
return line;
static string Join(string line, string hint)
{
if (hint.Length > 67)
hint = hint[..67] + "...";
return string.IsNullOrEmpty(line) ? hint : $"{line}{Environment.NewLine}{hint}";
}
}
}