1
0

feat: use TasFile model to replace some view model code.

This commit is contained in:
2026-04-14 16:59:43 +08:00
parent ef4506cf41
commit afcc848e5b
4 changed files with 149 additions and 36 deletions

View File

@@ -15,13 +15,9 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="CommunityToolkit.HighPerformance" Version="8.4.0" /> <PackageReference Include="CommunityToolkit.HighPerformance" Version="8.4.0" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.1" /> <PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.2" />
<PackageReference Include="OneOf" Version="3.0.271" /> <PackageReference Include="OneOf" Version="3.0.271" />
<PackageReference Include="System.Configuration.ConfigurationManager" Version="10.0.5" /> <PackageReference Include="System.Configuration.ConfigurationManager" Version="10.0.5" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Folder Include="Frontend\Models\" />
</ItemGroup>
</Project> </Project>

View File

@@ -0,0 +1,84 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BallanceTasEditor.Frontend.Models {
public partial class TasFile : ObservableObject {
public TasFile() {
FileBody = null;
}
#region File Load and Save
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(IsFileLoaded))]
[NotifyPropertyChangedFor(nameof(IsFileNotLoaded))]
private Backend.ITasSequence? fileBody;
[MemberNotNullWhen(true, nameof(FileBody))]
public bool IsFileLoaded {
get => FileBody is not null;
}
[MemberNotNullWhen(false, nameof(FileBody))]
public bool IsFileNotLoaded {
get => FileBody is null;
}
public void NewFile(TasSequenceKind kind, int count, uint fps) {
// Check status
if (IsFileLoaded) {
throw new InvalidOperationException();
}
// Initialize sequence
var seq = TasSequenceKindHelper.CreateSequenceByKind(kind);
// Initialize items
Backend.TasStorage.Init(seq, count, fps);
// Set members
FileBody = seq;
}
public void LoadFile(TasSequenceKind kind, string path) {
// Check status
if (IsFileLoaded) {
throw new InvalidOperationException();
}
// Initialize sequence
var seq = TasSequenceKindHelper.CreateSequenceByKind(kind);
// Load into sequence
Backend.TasStorage.Load(path, seq);
// Set members
FileBody = seq;
}
public void SaveFile(string path) {
// Check status
if (IsFileNotLoaded) {
throw new InvalidOperationException();
}
// Save sequence
Backend.TasStorage.Save(path, FileBody);
}
public void CloseFile() {
// Check status
if (IsFileNotLoaded) {
throw new InvalidOperationException();
}
// Set member
FileBody = null;
}
#endregion
}
}

View File

@@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace BallanceTasEditor.Frontend.Models {
public enum TasSequenceKind {
Array,
DoubleLinkedList
}
public static class TasSequenceKindHelper {
public static Backend.ITasSequence CreateSequenceByKind(TasSequenceKind kind) {
return kind switch {
TasSequenceKind.Array => new Backend.ListTasSequence(),
TasSequenceKind.DoubleLinkedList => new Backend.LegacyTasSequence(),
_ => throw new UnreachableException(),
};
}
}
}

View File

