Fix visual parity: sprites, tabs, layout, wallpapers

Major visual overhaul to match WinForms appearance:

Sprite rendering:
- Fix ResourceLoader key extraction: normalize hyphens to underscores
  to match WinForms .resx naming convention (b_100-1 → b_100_1)
- Same fix applied to both PokeSprite and Misc ResourceLoaders

PKM Editor layout:
- Tabs moved to LEFT side with compact 72x40px buttons
- Colored left-edge pip per tab (orange/blue/pink/green/yellow/brown)
- Added missing fields: PID (hex), EXP, Friendship, Language
- Ability changed from NumericUpDown to ComboBox
- EXP + Level displayed side-by-side
- Stats tab with Base/IV/EV/Total columns
- Dragout sprite area above tabs

SAV Editor:
- Box wallpaper background rendered behind slot grid
- Box navigation uses ComboBox for direct box selection
- SAV tab has actual tool buttons (Items, Layout, Wondercards, etc.)

Slot control:
- Hover effect: blue border highlight with smooth 150ms transition
- Empty slots have transparent borders
- Theme-aware colors for light/dark mode

Dark mode:
- Added PKHeX-specific theme dictionaries (Light/Dark)
- Box background, hover, tool button colors per theme
This commit is contained in:
montanon 2026-03-15 12:25:35 -03:00
parent 8814526cd3
commit 96baf3c439
9 changed files with 364 additions and 118 deletions

View File

@ -5,5 +5,36 @@
<Application.Styles>
<FluentTheme />
<StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml" />
<!-- PKMEditor tab styling: compact left-side tabs with colored pip -->
<Style Selector="TabControl.pkm-tabs TabItem">
<Setter Property="MinHeight" Value="40"/>
<Setter Property="MaxWidth" Value="72"/>
<Setter Property="FontSize" Value="11"/>
<Setter Property="Padding" Value="4,8"/>
</Style>
</Application.Styles>
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<!-- Light theme overrides -->
<ResourceDictionary x:Key="Light">
<SolidColorBrush x:Key="PKHeXBoxBackground" Color="#E8E8E8" />
<SolidColorBrush x:Key="PKHeXSlotHoverBorder" Color="#4090FF" />
<SolidColorBrush x:Key="PKHeXSlotHoverBackground" Color="#18000000" />
<SolidColorBrush x:Key="PKHeXToolButtonBackground" Color="#EEEEEE" />
<SolidColorBrush x:Key="PKHeXPanelBackground" Color="#F5F5F5" />
</ResourceDictionary>
<!-- Dark theme overrides -->
<ResourceDictionary x:Key="Dark">
<SolidColorBrush x:Key="PKHeXBoxBackground" Color="#2A2A2A" />
<SolidColorBrush x:Key="PKHeXSlotHoverBorder" Color="#5AA0FF" />
<SolidColorBrush x:Key="PKHeXSlotHoverBackground" Color="#18FFFFFF" />
<SolidColorBrush x:Key="PKHeXToolButtonBackground" Color="#383838" />
<SolidColorBrush x:Key="PKHeXPanelBackground" Color="#1E1E1E" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>

View File

@ -67,6 +67,11 @@ public MainWindowViewModel(IDialogService dialogService)
{
SlotSelected = pk => PkmEditor.PopulateFields(pk),
GetEditorPKM = () => PkmEditor.PreparePKM(),
OpenInventoryCommand = OpenInventoryCommand,
OpenBoxLayoutCommand = OpenBoxLayoutCommand,
OpenWondercardCommand = OpenWondercardCommand,
OpenEventFlagsCommand = OpenEventFlagsCommand,
OpenSettingsEditorCommand = OpenSettingsEditorCommand,
};
LoadPlugins();
}

View File

