PKHeX/PKHeX.Avalonia/ViewModels/Subforms/TrainerStatViewModel.cs
montanon 8b3da88a13 Convert all 37 remaining VMs to clone+CopyChangesFrom pattern
All subform ViewModels now use transactional cancel semantics
matching WinForms: edits happen on a clone, Cancel discards them,
Save copies the clone back to the origin via CopyChangesFrom.

Gen 1-2: SAVEventReset1, SAVHallOfFame1, SAVMisc2, SAVRTC2
Gen 3: SAVRoamer3, SAVSecretBase3, SAVHallOfFame3, SAVRTC3,
  SAVMisc3, PokeBlock3CaseEditor, SimplePokedex
Gen 4: PokeGear4, Trainer4BR, BattlePass4, Gear4, Apricorn4,
  HoneyTree4, Underground4, Geonet4, PoffinCase4, Pokedex4
Gen 5: Pokedex5, UnityTower5, DLC5
Gen 6: SAVPokepuff6
Gen 7: SAVHallOfFame7
Gen 8 BDSP: Poffin8b, SealStickers8b, Underground8b
Cross-gen: EventFlags, EventFlags2, EventWork, Inventory,
  MailBox, Wondercard, BoxLayout, TrainerStat
2026-03-18 15:32:37 -03:00

104 lines
2.9 KiB
C#

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using PKHeX.Core;
namespace PKHeX.Avalonia.ViewModels.Subforms;
/// <summary>
/// Model for a single trainer stat record.
/// </summary>
public partial class TrainerStatModel : ObservableObject
{
public int Index { get; }
public string Label { get; }
public string OffsetText { get; }
public int MaxValue { get; }
[ObservableProperty]
private int _value;
public TrainerStatModel(int index, string label, int value, int offset, int max)
{
Index = index;
Label = label;
_value = value;
OffsetText = $"0x{offset:X3}";
MaxValue = max;
}
}
/// <summary>
/// ViewModel for the Trainer Stat Records subform.
/// Displays and edits trainer statistics/records.
/// </summary>
public partial class TrainerStatViewModel : SaveEditorViewModelBase
{
private readonly SaveFile _origin;
private readonly SaveFile _clone;
private readonly ITrainerStatRecord _record;
[ObservableProperty]
private string _searchText = string.Empty;
public ObservableCollection<TrainerStatModel> AllRecords { get; } = [];
[ObservableProperty]
private ObservableCollection<TrainerStatModel> _filteredRecords = [];
public string WindowTitle { get; }
public TrainerStatViewModel(SaveFile sav, ITrainerStatRecord record, Dictionary<int, string> recordNames) : base(sav)
{
_origin = sav;
_clone = sav.Clone();
_record = (ITrainerStatRecord)_clone;
WindowTitle = $"Trainer Stats ({sav.Version})";
for (int i = 0; i < record.RecordCount; i++)
{
if (!recordNames.TryGetValue(i, out var name))
name = $"{i:D3}";
var value = record.GetRecord(i);
var offset = record.GetRecordOffset(i);
var max = record.GetRecordMax(i);
AllRecords.Add(new TrainerStatModel(i, name, value, offset, max));
}
FilteredRecords = new ObservableCollection<TrainerStatModel>(AllRecords);
}
partial void OnSearchTextChanged(string value)
{
ApplyFilter();
}
private void ApplyFilter()
{
if (string.IsNullOrWhiteSpace(SearchText))
{
FilteredRecords = new ObservableCollection<TrainerStatModel>(AllRecords);
return;
}
FilteredRecords = new ObservableCollection<TrainerStatModel>(
AllRecords.Where(r => r.Label.Contains(SearchText, System.StringComparison.OrdinalIgnoreCase)));
}
[RelayCommand]
private void Save()
{
foreach (var rec in AllRecords)
{
var clamped = System.Math.Clamp(rec.Value, 0, rec.MaxValue);
_record.SetRecord(rec.Index, clamped);
}
_origin.CopyChangesFrom(_clone);
Modified = true;
}
}