From 8a9eef94ae8cb083ba714a7466466e26009efe94 Mon Sep 17 00:00:00 2001 From: David Palmer Date: Mon, 29 Jul 2024 22:20:38 -0500 Subject: [PATCH 01/19] Bumped project version to 0.4.3-alpha. --- Horizon/Horizon.csproj | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Horizon/Horizon.csproj b/Horizon/Horizon.csproj index a63faa9..1edccdc 100644 --- a/Horizon/Horizon.csproj +++ b/Horizon/Horizon.csproj @@ -7,7 +7,7 @@ enable latest true - 0.4.2 + 0.4.3 alpha false false diff --git a/README.md b/README.md index bf678ba..6634741 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![.NET Core Desktop](https://github.com/TheHeadmaster/Horizon/actions/workflows/dotnet-desktop.yml/badge.svg)](https://github.com/TheHeadmaster/Horizon/actions/workflows/dotnet-desktop.yml) ![Version][versionimage] ![GitHub (Pre-)Release Date](https://img.shields.io/github/release-date-pre/TheHeadmaster/Horizon?color=%23aa00aa) ![GitHub last commit (branch)](https://img.shields.io/github/last-commit/TheHeadmaster/Horizon/0.4.2-alpha?color=%23aa00aa&label=dev%20activity) [![Github All Releases](https://img.shields.io/github/downloads/TheHeadmaster/Horizon/total.svg)]() ![GitHub issues](https://img.shields.io/github/issues-raw/TheHeadmaster/Horizon) ![GitHub closed issues](https://img.shields.io/github/issues-closed-raw/TheHeadmaster/Horizon) ![GitHub](https://img.shields.io/github/license/TheHeadmaster/Horizon) +[![.NET Core Desktop](https://github.com/TheHeadmaster/Horizon/actions/workflows/dotnet-desktop.yml/badge.svg)](https://github.com/TheHeadmaster/Horizon/actions/workflows/dotnet-desktop.yml) ![Version][versionimage] ![GitHub (Pre-)Release Date](https://img.shields.io/github/release-date-pre/TheHeadmaster/Horizon?color=%23aa00aa) ![GitHub last commit (branch)](https://img.shields.io/github/last-commit/TheHeadmaster/Horizon/0.4.3-alpha?color=%23aa00aa&label=dev%20activity) [![Github All Releases](https://img.shields.io/github/downloads/TheHeadmaster/Horizon/total.svg)]() ![GitHub issues](https://img.shields.io/github/issues-raw/TheHeadmaster/Horizon) ![GitHub closed issues](https://img.shields.io/github/issues-closed-raw/TheHeadmaster/Horizon) ![GitHub](https://img.shields.io/github/license/TheHeadmaster/Horizon) [versionimage]: https://img.shields.io/github/v/release/TheHeadmaster/Horizon?color=%23aa00aa&include_prereleases&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAD6sAAA+rASlZpX8AAAIWSURBVFhHtZbNKwZBHMcfLxclR1EOLhwoKYqLUnLxUo6PuDiLlOTOxdHJwenJv8BBkjh6uUm5KMoBOchByuv3u7u/NTvP7OM3y37q087+Zmd+s7Mzu1vwYA5+KTyC/8orlM41XEGf61MZhezkODjzZwyy/UNw5skb/PMdRHjPhncDBZtQ1ec7/O/kwhqs2PcIzCu58Aw/w2I5vyU/hSdhMUF9dNTCPLVh8QduNXO1T0JOmQkb2oPcg8NhUU01LLtZ191Lwku4A6sg2YAvUOqzwHbxLMxDV0eMLUEmZnkdrkblRtgRlbMwAOO2aZ0wwV1YdMJ2HJzMjC9BXj4Pgc98Gc5AVk7AJpgGE9/AC3gAb6EMygtzBliW89noWIm66Gi208Lr+6RgMh0dtbTA/bDoBXdQyXwEQk901DIFh8KiF+eQ6yyePpF3Y8d+s9UR07gtW0yogXxVmjENsvB82xXNAfTCNtgOV6C2swZ4DbkL+O3vhM1QQzBwJnK55YjZPjpiPgbwH85VydENWjFTvhmJq05rTKXKRcj9vgsPIRccX6VdkLjaahyHMXblB+y2YoIZ40eJz9uMaS3DrLy3zqn8btvxLDrfslzBrotNnxyxLMbI/hUSlTmRyGkPgOQ5iLJ8rgGQPAbhzOX6GBFenPr3moG0G00dAOF3gf6FBZia3Af+QMoK1ph4yeRBPyzBM7gNizADhcI3BycuWIMMM8IAAAAASUVORK5CYII= From f96964365388ded009bb35bfd2b7a8312a80c0d9 Mon Sep 17 00:00:00 2001 From: David Palmer Date: Tue, 30 Jul 2024 02:47:13 -0500 Subject: [PATCH 02/19] Added multiple converters. #22 #23 --- Horizon/Converters/ActiveDocumentConverter.cs | 28 +++++++++++++ .../Converters/BoolToVisibilityConverter.cs | 29 ++++++++++++++ Horizon/Converters/EnumToStringConverter.cs | 28 +++++++++++++ .../StringToImageSourceConverter.cs | 40 +++++++++++++++++++ Horizon/Resources/UI/Converters.xaml | 7 +++- 5 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 Horizon/Converters/ActiveDocumentConverter.cs create mode 100644 Horizon/Converters/BoolToVisibilityConverter.cs create mode 100644 Horizon/Converters/EnumToStringConverter.cs create mode 100644 Horizon/Converters/StringToImageSourceConverter.cs diff --git a/Horizon/Converters/ActiveDocumentConverter.cs b/Horizon/Converters/ActiveDocumentConverter.cs new file mode 100644 index 0000000..165b50a --- /dev/null +++ b/Horizon/Converters/ActiveDocumentConverter.cs @@ -0,0 +1,28 @@ +using Horizon.ViewModel.Panes; +using ReactiveUI; + +namespace Horizon.Converters; + +/// +/// Converts an to an active document binding. +/// +public sealed class ActiveDocumentConverter : IBindingTypeConverter +{ + /// + public int GetAffinityForObjects(Type fromType, Type toType) => 0; + + /// + public bool TryConvert(object? from, Type toType, object? conversionHint, out object? result) + { + if (from is PageViewModel) + { + result = from; + return true; + } + else + { + result = null; + return false; + } + } +} \ No newline at end of file diff --git a/Horizon/Converters/BoolToVisibilityConverter.cs b/Horizon/Converters/BoolToVisibilityConverter.cs new file mode 100644 index 0000000..3a71703 --- /dev/null +++ b/Horizon/Converters/BoolToVisibilityConverter.cs @@ -0,0 +1,29 @@ +using System.Globalization; +using System.Windows; +using System.Windows.Data; + +namespace Horizon.Converters; + +/// +/// Converts a to a and back. +/// +public sealed class BoolToVisibilityConverter : IValueConverter +{ + /// + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + try + { + Visibility boolVisibility = (bool)value ? Visibility.Visible : Visibility.Collapsed; + return boolVisibility; + } + catch + { + return Visibility.Collapsed; + } + } + + // No need to implement converting back on a one-way binding + /// + public object? ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => null; +} \ No newline at end of file diff --git a/Horizon/Converters/EnumToStringConverter.cs b/Horizon/Converters/EnumToStringConverter.cs new file mode 100644 index 0000000..b2ea97b --- /dev/null +++ b/Horizon/Converters/EnumToStringConverter.cs @@ -0,0 +1,28 @@ +using System.Globalization; +using System.Windows.Data; + +namespace Horizon.Converters; + +/// +/// Converts an to a and back. +/// +public sealed class EnumToStringConverter : IValueConverter +{ + /// + public object? Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + try + { + string EnumString = Enum.GetName(value.GetType(), value) ?? string.Empty; + return EnumString; + } + catch + { + return string.Empty; + } + } + + // No need to implement converting back on a one-way binding + /// + public object? ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => null; +} \ No newline at end of file diff --git a/Horizon/Converters/StringToImageSourceConverter.cs b/Horizon/Converters/StringToImageSourceConverter.cs new file mode 100644 index 0000000..124acf8 --- /dev/null +++ b/Horizon/Converters/StringToImageSourceConverter.cs @@ -0,0 +1,40 @@ +using System.Globalization; +using System.Windows.Data; +using System.Windows.Media.Imaging; + +namespace Horizon.Converters; + +/// +/// Converts a path to an Source and back. +/// +public class StringToImageSourceConverter : IValueConverter +{ + /// + public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) + { + string? path = value as string; + if (string.IsNullOrWhiteSpace(path)) + { + return null; + } + + BitmapImage src = new(); + + try + { + src.BeginInit(); + src.UriSource = new Uri(path); + src.EndInit(); + } + catch (UriFormatException) + { + return null; + } + + return src; + } + + // No need to implement converting back on a one-way binding + /// + public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) => null; +} \ No newline at end of file diff --git a/Horizon/Resources/UI/Converters.xaml b/Horizon/Resources/UI/Converters.xaml index 42f1cd1..3085e98 100644 --- a/Horizon/Resources/UI/Converters.xaml +++ b/Horizon/Resources/UI/Converters.xaml @@ -1,3 +1,8 @@  + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:converters="clr-namespace:Horizon.Converters"> + + + + \ No newline at end of file From d4a9d2f301e0c48af4b267f407c55bddf3428ec4 Mon Sep 17 00:00:00 2001 From: David Palmer Date: Tue, 30 Jul 2024 02:49:15 -0500 Subject: [PATCH 03/19] Added various images that will be used for icons. #22 #23 --- Horizon/Resources/Images/New.png | Bin 0 -> 423 bytes Horizon/Resources/Images/Open Small.png | Bin 0 -> 271 bytes Horizon/Resources/Images/Open.png | Bin 0 -> 662 bytes Horizon/Resources/Images/Read Only.png | Bin 0 -> 5576 bytes 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 Horizon/Resources/Images/New.png create mode 100644 Horizon/Resources/Images/Open Small.png create mode 100644 Horizon/Resources/Images/Open.png create mode 100644 Horizon/Resources/Images/Read Only.png diff --git a/Horizon/Resources/Images/New.png b/Horizon/Resources/Images/New.png new file mode 100644 index 0000000000000000000000000000000000000000..84c799adfdd90d874a7dd7f71b7014746a84216b GIT binary patch literal 423 zcmV;Y0a*TtP)?K@^1tu}xtuirOf4R{9S50KSEfur^`#+#h2X z>{E!nrC9p}wpK#ARH71$T%9#z5EA1|qOu1r40q1#{pM$9*ST7?exrzVkR$#cfJ>w- z07X$OGV7s*wPywZpaIAcpm&uIkb6Lnz4uGm07PU3P$tseu&~}*MEl$$5;AX603z)G z@*EYu2IQb9iUkX6TP|7=^LA1K42B^$Kx=b!jODEFt5cs6;3>?oZ!;8;EsL;1{n(QQ zpe#>UiKHEad@d~QcTXk&7Zib1+wxX;((ykA_-_9H05J3JIDoR$z2pFZcEh22A<}bZ zULW;&f3jhH!DDwtHz5ETMA`)8A%HxFTd_KXV!6;~o~DgS22e>=mx=T&di6dc%>2Up z)|$)xHDu;(BDsvN<|`4|nf7^84`Akfi*dee0F!EJ0GjtN8O;lzRxLL}y#jkC2It1C R0VMzc002ovPDHLkV1gd?t=j+q literal 0 HcmV?d00001 diff --git a/Horizon/Resources/Images/Open Small.png b/Horizon/Resources/Images/Open Small.png new file mode 100644 index 0000000000000000000000000000000000000000..c16115af4647a9cfe179fc1e1b2a783f76b9c872 GIT binary patch literal 271 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+1|-AI^@Rf|&H|6fVg?3oVGw3ym^DWND0tA* z#WAE}&f5!yT!$P4SOZvf->`TeG?vKO(|GIVjgIgQo2N#!&Yk~rjRMEU3(+?!*!)#C z2^l!5W^^DDCQG6trcqnYd=@gbu!ZzgnTWOxt*0*xchjTu@M+C+E?^{X8bJ#iZ@- znezQjE<3)}?q_;o_U?XTnbIBg+C&cipU!`l+AG~;u{bqbA;Q#Xq5l=uz14eUV+}=j Q0X@Uu>FVdQ&MBb@07m*^J^%m! literal 0 HcmV?d00001 diff --git a/Horizon/Resources/Images/Open.png b/Horizon/Resources/Images/Open.png new file mode 100644 index 0000000000000000000000000000000000000000..6a3b96d349afd4c00d47d82a8defccc734b3f6af GIT binary patch literal 662 zcmV;H0%`q;P)`-mq_Gj~QdtQ>5d&5c730V=t~{2P-F*v7@@DS`)6KB=oSk|1 z$2r3=j5(kviX(0wh%p}azPCu~H9+r^+6Q!<)E6!%VvGwSa-P(ir1*-^ZBoN8S4)QKFkmR$~D=UO)vpS+h#E*Aj3*VF)2J>hZ!4NL&!qstLM!-OWha=8&> zTmbalgg2o58K8=)9tCu5?lM40zu!Md>f^c004b#dq^yp(>c6wj0HnwW@xK7MHJlx$ z&H$XE*qHI^0CKmk)CJ@_sY`>{t0n;QG6Mr-wY>iS;#wkQt0M4g-5Vd6Fm(pto{Ro3 z@JUe=hpMrK5H0}v8PHGU%DLS%lqLXjyR`-Y`nqgy>*DiXE_b`V)8ll65RSFf0C@}G zc)j@EA|M-632TP|^1jn~aI!A0^7@mqS?;?4*zzU z`vG}0?ratYaOeH|YT*LN^GVKSWPtuPp=?_Lc{Aym%nZ=7^!?Ige|842;W7he3HQN2 zY&U@R1d@FNXdl63S%8)XlhpyVLMT}!NGpYt^#ZhJC|Ng1YloA~184)0WCM{|!hJCD wv4IF}Ad+k#LK}!A8;EcZ1|o)G7>3KAKb@0Vm4~IkDgXcg07*qoM6N<$f)q9;LI3~& literal 0 HcmV?d00001 diff --git a/Horizon/Resources/Images/Read Only.png b/Horizon/Resources/Images/Read Only.png new file mode 100644 index 0000000000000000000000000000000000000000..fab85056f05ae8c6816c499d44942dc6c882963d GIT binary patch literal 5576 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 Date: Tue, 30 Jul 2024 02:49:15 -0500 Subject: [PATCH 04/19] Added various images that will be used for icons. #22 #23 --- Horizon/Horizon.csproj | 8 ++++++++ Horizon/Resources/Images/New.png | Bin 0 -> 423 bytes Horizon/Resources/Images/Open Small.png | Bin 0 -> 271 bytes Horizon/Resources/Images/Open.png | Bin 0 -> 662 bytes Horizon/Resources/Images/Read Only.png | Bin 0 -> 5576 bytes 5 files changed, 8 insertions(+) create mode 100644 Horizon/Resources/Images/New.png create mode 100644 Horizon/Resources/Images/Open Small.png create mode 100644 Horizon/Resources/Images/Open.png create mode 100644 Horizon/Resources/Images/Read Only.png diff --git a/Horizon/Horizon.csproj b/Horizon/Horizon.csproj index 1edccdc..03f1c2e 100644 --- a/Horizon/Horizon.csproj +++ b/Horizon/Horizon.csproj @@ -31,6 +31,10 @@ + + + + @@ -82,6 +86,10 @@ + + + + diff --git a/Horizon/Resources/Images/New.png b/Horizon/Resources/Images/New.png new file mode 100644 index 0000000000000000000000000000000000000000..84c799adfdd90d874a7dd7f71b7014746a84216b GIT binary patch literal 423 zcmV;Y0a*TtP)?K@^1tu}xtuirOf4R{9S50KSEfur^`#+#h2X z>{E!nrC9p}wpK#ARH71$T%9#z5EA1|qOu1r40q1#{pM$9*ST7?exrzVkR$#cfJ>w- z07X$OGV7s*wPywZpaIAcpm&uIkb6Lnz4uGm07PU3P$tseu&~}*MEl$$5;AX603z)G z@*EYu2IQb9iUkX6TP|7=^LA1K42B^$Kx=b!jODEFt5cs6;3>?oZ!;8;EsL;1{n(QQ zpe#>UiKHEad@d~QcTXk&7Zib1+wxX;((ykA_-_9H05J3JIDoR$z2pFZcEh22A<}bZ zULW;&f3jhH!DDwtHz5ETMA`)8A%HxFTd_KXV!6;~o~DgS22e>=mx=T&di6dc%>2Up z)|$)xHDu;(BDsvN<|`4|nf7^84`Akfi*dee0F!EJ0GjtN8O;lzRxLL}y#jkC2It1C R0VMzc002ovPDHLkV1gd?t=j+q literal 0 HcmV?d00001 diff --git a/Horizon/Resources/Images/Open Small.png b/Horizon/Resources/Images/Open Small.png new file mode 100644 index 0000000000000000000000000000000000000000..c16115af4647a9cfe179fc1e1b2a783f76b9c872 GIT binary patch literal 271 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+1|-AI^@Rf|&H|6fVg?3oVGw3ym^DWND0tA* z#WAE}&f5!yT!$P4SOZvf->`TeG?vKO(|GIVjgIgQo2N#!&Yk~rjRMEU3(+?!*!)#C z2^l!5W^^DDCQG6trcqnYd=@gbu!ZzgnTWOxt*0*xchjTu@M+C+E?^{X8bJ#iZ@- znezQjE<3)}?q_;o_U?XTnbIBg+C&cipU!`l+AG~;u{bqbA;Q#Xq5l=uz14eUV+}=j Q0X@Uu>FVdQ&MBb@07m*^J^%m! literal 0 HcmV?d00001 diff --git a/Horizon/Resources/Images/Open.png b/Horizon/Resources/Images/Open.png new file mode 100644 index 0000000000000000000000000000000000000000..6a3b96d349afd4c00d47d82a8defccc734b3f6af GIT binary patch literal 662 zcmV;H0%`q;P)`-mq_Gj~QdtQ>5d&5c730V=t~{2P-F*v7@@DS`)6KB=oSk|1 z$2r3=j5(kviX(0wh%p}azPCu~H9+r^+6Q!<)E6!%VvGwSa-P(ir1*-^ZBoN8S4)QKFkmR$~D=UO)vpS+h#E*Aj3*VF)2J>hZ!4NL&!qstLM!-OWha=8&> zTmbalgg2o58K8=)9tCu5?lM40zu!Md>f^c004b#dq^yp(>c6wj0HnwW@xK7MHJlx$ z&H$XE*qHI^0CKmk)CJ@_sY`>{t0n;QG6Mr-wY>iS;#wkQt0M4g-5Vd6Fm(pto{Ro3 z@JUe=hpMrK5H0}v8PHGU%DLS%lqLXjyR`-Y`nqgy>*DiXE_b`V)8ll65RSFf0C@}G zc)j@EA|M-632TP|^1jn~aI!A0^7@mqS?;?4*zzU z`vG}0?ratYaOeH|YT*LN^GVKSWPtuPp=?_Lc{Aym%nZ=7^!?Ige|842;W7he3HQN2 zY&U@R1d@FNXdl63S%8)XlhpyVLMT}!NGpYt^#ZhJC|Ng1YloA~184)0WCM{|!hJCD wv4IF}Ad+k#LK}!A8;EcZ1|o)G7>3KAKb@0Vm4~IkDgXcg07*qoM6N<$f)q9;LI3~& literal 0 HcmV?d00001 diff --git a/Horizon/Resources/Images/Read Only.png b/Horizon/Resources/Images/Read Only.png new file mode 100644 index 0000000000000000000000000000000000000000..fab85056f05ae8c6816c499d44942dc6c882963d GIT binary patch literal 5576 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 Date: Tue, 30 Jul 2024 02:50:33 -0500 Subject: [PATCH 05/19] Added GlobalSuppressions file. Added ignore severity of unsupported platform to .editorconfig. --- .editorconfig | 5 ++++- Horizon/GlobalSuppressions.cs | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 Horizon/GlobalSuppressions.cs diff --git a/.editorconfig b/.editorconfig index 46d128a..aad034a 100644 --- a/.editorconfig +++ b/.editorconfig @@ -130,4 +130,7 @@ csharp_style_prefer_extended_property_pattern = true:suggestion csharp_style_var_for_built_in_types = false:silent csharp_style_var_when_type_is_apparent = false:silent csharp_style_var_elsewhere = false:silent -csharp_space_around_binary_operators = before_and_after \ No newline at end of file +csharp_space_around_binary_operators = before_and_after + +# CA1416: Validate platform compatibility +dotnet_diagnostic.CA1416.severity = none \ No newline at end of file diff --git a/Horizon/GlobalSuppressions.cs b/Horizon/GlobalSuppressions.cs new file mode 100644 index 0000000..94cff40 --- /dev/null +++ b/Horizon/GlobalSuppressions.cs @@ -0,0 +1,2 @@ +// This file is used by Code Analysis to maintain SuppressMessage attributes that are applied to this project. Project-level +// suppressions either have no target or are given a specific target and scoped to a namespace, type, member, etc. \ No newline at end of file From 4f1ef71b0c05a4069c510d15c68a6db0b68d8e67 Mon Sep 17 00:00:00 2001 From: David Palmer Date: Tue, 30 Jul 2024 03:10:46 -0500 Subject: [PATCH 06/19] Fixed glyph text being slightly off. Fixed size of key binding visuals in main menu. #26 --- .../ControlDefinitions/SubmenuHeaderTemplateKeyDefinition.xaml | 2 +- .../UI/ControlDefinitions/SubmenuItemTemplateKeyDefinition.xaml | 2 +- Horizon/View/Controls/KeyBindingGlyph.xaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Horizon/Resources/UI/ControlDefinitions/SubmenuHeaderTemplateKeyDefinition.xaml b/Horizon/Resources/UI/ControlDefinitions/SubmenuHeaderTemplateKeyDefinition.xaml index c79f654..8050914 100644 --- a/Horizon/Resources/UI/ControlDefinitions/SubmenuHeaderTemplateKeyDefinition.xaml +++ b/Horizon/Resources/UI/ControlDefinitions/SubmenuHeaderTemplateKeyDefinition.xaml @@ -19,7 +19,7 @@ Grid.Column="1" ContentSource="Header" RecognizesAccessKey="True" VerticalAlignment="Center" /> - + - + diff --git a/Horizon/View/Controls/KeyBindingGlyph.xaml b/Horizon/View/Controls/KeyBindingGlyph.xaml index 38b2e8e..9de1106 100644 --- a/Horizon/View/Controls/KeyBindingGlyph.xaml +++ b/Horizon/View/Controls/KeyBindingGlyph.xaml @@ -14,7 +14,7 @@ - + From 634ee6e2dbb091bdfaf39f1487ba3ff8c30e39c8 Mon Sep 17 00:00:00 2001 From: David Palmer Date: Tue, 30 Jul 2024 03:11:24 -0500 Subject: [PATCH 07/19] Changed ProjectTemplate to a ReactiveObject and made all of the properties Reactive. --- Horizon/ObjectModel/ProjectTemplate.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Horizon/ObjectModel/ProjectTemplate.cs b/Horizon/ObjectModel/ProjectTemplate.cs index f354133..38d0de0 100644 --- a/Horizon/ObjectModel/ProjectTemplate.cs +++ b/Horizon/ObjectModel/ProjectTemplate.cs @@ -1,10 +1,16 @@ -namespace Horizon.ObjectModel; +using ReactiveUI; +using ReactiveUI.Fody.Helpers; -public class ProjectTemplate +namespace Horizon.ObjectModel; + +public class ProjectTemplate : ReactiveObject { - public string Name { get; set; } + [Reactive] + public string Name { get; set; } = string.Empty; - public string Description { get; set; } + [Reactive] + public string Description { get; set; } = string.Empty; + [Reactive] public List Tags { get; set; } = []; } \ No newline at end of file From 34ab9f5c11edad78b021189d234c23906d52a23e Mon Sep 17 00:00:00 2001 From: David Palmer Date: Tue, 30 Jul 2024 03:15:45 -0500 Subject: [PATCH 08/19] Added AppDataDirectory to App. Moved AvailableTemplates to AppViewModel. ViewModel in App renamed to CommandsViewModel. Added AppViewmodel as ViewModel. #22 #23 --- Horizon/App.xaml.cs | 29 +++++++++++++++---- Horizon/View/Controls/MainMenu.xaml.cs | 2 +- Horizon/View/Windows/Workspace.xaml.cs | 2 +- .../ViewModel/NewProjectWindowViewModel.cs | 4 ++- 4 files changed, 29 insertions(+), 8 deletions(-) diff --git a/Horizon/App.xaml.cs b/Horizon/App.xaml.cs index 0d77916..e0503e4 100644 --- a/Horizon/App.xaml.cs +++ b/Horizon/App.xaml.cs @@ -1,5 +1,4 @@ -using Horizon.ObjectModel; -using Horizon.View.Windows; +using Horizon.View.Windows; using Horizon.ViewModel; using ReactiveUI; using ReactiveUI.Fody.Helpers; @@ -53,17 +52,37 @@ public static string UserDirectory ?.GetCustomAttribute() ?.InformationalVersion ?? "unknown-alpha"; - /// + /// [Reactive] - public static CommandsViewModel ViewModel { get; } = new CommandsViewModel(); + public static CommandsViewModel CommandsViewModel { get; } = new(); - public static List AvailableTemplates { get; set; } = [new() { Name = "Starbound Mod Project", Description = "A mod project for the game Starbound", Tags = ["Starbound", "Mod"] }]; + [Reactive] + public static AppViewModel ViewModel { get; } = new(); + + public static string AppDataDirectory + { + get + { + if (appDataDirectory is null) + { + appDataDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Horizon"); + if (!Directory.Exists(appDataDirectory)) + { + Directory.CreateDirectory(appDataDirectory); + } + } + + return appDataDirectory; + } + } /// /// The workspace. /// private static Workspace? workspace; + private static string? appDataDirectory; + /// /// Gets all the startup tasks that run while the splash screen is displayed. /// diff --git a/Horizon/View/Controls/MainMenu.xaml.cs b/Horizon/View/Controls/MainMenu.xaml.cs index 5f80baa..65e862a 100644 --- a/Horizon/View/Controls/MainMenu.xaml.cs +++ b/Horizon/View/Controls/MainMenu.xaml.cs @@ -24,7 +24,7 @@ public MainMenu() this.NewProjectMenuItem.Events() .Click .Select(x => new Unit()) - .InvokeCommand(App.ViewModel, x => x.NewProjectDialog) + .InvokeCommand(App.CommandsViewModel, x => x.NewProjectDialog) .DisposeWith(dispose); }); } diff --git a/Horizon/View/Windows/Workspace.xaml.cs b/Horizon/View/Windows/Workspace.xaml.cs index 3016901..225fa2b 100644 --- a/Horizon/View/Windows/Workspace.xaml.cs +++ b/Horizon/View/Windows/Workspace.xaml.cs @@ -28,7 +28,7 @@ public Workspace() /// The disposable. private void Interactions(CompositeDisposable dispose) { - App.ViewModel + App.CommandsViewModel .NewProjectDialogInteraction .RegisterHandler(interaction => { diff --git a/Horizon/ViewModel/NewProjectWindowViewModel.cs b/Horizon/ViewModel/NewProjectWindowViewModel.cs index 169e7fc..2678de7 100644 --- a/Horizon/ViewModel/NewProjectWindowViewModel.cs +++ b/Horizon/ViewModel/NewProjectWindowViewModel.cs @@ -4,6 +4,7 @@ using Nito.Disposables.Internals; using ReactiveUI; using ReactiveUI.Fody.Helpers; +using System.Collections.ObjectModel; using System.IO; using System.Reactive.Linq; @@ -68,5 +69,6 @@ public NewProjectWindowViewModel() [Reactive] public ProjectFile Project { get; set; } = new() { Name = "Project Name", FilePath = Path.Combine(App.UserDirectory, "Project.horizon") }; - public List AvailableTemplates { get; init; } = []; + [Reactive] + public ObservableCollection AvailableTemplates { get; init; } = []; } \ No newline at end of file From 379944de38da89854e09bac9fcea402fa60f9aae Mon Sep 17 00:00:00 2001 From: TheHeadmaster <45703202+TheHeadmaster@users.noreply.github.com> Date: Tue, 30 Jul 2024 03:19:32 -0500 Subject: [PATCH 09/19] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6634741..cb81050 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![.NET Core Desktop](https://github.com/TheHeadmaster/Horizon/actions/workflows/dotnet-desktop.yml/badge.svg)](https://github.com/TheHeadmaster/Horizon/actions/workflows/dotnet-desktop.yml) ![Version][versionimage] ![GitHub (Pre-)Release Date](https://img.shields.io/github/release-date-pre/TheHeadmaster/Horizon?color=%23aa00aa) ![GitHub last commit (branch)](https://img.shields.io/github/last-commit/TheHeadmaster/Horizon/0.4.3-alpha?color=%23aa00aa&label=dev%20activity) [![Github All Releases](https://img.shields.io/github/downloads/TheHeadmaster/Horizon/total.svg)]() ![GitHub issues](https://img.shields.io/github/issues-raw/TheHeadmaster/Horizon) ![GitHub closed issues](https://img.shields.io/github/issues-closed-raw/TheHeadmaster/Horizon) ![GitHub](https://img.shields.io/github/license/TheHeadmaster/Horizon) +[![.NET Core Desktop](https://github.com/TheHeadmaster/Horizon/actions/workflows/dotnet-desktop.yml/badge.svg)](https://github.com/TheHeadmaster/Horizon/actions/workflows/dotnet-desktop.yml) ![Version][versionimage] ![GitHub (Pre-)Release Date](https://img.shields.io/github/release-date-pre/TheHeadmaster/Horizon?color=%23aa00aa) ![GitHub last commit (branch)](https://img.shields.io/github/last-commit/TheHeadmaster/Horizon/0.4.3-alpha?color=%23aa00aa&label=dev%20activity) [![Github All Releases](https://img.shields.io/github/downloads/TheHeadmaster/Horizon/total.svg)]() ![GitHub issues](https://img.shields.io/github/issues-raw/TheHeadmaster/Horizon) ![GitHub closed issues](https://img.shields.io/github/issues-closed-raw/TheHeadmaster/Horizon) ![GitHub](https://img.shields.io/github/license/TheHeadmaster/Horizon) [![](https://dcbadge.limes.pink/api/server/B37tBEFZzU)](https://discord.gg/B37tBEFZzU) [versionimage]: https://img.shields.io/github/v/release/TheHeadmaster/Horizon?color=%23aa00aa&include_prereleases&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAD6sAAA+rASlZpX8AAAIWSURBVFhHtZbNKwZBHMcfLxclR1EOLhwoKYqLUnLxUo6PuDiLlOTOxdHJwenJv8BBkjh6uUm5KMoBOchByuv3u7u/NTvP7OM3y37q087+Zmd+s7Mzu1vwYA5+KTyC/8orlM41XEGf61MZhezkODjzZwyy/UNw5skb/PMdRHjPhncDBZtQ1ec7/O/kwhqs2PcIzCu58Aw/w2I5vyU/hSdhMUF9dNTCPLVh8QduNXO1T0JOmQkb2oPcg8NhUU01LLtZ191Lwku4A6sg2YAvUOqzwHbxLMxDV0eMLUEmZnkdrkblRtgRlbMwAOO2aZ0wwV1YdMJ2HJzMjC9BXj4Pgc98Gc5AVk7AJpgGE9/AC3gAb6EMygtzBliW89noWIm66Gi208Lr+6RgMh0dtbTA/bDoBXdQyXwEQk901DIFh8KiF+eQ6yyePpF3Y8d+s9UR07gtW0yogXxVmjENsvB82xXNAfTCNtgOV6C2swZ4DbkL+O3vhM1QQzBwJnK55YjZPjpiPgbwH85VydENWjFTvhmJq05rTKXKRcj9vgsPIRccX6VdkLjaahyHMXblB+y2YoIZ40eJz9uMaS3DrLy3zqn8btvxLDrfslzBrotNnxyxLMbI/hUSlTmRyGkPgOQ5iLJ8rgGQPAbhzOX6GBFenPr3moG0G00dAOF3gf6FBZia3Af+QMoK1ph4yeRBPyzBM7gNizADhcI3BycuWIMMM8IAAAAASUVORK5CYII= From fdddaf921ae4659ad5ca3238bd558c98ac049e84 Mon Sep 17 00:00:00 2001 From: David Palmer Date: Tue, 30 Jul 2024 03:26:13 -0500 Subject: [PATCH 10/19] Fleshed out AvalonDock and pane system. Layout loads and saves automatically. Added Project Explorer. Name of project (or Horizon if no project is open) now displays at top of workspace window. #22 #23 #24 --- Horizon/View/Panes/PanesStyleSelector.cs | 23 ++++ Horizon/View/Panes/PanesTemplateSelector.cs | 17 +++ Horizon/View/Panes/ProjectExplorerPane.xaml | 13 ++ .../View/Panes/ProjectExplorerPane.xaml.cs | 21 +++ Horizon/View/Windows/NewProjectWindow.xaml.cs | 2 +- Horizon/View/Windows/Workspace.xaml | 89 +++++++++++- Horizon/View/Windows/Workspace.xaml.cs | 81 ++++++++++- Horizon/ViewModel/AppViewModel.cs | 15 ++ Horizon/ViewModel/LayoutInitializer.cs | 39 ++++++ Horizon/ViewModel/Panes/PageViewModel.cs | 32 +++++ Horizon/ViewModel/Panes/PaneViewModel.cs | 22 +++ .../Panes/ProjectExplorerViewModel.cs | 29 ++++ Horizon/ViewModel/Panes/ToolViewModel.cs | 16 +++ Horizon/ViewModel/WorkspaceViewModel.cs | 130 +++++++++++++++++- 14 files changed, 519 insertions(+), 10 deletions(-) create mode 100644 Horizon/View/Panes/PanesStyleSelector.cs create mode 100644 Horizon/View/Panes/PanesTemplateSelector.cs create mode 100644 Horizon/View/Panes/ProjectExplorerPane.xaml create mode 100644 Horizon/View/Panes/ProjectExplorerPane.xaml.cs create mode 100644 Horizon/ViewModel/AppViewModel.cs create mode 100644 Horizon/ViewModel/LayoutInitializer.cs create mode 100644 Horizon/ViewModel/Panes/PageViewModel.cs create mode 100644 Horizon/ViewModel/Panes/PaneViewModel.cs create mode 100644 Horizon/ViewModel/Panes/ProjectExplorerViewModel.cs create mode 100644 Horizon/ViewModel/Panes/ToolViewModel.cs diff --git a/Horizon/View/Panes/PanesStyleSelector.cs b/Horizon/View/Panes/PanesStyleSelector.cs new file mode 100644 index 0000000..a9d6796 --- /dev/null +++ b/Horizon/View/Panes/PanesStyleSelector.cs @@ -0,0 +1,23 @@ +using Horizon.ViewModel.Panes; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; + +namespace Horizon.View.Panes; + +public sealed class PanesStyleSelector : StyleSelector +{ + public Style? ToolStyle { get; set; } + public Style? PageStyle { get; set; } + + public override Style SelectStyle(object item, DependencyObject container) => item switch + { + ToolViewModel => this.ToolStyle ?? base.SelectStyle(item, container), + PageViewModel => this.PageStyle ?? base.SelectStyle(item, container), + _ => base.SelectStyle(item, container) + }; +} \ No newline at end of file diff --git a/Horizon/View/Panes/PanesTemplateSelector.cs b/Horizon/View/Panes/PanesTemplateSelector.cs new file mode 100644 index 0000000..8255fdf --- /dev/null +++ b/Horizon/View/Panes/PanesTemplateSelector.cs @@ -0,0 +1,17 @@ +using Horizon.ViewModel.Panes; +using System.Windows; +using System.Windows.Controls; + +namespace Horizon.View.Panes; + +public sealed class PanesTemplateSelector : DataTemplateSelector +{ + public DataTemplate? ProjectExplorerViewTemplate { get; set; } + + public override DataTemplate? SelectTemplate(object item, DependencyObject container) => + item switch + { + ProjectExplorerViewModel => this.ProjectExplorerViewTemplate, + _ => base.SelectTemplate(item, container) + }; +} \ No newline at end of file diff --git a/Horizon/View/Panes/ProjectExplorerPane.xaml b/Horizon/View/Panes/ProjectExplorerPane.xaml new file mode 100644 index 0000000..a6bbd8d --- /dev/null +++ b/Horizon/View/Panes/ProjectExplorerPane.xaml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/Horizon/View/Panes/ProjectExplorerPane.xaml.cs b/Horizon/View/Panes/ProjectExplorerPane.xaml.cs new file mode 100644 index 0000000..cdcd887 --- /dev/null +++ b/Horizon/View/Panes/ProjectExplorerPane.xaml.cs @@ -0,0 +1,21 @@ +using Horizon.ViewModel.Panes; +using ReactiveUI; + +namespace Horizon.View.Panes; + +/// +/// Interaction logic for ProjectExplorerPane.xaml +/// +public partial class ProjectExplorerPane +{ + public ProjectExplorerPane() + { + this.InitializeComponent(); + + this.ViewModel = new ProjectExplorerViewModel(); + + this.WhenActivated(dispose => + { + }); + } +} \ No newline at end of file diff --git a/Horizon/View/Windows/NewProjectWindow.xaml.cs b/Horizon/View/Windows/NewProjectWindow.xaml.cs index 5372b00..be46eb5 100644 --- a/Horizon/View/Windows/NewProjectWindow.xaml.cs +++ b/Horizon/View/Windows/NewProjectWindow.xaml.cs @@ -18,7 +18,7 @@ public NewProjectWindow() this.ViewModel = new() { - AvailableTemplates = App.AvailableTemplates + AvailableTemplates = App.ViewModel.AvailableTemplates }; this.WhenActivated(dispose => diff --git a/Horizon/View/Windows/Workspace.xaml b/Horizon/View/Windows/Workspace.xaml index e5aebdf..dac5449 100644 --- a/Horizon/View/Windows/Workspace.xaml +++ b/Horizon/View/Windows/Workspace.xaml @@ -3,15 +3,92 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:panes="clr-namespace:Horizon.View.Panes" xmlns:windows="clr-namespace:Horizon.View.Windows" xmlns:vm="clr-namespace:Horizon.ViewModel" + xmlns:xcad="https://github.com/Dirkster99/AvalonDock" d:DataContext="{d:DesignInstance Type=vm:WorkspaceViewModel}" mc:Ignorable="d" Width="1366" Height="768" WindowStartupLocation="CenterScreen" WindowState="Maximized" Background="{StaticResource BaseBackgroundColor}" FontFamily="Cascadia Mono" Style="{StaticResource BorderlessWindowStyle}"> - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Horizon/View/Windows/Workspace.xaml.cs b/Horizon/View/Windows/Workspace.xaml.cs index 225fa2b..1357455 100644 --- a/Horizon/View/Windows/Workspace.xaml.cs +++ b/Horizon/View/Windows/Workspace.xaml.cs @@ -1,6 +1,14 @@ -using ReactiveUI; +using AvalonDock; +using Horizon.Converters; +using ReactiveMarbles.ObservableEvents; +using ReactiveUI; +using System.Reactive; using System.Reactive.Disposables; using System.Reactive.Linq; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using EventExtensions = System.Windows.Controls.EventExtensions; namespace Horizon.View.Windows; @@ -18,10 +26,79 @@ public Workspace() this.WhenActivated(dispose => { + this.PropertyBindings(dispose); + this.EventBindings(dispose); + this.KeyBindings(dispose); this.Interactions(dispose); }); } + /// + /// Binds view model properties to the view. + /// + /// The disposable. + private void PropertyBindings(CompositeDisposable dispose) + { + this.OneWayBind(this.ViewModel, vm => vm.Title, view => view.Title) + .DisposeWith(dispose); + + this.OneWayBind(this.ViewModel, vm => vm.Tools, view => view.Dock.AnchorablesSource) + .DisposeWith(dispose); + + this.OneWayBind(this.ViewModel, vm => vm.Pages, view => view.Dock.DocumentsSource) + .DisposeWith(dispose); + + this.Bind(this.ViewModel, vm => vm.ActiveDocument, view => view.Dock.ActiveContent, + vmToViewConverterOverride: new ActiveDocumentConverter(), + viewToVMConverterOverride: new ActiveDocumentConverter()) + .DisposeWith(dispose); + } + + /// + /// Binds view model commands to view events. + /// + /// The disposable. + private void EventBindings(CompositeDisposable dispose) + { + this.Dock.Events().DocumentClosing + .Select(x => x) + .InvokeCommand(this, x => x.ViewModel!.DocumentClosing) + .DisposeWith(dispose); + + this.Events().Closing + .Select(x => this.Dock) + .InvokeCommand(this, x => x.ViewModel!.SaveLayout) + .DisposeWith(dispose); + + EventExtensions.Events(this.Dock).Loaded + .Select(x => x.Source as DockingManager) + .InvokeCommand(this, x => x.ViewModel!.LoadLayout) + .DisposeWith(dispose); + + EventExtensions.Events(this.Dock).Unloaded + .Select(x => x.Source as DockingManager) + .InvokeCommand(this, x => x.ViewModel!.SaveLayout) + .DisposeWith(dispose); + } + + /// + /// Binds view model commands to key bindings. + /// + /// The disposable. + private void KeyBindings(CompositeDisposable dispose) + { + this.Events().KeyUp + .Where(x => x.Key == Key.N && Keyboard.Modifiers.HasFlag(ModifierKeys.Control | ModifierKeys.Shift)) + .Select(x => new Unit()) + .InvokeCommand(App.CommandsViewModel, x => x.NewProjectDialog) + .DisposeWith(dispose); + + this.Events().KeyUp + .Where(x => x.Key == Key.F4 && Keyboard.Modifiers.HasFlag(ModifierKeys.Alt)) + .Subscribe(_ => this.Close()) + .DisposeWith(dispose); + } + /// /// Registers interaction handlers. /// @@ -46,6 +123,6 @@ private void Interactions(CompositeDisposable dispose) } }, RxApp.MainThreadScheduler); }) - .DisposeWith(dispose); + .DisposeWith(dispose); } } \ No newline at end of file diff --git a/Horizon/ViewModel/AppViewModel.cs b/Horizon/ViewModel/AppViewModel.cs new file mode 100644 index 0000000..15d2241 --- /dev/null +++ b/Horizon/ViewModel/AppViewModel.cs @@ -0,0 +1,15 @@ +using Horizon.ObjectModel; +using ReactiveUI; +using ReactiveUI.Fody.Helpers; +using System.Collections.ObjectModel; + +namespace Horizon.ViewModel; + +public sealed class AppViewModel : ReactiveObject +{ + [Reactive] + public ObservableCollection AvailableTemplates { get; set; } = [new() { Name = "Starbound Mod Project", Description = "A mod project for the game Starbound", Tags = ["Starbound", "Mod"] }]; + + [Reactive] + public ProjectFile? CurrentProject { get; set; } +} \ No newline at end of file diff --git a/Horizon/ViewModel/LayoutInitializer.cs b/Horizon/ViewModel/LayoutInitializer.cs new file mode 100644 index 0000000..119b8f5 --- /dev/null +++ b/Horizon/ViewModel/LayoutInitializer.cs @@ -0,0 +1,39 @@ +using AvalonDock.Layout; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Horizon.ViewModel; + +public sealed class LayoutInitializer : ILayoutUpdateStrategy +{ + public bool BeforeInsertAnchorable(LayoutRoot layout, LayoutAnchorable anchorableToShow, ILayoutContainer destinationContainer) + { + // AvalonDock wants to add the anchorable into destinationContainer just for test provide a new anchorable pane if the + // pane is floating let the manager go ahead + LayoutAnchorablePane? destPane = destinationContainer as LayoutAnchorablePane; + if (destinationContainer?.FindParent() is not null) + { + return false; + } + + LayoutAnchorablePane? toolsPane = layout.Descendents().OfType().FirstOrDefault(d => d.Name == "ToolsPane"); + if (toolsPane is not null) + { + toolsPane.Children.Add(anchorableToShow); + return true; + } + + return false; + } + + public void AfterInsertAnchorable(LayoutRoot layout, LayoutAnchorable anchorableShown) + { } + + public bool BeforeInsertDocument(LayoutRoot layout, LayoutDocument anchorableToShow, ILayoutContainer destinationContainer) => false; + + public void AfterInsertDocument(LayoutRoot layout, LayoutDocument anchorableShown) + { } +} \ No newline at end of file diff --git a/Horizon/ViewModel/Panes/PageViewModel.cs b/Horizon/ViewModel/Panes/PageViewModel.cs new file mode 100644 index 0000000..e3f2601 --- /dev/null +++ b/Horizon/ViewModel/Panes/PageViewModel.cs @@ -0,0 +1,32 @@ +using ReactiveUI; +using ReactiveUI.Fody.Helpers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Horizon.ViewModel.Panes; + +public abstract class PageViewModel : PaneViewModel +{ + public PageViewModel() + { + this.WhenAnyValue(x => x.ID) + .Select(id => $"{this.GetType().Name}|{id}") + .ToPropertyEx(this, x => x.ContentID); + } + + [Reactive] + public int ID { get; set; } + + [Reactive] + public bool IsDirty { get; set; } + + [Reactive] + public bool IsReadOnly { get; set; } + + [Reactive] + public string? ReadOnlyReason { get; set; } +} \ No newline at end of file diff --git a/Horizon/ViewModel/Panes/PaneViewModel.cs b/Horizon/ViewModel/Panes/PaneViewModel.cs new file mode 100644 index 0000000..1d890a5 --- /dev/null +++ b/Horizon/ViewModel/Panes/PaneViewModel.cs @@ -0,0 +1,22 @@ +using ReactiveUI; +using ReactiveUI.Fody.Helpers; +using System.Windows.Media; + +namespace Horizon.ViewModel.Panes; + +public abstract class PaneViewModel : ReactiveObject +{ + public abstract string Title { get; set; } + + [Reactive] + public ImageSource? IconSource { get; set; } + + [ObservableAsProperty] + public string ContentID { get; } = string.Empty; + + [Reactive] + public bool IsSelected { get; set; } + + [Reactive] + public bool IsActive { get; set; } +} \ No newline at end of file diff --git a/Horizon/ViewModel/Panes/ProjectExplorerViewModel.cs b/Horizon/ViewModel/Panes/ProjectExplorerViewModel.cs new file mode 100644 index 0000000..2edde45 --- /dev/null +++ b/Horizon/ViewModel/Panes/ProjectExplorerViewModel.cs @@ -0,0 +1,29 @@ +using ReactiveUI.Fody.Helpers; +using System.Windows.Media.Imaging; + +namespace Horizon.ViewModel.Panes; + +public sealed class ProjectExplorerViewModel : ToolViewModel +{ + public ProjectExplorerViewModel() + { + string path = "pack://application:,,,/Resources/Images/New.png"; + + BitmapImage src = new(); + + try + { + src.BeginInit(); + src.UriSource = new Uri(path); + src.EndInit(); + } + catch (UriFormatException) + { + } + + this.IconSource = src; + } + + [Reactive] + public override string Title { get; set; } = "Project Explorer"; +} \ No newline at end of file diff --git a/Horizon/ViewModel/Panes/ToolViewModel.cs b/Horizon/ViewModel/Panes/ToolViewModel.cs new file mode 100644 index 0000000..0cbfc61 --- /dev/null +++ b/Horizon/ViewModel/Panes/ToolViewModel.cs @@ -0,0 +1,16 @@ +using ReactiveUI.Fody.Helpers; +using System.Reactive.Linq; + +namespace Horizon.ViewModel.Panes; + +public abstract class ToolViewModel : PaneViewModel +{ + public ToolViewModel() + { + Observable.Return(this.GetType().Name) + .ToPropertyEx(this, x => x.ContentID); + } + + [Reactive] + public bool IsVisible { get; set; } = true; +} \ No newline at end of file diff --git a/Horizon/ViewModel/WorkspaceViewModel.cs b/Horizon/ViewModel/WorkspaceViewModel.cs index 3a1eea8..4f9f065 100644 --- a/Horizon/ViewModel/WorkspaceViewModel.cs +++ b/Horizon/ViewModel/WorkspaceViewModel.cs @@ -1,7 +1,135 @@ -using ReactiveUI; +using AvalonDock; +using AvalonDock.Layout.Serialization; +using Horizon.ViewModel.Panes; +using ReactiveUI; +using ReactiveUI.Fody.Helpers; +using System.Collections.ObjectModel; +using System.IO; +using System.Reactive; namespace Horizon.ViewModel; public sealed class WorkspaceViewModel : ReactiveObject { + public WorkspaceViewModel() + { + this.SaveLayout = ReactiveCommand.Create(OnSaveLayout); + this.LoadLayout = ReactiveCommand.Create(this.OnLoadLayout); + this.DocumentClosing = ReactiveCommand.Create(this.OnDocumentClosing); + + App.ViewModel + .WhenAnyValue(vm => vm.CurrentProject, vm => vm.CurrentProject!.Name, (x, y) => x) + .Subscribe(x => this.Title = x is null ? "Horizon" : $"Horizon - {x.Name}"); + } + + [Reactive] + public ObservableCollection Pages { get; set; } = []; + + [Reactive] + public ObservableCollection Tools { get; set; } = [new ProjectExplorerViewModel()]; + + public ReactiveCommand SaveLayout { get; set; } + + public ReactiveCommand LoadLayout { get; set; } + + public ReactiveCommand DocumentClosing { get; set; } + + [Reactive] + public PageViewModel? ActiveDocument { get; set; } + + [Reactive] + public string Title { get; set; } = "Horizon"; + + private static void OnSaveLayout(DockingManager? manager) + { + if (manager is null) + { + return; + } + + using StringWriter fs = new(); + XmlLayoutSerializer xmlLayout = new(manager); + + xmlLayout.Serialize(fs); + + string xmlLayoutString = fs.ToString(); + + if (string.IsNullOrWhiteSpace(xmlLayoutString)) + { + return; + } + + string fileName = Path.Combine(App.AppDataDirectory, "Layout.config"); + + File.WriteAllText(fileName, xmlLayoutString); + } + + private void OnDocumentClosing(DocumentClosingEventArgs args) + { + PageViewModel? page = this.Pages.FirstOrDefault(x => x.ContentID == args.Document.ContentId); + + if (page is null) + { + args.Cancel = true; + } + else + { + // prompt for save + int x = 0; + } + } + + private void CreateDefaultLayout() + { + //this.Pages.Add(new EventPageViewModel()); + } + + private void ReloadContentOnStartup(LayoutSerializationCallbackEventArgs args) + { + string sId = args.Model.ContentId; + + if (string.IsNullOrWhiteSpace(sId)) + { + args.Cancel = true; + return; + } + + //EventPageViewModel vm = new(); + //this.Pages.Add(vm); + //args.Content = vm; + + ///TODO: Do an activator create instance + a loader for open documents + string x = args.Model.ContentId; + } + + private void OnLoadLayout(DockingManager? manager) + { + if (manager is null) + { + return; + } + + string layoutFileName = Path.Combine(App.AppDataDirectory, "Layout.config"); + + if (!File.Exists(layoutFileName)) + { + this.CreateDefaultLayout(); + return; + } + + XmlLayoutSerializer serializer = new(manager); + + serializer.LayoutSerializationCallback += (_, args) => + { + if (args.Model.ContentId is null) + { + args.Cancel = true; + return; + } + + this.ReloadContentOnStartup(args); + }; + + serializer.Deserialize(layoutFileName); + } } \ No newline at end of file From 89541dbbc501753931cf9767cfb6442b03d7ad2c Mon Sep 17 00:00:00 2001 From: David Palmer Date: Mon, 29 Jul 2024 22:20:38 -0500 Subject: [PATCH 11/19] Bumped project version to 0.4.3-alpha. --- Horizon/Horizon.csproj | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Horizon/Horizon.csproj b/Horizon/Horizon.csproj index a63faa9..1edccdc 100644 --- a/Horizon/Horizon.csproj +++ b/Horizon/Horizon.csproj @@ -7,7 +7,7 @@ enable latest true - 0.4.2 + 0.4.3 alpha false false diff --git a/README.md b/README.md index bf678ba..6634741 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![.NET Core Desktop](https://github.com/TheHeadmaster/Horizon/actions/workflows/dotnet-desktop.yml/badge.svg)](https://github.com/TheHeadmaster/Horizon/actions/workflows/dotnet-desktop.yml) ![Version][versionimage] ![GitHub (Pre-)Release Date](https://img.shields.io/github/release-date-pre/TheHeadmaster/Horizon?color=%23aa00aa) ![GitHub last commit (branch)](https://img.shields.io/github/last-commit/TheHeadmaster/Horizon/0.4.2-alpha?color=%23aa00aa&label=dev%20activity) [![Github All Releases](https://img.shields.io/github/downloads/TheHeadmaster/Horizon/total.svg)]() ![GitHub issues](https://img.shields.io/github/issues-raw/TheHeadmaster/Horizon) ![GitHub closed issues](https://img.shields.io/github/issues-closed-raw/TheHeadmaster/Horizon) ![GitHub](https://img.shields.io/github/license/TheHeadmaster/Horizon) +[![.NET Core Desktop](https://github.com/TheHeadmaster/Horizon/actions/workflows/dotnet-desktop.yml/badge.svg)](https://github.com/TheHeadmaster/Horizon/actions/workflows/dotnet-desktop.yml) ![Version][versionimage] ![GitHub (Pre-)Release Date](https://img.shields.io/github/release-date-pre/TheHeadmaster/Horizon?color=%23aa00aa) ![GitHub last commit (branch)](https://img.shields.io/github/last-commit/TheHeadmaster/Horizon/0.4.3-alpha?color=%23aa00aa&label=dev%20activity) [![Github All Releases](https://img.shields.io/github/downloads/TheHeadmaster/Horizon/total.svg)]() ![GitHub issues](https://img.shields.io/github/issues-raw/TheHeadmaster/Horizon) ![GitHub closed issues](https://img.shields.io/github/issues-closed-raw/TheHeadmaster/Horizon) ![GitHub](https://img.shields.io/github/license/TheHeadmaster/Horizon) [versionimage]: https://img.shields.io/github/v/release/TheHeadmaster/Horizon?color=%23aa00aa&include_prereleases&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAD6sAAA+rASlZpX8AAAIWSURBVFhHtZbNKwZBHMcfLxclR1EOLhwoKYqLUnLxUo6PuDiLlOTOxdHJwenJv8BBkjh6uUm5KMoBOchByuv3u7u/NTvP7OM3y37q087+Zmd+s7Mzu1vwYA5+KTyC/8orlM41XEGf61MZhezkODjzZwyy/UNw5skb/PMdRHjPhncDBZtQ1ec7/O/kwhqs2PcIzCu58Aw/w2I5vyU/hSdhMUF9dNTCPLVh8QduNXO1T0JOmQkb2oPcg8NhUU01LLtZ191Lwku4A6sg2YAvUOqzwHbxLMxDV0eMLUEmZnkdrkblRtgRlbMwAOO2aZ0wwV1YdMJ2HJzMjC9BXj4Pgc98Gc5AVk7AJpgGE9/AC3gAb6EMygtzBliW89noWIm66Gi208Lr+6RgMh0dtbTA/bDoBXdQyXwEQk901DIFh8KiF+eQ6yyePpF3Y8d+s9UR07gtW0yogXxVmjENsvB82xXNAfTCNtgOV6C2swZ4DbkL+O3vhM1QQzBwJnK55YjZPjpiPgbwH85VydENWjFTvhmJq05rTKXKRcj9vgsPIRccX6VdkLjaahyHMXblB+y2YoIZ40eJz9uMaS3DrLy3zqn8btvxLDrfslzBrotNnxyxLMbI/hUSlTmRyGkPgOQ5iLJ8rgGQPAbhzOX6GBFenPr3moG0G00dAOF3gf6FBZia3Af+QMoK1ph4yeRBPyzBM7gNizADhcI3BycuWIMMM8IAAAAASUVORK5CYII= From 41a5eef206d2de2b77007eb9cda6ea9260d3221e Mon Sep 17 00:00:00 2001 From: David Palmer Date: Tue, 30 Jul 2024 02:47:13 -0500 Subject: [PATCH 12/19] Added multiple converters. #22 #23 --- Horizon/Converters/ActiveDocumentConverter.cs | 28 +++++++++++++ .../Converters/BoolToVisibilityConverter.cs | 29 ++++++++++++++ Horizon/Converters/EnumToStringConverter.cs | 28 +++++++++++++ .../StringToImageSourceConverter.cs | 40 +++++++++++++++++++ Horizon/Resources/UI/Converters.xaml | 7 +++- 5 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 Horizon/Converters/ActiveDocumentConverter.cs create mode 100644 Horizon/Converters/BoolToVisibilityConverter.cs create mode 100644 Horizon/Converters/EnumToStringConverter.cs create mode 100644 Horizon/Converters/StringToImageSourceConverter.cs diff --git a/Horizon/Converters/ActiveDocumentConverter.cs b/Horizon/Converters/ActiveDocumentConverter.cs new file mode 100644 index 0000000..165b50a --- /dev/null +++ b/Horizon/Converters/ActiveDocumentConverter.cs @@ -0,0 +1,28 @@ +using Horizon.ViewModel.Panes; +using ReactiveUI; + +namespace Horizon.Converters; + +/// +/// Converts an to an active document binding. +/// +public sealed class ActiveDocumentConverter : IBindingTypeConverter +{ + /// + public int GetAffinityForObjects(Type fromType, Type toType) => 0; + + /// + public bool TryConvert(object? from, Type toType, object? conversionHint, out object? result) + { + if (from is PageViewModel) + { + result = from; + return true; + } + else + { + result = null; + return false; + } + } +} \ No newline at end of file diff --git a/Horizon/Converters/BoolToVisibilityConverter.cs b/Horizon/Converters/BoolToVisibilityConverter.cs new file mode 100644 index 0000000..3a71703 --- /dev/null +++ b/Horizon/Converters/BoolToVisibilityConverter.cs @@ -0,0 +1,29 @@ +using System.Globalization; +using System.Windows; +using System.Windows.Data; + +namespace Horizon.Converters; + +/// +/// Converts a to a and back. +/// +public sealed class BoolToVisibilityConverter : IValueConverter +{ + /// + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + try + { + Visibility boolVisibility = (bool)value ? Visibility.Visible : Visibility.Collapsed; + return boolVisibility; + } + catch + { + return Visibility.Collapsed; + } + } + + // No need to implement converting back on a one-way binding + /// + public object? ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => null; +} \ No newline at end of file diff --git a/Horizon/Converters/EnumToStringConverter.cs b/Horizon/Converters/EnumToStringConverter.cs new file mode 100644 index 0000000..b2ea97b --- /dev/null +++ b/Horizon/Converters/EnumToStringConverter.cs @@ -0,0 +1,28 @@ +using System.Globalization; +using System.Windows.Data; + +namespace Horizon.Converters; + +/// +/// Converts an to a and back. +/// +public sealed class EnumToStringConverter : IValueConverter +{ + /// + public object? Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + try + { + string EnumString = Enum.GetName(value.GetType(), value) ?? string.Empty; + return EnumString; + } + catch + { + return string.Empty; + } + } + + // No need to implement converting back on a one-way binding + /// + public object? ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => null; +} \ No newline at end of file diff --git a/Horizon/Converters/StringToImageSourceConverter.cs b/Horizon/Converters/StringToImageSourceConverter.cs new file mode 100644 index 0000000..124acf8 --- /dev/null +++ b/Horizon/Converters/StringToImageSourceConverter.cs @@ -0,0 +1,40 @@ +using System.Globalization; +using System.Windows.Data; +using System.Windows.Media.Imaging; + +namespace Horizon.Converters; + +/// +/// Converts a path to an Source and back. +/// +public class StringToImageSourceConverter : IValueConverter +{ + /// + public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) + { + string? path = value as string; + if (string.IsNullOrWhiteSpace(path)) + { + return null; + } + + BitmapImage src = new(); + + try + { + src.BeginInit(); + src.UriSource = new Uri(path); + src.EndInit(); + } + catch (UriFormatException) + { + return null; + } + + return src; + } + + // No need to implement converting back on a one-way binding + /// + public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) => null; +} \ No newline at end of file diff --git a/Horizon/Resources/UI/Converters.xaml b/Horizon/Resources/UI/Converters.xaml index 42f1cd1..3085e98 100644 --- a/Horizon/Resources/UI/Converters.xaml +++ b/Horizon/Resources/UI/Converters.xaml @@ -1,3 +1,8 @@  + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:converters="clr-namespace:Horizon.Converters"> + + + + \ No newline at end of file From c93014ce2ee336ae5ce53641b76fd432e1503073 Mon Sep 17 00:00:00 2001 From: David Palmer Date: Tue, 30 Jul 2024 02:49:15 -0500 Subject: [PATCH 13/19] Added various images that will be used for icons. #22 #23 --- Horizon/Horizon.csproj | 8 ++++++++ Horizon/Resources/Images/New.png | Bin 0 -> 423 bytes Horizon/Resources/Images/Open Small.png | Bin 0 -> 271 bytes Horizon/Resources/Images/Open.png | Bin 0 -> 662 bytes Horizon/Resources/Images/Read Only.png | Bin 0 -> 5576 bytes 5 files changed, 8 insertions(+) create mode 100644 Horizon/Resources/Images/New.png create mode 100644 Horizon/Resources/Images/Open Small.png create mode 100644 Horizon/Resources/Images/Open.png create mode 100644 Horizon/Resources/Images/Read Only.png diff --git a/Horizon/Horizon.csproj b/Horizon/Horizon.csproj index 1edccdc..03f1c2e 100644 --- a/Horizon/Horizon.csproj +++ b/Horizon/Horizon.csproj @@ -31,6 +31,10 @@ + + + + @@ -82,6 +86,10 @@ + + + + diff --git a/Horizon/Resources/Images/New.png b/Horizon/Resources/Images/New.png new file mode 100644 index 0000000000000000000000000000000000000000..84c799adfdd90d874a7dd7f71b7014746a84216b GIT binary patch literal 423 zcmV;Y0a*TtP)?K@^1tu}xtuirOf4R{9S50KSEfur^`#+#h2X z>{E!nrC9p}wpK#ARH71$T%9#z5EA1|qOu1r40q1#{pM$9*ST7?exrzVkR$#cfJ>w- z07X$OGV7s*wPywZpaIAcpm&uIkb6Lnz4uGm07PU3P$tseu&~}*MEl$$5;AX603z)G z@*EYu2IQb9iUkX6TP|7=^LA1K42B^$Kx=b!jODEFt5cs6;3>?oZ!;8;EsL;1{n(QQ zpe#>UiKHEad@d~QcTXk&7Zib1+wxX;((ykA_-_9H05J3JIDoR$z2pFZcEh22A<}bZ zULW;&f3jhH!DDwtHz5ETMA`)8A%HxFTd_KXV!6;~o~DgS22e>=mx=T&di6dc%>2Up z)|$)xHDu;(BDsvN<|`4|nf7^84`Akfi*dee0F!EJ0GjtN8O;lzRxLL}y#jkC2It1C R0VMzc002ovPDHLkV1gd?t=j+q literal 0 HcmV?d00001 diff --git a/Horizon/Resources/Images/Open Small.png b/Horizon/Resources/Images/Open Small.png new file mode 100644 index 0000000000000000000000000000000000000000..c16115af4647a9cfe179fc1e1b2a783f76b9c872 GIT binary patch literal 271 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+1|-AI^@Rf|&H|6fVg?3oVGw3ym^DWND0tA* z#WAE}&f5!yT!$P4SOZvf->`TeG?vKO(|GIVjgIgQo2N#!&Yk~rjRMEU3(+?!*!)#C z2^l!5W^^DDCQG6trcqnYd=@gbu!ZzgnTWOxt*0*xchjTu@M+C+E?^{X8bJ#iZ@- znezQjE<3)}?q_;o_U?XTnbIBg+C&cipU!`l+AG~;u{bqbA;Q#Xq5l=uz14eUV+}=j Q0X@Uu>FVdQ&MBb@07m*^J^%m! literal 0 HcmV?d00001 diff --git a/Horizon/Resources/Images/Open.png b/Horizon/Resources/Images/Open.png new file mode 100644 index 0000000000000000000000000000000000000000..6a3b96d349afd4c00d47d82a8defccc734b3f6af GIT binary patch literal 662 zcmV;H0%`q;P)`-mq_Gj~QdtQ>5d&5c730V=t~{2P-F*v7@@DS`)6KB=oSk|1 z$2r3=j5(kviX(0wh%p}azPCu~H9+r^+6Q!<)E6!%VvGwSa-P(ir1*-^ZBoN8S4)QKFkmR$~D=UO)vpS+h#E*Aj3*VF)2J>hZ!4NL&!qstLM!-OWha=8&> zTmbalgg2o58K8=)9tCu5?lM40zu!Md>f^c004b#dq^yp(>c6wj0HnwW@xK7MHJlx$ z&H$XE*qHI^0CKmk)CJ@_sY`>{t0n;QG6Mr-wY>iS;#wkQt0M4g-5Vd6Fm(pto{Ro3 z@JUe=hpMrK5H0}v8PHGU%DLS%lqLXjyR`-Y`nqgy>*DiXE_b`V)8ll65RSFf0C@}G zc)j@EA|M-632TP|^1jn~aI!A0^7@mqS?;?4*zzU z`vG}0?ratYaOeH|YT*LN^GVKSWPtuPp=?_Lc{Aym%nZ=7^!?Ige|842;W7he3HQN2 zY&U@R1d@FNXdl63S%8)XlhpyVLMT}!NGpYt^#ZhJC|Ng1YloA~184)0WCM{|!hJCD wv4IF}Ad+k#LK}!A8;EcZ1|o)G7>3KAKb@0Vm4~IkDgXcg07*qoM6N<$f)q9;LI3~& literal 0 HcmV?d00001 diff --git a/Horizon/Resources/Images/Read Only.png b/Horizon/Resources/Images/Read Only.png new file mode 100644 index 0000000000000000000000000000000000000000..fab85056f05ae8c6816c499d44942dc6c882963d GIT binary patch literal 5576 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 Date: Tue, 30 Jul 2024 02:50:33 -0500 Subject: [PATCH 14/19] Added GlobalSuppressions file. Added ignore severity of unsupported platform to .editorconfig. --- .editorconfig | 5 ++++- Horizon/GlobalSuppressions.cs | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 Horizon/GlobalSuppressions.cs diff --git a/.editorconfig b/.editorconfig index 46d128a..aad034a 100644 --- a/.editorconfig +++ b/.editorconfig @@ -130,4 +130,7 @@ csharp_style_prefer_extended_property_pattern = true:suggestion csharp_style_var_for_built_in_types = false:silent csharp_style_var_when_type_is_apparent = false:silent csharp_style_var_elsewhere = false:silent -csharp_space_around_binary_operators = before_and_after \ No newline at end of file +csharp_space_around_binary_operators = before_and_after + +# CA1416: Validate platform compatibility +dotnet_diagnostic.CA1416.severity = none \ No newline at end of file diff --git a/Horizon/GlobalSuppressions.cs b/Horizon/GlobalSuppressions.cs new file mode 100644 index 0000000..94cff40 --- /dev/null +++ b/Horizon/GlobalSuppressions.cs @@ -0,0 +1,2 @@ +// This file is used by Code Analysis to maintain SuppressMessage attributes that are applied to this project. Project-level +// suppressions either have no target or are given a specific target and scoped to a namespace, type, member, etc. \ No newline at end of file From c4a1b13287b420dcd8b1e28136b1585183b85c4b Mon Sep 17 00:00:00 2001 From: David Palmer Date: Tue, 30 Jul 2024 03:10:46 -0500 Subject: [PATCH 15/19] Fixed glyph text being slightly off. Fixed size of key binding visuals in main menu. #26 --- .../ControlDefinitions/SubmenuHeaderTemplateKeyDefinition.xaml | 2 +- .../UI/ControlDefinitions/SubmenuItemTemplateKeyDefinition.xaml | 2 +- Horizon/View/Controls/KeyBindingGlyph.xaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Horizon/Resources/UI/ControlDefinitions/SubmenuHeaderTemplateKeyDefinition.xaml b/Horizon/Resources/UI/ControlDefinitions/SubmenuHeaderTemplateKeyDefinition.xaml index c79f654..8050914 100644 --- a/Horizon/Resources/UI/ControlDefinitions/SubmenuHeaderTemplateKeyDefinition.xaml +++ b/Horizon/Resources/UI/ControlDefinitions/SubmenuHeaderTemplateKeyDefinition.xaml @@ -19,7 +19,7 @@ Grid.Column="1" ContentSource="Header" RecognizesAccessKey="True" VerticalAlignment="Center" /> - + - + diff --git a/Horizon/View/Controls/KeyBindingGlyph.xaml b/Horizon/View/Controls/KeyBindingGlyph.xaml index 38b2e8e..9de1106 100644 --- a/Horizon/View/Controls/KeyBindingGlyph.xaml +++ b/Horizon/View/Controls/KeyBindingGlyph.xaml @@ -14,7 +14,7 @@ - + From 422ed584abb5e9419820721a3c7a707bd6ec9462 Mon Sep 17 00:00:00 2001 From: David Palmer Date: Tue, 30 Jul 2024 03:11:24 -0500 Subject: [PATCH 16/19] Changed ProjectTemplate to a ReactiveObject and made all of the properties Reactive. --- Horizon/ObjectModel/ProjectTemplate.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Horizon/ObjectModel/ProjectTemplate.cs b/Horizon/ObjectModel/ProjectTemplate.cs index f354133..38d0de0 100644 --- a/Horizon/ObjectModel/ProjectTemplate.cs +++ b/Horizon/ObjectModel/ProjectTemplate.cs @@ -1,10 +1,16 @@ -namespace Horizon.ObjectModel; +using ReactiveUI; +using ReactiveUI.Fody.Helpers; -public class ProjectTemplate +namespace Horizon.ObjectModel; + +public class ProjectTemplate : ReactiveObject { - public string Name { get; set; } + [Reactive] + public string Name { get; set; } = string.Empty; - public string Description { get; set; } + [Reactive] + public string Description { get; set; } = string.Empty; + [Reactive] public List Tags { get; set; } = []; } \ No newline at end of file From 81ad08d4c5e6f2a84a9a83c767394915183529b2 Mon Sep 17 00:00:00 2001 From: David Palmer Date: Tue, 30 Jul 2024 03:15:45 -0500 Subject: [PATCH 17/19] Added AppDataDirectory to App. Moved AvailableTemplates to AppViewModel. ViewModel in App renamed to CommandsViewModel. Added AppViewmodel as ViewModel. #22 #23 --- Horizon/App.xaml.cs | 29 +++++++++++++++---- Horizon/View/Controls/MainMenu.xaml.cs | 2 +- Horizon/View/Windows/Workspace.xaml.cs | 2 +- .../ViewModel/NewProjectWindowViewModel.cs | 4 ++- 4 files changed, 29 insertions(+), 8 deletions(-) diff --git a/Horizon/App.xaml.cs b/Horizon/App.xaml.cs index 0d77916..e0503e4 100644 --- a/Horizon/App.xaml.cs +++ b/Horizon/App.xaml.cs @@ -1,5 +1,4 @@ -using Horizon.ObjectModel; -using Horizon.View.Windows; +using Horizon.View.Windows; using Horizon.ViewModel; using ReactiveUI; using ReactiveUI.Fody.Helpers; @@ -53,17 +52,37 @@ public static string UserDirectory ?.GetCustomAttribute() ?.InformationalVersion ?? "unknown-alpha"; - /// + /// [Reactive] - public static CommandsViewModel ViewModel { get; } = new CommandsViewModel(); + public static CommandsViewModel CommandsViewModel { get; } = new(); - public static List AvailableTemplates { get; set; } = [new() { Name = "Starbound Mod Project", Description = "A mod project for the game Starbound", Tags = ["Starbound", "Mod"] }]; + [Reactive] + public static AppViewModel ViewModel { get; } = new(); + + public static string AppDataDirectory + { + get + { + if (appDataDirectory is null) + { + appDataDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Horizon"); + if (!Directory.Exists(appDataDirectory)) + { + Directory.CreateDirectory(appDataDirectory); + } + } + + return appDataDirectory; + } + } /// /// The workspace. /// private static Workspace? workspace; + private static string? appDataDirectory; + /// /// Gets all the startup tasks that run while the splash screen is displayed. /// diff --git a/Horizon/View/Controls/MainMenu.xaml.cs b/Horizon/View/Controls/MainMenu.xaml.cs index 5f80baa..65e862a 100644 --- a/Horizon/View/Controls/MainMenu.xaml.cs +++ b/Horizon/View/Controls/MainMenu.xaml.cs @@ -24,7 +24,7 @@ public MainMenu() this.NewProjectMenuItem.Events() .Click .Select(x => new Unit()) - .InvokeCommand(App.ViewModel, x => x.NewProjectDialog) + .InvokeCommand(App.CommandsViewModel, x => x.NewProjectDialog) .DisposeWith(dispose); }); } diff --git a/Horizon/View/Windows/Workspace.xaml.cs b/Horizon/View/Windows/Workspace.xaml.cs index 3016901..225fa2b 100644 --- a/Horizon/View/Windows/Workspace.xaml.cs +++ b/Horizon/View/Windows/Workspace.xaml.cs @@ -28,7 +28,7 @@ public Workspace() /// The disposable. private void Interactions(CompositeDisposable dispose) { - App.ViewModel + App.CommandsViewModel .NewProjectDialogInteraction .RegisterHandler(interaction => { diff --git a/Horizon/ViewModel/NewProjectWindowViewModel.cs b/Horizon/ViewModel/NewProjectWindowViewModel.cs index 169e7fc..2678de7 100644 --- a/Horizon/ViewModel/NewProjectWindowViewModel.cs +++ b/Horizon/ViewModel/NewProjectWindowViewModel.cs @@ -4,6 +4,7 @@ using Nito.Disposables.Internals; using ReactiveUI; using ReactiveUI.Fody.Helpers; +using System.Collections.ObjectModel; using System.IO; using System.Reactive.Linq; @@ -68,5 +69,6 @@ public NewProjectWindowViewModel() [Reactive] public ProjectFile Project { get; set; } = new() { Name = "Project Name", FilePath = Path.Combine(App.UserDirectory, "Project.horizon") }; - public List AvailableTemplates { get; init; } = []; + [Reactive] + public ObservableCollection AvailableTemplates { get; init; } = []; } \ No newline at end of file From cfcddd2c2817febb77f643473803de1c8d2e443e Mon Sep 17 00:00:00 2001 From: TheHeadmaster <45703202+TheHeadmaster@users.noreply.github.com> Date: Tue, 30 Jul 2024 03:19:32 -0500 Subject: [PATCH 18/19] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6634741..cb81050 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![.NET Core Desktop](https://github.com/TheHeadmaster/Horizon/actions/workflows/dotnet-desktop.yml/badge.svg)](https://github.com/TheHeadmaster/Horizon/actions/workflows/dotnet-desktop.yml) ![Version][versionimage] ![GitHub (Pre-)Release Date](https://img.shields.io/github/release-date-pre/TheHeadmaster/Horizon?color=%23aa00aa) ![GitHub last commit (branch)](https://img.shields.io/github/last-commit/TheHeadmaster/Horizon/0.4.3-alpha?color=%23aa00aa&label=dev%20activity) [![Github All Releases](https://img.shields.io/github/downloads/TheHeadmaster/Horizon/total.svg)]() ![GitHub issues](https://img.shields.io/github/issues-raw/TheHeadmaster/Horizon) ![GitHub closed issues](https://img.shields.io/github/issues-closed-raw/TheHeadmaster/Horizon) ![GitHub](https://img.shields.io/github/license/TheHeadmaster/Horizon) +[![.NET Core Desktop](https://github.com/TheHeadmaster/Horizon/actions/workflows/dotnet-desktop.yml/badge.svg)](https://github.com/TheHeadmaster/Horizon/actions/workflows/dotnet-desktop.yml) ![Version][versionimage] ![GitHub (Pre-)Release Date](https://img.shields.io/github/release-date-pre/TheHeadmaster/Horizon?color=%23aa00aa) ![GitHub last commit (branch)](https://img.shields.io/github/last-commit/TheHeadmaster/Horizon/0.4.3-alpha?color=%23aa00aa&label=dev%20activity) [![Github All Releases](https://img.shields.io/github/downloads/TheHeadmaster/Horizon/total.svg)]() ![GitHub issues](https://img.shields.io/github/issues-raw/TheHeadmaster/Horizon) ![GitHub closed issues](https://img.shields.io/github/issues-closed-raw/TheHeadmaster/Horizon) ![GitHub](https://img.shields.io/github/license/TheHeadmaster/Horizon) [![](https://dcbadge.limes.pink/api/server/B37tBEFZzU)](https://discord.gg/B37tBEFZzU) [versionimage]: https://img.shields.io/github/v/release/TheHeadmaster/Horizon?color=%23aa00aa&include_prereleases&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAD6sAAA+rASlZpX8AAAIWSURBVFhHtZbNKwZBHMcfLxclR1EOLhwoKYqLUnLxUo6PuDiLlOTOxdHJwenJv8BBkjh6uUm5KMoBOchByuv3u7u/NTvP7OM3y37q087+Zmd+s7Mzu1vwYA5+KTyC/8orlM41XEGf61MZhezkODjzZwyy/UNw5skb/PMdRHjPhncDBZtQ1ec7/O/kwhqs2PcIzCu58Aw/w2I5vyU/hSdhMUF9dNTCPLVh8QduNXO1T0JOmQkb2oPcg8NhUU01LLtZ191Lwku4A6sg2YAvUOqzwHbxLMxDV0eMLUEmZnkdrkblRtgRlbMwAOO2aZ0wwV1YdMJ2HJzMjC9BXj4Pgc98Gc5AVk7AJpgGE9/AC3gAb6EMygtzBliW89noWIm66Gi208Lr+6RgMh0dtbTA/bDoBXdQyXwEQk901DIFh8KiF+eQ6yyePpF3Y8d+s9UR07gtW0yogXxVmjENsvB82xXNAfTCNtgOV6C2swZ4DbkL+O3vhM1QQzBwJnK55YjZPjpiPgbwH85VydENWjFTvhmJq05rTKXKRcj9vgsPIRccX6VdkLjaahyHMXblB+y2YoIZ40eJz9uMaS3DrLy3zqn8btvxLDrfslzBrotNnxyxLMbI/hUSlTmRyGkPgOQ5iLJ8rgGQPAbhzOX6GBFenPr3moG0G00dAOF3gf6FBZia3Af+QMoK1ph4yeRBPyzBM7gNizADhcI3BycuWIMMM8IAAAAASUVORK5CYII= From 297b6b098b32257b425b939d5d3bd44eb9ab547a Mon Sep 17 00:00:00 2001 From: David Palmer Date: Tue, 30 Jul 2024 03:26:13 -0500 Subject: [PATCH 19/19] Fleshed out AvalonDock and pane system. Layout loads and saves automatically. Added Project Explorer. Name of project (or Horizon if no project is open) now displays at top of workspace window. #22 #23 #24 --- Horizon/View/Panes/PanesStyleSelector.cs | 23 ++++ Horizon/View/Panes/PanesTemplateSelector.cs | 17 +++ Horizon/View/Panes/ProjectExplorerPane.xaml | 13 ++ .../View/Panes/ProjectExplorerPane.xaml.cs | 21 +++ Horizon/View/Windows/NewProjectWindow.xaml.cs | 2 +- Horizon/View/Windows/Workspace.xaml | 89 +++++++++++- Horizon/View/Windows/Workspace.xaml.cs | 81 ++++++++++- Horizon/ViewModel/AppViewModel.cs | 15 ++ Horizon/ViewModel/LayoutInitializer.cs | 39 ++++++ Horizon/ViewModel/Panes/PageViewModel.cs | 32 +++++ Horizon/ViewModel/Panes/PaneViewModel.cs | 22 +++ .../Panes/ProjectExplorerViewModel.cs | 29 ++++ Horizon/ViewModel/Panes/ToolViewModel.cs | 16 +++ Horizon/ViewModel/WorkspaceViewModel.cs | 130 +++++++++++++++++- 14 files changed, 519 insertions(+), 10 deletions(-) create mode 100644 Horizon/View/Panes/PanesStyleSelector.cs create mode 100644 Horizon/View/Panes/PanesTemplateSelector.cs create mode 100644 Horizon/View/Panes/ProjectExplorerPane.xaml create mode 100644 Horizon/View/Panes/ProjectExplorerPane.xaml.cs create mode 100644 Horizon/ViewModel/AppViewModel.cs create mode 100644 Horizon/ViewModel/LayoutInitializer.cs create mode 100644 Horizon/ViewModel/Panes/PageViewModel.cs create mode 100644 Horizon/ViewModel/Panes/PaneViewModel.cs create mode 100644 Horizon/ViewModel/Panes/ProjectExplorerViewModel.cs create mode 100644 Horizon/ViewModel/Panes/ToolViewModel.cs diff --git a/Horizon/View/Panes/PanesStyleSelector.cs b/Horizon/View/Panes/PanesStyleSelector.cs new file mode 100644 index 0000000..a9d6796 --- /dev/null +++ b/Horizon/View/Panes/PanesStyleSelector.cs @@ -0,0 +1,23 @@ +using Horizon.ViewModel.Panes; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; + +namespace Horizon.View.Panes; + +public sealed class PanesStyleSelector : StyleSelector +{ + public Style? ToolStyle { get; set; } + public Style? PageStyle { get; set; } + + public override Style SelectStyle(object item, DependencyObject container) => item switch + { + ToolViewModel => this.ToolStyle ?? base.SelectStyle(item, container), + PageViewModel => this.PageStyle ?? base.SelectStyle(item, container), + _ => base.SelectStyle(item, container) + }; +} \ No newline at end of file diff --git a/Horizon/View/Panes/PanesTemplateSelector.cs b/Horizon/View/Panes/PanesTemplateSelector.cs new file mode 100644 index 0000000..8255fdf --- /dev/null +++ b/Horizon/View/Panes/PanesTemplateSelector.cs @@ -0,0 +1,17 @@ +using Horizon.ViewModel.Panes; +using System.Windows; +using System.Windows.Controls; + +namespace Horizon.View.Panes; + +public sealed class PanesTemplateSelector : DataTemplateSelector +{ + public DataTemplate? ProjectExplorerViewTemplate { get; set; } + + public override DataTemplate? SelectTemplate(object item, DependencyObject container) => + item switch + { + ProjectExplorerViewModel => this.ProjectExplorerViewTemplate, + _ => base.SelectTemplate(item, container) + }; +} \ No newline at end of file diff --git a/Horizon/View/Panes/ProjectExplorerPane.xaml b/Horizon/View/Panes/ProjectExplorerPane.xaml new file mode 100644 index 0000000..a6bbd8d --- /dev/null +++ b/Horizon/View/Panes/ProjectExplorerPane.xaml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/Horizon/View/Panes/ProjectExplorerPane.xaml.cs b/Horizon/View/Panes/ProjectExplorerPane.xaml.cs new file mode 100644 index 0000000..cdcd887 --- /dev/null +++ b/Horizon/View/Panes/ProjectExplorerPane.xaml.cs @@ -0,0 +1,21 @@ +using Horizon.ViewModel.Panes; +using ReactiveUI; + +namespace Horizon.View.Panes; + +/// +/// Interaction logic for ProjectExplorerPane.xaml +/// +public partial class ProjectExplorerPane +{ + public ProjectExplorerPane() + { + this.InitializeComponent(); + + this.ViewModel = new ProjectExplorerViewModel(); + + this.WhenActivated(dispose => + { + }); + } +} \ No newline at end of file diff --git a/Horizon/View/Windows/NewProjectWindow.xaml.cs b/Horizon/View/Windows/NewProjectWindow.xaml.cs index 5372b00..be46eb5 100644 --- a/Horizon/View/Windows/NewProjectWindow.xaml.cs +++ b/Horizon/View/Windows/NewProjectWindow.xaml.cs @@ -18,7 +18,7 @@ public NewProjectWindow() this.ViewModel = new() { - AvailableTemplates = App.AvailableTemplates + AvailableTemplates = App.ViewModel.AvailableTemplates }; this.WhenActivated(dispose => diff --git a/Horizon/View/Windows/Workspace.xaml b/Horizon/View/Windows/Workspace.xaml index e5aebdf..dac5449 100644 --- a/Horizon/View/Windows/Workspace.xaml +++ b/Horizon/View/Windows/Workspace.xaml @@ -3,15 +3,92 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:panes="clr-namespace:Horizon.View.Panes" xmlns:windows="clr-namespace:Horizon.View.Windows" xmlns:vm="clr-namespace:Horizon.ViewModel" + xmlns:xcad="https://github.com/Dirkster99/AvalonDock" d:DataContext="{d:DesignInstance Type=vm:WorkspaceViewModel}" mc:Ignorable="d" Width="1366" Height="768" WindowStartupLocation="CenterScreen" WindowState="Maximized" Background="{StaticResource BaseBackgroundColor}" FontFamily="Cascadia Mono" Style="{StaticResource BorderlessWindowStyle}"> - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Horizon/View/Windows/Workspace.xaml.cs b/Horizon/View/Windows/Workspace.xaml.cs index 225fa2b..1357455 100644 --- a/Horizon/View/Windows/Workspace.xaml.cs +++ b/Horizon/View/Windows/Workspace.xaml.cs @@ -1,6 +1,14 @@ -using ReactiveUI; +using AvalonDock; +using Horizon.Converters; +using ReactiveMarbles.ObservableEvents; +using ReactiveUI; +using System.Reactive; using System.Reactive.Disposables; using System.Reactive.Linq; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using EventExtensions = System.Windows.Controls.EventExtensions; namespace Horizon.View.Windows; @@ -18,10 +26,79 @@ public Workspace() this.WhenActivated(dispose => { + this.PropertyBindings(dispose); + this.EventBindings(dispose); + this.KeyBindings(dispose); this.Interactions(dispose); }); } + /// + /// Binds view model properties to the view. + /// + /// The disposable. + private void PropertyBindings(CompositeDisposable dispose) + { + this.OneWayBind(this.ViewModel, vm => vm.Title, view => view.Title) + .DisposeWith(dispose); + + this.OneWayBind(this.ViewModel, vm => vm.Tools, view => view.Dock.AnchorablesSource) + .DisposeWith(dispose); + + this.OneWayBind(this.ViewModel, vm => vm.Pages, view => view.Dock.DocumentsSource) + .DisposeWith(dispose); + + this.Bind(this.ViewModel, vm => vm.ActiveDocument, view => view.Dock.ActiveContent, + vmToViewConverterOverride: new ActiveDocumentConverter(), + viewToVMConverterOverride: new ActiveDocumentConverter()) + .DisposeWith(dispose); + } + + /// + /// Binds view model commands to view events. + /// + /// The disposable. + private void EventBindings(CompositeDisposable dispose) + { + this.Dock.Events().DocumentClosing + .Select(x => x) + .InvokeCommand(this, x => x.ViewModel!.DocumentClosing) + .DisposeWith(dispose); + + this.Events().Closing + .Select(x => this.Dock) + .InvokeCommand(this, x => x.ViewModel!.SaveLayout) + .DisposeWith(dispose); + + EventExtensions.Events(this.Dock).Loaded + .Select(x => x.Source as DockingManager) + .InvokeCommand(this, x => x.ViewModel!.LoadLayout) + .DisposeWith(dispose); + + EventExtensions.Events(this.Dock).Unloaded + .Select(x => x.Source as DockingManager) + .InvokeCommand(this, x => x.ViewModel!.SaveLayout) + .DisposeWith(dispose); + } + + /// + /// Binds view model commands to key bindings. + /// + /// The disposable. + private void KeyBindings(CompositeDisposable dispose) + { + this.Events().KeyUp + .Where(x => x.Key == Key.N && Keyboard.Modifiers.HasFlag(ModifierKeys.Control | ModifierKeys.Shift)) + .Select(x => new Unit()) + .InvokeCommand(App.CommandsViewModel, x => x.NewProjectDialog) + .DisposeWith(dispose); + + this.Events().KeyUp + .Where(x => x.Key == Key.F4 && Keyboard.Modifiers.HasFlag(ModifierKeys.Alt)) + .Subscribe(_ => this.Close()) + .DisposeWith(dispose); + } + /// /// Registers interaction handlers. /// @@ -46,6 +123,6 @@ private void Interactions(CompositeDisposable dispose) } }, RxApp.MainThreadScheduler); }) - .DisposeWith(dispose); + .DisposeWith(dispose); } } \ No newline at end of file diff --git a/Horizon/ViewModel/AppViewModel.cs b/Horizon/ViewModel/AppViewModel.cs new file mode 100644 index 0000000..15d2241 --- /dev/null +++ b/Horizon/ViewModel/AppViewModel.cs @@ -0,0 +1,15 @@ +using Horizon.ObjectModel; +using ReactiveUI; +using ReactiveUI.Fody.Helpers; +using System.Collections.ObjectModel; + +namespace Horizon.ViewModel; + +public sealed class AppViewModel : ReactiveObject +{ + [Reactive] + public ObservableCollection AvailableTemplates { get; set; } = [new() { Name = "Starbound Mod Project", Description = "A mod project for the game Starbound", Tags = ["Starbound", "Mod"] }]; + + [Reactive] + public ProjectFile? CurrentProject { get; set; } +} \ No newline at end of file diff --git a/Horizon/ViewModel/LayoutInitializer.cs b/Horizon/ViewModel/LayoutInitializer.cs new file mode 100644 index 0000000..119b8f5 --- /dev/null +++ b/Horizon/ViewModel/LayoutInitializer.cs @@ -0,0 +1,39 @@ +using AvalonDock.Layout; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Horizon.ViewModel; + +public sealed class LayoutInitializer : ILayoutUpdateStrategy +{ + public bool BeforeInsertAnchorable(LayoutRoot layout, LayoutAnchorable anchorableToShow, ILayoutContainer destinationContainer) + { + // AvalonDock wants to add the anchorable into destinationContainer just for test provide a new anchorable pane if the + // pane is floating let the manager go ahead + LayoutAnchorablePane? destPane = destinationContainer as LayoutAnchorablePane; + if (destinationContainer?.FindParent() is not null) + { + return false; + } + + LayoutAnchorablePane? toolsPane = layout.Descendents().OfType().FirstOrDefault(d => d.Name == "ToolsPane"); + if (toolsPane is not null) + { + toolsPane.Children.Add(anchorableToShow); + return true; + } + + return false; + } + + public void AfterInsertAnchorable(LayoutRoot layout, LayoutAnchorable anchorableShown) + { } + + public bool BeforeInsertDocument(LayoutRoot layout, LayoutDocument anchorableToShow, ILayoutContainer destinationContainer) => false; + + public void AfterInsertDocument(LayoutRoot layout, LayoutDocument anchorableShown) + { } +} \ No newline at end of file diff --git a/Horizon/ViewModel/Panes/PageViewModel.cs b/Horizon/ViewModel/Panes/PageViewModel.cs new file mode 100644 index 0000000..e3f2601 --- /dev/null +++ b/Horizon/ViewModel/Panes/PageViewModel.cs @@ -0,0 +1,32 @@ +using ReactiveUI; +using ReactiveUI.Fody.Helpers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Horizon.ViewModel.Panes; + +public abstract class PageViewModel : PaneViewModel +{ + public PageViewModel() + { + this.WhenAnyValue(x => x.ID) + .Select(id => $"{this.GetType().Name}|{id}") + .ToPropertyEx(this, x => x.ContentID); + } + + [Reactive] + public int ID { get; set; } + + [Reactive] + public bool IsDirty { get; set; } + + [Reactive] + public bool IsReadOnly { get; set; } + + [Reactive] + public string? ReadOnlyReason { get; set; } +} \ No newline at end of file diff --git a/Horizon/ViewModel/Panes/PaneViewModel.cs b/Horizon/ViewModel/Panes/PaneViewModel.cs new file mode 100644 index 0000000..1d890a5 --- /dev/null +++ b/Horizon/ViewModel/Panes/PaneViewModel.cs @@ -0,0 +1,22 @@ +using ReactiveUI; +using ReactiveUI.Fody.Helpers; +using System.Windows.Media; + +namespace Horizon.ViewModel.Panes; + +public abstract class PaneViewModel : ReactiveObject +{ + public abstract string Title { get; set; } + + [Reactive] + public ImageSource? IconSource { get; set; } + + [ObservableAsProperty] + public string ContentID { get; } = string.Empty; + + [Reactive] + public bool IsSelected { get; set; } + + [Reactive] + public bool IsActive { get; set; } +} \ No newline at end of file diff --git a/Horizon/ViewModel/Panes/ProjectExplorerViewModel.cs b/Horizon/ViewModel/Panes/ProjectExplorerViewModel.cs new file mode 100644 index 0000000..2edde45 --- /dev/null +++ b/Horizon/ViewModel/Panes/ProjectExplorerViewModel.cs @@ -0,0 +1,29 @@ +using ReactiveUI.Fody.Helpers; +using System.Windows.Media.Imaging; + +namespace Horizon.ViewModel.Panes; + +public sealed class ProjectExplorerViewModel : ToolViewModel +{ + public ProjectExplorerViewModel() + { + string path = "pack://application:,,,/Resources/Images/New.png"; + + BitmapImage src = new(); + + try + { + src.BeginInit(); + src.UriSource = new Uri(path); + src.EndInit(); + } + catch (UriFormatException) + { + } + + this.IconSource = src; + } + + [Reactive] + public override string Title { get; set; } = "Project Explorer"; +} \ No newline at end of file diff --git a/Horizon/ViewModel/Panes/ToolViewModel.cs b/Horizon/ViewModel/Panes/ToolViewModel.cs new file mode 100644 index 0000000..0cbfc61 --- /dev/null +++ b/Horizon/ViewModel/Panes/ToolViewModel.cs @@ -0,0 +1,16 @@ +using ReactiveUI.Fody.Helpers; +using System.Reactive.Linq; + +namespace Horizon.ViewModel.Panes; + +public abstract class ToolViewModel : PaneViewModel +{ + public ToolViewModel() + { + Observable.Return(this.GetType().Name) + .ToPropertyEx(this, x => x.ContentID); + } + + [Reactive] + public bool IsVisible { get; set; } = true; +} \ No newline at end of file diff --git a/Horizon/ViewModel/WorkspaceViewModel.cs b/Horizon/ViewModel/WorkspaceViewModel.cs index 3a1eea8..4f9f065 100644 --- a/Horizon/ViewModel/WorkspaceViewModel.cs +++ b/Horizon/ViewModel/WorkspaceViewModel.cs @@ -1,7 +1,135 @@ -using ReactiveUI; +using AvalonDock; +using AvalonDock.Layout.Serialization; +using Horizon.ViewModel.Panes; +using ReactiveUI; +using ReactiveUI.Fody.Helpers; +using System.Collections.ObjectModel; +using System.IO; +using System.Reactive; namespace Horizon.ViewModel; public sealed class WorkspaceViewModel : ReactiveObject { + public WorkspaceViewModel() + { + this.SaveLayout = ReactiveCommand.Create(OnSaveLayout); + this.LoadLayout = ReactiveCommand.Create(this.OnLoadLayout); + this.DocumentClosing = ReactiveCommand.Create(this.OnDocumentClosing); + + App.ViewModel + .WhenAnyValue(vm => vm.CurrentProject, vm => vm.CurrentProject!.Name, (x, y) => x) + .Subscribe(x => this.Title = x is null ? "Horizon" : $"Horizon - {x.Name}"); + } + + [Reactive] + public ObservableCollection Pages { get; set; } = []; + + [Reactive] + public ObservableCollection Tools { get; set; } = [new ProjectExplorerViewModel()]; + + public ReactiveCommand SaveLayout { get; set; } + + public ReactiveCommand LoadLayout { get; set; } + + public ReactiveCommand DocumentClosing { get; set; } + + [Reactive] + public PageViewModel? ActiveDocument { get; set; } + + [Reactive] + public string Title { get; set; } = "Horizon"; + + private static void OnSaveLayout(DockingManager? manager) + { + if (manager is null) + { + return; + } + + using StringWriter fs = new(); + XmlLayoutSerializer xmlLayout = new(manager); + + xmlLayout.Serialize(fs); + + string xmlLayoutString = fs.ToString(); + + if (string.IsNullOrWhiteSpace(xmlLayoutString)) + { + return; + } + + string fileName = Path.Combine(App.AppDataDirectory, "Layout.config"); + + File.WriteAllText(fileName, xmlLayoutString); + } + + private void OnDocumentClosing(DocumentClosingEventArgs args) + { + PageViewModel? page = this.Pages.FirstOrDefault(x => x.ContentID == args.Document.ContentId); + + if (page is null) + { + args.Cancel = true; + } + else + { + // prompt for save + int x = 0; + } + } + + private void CreateDefaultLayout() + { + //this.Pages.Add(new EventPageViewModel()); + } + + private void ReloadContentOnStartup(LayoutSerializationCallbackEventArgs args) + { + string sId = args.Model.ContentId; + + if (string.IsNullOrWhiteSpace(sId)) + { + args.Cancel = true; + return; + } + + //EventPageViewModel vm = new(); + //this.Pages.Add(vm); + //args.Content = vm; + + ///TODO: Do an activator create instance + a loader for open documents + string x = args.Model.ContentId; + } + + private void OnLoadLayout(DockingManager? manager) + { + if (manager is null) + { + return; + } + + string layoutFileName = Path.Combine(App.AppDataDirectory, "Layout.config"); + + if (!File.Exists(layoutFileName)) + { + this.CreateDefaultLayout(); + return; + } + + XmlLayoutSerializer serializer = new(manager); + + serializer.LayoutSerializationCallback += (_, args) => + { + if (args.Model.ContentId is null) + { + args.Cancel = true; + return; + } + + this.ReloadContentOnStartup(args); + }; + + serializer.Deserialize(layoutFileName); + } } \ No newline at end of file