Browse Source

Added 'And' and 'Or' operator to the event system.

Kenric Nugteren 9 months ago
parent
commit
8f8ffbd56a
2 changed files with 263 additions and 23 deletions
  1. 224 23
      prs.shared/Grids/EventEditor/EventTriggerGrid.cs
  2. 39 0
      prs.stores/Events/Event.cs

+ 224 - 23
prs.shared/Grids/EventEditor/EventTriggerGrid.cs

@@ -1,6 +1,8 @@
-using InABox.Core;
+using com.sun.xml.@internal.bind.v2.runtime;
+using InABox.Core;
 using InABox.DynamicGrid;
 using InABox.WPF;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
 using PRS.Shared.Events;
 using PRS.Shared.Grids.EventEditor;
 using System;
@@ -9,6 +11,7 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 using System.Windows.Controls;
+using System.Windows.Markup.Localizer;
 using System.Windows.Media;
 
 namespace PRS.Shared;
@@ -17,6 +20,8 @@ public class EventTriggerContainer<TEvent, TDataModel> : BaseObject
     where TEvent : IEvent<TDataModel>
     where TDataModel : IEventDataModel
 {
+    public IEventTriggerParent<TEvent, TDataModel>? Parent { get; set; }
+
     public IEventTrigger<TEvent, TDataModel> Trigger { get; set; }
 
     public string TriggerType => Trigger.GetType().GetCaption();
@@ -28,11 +33,78 @@ public class EventTriggerGrid<TEvent, TDataModel> : DynamicItemsListGrid<EventTr
     where TEvent : IEvent<TDataModel>
     where TDataModel : IEventDataModel
 {
-    public IEnumerable<IEventTrigger<TEvent, TDataModel>> EventTriggers => Items.Select(x => x.Trigger);
+    public IEnumerable<IEventTrigger<TEvent, TDataModel>> EventTriggers => Items.Where(x => x.Parent is null).Select(x => x.Trigger);
 
     public EventTriggerGrid(IEnumerable<IEventTrigger<TEvent, TDataModel>> items)
     {
-        Items.AddRange(items.Select(x => new EventTriggerContainer<TEvent, TDataModel> { Trigger = x }));
+        foreach(var item in items)
+        {
+            AddItem(item, null);
+        }
+    }
+
+    private EventTriggerContainer<TEvent, TDataModel> GetContainer(IEventTrigger<TEvent, TDataModel> item)
+    {
+        return Items.First(x => x.Trigger == item);
+    }
+
+    private void AddItem(IEventTrigger<TEvent, TDataModel> item, IEventTriggerParent<TEvent, TDataModel>? parent)
+    {
+        Items.Add(new EventTriggerContainer<TEvent, TDataModel>
+        {
+            Trigger = item,
+            Parent = parent
+        });
+        if(item is IEventTriggerParent<TEvent, TDataModel> parentTrigger)
+        {
+            foreach(var child in parentTrigger.ChildTriggers)
+            {
+                AddItem(child, parentTrigger);
+            }
+        }
+    }
+
+    protected override IDynamicGridUIComponent CreateUIComponent()
+    {
+        var component = new DynamicGridTreeUIComponent<EventTriggerContainer<TEvent, TDataModel>, IEventTrigger<TEvent, TDataModel>?>(
+            x => LoadItem(x).Trigger,
+            x => LoadItem(x).Parent,
+            null,
+            (container, parent) =>
+            {
+                if (parent is IEventTriggerParent<TEvent, TDataModel> parentTrigger)
+                {
+                    parentTrigger.ChildTriggers.Add(container.Trigger);
+                    container.Parent = parentTrigger;
+                }
+            })
+        {
+            Parent = this,
+        };
+        component.OnContextMenuOpening += Component_OnContextMenuOpening;
+        return component;
+    }
+
+    private enum OperatorType
+    {
+        And,
+        Or
+    }
+
+    private void Component_OnContextMenuOpening(CoreTreeNode<IEventTrigger<TEvent, TDataModel>?>? node, ContextMenu menu)
+    {
+        if(node is null)
+        {
+            PopulateAddMenu(menu, null, null);
+        }
+        else
+        {
+            var andItem = menu.AddItem("And", null, null);
+            PopulateAddMenu(andItem, node.ID, OperatorType.And);
+
+            var orItem = menu.AddItem("Or", null, null);
+            PopulateAddMenu(orItem, node.ID, OperatorType.Or);
+        }
     }
 
     protected override void DoReconfigure(DynamicGridOptions options)
@@ -51,47 +123,176 @@ public class EventTriggerGrid<TEvent, TDataModel> : DynamicItemsListGrid<EventTr
         return cols;
     }
 
-    protected override void DoAdd(bool openEditorOnDirectEdit = false)
+    private void PopulateAddMenu(ItemsControl menu, IEventTrigger<TEvent, TDataModel>? trigger, OperatorType? op)
     {
         var types = EventUtils.GetEventTriggerTypes(typeof(TEvent));
 
-        var menu = new ContextMenu();
         foreach(var type in types)
         {
-            menu.AddItem(type.GetCaption(), null, type, MenuAdd_Click);
+            menu.AddMenuItem(type.GetCaption(), null, (type, trigger, op), MenuAdd_Click);
         }
-        menu.IsOpen = true;
     }
 
-    protected override void DoEdit()
+    private void MenuAdd_Click((Type type, IEventTrigger<TEvent, TDataModel>? trigger, OperatorType? op) tuple)
     {
-        if (SelectedRows.Length != 1) return;
+        if (tuple.type.IsGenericType)
+        {
+            tuple.type = tuple.type.MakeGenericType(typeof(TEvent).GenericTypeArguments);
+        }
+        var trigger = (Activator.CreateInstance(tuple.type) as IEventTrigger<TEvent, TDataModel>)!;
+        if (!EventTriggerEditors.EditTrigger(trigger))
+        {
+            return;
+        }
 
-        var row = SelectedRows.First();
-        var item = LoadItem(row);
-        if (EventTriggerEditors.EditTrigger(item.Trigger))
+        var container = new EventTriggerContainer<TEvent, TDataModel> { Trigger = trigger };
+
+        if(tuple.trigger is not null)
         {
-            UpdateRow(row, item);
-            DoChanged();
+            // Insert index of 'trigger' into 'parent'
+            int? parentInsertIndex = null;
+            IEventTriggerParent<TEvent, TDataModel>? parent = null;
+
+            var triggerContainer = GetContainer(tuple.trigger);
+            if(tuple.op == OperatorType.And && triggerContainer.Trigger is AndTrigger<TEvent, TDataModel> andTrigger)
+            {
+                parent = andTrigger;
+                parentInsertIndex = null;
+            }
+            else if(tuple.op == OperatorType.Or && triggerContainer.Trigger is OrTrigger<TEvent, TDataModel> orTrigger)
+            {
+                parent = orTrigger;
+                parentInsertIndex = null;
+            }
+            else if(tuple.op == OperatorType.And && triggerContainer.Parent is AndTrigger<TEvent, TDataModel> andTrigger2)
+            {
+                parent = andTrigger2;
+                parentInsertIndex = triggerContainer.Parent.ChildTriggers.IndexOf(triggerContainer.Trigger) + 1;
+            }
+            else if(tuple.op == OperatorType.Or && triggerContainer.Parent is OrTrigger<TEvent, TDataModel> orTrigger2)
+            {
+                parent = orTrigger2;
+                parentInsertIndex = triggerContainer.Parent.ChildTriggers.IndexOf(triggerContainer.Trigger) + 1;
+            }
+
+            if(parent is not null)
+            {
+                container.Parent = parent;
+                if (parentInsertIndex.HasValue && parentInsertIndex < parent.ChildTriggers.Count)
+                {
+                    parent.ChildTriggers.Insert(parentInsertIndex.Value, trigger);
+                }
+                else
+                {
+                    parent.ChildTriggers.Add(trigger);
+                }
+            }
+            else
+            {
+                if(tuple.op == OperatorType.And)
+                {
+                    parent = new AndTrigger<TEvent, TDataModel>();
+                }
+                else if(tuple.op == OperatorType.Or)
+                {
+                    parent = new OrTrigger<TEvent, TDataModel>();
+                }
+
+                if(parent is not null)
+                {
+                    int? itemsInsertIndex = null;
+
+                    var parentContainer = new EventTriggerContainer<TEvent, TDataModel>()
+                    {
+                        Trigger = parent,
+                        Parent = triggerContainer.Parent
+                    };
+                    if(triggerContainer.Parent is not null)
+                    {
+                        var index = triggerContainer.Parent.ChildTriggers.IndexOf(triggerContainer.Trigger);
+
+                        triggerContainer.Parent.ChildTriggers[index] = parent;
+                    }
+                    else
+                    {
+                        itemsInsertIndex = Items.IndexOf(triggerContainer);
+                    }
+
+                    triggerContainer.Parent = parent;
+                    container.Parent = parent;
+                    parent.ChildTriggers.Add(triggerContainer.Trigger);
+                    parent.ChildTriggers.Add(trigger);
+
+                    if (itemsInsertIndex.HasValue)
+                    {
+                        Items.Insert(itemsInsertIndex.Value, parentContainer);
+                    }
+                    else
+                    {
+                        Items.Add(parentContainer);
+                    }
+                }
+            }
         }
+
+        Items.Add(container);
+
+        RebuildItemsList();
+
+        Refresh(false, true);
+        DoChanged();
     }
 
-    private void MenuAdd_Click(Type type)
+    private void RebuildItemsList()
     {
-        if (type.IsGenericType)
+        var triggers = Items.Where(x => x.Parent is null).Select(x => x.Trigger).ToArray();
+        Items.Clear();
+        foreach(var item in triggers)
         {
-            type = type.MakeGenericType(typeof(TEvent).GenericTypeArguments);
+            AddItem(item, null);
         }
-        var trigger = (Activator.CreateInstance(type) as IEventTrigger<TEvent, TDataModel>)!;
-        if (!EventTriggerEditors.EditTrigger(trigger))
+    }
+
+    private IEnumerable<EventTriggerContainer<TEvent, TDataModel>> GetAllChildren(EventTriggerContainer<TEvent, TDataModel> item)
+    {
+        yield return item;
+        if(item.Trigger is IEventTriggerParent<TEvent, TDataModel> parent)
         {
-            return;
+            foreach(var childItem in parent.ChildTriggers.SelectMany(x => GetAllChildren(GetContainer(x))))
+            {
+                yield return childItem;
+            }
+        }
+    }
+
+    public override void DeleteItems(params CoreRow[] rows)
+    {
+        var toDelete = rows.SelectMany(x => GetAllChildren(LoadItem(x))).ToArray();
+        foreach(var item in toDelete)
+        {
+            item.Parent?.ChildTriggers.Remove(item.Trigger);
+            Items.Remove(item);
         }
+    }
 
-        Items.Add(new() { Trigger = trigger });
+    protected override void DoAdd(bool openEditorOnDirectEdit = false)
+    {
+        var menu = new ContextMenu();
+        PopulateAddMenu(menu, null, null);
+        menu.IsOpen = true;
+    }
 
-        Refresh(false, true);
-        DoChanged();
+    protected override void DoEdit()
+    {
+        if (SelectedRows.Length != 1) return;
+
+        var row = SelectedRows.First();
+        var item = LoadItem(row);
+        if (EventTriggerEditors.EditTrigger(item.Trigger))
+        {
+            UpdateRow(row, item);
+            DoChanged();
+        }
     }
 }
 

