Enhanced Slot Hover Preview (#4059)

Adds a new primary Hover Preview tooltip form. Users can change setting to use the old tooltip if they want.

When the user hovers over a slot in their Box / Party, PKHeX displays a tooltip indicating details about the Pokémon. This text tooltip shows the Showdown text (with some localization based on program setting), and includes details about the encounter the legality check matched it to.
This commit is contained in:
Kurt
2023-11-14 19:36:11 -08:00
committed by GitHub
parent d43b78349c
commit c505b5a49d
16 changed files with 742 additions and 35 deletions

View File

@@ -425,7 +425,7 @@ private string GetSpeciesNickname(string specForm)
return $"{Nickname} ({specForm})";
}
private static string[] GetStringStats(ReadOnlySpan<int> stats, int ignoreValue)
public static string[] GetStringStats<T>(ReadOnlySpan<T> stats, T ignoreValue) where T : IEquatable<T>
{
var count = stats.Length - stats.Count(ignoreValue);
if (count == 0)
@@ -437,7 +437,7 @@ private static string[] GetStringStats(ReadOnlySpan<int> stats, int ignoreValue)
{
var statIndex = GetStatIndexStored(i);
var statValue = stats[statIndex];
if (statValue == ignoreValue)
if (statValue.Equals(ignoreValue))
continue; // ignore unused stats
var statName = StatNames[statIndex];
result[ctr++] = $"{statValue} {statName}";

View File

@@ -137,6 +137,23 @@ public static void ClearGanbaruValues(this IGanbaru g)
_ => throw new ArgumentOutOfRangeException(nameof(index), index, "Index must be between 0 and 5."),
};
/// <summary>
/// Loads the <see cref="IGanbaru"/> values and stores them in the provided <see cref="Span{T}"/>.
/// </summary>
/// <param name="pk">Pokémon to check.</param>
/// <param name="value">Span to store values in.</param>
public static void GetGVs(this IGanbaru pk, Span<byte> value)
{
if (value.Length != 6)
return;
value[0] = pk.GV_HP;
value[1] = pk.GV_ATK;
value[2] = pk.GV_DEF;
value[3] = pk.GV_SPE;
value[4] = pk.GV_SPA;
value[5] = pk.GV_SPD;
}
/// <summary>
/// Checks if any of the <see cref="IGanbaru"/> values are below a reference's minimum value.
/// </summary>

View File

@@ -51,7 +51,7 @@ public void UpdateLegality(MoveResult move, PKM entity, int i)
return;
}
PB_Triangle.Visible = true;
PB_Triangle.Image = MoveDisplay.GetMoveImage(!move.Valid, entity, i);
PB_Triangle.Image = MoveDisplayState.GetMoveImage(!move.Valid, entity, i);
}
public void HealPP(PKM pk)

View File

@@ -0,0 +1,87 @@
namespace PKHeX.WinForms.Controls
{
partial class MoveDisplay
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
FLP_Move = new System.Windows.Forms.FlowLayoutPanel();
PB_Type = new System.Windows.Forms.PictureBox();
L_Move = new System.Windows.Forms.Label();
FLP_Move.SuspendLayout();
((System.ComponentModel.ISupportInitialize)PB_Type).BeginInit();
SuspendLayout();
//
// FLP_Move
//
FLP_Move.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
FLP_Move.Controls.Add(PB_Type);
FLP_Move.Controls.Add(L_Move);
FLP_Move.Location = new System.Drawing.Point(0, 0);
FLP_Move.Name = "FLP_Move";
FLP_Move.Size = new System.Drawing.Size(138, 24);
FLP_Move.TabIndex = 18;
//
// PB_Type
//
PB_Type.Location = new System.Drawing.Point(0, 0);
PB_Type.Margin = new System.Windows.Forms.Padding(0, 0, 2, 0);
PB_Type.Name = "PB_Type";
PB_Type.Size = new System.Drawing.Size(24, 24);
PB_Type.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
PB_Type.TabIndex = 4;
PB_Type.TabStop = false;
//
// L_Move
//
L_Move.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
L_Move.Location = new System.Drawing.Point(26, 0);
L_Move.Margin = new System.Windows.Forms.Padding(0);
L_Move.Name = "L_Move";
L_Move.Size = new System.Drawing.Size(112, 24);
L_Move.TabIndex = 79;
L_Move.Text = "Name";
L_Move.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
//
// MoveDisplay
//
AutoScaleMode = System.Windows.Forms.AutoScaleMode.Inherit;
AutoSize = true;
AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
Controls.Add(FLP_Move);
Name = "MoveDisplay";
Size = new System.Drawing.Size(138, 24);
FLP_Move.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)PB_Type).EndInit();
ResumeLayout(false);
}
#endregion
private System.Windows.Forms.FlowLayoutPanel FLP_Move;
private System.Windows.Forms.PictureBox PB_Type;
private System.Windows.Forms.Label L_Move;
}
}

