Skip to content

Commit

Permalink
Created layout preset library dialog (AnnoDesigner#375)
Browse files Browse the repository at this point in the history
  • Loading branch information
Atria1234 committed Nov 28, 2021
1 parent 1c38e42 commit f2dc472
Show file tree
Hide file tree
Showing 9 changed files with 661 additions and 127 deletions.
211 changes: 211 additions & 0 deletions AnnoDesigner.Core/Layout/PreparedLayout/PresetLayoutLoader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using AnnoDesigner.Core.Layout.Models;
using Newtonsoft.Json;

namespace AnnoDesigner.Core.Layout.PreparedLayout
{
public interface IPresetLayoutBase
{
public MultilangInfo Name { get; }
}

public class PresetLayoutDirectory : IPresetLayoutBase
{
public MultilangInfo Name { get; set; }

public List<PresetLayoutDirectory> Directories { get; set; } = new List<PresetLayoutDirectory>();

public List<PresetLayout> Layouts { get; set; } = new List<PresetLayout>();

public IEnumerable<IPresetLayoutBase> Presets => Directories.Cast<IPresetLayoutBase>().Concat(Layouts);
}

[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);
}
}

public class LayoutPresetInfo
{
public MultilangInfo Name { get; set; }

public MultilangInfo Description { get; set; }

public string Author { get; set; }

public string AuthorContact { get; set; }
}

public class PresetLayout : IPresetLayoutBase
{
public MultilangInfo Name => Info.Name;

public LayoutPresetInfo Info { get; set; }

public LayoutFile Layout { get; set; }

public List<ImageSource> Images { get; set; }
}

public class PresetLayoutLoader
{
public Func<LayoutFile, ImageSource> RenderLayoutToImage { get; set; }

public PresetLayoutLoader(Func<LayoutFile, ImageSource> renderLayoutToImage)
{
RenderLayoutToImage = renderLayoutToImage;
}

public List<IPresetLayoutBase> Load(string rootDirectory)
{
var data = LoadDirectory(rootDirectory);

return data.Directories.Cast<IPresetLayoutBase>().Concat(data.Layouts).ToList();
}

private PresetLayoutDirectory LoadDirectory(string directory)
{
try
{
return new PresetLayoutDirectory()
{
Name = Path.GetFileName(directory),
Directories = Directory.GetDirectories(directory).Select(LoadDirectory).Where(d => d != null).ToList(),
Layouts = Directory.GetFiles(directory, "*.zip").Select(LoadLayout).Where(f => f != null).ToList()
};
}
catch { /* TODO log */ }

return null;
}

private PresetLayout LoadLayout(string file)
{
try
{
var images = new List<ImageSource>();
LayoutFile layout = null;
var info = new LayoutPresetInfo()
{
Name = Path.GetFileNameWithoutExtension(file)
};

using var zipFile = ZipFile.OpenRead(file);
foreach (var item in zipFile.Entries)
{
using var stream = item.Open();
switch (Path.GetExtension(item.FullName).ToLowerInvariant())
{
case ".png":
case ".jpg":
case ".jpeg":
var imageStream = new MemoryStream();
stream.CopyTo(imageStream);
var image = new BitmapImage();
image.BeginInit();
image.StreamSource = imageStream;
image.EndInit();
image.Freeze();
images.Add(image);
break;
case ".ad":
layout = new LayoutLoader().LoadLayout(stream, true);
images.Insert(0, RenderLayoutToImage(layout));
break;
case ".json":
using (var streamReader = new StreamReader(stream))
{
info = JsonConvert.DeserializeObject<LayoutPresetInfo>(streamReader.ReadToEnd());
}
break;
}
}

if (layout != null)
{
return new PresetLayout()
{
Info = info,
Layout = layout,
Images = images
};
}
}
catch { /* TODO log */ }

return null;
}
}
}
40 changes: 40 additions & 0 deletions AnnoDesigner/Converters/ReferenceToBooleanConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System;
using System.Globalization;
using System.Windows.Data;

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

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

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

