Add quick interactions locally
This commit is contained in:
@@ -0,0 +1,227 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.IO;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
|
||||
using Barotrauma;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace QICrabUI
|
||||
{
|
||||
|
||||
public class CommandAttribute : System.Attribute { }
|
||||
|
||||
/// <summary>
|
||||
/// Can be dispatched up the component tree to notify parent about something
|
||||
/// add pass some event data without creating a hard link
|
||||
/// </summary>
|
||||
/// <param name="Name"></param>
|
||||
public record CUICommand(string Name, object Data = null);
|
||||
|
||||
/// <summary>
|
||||
/// Can be dispatched down the component tree to pass some data to the children
|
||||
/// without creating a hard link
|
||||
/// </summary>
|
||||
public record CUIData(string Name, object Data = null);
|
||||
public partial class CUIComponent
|
||||
{
|
||||
private void SetupCommands()
|
||||
{
|
||||
// This is actually expensive
|
||||
//AddCommands();
|
||||
OnTreeChanged += UpdateDataTargets;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// This command will be dispatched up when some component specific event happens
|
||||
/// </summary>
|
||||
[CUISerializable] public string Command { get; set; }
|
||||
/// <summary>
|
||||
/// this will be executed on any command
|
||||
/// </summary>
|
||||
public event Action<CUICommand> OnAnyCommand;
|
||||
/// <summary>
|
||||
/// Will be executed when receiving any data
|
||||
/// </summary>
|
||||
public event Action<CUIData> OnAnyData;
|
||||
/// <summary>
|
||||
/// Happens when appropriate data is received
|
||||
/// </summary>
|
||||
public event Action<Object> OnConsume;
|
||||
/// <summary>
|
||||
/// Will consume data with this name
|
||||
/// </summary>
|
||||
[CUISerializable] public string Consumes { get; set; }
|
||||
|
||||
private bool reflectCommands;
|
||||
[CUISerializable]
|
||||
public bool ReflectCommands
|
||||
{
|
||||
get => reflectCommands;
|
||||
set
|
||||
{
|
||||
reflectCommands = value;
|
||||
OnAnyCommand += (command) =>
|
||||
{
|
||||
foreach (CUIComponent child in Children)
|
||||
{
|
||||
child.DispatchDown(new CUIData(command.Name, command.Data));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private bool retranslateCommands;
|
||||
[CUISerializable]
|
||||
public bool RetranslateCommands
|
||||
{
|
||||
get => retranslateCommands;
|
||||
set
|
||||
{
|
||||
retranslateCommands = value;
|
||||
OnAnyCommand += (command) =>
|
||||
{
|
||||
Parent?.DispatchUp(command);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Optimization to data flow
|
||||
/// If not empty component will search for consumers of the data
|
||||
/// and pass it directly to them instead of broadcasting it
|
||||
/// </summary>
|
||||
//[CUISerializable]
|
||||
public ObservableCollection<string> Emits
|
||||
{
|
||||
get => emits;
|
||||
set
|
||||
{
|
||||
emits = value;
|
||||
emits.CollectionChanged += (o, e) => UpdateDataTargets();
|
||||
UpdateDataTargets();
|
||||
}
|
||||
}
|
||||
private ObservableCollection<string> emits = new();
|
||||
|
||||
private void UpdateDataTargets()
|
||||
{
|
||||
if (Emits.Count > 0)
|
||||
{
|
||||
DataTargets.Clear();
|
||||
|
||||
RunRecursiveOn(this, (c) =>
|
||||
{
|
||||
if (Emits.Contains(c.Consumes))
|
||||
{
|
||||
if (!DataTargets.ContainsKey(c.Consumes)) DataTargets[c.Consumes] = new();
|
||||
DataTargets[c.Consumes].Add(c);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Consumers of emmited data, updates on tree change
|
||||
/// </summary>
|
||||
public Dictionary<string, List<CUIComponent>> DataTargets = new();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// All commands
|
||||
/// </summary>
|
||||
public Dictionary<string, Action<object>> Commands { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Manually adds command
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="action"></param>
|
||||
public void AddCommand(string name, Action<object> action) => Commands.Add(name, action);
|
||||
public void RemoveCommand(string name) => Commands.Remove(name);
|
||||
|
||||
/// <summary>
|
||||
/// Executed autpmatically on component creation
|
||||
/// Methods ending in "Command" will be added as commands
|
||||
/// </summary>
|
||||
private void AddCommands()
|
||||
{
|
||||
foreach (MethodInfo mi in this.GetType().GetMethods())
|
||||
{
|
||||
if (Attribute.IsDefined(mi, typeof(CommandAttribute)))
|
||||
{
|
||||
try
|
||||
{
|
||||
string name = mi.Name;
|
||||
if (name != "Command" && name.EndsWith("Command"))
|
||||
{
|
||||
name = name.Substring(0, name.Length - "Command".Length);
|
||||
}
|
||||
AddCommand(name, mi.CreateDelegate<Action<object>>(this));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Info($"{e.Message}\nMethod: {this.GetType()}.{mi.Name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispathes command up the component tree until someone consumes it
|
||||
/// </summary>
|
||||
/// <param name="command"></param>
|
||||
public void DispatchUp(CUICommand command)
|
||||
{
|
||||
if (OnAnyCommand != null) OnAnyCommand?.Invoke(command);
|
||||
else if (Commands.ContainsKey(command.Name)) Execute(command);
|
||||
else Parent?.DispatchUp(command);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispathes command down the component tree until someone consumes it
|
||||
/// </summary>
|
||||
public void DispatchDown(CUIData data)
|
||||
{
|
||||
if (Emits.Contains(data.Name))
|
||||
{
|
||||
if (DataTargets.ContainsKey(data.Name))
|
||||
{
|
||||
foreach (CUIComponent target in DataTargets[data.Name])
|
||||
{
|
||||
target.OnConsume?.Invoke(data.Data);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Consumes == data.Name) OnConsume?.Invoke(data.Data);
|
||||
else if (OnAnyData != null) OnAnyData.Invoke(data);
|
||||
else
|
||||
{
|
||||
foreach (CUIComponent child in Children) child.DispatchDown(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will execute action corresponding to this command
|
||||
/// </summary>
|
||||
/// <param name="commandName"></param>
|
||||
public void Execute(CUICommand command)
|
||||
{
|
||||
Commands.GetValueOrDefault(command.Name)?.Invoke(command.Data);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user