@ -35,6 +35,12 @@ public partial class PKMEditorViewModel : ObservableObject
[ObservableProperty] private bool _isShiny;
[ObservableProperty] private bool _isEgg;
// New fields
[ObservableProperty] private string _pidHex = "00000000";
[ObservableProperty] private uint _exp;
[ObservableProperty] private int _friendship;
[ObservableProperty] private int _language;
// Stats
[ObservableProperty] private int _hp;
[ObservableProperty] private int _atk;
@ -43,6 +49,14 @@ public partial class PKMEditorViewModel : ObservableObject
[ObservableProperty] private int _spD;
[ObservableProperty] private int _spe;
// Base Stats (read-only display)
[ObservableProperty] private int _base_HP;
[ObservableProperty] private int _base_ATK;
[ObservableProperty] private int _base_DEF;
[ObservableProperty] private int _base_SPA;
[ObservableProperty] private int _base_SPD;
[ObservableProperty] private int _base_SPE;
// IVs
[ObservableProperty] private int _iv_HP;
[ObservableProperty] private int _iv_ATK;
@ -78,6 +92,8 @@ public partial class PKMEditorViewModel : ObservableObject
public IReadOnlyList<ComboItem> NatureList => GameInfo.FilteredSources.Natures;
public IReadOnlyList<ComboItem> HeldItemList => GameInfo.FilteredSources.Items;
public IReadOnlyList<ComboItem> MoveList => GameInfo.FilteredSources.Moves;
public IReadOnlyList<ComboItem> AbilityList => GameInfo.FilteredSources.Abilities;
public IReadOnlyList<ComboItem> LanguageList => GameInfo.FilteredSources.Languages;
// Selected ComboItem bindings
[ObservableProperty] private ComboItem? _selectedSpecies;
@ -87,6 +103,8 @@ public partial class PKMEditorViewModel : ObservableObject
[ObservableProperty] private ComboItem? _selectedMove2;
[ObservableProperty] private ComboItem? _selectedMove3;
[ObservableProperty] private ComboItem? _selectedMove4;
[ObservableProperty] private ComboItem? _selectedAbility;
[ObservableProperty] private ComboItem? _selectedLanguage;
partial void OnSelectedSpeciesChanged(ComboItem? value)
{
@ -130,6 +148,18 @@ public partial class PKMEditorViewModel : ObservableObject
Move4 = (ushort)value.Value;
}
partial void OnSelectedAbilityChanged(ComboItem? value)
{
if (value is not null)
Ability = value.Value;
}
partial void OnSelectedLanguageChanged(ComboItem? value)
{
if (value is not null)
Language = value.Value;
}
public void Initialize(SaveFile sav)
{
_sav = sav;
@ -151,6 +181,12 @@ public void PopulateFields(PKM pk)
IsShiny = pk.IsShiny;
IsEgg = pk.IsEgg;
// New fields
PidHex = pk.PID.ToString("X8");
Exp = pk.EXP;
Friendship = pk.CurrentFriendship;
Language = pk.Language;
Hp = pk.Stat_HPCurrent;
Atk = pk.Stat_ATK;
Def = pk.Stat_DEF;
@ -158,6 +194,15 @@ public void PopulateFields(PKM pk)
SpD = pk.Stat_SPD;
Spe = pk.Stat_SPE;
// Base stats
var pi = pk.PersonalInfo;
Base_HP = pi.HP;
Base_ATK = pi.ATK;
Base_DEF = pi.DEF;
Base_SPA = pi.SPA;
Base_SPD = pi.SPD;
Base_SPE = pi.SPE;
Iv_HP = pk.IV_HP;
Iv_ATK = pk.IV_ATK;
Iv_DEF = pk.IV_DEF;
@ -189,6 +234,8 @@ public void PopulateFields(PKM pk)
SelectedMove2 = MoveList.FirstOrDefault(x => x.Value == pk.Move2);
SelectedMove3 = MoveList.FirstOrDefault(x => x.Value == pk.Move3);
SelectedMove4 = MoveList.FirstOrDefault(x => x.Value == pk.Move4);
SelectedAbility = AbilityList.FirstOrDefault(x => x.Value == pk.Ability);
SelectedLanguage = LanguageList.FirstOrDefault(x => x.Value == pk.Language);
UpdateSprite();
}
@ -210,6 +257,13 @@ public void PopulateFields(PKM pk)
Entity.Ability = Ability;
Entity.HeldItem = HeldItem;
// New fields
if (uint.TryParse(PidHex, System.Globalization.NumberStyles.HexNumber, null, out var pid))
Entity.PID = pid;
Entity.EXP = Exp;
Entity.CurrentFriendship = (byte)Math.Clamp(Friendship, 0, 255);
Entity.Language = Language;
Entity.IV_HP = Iv_HP;
Entity.IV_ATK = Iv_ATK;
Entity.IV_DEF = Iv_DEF;

View File

