Skip to content

Commit 0906810

Browse files
feat: unified editor layout - remove OTB tab, add OTB panel + sprite strip
- Remove pill toggle (OTB Items / Client Items tabs) - Remove separate OTB VIEW entirely - Unified 3-column layout: client list | editor+OTB detail | OTB items list - Move sprites from right column to horizontal bottom strip - Add OTB panel with search, pagination, bidirectional linking - Double-click client item opens editor + linked OTB detail - Double-click OTB item opens OTB detail panel - Animation timer starts on client data load
1 parent 8a117f6 commit 0906810

9 files changed

Lines changed: 2965 additions & 363 deletions

src/App/AppSettings.cs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using System.Text.Json;
2+
3+
namespace POriginsItemEditor.App;
4+
5+
public class AppSettings
6+
{
7+
public string? LastOtbPath { get; set; }
8+
public string? LastClientFolderPath { get; set; }
9+
10+
private static string SettingsFilePath =>
11+
Path.Combine(
12+
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
13+
"POriginsItemEditor",
14+
"settings.json");
15+
16+
public static AppSettings Load()
17+
{
18+
try
19+
{
20+
var path = SettingsFilePath;
21+
if (File.Exists(path))
22+
{
23+
var json = File.ReadAllText(path);
24+
return JsonSerializer.Deserialize<AppSettings>(json) ?? new AppSettings();
25+
}
26+
}
27+
catch { }
28+
return new AppSettings();
29+
}
30+
31+
public void Save()
32+
{
33+
try
34+
{
35+
var path = SettingsFilePath;
36+
Directory.CreateDirectory(Path.GetDirectoryName(path)!);
37+
var json = JsonSerializer.Serialize(this, new JsonSerializerOptions { WriteIndented = true });
38+
File.WriteAllText(path, json);
39+
}
40+
catch { }
41+
}
42+
}

src/App/MainWindow.axaml

Lines changed: 1007 additions & 224 deletions
Large diffs are not rendered by default.

src/App/MainWindow.axaml.cs

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using Avalonia.Controls;
22
using Avalonia.Controls.Primitives;
33
using Avalonia.Input;
4+
using Avalonia.Interactivity;
45
using POriginsItemEditor.App.ViewModels;
56

67
namespace POriginsItemEditor.App;
@@ -10,6 +11,11 @@ public partial class MainWindow : Window
1011
public MainWindow()
1112
{
1213
InitializeComponent();
14+
Loaded += async (_, _) =>
15+
{
16+
if (DataContext is MainWindowViewModel vm)
17+
await vm.TryLoadLastSessionAsync();
18+
};
1319
}
1420

1521
private void OnMinimapSwatchClicked(object? sender, PointerPressedEventArgs e)
@@ -23,7 +29,6 @@ private void OnMinimapColorPicked(object? sender, PointerPressedEventArgs e)
2329
if (sender is not Border border || DataContext is not MainWindowViewModel vm || vm.SelectedItem is not { } item)
2430
return;
2531

26-
// Tag may be boxed as ushort or int depending on XAML binding
2732
ushort index = border.Tag switch
2833
{
2934
ushort u => u,
@@ -34,7 +39,6 @@ private void OnMinimapColorPicked(object? sender, PointerPressedEventArgs e)
3439

3540
item.MinimapColor = index;
3641

37-
// Walk up to find the control with the attached flyout and close it
3842
Control? current = border;
3943
while (current != null)
4044
{
@@ -47,4 +51,26 @@ private void OnMinimapColorPicked(object? sender, PointerPressedEventArgs e)
4751
current = current.Parent as Control;
4852
}
4953
}
54+
55+
private void OnCompositionSpriteDoubleTapped(object? sender, TappedEventArgs e)
56+
{
57+
if (sender is Border border
58+
&& border.DataContext is SpriteViewModel svm
59+
&& DataContext is MainWindowViewModel vm)
60+
{
61+
vm.NavigateRightSpriteToIdCommand.Execute(svm.SpriteId);
62+
}
63+
}
64+
65+
private void OnClientItemDoubleTapped(object? sender, TappedEventArgs e)
66+
{
67+
if (DataContext is MainWindowViewModel vm)
68+
vm.OpenClientItemEditor();
69+
}
70+
71+
private void OnOtbItemDoubleTapped(object? sender, TappedEventArgs e)
72+
{
73+
if (DataContext is MainWindowViewModel vm)
74+
vm.OpenOtbItemEditor();
75+
}
5076
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
using Avalonia.Media.Imaging;
2+
using CommunityToolkit.Mvvm.ComponentModel;
3+
using POriginsItemEditor.OTB;
4+
5+
namespace POriginsItemEditor.App.ViewModels;
6+
7+
/// <summary>
8+
/// VM for a client item (DAT perspective) in the client items list.
9+
/// </summary>
10+
public partial class ClientItemViewModel : ObservableObject
11+
{
12+
public DatThingType ThingType { get; }
13+
14+
public ClientItemViewModel(DatThingType thingType)
15+
{
16+
ThingType = thingType;
17+
}
18+
19+
public ushort Id => ThingType.Id;
20+
public ThingCategory Category => ThingType.Category;
21+
public string CategoryName => Category.ToString();
22+
public uint FirstSpriteId => ThingType.FirstSpriteId;
23+
24+
[ObservableProperty] private WriteableBitmap? _sprite;
25+
26+
public int TotalSprites => ThingType.TotalSpriteCount;
27+
28+
/// <summary>Current animation frame (cycled by timer in MainWindowViewModel).</summary>
29+
public int AnimFrame { get; set; }
30+
31+
public int AnimPhases
32+
{
33+
get
34+
{
35+
if (ThingType.FrameGroups.Length == 0) return 0;
36+
return ThingType.FrameGroups[0].Frames;
37+
}
38+
}
39+
40+
// ── Frame group info ──
41+
public byte Width => ThingType.FrameGroups.Length > 0 ? ThingType.FrameGroups[0].Width : (byte)1;
42+
public byte Height => ThingType.FrameGroups.Length > 0 ? ThingType.FrameGroups[0].Height : (byte)1;
43+
public byte ExactSize => ThingType.FrameGroups.Length > 0 ? ThingType.FrameGroups[0].ExactSize : (byte)32;
44+
public byte Layers => ThingType.FrameGroups.Length > 0 ? ThingType.FrameGroups[0].Layers : (byte)1;
45+
public byte PatternX => ThingType.FrameGroups.Length > 0 ? ThingType.FrameGroups[0].PatternX : (byte)1;
46+
public byte PatternY => ThingType.FrameGroups.Length > 0 ? ThingType.FrameGroups[0].PatternY : (byte)1;
47+
public byte PatternZ => ThingType.FrameGroups.Length > 0 ? ThingType.FrameGroups[0].PatternZ : (byte)1;
48+
public byte Frames => ThingType.FrameGroups.Length > 0 ? ThingType.FrameGroups[0].Frames : (byte)1;
49+
}

src/App/ViewModels/ItemViewModel.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,9 @@ private void DecrementClientId()
124124
[ObservableProperty] private bool _hasMismatch;
125125
[ObservableProperty] private uint _firstSpriteId;
126126

127+
/// <summary>Full DAT thing type (set during cross-reference).</summary>
128+
public DatThingType? DatThingType { get; set; }
129+
127130
public string MismatchDetail
128131
{
129132
get

0 commit comments

Comments
 (0)