PKHeX/PKHeX.WinForms/Controls/Slots/SlotList.cs
Kurt 20905cbe67
Add a search interface for visually filtering all slots (#4712)
* Add slot search to box editor
Alt-Click: Clears the current search.
Shift-Click: Jump to the next box with a result.
2026-02-09 22:03:18 -06:00

211 lines
6.0 KiB
C#

using System;
using System.Collections.Generic;
using System.Windows.Forms;
using PKHeX.Core;
using PKHeX.Drawing.PokeSprite;
namespace PKHeX.WinForms.Controls;
public partial class SlotList : UserControl, ISlotViewer<PictureBox>
{
private static readonly string[] names = GetEnumNames();
public static string[] GetEnumNames()
{
var list = Enum.GetNames<StorageSlotType>();
foreach (ref var item in list.AsSpan())
{
if (item.StartsWith("Fused"))
item = "Fused";
}
return list;
}
public readonly Label[] Labels = new Label[names.Length];
private readonly List<PictureBox> slots = [];
private List<SlotInfoMisc> SlotOffsets = [];
public int SlotCount { get; private set; }
public SaveFile SAV { get; set; } = null!;
public bool FlagIllegal { get; set; }
private Func<PKM, bool>? _searchFilter;
public SlotList()
{
InitializeComponent();
AddLabels();
}
/// <summary>
/// Initializes the extra slot viewers with a list of offsets and sets up event handling.
/// </summary>
/// <param name="list">Extra slots to show</param>
/// <param name="enableDragDropContext">Events to set up</param>
/// <remarks>Uses an object pool for viewers (only generates as needed)</remarks>
public void Initialize(List<SlotInfoMisc> list, Action<Control> enableDragDropContext)
{
SlotOffsets = list;
LoadSlots(list.Count, enableDragDropContext);
}
/// <summary>
/// Hides all slots from the <see cref="SlotList"/>.
/// </summary>
public void HideAllSlots() => LoadSlots(0, _ => { });
public void NotifySlotOld(ISlotInfo previous)
{
if (previous is not SlotInfoMisc m)
return;
var index = SlotOffsets.FindIndex(m.Equals);
if (index < 0)
return;
var pb = slots[index];
pb.BackgroundImage = null;
}
public void NotifySlotChanged(ISlotInfo slot, SlotTouchType type, PKM pk)
{
if (slot is not SlotInfoMisc m)
return;
var index = GetViewIndex(m);
if (index < 0)
return;
var pb = slots[index];
var hideLegality = m is { HideLegality: true };
var flags = GetFlags(pk, hideLegality);
SlotUtil.UpdateSlot(pb, slot, pk, SAV, flags, type);
}
public void ApplyNewFilter(Func<PKM, bool>? filter, bool reload = true)
{
_searchFilter = filter;
if (reload)
ResetSlots();
}
private void ResetSlots()
{
for (int i = 0; i < SlotOffsets.Count; i++)
{
var info = SlotOffsets[i];
var pb = slots[i];
var hideLegality = info is { HideLegality: true };
var flags = GetFlags(info.Read(SAV), hideLegality);
var type = SlotTouchType.None;
SlotUtil.UpdateSlot(pb, info, info.Read(SAV), SAV, flags, type);
}
}
private SlotVisibilityType GetFlags(PKM pk, bool ignoreLegality = false)
{
var result = SlotVisibilityType.None;
if (FlagIllegal && !ignoreLegality)
result |= SlotVisibilityType.CheckLegalityIndicate;
if (_searchFilter != null && !_searchFilter(pk))
result |= SlotVisibilityType.FilterMismatch;
return result;
}
public int GetViewIndex(ISlotInfo info) => SlotOffsets.FindIndex(info.Equals);
public ISlotInfo GetSlotData(PictureBox view)
{
int slot = GetSlot(view);
return GetSlotData(slot);
}
public ISlotInfo GetSlotData(int slot) => SlotOffsets[slot];
public IList<PictureBox> SlotPictureBoxes => slots;
public int GetSlot(PictureBox sender)
{
if (!WinFormsUtil.TryGetUnderlying<PictureBox>(sender, out var view))
return -1;
return slots.IndexOf(view);
}
public int ViewIndex { get; set; } = -1;
private void LoadSlots(int count, Action<Control> enableDragDropContext)
{
var controls = FLP_Slots.Controls;
controls.Clear();
if (count == 0)
{
SlotCount = 0;
return;
}
AddSlots(count, enableDragDropContext);
AddControls(count);
SlotCount = count;
}
private void AddControls(int countTotal)
{
var type = string.Empty;
int added = -1;
for (int i = 0; i < countTotal; i++)
{
var info = SlotOffsets[i];
var label = Labels[(int)info.Type];
if (label.Text != type)
{
added++;
type = label.Text;
FLP_Slots.Controls.Add(label, 0, added++);
}
var slot = slots[i];
FLP_Slots.Controls.Add(slot, 0, added);
}
}
private void AddSlots(int after, Action<Control> enableDragDropContext)
{
int before = SlotCount;
int diff = after - before;
if (diff <= 0)
return;
for (int i = 0; i < diff; i++)
{
var name = $"bpkm{before + i}";
var slot = GetPictureBox(SpriteUtil.Spriter, name);
enableDragDropContext(slot);
slots.Add(slot);
}
}
private const int PadPixels = 2;
private static SelectablePictureBox GetPictureBox(SpriteBuilder s, string name) => new()
{
BorderStyle = BorderStyle.FixedSingle,
Width = s.Width + 2,
Height = s.Height + 2,
AllowDrop = true,
Margin = new Padding(PadPixels),
Padding = Padding.Empty,
SizeMode = PictureBoxSizeMode.CenterImage,
Name = name,
AccessibleName = name,
AccessibleRole = AccessibleRole.Graphic,
};
private void AddLabels()
{
for (var i = 0; i < names.Length; i++)
{
var name = names[i];
Labels[i] = new Label
{
Name = $"L_{name}",
Text = name,
AutoSize = true,
Margin = Padding.Empty,
Padding = Padding.Empty,
};
}
}
}