diff --git a/src/Artemis.Core/Plugins/Modules/Module.cs b/src/Artemis.Core/Plugins/Modules/Module.cs
index c891fa422..cd2d9a19e 100644
--- a/src/Artemis.Core/Plugins/Modules/Module.cs
+++ b/src/Artemis.Core/Plugins/Modules/Module.cs
@@ -69,19 +69,16 @@ namespace Artemis.Core.Modules
///
/// The modules display name that's shown in the menu
///
- public string DisplayName { get; protected set; }
+ public string? DisplayName { get; protected set; }
///
- /// The modules display icon that's shown in the menu see for available
- /// icons
+ /// The modules display icon that's shown in the UI accepts:
+ ///
+ /// Either set to the name of a Material Icon see ( for available
+ /// icons) or set to a path relative to the plugin folder pointing to a .svg file
+ ///
///
- public string DisplayIcon { get; set; }
-
- ///
- /// A path to an image to use as the modules display icon that's shown in the menu.
- /// If set, takes precedence over
- ///
- public string DisplayIconPath { get; set; }
+ public string? DisplayIcon { get; set; }
///
/// Gets whether this module is activated. A module can only be active while its
@@ -97,7 +94,8 @@ namespace Artemis.Core.Modules
///
/// Gets whether this module should update while is . When
- /// set to and any timed updates will not get called during an activation override.
+ /// set to and any timed updates will not get called during an
+ /// activation override.
/// Defaults to
///
public bool UpdateDuringActivationOverride { get; protected set; }
@@ -129,21 +127,21 @@ namespace Artemis.Core.Modules
///
public int Priority { get; internal set; }
- internal DataModel InternalDataModel { get; set; }
-
- internal bool InternalExpandsMainDataModel { get; set; }
- internal ModuleSettingsEntity Entity { get; set; }
-
///
/// A list of custom module tabs that show in the UI
///
- public IEnumerable ModuleTabs { get; protected set; }
+ public IEnumerable? ModuleTabs { get; protected set; }
///
/// Gets whether updating this module is currently allowed
///
public bool IsUpdateAllowed => IsActivated && (UpdateDuringActivationOverride || !IsActivatedOverride);
+ internal DataModel? InternalDataModel { get; set; }
+
+ internal bool InternalExpandsMainDataModel { get; set; }
+ internal ModuleSettingsEntity? Entity { get; set; }
+
///
/// Called each frame when the module should update
///
diff --git a/src/Artemis.Core/Plugins/PluginInfo.cs b/src/Artemis.Core/Plugins/PluginInfo.cs
index 414b33d44..1c071d7cd 100644
--- a/src/Artemis.Core/Plugins/PluginInfo.cs
+++ b/src/Artemis.Core/Plugins/PluginInfo.cs
@@ -176,5 +176,10 @@ namespace Artemis.Core
{
return File.Exists(Path.Combine(Directory.FullName, "artemis.lock"));
}
+
+ public string? ResolveRelativePath(string path)
+ {
+ return path == null ? null : Path.Combine(Directory.FullName, path);
+ }
}
}
\ No newline at end of file
diff --git a/src/Artemis.UI.Shared/Converters/TypeToStringConverter.cs b/src/Artemis.UI.Shared/Converters/TypeToStringConverter.cs
new file mode 100644
index 000000000..3afd7cc80
--- /dev/null
+++ b/src/Artemis.UI.Shared/Converters/TypeToStringConverter.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Globalization;
+using System.Windows.Data;
+using Artemis.Core;
+
+namespace Artemis.UI.Shared
+{
+ ///
+ /// Converts into .
+ ///
+ public class TypeToStringConverter : IValueConverter
+ {
+ ///
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ bool humanizeProvided = bool.TryParse(parameter?.ToString(), out bool humanize);
+ if (value is Type type)
+ return type.GetDisplayName(humanizeProvided && humanize);
+
+ return value?.ToString();
+ }
+
+ ///
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListViewModel.cs b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListViewModel.cs
index 4190752ea..c4ff3d4eb 100644
--- a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListViewModel.cs
+++ b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListViewModel.cs
@@ -12,6 +12,7 @@ namespace Artemis.UI.Shared
private string _countDisplay;
private IEnumerable _list;
private int _listCount;
+ private Type _displayValueType;
internal DataModelListViewModel(DataModel dataModel, DataModelVisualizationViewModel parent, DataModelPath dataModelPath) : base(dataModel, parent, dataModelPath)
{
@@ -30,6 +31,12 @@ namespace Artemis.UI.Shared
set => SetAndNotify(ref _listCount, value);
}
+ public Type DisplayValueType
+ {
+ get => _displayValueType;
+ set => SetAndNotify(ref _displayValueType, value);
+ }
+
public string CountDisplay
{
get => _countDisplay;
@@ -68,6 +75,7 @@ namespace Artemis.UI.Shared
return;
List = GetCurrentValue() as IEnumerable;
+ DisplayValueType = List?.GetType();
if (List == null)
return;
diff --git a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelPropertiesViewModel.cs b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelPropertiesViewModel.cs
index 6da643ac7..57623d7b4 100644
--- a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelPropertiesViewModel.cs
+++ b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelPropertiesViewModel.cs
@@ -1,4 +1,5 @@
-using Artemis.Core;
+using System;
+using Artemis.Core;
using Artemis.Core.DataModelExpansions;
using Artemis.UI.Shared.Services;
@@ -6,12 +7,21 @@ namespace Artemis.UI.Shared
{
public class DataModelPropertiesViewModel : DataModelVisualizationViewModel
{
+ private Type _displayValueType;
+
internal DataModelPropertiesViewModel(DataModel dataModel, DataModelVisualizationViewModel parent, DataModelPath dataModelPath) : base(dataModel, parent, dataModelPath)
{
}
+
+ public Type DisplayValueType
+ {
+ get => _displayValueType;
+ set => SetAndNotify(ref _displayValueType, value);
+ }
public override void Update(IDataModelUIService dataModelUIService)
{
+ DisplayValueType = DataModelPath?.GetPropertyType();
// Always populate properties
PopulateProperties(dataModelUIService);
@@ -28,15 +38,15 @@ namespace Artemis.UI.Shared
return Parent.IsRootViewModel ? DataModel : base.GetCurrentValue();
}
- internal override int GetChildDepth()
- {
- return PropertyDescription != null && !PropertyDescription.ResetsDepth ? Depth + 1 : 1;
- }
-
///
public override string ToString()
{
return DisplayPath ?? Path;
}
+
+ internal override int GetChildDepth()
+ {
+ return PropertyDescription != null && !PropertyDescription.ResetsDepth ? Depth + 1 : 1;
+ }
}
}
\ No newline at end of file
diff --git a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelVisualizationViewModel.cs b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelVisualizationViewModel.cs
index 4fb72523f..59764842e 100644
--- a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelVisualizationViewModel.cs
+++ b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelVisualizationViewModel.cs
@@ -81,7 +81,9 @@ namespace Artemis.UI.Shared
}
}
- public virtual string DisplayPath => string.Join(" › ", DataModelPath.Segments.Select(s => s.GetPropertyDescription()?.Name ?? s.Identifier));
+ public virtual string DisplayPath => DataModelPath != null
+ ? string.Join(" › ", DataModelPath.Segments.Select(s => s.GetPropertyDescription()?.Name ?? s.Identifier))
+ : null;
///
/// Updates the datamodel and if in an parent, any children
@@ -133,7 +135,7 @@ namespace Artemis.UI.Shared
}
if (looseMatch)
- IsMatchingFilteredTypes = filteredTypes.Any(t => t.IsCastableFrom(type) ||
+ IsMatchingFilteredTypes = filteredTypes.Any(t => t.IsCastableFrom(type) ||
t == typeof(Enum) && type.IsEnum ||
t == typeof(IEnumerable<>) && type.IsGenericEnumerable());
else
@@ -267,7 +269,7 @@ namespace Artemis.UI.Shared
// For other value types create a child view model
if (propertyType.IsClass || propertyType.IsStruct())
return new DataModelPropertiesViewModel(DataModel, this, dataModelPath) {Depth = depth};
-
+
return null;
}
diff --git a/src/Artemis.UI.Shared/DefaultTypes/DataModel/Display/DefaultDataModelDisplayViewModel.cs b/src/Artemis.UI.Shared/DefaultTypes/DataModel/Display/DefaultDataModelDisplayViewModel.cs
index fef262aa4..07d51249d 100644
--- a/src/Artemis.UI.Shared/DefaultTypes/DataModel/Display/DefaultDataModelDisplayViewModel.cs
+++ b/src/Artemis.UI.Shared/DefaultTypes/DataModel/Display/DefaultDataModelDisplayViewModel.cs
@@ -5,6 +5,11 @@
private bool _showNull;
private bool _showToString;
+ public DefaultDataModelDisplayViewModel()
+ {
+ ShowNull = true;
+ }
+
public bool ShowToString
{
get => _showToString;
diff --git a/src/Artemis.UI.Shared/Utilities/PluginUtilities.cs b/src/Artemis.UI.Shared/Utilities/PluginUtilities.cs
new file mode 100644
index 000000000..140b104bd
--- /dev/null
+++ b/src/Artemis.UI.Shared/Utilities/PluginUtilities.cs
@@ -0,0 +1,46 @@
+using System;
+using System.IO;
+using Artemis.Core;
+using Artemis.UI.Shared.Controls;
+using MaterialDesignThemes.Wpf;
+
+namespace Artemis.UI.Shared
+{
+ ///
+ /// Provides utilities for UI-related plugin tasks
+ ///
+ public static class PluginUtilities
+ {
+ ///
+ /// Transforms the provided icon so that it is usable by the control
+ ///
+ /// The info of the plugin the icon belongs to
+ ///
+ /// The icon, may be a string representation of a or a relative path
+ /// pointing to a .svg file
+ ///
+ ///
+ public static object GetPluginIcon(PluginInfo pluginInfo, string icon)
+ {
+ if (icon == null)
+ return PackIconKind.QuestionMarkCircle;
+
+ // Icon is provided as a path
+ if (icon.EndsWith(".svg"))
+ {
+ string iconPath = pluginInfo.ResolveRelativePath(icon);
+ if (!File.Exists(iconPath))
+ return PackIconKind.QuestionMarkCircle;
+ return iconPath;
+ }
+
+ // Icon is provided as string to avoid having to reference MaterialDesignThemes
+ bool parsedIcon = Enum.TryParse(icon, true, out PackIconKind iconEnum);
+ if (parsedIcon == false)
+ iconEnum = PackIconKind.QuestionMarkCircle;
+ return iconEnum;
+
+ return icon;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/App.xaml b/src/Artemis.UI/App.xaml
index ae724e222..ea1d2f328 100644
--- a/src/Artemis.UI/App.xaml
+++ b/src/Artemis.UI/App.xaml
@@ -24,9 +24,11 @@
-
+
+
+
diff --git a/src/Artemis.UI/ResourceDictionaries/SideNavigation.xaml b/src/Artemis.UI/ResourceDictionaries/SideNavigation.xaml
new file mode 100644
index 000000000..199df5fb4
--- /dev/null
+++ b/src/Artemis.UI/ResourceDictionaries/SideNavigation.xaml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Settings/Debug/Tabs/DataModelDebugView.xaml b/src/Artemis.UI/Screens/Settings/Debug/Tabs/DataModelDebugView.xaml
index e0f56aa7b..429cf4e16 100644
--- a/src/Artemis.UI/Screens/Settings/Debug/Tabs/DataModelDebugView.xaml
+++ b/src/Artemis.UI/Screens/Settings/Debug/Tabs/DataModelDebugView.xaml
@@ -10,6 +10,9 @@
xmlns:dataModel="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" d:DataContext="{d:DesignInstance local:DataModelDebugViewModel}">
+
+
+
@@ -60,7 +63,17 @@
-
+
+
+
+
+
+
+ []
+
+
+
+
@@ -69,7 +82,9 @@
-
+
+ []
+
- []
+ []
@@ -105,7 +120,7 @@
- []
+ []
List item []
diff --git a/src/Artemis.UI/Screens/Sidebar/SidebarViewModel.cs b/src/Artemis.UI/Screens/Sidebar/SidebarViewModel.cs
index cccc40208..9b4577cb8 100644
--- a/src/Artemis.UI/Screens/Sidebar/SidebarViewModel.cs
+++ b/src/Artemis.UI/Screens/Sidebar/SidebarViewModel.cs
@@ -15,6 +15,7 @@ using Artemis.UI.Screens.News;
using Artemis.UI.Screens.Settings;
using Artemis.UI.Screens.SurfaceEditor;
using Artemis.UI.Screens.Workshop;
+using Artemis.UI.Shared;
using MaterialDesignExtensions.Controls;
using MaterialDesignExtensions.Model;
using MaterialDesignThemes.Wpf;
@@ -143,19 +144,11 @@ namespace Artemis.UI.Screens.Sidebar
if (SidebarModules.Any(io => io.Value == module))
return;
- object icon;
- if (module.DisplayIconPath != null && File.Exists(Path.Combine(module.PluginInfo.Directory.FullName, module.DisplayIconPath)))
- icon = new BitmapImage(new Uri(Path.Combine(module.PluginInfo.Directory.FullName, module.DisplayIconPath)));
- else
+ FirstLevelNavigationItem sidebarItem = new FirstLevelNavigationItem
{
- // Icon is provided as string to avoid having to reference MaterialDesignThemes
- bool parsedIcon = Enum.TryParse(module.DisplayIcon, true, out PackIconKind iconEnum);
- if (parsedIcon == false)
- iconEnum = PackIconKind.QuestionMarkCircle;
- icon = iconEnum;
- }
-
- FirstLevelNavigationItem sidebarItem = new FirstLevelNavigationItem {Icon = icon, Label = module.DisplayName};
+ Icon = PluginUtilities.GetPluginIcon(module.PluginInfo, module.DisplayIcon),
+ Label = module.DisplayName
+ };
SidebarItems.Add(sidebarItem);
SidebarModules.Add(sidebarItem, module);
}
diff --git a/src/Plugins/Artemis.Plugins.Modules.General/Artemis.Plugins.Modules.General.csproj b/src/Plugins/Artemis.Plugins.Modules.General/Artemis.Plugins.Modules.General.csproj
index 5e84fc74f..8638b8d53 100644
--- a/src/Plugins/Artemis.Plugins.Modules.General/Artemis.Plugins.Modules.General.csproj
+++ b/src/Plugins/Artemis.Plugins.Modules.General/Artemis.Plugins.Modules.General.csproj
@@ -32,6 +32,11 @@
false
+
+
+ PreserveNewest
+
+
diff --git a/src/Plugins/Artemis.Plugins.Modules.General/GeneralModule.cs b/src/Plugins/Artemis.Plugins.Modules.General/GeneralModule.cs
index 1099f977d..9fc1b14f2 100644
--- a/src/Plugins/Artemis.Plugins.Modules.General/GeneralModule.cs
+++ b/src/Plugins/Artemis.Plugins.Modules.General/GeneralModule.cs
@@ -16,7 +16,7 @@ namespace Artemis.Plugins.Modules.General
public override void EnablePlugin()
{
DisplayName = "General";
- DisplayIcon = "AllInclusive";
+ DisplayIcon = "Images/bow.svg";
ExpandsDataModel = true;
ModuleTabs = new List {new ModuleTab("General")};
diff --git a/src/Plugins/Artemis.Plugins.Modules.General/Images/bow.svg b/src/Plugins/Artemis.Plugins.Modules.General/Images/bow.svg
new file mode 100644
index 000000000..66817473d
--- /dev/null
+++ b/src/Plugins/Artemis.Plugins.Modules.General/Images/bow.svg
@@ -0,0 +1,44 @@
+
+
+