diff --git a/src/Artemis.Core/Services/Interfaces/IPluginService.cs b/src/Artemis.Core/Services/Interfaces/IPluginService.cs index 931e31f95..25ac1e82a 100644 --- a/src/Artemis.Core/Services/Interfaces/IPluginService.cs +++ b/src/Artemis.Core/Services/Interfaces/IPluginService.cs @@ -99,6 +99,11 @@ namespace Artemis.Core.Services.Interfaces /// event EventHandler PluginUnloaded; + /// + /// Occurs when a plugin is being enabled + /// + event EventHandler PluginEnabling; + /// /// Occurs when a plugin has been enabled /// diff --git a/src/Artemis.Core/Services/PluginService.cs b/src/Artemis.Core/Services/PluginService.cs index 8c5565862..75cac73b3 100644 --- a/src/Artemis.Core/Services/PluginService.cs +++ b/src/Artemis.Core/Services/PluginService.cs @@ -287,10 +287,11 @@ namespace Artemis.Core.Services public void EnablePlugin(Plugin plugin, bool isAutoEnable = false) { + _logger.Debug("Enabling plugin {pluginInfo}", plugin.PluginInfo); + OnPluginEnabling(new PluginEventArgs(plugin.PluginInfo)); + lock (_plugins) { - _logger.Debug("Enabling plugin {pluginInfo}", plugin.PluginInfo); - try { plugin.SetEnabled(true, isAutoEnable); @@ -402,6 +403,7 @@ namespace Artemis.Core.Services public event EventHandler PluginLoading; public event EventHandler PluginLoaded; public event EventHandler PluginUnloaded; + public event EventHandler PluginEnabling; public event EventHandler PluginEnabled; public event EventHandler PluginDisabled; @@ -425,6 +427,11 @@ namespace Artemis.Core.Services PluginUnloaded?.Invoke(this, e); } + protected virtual void OnPluginEnabling(PluginEventArgs e) + { + PluginEnabling?.Invoke(this, e); + } + protected virtual void OnPluginEnabled(PluginEventArgs e) { PluginEnabled?.Invoke(this, e); diff --git a/src/Artemis.UI/DataModelVisualization/DataModelGroupViewModel.cs b/src/Artemis.UI/DataModelVisualization/DataModelGroupViewModel.cs deleted file mode 100644 index 0a3514cd7..000000000 --- a/src/Artemis.UI/DataModelVisualization/DataModelGroupViewModel.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Reflection; -using Artemis.Core.Plugins.Abstract.DataModels.Attributes; -using Stylet; - -namespace Artemis.UI.DataModelVisualization -{ - public abstract class DataModelVisualizationViewModel : PropertyChangedBase - { - public PropertyInfo PropertyInfo { get; protected set; } - public DataModelPropertyAttribute PropertyDescription { get; protected set; } - public DataModelViewModel Parent { get; protected set; } - - public abstract void Update(); - } -} \ No newline at end of file diff --git a/src/Artemis.UI/DataModelVisualization/DataModelListViewModel.cs b/src/Artemis.UI/DataModelVisualization/DataModelListViewModel.cs new file mode 100644 index 000000000..4c6ad5910 --- /dev/null +++ b/src/Artemis.UI/DataModelVisualization/DataModelListViewModel.cs @@ -0,0 +1,58 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Artemis.Core.Plugins.Abstract.DataModels.Attributes; +using Stylet; + +namespace Artemis.UI.DataModelVisualization +{ + public class DataModelListViewModel : DataModelVisualizationViewModel + { + public DataModelListViewModel(PropertyInfo propertyInfo, DataModelPropertyAttribute propertyDescription, DataModelVisualizationViewModel parent) + { + PropertyInfo = propertyInfo; + Parent = parent; + PropertyDescription = propertyDescription; + Children = new BindableCollection(); + } + + public BindableCollection Children { get; set; } + public IList List { get; set; } + public string Count { get; set; } + + public override void Update() + { + if (PropertyInfo != null && Parent?.Model != null && PropertyInfo.GetValue(Parent.Model) is IList listValue) + { + Model = new List(listValue.Cast()); + List = (IList) Model; + } + + var index = 0; + foreach (var item in List) + { + DataModelVisualizationViewModel child; + if (Children.Count < index ) + { + child = CreateChild(item); + Children.Add(child); + } + else + { + // TODO: This wont fly when mixing DataModels and DataModelProperties + child = Children[index]; + child.Model = item; + } + + child.Update(); + index++; + } + + while (Children.Count > List.Count) + Children.RemoveAt(Children.Count - 1); + + Count = $"{Children.Count} {(Children.Count == 1 ? "item" : "items")}"; + } + } +} \ No newline at end of file diff --git a/src/Artemis.UI/DataModelVisualization/DataModelPropertyViewModel.cs b/src/Artemis.UI/DataModelVisualization/DataModelPropertyViewModel.cs index bb9527018..bae43b1d5 100644 --- a/src/Artemis.UI/DataModelVisualization/DataModelPropertyViewModel.cs +++ b/src/Artemis.UI/DataModelVisualization/DataModelPropertyViewModel.cs @@ -1,39 +1,36 @@ using System; -using System.Linq.Expressions; using System.Reflection; using Artemis.Core.Plugins.Abstract.DataModels.Attributes; namespace Artemis.UI.DataModelVisualization { - public class DataModelPropertyViewModel : DataModelPropertyViewModel + public class DataModelPropertyViewModel : DataModelVisualizationViewModel { - private readonly Func _expression; - - public DataModelPropertyViewModel(PropertyInfo propertyInfo, DataModelPropertyAttribute propertyDescription, DataModelViewModel parent) + public DataModelPropertyViewModel(PropertyInfo propertyInfo, DataModelPropertyAttribute propertyDescription, DataModelVisualizationViewModel parent) { PropertyInfo = propertyInfo; Parent = parent; PropertyDescription = propertyDescription; - - var instance = Expression.Parameter(typeof(T), "instance"); - var body = Expression.Property(instance, propertyInfo); - _expression = Expression.Lambda>(body, instance).Compile(); } - public TP Value - { - get => BaseValue is TP value ? value : default; - set => BaseValue = value; - } + public bool IsListProperty { get; set; } + public string ListDescription { get; set; } + public Type PropertyType { get; set; } public override void Update() { - Value = _expression((T) Parent.Model); + if (PropertyInfo != null && Parent?.Model != null) + { + IsListProperty = false; + Model = PropertyInfo.GetValue(Parent.Model); + PropertyType = PropertyInfo.PropertyType; + } + else if (Parent is DataModelListViewModel listViewModel) + { + IsListProperty = true; + ListDescription = $"List item [{listViewModel.List.IndexOf(Model)}]"; + PropertyType = Model.GetType(); + } } } - - public abstract class DataModelPropertyViewModel : DataModelVisualizationViewModel - { - public object BaseValue { get; protected set; } - } } \ No newline at end of file diff --git a/src/Artemis.UI/DataModelVisualization/DataModelViewModel.cs b/src/Artemis.UI/DataModelVisualization/DataModelViewModel.cs index 997c3cd29..a690a1c95 100644 --- a/src/Artemis.UI/DataModelVisualization/DataModelViewModel.cs +++ b/src/Artemis.UI/DataModelVisualization/DataModelViewModel.cs @@ -14,7 +14,7 @@ namespace Artemis.UI.DataModelVisualization Children = new BindableCollection(); } - public DataModelViewModel(PropertyInfo propertyInfo, object model, DataModelPropertyAttribute propertyDescription, DataModelViewModel parent) + public DataModelViewModel(PropertyInfo propertyInfo, object model, DataModelPropertyAttribute propertyDescription, DataModelVisualizationViewModel parent) { PropertyInfo = propertyInfo; Model = model; @@ -25,7 +25,6 @@ namespace Artemis.UI.DataModelVisualization PopulateProperties(); } - public object Model { get; private set; } public BindableCollection Children { get; set; } public void PopulateProperties() @@ -33,33 +32,9 @@ namespace Artemis.UI.DataModelVisualization Children.Clear(); foreach (var propertyInfo in Model.GetType().GetProperties()) { - // Skip properties decorated with DataModelIgnore - if (Attribute.IsDefined(propertyInfo, typeof(DataModelIgnoreAttribute))) - continue; - - var dataModelPropertyAttribute = (DataModelPropertyAttribute) Attribute.GetCustomAttribute(propertyInfo, typeof(DataModelPropertyAttribute)); - // If no DataModelProperty attribute was provided, pull one out of our ass - if (dataModelPropertyAttribute == null) - dataModelPropertyAttribute = new DataModelPropertyAttribute {Name = propertyInfo.Name.Humanize()}; - - // For primitives, create a property view model, it may be null that is fine - if (propertyInfo.PropertyType.IsPrimitive || propertyInfo.PropertyType == typeof(string)) - { - // This may be slower than avoiding generics and Activator.CreateInstance but it allows for expression trees inside the VM we're creating - // here, this means slow creation but fast updates after that - var viewModelType = typeof(DataModelPropertyViewModel<,>).MakeGenericType(Model.GetType(), propertyInfo.PropertyType); - var viewModel = (DataModelVisualizationViewModel) Activator.CreateInstance(viewModelType, propertyInfo, dataModelPropertyAttribute, this); - Children.Add(viewModel); - } - // For other value types create a child view model if the value type is not null - else if (propertyInfo.PropertyType.IsClass || propertyInfo.PropertyType.IsStruct()) - { - var value = propertyInfo.GetValue(Model); - if (value == null) - continue; - - Children.Add(new DataModelViewModel(propertyInfo, value, dataModelPropertyAttribute, this)); - } + var child = CreateChild(propertyInfo); + if (child != null) + Children.Add(child); } } diff --git a/src/Artemis.UI/DataModelVisualization/DataModelVisualizationViewModel.cs b/src/Artemis.UI/DataModelVisualization/DataModelVisualizationViewModel.cs new file mode 100644 index 000000000..6a59bc69c --- /dev/null +++ b/src/Artemis.UI/DataModelVisualization/DataModelVisualizationViewModel.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections; +using System.Reflection; +using Artemis.Core.Extensions; +using Artemis.Core.Plugins.Abstract.DataModels.Attributes; +using Humanizer; +using Stylet; + +namespace Artemis.UI.DataModelVisualization +{ + public abstract class DataModelVisualizationViewModel : PropertyChangedBase + { + public PropertyInfo PropertyInfo { get; protected set; } + public DataModelPropertyAttribute PropertyDescription { get; protected set; } + public DataModelVisualizationViewModel Parent { get; protected set; } + public object Model { get; set; } + + public abstract void Update(); + + protected DataModelVisualizationViewModel CreateChild(PropertyInfo propertyInfo) + { + // Skip properties decorated with DataModelIgnore + if (Attribute.IsDefined(propertyInfo, typeof(DataModelIgnoreAttribute))) + return null; + + var dataModelPropertyAttribute = (DataModelPropertyAttribute) Attribute.GetCustomAttribute(propertyInfo, typeof(DataModelPropertyAttribute)); + // If no DataModelProperty attribute was provided, pull one out of our ass + if (dataModelPropertyAttribute == null) + dataModelPropertyAttribute = new DataModelPropertyAttribute {Name = propertyInfo.Name.Humanize()}; + + // For primitives, create a property view model, it may be null that is fine + if (propertyInfo.PropertyType.IsPrimitive || propertyInfo.PropertyType == typeof(string)) + return new DataModelPropertyViewModel(propertyInfo, dataModelPropertyAttribute, this); + if (typeof(IList).IsAssignableFrom(propertyInfo.PropertyType)) + return new DataModelListViewModel(propertyInfo, dataModelPropertyAttribute, this); + // For other value types create a child view model if the value type is not null + if (propertyInfo.PropertyType.IsClass || propertyInfo.PropertyType.IsStruct()) + { + var value = propertyInfo.GetValue(Model); + if (value == null) + return null; + + return new DataModelViewModel(propertyInfo, value, dataModelPropertyAttribute, this); + } + + return null; + } + + protected DataModelVisualizationViewModel CreateChild(object value) + { + var dataModelPropertyAttribute = new DataModelPropertyAttribute {Name = "Unknown property"}; + + // For primitives, create a property view model, it may be null that is fine + if (value.GetType().IsPrimitive || value is string) + return new DataModelPropertyViewModel(null, dataModelPropertyAttribute, this) {Model = value}; + // For other value types create a child view model if the value type is not null + if (value.GetType().IsClass || value.GetType().IsStruct()) + return new DataModelViewModel(null, value, dataModelPropertyAttribute, this); + + return null; + } + } +} \ 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 647139d3b..65a8ac1eb 100644 --- a/src/Artemis.UI/Screens/Settings/Debug/Tabs/DataModelDebugView.xaml +++ b/src/Artemis.UI/Screens/Settings/Debug/Tabs/DataModelDebugView.xaml @@ -19,6 +19,23 @@ + + + + + + + + + [List] + + + + + @@ -27,20 +44,27 @@ - [] + [] - + + + Visibility="{Binding Model, Converter={StaticResource NullToVisibilityConverter}}"/> + Visibility="{Binding Model, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Inverted}"/> diff --git a/src/Artemis.UI/Screens/Splash/SplashViewModel.cs b/src/Artemis.UI/Screens/Splash/SplashViewModel.cs index 47955c793..9a286719f 100644 --- a/src/Artemis.UI/Screens/Splash/SplashViewModel.cs +++ b/src/Artemis.UI/Screens/Splash/SplashViewModel.cs @@ -35,6 +35,8 @@ namespace Artemis.UI.Screens.Splash _pluginService.CopyingBuildInPlugins += OnPluginServiceOnCopyingBuildInPlugins; _pluginService.PluginLoading += OnPluginServiceOnPluginLoading; _pluginService.PluginLoaded += OnPluginServiceOnPluginLoaded; + _pluginService.PluginEnabling += PluginServiceOnPluginEnabling; + _pluginService.PluginEnabled += PluginServiceOnPluginEnabled; base.OnInitialActivate(); } @@ -57,6 +59,16 @@ namespace Artemis.UI.Screens.Splash Status = "Loading plugin: " + args.PluginInfo.Name; } + private void PluginServiceOnPluginEnabled(object sender, PluginEventArgs args) + { + Status = "Initializing UI"; + } + + private void PluginServiceOnPluginEnabling(object sender, PluginEventArgs args) + { + Status = "Enabling plugin: " + args.PluginInfo.Name; + } + private void OnPluginServiceOnCopyingBuildInPlugins(object sender, EventArgs args) { Status = "Updating built-in plugins"; diff --git a/src/Plugins/Artemis.Plugins.Modules.General/GeneralDataModel.cs b/src/Plugins/Artemis.Plugins.Modules.General/GeneralDataModel.cs index 1b849b988..c027f774e 100644 --- a/src/Plugins/Artemis.Plugins.Modules.General/GeneralDataModel.cs +++ b/src/Plugins/Artemis.Plugins.Modules.General/GeneralDataModel.cs @@ -1,4 +1,5 @@ -using Artemis.Core.Plugins.Abstract.DataModels; +using System.Collections.Generic; +using Artemis.Core.Plugins.Abstract.DataModels; using Artemis.Core.Plugins.Abstract.DataModels.Attributes; using SkiaSharp; @@ -9,6 +10,8 @@ namespace Artemis.Plugins.Modules.General public GeneralDataModel() { PlayerInfo = new PlayerInfo(); + IntsList = new List(); + PlayerInfosList = new List(); } [DataModelProperty(Name = "A test string", Description = "This is a test string that's not of any use outside testing!")] @@ -21,6 +24,9 @@ namespace Artemis.Plugins.Modules.General public PlayerInfo PlayerInfo { get; set; } public double UpdatesDividedByFour { get; set; } + + public List IntsList { get; set; } + public List PlayerInfosList { get; set; } } public class PlayerInfo : DataModel diff --git a/src/Plugins/Artemis.Plugins.Modules.General/GeneralModule.cs b/src/Plugins/Artemis.Plugins.Modules.General/GeneralModule.cs index 2651738aa..f7bebfc53 100644 --- a/src/Plugins/Artemis.Plugins.Modules.General/GeneralModule.cs +++ b/src/Plugins/Artemis.Plugins.Modules.General/GeneralModule.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using Artemis.Core.Plugins.Abstract; -using Artemis.Core.Plugins.Abstract.DataModels; using Artemis.Core.Plugins.Abstract.ViewModels; using Artemis.Core.Plugins.Models; using Artemis.Plugins.Modules.General.ViewModels; @@ -12,7 +11,7 @@ namespace Artemis.Plugins.Modules.General public class GeneralModule : ProfileModule { private readonly PluginSettings _settings; - private Random _rand; + private readonly Random _rand; public GeneralModule(PluginSettings settings) { @@ -29,6 +28,9 @@ namespace Artemis.Plugins.Modules.General { DataModel.UpdatesDividedByFour += 0.25; DataModel.PlayerInfo.Position = new SKPoint(_rand.Next(100), _rand.Next(100)); + + DataModel.IntsList[0] = _rand.Next(); + DataModel.IntsList[2] = _rand.Next(); } public override void EnablePlugin() @@ -37,6 +39,8 @@ namespace Artemis.Plugins.Modules.General DisplayIcon = "AllInclusive"; ExpandsDataModel = true; + DataModel.IntsList = new List {_rand.Next(), _rand.Next(), _rand.Next()}; + DataModel.PlayerInfosList = new List {new PlayerInfo()}; var testSetting = _settings.GetSetting("TestSetting", DateTime.Now); }