Add quick interactions locally
This commit is contained in:
@@ -0,0 +1,210 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
using Barotrauma;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace QICrabUI
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains all logic for resolving styles in the framework
|
||||
/// </summary>
|
||||
public static class CUIGlobalStyleResolver
|
||||
{
|
||||
|
||||
|
||||
public static void OnComponentStyleChanged(CUIComponent host)
|
||||
{
|
||||
CUITypeMetaData meta = CUITypeMetaData.Get(host.GetType());
|
||||
host.ResolvedStyle = CUIStyle.Merge(meta.ResolvedDefaultStyle, host.Style);
|
||||
ApplyStyleOn(host.ResolvedStyle, host);
|
||||
host.InvokeOnStyleApplied();
|
||||
}
|
||||
|
||||
public static void OnComponentStylePropChanged(CUIComponent host, string key)
|
||||
{
|
||||
CUITypeMetaData meta = CUITypeMetaData.Get(host.GetType());
|
||||
|
||||
if (meta.ResolvedDefaultStyle.Props.ContainsKey(key))
|
||||
{
|
||||
host.ResolvedStyle[key] = meta.ResolvedDefaultStyle[key];
|
||||
}
|
||||
|
||||
if (host.Style.Props.ContainsKey(key))
|
||||
{
|
||||
host.ResolvedStyle[key] = host.Style[key];
|
||||
}
|
||||
|
||||
ApplyStylePropOn(host.ResolvedStyle, key, host, meta);
|
||||
host.InvokeOnStyleApplied();
|
||||
}
|
||||
|
||||
public static void OnPaletteChange(CUIPalette palette)
|
||||
{
|
||||
foreach (Type CUIType in CUIReflection.CUITypes.Values)
|
||||
{
|
||||
foreach (CUIComponent c in CUIComponent.ComponentsByType.GetPage(CUIType))
|
||||
{
|
||||
ApplyStyleOn(c.ResolvedStyle, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void OnDefaultStyleChanged(Type CUIType)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Merge default styles
|
||||
CUIReflection.CUITypeTree[CUIType].RunRecursive((node) =>
|
||||
{
|
||||
node.Meta.ResolvedDefaultStyle = CUIStyle.Merge(
|
||||
node.Parent?.Meta.ResolvedDefaultStyle,
|
||||
node.Meta.DefaultStyle
|
||||
);
|
||||
});
|
||||
|
||||
// Apply default styles
|
||||
CUIReflection.CUITypeTree[CUIType].RunRecursive((node) =>
|
||||
{
|
||||
foreach (CUIComponent c in CUIComponent.ComponentsByType.GetPage(node.T))
|
||||
{
|
||||
OnComponentStyleChanged(c);
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
CUI.Warning($"OnDefaultStyleChanged| {e}");
|
||||
}
|
||||
}
|
||||
|
||||
public static void OnDefaultStylePropChanged(Type CUIType, string key)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Merge default styles
|
||||
CUIReflection.CUITypeTree[CUIType].RunRecursive((node) =>
|
||||
{
|
||||
if (node.Parent != null)
|
||||
{
|
||||
if (node.Parent.Meta.ResolvedDefaultStyle.Props.ContainsKey(key))
|
||||
{
|
||||
node.Meta.ResolvedDefaultStyle[key] = node.Parent.Meta.ResolvedDefaultStyle[key];
|
||||
}
|
||||
}
|
||||
|
||||
if (node.Meta.DefaultStyle.Props.ContainsKey(key))
|
||||
{
|
||||
node.Meta.ResolvedDefaultStyle[key] = node.Meta.DefaultStyle[key];
|
||||
}
|
||||
});
|
||||
|
||||
// Apply default styles
|
||||
CUIReflection.CUITypeTree[CUIType].RunRecursive((node) =>
|
||||
{
|
||||
foreach (CUIComponent c in CUIComponent.ComponentsByType.GetPage(node.T))
|
||||
{
|
||||
OnComponentStylePropChanged(c, key);
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
CUI.Warning(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public static void ApplyStyleOn(CUIStyle style, CUIComponent target)
|
||||
{
|
||||
if (target == null)
|
||||
{
|
||||
CUI.Warning($"Style target is null");
|
||||
return;
|
||||
}
|
||||
|
||||
CUITypeMetaData meta = CUITypeMetaData.Get(target.GetType());
|
||||
|
||||
foreach (string name in style.Props.Keys)
|
||||
{
|
||||
ApplyStylePropOn(style, name, target, meta);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static string CUIPalettePrefix = "CUIPalette.";
|
||||
/// <summary>
|
||||
/// Applies 1 prop with name on the target
|
||||
/// </summary>
|
||||
public static void ApplyStylePropOn(CUIStyle style, string name, CUIComponent target, CUITypeMetaData meta = null)
|
||||
{
|
||||
if (target.Unreal) return;
|
||||
|
||||
if (target == null) { CUI.Warning($"Style target is null"); return; }
|
||||
|
||||
meta ??= CUITypeMetaData.Get(target.GetType());
|
||||
|
||||
PropertyInfo pi = meta.Assignable.GetValueOrDefault(name);
|
||||
|
||||
if (pi == null)
|
||||
{
|
||||
if (CUIPalette.NotifyExcessivePropStyles) CUI.Warning($"Can't apply style: Couldn't find {name} prop in {target}");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
string raw = style[name];
|
||||
|
||||
if (raw.StartsWith(CUIPalettePrefix))
|
||||
{
|
||||
PaletteExtractResult result = CUIPalette.Extract(raw.Substring(CUIPalettePrefix.Length), target.Palette);
|
||||
if (result.Ok)
|
||||
{
|
||||
raw = result.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (CUIPalette.NotifiMissingPropStyles)
|
||||
{
|
||||
CUI.Warning($"Can't find {raw.Substring(CUIPalettePrefix.Length)} palette style for {target}");
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
MethodInfo parse = CUIExtensions.Parse.GetValueOrDefault(pi.PropertyType);
|
||||
|
||||
parse ??= pi.PropertyType.GetMethod(
|
||||
"Parse",
|
||||
BindingFlags.Public | BindingFlags.Static,
|
||||
new Type[] { typeof(string) }
|
||||
);
|
||||
|
||||
if (parse == null)
|
||||
{
|
||||
CUI.Warning($"Can't parse style prop {name} for {target} because it's type {pi.PropertyType.Name} is missing Parse method");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
pi.SetValue(target, parse.Invoke(null, new object[] { raw }));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
CUI.Warning($"Can't parse {raw} into {pi.PropertyType.Name} for {target}");
|
||||
CUI.Warning(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
189
Quick Interactions/CSharp/Client/CrabUI/Style/CUIStyle.cs
Normal file
189
Quick Interactions/CSharp/Client/CrabUI/Style/CUIStyle.cs
Normal file
@@ -0,0 +1,189 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// In Fact just an observable dict
|
||||
/// </summary>
|
||||
public partial class CUIStyle : IEnumerable<KeyValuePair<string, string>>, ICloneable
|
||||
{
|
||||
public static CUIStyle DefaultFor(Type T) => CUITypeMetaData.Get(T).DefaultStyle;
|
||||
public static CUIStyle DefaultFor<T>() where T : CUIComponent => CUITypeMetaData.Get(typeof(T)).DefaultStyle;
|
||||
|
||||
IEnumerator<KeyValuePair<string, string>> IEnumerable<KeyValuePair<string, string>>.GetEnumerator()
|
||||
=> Props.GetEnumerator();
|
||||
|
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
|
||||
=> Props.GetEnumerator();
|
||||
|
||||
public void Add(string key, string value)
|
||||
{
|
||||
Props[key] = value;
|
||||
OnPropChanged?.Invoke(key, value);
|
||||
}
|
||||
|
||||
public void Clear() => Props.Clear();
|
||||
|
||||
/// <summary>
|
||||
/// Prop name -> value
|
||||
/// </summary>
|
||||
public Dictionary<string, string> Props = new();
|
||||
|
||||
public event Action<string, string> OnPropChanged;
|
||||
public event Action<CUIStyle> OnUse;
|
||||
|
||||
public virtual string this[string name]
|
||||
{
|
||||
get => Props.ContainsKey(name) ? Props[name] : "";
|
||||
set => Add(name, value);
|
||||
}
|
||||
|
||||
public object Clone()
|
||||
{
|
||||
CUIStyle style = new CUIStyle();
|
||||
style.Props = new Dictionary<string, string>(Props);
|
||||
return style;
|
||||
}
|
||||
|
||||
public static CUIStyle Merge(CUIStyle baseStyle, CUIStyle addedStyle)
|
||||
{
|
||||
CUIStyle result = new CUIStyle();
|
||||
|
||||
if (baseStyle != null)
|
||||
{
|
||||
foreach (string key in baseStyle.Props.Keys)
|
||||
{
|
||||
result[key] = baseStyle.Props[key];
|
||||
}
|
||||
}
|
||||
|
||||
if (addedStyle != null)
|
||||
{
|
||||
foreach (string key in addedStyle.Props.Keys)
|
||||
{
|
||||
result[key] = addedStyle.Props[key];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void Use(CUIStyle source)
|
||||
{
|
||||
Props = new Dictionary<string, string>(source.Props);
|
||||
OnUse?.Invoke(this);
|
||||
}
|
||||
|
||||
public void UseString(string raw)
|
||||
{
|
||||
Clear();
|
||||
|
||||
try
|
||||
{
|
||||
string content = raw.Split('{', '}')[1];
|
||||
if (content.Trim() == "") return;
|
||||
var pairs = content.Split(',').Select(s => s.Split(':').Select(sub => sub.Trim()).ToArray());
|
||||
|
||||
foreach (var pair in pairs)
|
||||
{
|
||||
Props[pair[0]] = pair[1];
|
||||
}
|
||||
}
|
||||
catch (Exception e) { CUI.Warning($"Style.UseString failed: {e.Message}"); }
|
||||
OnUse?.Invoke(this);
|
||||
}
|
||||
|
||||
public void UseXML(XElement element)
|
||||
{
|
||||
Clear();
|
||||
foreach (XElement prop in element.Elements())
|
||||
{
|
||||
Props[prop.Name.ToString()] = prop.Value;
|
||||
}
|
||||
OnUse?.Invoke(this);
|
||||
}
|
||||
|
||||
public static CUIStyle FromXML(XElement element)
|
||||
{
|
||||
CUIStyle style = new CUIStyle();
|
||||
style.UseXML(element);
|
||||
return style;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "{ " + String.Join(", ", Props.Select(kvp => $"{kvp.Key} : {kvp.Value}")) + " }";
|
||||
}
|
||||
|
||||
public static CUIStyle Parse(string raw)
|
||||
{
|
||||
CUIStyle style = new CUIStyle();
|
||||
style.UseString(raw);
|
||||
return style;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (!(obj is CUIStyle styleB)) return false;
|
||||
CUIStyle styleA = this;
|
||||
if (styleA is null && styleB is null) return true;
|
||||
if (styleA is null || styleB is null) return false;
|
||||
if (styleA.Props is null || styleB.Props is null) return false;
|
||||
if (styleA.Props.Count != styleB.Props.Count) return false;
|
||||
foreach (var (key, value) in styleA.Props)
|
||||
{
|
||||
if (!styleB.Props.ContainsKey(key)) return false;
|
||||
if (styleA[key] != styleB[key]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static CUIStyle operator +(CUIStyle styleA, CUIStyle styleB) => Merge(styleA, styleB);
|
||||
|
||||
public static bool operator ==(CUIStyle styleA, CUIStyle styleB)
|
||||
{
|
||||
if (styleA is null && styleB is null) return true;
|
||||
if (styleA is null || styleB is null) return false;
|
||||
if (styleA.Props is null || styleB.Props is null) return false;
|
||||
if (styleA.Props.Count != styleB.Props.Count) return false;
|
||||
foreach (var (key, value) in styleA.Props)
|
||||
{
|
||||
if (!styleB.Props.ContainsKey(key)) return false;
|
||||
if (styleA[key] != styleB[key]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool operator !=(CUIStyle styleA, CUIStyle styleB)
|
||||
{
|
||||
if (styleA is null && styleB is null) return false;
|
||||
if (styleA is null || styleB is null) return true;
|
||||
if (styleA.Props is null || styleB.Props is null) return true;
|
||||
if (styleA.Props.Count != styleB.Props.Count) return true;
|
||||
foreach (var (key, value) in styleA.Props)
|
||||
{
|
||||
if (!styleB.Props.ContainsKey(key)) return true;
|
||||
if (styleA[key] != styleB[key]) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Diagnostics;
|
||||
|
||||
using Barotrauma;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
using System.IO;
|
||||
using System.Diagnostics;
|
||||
namespace QICrabUI
|
||||
{
|
||||
public class CUIStyleLoader
|
||||
{
|
||||
internal static void InitStatic()
|
||||
{
|
||||
CUI.OnInit += () => LoadDefaultStyles();
|
||||
}
|
||||
|
||||
public static string DefaultStylesPath => Path.Combine(CUI.AssetsPath, "Default Styles.xml");
|
||||
|
||||
|
||||
public static void LoadDefaultStyles()
|
||||
{
|
||||
Stopwatch sw = Stopwatch.StartNew();
|
||||
|
||||
if (CUI.AssetsPath == null) return;
|
||||
if (!File.Exists(DefaultStylesPath)) return;
|
||||
|
||||
|
||||
Dictionary<Type, CUIStyle> DefaultStyles = new();
|
||||
|
||||
XDocument xdoc = XDocument.Load(DefaultStylesPath);
|
||||
XElement root = xdoc.Element("DefaultStyles");
|
||||
foreach (XElement componentStyle in root.Elements())
|
||||
{
|
||||
Type componentType = CUIReflection.GetComponentTypeByName(componentStyle.Name.ToString());
|
||||
if (componentType == null)
|
||||
{
|
||||
CUI.Warning($"Couldn't find type for default style {componentStyle.Name}");
|
||||
continue;
|
||||
}
|
||||
|
||||
DefaultStyles[componentType] = CUIStyle.FromXML(componentStyle);
|
||||
}
|
||||
sw.Stop();
|
||||
CUIDebug.Log($"Parsing default styles took {sw.ElapsedMilliseconds}ms");
|
||||
sw.Restart();
|
||||
|
||||
// It's heavy because CUITypeMetaData.Get creates defaults here
|
||||
foreach (Type T in DefaultStyles.Keys)
|
||||
{
|
||||
CUITypeMetaData.Get(T).DefaultStyle = DefaultStyles[T];
|
||||
}
|
||||
|
||||
sw.Stop();
|
||||
CUIDebug.Log($"Applying default styles took {sw.ElapsedMilliseconds}ms");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user