Minor tweaks

Add Get/Set Relearn method to PKM.cs
Alpha Mastered move now indicates incomplete text entry (like moves/relearn)
Split up legal move indication helper class, use DI to allow other implementations of DataSource to be returned (pkhex mobile?).
Remove unused gender refresh method (see previous added UserControl commit)
Add helper method to center control within control
This commit is contained in:
Kurt 2022-05-02 18:11:31 -07:00
parent fa76da56c9
commit 41578132cf
8 changed files with 178 additions and 87 deletions

View file

@ -0,0 +1,14 @@
using System.Collections.Generic;
namespace PKHeX.Core;
public interface ILegalMoveDisplaySource<T>
{
void ReloadMoves(LegalMoveInfo info);
void ReloadMoves(IReadOnlyList<T> moves);
bool GetIsMoveBoxOrdered(int index);
void SetIsMoveBoxOrdered(int index, bool value);
/// <summary> Creates a shallow copy of the array reference for use in binding. </summary>
IReadOnlyList<T> DataSource { get; }
}

View file

@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace PKHeX.Core;
public sealed class LegalMoveComboSource : ILegalMoveDisplaySource<ComboItem>
{
private readonly bool[] IsMoveBoxOrdered = new bool[4];
private ComboItem[] MoveDataAllowed = Array.Empty<ComboItem>();
public IReadOnlyList<ComboItem> DataSource => (ComboItem[])MoveDataAllowed.Clone();
/// <summary>
/// Resets the <see cref="MoveDataAllowed"/> data source with an updated collection.
/// </summary>
public void ReloadMoves(IReadOnlyList<ComboItem> moves)
{
MoveDataAllowed = moves.ToArray();
ClearUpdateCheck();
}
public bool GetIsMoveBoxOrdered(int index) => IsMoveBoxOrdered[index];
public void SetIsMoveBoxOrdered(int index, bool value) => IsMoveBoxOrdered[index] = value;
public void ReloadMoves(LegalMoveInfo info)
{
ClearUpdateCheck();
SortMoves(info);
}
private void SortMoves(LegalMoveInfo info) => Array.Sort(MoveDataAllowed, (i1, i2) => Compare(i1, i2, info.CanLearn));
// defer re-population until dropdown is opened; handled by dropdown event
private void ClearUpdateCheck() => Array.Clear(IsMoveBoxOrdered, 0, IsMoveBoxOrdered.Length);
private static int Compare(ComboItem i1, ComboItem i2, Func<int, bool> check)
{
// split into 2 groups: Allowed & Not, and sort each sublist
var (strA, value1) = i1;
var (strB, value2) = i2;
var c1 = check(value1);
var c2 = check(value2);
if (c1)
return c2 ? string.CompareOrdinal(strA, strB) : -1;
return c2 ? 1 : string.CompareOrdinal(strA, strB);
}
}

View file

@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace PKHeX.Core;
public sealed class LegalMoveInfo
{
private int LearnCount;
private readonly bool[] AllowedMoves = new bool[(int)Move.MAX_COUNT];
/// <summary>
/// Checks if the requested <see cref="move"/> is legally able to be learned.
/// </summary>
/// <param name="move">Move to check if can be learned</param>
/// <returns>True if can learn the move</returns>
public bool CanLearn(int move) => AllowedMoves[move];
/// <summary>
/// Reloads the legality sources to permit the provided legal <see cref="moves"/>.
/// </summary>
/// <param name="moves">Legal moves to allow</param>
public bool ReloadMoves(IReadOnlyList<int> moves)
{
// check prior move-pool to not needlessly refresh the data set
if (moves.Count == LearnCount && moves.All(CanLearn))
return false;
SetMoves(moves);
return true;
}
private void SetMoves(IReadOnlyList<int> moves)
{
var arr = AllowedMoves;
Array.Clear(arr, 0, arr.Length);
foreach (var index in moves)
arr[index] = true;
LearnCount = moves.Count;
}
}

View file