@ -1,9 +1,13 @@
using System;
using System.Collections.ObjectModel;
using System.Windows.Input;
using Avalonia.Media.Imaging;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using PKHeX.Avalonia.Controls;
using PKHeX.Avalonia.Converters;
using PKHeX.Core;
using PKHeX.Drawing.Misc.Avalonia;
using PKHeX.Drawing.PokeSprite.Avalonia;
namespace PKHeX.Avalonia.ViewModels;
@ -30,12 +34,23 @@ public partial class SAVEditorViewModel : ObservableObject
[ObservableProperty]
private bool _isLoaded;
[ObservableProperty]
private Bitmap? _boxWallpaper;
public ObservableCollection<SlotModel> BoxSlots { get; } = [];
public ObservableCollection<SlotModel> PartySlots { get; } = [];
public ObservableCollection<string> BoxNames { get; } = [];
/// <summary>Manages drag-and-drop operations between slots.</summary>
public SlotChangeManager SlotManager { get; }
// SAV tool command delegates — wired from MainWindowViewModel
public ICommand? OpenInventoryCommand { get; set; }
public ICommand? OpenBoxLayoutCommand { get; set; }
public ICommand? OpenWondercardCommand { get; set; }
public ICommand? OpenEventFlagsCommand { get; set; }
public ICommand? OpenSettingsEditorCommand { get; set; }
public SAVEditorViewModel()
{
SlotManager = new SlotChangeManager(this);
@ -55,17 +70,37 @@ public void LoadSaveFile(SaveFile sav)
BoxCount = sav.BoxCount;
CurrentBox = 0;
IsLoaded = true;
RefreshBoxNames();
RefreshBox();
RefreshParty();
}
private void RefreshBoxNames()
{
BoxNames.Clear();
if (_sav is null)
return;
for (int i = 0; i < _sav.BoxCount; i++)
{
var name = _sav is IBoxDetailNameRead n ? n.GetBoxName(i) : $"Box {i + 1}";
BoxNames.Add(name);
}
}
partial void OnCurrentBoxChanged(int value)
{
if (_sav is null)
return;
RefreshBox();
}
[RelayCommand]
private void NextBox()
{
if (_sav is null)
return;
CurrentBox = (CurrentBox + 1) % BoxCount;
RefreshBox();
}
[RelayCommand]
@ -74,7 +109,6 @@ private void PreviousBox()
if (_sav is null)
return;
CurrentBox = (CurrentBox - 1 + BoxCount) % BoxCount;
RefreshBox();
}
private void RefreshBox()
@ -84,6 +118,17 @@ private void RefreshBox()
BoxName = _sav is IBoxDetailNameRead n ? n.GetBoxName(CurrentBox) : $"Box {CurrentBox + 1}";
// Update wallpaper
try
{
var wpBitmap = _sav.WallpaperImage(CurrentBox);
BoxWallpaper = SKBitmapToAvaloniaBitmapConverter.ToAvaloniaBitmap(wpBitmap);
}
catch
{
BoxWallpaper = null;
}
int slotCount = Math.Min(30, _sav.BoxSlotCount);
for (int i = 0; i < slotCount && i < BoxSlots.Count; i++)
{

View File

@ -6,88 +6,117 @@
x:DataType="vm:PKMEditorViewModel">
<DockPanel>
<!-- Top row: Legality indicator + Dragout sprite -->
<Grid DockPanel.Dock="Top" Height="58" Margin="2,2,2,0">
<!-- Legality indicator (24x24) at left -->
<Image Name="PB_Legal"
Width="24" Height="24"
HorizontalAlignment="Left" VerticalAlignment="Top"
Margin="0,0,0,0"
RenderOptions.BitmapInterpolationMode="None" />
<!-- Top row: Ball space + Dragout sprite -->
<Grid DockPanel.Dock="Top" Height="60" Margin="2,2,2,0">
<!-- Ball icon placeholder (24x24) -->
<Border Width="24" Height="24"
HorizontalAlignment="Left" VerticalAlignment="Center"
Margin="4,0,0,0" />
<!-- Dragout sprite (72x56) -->
<Image Name="PB_Sprite"
Source="{Binding SpriteImage}"
Width="72" Height="56"
HorizontalAlignment="Left" VerticalAlignment="Top"
Margin="24,0,0,0"
HorizontalAlignment="Left" VerticalAlignment="Center"
Margin="32,0,0,0"
RenderOptions.BitmapInterpolationMode="None" />
</Grid>
<!-- TabControl with RIGHT-side tabs -->
<TabControl TabStripPlacement="Right" Padding="0" Margin="0">
<!-- TabControl with LEFT-side tabs -->
<TabControl Classes="pkm-tabs" TabStripPlacement="Left" Padding="0" Margin="0">
<!-- Main Tab -->
<TabItem Header="Main" MinHeight="0">
<TabItem MinHeight="0">
<TabItem.Header>
<StackPanel Orientation="Horizontal" Spacing="0">
<Border Width="4" Height="28" CornerRadius="2" Background="#F89860" Margin="0,0,4,0" />
<TextBlock Text="Main" VerticalAlignment="Center" />
</StackPanel>
</TabItem.Header>
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<StackPanel Spacing="3" Margin="4,4,4,2">
<Grid ColumnDefinitions="104,144" RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto" RowSpacing="3">
<!-- Species -->
<TextBlock Grid.Row="0" Grid.Column="0" Text="Species:" VerticalAlignment="Center" FontSize="12" />
<ComboBox Grid.Row="0" Grid.Column="1"
<StackPanel Spacing="1" Margin="4,4,4,2">
<Grid ColumnDefinitions="104,*" RowDefinitions="24,24,24,24,24,24,24,24,24,24,24" RowSpacing="1">
<!-- Row 0: PID -->
<TextBlock Grid.Row="0" Grid.Column="0" Text="PID:" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0,0,6,0" FontSize="12" />
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding PidHex}" Width="80" HorizontalAlignment="Left" Height="24" FontSize="12" FontFamily="Consolas,Courier New,monospace" Padding="4,2" />
<!-- Row 1: Species -->
<TextBlock Grid.Row="1" Grid.Column="0" Text="Species:" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0,0,6,0" FontSize="12" />
<ComboBox Grid.Row="1" Grid.Column="1"
ItemsSource="{Binding SpeciesList}"
SelectedItem="{Binding SelectedSpecies}"
DisplayMemberBinding="{Binding Text, DataType=core:ComboItem}"
HorizontalAlignment="Stretch" Height="24" FontSize="12" />
<!-- Nickname -->
<TextBlock Grid.Row="1" Grid.Column="0" Text="Nickname:" VerticalAlignment="Center" FontSize="12" />
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Nickname}" Height="24" FontSize="12" Padding="4,2" />
<!-- Row 2: Nickname -->
<TextBlock Grid.Row="2" Grid.Column="0" Text="Nickname:" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0,0,6,0" FontSize="12" />
<TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Nickname}" Height="24" FontSize="12" Padding="4,2" />
<!-- Level -->
<TextBlock Grid.Row="2" Grid.Column="0" Text="Level:" VerticalAlignment="Center" FontSize="12" />
<NumericUpDown Grid.Row="2" Grid.Column="1" Value="{Binding Level}" Minimum="1" Maximum="100" Height="24" FontSize="12" />
<!-- Row 3: EXP + Level side by side -->
<TextBlock Grid.Row="3" Grid.Column="0" Text="EXP:" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0,0,6,0" FontSize="12" />
<StackPanel Grid.Row="3" Grid.Column="1" Orientation="Horizontal" Spacing="4">
<TextBox Text="{Binding Exp}" Width="72" Height="24" FontSize="12" Padding="4,2" />
<TextBlock Text="Level:" VerticalAlignment="Center" FontSize="11" Margin="2,0,2,0" />
<NumericUpDown Value="{Binding Level}" Minimum="1" Maximum="100" Width="64" Height="24" FontSize="11" />
</StackPanel>
<!-- Nature -->
<TextBlock Grid.Row="3" Grid.Column="0" Text="Nature:" VerticalAlignment="Center" FontSize="12" />
<ComboBox Grid.Row="3" Grid.Column="1"
<!-- Row 4: Nature -->
<TextBlock Grid.Row="4" Grid.Column="0" Text="Nature:" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0,0,6,0" FontSize="12" />
<ComboBox Grid.Row="4" Grid.Column="1"
ItemsSource="{Binding NatureList}"
SelectedItem="{Binding SelectedNature}"
DisplayMemberBinding="{Binding Text, DataType=core:ComboItem}"
HorizontalAlignment="Stretch" Height="24" FontSize="12" />
<!-- Form -->
<TextBlock Grid.Row="4" Grid.Column="0" Text="Form:" VerticalAlignment="Center" FontSize="12" />
<NumericUpDown Grid.Row="4" Grid.Column="1" Value="{Binding Form}" Minimum="0" Height="24" FontSize="12" />
<!-- Row 5: Form -->
<TextBlock Grid.Row="5" Grid.Column="0" Text="Form:" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0,0,6,0" FontSize="12" />
<NumericUpDown Grid.Row="5" Grid.Column="1" Value="{Binding Form}" Minimum="0" Width="64" HorizontalAlignment="Left" Height="24" FontSize="12" />
<!-- Held Item -->
<TextBlock Grid.Row="5" Grid.Column="0" Text="Held Item:" VerticalAlignment="Center" FontSize="12" />
<ComboBox Grid.Row="5" Grid.Column="1"
<!-- Row 6: Held Item -->
<TextBlock Grid.Row="6" Grid.Column="0" Text="Held Item:" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0,0,6,0" FontSize="12" />
<ComboBox Grid.Row="6" Grid.Column="1"
ItemsSource="{Binding HeldItemList}"
SelectedItem="{Binding SelectedHeldItem}"
DisplayMemberBinding="{Binding Text, DataType=core:ComboItem}"
HorizontalAlignment="Stretch" Height="24" FontSize="12" />
<!-- Ability (displays numeric value; ViewModel has int _ability) -->
<TextBlock Grid.Row="6" Grid.Column="0" Text="Ability:" VerticalAlignment="Center" FontSize="12" />
<NumericUpDown Grid.Row="6" Grid.Column="1" Value="{Binding Ability}" Minimum="0" Height="24" FontSize="12" />
<!-- Row 7: Ability (ComboBox) -->
<TextBlock Grid.Row="7" Grid.Column="0" Text="Ability:" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0,0,6,0" FontSize="12" />
<ComboBox Grid.Row="7" Grid.Column="1"
ItemsSource="{Binding AbilityList}"
SelectedItem="{Binding SelectedAbility}"
DisplayMemberBinding="{Binding Text, DataType=core:ComboItem}"
HorizontalAlignment="Stretch" Height="24" FontSize="12" />
<!-- Shiny -->
<TextBlock Grid.Row="7" Grid.Column="0" Text="Shiny:" VerticalAlignment="Center" FontSize="12" />
<CheckBox Grid.Row="7" Grid.Column="1" IsChecked="{Binding IsShiny}" VerticalAlignment="Center" />
<!-- Row 8: Friendship -->
<TextBlock Grid.Row="8" Grid.Column="0" Text="Friendship:" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0,0,6,0" FontSize="12" />
<NumericUpDown Grid.Row="8" Grid.Column="1" Value="{Binding Friendship}" Minimum="0" Maximum="255" Width="64" HorizontalAlignment="Left" Height="24" FontSize="12" />
<!-- Is Egg -->
<TextBlock Grid.Row="8" Grid.Column="0" Text="Is Egg:" VerticalAlignment="Center" FontSize="12" />
<CheckBox Grid.Row="8" Grid.Column="1" IsChecked="{Binding IsEgg}" VerticalAlignment="Center" />
<!-- Row 9: Language -->
<TextBlock Grid.Row="9" Grid.Column="0" Text="Language:" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0,0,6,0" FontSize="12" />
<ComboBox Grid.Row="9" Grid.Column="1"
ItemsSource="{Binding LanguageList}"
SelectedItem="{Binding SelectedLanguage}"
DisplayMemberBinding="{Binding Text, DataType=core:ComboItem}"
HorizontalAlignment="Stretch" Height="24" FontSize="12" />
<!-- Gender -->
<TextBlock Grid.Row="9" Grid.Column="0" Text="Gender:" VerticalAlignment="Center" FontSize="12" />
<NumericUpDown Grid.Row="9" Grid.Column="1" Value="{Binding Gender}" Minimum="0" Maximum="2" Height="24" FontSize="12" />
<!-- Row 10: IsEgg + Shiny on same row -->
<TextBlock Grid.Row="10" Grid.Column="0" Text="" VerticalAlignment="Center" FontSize="12" />
<StackPanel Grid.Row="10" Grid.Column="1" Orientation="Horizontal" Spacing="12">
<CheckBox IsChecked="{Binding IsEgg}" Content="Is Egg" VerticalAlignment="Center" FontSize="11" Padding="4,0" />
<CheckBox IsChecked="{Binding IsShiny}" Content="Shiny" VerticalAlignment="Center" FontSize="11" Padding="4,0" />
</StackPanel>
</Grid>
</StackPanel>
</ScrollViewer>
</TabItem>
<!-- Met Tab -->
<TabItem Header="Met" MinHeight="0">
<TabItem MinHeight="0">
<TabItem.Header>
<StackPanel Orientation="Horizontal" Spacing="0">
<Border Width="4" Height="28" CornerRadius="2" Background="#8098F8" Margin="0,0,4,0" />
<TextBlock Text="Met" VerticalAlignment="Center" />
</StackPanel>
</TabItem.Header>
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<StackPanel Spacing="4" Margin="4">
<TextBlock Text="Met Conditions" FontWeight="SemiBold" FontSize="12" />
@ -97,63 +126,83 @@
</TabItem>
<!-- Stats Tab -->
<TabItem Header="Stats" MinHeight="0">
<TabItem MinHeight="0">
<TabItem.Header>
<StackPanel Orientation="Horizontal" Spacing="0">
<Border Width="4" Height="28" CornerRadius="2" Background="#F8A8D0" Margin="0,0,4,0" />
<TextBlock Text="Stats" VerticalAlignment="Center" />
</StackPanel>
</TabItem.Header>
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<StackPanel Spacing="2" Margin="4,4,4,2">
<StackPanel Spacing="1" Margin="4,4,4,2">
<!-- Header row -->
<Grid ColumnDefinitions="40,55,55,55" Margin="0,0,0,2">
<TextBlock Grid.Column="1" Text="IV" FontWeight="Bold" HorizontalAlignment="Center" FontSize="11" />
<TextBlock Grid.Column="2" Text="EV" FontWeight="Bold" HorizontalAlignment="Center" FontSize="11" />
<TextBlock Grid.Column="3" Text="Total" FontWeight="Bold" HorizontalAlignment="Center" FontSize="11" />
<Grid ColumnDefinitions="36,44,50,50,50" Margin="0,0,0,2">
<TextBlock Grid.Column="0" Text="Stat" FontWeight="Bold" HorizontalAlignment="Center" FontSize="10" />
<TextBlock Grid.Column="1" Text="Base" FontWeight="Bold" HorizontalAlignment="Center" FontSize="10" />
<TextBlock Grid.Column="2" Text="IV" FontWeight="Bold" HorizontalAlignment="Center" FontSize="10" />
<TextBlock Grid.Column="3" Text="EV" FontWeight="Bold" HorizontalAlignment="Center" FontSize="10" />
<TextBlock Grid.Column="4" Text="Total" FontWeight="Bold" HorizontalAlignment="Center" FontSize="10" />
</Grid>
<!-- HP -->
<Grid ColumnDefinitions="40,55,55,55">
<TextBlock Grid.Column="0" Text="HP" VerticalAlignment="Center" FontSize="11" />
<NumericUpDown Grid.Column="1" Value="{Binding Iv_HP}" Minimum="0" Maximum="31" Height="22" FontSize="11" />
<NumericUpDown Grid.Column="2" Value="{Binding Ev_HP}" Minimum="0" Maximum="252" Height="22" FontSize="11" />
<TextBlock Grid.Column="3" Text="{Binding Hp}" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="11" />
<Grid ColumnDefinitions="36,44,50,50,50" Height="22">
<TextBlock Grid.Column="0" Text="HP" VerticalAlignment="Center" FontSize="10" HorizontalAlignment="Center" />
<TextBlock Grid.Column="1" Text="{Binding Base_HP}" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="10" />
<NumericUpDown Grid.Column="2" Value="{Binding Iv_HP}" Minimum="0" Maximum="31" Height="20" FontSize="10" ShowButtonSpinner="False" />
<NumericUpDown Grid.Column="3" Value="{Binding Ev_HP}" Minimum="0" Maximum="252" Height="20" FontSize="10" ShowButtonSpinner="False" />
<TextBlock Grid.Column="4" Text="{Binding Hp}" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="10" />
</Grid>
<!-- ATK -->
<Grid ColumnDefinitions="40,55,55,55">
<TextBlock Grid.Column="0" Text="Atk" VerticalAlignment="Center" FontSize="11" />
<NumericUpDown Grid.Column="1" Value="{Binding Iv_ATK}" Minimum="0" Maximum="31" Height="22" FontSize="11" />
<NumericUpDown Grid.Column="2" Value="{Binding Ev_ATK}" Minimum="0" Maximum="252" Height="22" FontSize="11" />
<TextBlock Grid.Column="3" Text="{Binding Atk}" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="11" />
<Grid ColumnDefinitions="36,44,50,50,50" Height="22">
<TextBlock Grid.Column="0" Text="Atk" VerticalAlignment="Center" FontSize="10" HorizontalAlignment="Center" />
<TextBlock Grid.Column="1" Text="{Binding Base_ATK}" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="10" />
<NumericUpDown Grid.Column="2" Value="{Binding Iv_ATK}" Minimum="0" Maximum="31" Height="20" FontSize="10" ShowButtonSpinner="False" />
<NumericUpDown Grid.Column="3" Value="{Binding Ev_ATK}" Minimum="0" Maximum="252" Height="20" FontSize="10" ShowButtonSpinner="False" />
<TextBlock Grid.Column="4" Text="{Binding Atk}" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="10" />
</Grid>
<!-- DEF -->
<Grid ColumnDefinitions="40,55,55,55">
<TextBlock Grid.Column="0" Text="Def" VerticalAlignment="Center" FontSize="11" />
<NumericUpDown Grid.Column="1" Value="{Binding Iv_DEF}" Minimum="0" Maximum="31" Height="22" FontSize="11" />
<NumericUpDown Grid.Column="2" Value="{Binding Ev_DEF}" Minimum="0" Maximum="252" Height="22" FontSize="11" />
<TextBlock Grid.Column="3" Text="{Binding Def}" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="11" />
<Grid ColumnDefinitions="36,44,50,50,50" Height="22">
<TextBlock Grid.Column="0" Text="Def" VerticalAlignment="Center" FontSize="10" HorizontalAlignment="Center" />
<TextBlock Grid.Column="1" Text="{Binding Base_DEF}" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="10" />
<NumericUpDown Grid.Column="2" Value="{Binding Iv_DEF}" Minimum="0" Maximum="31" Height="20" FontSize="10" ShowButtonSpinner="False" />
<NumericUpDown Grid.Column="3" Value="{Binding Ev_DEF}" Minimum="0" Maximum="252" Height="20" FontSize="10" ShowButtonSpinner="False" />
<TextBlock Grid.Column="4" Text="{Binding Def}" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="10" />
</Grid>
<!-- SpA -->
<Grid ColumnDefinitions="40,55,55,55">
<TextBlock Grid.Column="0" Text="SpA" VerticalAlignment="Center" FontSize="11" />
<NumericUpDown Grid.Column="1" Value="{Binding Iv_SPA}" Minimum="0" Maximum="31" Height="22" FontSize="11" />
<NumericUpDown Grid.Column="2" Value="{Binding Ev_SPA}" Minimum="0" Maximum="252" Height="22" FontSize="11" />
<TextBlock Grid.Column="3" Text="{Binding SpA}" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="11" />
<Grid ColumnDefinitions="36,44,50,50,50" Height="22">
<TextBlock Grid.Column="0" Text="SpA" VerticalAlignment="Center" FontSize="10" HorizontalAlignment="Center" />
<TextBlock Grid.Column="1" Text="{Binding Base_SPA}" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="10" />
<NumericUpDown Grid.Column="2" Value="{Binding Iv_SPA}" Minimum="0" Maximum="31" Height="20" FontSize="10" ShowButtonSpinner="False" />
<NumericUpDown Grid.Column="3" Value="{Binding Ev_SPA}" Minimum="0" Maximum="252" Height="20" FontSize="10" ShowButtonSpinner="False" />
<TextBlock Grid.Column="4" Text="{Binding SpA}" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="10" />
</Grid>
<!-- SpD -->
<Grid ColumnDefinitions="40,55,55,55">
<TextBlock Grid.Column="0" Text="SpD" VerticalAlignment="Center" FontSize="11" />
<NumericUpDown Grid.Column="1" Value="{Binding Iv_SPD}" Minimum="0" Maximum="31" Height="22" FontSize="11" />
<NumericUpDown Grid.Column="2" Value="{Binding Ev_SPD}" Minimum="0" Maximum="252" Height="22" FontSize="11" />
<TextBlock Grid.Column="3" Text="{Binding SpD}" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="11" />
<Grid ColumnDefinitions="36,44,50,50,50" Height="22">
<TextBlock Grid.Column="0" Text="SpD" VerticalAlignment="Center" FontSize="10" HorizontalAlignment="Center" />
<TextBlock Grid.Column="1" Text="{Binding Base_SPD}" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="10" />
<NumericUpDown Grid.Column="2" Value="{Binding Iv_SPD}" Minimum="0" Maximum="31" Height="20" FontSize="10" ShowButtonSpinner="False" />
<NumericUpDown Grid.Column="3" Value="{Binding Ev_SPD}" Minimum="0" Maximum="252" Height="20" FontSize="10" ShowButtonSpinner="False" />
<TextBlock Grid.Column="4" Text="{Binding SpD}" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="10" />
</Grid>
<!-- Spe -->
<Grid ColumnDefinitions="40,55,55,55">
<TextBlock Grid.Column="0" Text="Spe" VerticalAlignment="Center" FontSize="11" />
<NumericUpDown Grid.Column="1" Value="{Binding Iv_SPE}" Minimum="0" Maximum="31" Height="22" FontSize="11" />
<NumericUpDown Grid.Column="2" Value="{Binding Ev_SPE}" Minimum="0" Maximum="252" Height="22" FontSize="11" />
<TextBlock Grid.Column="3" Text="{Binding Spe}" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="11" />
<Grid ColumnDefinitions="36,44,50,50,50" Height="22">
<TextBlock Grid.Column="0" Text="Spe" VerticalAlignment="Center" FontSize="10" HorizontalAlignment="Center" />
<TextBlock Grid.Column="1" Text="{Binding Base_SPE}" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="10" />
<NumericUpDown Grid.Column="2" Value="{Binding Iv_SPE}" Minimum="0" Maximum="31" Height="20" FontSize="10" ShowButtonSpinner="False" />
<NumericUpDown Grid.Column="3" Value="{Binding Ev_SPE}" Minimum="0" Maximum="252" Height="20" FontSize="10" ShowButtonSpinner="False" />
<TextBlock Grid.Column="4" Text="{Binding Spe}" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="10" />
</Grid>
</StackPanel>
</ScrollViewer>
</TabItem>
<!-- Moves Tab -->
<TabItem Header="Moves" MinHeight="0">
<TabItem MinHeight="0">
<TabItem.Header>
<StackPanel Orientation="Horizontal" Spacing="0">
<Border Width="4" Height="28" CornerRadius="2" Background="#70E070" Margin="0,0,4,0" />
<TextBlock Text="Moves" VerticalAlignment="Center" />
</StackPanel>
</TabItem.Header>
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<StackPanel Spacing="3" Margin="4,4,4,2">
<Grid ColumnDefinitions="52,*" RowDefinitions="Auto,Auto,Auto,Auto" RowSpacing="3">
@ -190,7 +239,13 @@
</TabItem>
<!-- Cosmetic Tab (placeholder) -->
<TabItem Header="Cosm" MinHeight="0">
<TabItem MinHeight="0">
<TabItem.Header>
<StackPanel Orientation="Horizontal" Spacing="0">
<Border Width="4" Height="28" CornerRadius="2" Background="#F8F038" Margin="0,0,4,0" />
<TextBlock Text="Cosm" VerticalAlignment="Center" />
</StackPanel>
</TabItem.Header>
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<StackPanel Spacing="4" Margin="4">
<TextBlock Text="Cosmetic" FontWeight="SemiBold" FontSize="12" />
@ -200,17 +255,23 @@
</TabItem>
<!-- OT/Misc Tab -->
<TabItem Header="OT/Misc" MinHeight="0">
<TabItem MinHeight="0">
<TabItem.Header>
<StackPanel Orientation="Horizontal" Spacing="0">
<Border Width="4" Height="28" CornerRadius="2" Background="#BC8F8F" Margin="0,0,4,0" />
<TextBlock Text="OT/Misc" VerticalAlignment="Center" />
</StackPanel>
</TabItem.Header>
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<StackPanel Spacing="3" Margin="4,4,4,2">
<Grid ColumnDefinitions="104,144" RowDefinitions="Auto,Auto,Auto" RowSpacing="3">
<TextBlock Grid.Row="0" Grid.Column="0" Text="OT Name:" VerticalAlignment="Center" FontSize="12" />
<TextBlock Grid.Row="0" Grid.Column="0" Text="OT Name:" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0,0,6,0" FontSize="12" />
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Ot}" Height="24" FontSize="12" Padding="4,2" />
<TextBlock Grid.Row="1" Grid.Column="0" Text="TID:" VerticalAlignment="Center" FontSize="12" />
<TextBlock Grid.Row="1" Grid.Column="0" Text="TID:" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0,0,6,0" FontSize="12" />
<NumericUpDown Grid.Row="1" Grid.Column="1" Value="{Binding Tid}" Minimum="0" Maximum="65535" Height="24" FontSize="12" />
<TextBlock Grid.Row="2" Grid.Column="0" Text="SID:" VerticalAlignment="Center" FontSize="12" />
<TextBlock Grid.Row="2" Grid.Column="0" Text="SID:" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0,0,6,0" FontSize="12" />
<NumericUpDown Grid.Row="2" Grid.Column="1" Value="{Binding Sid}" Minimum="0" Maximum="65535" Height="24" FontSize="12" />
</Grid>
</StackPanel>

