CrabUI
Loading...
Searching...
No Matches
CUIComponent.Commands.cs
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using System.Reflection;
5using System.Diagnostics;
6using System.Runtime.CompilerServices;
7using System.IO;
8using System.Collections.ObjectModel;
9using System.Collections.Specialized;
10
11using Barotrauma;
12using Microsoft.Xna.Framework;
13using Microsoft.Xna.Framework.Input;
14using Microsoft.Xna.Framework.Graphics;
15
16using System.Xml;
17using System.Xml.Linq;
18
19namespace CrabUI
20{
21
22 public class CommandAttribute : System.Attribute { }
23
24 /// <summary>
25 /// Can be dispatched up the component tree to notify parent about something
26 /// add pass some event data without creating a hard link
27 /// </summary>
28 /// <param name="Name"></param>
29 public record CUICommand(string Name, object Data = null);
30
31 /// <summary>
32 /// Can be dispatched down the component tree to pass some data to the children
33 /// without creating a hard link
34 /// </summary>
35 public record CUIData(string Name, object Data = null);
36 public partial class CUIComponent
37 {
38 private void SetupCommands()
39 {
40 // This is actually expensive
41 //AddCommands();
42 OnTreeChanged += UpdateDataTargets;
43 }
44
45
46 /// <summary>
47 /// This command will be dispatched up when some component specific event happens
48 /// </summary>
49 [CUISerializable] public string Command { get; set; }
50 /// <summary>
51 /// this will be executed on any command
52 /// </summary>
53 public event Action<CUICommand> OnAnyCommand;
54 /// <summary>
55 /// Will be executed when receiving any data
56 /// </summary>
57 public event Action<CUIData> OnAnyData;
58 /// <summary>
59 /// Happens when appropriate data is received
60 /// </summary>
61 public event Action<Object> OnConsume;
62 /// <summary>
63 /// Will consume data with this name
64 /// </summary>
65 [CUISerializable] public string Consumes { get; set; }
66
67 private bool reflectCommands;
68 [CUISerializable]
69 public bool ReflectCommands
70 {
71 get => reflectCommands;
72 set
73 {
74 reflectCommands = value;
75 OnAnyCommand += (command) =>
76 {
77 foreach (CUIComponent child in Children)
78 {
79 child.DispatchDown(new CUIData(command.Name, command.Data));
80 }
81 };
82 }
83 }
84
85 private bool retranslateCommands;
86 [CUISerializable]
87 public bool RetranslateCommands
88 {
89 get => retranslateCommands;
90 set
91 {
92 retranslateCommands = value;
93 OnAnyCommand += (command) =>
94 {
95 Parent?.DispatchUp(command);
96 };
97 }
98 }
99
100 /// <summary>
101 /// Optimization to data flow
102 /// If not empty component will search for consumers of the data
103 /// and pass it directly to them instead of broadcasting it
104 /// </summary>
105 //[CUISerializable]
106 public ObservableCollection<string> Emits
107 {
108 get => emits;
109 set
110 {
111 emits = value;
112 emits.CollectionChanged += (o, e) => UpdateDataTargets();
113 UpdateDataTargets();
114 }
115 }
116 private ObservableCollection<string> emits = new();
117
118 private void UpdateDataTargets()
119 {
120 if (Emits.Count > 0)
121 {
122 DataTargets.Clear();
123
124 RunRecursiveOn(this, (c) =>
125 {
126 if (Emits.Contains(c.Consumes))
127 {
128 if (!DataTargets.ContainsKey(c.Consumes)) DataTargets[c.Consumes] = new();
129 DataTargets[c.Consumes].Add(c);
130 }
131 });
132 }
133 }
134
135 /// <summary>
136 /// Consumers of emmited data, updates on tree change
137 /// </summary>
138 public Dictionary<string, List<CUIComponent>> DataTargets = new();
139
140
141 /// <summary>
142 /// All commands
143 /// </summary>
144 public Dictionary<string, Action<object>> Commands { get; set; } = new();
145
146 /// <summary>
147 /// Manually adds command
148 /// </summary>
149 /// <param name="name"></param>
150 /// <param name="action"></param>
151 public void AddCommand(string name, Action<object> action) => Commands.Add(name, action);
152 public void RemoveCommand(string name) => Commands.Remove(name);
153
154 /// <summary>
155 /// Executed autpmatically on component creation
156 /// Methods ending in "Command" will be added as commands
157 /// </summary>
158 private void AddCommands()
159 {
160 foreach (MethodInfo mi in this.GetType().GetMethods())
161 {
162 if (Attribute.IsDefined(mi, typeof(CommandAttribute)))
163 {
164 try
165 {
166 string name = mi.Name;
167 if (name != "Command" && name.EndsWith("Command"))
168 {
169 name = name.Substring(0, name.Length - "Command".Length);
170 }
171 AddCommand(name, mi.CreateDelegate<Action<object>>(this));
172 }
173 catch (Exception e)
174 {
175 Info($"{e.Message}\nMethod: {this.GetType()}.{mi.Name}");
176 }
177 }
178 }
179 }
180
181 /// <summary>
182 /// Dispathes command up the component tree until someone consumes it
183 /// </summary>
184 /// <param name="command"></param>
185 public void DispatchUp(CUICommand command)
186 {
187 if (OnAnyCommand != null) OnAnyCommand?.Invoke(command);
188 else if (Commands.ContainsKey(command.Name)) Execute(command);
189 else Parent?.DispatchUp(command);
190 }
191
192 /// <summary>
193 /// Dispathes command down the component tree until someone consumes it
194 /// </summary>
195 public void DispatchDown(CUIData data)
196 {
197 if (Emits.Contains(data.Name))
198 {
199 if (DataTargets.ContainsKey(data.Name))
200 {
201 foreach (CUIComponent target in DataTargets[data.Name])
202 {
203 target.OnConsume?.Invoke(data.Data);
204 }
205 }
206 }
207 else
208 {
209 if (Consumes == data.Name) OnConsume?.Invoke(data.Data);
210 else if (OnAnyData != null) OnAnyData.Invoke(data);
211 else
212 {
213 foreach (CUIComponent child in Children) child.DispatchDown(data);
214 }
215 }
216 }
217
218 /// <summary>
219 /// Will execute action corresponding to this command
220 /// </summary>
221 /// <param name="commandName"></param>
222 public void Execute(CUICommand command)
223 {
224 Commands.GetValueOrDefault(command.Name)?.Invoke(command.Data);
225 }
226 }
227}
Base class for all components.
Dictionary< string, Action< object > > Commands
All commands.
void DispatchDown(CUIData data)
Dispathes command down the component tree until someone consumes it.
string Consumes
Will consume data with this name.
Action< CUICommand > OnAnyCommand
this will be executed on any command
Dictionary< string, List< CUIComponent > > DataTargets
Consumers of emmited data, updates on tree change.
string Command
This command will be dispatched up when some component specific event happens.
void AddCommand(string name, Action< object > action)
Manually adds command.
void Execute(CUICommand command)
Will execute action corresponding to this command.
ObservableCollection< string > Emits
Optimization to data flow If not empty component will search for consumers of the data and pass it ...
void Info(object msg, [CallerFilePath] string source="", [CallerLineNumber] int lineNumber=0)
Prints component and then message.
Action< Object > OnConsume
Happens when appropriate data is received.
void DispatchUp(CUICommand command)
Dispathes command up the component tree until someone consumes it.
static void RunRecursiveOn(CUIComponent component, Action< CUIComponent > action)
designed to be versatile, in fact never used
Action< CUIData > OnAnyData
Will be executed when receiving any data.
record CUIData(string Name, object Data=null)
Can be dispatched down the component tree to pass some data to the children without creating a hard l...
record CUICommand(string Name, object Data=null)
Can be dispatched up the component tree to notify parent about something add pass some event data wit...