View File

@@ -0,0 +1,44 @@
using System;
using System.Drawing;
using System.Windows.Forms;
using PKHeX.Core;
using PKHeX.Drawing.Misc;
namespace PKHeX.WinForms.Controls;
public partial class MoveDisplay : UserControl
{
public MoveDisplay() => InitializeComponent();
public int Populate(PKM pk, ushort move, EntityContext context, ReadOnlySpan<string> moves, bool valid = true)
{
if (move == 0 || move >= moves.Length)
{
Visible = false;
return 0;
}
Visible = true;
byte type = MoveInfo.GetType(move, context);
var name = moves[move];
if (move == (int)Core.Move.HiddenPower && pk.Context is not EntityContext.Gen8a)
{
type = (byte)pk.HPType;
name = $"{name} ({GameInfo.Strings.types[type]}) [{pk.HPPower}]";
}
var size = PokePreview.MeasureSize(name, L_Move.Font);
var ctrlWidth = PB_Type.Width + PB_Type.Margin.Horizontal + size.Width + L_Move.Margin.Horizontal;
PB_Type.Image = TypeSpriteUtil.GetTypeSpriteIcon(type);
L_Move.Text = name;
if (valid)
L_Move.ResetForeColor();
else
L_Move.ForeColor = Color.Red;
L_Move.Width = size.Width;
Width = ctrlWidth;
return ctrlWidth;
}
}

View File

@@ -2238,7 +2238,7 @@ private void PB_MarkCured_Click(object sender, EventArgs e)
}
}
public static class MoveDisplay
public static class MoveDisplayState
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Bitmap? GetMoveImage(bool isIllegal, PKM pk, int index)

View File