View File

@ -9,32 +9,44 @@
<!-- Box Tab -->
<TabItem Header="Box">
<DockPanel Margin="2">
<!-- Box Navigation: [<] BoxName [>] -->
<!-- Box Navigation: [<<] ComboBox [>>] -->
<Grid DockPanel.Dock="Top" ColumnDefinitions="30,*,30" Margin="0,2,0,4">
<Button Grid.Column="0" Content="&lt;" Command="{Binding PreviousBoxCommand}"
<Button Grid.Column="0" Content="&lt;&lt;" Command="{Binding PreviousBoxCommand}"
Width="30" Height="24" HorizontalContentAlignment="Center"
FontSize="12" Padding="0" />
<TextBlock Grid.Column="1" Text="{Binding BoxName}"
HorizontalAlignment="Center" VerticalAlignment="Center"
FontWeight="SemiBold" FontSize="12" />
<Button Grid.Column="2" Content="&gt;" Command="{Binding NextBoxCommand}"
FontSize="11" Padding="0" />
<ComboBox Grid.Column="1"
ItemsSource="{Binding BoxNames}"
SelectedIndex="{Binding CurrentBox}"
HorizontalAlignment="Stretch"
Height="24" FontSize="12" Padding="4,2"
Margin="2,0" />
<Button Grid.Column="2" Content="&gt;&gt;" Command="{Binding NextBoxCommand}"
Width="30" Height="24" HorizontalContentAlignment="Center"
FontSize="12" Padding="0" />
FontSize="11" Padding="0" />
</Grid>
<!-- Box Grid: 6 columns x 5 rows = 30 slots, each 70x58 -->
<ItemsControl ItemsSource="{Binding BoxSlots}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="6" Rows="5" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<views:SlotControl />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!-- Box Grid: 6 columns x 5 rows = 30 slots, with wallpaper background -->
<Border CornerRadius="4" ClipToBounds="True">
<Panel>
<!-- Wallpaper background layer -->
<Image Source="{Binding BoxWallpaper}"
Stretch="Fill"
IsHitTestVisible="False" />
<!-- Slot grid layer -->
<ItemsControl ItemsSource="{Binding BoxSlots}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="6" Rows="5" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<views:SlotControl />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Panel>
</Border>
</DockPanel>
</TabItem>
@ -69,9 +81,24 @@
<TabItem Header="SAV">
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<WrapPanel Margin="4" Orientation="Horizontal">
<TextBlock Text="SAV tools appear here when a save file is loaded."
FontSize="11" Opacity="0.6" Margin="0,0,0,8"
TextWrapping="Wrap" />
<Button Content="Items" Command="{Binding OpenInventoryCommand}"
Margin="2" Padding="10,4" FontSize="11"
IsVisible="{Binding IsLoaded}" />
<Button Content="Box Layout" Command="{Binding OpenBoxLayoutCommand}"
Margin="2" Padding="10,4" FontSize="11"
IsVisible="{Binding IsLoaded}" />
<Button Content="Wondercards" Command="{Binding OpenWondercardCommand}"
Margin="2" Padding="10,4" FontSize="11"
IsVisible="{Binding IsLoaded}" />
<Button Content="Event Flags" Command="{Binding OpenEventFlagsCommand}"
Margin="2" Padding="10,4" FontSize="11"
IsVisible="{Binding IsLoaded}" />
<Button Content="Settings" Command="{Binding OpenSettingsEditorCommand}"
Margin="2" Padding="10,4" FontSize="11" />
<TextBlock Text="Load a save file to access tools."
FontSize="11" Opacity="0.6" Margin="4,6,0,0"
TextWrapping="Wrap"
IsVisible="{Binding !IsLoaded}" />
</WrapPanel>
</ScrollViewer>
</TabItem>

