Skip to content

Commit

Permalink
Add Item editor to Avalonia branch (#448)
Browse files Browse the repository at this point in the history
  • Loading branch information
jonko0493 authored Jan 25, 2025
1 parent 0576102 commit f7bd775
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 12 deletions.
18 changes: 18 additions & 0 deletions src/SerialLoops/Assets/Strings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions src/SerialLoops/Assets/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -3080,4 +3080,10 @@ This action is irreversible.</value>
<data name="Enter" xml:space="preserve">
<value>Enter</value>
</data>
<data name="Export Item Image" xml:space="preserve">
<value>Export Item Image</value>
</data>
<data name="Import Item Image" xml:space="preserve">
<value>Import Item Image</value>
</data>
</root>
74 changes: 74 additions & 0 deletions src/SerialLoops/ViewModels/Editors/ItemEditorViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
using System;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Input;
using Avalonia.Platform.Storage;
using HaruhiChokuretsuLib.Archive.Graphics;
using HaruhiChokuretsuLib.Util;
using ReactiveUI;
using SerialLoops.Assets;
using SerialLoops.Controls;
using SerialLoops.Lib.Items;
using SerialLoops.Utility;
using SerialLoops.ViewModels.Dialogs;
using SerialLoops.Views.Dialogs;
using SkiaSharp;

namespace SerialLoops.ViewModels.Editors;

public class ItemEditorViewModel : EditorViewModel
{
private ItemItem _item;
public SKBitmap ItemBitmap => _item.GetImage();

public ICommand ExportCommand { get; }
public ICommand ImportCommand { get; }

public ItemEditorViewModel(ItemItem item, MainWindowViewModel window, ILogger log) : base(item, window, log)
{
_item = item;
ExportCommand = ReactiveCommand.CreateFromTask(Export);
ImportCommand = ReactiveCommand.CreateFromTask(Import);
}

private async Task Export()
{
string exportPath = (await Window.Window.ShowSaveFilePickerAsync(Strings.Export_Item_Image,
[new(Strings.PNG_Image) { Patterns = ["*.png"] }]))?.TryGetLocalPath();
if (!string.IsNullOrEmpty(exportPath))
{
await using FileStream fs = File.Create(exportPath);
ItemBitmap.Encode(fs, SKEncodedImageFormat.Png, GraphicsFile.PNG_QUALITY);
}
}

private async Task Import()
{
string importPath = (await Window.Window.ShowOpenFilePickerAsync(Strings.Import_Item_Image,
[new(Strings.Supported_Images) { Patterns = Shared.SupportedImageFiletypes }]))?.TryGetLocalPath();
if (!string.IsNullOrEmpty(importPath))
{
SKBitmap original = _item.GetImage();
ImageCropResizeDialogViewModel cropResizeDialogViewModel = new(importPath, original.Width, original.Height, _log);
SKBitmap finalImage = await new ImageCropResizeDialog
{
DataContext = cropResizeDialogViewModel,
}.ShowDialog<SKBitmap>(Window.Window);
if (finalImage is not null)
{
try
{
LoopyProgressTracker tracker = new();
await new ProgressDialog(() => _item.SetImage(finalImage, tracker, _log),
() => { }, tracker, string.Format(Strings.Replacing__0____, _item.DisplayName)).ShowDialog(Window.Window);
this.RaisePropertyChanged(nameof(ItemBitmap));
Description.UnsavedChanges = true;
}
catch (Exception ex)
{
_log.LogException(string.Format(Strings.Failed_to_replace_background__0__with_file__1_, _item.DisplayName, importPath), ex);
}
}
}
}
}
28 changes: 16 additions & 12 deletions src/SerialLoops/ViewModels/MainWindowViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -835,6 +835,21 @@ public void SaveProject_Executed()
OpenProject.Scenario.Selects[groupSelectionItem.Index] = groupSelectionItem.Selection;
changedScenario = true;
break;
case ItemDescription.ItemType.Item:
((ItemItem)item).Write(OpenProject, Log);
break;
case ItemDescription.ItemType.Layout:
GraphicsFile layout = ((LayoutItem)item).Layout;
if (!changedLayouts.Contains(layout.Index))
{
changedLayouts.Add(layout.Index);
IO.WriteStringFile(Path.Combine("assets", "graphics", $"{layout.Index:X3}.lay"), JsonSerializer.Serialize(layout.LayoutEntries, Project.SERIALIZER_OPTIONS), OpenProject, Log);
}
break;
case ItemDescription.ItemType.Puzzle:
PuzzleFile puzzle = ((PuzzleItem)item).Puzzle;
IO.WriteStringFile(Path.Combine("assets", "data", $"{puzzle.Index:X3}.s"), puzzle.GetSource(includes), OpenProject, Log);
break;
case ItemDescription.ItemType.Scenario:
ScenarioStruct scenario = ((ScenarioItem)item).Scenario;
OpenProject.Scenario.Commands = scenario.Commands;
Expand All @@ -850,25 +865,14 @@ public void SaveProject_Executed()
evt.CollectGarbage();
IO.WriteStringFile(Path.Combine("assets", "events", $"{evt.Index:X3}.s"), evt.GetSource(includes), OpenProject, Log);
break;
case ItemDescription.ItemType.Layout:
GraphicsFile layout = ((LayoutItem)item).Layout;
if (!changedLayouts.Contains(layout.Index))
{
changedLayouts.Add(layout.Index);
IO.WriteStringFile(Path.Combine("assets", "graphics", $"{layout.Index:X3}.lay"), JsonSerializer.Serialize(layout.LayoutEntries, Project.SERIALIZER_OPTIONS), OpenProject, Log);
}
break;
case ItemDescription.ItemType.Puzzle:
PuzzleFile puzzle = ((PuzzleItem)item).Puzzle;
IO.WriteStringFile(Path.Combine("assets", "data", $"{puzzle.Index:X3}.s"), puzzle.GetSource(includes), OpenProject, Log);
break;
case ItemDescription.ItemType.System_Texture:
((SystemTextureItem)item).Write(OpenProject, Log);
break;
case ItemDescription.ItemType.Topic:
changedTopics = true;
break;
case ItemDescription.ItemType.Voice:
VoicedLineItem vce = (VoicedLineItem)item;
if (OpenProject.VoiceMap is not null)
{
changedSubs = true;
Expand Down
2 changes: 2 additions & 0 deletions src/SerialLoops/ViewModels/Panels/EditorTabsPanelViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ private EditorViewModel CreateTab(ItemDescription item)
return new ChessPuzzleEditorViewModel((ChessPuzzleItem)item, MainWindow, _log);
case ItemDescription.ItemType.Group_Selection:
return new GroupSelectionEditorViewModel((GroupSelectionItem)item, MainWindow, _log);
case ItemDescription.ItemType.Item:
return new ItemEditorViewModel((ItemItem)item, MainWindow, _log);
case ItemDescription.ItemType.Layout:
return new LayoutEditorViewModel((LayoutItem)item, MainWindow, _log);
case ItemDescription.ItemType.Map:
Expand Down
20 changes: 20 additions & 0 deletions src/SerialLoops/Views/Editors/ItemEditorView.axaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:vm="using:SerialLoops.ViewModels.Editors"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:assets="using:SerialLoops.Assets"
xmlns:utility="using:SerialLoops.Utility"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:DataType="vm:ItemEditorViewModel"
x:Class="SerialLoops.Views.Editors.ItemEditorView">
<Grid RowDefinitions="Auto,Auto" ColumnDefinitions="Auto">
<Image Grid.Row="0" Source="{Binding ItemBitmap, Converter={x:Static utility:SLConverters.SKBitmapToAvaloniaConverter}}"/>

<StackPanel Grid.Row="1" Orientation="Horizontal" Spacing="3">
<Button Content="{x:Static assets:Strings.Export}" Command="{Binding ExportCommand}"/>
<Button Content="{x:Static assets:Strings.Import}" Command="{Binding ImportCommand}"/>
</StackPanel>
</Grid>
</UserControl>

12 changes: 12 additions & 0 deletions src/SerialLoops/Views/Editors/ItemEditorView.axaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Avalonia.Controls;

namespace SerialLoops.Views.Editors;

public partial class ItemEditorView : UserControl
{
public ItemEditorView()
{
InitializeComponent();
}
}

0 comments on commit f7bd775

Please sign in to comment.