@@ -111,7 +111,10 @@ public void DragEnter(object? sender, DragEventArgs e)
public void MouseMove(object? sender, MouseEventArgs e)
{
if (!Drag.CanStartDrag)
{
Hover.UpdateMousePosition(e.Location);
return;
}
if (sender is not PictureBox pb)
return;

View File

@@ -0,0 +1,260 @@
namespace PKHeX.WinForms.Controls
{
partial class PokePreview
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
PAN_All = new System.Windows.Forms.Panel();
FLP_List = new System.Windows.Forms.FlowLayoutPanel();
L_Stats = 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_Etc = 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.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, 180);
PAN_All.TabIndex = 19;
//
// FLP_List
//
FLP_List.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
FLP_List.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
FLP_List.Controls.Add(L_Stats);
FLP_List.Controls.Add(FLP_Moves);
FLP_List.Controls.Add(L_Etc);
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, 144);
FLP_List.TabIndex = 1;
FLP_List.WrapContents = false;
//
// L_Stats
//
L_Stats.AutoSize = true;
L_Stats.Location = new System.Drawing.Point(2, 4);
L_Stats.Margin = new System.Windows.Forms.Padding(2, 4, 0, 0);
L_Stats.Name = "L_Stats";
L_Stats.Size = new System.Drawing.Size(32, 15);
L_Stats.TabIndex = 5;
L_Stats.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, 23);
FLP_Moves.Margin = new System.Windows.Forms.Padding(0, 4, 0, 4);
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_Etc
//
L_Etc.AutoSize = true;
L_Etc.Location = new System.Drawing.Point(2, 123);
L_Etc.Margin = new System.Windows.Forms.Padding(2, 0, 0, 4);
L_Etc.Name = "L_Etc";
L_Etc.Size = new System.Drawing.Size(28, 15);
L_Etc.TabIndex = 6;
L_Etc.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
//
AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
ClientSize = new System.Drawing.Size(148, 180);
Controls.Add(PAN_All);
FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
MaximizeBox = false;
MinimizeBox = false;
Name = "PokePreview";
ShowIcon = false;
ShowInTaskbar = false;
Text = "PokePreview";
PAN_All.ResumeLayout(false);
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);
}
#endregion
private System.Windows.Forms.Panel PAN_All;
private System.Windows.Forms.FlowLayoutPanel FLP_List;
private System.Windows.Forms.Label L_Stats;
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_Etc;
private System.Windows.Forms.FlowLayoutPanel FLP_Moves;
private System.Windows.Forms.FlowLayoutPanel FLP_Top;
}
}

View File

