Skip to content

Commit

Permalink
Created layout preset library dialog (#375)
Browse files Browse the repository at this point in the history
  • Loading branch information
Atria1234 committed Jan 17, 2022
1 parent 9de3dac commit a07f581
Show file tree
Hide file tree
Showing 29 changed files with 895 additions and 14 deletions.
16 changes: 16 additions & 0 deletions AnnoDesigner.Core/Layout/LayoutLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,17 @@ public LayoutFile LoadLayout(Stream streamWithLayout, bool forceLoad = false)
return Load(jsonString, forceLoad);
}

public async Task<LayoutFile> LoadLayoutAsync(Stream streamWithLayout, bool forceLoad = false)
{
if (streamWithLayout == null)
{
throw new ArgumentNullException(nameof(streamWithLayout));
}
using var sr = new StreamReader(streamWithLayout);
var jsonString = await sr.ReadToEndAsync();
return await LoadAsync(jsonString, forceLoad);
}

private LayoutFile Load(string jsonString, bool forceLoad)
{
var layoutVersion = new LayoutFileVersionContainer() { FileVersion = 0 };
Expand All @@ -88,5 +99,10 @@ private LayoutFile Load(string jsonString, bool forceLoad)
_ => throw new NotImplementedException()
};
}

private Task<LayoutFile> LoadAsync(string jsonString, bool forceLoad)
{
return Task.Run(() => Load(jsonString, forceLoad));
}
}
}
11 changes: 10 additions & 1 deletion AnnoDesigner.Core/Layout/Models/LayoutFile.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using AnnoDesigner.Core.Models;
Expand All @@ -21,5 +22,13 @@ public LayoutFile(IEnumerable<AnnoObject> objects)
FileVersion = CoreConstants.LayoutFileVersion;
Objects = objects.ToList();
}

public LayoutFile(LayoutFile copy)
{
FileVersion = copy.FileVersion;
LayoutVersion = (Version)copy.LayoutVersion.Clone();
Modified = copy.Modified;
Objects = copy.Objects.Select(x => new AnnoObject(x)).ToList();
}
}
}
7 changes: 7 additions & 0 deletions AnnoDesigner.Core/Layout/Presets/IPresetLayout.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace AnnoDesigner.Core.Layout.Presets
{
public interface IPresetLayout
{
public MultilangInfo Name { get; }
}
}
20 changes: 20 additions & 0 deletions AnnoDesigner.Core/Layout/Presets/LayoutPresetInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
namespace AnnoDesigner.Core.Layout.Presets
{
public class LayoutPresetInfo
{
public MultilangInfo Name { get; set; }

public MultilangInfo Description { get; set; }

public string Author { get; set; }

public string AuthorContact { get; set; }

public LayoutPresetInfo() { }

public LayoutPresetInfo(string name)
{
Name = name;
}
}
}
78 changes: 78 additions & 0 deletions AnnoDesigner.Core/Layout/Presets/MultilangInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;

namespace AnnoDesigner.Core.Layout.Presets
{
[JsonConverter(typeof(MultilangInfoConverter))]
public class MultilangInfo
{
private class MultilangInfoConverter : JsonConverter<MultilangInfo>
{
public override MultilangInfo ReadJson(JsonReader reader, Type objectType, MultilangInfo existingValue, bool hasExistingValue, JsonSerializer serializer)
{
switch (reader.TokenType)
{
case JsonToken.String:
return reader.Value as string;
case JsonToken.StartObject:
return serializer.Deserialize<Dictionary<string, string>>(reader);
default:
throw new JsonSerializationException($"Unexpected token during deserialization of {nameof(MultilangInfo)}");
}
}

public override void WriteJson(JsonWriter writer, MultilangInfo value, JsonSerializer serializer)
{
serializer.Serialize(writer, (object)value.Default ?? value.Translations);
}
}

private Dictionary<string, string> Translations { get; set; }

private string Default { get; set; }

public string this[string language]
{
set
{
Translations ??= new Dictionary<string, string>();
Translations[language] = value;
}
}

public string Translate(string language)
{
return Default ?? (
Translations.TryGetValue(language, out var translation)
? translation
: Translations.Count > 0
? $"{Translations.FirstOrDefault().Value} ({Translations.FirstOrDefault().Key})"
: string.Empty
);
}

public static implicit operator MultilangInfo(string value)
{
return new MultilangInfo()
{
Default = value
};
}

public static implicit operator MultilangInfo(Dictionary<string, string> value)
{
return new MultilangInfo()
{
Translations = value
};
}

public static explicit operator string(MultilangInfo info)
{
var first = info.Translations.FirstOrDefault();
return info.Default ?? (info.Translations.Count > 0 ? $"{first.Value} ({first.Key})" : string.Empty);
}
}
}
21 changes: 21 additions & 0 deletions AnnoDesigner.Core/Layout/Presets/PresetLayout.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System.Collections.Generic;
using System.Windows.Media;
using AnnoDesigner.Core.Layout.Models;

namespace AnnoDesigner.Core.Layout.Presets
{
public class PresetLayout : IPresetLayout
{
public MultilangInfo Name => Info.Name;

public LayoutPresetInfo Info { get; set; }

public string Author { get; set; }

public string AuthorContact { get; set; }

public LayoutFile Layout { get; set; }

public List<ImageSource> Images { get; set; }
}
}
11 changes: 11 additions & 0 deletions AnnoDesigner.Core/Layout/Presets/PresetLayoutDirectory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System.Collections.Generic;