@ -1,64 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Collections.Generic;
namespace PKHeX.Core
namespace PKHeX.Core;
/// <summary>
/// Legal Move information for a single <see cref="PKM"/>, for indicating if a move is legal or not.
/// </summary>
public sealed class LegalMoveSource<T>
{
/// <summary>
/// Legal Move information for a single <see cref="PKM"/>, for indicating if a move is legal or not.
/// </summary>
public sealed class LegalMoveSource
public LegalMoveInfo Info { get; } = new();
public readonly ILegalMoveDisplaySource<T> Display;
public LegalMoveSource(ILegalMoveDisplaySource<T> display) => Display = display;
public void ReloadMoves(IReadOnlyList<int> moves)
{
public readonly bool[] IsMoveBoxOrdered = new bool[4];
if (!Info.ReloadMoves(moves))
return;
Display.ReloadMoves(Info);
}
/// <summary> Creates a shallow copy of the array reference for use in binding. </summary>
public IReadOnlyList<ComboItem> DataSource => (ComboItem[])MoveDataAllowed.Clone();
/// <summary>
/// Checks if the requested <see cref="move"/> is legally able to be learned.
/// </summary>
/// <param name="move">Move to check if can be learned</param>
/// <returns>True if can learn the move</returns>
public bool CanLearn(int move) => AllowedMoves.Contains(move);
private readonly HashSet<int> AllowedMoves = new();
private ComboItem[] MoveDataAllowed = Array.Empty<ComboItem>();
/// <summary>
/// Reloads the legality sources to permit the provided legal <see cref="moves"/>.
/// </summary>
/// <param name="moves">Legal moves to allow</param>
public void ReloadMoves(IReadOnlyList<int> moves)
{
// check prior move-pool to not needlessly refresh the data set
if (AllowedMoves.Count == moves.Count && AllowedMoves.SetEquals(moves))
return;
AllowedMoves.Clear();
foreach (var m in moves)
AllowedMoves.Add(m);
Array.Sort(MoveDataAllowed, Compare);
// MoveDataAllowed = MoveDataAllowed.OrderByDescending(m => AllowedMoves.Contains(m.Value)).ToArray();
// defer re-population until dropdown is opened; handled by dropdown event
Array.Clear(IsMoveBoxOrdered, 0, IsMoveBoxOrdered.Length);
}
private int Compare(ComboItem i1, ComboItem i2)
{
// split into 2 groups: Allowed & Not, and sort each sublist
var (strA, value1) = i1;
var (strB, value2) = i2;
var c1 = AllowedMoves.Contains(value1);
var c2 = AllowedMoves.Contains(value2);
if (c1)
return c2 ? string.CompareOrdinal(strA, strB) : -1;
return c2 ? 1 : string.CompareOrdinal(strA, strB);
}
public void ReloadMoves(IReadOnlyList<ComboItem> moves)
{
MoveDataAllowed = moves.ToArray();
}
public void ChangeMoveSource(IReadOnlyList<T> moves)
{
Display.ReloadMoves(moves);
}
}

View file

@ -1081,6 +1081,24 @@ namespace PKHeX.Core
_ => throw new IndexOutOfRangeException(nameof(index)),
};
public int GetRelearnMove(int index) => index switch
{
0 => RelearnMove1,
1 => RelearnMove2,
2 => RelearnMove3,
3 => RelearnMove4,
_ => throw new IndexOutOfRangeException(nameof(index)),
};
public int SetRelearnMove(int index, int value) => index switch
{
0 => RelearnMove1 = value,
1 => RelearnMove2 = value,
2 => RelearnMove3 = value,
3 => RelearnMove4 = value,
_ => throw new IndexOutOfRangeException(nameof(index)),
};
/// <summary>
/// Clears moves that a <see cref="PKM"/> may have, possibly from a future generation.
/// </summary>

View file

@ -1931,6 +1931,7 @@
this.CB_AlphaMastered.Location = new System.Drawing.Point(123, 317);
this.CB_AlphaMastered.Name = "CB_AlphaMastered";
this.CB_AlphaMastered.Size = new System.Drawing.Size(124, 21);
this.CB_AlphaMastered.SelectedIndexChanged += new System.EventHandler(this.ValidateMove);
this.CB_AlphaMastered.TabIndex = 20;
//
// FLP_MoveFlags

View file