@@ -0,0 +1,227 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using PKHeX.Core;
namespace PKHeX.WinForms.Controls;
public 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;
}
private static readonly Image[] GenderImages =
{
Properties.Resources.gender_0,
Properties.Resources.gender_1,
Properties.Resources.gender_2,
};
public void Populate(PKM pk)
{
var la = new LegalityAnalysis(pk);
int width = PopulateHeader(pk);
PopulateMoves(pk, la, ref width);
PopulateText(pk, la, width);
}
private int PopulateHeader(PKM pk)
{
var name = GetNameTitle(pk);
var size = MeasureSize(name, L_Name.Font);
L_Name.Width = Math.Max(InitialNameWidth, size.Width);
L_Name.Text = name;
PopulateBall(pk);
PopulateGender(pk);
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);
}
private static string GetNameTitle(PKM pk)
{
var nick = pk.Nickname;
var all = GameInfo.Strings.Species;
var species = pk.Species;
if (species >= all.Count)
return nick;
var expect = all[species];
if (nick.Equals(expect, StringComparison.OrdinalIgnoreCase))
return nick;
return $"{nick} ({expect})";
}
private void PopulateBall(PKM pk)
{
int ball = (int)Ball.Poke;
if (pk.Format >= 3)
ball = pk.Ball;
PB_Ball.Image = Drawing.PokeSprite.SpriteUtil.GetBallSprite(ball);
}
private void PopulateGender(PKM pk)
{
if (pk.Format == 1)
{
PB_Gender.Image = null;
return;
}
var gender = pk.Gender;
if (gender > GenderImages.Length)
gender = 2;
PB_Gender.Image = GenderImages[gender];
}
private void PopulateMoves(PKM pk, LegalityAnalysis la, ref int width)
{
var context = pk.Context;
var names = GameInfo.Strings.movelist;
var check = la.Info.Moves;
var w1 = Move1.Populate(pk, pk.Move1, context, names, check[0].Valid);
var w2 = Move2.Populate(pk, pk.Move2, context, names, check[1].Valid);
var w3 = Move3.Populate(pk, pk.Move3, context, names, check[2].Valid);
var w4 = Move4.Populate(pk, 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, LegalityAnalysis la, int width)
{
var (stats, enc) = GetStatsString(pk, la);
var settings = 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_Stats, stats, settings.PreviewShowPaste, ref width, ref height);
ToggleLabel(L_Etc, enc, settings.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)
{
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(string text, Font font)
{
const TextFormatFlags flags = TextFormatFlags.LeftAndRightPadding | TextFormatFlags.VerticalCenter;
return TextRenderer.MeasureText(text, font, new Size(), flags);
}
private static (string Detail, string Encounter) GetStatsString(PKM pk, LegalityAnalysis la)
{
var setText = SummaryPreviewer.GetPreviewText(pk, la);
var sb = new StringBuilder();
var lines = setText.AsSpan().EnumerateLines();
if (!lines.MoveNext())
throw new ArgumentException("Invalid text format", nameof(pk));
var first = lines.Current;
var itemIndex = first.IndexOf('@');
if (itemIndex != -1) // Held Item
{
var remaining = first[(itemIndex + 2)..];
if (remaining[^1] == ')')
remaining = remaining[..^3]; // lop off gender
var item = remaining.Trim();
if (item.Length > 0)
sb.AppendLine($"Held Item: {item}");
}
if (pk is IGanbaru g)
AddGanbaru(g, sb);
if (pk is IAwakened a)
AddAwakening(a, sb);
while (lines.MoveNext())
{
var line = lines.Current;
if (IsMoveLine(line))
{
while (lines.MoveNext())
{
if (!IsMoveLine(lines.Current))
break;
}
break;
}
sb.AppendLine(line.ToString());
}
var detail = sb.ToString();
sb.Clear();
while (lines.MoveNext())
{
var line = lines.Current;
sb.AppendLine(line.ToString());
}
var enc = sb.ToString();
return (detail.TrimEnd(), enc.TrimEnd());
static bool IsMoveLine(ReadOnlySpan<char> line) => line.Length != 0 && line[0] == '-';
}
private static void AddGanbaru(IGanbaru g, StringBuilder sb)
{
Span<byte> gvs = stackalloc byte[6];
g.GetGVs(gvs);
TryAdd<byte>(sb, "GVs", gvs);
}
private static void AddAwakening(IAwakened a, StringBuilder sb)
{
Span<byte> avs = stackalloc byte[6];
a.GetAVs(avs);
TryAdd<byte>(sb, "AVs", avs);
}
private static void TryAdd<T>(StringBuilder sb, [ConstantExpected] string type, ReadOnlySpan<T> stats, T ignore = default) where T : unmanaged, IEquatable<T>
{
var chunks = ShowdownSet.GetStringStats(stats, ignore);
if (chunks.Length != 0)
sb.AppendLine($"{type}: {string.Join(" / ", chunks)}");
}
/// <summary> Prevent stealing focus from the form that shows this. </summary>
protected override bool ShowWithoutActivation => true;
private const int WS_EX_TOPMOST = 0x00000008;
protected override CreateParams CreateParams
{
get
{
CreateParams createParams = base.CreateParams;
createParams.ExStyle |= WS_EX_TOPMOST;
return createParams;
}
}
}

View File

@@ -15,8 +15,7 @@ public sealed class SlotHoverHandler : IDisposable
public DrawConfig Draw { private get; set; } = new();
public bool GlowHover { private get; set; } = true;
public static readonly CryPlayer CryPlayer = new();
public static readonly SummaryPreviewer Preview = new();
private readonly SummaryPreviewer Preview = new();
private static Bitmap Hover => SpriteUtil.Spriter.Hover;
private readonly BitmapAnimator HoverWorker = new();
@@ -80,4 +79,9 @@ public void Dispose()
Slot = null;
Draw.Dispose();
}
public void UpdateMousePosition(Point location)
{
Preview.UpdatePreviewPosition(location);
}
}

View File