[ValueConversion(typeof(object), typeof(bool))]
public class ReferenceTypeToBooleanConverter : IValueConverter
{
public Type ExpectedType { get; set; }

public bool ValueOnCorrectType { get; set; }

public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (value != null && value.GetType() == ExpectedType) == ValueOnCorrectType;
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,35 @@
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace AnnoDesigner
namespace AnnoDesigner.Core.Extensions
{
/// <summary>
/// Provides I/O methods
/// Provides extension methods for framework elements to render it to multiple targets.
/// </summary>
public static class DataIO
public static class FrameworkElementExtensions
{
#region Render to file

/// <summary>
/// Renders the given target to an image file, png encoded.
/// Renders the given target to a bitmap.
/// </summary>
/// <param name="target">target to be rendered</param>
/// <param name="filename">output filename</param>
public static void RenderToFile(FrameworkElement target, string filename)
/// <returns>Bitmap containing rendered framework element</returns>
public static RenderTargetBitmap RenderToBitmap(this FrameworkElement target)
{
// render control
const int dpi = 96;
var rtb = new RenderTargetBitmap((int)target.ActualWidth, (int)target.ActualHeight, dpi, dpi, PixelFormats.Default);
rtb.Render(target);

return rtb;
}

/// <summary>
/// Renders the given target to an image file, png encoded.
/// </summary>
/// <param name="target">target to be rendered</param>
/// <param name="filename">output filename</param>
public static void RenderToFile(this FrameworkElement target, string filename)
{
var rtb = target.RenderToBitmap();
// put result into bitmap
var encoder = Constants.GetExportImageEncoder();
encoder.Frames.Add(BitmapFrame.Create(rtb));
Expand All @@ -32,7 +41,5 @@ public static void RenderToFile(FrameworkElement target, string filename)
encoder.Save(file);
}
}

#endregion
}
}
3 changes: 2 additions & 1 deletion AnnoDesigner/Localization/Localization.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ private Localization() { }

private static IDictionary<string, IDictionary<string, string>> TranslationsRaw { get; set; }

private string SelectedLanguageCode => _commons.CurrentLanguageCode;
public string SelectedLanguageCode => _commons.CurrentLanguageCode;

public static IDictionary<string, string> Translations => TranslationsRaw[Instance.SelectedLanguageCode];

Expand Down Expand Up @@ -1253,6 +1253,7 @@ private void Commons_SelectedLanguageChanged(object sender, EventArgs e)
{
OnPropertyChanged(nameof(Translations));
OnPropertyChanged(nameof(InstanceTranslations));
OnPropertyChanged(nameof(SelectedLanguageCode));
}

public string GetLocalization(string valueToTranslate)
Expand Down
54 changes: 54 additions & 0 deletions AnnoDesigner/Localization/LocalizeBinding.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using AnnoDesigner.Core.Layout.PreparedLayout;

namespace AnnoDesigner.Localization
{
Expand Down Expand Up @@ -92,4 +94,56 @@ public DynamicLocalize(string keyPath) : this()
KeyPath = keyPath;
}
}

public class Multilang : MultiBinding
{
private class MultilangInfoConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length == 2)
{
if (values[0] == DependencyProperty.UnsetValue || values[1] == DependencyProperty.UnsetValue)
{
return DependencyProperty.UnsetValue;
}

if (values[0] is string language && values[1] is MultilangInfo translations)
{
return translations.Translate(language);
}
}
throw new Exception($"Incorrect Multilang parameters. {values[1]}");
}

public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

private static MultilangInfoConverter SingletonConverter { get; } = new MultilangInfoConverter();

public string Path
{
set
{
Bindings.Add(new Binding(value));
}
}

public Multilang()
{
Bindings.Add(new Binding(nameof(Localization.Instance.SelectedLanguageCode))
{
Source = Localization.Instance
});
Converter = SingletonConverter;
}

public Multilang(string path) : this()
{
Path = path;
}
}
}
2 changes: 2 additions & 0 deletions AnnoDesigner/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,8 @@
Command="{Binding CanvasResetZoomCommand}" />
<MenuItem Header="{l:Localize MergeRoads}"
Command="{Binding MergeRoadsCommand}" />
<MenuItem Header="LoadPresetLayout"
Command="{Binding LoadPresetLayoutCommand}" />
<Separator />
<MenuItem Header="{l:Localize RegisterFileExtension}"
Command="{Binding RegisterExtensionCommand}" />
Expand Down
Loading

0 comments on commit f2dc472

Please sign in to comment.