PKHeX/PKHeX.WinForms/Controls/Slots/SlotList.cs
Kurt 47071b41f3
Refactoring: Span-based value writes and method signatures (#3361)
Existing `get`/`set` logic is flawed in that it doesn't work on Big Endian operating systems, and it allocates heap objects when it doesn't need to.

`System.Buffers.Binary.BinaryPrimitives` in the `System.Memory` NuGet package provides both Little Endian and Big Endian methods to read and write data; all the `get`/`set` operations have been reworked to use this new API. This removes the need for PKHeX's manual `BigEndian` class, as all functions are already covered by the BinaryPrimitives API.

The `StringConverter` has now been rewritten to accept a Span to read from & write to, no longer requiring a temporary StringBuilder.

Other Fixes included:
- The Super Training UI for Gen6 has been reworked according to the latest block structure additions.
- Cloning a Stadium2 Save File now works correctly (opening from the Folder browser list).
- Checksum & Sanity properties removed from parent PKM class, and is now implemented via interface.
2022-01-02 21:35:59 -08:00

179 lines
5.7 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 = Enum.GetNames(typeof(StorageSlotType));
private readonly LabelType[] Labels = new LabelType[names.Length];
private readonly List<PictureBox> slots = new();
private List<SlotInfoMisc> SlotOffsets = new();
public int SlotCount { get; private set; }
public SaveFile SAV { get; set; } = null!;
public bool FlagIllegal { get; set; }
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(z => m.Equals(z));
if (index < 0)
return;
var pb = slots[index];
pb.BackgroundImage = null;
}
public void NotifySlotChanged(ISlotInfo slot, SlotTouchType type, PKM pkm)
{
if (slot is not SlotInfoMisc m)
return;
var index = GetViewIndex(m);
if (index < 0)
return;
var pb = slots[index];
SlotUtil.UpdateSlot(pb, slot, pkm, SAV, FlagIllegal, type);
}
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)
{
var view = WinFormsUtil.GetUnderlyingControl<PictureBox>(sender);
if (view == null)
return -1;
return slots.IndexOf(view);
}
public int GetSlotOffset(int slot) => SlotOffsets[slot].Offset;
public int ViewIndex { get; set; } = -1;
private IEnumerable<PictureBox> LoadSlots(int after, Action<Control> enableDragDropContext)
{
var generated = new List<PictureBox>();
int before = SlotCount;
SlotCount = after;
int diff = after - before;
if (diff > 0)
{
AddSlots(diff);
for (int i = before; i < after; i++)
{
var slot = slots[i];
enableDragDropContext(slot);
FLP_Slots.Controls.Add(slot);
FLP_Slots.SetFlowBreak(slot, true);
generated.Add(slot);
}
}
else
{
for (int i = before - 1; i >= after; i--)
FLP_Slots.Controls.Remove(slots[i]);
}
SetLabelVisibility();
return generated;
}
private void AddSlots(int count)
{
for (int i = 0; i < count; i++)
slots.Add(GetPictureBox(i, SpriteUtil.Spriter));
}
private const int PadPixels = 2;
private static PictureBox GetPictureBox(int index, SpriteBuilder s) => new()
{
BorderStyle = BorderStyle.FixedSingle,
Width = s.Width + 2,
Height = s.Height + 2,
AllowDrop = true,
Margin = new Padding(PadPixels),
SizeMode = PictureBoxSizeMode.CenterImage,
Name = $"bpkm{index}",
};
private sealed class LabelType : Label
{
public StorageSlotType Type;
}
private void AddLabels()
{
for (var i = 0; i < names.Length; i++)
{
var name = names[i];
bool result = Enum.TryParse<StorageSlotType>(name, out var value);
if (!result)
continue;
var label = new LabelType
{
Name = $"L_{name}",
Text = name,
Type = value,
AutoSize = true,
Visible = false,
};
Labels[i] = label;
FLP_Slots.Controls.Add(label);
FLP_Slots.SetFlowBreak(label, true);
}
}
private void SetLabelVisibility()
{
foreach (var l in Labels)
{
int index = SlotOffsets.FindIndex(z => z.Type == l.Type);
if (index < 0)
{
l.Visible = false;
continue;
}
int pos = FLP_Slots.Controls.IndexOf(slots[index]);
if (pos > FLP_Slots.Controls.IndexOf(l))
pos--;
FLP_Slots.Controls.SetChildIndex(l, pos);
l.Visible = true;
}
}
}
}