+ 39 - 0
prs.stores/Events/Event.cs

@@ -531,6 +531,45 @@ public interface IEventTrigger<TEvent, TDataModel> : IEventTrigger
     bool Check(TDataModel dataModel);
 }
 
+public interface IEventTriggerParent<TEvent, TDataModel> : IEventTrigger<TEvent, TDataModel>
+    where TEvent : IEvent<TDataModel>
+    where TDataModel : IEventDataModel
+{
+    public List<IEventTrigger<TEvent, TDataModel>> ChildTriggers { get; }
+}
+
+public class AndTrigger<TEvent, TDataModel> : IEventTrigger<TEvent, TDataModel>, IEventTriggerParent<TEvent, TDataModel>
+    where TEvent : IEvent<TDataModel>
+    where TDataModel : IEventDataModel
+{
+    public List<IEventTrigger<TEvent, TDataModel>> ChildTriggers { get; set; } = new();
+
+    public IEnumerable<string> ReferencedVariables => ChildTriggers.SelectMany(x => x.ReferencedVariables);
+
+    public string Description => "And";
+
+    public bool Check(TDataModel dataModel)
+    {
+        return ChildTriggers.All(x => x.Check(dataModel));
+    }
+}
+
+public class OrTrigger<TEvent, TDataModel> : IEventTrigger<TEvent, TDataModel>, IEventTriggerParent<TEvent, TDataModel>
+    where TEvent : IEvent<TDataModel>
+    where TDataModel : IEventDataModel
+{
+    public List<IEventTrigger<TEvent, TDataModel>> ChildTriggers { get; set; } = new();
+
+    public IEnumerable<string> ReferencedVariables => ChildTriggers.SelectMany(x => x.ReferencedVariables);
+
+    public string Description => "Or";
+
+    public bool Check(TDataModel dataModel)
+    {
+        return ChildTriggers.Any(x => x.Check(dataModel));
+    }
+}
+
 public interface IEventAction
 {
     IEnumerable<string> ReferencedVariables { get; }