@ -170,7 +170,7 @@ namespace PKHeX.WinForms.Controls
/// <summary>
/// List of legal moves for the latest <see cref="Legality"/>.
/// </summary>
private readonly LegalMoveSource LegalMoveSource = new();
private readonly LegalMoveSource<ComboItem> LegalMoveSource = new(new LegalMoveComboSource());
/// <summary>
/// Gender Symbols for showing Genders
@ -391,8 +391,6 @@ namespace PKHeX.WinForms.Controls
return genders[index];
}
private static void ReloadGender(Label l, IReadOnlyList<string> genders) => l.Text = ReloadGender(l.Text, genders);
internal void UpdateSprite()
{
if (FieldsLoaded && !forceValidation)
@ -1310,14 +1308,12 @@ namespace PKHeX.WinForms.Controls
}
}
int lang = WinFormsUtil.GetIndex(CB_Language);
if (CHK_Nicknamed.Checked)
return;
// Fetch Current Species and set it as Nickname Text
int species = WinFormsUtil.GetIndex(CB_Species);
if (species < 1 || species > Entity.MaxSpeciesID)
if ((uint)(species - 1) >= Entity.MaxSpeciesID)
{ TB_Nickname.Text = string.Empty; return; }
if (CHK_IsEgg.Checked)
@ -1327,6 +1323,7 @@ namespace PKHeX.WinForms.Controls
if (sender != CB_Language && species != 0 && !SpeciesName.IsNicknamedAnyLanguage(species, TB_Nickname.Text, Entity.Format))
return;
int lang = WinFormsUtil.GetIndex(CB_Language);
TB_Nickname.Text = SpeciesName.GetSpeciesNameGeneration(species, lang, Entity.Format);
if (Entity is GBPKM pk)
pk.SetNotNicknamed();
@ -1663,28 +1660,32 @@ namespace PKHeX.WinForms.Controls
private void ValidateMove(object sender, EventArgs e)
{
if (!FieldsLoaded)
if (!FieldsLoaded || sender is not ComboBox cb)
return;
var cb = (ComboBox) sender;
ValidateComboBox(cb);
if (Moves.Contains(cb)) // Move
UpdatePP(sender, e);
// Legality -- set all moves back (why all?)
Entity.Move1 = WinFormsUtil.GetIndex(CB_Move1);
Entity.Move2 = WinFormsUtil.GetIndex(CB_Move2);
Entity.Move3 = WinFormsUtil.GetIndex(CB_Move3);
Entity.Move4 = WinFormsUtil.GetIndex(CB_Move4);
if (Entity.Format >= 6)
// Store value back, repopulate legality.
int value = WinFormsUtil.GetIndex(cb);
int index = Array.IndexOf(Moves, cb);
if (index != -1)
{
Entity.RelearnMove1 = WinFormsUtil.GetIndex(CB_RelearnMove1);
Entity.RelearnMove2 = WinFormsUtil.GetIndex(CB_RelearnMove2);
Entity.RelearnMove3 = WinFormsUtil.GetIndex(CB_RelearnMove3);
Entity.RelearnMove4 = WinFormsUtil.GetIndex(CB_RelearnMove4);
UpdatePP(sender, e);
Entity.SetMove(index, value);
}
else if ((index = Array.IndexOf(Relearn, cb)) != -1)
{
Entity.SetRelearnMove(index, value);
}
else if (cb == CB_AlphaMastered && Entity is PA8 pa8)
{
pa8.AlphaMove = (ushort)value;
}
else
{
// Shouldn't hit here.
throw new ArgumentException(nameof(sender));
}
if (Entity is PA8 pa8)
pa8.AlphaMove = (ushort)WinFormsUtil.GetIndex(CB_AlphaMastered);
UpdateLegality(skipMoveRepop: true);
}
@ -1694,7 +1695,7 @@ namespace PKHeX.WinForms.Controls
return;
var (text, value) = (ComboItem)((ComboBox)sender).Items[e.Index];
var valid = LegalMoveSource.CanLearn(value) && !HaX;
var valid = LegalMoveSource.Info.CanLearn(value) && !HaX;
var current = (e.State & DrawItemState.Selected) != 0;
var brush = Draw.Brushes.GetBackground(valid, current);
@ -1721,16 +1722,16 @@ namespace PKHeX.WinForms.Controls
// Populating the combobox drop-down list is deferred until the dropdown is entered into at least once.
// Saves some lag delays when viewing a pkm.
if (LegalMoveSource.IsMoveBoxOrdered[index])
if (LegalMoveSource.Display.GetIsMoveBoxOrdered(index))
return;
SetMoveDataSource(s);
LegalMoveSource.IsMoveBoxOrdered[index] = true;
LegalMoveSource.Display.SetIsMoveBoxOrdered(index, true);
}
private void SetMoveDataSource(ComboBox c)
{
var index = WinFormsUtil.GetIndex(c);
c.DataSource = new BindingSource(LegalMoveSource.DataSource, null);
c.DataSource = new BindingSource(LegalMoveSource.Display.DataSource, null);
c.SelectedValue = index;
}
@ -1943,8 +1944,9 @@ namespace PKHeX.WinForms.Controls
private void CenterSubEditors()
{
// Recenter PKM SubEditors
FLP_PKMEditors.Location = new Point((tabMain.TabPages[0].Width - FLP_PKMEditors.Width) / 2, FLP_PKMEditors.Location.Y);
FLP_MoveFlags.Location = new Point((tabMain.TabPages[0].Width - FLP_MoveFlags.Width) / 2, FLP_MoveFlags.Location.Y);
var firstTabArea = tabMain.TabPages[0]; // first is always initialized
FLP_PKMEditors.CenterWithin(firstTabArea);
FLP_MoveFlags.CenterWithin(firstTabArea);
}
public void EnableDragDrop(DragEventHandler enter, DragEventHandler drop)
@ -2070,7 +2072,7 @@ namespace PKHeX.WinForms.Controls
SetIfDifferentCount(source.Species, CB_Species, force);
// Set the Move ComboBoxes too..
LegalMoveSource.ReloadMoves(source.Moves);
LegalMoveSource.ChangeMoveSource(source.Moves);
foreach (var cb in Moves.Concat(Relearn))
SetIfDifferentCount(source.Moves, cb, force);
if (sav is SAV8LA)

View file

@ -31,6 +31,11 @@ namespace PKHeX.WinForms
child.Location = new Point(Math.Max(x, 0), Math.Max(y, 0));
}
internal static void CenterWithin(this Control child, Control parent)
{
child.Location = new Point((parent.Width - child.Width) / 2, child.Location.Y);
}
/// <summary>
/// Horizontally centers the <see cref="child"/> to the <see cref="parent"/>'s horizontal center.
/// </summary>