using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Diagnostics; using Barotrauma; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Graphics; using HarmonyLib; using System.IO; using System.Xml; using System.Xml.Linq; namespace QICrabUI { public enum PaletteOrder { Primary, Secondary, Tertiary, Quaternary } public record PaletteExtractResult(bool Ok, string Value = null); /// /// Contains abstract values that could be referenced in Styles /// public class CUIPalette { internal static void InitStatic() { CUI.OnInit += () => { Initialize(); }; CUI.OnDispose += () => { LoadedPalettes.Clear(); }; } public override string ToString() => $"CUIPalette {Name}"; public static string PaletteSetsPath => Path.Combine(CUI.PalettesPath, "Sets"); public static string DefaultPalette = "Blue"; //TODO why is it here? how could sane person find these? public static bool NotifyExcessivePropStyles { get; set; } = false; public static bool NotifiMissingPropStyles { get; set; } = true; public static PaletteExtractResult Extract(string nestedName, PaletteOrder order) { CUIPalette palette = order switch { PaletteOrder.Primary => Primary, PaletteOrder.Secondary => Secondary, PaletteOrder.Tertiary => Tertiary, PaletteOrder.Quaternary => Quaternary, _ => Empty, }; if (!palette.Values.ContainsKey(nestedName)) return new PaletteExtractResult(false); return new PaletteExtractResult(true, palette.Values[nestedName]); } public static CUIPalette Empty => new CUIPalette(); public static Dictionary LoadedPalettes = new(); public static string Default = "Blue"; private static CUIPalette primary = new CUIPalette(); public static CUIPalette Primary { get => primary; set { if (value == null) return; primary = value; CUIGlobalStyleResolver.OnPaletteChange(primary); } } private static CUIPalette secondary = new CUIPalette(); public static CUIPalette Secondary { get => secondary; set { if (value == null) return; secondary = value; CUIGlobalStyleResolver.OnPaletteChange(secondary); } } private static CUIPalette tertiary = new CUIPalette(); public static CUIPalette Tertiary { get => tertiary; set { if (value == null) return; tertiary = value; CUIGlobalStyleResolver.OnPaletteChange(tertiary); } } private static CUIPalette quaternary = new CUIPalette(); public static CUIPalette Quaternary { get => quaternary; set { if (value == null) return; quaternary = value; CUIGlobalStyleResolver.OnPaletteChange(quaternary); } } public Dictionary Values = new(); public string Name = "???"; public string BaseColor { get; set; } = ""; public static void Initialize() { Stopwatch sw = Stopwatch.StartNew(); if (CUI.PalettesPath == null) return; LoadedPalettes.Clear(); LoadPalettes(); LoadSet(Path.Combine(PaletteSetsPath, DefaultPalette + ".xml")); // Primary = LoadedPalettes.GetValueOrDefault("red"); // Secondary = LoadedPalettes.GetValueOrDefault("purple"); // Tertiary = LoadedPalettes.GetValueOrDefault("blue"); // Quaternary = LoadedPalettes.GetValueOrDefault("cyan"); CUIDebug.Log($"CUIPalette.Initialize took {sw.ElapsedMilliseconds}ms"); } public static void LoadPalettes() { foreach (string file in Directory.GetFiles(CUI.PalettesPath, "*.xml")) { CUIPalette palette = LoadFrom(file); LoadedPalettes[palette.Name] = palette; } } public static CUIPalette FromXML(XElement root) { CUIPalette palette = new CUIPalette(); palette.Name = root.Attribute("Name")?.Value.ToString(); foreach (XElement element in root.Elements()) { foreach (XAttribute attribute in element.Attributes()) { palette.Values[$"{element.Name}.{attribute.Name}"] = attribute.Value; } if (element.Value != "") { palette.Values[$"{element.Name}"] = element.Value; } } return palette; } public static CUIPalette LoadFrom(string path) { CUIPalette palette = new CUIPalette(); try { XDocument xdoc = XDocument.Load(path); XElement root = xdoc.Root; palette = CUIPalette.FromXML(root); palette.Name ??= Path.GetFileNameWithoutExtension(path); } catch (Exception e) { CUI.Warning($"Failed to load palette from {path}"); CUI.Warning(e); } return palette; } public XElement ToXML() { XElement root = new XElement("Palette"); root.Add(new XAttribute("Name", Name)); root.Add(new XAttribute("BaseColor", BaseColor)); foreach (string key in Values.Keys) { string component = key.Split('.').ElementAtOrDefault(0); string prop = key.Split('.').ElementAtOrDefault(1); if (component == null) continue; if (root.Element(component) == null) root.Add(new XElement(component)); if (prop != null) { root.Element(component).Add(new XAttribute(prop, Values[key])); } else { root.Element(component).Value = Values[key]; } } return root; } public void SaveTo(string path) { try { XDocument xdoc = new XDocument(); xdoc.Add(this.ToXML()); xdoc.Save(path); } catch (Exception e) { CUI.Warning($"Failed to save palette to {path}"); CUI.Warning(e); } } public static void PaletteDemo() { if (CUI.AssetsPath == null) { CUI.Warning($"Can't load PaletteDemo, CUI.AssetsPath is null"); return; } void loadFrame(Vector2 offset, PaletteOrder order) { CUIFrame frame = CUIComponent.LoadFromFile(Path.Combine(CUI.AssetsPath, $"PaletteDemo.xml")); frame.DeepPalette = order; frame.Absolute = frame.Absolute with { Position = offset }; frame.AddCommand("Close", (o) => frame.RemoveSelf()); CUI.TopMain.Append(frame); } loadFrame(new Vector2(0, 0), PaletteOrder.Primary); loadFrame(new Vector2(180, 0), PaletteOrder.Secondary); loadFrame(new Vector2(360, 0), PaletteOrder.Tertiary); loadFrame(new Vector2(540, 0), PaletteOrder.Quaternary); } public static CUIPalette CreatePaletteFromColors(string name, Color colorA, Color? colorb = null) { CUIPalette palette = new CUIPalette() { Name = name, BaseColor = CUIExtensions.ColorToString(colorA), }; Color colorB = colorb ?? Color.Black; Dictionary colors = new(); colors["Frame.Background"] = colorA.To(colorB, 1.0f); colors["Header.Background"] = colorA.To(colorB, 0.7f); colors["Nav.Background"] = colorA.To(colorB, 0.8f); colors["Main.Background"] = colorA.To(colorB, 0.9f); colors["Frame.Border"] = colorA.To(colorB, 0.5f); colors["Header.Border"] = colorA.To(colorB, 0.6f); colors["Nav.Border"] = colorA.To(colorB, 0.7f); colors["Main.Border"] = colorA.To(colorB, 0.8f); colors["Frame.Text"] = colorA.To(Color.White, 0.9f); colors["Header.Text"] = colorA.To(Color.White, 0.9f); colors["Nav.Text"] = colorA.To(Color.White, 0.8f); colors["Main.Text"] = colorA.To(Color.White, 0.8f); colors["Component.Background"] = Color.Transparent; colors["Component.Border"] = Color.Transparent; colors["Component.Text"] = colors["Main.Text"]; colors["Button.Background"] = colorA.To(colorB, 0.0f); colors["Button.Border"] = colorA.To(colorB, 0.5f); colors["Button.Disabled"] = colorA.To(new Color(16, 16, 16), 0.8f); colors["CloseButton.Background"] = colorA.To(Color.White, 0.2f); colors["DDOption.Background"] = colors["Header.Background"]; colors["DDOption.Border"] = colors["Main.Border"]; colors["DDOption.Hover"] = colorA.To(colorB, 0.5f); colors["DDOption.Text"] = colors["Main.Text"]; colors["Handle.Background"] = colorA.To(colorB, 0.5f).To(Color.White, 0.2f); colors["Handle.Grabbed"] = colorA.To(colorB, 0.0f).To(Color.White, 0.2f); colors["Slider"] = colorA.To(Color.White, 0.7f); colors["Input.Background"] = colors["Nav.Background"]; colors["Input.Border"] = colors["Nav.Border"]; colors["Input.Text"] = colors["Main.Text"]; colors["Input.Focused"] = colorA; colors["Input.Invalid"] = Color.Red; colors["Input.Valid"] = Color.Lime; colors["Input.Selection"] = colorA.To(Color.White, 0.7f) * 0.5f; colors["Input.Caret"] = colorA.To(Color.White, 0.7f) * 0.5f; foreach (var (key, cl) in colors) { palette.Values[key] = CUIExtensions.ColorToString(cl); } palette.SaveTo(Path.Combine(CUI.PalettesPath, $"{name}.xml")); LoadedPalettes[name] = palette; CUI.Log($"Created {name} palette and saved it to {Path.Combine(CUI.PalettesPath, $"{name}.xml")}"); return palette; } /// /// Packs 4 palettes into 1 set /// /// /// /// /// /// public static void SaveSet(string setName, string primary = "", string secondary = "", string tertiary = "", string quaternary = "") { if (setName == null || setName == "") return; string savePath = Path.Combine(PaletteSetsPath, $"{setName}.xml"); try { XDocument xdoc = new XDocument(new XElement("PaletteSet")); XElement root = xdoc.Root; root.Add(new XAttribute("Name", setName)); root.Add((LoadedPalettes.GetValueOrDefault(primary ?? "") ?? Primary).ToXML()); root.Add((LoadedPalettes.GetValueOrDefault(secondary ?? "") ?? Secondary).ToXML()); root.Add((LoadedPalettes.GetValueOrDefault(tertiary ?? "") ?? Tertiary).ToXML()); root.Add((LoadedPalettes.GetValueOrDefault(quaternary ?? "") ?? Quaternary).ToXML()); xdoc.Save(savePath); CUI.Log($"Created {setName} palette set and saved it to {savePath}"); LoadSet(savePath); } catch (Exception e) { CUI.Warning($"Failed to save palette set to {savePath}"); CUI.Warning(e); } } public static void LoadSet(string path) { if (path == null || path == "") return; try { XDocument xdoc = XDocument.Load(path); XElement root = xdoc.Root; List palettes = new(); foreach (XElement element in root.Elements("Palette")) { palettes.Add(CUIPalette.FromXML(element)); } Primary = palettes.ElementAtOrDefault(0) ?? Empty; Secondary = palettes.ElementAtOrDefault(1) ?? Empty; Tertiary = palettes.ElementAtOrDefault(2) ?? Empty; Quaternary = palettes.ElementAtOrDefault(3) ?? Empty; } catch (Exception e) { CUI.Warning($"Failed to load palette set from {path}"); CUI.Warning(e); } } } }