@@ -1,5 +1,8 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using PKHeX.Core;
@@ -9,6 +12,9 @@ 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)
{
@@ -18,19 +24,31 @@ public void Show(Control pb, PKM pk)
return;
}
if (Main.Settings.Hover.HoverSlotShowText)
{
var text = ShowdownParsing.GetLocalizedPreviewText(pk, Main.Settings.Startup.Language);
var la = new LegalityAnalysis(pk);
var result = new List<string> { text, string.Empty };
LegalityFormatting.AddEncounterInfo(la, result);
ShowSet.SetToolTip(pb, string.Join(Environment.NewLine, result));
}
if (Main.Settings.Hover.HoverSlotPlayCry)
if (Settings.HoverSlotShowPreview && Control.ModifierKeys != Keys.Alt)
UpdatePreview(pb, pk);
else if (Settings.HoverSlotShowText)
ShowSet.SetToolTip(pb, GetPreviewText(pk, new LegalityAnalysis(pk)));
if (Settings.HoverSlotPlayCry)
Cry.PlayCry(pk, pk.Context);
}
private void UpdatePreview(Control pb, PKM pk)
{
_source.Cancel();
_source = new();
UpdatePreviewPosition(new());
Previewer.Populate(pk);
Previewer.Show();
}
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)
@@ -39,20 +57,38 @@ public void Show(Control pb, IEncounterInfo enc)
return;
}
if (Main.Settings.Hover.HoverSlotShowText)
{
var lines = enc.GetTextLines(GameInfo.Strings);
var text = string.Join(Environment.NewLine, lines);
ShowSet.SetToolTip(pb, text);
}
if (Main.Settings.Hover.HoverSlotPlayCry)
if (Settings.HoverSlotShowText)
ShowSet.SetToolTip(pb, GetPreviewText(enc));
if (Settings.HoverSlotPlayCry)
Cry.PlayCry(enc, enc.Context);
}
public void Clear()
{
var src = _source;
Task.Run(async () =>
{
await Task.Delay(50, CancellationToken.None).ConfigureAwait(false);
if (!src.IsCancellationRequested)
Previewer.Invoke(Previewer.Hide);
}, src.Token);
ShowSet.RemoveAll();
Cry.Stop();
}
public static string GetPreviewText(PKM pk, LegalityAnalysis la)
{
var text = ShowdownParsing.GetLocalizedPreviewText(pk, Main.Settings.Startup.Language);
if (!Main.Settings.Hover.HoverSlotShowEncounter)
return text;
var result = new List<string> { text, string.Empty };
LegalityFormatting.AddEncounterInfo(la, result);
return string.Join(Environment.NewLine, result);
}
private static string GetPreviewText(IEncounterInfo enc)
{
var lines = enc.GetTextLines(GameInfo.Strings);
return string.Join(Environment.NewLine, lines);
}
}

View File