@@ -15,8 +15,9 @@ namespace BallanceTasEditor.Frontend.ViewModels {
public MainWindow(Shared.IDialogService dialogService) { public MainWindow(Shared.IDialogService dialogService) {
m_DialogService = dialogService; m_DialogService = dialogService;
this.TasFile = null; TasFile = new Models.TasFile();
this.TasFilePath = null; TasFile.PropertyChanged += TasFile_PropertyChanged;
TasFilePath = null;
this.StatusMessage = ""; this.StatusMessage = "";
m_StatusMessageDimmer = new DispatcherTimer(); m_StatusMessageDimmer = new DispatcherTimer();
@@ -33,34 +34,38 @@ namespace BallanceTasEditor.Frontend.ViewModels {
[ObservableProperty] [ObservableProperty]
[NotifyPropertyChangedFor(nameof(WindowTitle))] [NotifyPropertyChangedFor(nameof(WindowTitle))]
[NotifyCanExecuteChangedFor(nameof(NewFileCommand))] private Models.TasFile tasFile;
[NotifyCanExecuteChangedFor(nameof(OpenFileCommand))]
[NotifyCanExecuteChangedFor(nameof(SaveFileCommand))]
[NotifyCanExecuteChangedFor(nameof(SaveFileAsCommand))]
[NotifyCanExecuteChangedFor(nameof(CloseFileCommand))]
private Backend.ITasSequence? tasFile;
[ObservableProperty] [ObservableProperty]
[NotifyPropertyChangedFor(nameof(WindowTitle))] [NotifyPropertyChangedFor(nameof(WindowTitle))]
private string? tasFilePath; private string? tasFilePath;
private void TasFile_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e) {
// YYC MARK:
// Due to the shitty limit of MVVM Toolkit,
// I was forced trigger these command manually.
NewFileCommand.NotifyCanExecuteChanged();
OpenFileCommand.NotifyCanExecuteChanged();
SaveFileCommand.NotifyCanExecuteChanged();
SaveFileAsCommand.NotifyCanExecuteChanged();
CloseFileCommand.NotifyCanExecuteChanged();
}
[RelayCommand(CanExecute = nameof(CanNewFile))] [RelayCommand(CanExecute = nameof(CanNewFile))]
private void NewFile() { private void NewFile() {
// Request new file properties // Request new file properties
var dialog = m_DialogService.ShowNewFileDialog(); var dialog = m_DialogService.ShowNewFileDialog();
if (dialog is null) return; if (dialog is null) return;
// Initialize sequence // Create new file
var seq = new Backend.ListTasSequence(); TasFile.NewFile(Models.TasSequenceKind.Array, dialog.Count, dialog.Fps);
// Initialize items
Backend.TasStorage.Init(seq, dialog.Count, dialog.Fps);
// Set members // Set members
this.TasFile = seq; TasFilePath = null;
this.TasFilePath = null; // Send notification
UpdateStatusMessage($"New TAS file is created."); UpdateStatusMessage($"New TAS file is created.");
} }
private bool CanNewFile() { private bool CanNewFile() {
return this.TasFile is null; return TasFile.IsFileNotLoaded;
} }
[RelayCommand(CanExecute = nameof(CanOpenFile))] [RelayCommand(CanExecute = nameof(CanOpenFile))]
@@ -69,23 +74,21 @@ namespace BallanceTasEditor.Frontend.ViewModels {
var dialog = m_DialogService.ShowOpenFileDialog(); var dialog = m_DialogService.ShowOpenFileDialog();
if (dialog is null) return; if (dialog is null) return;
// Initialize sequence // Load file
var seq = new Backend.ListTasSequence();
// Load into sequence
try { try {
Backend.TasStorage.Load(dialog.Path, seq); TasFile.LoadFile(Models.TasSequenceKind.Array, dialog.Path);
} catch (Exception e) { } catch (Exception e) {
m_DialogService.ShowOpenFileFailedDialog(e); m_DialogService.ShowOpenFileFailedDialog(e);
return; return;
} }
// Set members // Set members
this.TasFile = seq; TasFilePath = dialog.Path;
this.TasFilePath = dialog.Path; // Send notification
UpdateStatusMessage($"TAS file {this.TasFilePath} is loaded."); UpdateStatusMessage($"TAS file {this.TasFilePath} is loaded.");
} }
private bool CanOpenFile() { private bool CanOpenFile() {
return this.TasFile is null; return TasFile.IsFileNotLoaded;
} }
[RelayCommand(CanExecute = nameof(CanSaveFile))] [RelayCommand(CanExecute = nameof(CanSaveFile))]
@@ -102,18 +105,19 @@ namespace BallanceTasEditor.Frontend.ViewModels {
// Save file // Save file
try { try {
Backend.TasStorage.Save(filePath, this.TasFile.Unwrap()); TasFile.SaveFile(filePath);
} catch (Exception e) { } catch (Exception e) {
m_DialogService.ShowSaveFileFailedDialog(e); m_DialogService.ShowSaveFileFailedDialog(e);
return; return;
} }
// Update member // Update member
this.TasFilePath = filePath; TasFilePath = filePath;
// Send notification
UpdateStatusMessage($"TAS file {this.TasFilePath} is saved."); UpdateStatusMessage($"TAS file {this.TasFilePath} is saved.");
} }
private bool CanSaveFile() { private bool CanSaveFile() {
return this.TasFile is not null; return TasFile.IsFileLoaded;
} }
[RelayCommand(CanExecute = nameof(CanSaveFileAs))] [RelayCommand(CanExecute = nameof(CanSaveFileAs))]
@@ -124,29 +128,33 @@ namespace BallanceTasEditor.Frontend.ViewModels {
// Save file // Save file
try { try {
Backend.TasStorage.Save(dialog.Path, this.TasFile.Unwrap()); TasFile.SaveFile(dialog.Path);
} catch (Exception e) { } catch (Exception e) {
m_DialogService.ShowSaveFileFailedDialog(e); m_DialogService.ShowSaveFileFailedDialog(e);
return; return;
} }
// Set file path // Set file path
TasFilePath = dialog.Path; TasFilePath = dialog.Path;
// Send notification
UpdateStatusMessage($"TAS file {this.TasFilePath} is saved."); UpdateStatusMessage($"TAS file {this.TasFilePath} is saved.");
} }
private bool CanSaveFileAs() { private bool CanSaveFileAs() {
return this.TasFile is not null; return TasFile.IsFileLoaded;
} }
[RelayCommand(CanExecute = nameof(CanCloseFile))] [RelayCommand(CanExecute = nameof(CanCloseFile))]
private void CloseFile() { private void CloseFile() {
this.TasFile = null; // Close file
this.TasFilePath = null; TasFile.CloseFile();
// Set members
TasFilePath = null;
// Send notification
UpdateStatusMessage($"TAS file is closed."); UpdateStatusMessage($"TAS file is closed.");
} }
private bool CanCloseFile() { private bool CanCloseFile() {
return this.TasFile is not null; return TasFile.IsFileLoaded;
} }
#endregion #endregion
@@ -228,7 +236,7 @@ namespace BallanceTasEditor.Frontend.ViewModels {
public string WindowTitle { public string WindowTitle {
get { get {
if (TasFile is null) { if (TasFile.IsFileNotLoaded) {
return "Ballance TAS Editor"; return "Ballance TAS Editor";
} else { } else {
if (TasFilePath is null) { if (TasFilePath is null) {