namespace AnnoDesigner.Core.Layout.Presets
{
public class PresetLayoutDirectory : IPresetLayout
{
public MultilangInfo Name { get; set; }

public List<IPresetLayout> Presets { get; set; }
}
}
102 changes: 102 additions & 0 deletions AnnoDesigner.Core/Layout/Presets/PresetLayoutLoader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Abstractions;
using System.IO.Compression;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using AnnoDesigner.Core.Layout.Models;
using Newtonsoft.Json;
using NLog;

namespace AnnoDesigner.Core.Layout.Presets
{
public class PresetLayoutLoader
{
private readonly IFileSystem _fileSystem;
private static readonly Logger Logger = LogManager.GetLogger(nameof(PresetLayoutLoader));

public Func<LayoutFile, ImageSource> RenderLayoutToImage { get; set; }
public PresetLayoutLoader(Func<LayoutFile, ImageSource> renderLayoutToImage, IFileSystem fileSystem = null)
{
RenderLayoutToImage = renderLayoutToImage;
_fileSystem = fileSystem ?? new FileSystem();
}

public async Task<List<IPresetLayout>> LoadAsync(string rootDirectory)
{
var data = await LoadDirectoryAsync(rootDirectory);

return data.Presets;
}

private async Task<PresetLayoutDirectory> LoadDirectoryAsync(string directory)
{
var subdirectories = await Task.WhenAll(_fileSystem.Directory.GetDirectories(directory).Select(LoadDirectoryAsync));
var layouts = await Task.WhenAll(_fileSystem.Directory.GetFiles(directory, "*.zip").Select(LoadLayout));
return new PresetLayoutDirectory()
{
Name = _fileSystem.Path.GetFileName(directory),
Presets = subdirectories.Cast<IPresetLayout>().Concat(layouts.Where(f => f != null)).ToList()
};
}

private async Task<PresetLayout> LoadLayout(string file)
{
try
{
using var zipFile = ZipFile.OpenRead(file);
using var layoutFile = zipFile.GetEntry("layout.ad").Open();
using var infoFile = zipFile.GetEntry("info.json")?.Open() ?? Stream.Null;
using var infoStream = new StreamReader(infoFile);

var layout = await new LayoutLoader().LoadLayoutAsync(layoutFile, true);
if (layout != null)
{
var info = JsonConvert.DeserializeObject<LayoutPresetInfo>(await infoStream.ReadToEndAsync()) ?? new LayoutPresetInfo(_fileSystem.Path.GetFileNameWithoutExtension(file));
var images = new List<ImageSource>()
{
RenderLayoutToImage(layout)
};

foreach (var item in zipFile.Entries)
{
using var stream = item.Open();
switch (_fileSystem.Path.GetExtension(item.FullName).ToLowerInvariant())
{
case ".png":
case ".jpg":
case ".jpeg":
await Task.Yield();

var imageStream = new MemoryStream();
stream.CopyTo(imageStream);
var image = new BitmapImage();
image.BeginInit();
image.StreamSource = imageStream;
image.EndInit();
image.Freeze();
images.Add(image);
break;
}
}

return new PresetLayout()
{
Info = info,
Layout = layout,
Images = images
};
}
}
catch (Exception e)
{
Logger.Warn(e, $"Failed to parse loadout preset {file}");
}

return null;
}
}
}
1 change: 1 addition & 0 deletions AnnoDesigner.Core/Models/IAppSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,6 @@ public interface IAppSettings
bool InvertScrollingDirection { get; set; }
bool ShowScrollbars { get; set; }
bool IncludeRoadsInStatisticCalculation { get; set; }
string PresetLayoutLocation { get; set; }
}
}
7 changes: 7 additions & 0 deletions AnnoDesigner/AnnoDesigner.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@
<None Include="..\Presets\treeLocalization.json" Link="treeLocalization.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Layouts\LayoutInfoSchema.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Layouts\Readme.md">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
Expand Down Expand Up @@ -83,6 +89,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Extended.Wpf.Toolkit" Version="3.8.2" />
<PackageReference Include="Microsoft-WindowsAPICodePack-Shell" Version="1.1.4" />
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.31" />
<PackageReference Include="NLog" />
Expand Down
5 changes: 5 additions & 0 deletions AnnoDesigner/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,5 +108,10 @@ public static class Constants
/// The default number of recent files to show.
/// </summary>
public const int MaxRecentFiles = 10;

/// <summary>
/// Default location of preset layout folder.
/// </summary>
public const string DefaultPresetLayoutLocation = ".\\Layouts";
}
}
24 changes: 24 additions & 0 deletions AnnoDesigner/Converters/ReferenceToBooleanConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System;
using System.Globalization;
using System.Windows.Data;

namespace AnnoDesigner.Converters
{
[ValueConversion(typeof(object), typeof(object))]
public class ReferenceToValueConverter : IValueConverter
{
public object NullValue { get; set; }

public object NotNullValue { get; set; }

public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value == null ? NullValue : NotNullValue;
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
19 changes: 19 additions & 0 deletions AnnoDesigner/ImageWindow.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Window x:Class="AnnoDesigner.ImageWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="ImageWindow" Height="600" Width="800">
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Canvas Width="{Binding ActualWidth, ElementName=image}" Height="{Binding ActualHeight, ElementName=image}">
<Canvas.Background>
<VisualBrush>
<VisualBrush.Visual>
<Image Source="{Binding .}" x:Name="image"/>
</VisualBrush.Visual>
</VisualBrush>
</Canvas.Background>
</Canvas>
</ScrollViewer>
</Window>
Loading

0 comments on commit a07f581

Please sign in to comment.