@@ -380,7 +380,7 @@ private static void ClosePopups()
}
}
private static bool IsPopupFormType(Form z) => z is not (Main or SplashScreen or SAV_FolderList);
private static bool IsPopupFormType(Form z) => z is not (Main or SplashScreen or SAV_FolderList or PokePreview);
private void MainMenuSettings(object sender, EventArgs e)
{

View File

@@ -317,6 +317,12 @@ public sealed class MysteryGiftDatabaseSettings
[Serializable]
public sealed class HoverSettings
{
[LocalizedDescription("Show PKM Slot Preview on Hover")]
public bool HoverSlotShowPreview { get; set; } = true;
[LocalizedDescription("Show Encounter Info in on Hover")]
public bool HoverSlotShowEncounter { get; set; } = true;
[LocalizedDescription("Show PKM Slot ToolTip on Hover")]
public bool HoverSlotShowText { get; set; } = true;
@@ -325,6 +331,12 @@ public sealed class HoverSettings
[LocalizedDescription("Show a Glow effect around the PKM on Hover")]
public bool HoverSlotGlowEdges { get; set; } = true;
[LocalizedDescription("Show Showdown Paste in special Preview on Hover")]
public bool PreviewShowPaste { get; set; } = true;
[LocalizedDescription("Show a Glow effect around the PKM on Hover")]
public Point PreviewCursorShift { get; set; } = new(16, 8);
}
[Serializable]

View File

@@ -77,7 +77,11 @@ public SAV_Database(PKMEditor f1, SAVEditor saveditor)
slot.ContextMenuStrip = mnu;
if (Main.Settings.Hover.HoverSlotShowText)
{
slot.MouseMove += (o, args) => ShowSet.UpdatePreviewPosition(args.Location);
slot.MouseEnter += (o, args) => ShowHoverTextForSlot(slot, args);
slot.MouseLeave += (o, args) => ShowSet.Clear();
}
slot.Enter += (sender, e) =>
{
if (sender is not PictureBox pb)
@@ -121,6 +125,7 @@ public SAV_Database(PKMEditor f1, SAVEditor saveditor)
};
CB_Format.Items[0] = MsgAny;
CenterToParent();
Closing += (sender, e) => ShowSet.Clear();
}
private readonly PictureBox[] PKXBOXES;
@@ -615,8 +620,10 @@ private async void B_Search_Click(object sender, EventArgs e)
private void UpdateScroll(object sender, ScrollEventArgs e)
{
if (e.OldValue != e.NewValue)
FillPKXBoxes(e.NewValue);
if (e.OldValue == e.NewValue)
return;
FillPKXBoxes(e.NewValue);
ShowSet.Clear();
}
private void SetResults(List<SlotCache> res)
@@ -688,8 +695,10 @@ protected override void OnMouseWheel(MouseEventArgs e)
return;
int oldval = SCR_Box.Value;
int newval = oldval + (e.Delta < 0 ? 1 : -1);
if (newval >= SCR_Box.Minimum && SCR_Box.Maximum >= newval)
FillPKXBoxes(SCR_Box.Value = newval);
if (newval < SCR_Box.Minimum || SCR_Box.Maximum < newval)
return;
FillPKXBoxes(SCR_Box.Value = newval);
ShowSet.Clear();
}
private void ChangeFormatFilter(object sender, EventArgs e)

View File

@@ -359,8 +359,10 @@ private void B_Search_Click(object sender, EventArgs e)
private void UpdateScroll(object sender, ScrollEventArgs e)
{
if (e.OldValue != e.NewValue)
FillPKXBoxes(e.NewValue);
if (e.OldValue == e.NewValue)
return;
FillPKXBoxes(e.NewValue);
ShowSet.Clear();
}
private void SetResults(List<MysteryGift> res)
@@ -369,7 +371,8 @@ private void SetResults(List<MysteryGift> res)
ShowSet.Clear();
SCR_Box.Maximum = (int)Math.Ceiling((decimal)Results.Count / RES_MIN);
if (SCR_Box.Maximum > 0) SCR_Box.Maximum--;
if (SCR_Box.Maximum > 0)
SCR_Box.Maximum--;
SCR_Box.Value = 0;
FillPKXBoxes(0);
@@ -428,8 +431,10 @@ protected override void OnMouseWheel(MouseEventArgs e)
return;
int oldval = SCR_Box.Value;
int newval = oldval + (e.Delta < 0 ? 1 : -1);
if (newval >= SCR_Box.Minimum && SCR_Box.Maximum >= newval)
FillPKXBoxes(SCR_Box.Value = newval);
if (newval < SCR_Box.Minimum || SCR_Box.Maximum < newval)
return;
FillPKXBoxes(SCR_Box.Value = newval);
ShowSet.Clear();
}
private void ChangeFormatFilter(object sender, EventArgs e)

View File

@@ -37,9 +37,12 @@ public SAV_GroupViewer(SaveFile sav, IPKMView view, IReadOnlyList<SlotGroup> gro
foreach (PictureBox pb in Box.Entries)
{
pb.Click += (o, args) => OmniClick(pb, args);
pb.MouseHover += (o, args) => HoverSlot(pb, args);
pb.ContextMenuStrip = mnu;
pb.MouseMove += (o, args) => Preview.UpdatePreviewPosition(args.Location);
pb.MouseEnter += (o, args) => HoverSlot(pb, args);
pb.MouseLeave += (o, args) => Preview.Clear();
}
Closing += (s, e) => Preview.Clear();
}
private void HoverSlot(object sender, EventArgs e)