mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-10 06:34:19 +00:00
Allow adding/hiding extra properties in Report grid
#3933 3. top level settings for Report 1. was already implemented with the file namer settings on dump not sure how I want to do dragdrop-multi, but I did recently add a record type for `ConcatenatedEntitySet`...
This commit is contained in:
parent
e0172b601c
commit
dcb23c7981
7 changed files with 196 additions and 86 deletions
30
PKHeX.Core/Editing/Bulk/IPropertyProvider.cs
Normal file
30
PKHeX.Core/Editing/Bulk/IPropertyProvider.cs
Normal file
|
@ -0,0 +1,30 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
public interface IPropertyProvider
|
||||
{
|
||||
bool TryGetProperty(PKM pk, string prop, [NotNullWhen(true)] out string? result);
|
||||
}
|
||||
|
||||
public sealed class DefaultPropertyProvider : IPropertyProvider
|
||||
{
|
||||
public static readonly DefaultPropertyProvider Instance = new();
|
||||
|
||||
public bool TryGetProperty(PKM pk, string prop, [NotNullWhen(true)] out string? result)
|
||||
{
|
||||
result = null;
|
||||
if (!BatchEditing.TryGetHasProperty(pk, prop, out var pi))
|
||||
return false;
|
||||
try
|
||||
{
|
||||
var value = pi.GetValue(pk);
|
||||
result = value?.ToString();
|
||||
return result != null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,105 +14,105 @@ public class EntitySummary : IFatefulEncounterReadOnly // do NOT seal, allow inh
|
|||
|
||||
private readonly GameStrings Strings;
|
||||
private readonly ushort[] Stats;
|
||||
protected readonly PKM pk; // protected for children generating extra properties
|
||||
public readonly PKM Entity; // protected for children generating extra properties
|
||||
|
||||
public virtual string Position => "???";
|
||||
public string Nickname => pk.Nickname;
|
||||
public string Species => Get(Strings.specieslist, pk.Species);
|
||||
public string Nature => Get(Strings.natures, (byte)pk.StatNature);
|
||||
public string Gender => Get(GenderSymbols, pk.Gender);
|
||||
public string ESV => pk.PSV.ToString("0000");
|
||||
public string HP_Type => Get(Strings.types, pk.HPType + 1);
|
||||
public string Ability => Get(Strings.abilitylist, pk.Ability);
|
||||
public string Move1 => Get(Strings.movelist, pk.Move1);
|
||||
public string Move2 => Get(Strings.movelist, pk.Move2);
|
||||
public string Move3 => Get(Strings.movelist, pk.Move3);
|
||||
public string Move4 => Get(Strings.movelist, pk.Move4);
|
||||
public string HeldItem => GetSpan(Strings.GetItemStrings(pk.Context), pk.HeldItem);
|
||||
public string Nickname => Entity.Nickname;
|
||||
public string Species => Get(Strings.specieslist, Entity.Species);
|
||||
public string Nature => Get(Strings.natures, (byte)Entity.StatNature);
|
||||
public string Gender => Get(GenderSymbols, Entity.Gender);
|
||||
public string ESV => Entity.PSV.ToString("0000");
|
||||
public string HP_Type => Get(Strings.types, Entity.HPType + 1);
|
||||
public string Ability => Get(Strings.abilitylist, Entity.Ability);
|
||||
public string Move1 => Get(Strings.movelist, Entity.Move1);
|
||||
public string Move2 => Get(Strings.movelist, Entity.Move2);
|
||||
public string Move3 => Get(Strings.movelist, Entity.Move3);
|
||||
public string Move4 => Get(Strings.movelist, Entity.Move4);
|
||||
public string HeldItem => GetSpan(Strings.GetItemStrings(Entity.Context), Entity.HeldItem);
|
||||
public string HP => Stats[0].ToString();
|
||||
public string ATK => Stats[1].ToString();
|
||||
public string DEF => Stats[2].ToString();
|
||||
public string SPA => Stats[4].ToString();
|
||||
public string SPD => Stats[5].ToString();
|
||||
public string SPE => Stats[3].ToString();
|
||||
public string MetLoc => pk.GetLocationString(eggmet: false);
|
||||
public string EggLoc => pk.GetLocationString(eggmet: true);
|
||||
public string Ball => Get(Strings.balllist, pk.Ball);
|
||||
public string OT => pk.OriginalTrainerName;
|
||||
public string Version => Get(Strings.gamelist, (int)pk.Version);
|
||||
public string OTLang => ((LanguageID)pk.Language).ToString();
|
||||
public string Legal { get { var la = new LegalityAnalysis(pk); return la.Parsed ? la.Valid.ToString() : "-"; } }
|
||||
public string MetLoc => Entity.GetLocationString(eggmet: false);
|
||||
public string EggLoc => Entity.GetLocationString(eggmet: true);
|
||||
public string Ball => Get(Strings.balllist, Entity.Ball);
|
||||
public string OT => Entity.OriginalTrainerName;
|
||||
public string Version => Get(Strings.gamelist, (int)Entity.Version);
|
||||
public string OTLang => ((LanguageID)Entity.Language).ToString();
|
||||
public string Legal { get { var la = new LegalityAnalysis(Entity); return la.Parsed ? la.Valid.ToString() : "-"; } }
|
||||
|
||||
#region Extraneous
|
||||
public string EC => pk.EncryptionConstant.ToString("X8");
|
||||
public string PID => pk.PID.ToString("X8");
|
||||
public int IV_HP => pk.IV_HP;
|
||||
public int IV_ATK => pk.IV_ATK;
|
||||
public int IV_DEF => pk.IV_DEF;
|
||||
public int IV_SPA => pk.IV_SPA;
|
||||
public int IV_SPD => pk.IV_SPD;
|
||||
public int IV_SPE => pk.IV_SPE;
|
||||
public uint EXP => pk.EXP;
|
||||
public int Level => pk.CurrentLevel;
|
||||
public int EV_HP => pk.EV_HP;
|
||||
public int EV_ATK => pk.EV_ATK;
|
||||
public int EV_DEF => pk.EV_DEF;
|
||||
public int EV_SPA => pk.EV_SPA;
|
||||
public int EV_SPD => pk.EV_SPD;
|
||||
public int EV_SPE => pk.EV_SPE;
|
||||
public int Cool => pk is IContestStatsReadOnly s ? s.ContestCool : 0;
|
||||
public int Beauty => pk is IContestStatsReadOnly s ? s.ContestBeauty : 0;
|
||||
public int Cute => pk is IContestStatsReadOnly s ? s.ContestCute : 0;
|
||||
public int Smart => pk is IContestStatsReadOnly s ? s.ContestSmart : 0;
|
||||
public int Tough => pk is IContestStatsReadOnly s ? s.ContestTough : 0;
|
||||
public int Sheen => pk is IContestStatsReadOnly s ? s.ContestSheen : 0;
|
||||
public string EC => Entity.EncryptionConstant.ToString("X8");
|
||||
public string PID => Entity.PID.ToString("X8");
|
||||
public int IV_HP => Entity.IV_HP;
|
||||
public int IV_ATK => Entity.IV_ATK;
|
||||
public int IV_DEF => Entity.IV_DEF;
|
||||
public int IV_SPA => Entity.IV_SPA;
|
||||
public int IV_SPD => Entity.IV_SPD;
|
||||
public int IV_SPE => Entity.IV_SPE;
|
||||
public uint EXP => Entity.EXP;
|
||||
public int Level => Entity.CurrentLevel;
|
||||
public int EV_HP => Entity.EV_HP;
|
||||
public int EV_ATK => Entity.EV_ATK;
|
||||
public int EV_DEF => Entity.EV_DEF;
|
||||
public int EV_SPA => Entity.EV_SPA;
|
||||
public int EV_SPD => Entity.EV_SPD;
|
||||
public int EV_SPE => Entity.EV_SPE;
|
||||
public int Cool => Entity is IContestStatsReadOnly s ? s.ContestCool : 0;
|
||||
public int Beauty => Entity is IContestStatsReadOnly s ? s.ContestBeauty : 0;
|
||||
public int Cute => Entity is IContestStatsReadOnly s ? s.ContestCute : 0;
|
||||
public int Smart => Entity is IContestStatsReadOnly s ? s.ContestSmart : 0;
|
||||
public int Tough => Entity is IContestStatsReadOnly s ? s.ContestTough : 0;
|
||||
public int Sheen => Entity is IContestStatsReadOnly s ? s.ContestSheen : 0;
|
||||
|
||||
public string NotOT => pk.Format > 5 ? pk.HandlingTrainerName : "N/A";
|
||||
public string NotOT => Entity.Format > 5 ? Entity.HandlingTrainerName : "N/A";
|
||||
|
||||
public int AbilityNum => pk.Format > 5 ? pk.AbilityNumber : -1;
|
||||
public byte GenderFlag => pk.Gender;
|
||||
public byte Form => pk.Form;
|
||||
public int PokerusStrain => pk.PokerusStrain;
|
||||
public int PokerusDays => pk.PokerusDays;
|
||||
public int MetLevel => pk.MetLevel;
|
||||
public byte OriginalTrainerGender => pk.OriginalTrainerGender;
|
||||
public int AbilityNum => Entity.Format > 5 ? Entity.AbilityNumber : -1;
|
||||
public byte GenderFlag => Entity.Gender;
|
||||
public byte Form => Entity.Form;
|
||||
public int PokerusStrain => Entity.PokerusStrain;
|
||||
public int PokerusDays => Entity.PokerusDays;
|
||||
public int MetLevel => Entity.MetLevel;
|
||||
public byte OriginalTrainerGender => Entity.OriginalTrainerGender;
|
||||
|
||||
public bool FatefulEncounter => pk.FatefulEncounter;
|
||||
public bool IsEgg => pk.IsEgg;
|
||||
public bool IsNicknamed => pk.IsNicknamed;
|
||||
public bool IsShiny => pk.IsShiny;
|
||||
public bool FatefulEncounter => Entity.FatefulEncounter;
|
||||
public bool IsEgg => Entity.IsEgg;
|
||||
public bool IsNicknamed => Entity.IsNicknamed;
|
||||
public bool IsShiny => Entity.IsShiny;
|
||||
|
||||
public ushort TID16 => pk.TID16;
|
||||
public ushort SID16 => pk.SID16;
|
||||
public uint TSV => pk.TSV;
|
||||
public int Move1_PP => pk.Move1_PP;
|
||||
public int Move2_PP => pk.Move2_PP;
|
||||
public int Move3_PP => pk.Move3_PP;
|
||||
public int Move4_PP => pk.Move4_PP;
|
||||
public int Move1_PPUp => pk.Move1_PPUps;
|
||||
public int Move2_PPUp => pk.Move2_PPUps;
|
||||
public int Move3_PPUp => pk.Move3_PPUps;
|
||||
public int Move4_PPUp => pk.Move4_PPUps;
|
||||
public string Relearn1 => Get(Strings.movelist, pk.RelearnMove1);
|
||||
public string Relearn2 => Get(Strings.movelist, pk.RelearnMove2);
|
||||
public string Relearn3 => Get(Strings.movelist, pk.RelearnMove3);
|
||||
public string Relearn4 => Get(Strings.movelist, pk.RelearnMove4);
|
||||
public ushort Checksum => pk is ISanityChecksum s ? s.Checksum : Checksums.CRC16_CCITT(pk.Data.AsSpan(pk.SIZE_STORED));
|
||||
public int Friendship => pk.OriginalTrainerFriendship;
|
||||
public int EggYear => pk.EggMetDate.GetValueOrDefault().Year;
|
||||
public int EggMonth => pk.EggMetDate.GetValueOrDefault().Month;
|
||||
public int EggDay => pk.EggMetDate.GetValueOrDefault().Day;
|
||||
public int MetYear => pk.MetDate.GetValueOrDefault().Year;
|
||||
public int MetMonth => pk.MetDate.GetValueOrDefault().Month;
|
||||
public int MetDay => pk.MetDate.GetValueOrDefault().Day;
|
||||
public ushort TID16 => Entity.TID16;
|
||||
public ushort SID16 => Entity.SID16;
|
||||
public uint TSV => Entity.TSV;
|
||||
public int Move1_PP => Entity.Move1_PP;
|
||||
public int Move2_PP => Entity.Move2_PP;
|
||||
public int Move3_PP => Entity.Move3_PP;
|
||||
public int Move4_PP => Entity.Move4_PP;
|
||||
public int Move1_PPUp => Entity.Move1_PPUps;
|
||||
public int Move2_PPUp => Entity.Move2_PPUps;
|
||||
public int Move3_PPUp => Entity.Move3_PPUps;
|
||||
public int Move4_PPUp => Entity.Move4_PPUps;
|
||||
public string Relearn1 => Get(Strings.movelist, Entity.RelearnMove1);
|
||||
public string Relearn2 => Get(Strings.movelist, Entity.RelearnMove2);
|
||||
public string Relearn3 => Get(Strings.movelist, Entity.RelearnMove3);
|
||||
public string Relearn4 => Get(Strings.movelist, Entity.RelearnMove4);
|
||||
public ushort Checksum => Entity is ISanityChecksum s ? s.Checksum : Checksums.CRC16_CCITT(Entity.Data.AsSpan(Entity.SIZE_STORED));
|
||||
public int Friendship => Entity.OriginalTrainerFriendship;
|
||||
public int EggYear => Entity.EggMetDate.GetValueOrDefault().Year;
|
||||
public int EggMonth => Entity.EggMetDate.GetValueOrDefault().Month;
|
||||
public int EggDay => Entity.EggMetDate.GetValueOrDefault().Day;
|
||||
public int MetYear => Entity.MetDate.GetValueOrDefault().Year;
|
||||
public int MetMonth => Entity.MetDate.GetValueOrDefault().Month;
|
||||
public int MetDay => Entity.MetDate.GetValueOrDefault().Day;
|
||||
|
||||
#endregion
|
||||
|
||||
protected EntitySummary(PKM p, GameStrings strings)
|
||||
protected EntitySummary(PKM pk, GameStrings strings)
|
||||
{
|
||||
pk = p;
|
||||
Entity = pk;
|
||||
Strings = strings;
|
||||
Stats = pk.GetStats(pk.PersonalInfo);
|
||||
Stats = Entity.GetStats(Entity.PersonalInfo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -6,6 +6,7 @@ using System.Globalization;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Media;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
@ -321,7 +322,11 @@ public partial class Main : Form
|
|||
report.Show();
|
||||
var list = new List<SlotCache>();
|
||||
SlotInfoLoader.AddFromSaveFile(C_SAV.SAV, list);
|
||||
report.PopulateData(list);
|
||||
|
||||
var settings = Settings.Report;
|
||||
var extra = CollectionsMarshal.AsSpan(settings.ExtraProperties);
|
||||
var hide = CollectionsMarshal.AsSpan(settings.HiddenProperties);
|
||||
report.PopulateData(list, extra, hide);
|
||||
}
|
||||
|
||||
private void MainMenuDatabase(object sender, EventArgs e)
|
||||
|
|
|
@ -43,6 +43,7 @@ public sealed class PKHeXSettings
|
|||
public EntityDatabaseSettings EntityDb { get; set; } = new();
|
||||
public EncounterDatabaseSettings EncounterDb { get; set; } = new();
|
||||
public MysteryGiftDatabaseSettings MysteryDb { get; set; } = new();
|
||||
public ReportGridSettings Report { get; set; } = new();
|
||||
|
||||
[Browsable(false)]
|
||||
public SlotExportSettings SlotExport { get; set; } = new();
|
||||
|
@ -297,6 +298,15 @@ public sealed class MysteryGiftDatabaseSettings
|
|||
public bool FilterUnavailableSpecies { get; set; } = true;
|
||||
}
|
||||
|
||||
public sealed class ReportGridSettings
|
||||
{
|
||||
[LocalizedDescription("Extra entity properties to try and show in addition to the default properties displayed.")]
|
||||
public List<string> ExtraProperties { get; set; } = [];
|
||||
|
||||
[LocalizedDescription("Properties to hide from the report grid.")]
|
||||
public List<string> HiddenProperties { get; set; } = [];
|
||||
}
|
||||
|
||||
public sealed class HoverSettings
|
||||
{
|
||||
[LocalizedDescription("Show PKM Slot Preview on Hover")]
|
||||
|
|
|
@ -9,8 +9,8 @@ namespace PKHeX.WinForms;
|
|||
/// Bind-able summary object that can fetch sprite and strings that summarize a <see cref="PKM"/>.
|
||||
/// </summary>
|
||||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
|
||||
public sealed class EntitySummaryImage(PKM p, GameStrings strings, string Position) : EntitySummary(p, strings)
|
||||
public sealed class EntitySummaryImage(PKM pk, GameStrings strings, string Position) : EntitySummary(pk, strings)
|
||||
{
|
||||
public Image Sprite => pk.Sprite();
|
||||
public Image Sprite => Entity.Sprite();
|
||||
public override string Position { get; } = Position;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
@ -51,12 +53,14 @@ public partial class ReportGrid : Form
|
|||
|
||||
private sealed class PokemonList<T> : SortableBindingList<T> where T : class;
|
||||
|
||||
public void PopulateData(IList<SlotCache> Data)
|
||||
public void PopulateData(IReadOnlyList<SlotCache> data) => PopulateData(data, [], []);
|
||||
|
||||
public void PopulateData(IReadOnlyList<SlotCache> data, ReadOnlySpan<string> extra, ReadOnlySpan<string> hide)
|
||||
{
|
||||
SuspendLayout();
|
||||
var PL = new PokemonList<EntitySummaryImage>();
|
||||
var strings = GameInfo.Strings;
|
||||
foreach (var entry in Data)
|
||||
foreach (var entry in data)
|
||||
{
|
||||
var pk = entry.Entity;
|
||||
if (pk.Species - 1u >= pk.MaxSpeciesID)
|
||||
|
@ -69,6 +73,12 @@ public partial class ReportGrid : Form
|
|||
|
||||
dgData.DataSource = PL;
|
||||
dgData.AutoGenerateColumns = true;
|
||||
|
||||
if (hide.Length != 0)
|
||||
HideSpecifiedColumns(hide);
|
||||
if (extra.Length != 0)
|
||||
AddExtraColumns(PL, extra);
|
||||
|
||||
for (int i = 0; i < dgData.Columns.Count; i++)
|
||||
{
|
||||
var col = dgData.Columns[i];
|
||||
|
@ -91,6 +101,57 @@ public partial class ReportGrid : Form
|
|||
ResumeLayout();
|
||||
}
|
||||
|
||||
private void HideSpecifiedColumns(ReadOnlySpan<string> hide)
|
||||
{
|
||||
foreach (var prop in hide)
|
||||
{
|
||||
if (prop.Length == 0)
|
||||
continue;
|
||||
var col = dgData.Columns[prop];
|
||||
if (col != null)
|
||||
col.Visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void AddExtraColumns(PokemonList<EntitySummaryImage> data, ReadOnlySpan<string> extra)
|
||||
{
|
||||
var rent = ArrayPool<string>.Shared.Rent(data.Count);
|
||||
var span = rent.AsSpan(0, data.Count);
|
||||
foreach (var prop in extra)
|
||||
{
|
||||
if (prop.Length == 0)
|
||||
continue;
|
||||
span.Clear();
|
||||
bool any = false;
|
||||
for (int i = 0; i < data.Count; i++)
|
||||
{
|
||||
var pk = data[i].Entity;
|
||||
if (!TryGetCustomCell(pk, prop, out var str))
|
||||
continue;
|
||||
span[i] = str;
|
||||
any = true;
|
||||
}
|
||||
|
||||
if (!any)
|
||||
continue;
|
||||
|
||||
var col = new DataGridViewTextBoxColumn { Name = prop, HeaderText = prop };
|
||||
var c = dgData.Columns.Add(col);
|
||||
for (int i = 0; i < data.Count; i++)
|
||||
dgData.Rows[i].Cells[c].Value = span[i];
|
||||
}
|
||||
ArrayPool<string>.Shared.Return(rent, true);
|
||||
}
|
||||
|
||||
public IPropertyProvider PropertyProvider { get; init; } = DefaultPropertyProvider.Instance;
|
||||
|
||||
private bool TryGetCustomCell(PKM pk, string prop, [NotNullWhen(true)] out string? result)
|
||||
{
|
||||
if (PropertyProvider.TryGetProperty(pk, prop, out result))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private void Data_Sorted(object sender, EventArgs e)
|
||||
{
|
||||
int height = SpriteUtil.Spriter.Height + 1; // max height of a row, +1px
|
||||
|
|
|
@ -5,6 +5,7 @@ using System.Diagnostics;
|
|||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using PKHeX.Core;
|
||||
|
@ -344,7 +345,10 @@ public partial class SAV_Database : Form
|
|||
|
||||
ReportGrid reportGrid = new();
|
||||
reportGrid.Show();
|
||||
reportGrid.PopulateData(Results);
|
||||
var settings = Main.Settings.Report;
|
||||
var extra = CollectionsMarshal.AsSpan(settings.ExtraProperties);
|
||||
var hide = CollectionsMarshal.AsSpan(settings.HiddenProperties);
|
||||
reportGrid.PopulateData(Results, extra, hide);
|
||||
}
|
||||
|
||||
private sealed class SearchFolderDetail(string path, bool ignoreBackupFiles)
|
||||
|
|
Loading…
Reference in a new issue