diff --git a/src/Artemis.Core/Artemis.Core.csproj.DotSettings b/src/Artemis.Core/Artemis.Core.csproj.DotSettings index 5a6e7bef3..f99fab4de 100644 --- a/src/Artemis.Core/Artemis.Core.csproj.DotSettings +++ b/src/Artemis.Core/Artemis.Core.csproj.DotSettings @@ -38,6 +38,7 @@ True True True + True True True True diff --git a/src/Artemis.Core/Models/Profile/Conditions/DataModelConditionEvent.cs b/src/Artemis.Core/Models/Profile/Conditions/DataModelConditionEvent.cs index dd849741c..0ff661385 100644 --- a/src/Artemis.Core/Models/Profile/Conditions/DataModelConditionEvent.cs +++ b/src/Artemis.Core/Models/Profile/Conditions/DataModelConditionEvent.cs @@ -12,6 +12,7 @@ namespace Artemis.Core { private bool _disposed; private bool _reinitializing; + private IDataModelEvent? _valueChangedEvent; /// /// Creates a new instance of the class @@ -56,8 +57,11 @@ namespace Artemis.Core if (_disposed) throw new ObjectDisposedException("DataModelConditionEvent"); - if (EventPath?.GetValue() is not IDataModelEvent dataModelEvent) + IDataModelEvent? dataModelEvent = GetDataModelEvent(); + if (dataModelEvent == null) return false; + dataModelEvent.Update(); + // Only evaluate to true once every time the event has been triggered since the last evaluation if (dataModelEvent.LastTrigger <= LastTrigger) return false; @@ -86,6 +90,7 @@ namespace Artemis.Core EventPath?.Dispose(); EventPath = path != null ? new DataModelPath(path) : null; SubscribeToEventPath(); + CreateValueChangedEventIfNeeded(); // Remove the old root group that was tied to the old data model ClearChildren(); @@ -100,6 +105,19 @@ namespace Artemis.Core { EventArgumentType = null; } + + LastTrigger = GetDataModelEvent()?.LastTrigger ?? DateTime.Now; + } + + /// + /// Returns the this is triggered by + /// + /// The this is triggered by + public IDataModelEvent? GetDataModelEvent() + { + if (_valueChangedEvent != null) + return _valueChangedEvent; + return EventPath?.GetValue() as IDataModelEvent; } #region IDisposable @@ -153,14 +171,10 @@ namespace Artemis.Core if (Entity.EventPath == null) return; - // Ensure the list path is valid and points to a list DataModelPath eventPath = new(null, Entity.EventPath); - // Can't check this on an invalid list, if it becomes valid later lets hope for the best - if (eventPath.IsValid && !PointsToEvent(eventPath)) - return; - EventPath = eventPath; SubscribeToEventPath(); + CreateValueChangedEventIfNeeded(); EventArgumentType = GetEventArgumentType(); // There should only be one child and it should be a group @@ -174,8 +188,7 @@ namespace Artemis.Core AddChild(new DataModelConditionGroup(this)); } - if (EventPath?.GetValue() is IDataModelEvent dataModelEvent) - LastTrigger = dataModelEvent.LastTrigger; + LastTrigger = GetDataModelEvent()?.LastTrigger ?? DateTime.Now; } private Type? GetEventArgumentType() @@ -183,20 +196,14 @@ namespace Artemis.Core if (EventPath == null || !EventPath.IsValid) return null; + if (_valueChangedEvent != null) + return _valueChangedEvent.ArgumentsType; + // Cannot rely on EventPath.GetValue() because part of the path might be null Type eventType = EventPath.GetPropertyType()!; return eventType.IsGenericType ? eventType.GetGenericArguments()[0] : typeof(DataModelEventArgs); } - private bool PointsToEvent(DataModelPath dataModelPath) - { - Type? type = dataModelPath.GetPropertyType(); - if (type == null) - return false; - - return typeof(IDataModelEvent).IsAssignableFrom(type); - } - private void SubscribeToEventPath() { if (EventPath == null) return; @@ -204,6 +211,23 @@ namespace Artemis.Core EventPath.PathInvalidated += EventPathOnPathInvalidated; } + private void CreateValueChangedEventIfNeeded() + { + Type? propertyType = EventPath?.GetPropertyType(); + if (propertyType == null) + return; + + if (!typeof(IDataModelEvent).IsAssignableFrom(propertyType)) + { + IDataModelEvent? instance = (IDataModelEvent?) Activator.CreateInstance(typeof(DataModelValueChangedEvent<>).MakeGenericType(propertyType), EventPath); + _valueChangedEvent = instance ?? throw new ArtemisCoreException("Failed to create a DataModelValueChangedEvent for a property changed data model event"); + } + else + { + _valueChangedEvent = null; + } + } + #region Event handlers private void EventPathOnPathValidated(object? sender, EventArgs e) diff --git a/src/Artemis.Core/Models/Profile/DataModel/DataModelEvent.cs b/src/Artemis.Core/Models/Profile/DataModel/DataModelEvent.cs index e4165a125..aeca78a60 100644 --- a/src/Artemis.Core/Models/Profile/DataModel/DataModelEvent.cs +++ b/src/Artemis.Core/Models/Profile/DataModel/DataModelEvent.cs @@ -84,6 +84,9 @@ namespace Artemis.Core [DataModelIgnore] public Type ArgumentsType => typeof(T); + /// + public string TriggerPastParticiple => "triggered"; + /// [DataModelIgnore] public bool TrackHistory @@ -113,6 +116,11 @@ namespace Artemis.Core TriggerCount = 0; EventArgumentsHistory.Clear(); } + + /// + public void Update() + { + } } /// @@ -192,6 +200,9 @@ namespace Artemis.Core [DataModelIgnore] public Type ArgumentsType => typeof(DataModelEventArgs); + /// + public string TriggerPastParticiple => "triggered"; + /// [DataModelIgnore] public bool TrackHistory @@ -221,5 +232,10 @@ namespace Artemis.Core TriggerCount = 0; EventArgumentsHistory.Clear(); } + + /// + public void Update() + { + } } } \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/DataModel/DataModelEventArgs.cs b/src/Artemis.Core/Models/Profile/DataModel/DataModelEventArgs.cs index 3e20a9b1c..4a0431155 100644 --- a/src/Artemis.Core/Models/Profile/DataModel/DataModelEventArgs.cs +++ b/src/Artemis.Core/Models/Profile/DataModel/DataModelEventArgs.cs @@ -1,4 +1,5 @@ using System; +using Artemis.Core.DataModelExpansions; namespace Artemis.Core { @@ -10,6 +11,7 @@ namespace Artemis.Core /// /// Gets the time at which the event with these arguments was triggered /// + [DataModelIgnore] public DateTime TriggerTime { get; internal set; } } } \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/DataModel/IDataModelEvent.cs b/src/Artemis.Core/Models/Profile/DataModel/IDataModelEvent.cs index 36b9c3824..954c2b7a6 100644 --- a/src/Artemis.Core/Models/Profile/DataModel/IDataModelEvent.cs +++ b/src/Artemis.Core/Models/Profile/DataModel/IDataModelEvent.cs @@ -3,7 +3,10 @@ using System.Collections.Generic; namespace Artemis.Core { - internal interface IDataModelEvent + /// + /// Represents a data model event that can trigger s. + /// + public interface IDataModelEvent { /// /// Gets the last time the event was triggered @@ -20,6 +23,11 @@ namespace Artemis.Core /// Type ArgumentsType { get; } + /// + /// Gets the past participle for this event shown in the UI + /// + string TriggerPastParticiple { get; } + /// /// Gets or sets a boolean indicating whether the last 20 events should be tracked /// Note: setting this to will clear the current history @@ -46,5 +54,11 @@ namespace Artemis.Core /// Resets the trigger count and history of this data model event /// void Reset(); + + /// + /// Updates the event, not required for standard events but included in case your custom event needs to update every + /// tick + /// + void Update(); } } \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/DataModel/ValueChangedEvent/DataModelValueChangedEvent.cs b/src/Artemis.Core/Models/Profile/DataModel/ValueChangedEvent/DataModelValueChangedEvent.cs new file mode 100644 index 000000000..3a2ee7de8 --- /dev/null +++ b/src/Artemis.Core/Models/Profile/DataModel/ValueChangedEvent/DataModelValueChangedEvent.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; + +namespace Artemis.Core +{ + internal class DataModelValueChangedEvent : IDataModelEvent + { + public DataModelValueChangedEvent(DataModelPath path) + { + Path = path; + } + + public DataModelPath Path { get; } + public T? LastValue { get; private set; } + public T? CurrentValue { get; private set; } + public DateTime LastTrigger { get; private set; } + public int TriggerCount { get; private set; } + public Type ArgumentsType { get; } = typeof(DataModelValueChangedEventArgs); + public string TriggerPastParticiple => "changed"; + public bool TrackHistory { get; set; } = false; + public DataModelEventArgs? LastEventArgumentsUntyped { get; private set; } + public List EventArgumentsHistoryUntyped { get; } = new(); + + public void Update() + { + object? value = Path.GetValue(); + if (value != null) + CurrentValue = (T?) value; + else + CurrentValue = default; + + if (!Equals(LastValue, CurrentValue)) + Trigger(); + + LastValue = CurrentValue; + } + + public void Reset() + { + TriggerCount = 0; + } + + private void Trigger() + { + LastEventArgumentsUntyped = new DataModelValueChangedEventArgs(CurrentValue, LastValue); + LastTrigger = DateTime.Now; + TriggerCount++; + + OnEventTriggered(); + } + + #region Events + + public event EventHandler? EventTriggered; + + internal virtual void OnEventTriggered() + { + EventTriggered?.Invoke(this, EventArgs.Empty); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/DataModel/ValueChangedEvent/DataModelValueChangedEventArgs.cs b/src/Artemis.Core/Models/Profile/DataModel/ValueChangedEvent/DataModelValueChangedEventArgs.cs new file mode 100644 index 000000000..f82169770 --- /dev/null +++ b/src/Artemis.Core/Models/Profile/DataModel/ValueChangedEvent/DataModelValueChangedEventArgs.cs @@ -0,0 +1,18 @@ +using Artemis.Core.DataModelExpansions; + +namespace Artemis.Core +{ + internal class DataModelValueChangedEventArgs : DataModelEventArgs + { + public DataModelValueChangedEventArgs(T? currentValue, T? previousValue) + { + CurrentValue = currentValue; + PreviousValue = previousValue; + } + + [DataModelProperty(Description = "The current value of the property")] + public T? CurrentValue { get; } + [DataModelProperty(Description = "The previous value of the property")] + public T? PreviousValue { get; } + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionEventView.xaml b/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionEventView.xaml index 8b9a2b81b..0c93f64b5 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionEventView.xaml +++ b/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionEventView.xaml @@ -56,8 +56,8 @@ - triggered + Margin="5 0 0 0" Text="{Binding TriggerPastParticiple}"> + SetAndNotify(ref _lastTrigger, value); } + public string TriggerPastParticiple + { + get => _triggerPastParticiple; + set => SetAndNotify(ref _triggerPastParticiple, value); + } + public void Initialize() { LeftSideSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_profileEditorService.GetCurrentModule()); @@ -44,7 +51,10 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions LeftSideSelectionViewModel.LoadEventChildren = false; IReadOnlyCollection editors = _dataModelUIService.RegisteredDataModelEditors; - List supportedInputTypes = new() {typeof(DataModelEvent), typeof(DataModelEvent<>)}; + List supportedInputTypes = editors.Select(e => e.SupportedType).ToList(); + supportedInputTypes.AddRange(editors.Where(e => e.CompatibleConversionTypes != null).SelectMany(e => e.CompatibleConversionTypes)); + supportedInputTypes.Add(typeof(DataModelEvent)); + supportedInputTypes.Add(typeof(DataModelEvent<>)); LeftSideSelectionViewModel.FilterTypes = supportedInputTypes.ToArray(); LeftSideSelectionViewModel.ButtonBrush = new SolidColorBrush(Color.FromRgb(185, 164, 10)); @@ -63,6 +73,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions if (DataModelConditionEvent.EventPath == null || !DataModelConditionEvent.EventPath.IsValid) return; + TriggerPastParticiple = DataModelConditionEvent.GetDataModelEvent()?.TriggerPastParticiple; List viewModels = new(); foreach (DataModelConditionPart childModel in Model.Children) { diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs index 4c4088d79..4d27041d7 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs @@ -322,8 +322,6 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties .Where(l => l.LayerPropertyGroup.LayerEffect != null && !SelectedProfileElement.LayerEffects.Contains(l.LayerPropertyGroup.LayerEffect)) .ToList(); Items.RemoveRange(toRemove); - foreach (LayerPropertyGroupViewModel layerPropertyGroupViewModel in toRemove) - layerPropertyGroupViewModel.RequestClose(); foreach (BaseLayerEffect layerEffect in SelectedProfileElement.LayerEffects) { diff --git a/src/Artemis.UI/Screens/Settings/Device/Tabs/DevicePropertiesTabViewModel.cs b/src/Artemis.UI/Screens/Settings/Device/Tabs/DevicePropertiesTabViewModel.cs index 59f3ddc57..a03ce103e 100644 --- a/src/Artemis.UI/Screens/Settings/Device/Tabs/DevicePropertiesTabViewModel.cs +++ b/src/Artemis.UI/Screens/Settings/Device/Tabs/DevicePropertiesTabViewModel.cs @@ -149,7 +149,7 @@ namespace Artemis.UI.Screens.Settings.Device.Tabs Device.GreenScale = GreenScale / 100f; Device.BlueScale = BlueScale / 100f; _rgbService.SaveDevice(Device); - + _coreService.ModuleRenderingDisabled = false; }