From 034d7401d1b7fd57ba9361aee82a37fde4717c21 Mon Sep 17 00:00:00 2001 From: Brett Hewitson Date: Thu, 2 Dec 2021 17:23:26 +1000 Subject: [PATCH] Globalisation Changes --- .../ResourceDictionaryChangedEventArgs.cs | 14 +++ src/Plugin.Common/PluginHelper.cs | 17 +++ src/Plugin.Common/Utils/ResourceUtils.cs | 111 ++++++++++++++++++ .../DiscordPluginUnitTest.cs | 26 ++++ .../Globalization/en-US/en-US.xaml | 12 ++ src/Plugin.Discord/Models/ComboBoxItemList.cs | 2 +- .../Models/SortableObservableCollection.cs | 84 +++++++++++++ src/Plugin.Discord/Plugin.Discord.csproj | 6 +- src/Plugin.Discord/Properties/AssemblyInfo.cs | 4 +- src/Plugin.Discord/Utils/WindowUtils.cs | 28 +---- .../Windows/ConfigProfileWindow.xaml | 12 +- .../Windows/ConfigProfileWindow.xaml.cs | 63 +++++++++- .../Windows/ConfigWindow.xaml.cs | 39 +++++- .../Windows/VersionFeedWindow.xaml | 2 +- .../Windows/VersionFeedWindow.xaml.cs | 41 ++++++- 15 files changed, 410 insertions(+), 51 deletions(-) create mode 100644 src/Plugin.Common/Events/ResourceDictionaryChangedEventArgs.cs create mode 100644 src/Plugin.Discord/Models/SortableObservableCollection.cs diff --git a/src/Plugin.Common/Events/ResourceDictionaryChangedEventArgs.cs b/src/Plugin.Common/Events/ResourceDictionaryChangedEventArgs.cs new file mode 100644 index 00000000..94f068eb --- /dev/null +++ b/src/Plugin.Common/Events/ResourceDictionaryChangedEventArgs.cs @@ -0,0 +1,14 @@ +using System; + +namespace ServerManagerTool.Plugin.Common.Events +{ + public class ResourceDictionaryChangedEventArgs : EventArgs + { + public ResourceDictionaryChangedEventArgs(string languageCode) + { + LanguageCode = languageCode; + } + + public string LanguageCode; + } +} diff --git a/src/Plugin.Common/PluginHelper.cs b/src/Plugin.Common/PluginHelper.cs index 20c1ea03..0f1730bb 100644 --- a/src/Plugin.Common/PluginHelper.cs +++ b/src/Plugin.Common/PluginHelper.cs @@ -1,4 +1,5 @@ using ServerManagerTool.Plugin.Common.Delegates; +using ServerManagerTool.Plugin.Common.Events; using ServerManagerTool.Plugin.Common.Lib; using System; using System.Collections.Generic; @@ -15,6 +16,7 @@ namespace ServerManagerTool.Plugin.Common { private const string PLUGINFILE_FOLDER = "Plugins"; private const string PLUGINFILE_EXTENSION = "dll"; + public const string LANGUAGECODE_FALLBACK = "en-US"; private static volatile PluginHelper _instance; private static readonly object _syncLock = new object(); @@ -24,9 +26,12 @@ namespace ServerManagerTool.Plugin.Common private FetchProfilesDelegate _fetchProfilesCallback; private bool _disposed; + public EventHandler ResourceDictionaryChanged; + private PluginHelper() { BetaEnabled = false; + LanguageCode = LANGUAGECODE_FALLBACK; Plugins = new ObservableCollection(); } @@ -61,6 +66,12 @@ namespace ServerManagerTool.Plugin.Common set; } + public string LanguageCode + { + get; + set; + } + public ObservableCollection Plugins { get; @@ -236,6 +247,12 @@ namespace ServerManagerTool.Plugin.Common } } + public void OnResourceDictionaryChanged(string languageCode) + { + LanguageCode = languageCode; + ResourceDictionaryChanged?.Invoke(this, new ResourceDictionaryChangedEventArgs(languageCode)); + } + internal void OpenConfigForm(string pluginCode, Window owner) { if (Plugins == null) diff --git a/src/Plugin.Common/Utils/ResourceUtils.cs b/src/Plugin.Common/Utils/ResourceUtils.cs index 18b9ae62..649219d6 100644 --- a/src/Plugin.Common/Utils/ResourceUtils.cs +++ b/src/Plugin.Common/Utils/ResourceUtils.cs @@ -1,5 +1,10 @@ using System; +using System.IO; +using System.Linq; +using System.Reflection; using System.Windows; +using System.Windows.Controls; +using System.Windows.Markup; namespace ServerManagerTool.Plugin.Common { @@ -21,5 +26,111 @@ namespace ServerManagerTool.Plugin.Common } return null; } + + public static void RemoveExceptResourceDictionary(Window window, string dictionaryName) + { + if (window == null || string.IsNullOrWhiteSpace(dictionaryName)) + return; + + var dictionariesToRemove = window.Resources.MergedDictionaries.Where(d => !d.Source.OriginalString.Contains(dictionaryName)).ToList(); + if (dictionariesToRemove != null) + { + foreach (var dictionaryToRemove in dictionariesToRemove) + { + window.Resources.MergedDictionaries.Remove(dictionaryToRemove); + } + } + } + + public static void RemoveExceptResourceDictionary(UserControl control, string dictionaryName) + { + if (control == null || string.IsNullOrWhiteSpace(dictionaryName)) + return; + + var dictionariesToRemove = control.Resources.MergedDictionaries.Where(d => !d.Source.OriginalString.Contains(dictionaryName)).ToList(); + if (dictionariesToRemove != null) + { + foreach (var dictionaryToRemove in dictionariesToRemove) + { + control.Resources.MergedDictionaries.Remove(dictionaryToRemove); + } + } + } + + public static void RemoveResourceDictionary(Window window, string dictionaryName) + { + if (window == null || string.IsNullOrWhiteSpace(dictionaryName)) + return; + + var dictionaryToRemove = window.Resources.MergedDictionaries.FirstOrDefault(d => d.Source.OriginalString.Contains(dictionaryName)); + if (dictionaryToRemove != null) + { + window.Resources.MergedDictionaries.Remove(dictionaryToRemove); + } + } + + public static void RemoveResourceDictionary(UserControl control, string dictionaryName) + { + if (control == null || string.IsNullOrWhiteSpace(dictionaryName)) + return; + + var dictionaryToRemove = control.Resources.MergedDictionaries.FirstOrDefault(d => d.Source.OriginalString.Contains(dictionaryName)); + if (dictionaryToRemove != null) + { + control.Resources.MergedDictionaries.Remove(dictionaryToRemove); + } + } + + public static void UpdateResourceDictionary(Window window, string languageCode) + { + if (window == null) + return; + + RemoveExceptResourceDictionary(window, PluginHelper.LANGUAGECODE_FALLBACK); + + var assembly = Assembly.GetCallingAssembly(); + + var resourcePath = assembly.GetManifestResourceNames().FirstOrDefault(r => r.EndsWith($"{languageCode}.xaml")); + if (string.IsNullOrWhiteSpace(resourcePath)) + return; + + using (Stream stream = assembly.GetManifestResourceStream(resourcePath)) + { + using (StreamReader reader = new StreamReader(stream)) + { + var resourceDictionary = XamlReader.Load(reader.BaseStream) as ResourceDictionary; + if (resourceDictionary != null) + { + window.Resources.MergedDictionaries.Add(resourceDictionary); + } + } + } + } + + public static void UpdateResourceDictionary(UserControl control, string languageCode) + { + if (control == null) + return; + + RemoveExceptResourceDictionary(control, PluginHelper.LANGUAGECODE_FALLBACK); + + var assembly = Assembly.GetCallingAssembly(); + + var resourcePath = assembly.GetManifestResourceNames().FirstOrDefault(r => r.EndsWith($"{languageCode}.xaml")); + if (string.IsNullOrWhiteSpace(resourcePath)) + return; + + using (Stream stream = assembly.GetManifestResourceStream(resourcePath)) + { + using (StreamReader reader = new StreamReader(stream)) + { + var resourceDictionary = XamlReader.Load(reader.BaseStream) as ResourceDictionary; + if (resourceDictionary != null) + { + control.Resources.MergedDictionaries.Add(resourceDictionary); + } + } + } + } } } diff --git a/src/Plugin.Discord.UnitTests/DiscordPluginUnitTest.cs b/src/Plugin.Discord.UnitTests/DiscordPluginUnitTest.cs index ff62a4b9..d4f7ec56 100644 --- a/src/Plugin.Discord.UnitTests/DiscordPluginUnitTest.cs +++ b/src/Plugin.Discord.UnitTests/DiscordPluginUnitTest.cs @@ -305,6 +305,32 @@ namespace Plugin.Discord.UnitTests Assert.IsTrue(true); } + [TestMethod] + public void DiscordPlugin_OpenConfigForm_WithDifferentLanguage() + { + // Arrange + ServerManagerTool.Plugin.Common.PluginHelper.Instance.SetFetchProfileCallback(FetchProfiles); + + var plugin = new DiscordPlugin(); + plugin.BetaEnabled = true; + plugin.Initialize(); + + // Act + plugin.OpenConfigForm(null); + + // Assert + Assert.IsTrue(true); + + // Arrange + ServerManagerTool.Plugin.Common.PluginHelper.Instance.OnResourceDictionaryChanged("zh-CN"); + + // Act + plugin.OpenConfigForm(null); + + // Assert + Assert.IsTrue(true); + } + private IList FetchProfiles() { return new List() diff --git a/src/Plugin.Discord/Globalization/en-US/en-US.xaml b/src/Plugin.Discord/Globalization/en-US/en-US.xaml index 7e86cb25..9783fead 100644 --- a/src/Plugin.Discord/Globalization/en-US/en-US.xaml +++ b/src/Plugin.Discord/Globalization/en-US/en-US.xaml @@ -4,6 +4,18 @@ xmlns:sys="clr-namespace:System;assembly=mscorlib" > + + Backup + Error + Mod Update Detected + Shutdown + Shutdown Message + Shutdown Reason + Server Status Change + Startup + Update Results + + Cancel Close diff --git a/src/Plugin.Discord/Models/ComboBoxItemList.cs b/src/Plugin.Discord/Models/ComboBoxItemList.cs index eb0c490c..bc4a4ebd 100644 --- a/src/Plugin.Discord/Models/ComboBoxItemList.cs +++ b/src/Plugin.Discord/Models/ComboBoxItemList.cs @@ -2,7 +2,7 @@ namespace ServerManagerTool.Plugin.Discord { - public class ComboBoxItemList : ObservableCollection + public class ComboBoxItemList : SortableObservableCollection { public ComboBoxItemList() { diff --git a/src/Plugin.Discord/Models/SortableObservableCollection.cs b/src/Plugin.Discord/Models/SortableObservableCollection.cs new file mode 100644 index 00000000..f9221664 --- /dev/null +++ b/src/Plugin.Discord/Models/SortableObservableCollection.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; + +namespace ServerManagerTool.Plugin.Discord +{ + /// + /// Represents a dynamic data collection that provides notifications when items get added, removed, or when the whole list is refreshed and allows sorting. + /// + /// The type of elements in the collection. + public class SortableObservableCollection : ObservableCollection + { + public SortableObservableCollection() + { + } + + public SortableObservableCollection(List list) + : base(list) + { + } + + public SortableObservableCollection(IEnumerable collection) + : base(collection) + { + } + + /// + /// Sorts the items of the collection in ascending order according to a key. + /// + /// The type of the key returned by . + /// A function to extract a key from an item. + public void Sort(Func keySelector) + { + InternalSort(Items.OrderBy(keySelector)); + } + + /// + /// Sorts the items of the collection in ascending order according to a key. + /// + /// The type of the key returned by . + /// A function to extract a key from an item. + /// An to compare keys. + public void Sort(Func keySelector, IComparer comparer) + { + InternalSort(Items.OrderBy(keySelector, comparer)); + } + + /// + /// Sorts the items of the collection in descending order according to a key. + /// + /// The type of the key returned by . + /// A function to extract a key from an item. + public void SortDescending(Func keySelector) + { + InternalSort(Items.OrderByDescending(keySelector)); + } + + /// + /// Sorts the items of the collection in descending order according to a key. + /// + /// The type of the key returned by . + /// A function to extract a key from an item. + /// An to compare keys. + public void SortDescending(Func keySelector, IComparer comparer) + { + InternalSort(Items.OrderByDescending(keySelector, comparer)); + } + + /// + /// Moves the items of the collection so that their orders are the same as those of the items provided. + /// + /// An to provide item orders. + private void InternalSort(IEnumerable sortedItems) + { + var sortedItemsList = sortedItems.ToList(); + + foreach (var item in sortedItemsList) + { + Move(IndexOf(item), sortedItemsList.IndexOf(item)); + } + } + } +} diff --git a/src/Plugin.Discord/Plugin.Discord.csproj b/src/Plugin.Discord/Plugin.Discord.csproj index b0530f7b..bc06558f 100644 --- a/src/Plugin.Discord/Plugin.Discord.csproj +++ b/src/Plugin.Discord/Plugin.Discord.csproj @@ -65,10 +65,8 @@ - - - - + + diff --git a/src/Plugin.Discord/Properties/AssemblyInfo.cs b/src/Plugin.Discord/Properties/AssemblyInfo.cs index d7975adb..d314842d 100644 --- a/src/Plugin.Discord/Properties/AssemblyInfo.cs +++ b/src/Plugin.Discord/Properties/AssemblyInfo.cs @@ -30,5 +30,5 @@ using System.Runtime.InteropServices; // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: -[assembly: AssemblyVersion("1.0.17.2")] -[assembly: AssemblyFileVersion("1.0.17.2")] +[assembly: AssemblyVersion("1.0.18.1")] +[assembly: AssemblyFileVersion("1.0.18.1")] diff --git a/src/Plugin.Discord/Utils/WindowUtils.cs b/src/Plugin.Discord/Utils/WindowUtils.cs index 6c765945..d8f76b31 100644 --- a/src/Plugin.Discord/Utils/WindowUtils.cs +++ b/src/Plugin.Discord/Utils/WindowUtils.cs @@ -1,6 +1,4 @@ -using System.Linq; -using System.Windows; -using System.Windows.Controls; +using System.Windows; using System.Windows.Data; using System.Windows.Media; @@ -8,30 +6,6 @@ namespace ServerManagerTool.Plugin.Discord { public static class WindowUtils { - public static void RemoveDefaultResourceDictionary(Window window, string defaultDictionary) - { - if (window == null) - return; - - var dictToRemove = window.Resources.MergedDictionaries.FirstOrDefault(d => d.Source.OriginalString.Contains(defaultDictionary)); - if (dictToRemove != null) - { - window.Resources.MergedDictionaries.Remove(dictToRemove); - } - } - - public static void RemoveDefaultResourceDictionary(UserControl control, string defaultDictionary) - { - if (control == null) - return; - - var dictToRemove = control.Resources.MergedDictionaries.FirstOrDefault(d => d.Source.OriginalString.Contains(defaultDictionary)); - if (dictToRemove != null) - { - control.Resources.MergedDictionaries.Remove(dictToRemove); - } - } - /// /// Finds a parent of a given item on the visual tree. /// diff --git a/src/Plugin.Discord/Windows/ConfigProfileWindow.xaml b/src/Plugin.Discord/Windows/ConfigProfileWindow.xaml index d1ce9835..f11bbbd7 100644 --- a/src/Plugin.Discord/Windows/ConfigProfileWindow.xaml +++ b/src/Plugin.Discord/Windows/ConfigProfileWindow.xaml @@ -1,11 +1,11 @@  + Width="640" Height="520" MinWidth="640" MinHeight="480" ResizeMode="CanResizeWithGrip" WindowStyle="ToolWindow" WindowStartupLocation="CenterOwner" ShowInTaskbar="False" + Closing="ConfigProfileWindow_Closing"> @@ -18,12 +18,6 @@ - - - - - -