View File

@ -7,10 +7,29 @@
Width="70" Height="58"
Margin="0">
<UserControl.Styles>
<!-- Subtle default border -->
<Style Selector="Border#SlotBorder">
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="3" />
<Setter Property="Transitions">
<Transitions>
<BrushTransition Property="BorderBrush" Duration="0:0:0.15" />
<BrushTransition Property="Background" Duration="0:0:0.15" />
</Transitions>
</Setter>
</Style>
<!-- Hover: highlight border -->
<Style Selector="Border#SlotBorder:pointerover">
<Setter Property="BorderBrush" Value="#4090FF" />
<Setter Property="Background" Value="#18FFFFFF" />
</Style>
</UserControl.Styles>
<Border Name="SlotBorder"
Background="Transparent"
BorderBrush="{DynamicResource SystemControlForegroundBaseLowBrush}"
BorderThickness="1"
Cursor="Hand"
DragDrop.AllowDrop="True">
<Border.ContextMenu>

View File

@ -39,7 +39,9 @@ private static void EnsureInitialized()
if (lastDot < 0)
return null;
return manifestName[(lastDot + 1)..^4];
var key = manifestName[(lastDot + 1)..^4];
key = key.Replace('-', '_'); // Normalize hyphens to underscores to match WinForms convention
return key;
}
/// <summary>

View File

@ -42,7 +42,9 @@ private static void EnsureInitialized()
if (lastDot < 0)
return null;
return manifestName[(lastDot + 1)..^4]; // between last dot and .png
var key = manifestName[(lastDot + 1)..^4]; // between last dot and .png
key = key.Replace('-', '_'); // Normalize hyphens to underscores to match WinForms convention
return key;
}
/// <summary>