CrabUI
Loading...
Searching...
No Matches
CUIPalette.cs
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using System.Reflection;
5using System.Diagnostics;
6
7using Barotrauma;
8using Microsoft.Xna.Framework;
9using Microsoft.Xna.Framework.Input;
10using Microsoft.Xna.Framework.Graphics;
11using HarmonyLib;
12using System.IO;
13using System.Xml;
14using System.Xml.Linq;
15
16namespace CrabUI
17{
18 public enum PaletteOrder { Primary, Secondary, Tertiary, Quaternary }
19
20 public record PaletteExtractResult(bool Ok, string Value = null);
21 /// <summary>
22 /// Contains abstract values that could be referenced in Styles
23 /// </summary>
24 public class CUIPalette
25 {
26 internal static void InitStatic()
27 {
28 CUI.OnInit += () => Initialize();
29 CUI.OnDispose += () => LoadedPalettes.Clear();
30 }
31
32 public override string ToString() => $"CUIPalette {Name}";
33 public static string PaletteSetsPath => Path.Combine(CUI.PalettesPath, "Sets");
34 /// <summary>
35 /// This palette set will be loadead on CUI.Initialize
36 /// </summary>
37 public static string DefaultPalette = "Blue";
38
39 //TODO why are they here? how could sane person find these?
40 /// <summary>
41 /// Notifies when you try to set a style to a prop that doesn't exist
42 /// </summary>
43 public static bool NotifyExcessivePropStyles { get; set; } = false;
44 /// <summary>
45 /// Notifies when prop asks for style that is missing in palette
46 /// </summary>
47 public static bool NotifiMissingPropStyles { get; set; } = true;
48
49 public Dictionary<string, string> Values = BackupPalette;
50
51 public string Name = "???";
52 public string BaseColor { get; set; } = "";
53
54 public static Dictionary<string, CUIPalette> LoadedPalettes = new();
55 public static CUIPalette Empty => new CUIPalette();
56 public static Dictionary<string, string> BackupPalette => new Dictionary<string, string>()
57 {
58 ["Frame.Background"] = "0,0,0,200",
59 ["Frame.Border"] = "0,0,127,227",
60 ["Frame.Text"] = "229,229,255,255",
61 ["Header.Background"] = "0,0,76,216",
62 ["Header.Border"] = "0,0,102,222",
63 ["Header.Text"] = "229,229,255,255",
64 ["Nav.Background"] = "0,0,51,211",
65 ["Nav.Border"] = "0,0,76,216",
66 ["Nav.Text"] = "204,204,255,255",
67 ["Main.Background"] = "0,0,25,205",
68 ["Main.Border"] = "0,0,51,211",
69 ["Main.Text"] = "204,204,255,255",
70 ["Component.Background"] = "0,0,0,0",
71 ["Component.Border"] = "0,0,0,0",
72 ["Component.Text"] = "204,204,255,255",
73 ["Button.Background"] = "0,0,255,255",
74 ["Button.Border"] = "0,0,127,227",
75 ["Button.Disabled"] = "12,12,63,255",
76 ["CloseButton.Background"] = "51,51,255,255",
77 ["DDOption.Background"] = "0,0,76,216",
78 ["DDOption.Border"] = "0,0,51,211",
79 ["DDOption.Hover"] = "0,0,127,227",
80 ["DDOption.Text"] = "204,204,255,255",
81 ["Handle.Background"] = "51,51,152,232",
82 ["Handle.Grabbed"] = "51,51,255,255",
83 ["Slider"] = "178,178,255,255",
84 ["Input.Background"] = "0,0,51,211",
85 ["Input.Border"] = "0,0,76,216",
86 ["Input.Text"] = "204,204,255,255",
87 ["Input.Focused"] = "0,0,255,255",
88 ["Input.Invalid"] = "255,0,0,255",
89 ["Input.Valid"] = "0,255,0,255",
90 ["Input.Selection"] = "178,178,255,127",
91 ["Input.Caret"] = "178,178,255,127",
92 };
93
94
95 private static CUIPalette primary = new CUIPalette();
96 public static CUIPalette Primary
97 {
98 get => primary;
99 set
100 {
101 if (value == null) return;
102 primary = value;
103 CUIGlobalStyleResolver.OnPaletteChange(primary);
104 }
105 }
106
107 private static CUIPalette secondary = new CUIPalette();
108 public static CUIPalette Secondary
109 {
110 get => secondary;
111 set
112 {
113 if (value == null) return;
114 secondary = value;
115 CUIGlobalStyleResolver.OnPaletteChange(secondary);
116 }
117 }
118
119 private static CUIPalette tertiary = new CUIPalette();
120 public static CUIPalette Tertiary
121 {
122 get => tertiary;
123 set
124 {
125 if (value == null) return;
126 tertiary = value;
127 CUIGlobalStyleResolver.OnPaletteChange(tertiary);
128 }
129 }
130
131 private static CUIPalette quaternary = new CUIPalette();
132 public static CUIPalette Quaternary
133 {
134 get => quaternary;
135 set
136 {
137 if (value == null) return;
138 quaternary = value;
139 CUIGlobalStyleResolver.OnPaletteChange(quaternary);
140 }
141 }
142
143
144
145 public static void Initialize()
146 {
147 if (CUI.PalettesPath == null) return;
148 Stopwatch sw = Stopwatch.StartNew();
149
150 try
151 {
152 LoadedPalettes.Clear();
153 LoadPalettes(CUI.PalettesPath);
154 LoadSet(Path.Combine(PaletteSetsPath, DefaultPalette + ".xml"));
155
156 //TransformToBackup(Primary);
157 }
158 catch (Exception e) { CUI.Warning(e); }
159
160 CUIDebug.Log($"CUIPalette.Initialize took {sw.ElapsedMilliseconds}ms");
161 }
162
163 public static PaletteExtractResult Extract(string nestedName, PaletteOrder order)
164 {
165 CUIPalette palette = order switch
166 {
167 PaletteOrder.Primary => Primary,
168 PaletteOrder.Secondary => Secondary,
169 PaletteOrder.Tertiary => Tertiary,
170 PaletteOrder.Quaternary => Quaternary,
171 _ => Empty,
172 };
173 if (!palette.Values.ContainsKey(nestedName)) return new PaletteExtractResult(false);
174 return new PaletteExtractResult(true, palette.Values[nestedName]);
175 }
176
177 public static void LoadPalettes(string path)
178 {
179 foreach (string file in Directory.GetFiles(path, "*.xml"))
180 {
181 CUIPalette palette = LoadFrom(file);
182 LoadedPalettes[palette.Name] = palette;
183 }
184 }
185
186 public static CUIPalette FromXML(XElement root)
187 {
188 CUIPalette palette = new CUIPalette();
189
190 palette.Name = root.Attribute("Name")?.Value.ToString();
191
192 foreach (XElement element in root.Elements())
193 {
194 foreach (XAttribute attribute in element.Attributes())
195 {
196 palette.Values[$"{element.Name}.{attribute.Name}"] = attribute.Value;
197 }
198
199 if (element.Value != "")
200 {
201 palette.Values[$"{element.Name}"] = element.Value;
202 }
203 }
204
205 return palette;
206 }
207
208 public static CUIPalette LoadFrom(string path)
209 {
210 CUIPalette palette = new CUIPalette();
211 try
212 {
213 XDocument xdoc = XDocument.Load(path);
214 XElement root = xdoc.Root;
215
216 palette = CUIPalette.FromXML(root);
217 palette.Name ??= Path.GetFileNameWithoutExtension(path);
218 }
219 catch (Exception e)
220 {
221 CUI.Warning($"Failed to load palette from {path}");
222 CUI.Warning(e);
223 }
224
225 return palette;
226 }
227
228 public XElement ToXML()
229 {
230 XElement root = new XElement("Palette");
231 root.Add(new XAttribute("Name", Name));
232 root.Add(new XAttribute("BaseColor", BaseColor));
233
234 foreach (string key in Values.Keys)
235 {
236 string component = key.Split('.').ElementAtOrDefault(0);
237 string prop = key.Split('.').ElementAtOrDefault(1);
238
239 if (component == null) continue;
240
241 if (root.Element(component) == null) root.Add(new XElement(component));
242
243 if (prop != null)
244 {
245 root.Element(component).Add(new XAttribute(prop, Values[key]));
246 }
247 else
248 {
249 root.Element(component).Value = Values[key];
250 }
251 }
252 return root;
253 }
254 public void SaveTo(string path)
255 {
256 try
257 {
258 XDocument xdoc = new XDocument();
259 xdoc.Add(this.ToXML());
260 xdoc.Save(path);
261 }
262 catch (Exception e)
263 {
264 CUI.Warning($"Failed to save palette to {path}");
265 CUI.Warning(e);
266 }
267 }
268
269
270 public static void PaletteDemo()
271 {
272 if (CUI.AssetsPath == null)
273 {
274 CUI.Warning($"Can't load PaletteDemo, CUI.AssetsPath is null");
275 return;
276 }
277
278 void loadFrame(Vector2 offset, PaletteOrder order)
279 {
280 CUIFrame frame = CUIComponent.LoadFromFile<CUIFrame>(Path.Combine(CUI.AssetsPath, $"PaletteDemo.xml"));
281 frame.DeepPalette = order;
282 frame.Absolute = frame.Absolute with { Position = offset };
283 frame.AddCommand("Close", (o) => frame.RemoveSelf());
284 CUI.TopMain.Append(frame);
285 }
286
287 loadFrame(new Vector2(0, 0), PaletteOrder.Primary);
288 loadFrame(new Vector2(180, 0), PaletteOrder.Secondary);
289 loadFrame(new Vector2(360, 0), PaletteOrder.Tertiary);
290 loadFrame(new Vector2(540, 0), PaletteOrder.Quaternary);
291 }
292
293 public static CUIPalette CreatePaletteFromColors(string name, Color colorA, Color? colorb = null)
294 {
295 CUIPalette palette = new CUIPalette()
296 {
297 Name = name,
298 BaseColor = CUIExtensions.ColorToString(colorA),
299 };
300
301 Color colorB = colorb ?? Color.Black;
302
303 Dictionary<string, Color> colors = new();
304
305 colors["Frame.Background"] = colorA.To(colorB, 1.0f);
306 colors["Header.Background"] = colorA.To(colorB, 0.7f);
307 colors["Nav.Background"] = colorA.To(colorB, 0.8f);
308 colors["Main.Background"] = colorA.To(colorB, 0.9f);
309
310 colors["Frame.Border"] = colorA.To(colorB, 0.5f);
311 colors["Header.Border"] = colorA.To(colorB, 0.6f);
312 colors["Nav.Border"] = colorA.To(colorB, 0.7f);
313 colors["Main.Border"] = colorA.To(colorB, 0.8f);
314
315 colors["Frame.Text"] = colorA.To(Color.White, 0.9f);
316 colors["Header.Text"] = colorA.To(Color.White, 0.9f);
317 colors["Nav.Text"] = colorA.To(Color.White, 0.8f);
318 colors["Main.Text"] = colorA.To(Color.White, 0.8f);
319
320 colors["Component.Background"] = Color.Transparent;
321 colors["Component.Border"] = Color.Transparent;
322 colors["Component.Text"] = colors["Main.Text"];
323 colors["Button.Background"] = colorA.To(colorB, 0.0f);
324 colors["Button.Border"] = colorA.To(colorB, 0.5f);
325 colors["Button.Disabled"] = colorA.To(new Color(16, 16, 16), 0.8f);
326 colors["CloseButton.Background"] = colorA.To(Color.White, 0.2f);
327 colors["DDOption.Background"] = colors["Header.Background"];
328 colors["DDOption.Border"] = colors["Main.Border"];
329 colors["DDOption.Hover"] = colorA.To(colorB, 0.5f);
330 colors["DDOption.Text"] = colors["Main.Text"];
331 colors["Handle.Background"] = colorA.To(colorB, 0.5f).To(Color.White, 0.2f);
332 colors["Handle.Grabbed"] = colorA.To(colorB, 0.0f).To(Color.White, 0.2f);
333 colors["Slider"] = colorA.To(Color.White, 0.7f);
334 colors["Input.Background"] = colors["Nav.Background"];
335 colors["Input.Border"] = colors["Nav.Border"];
336 colors["Input.Text"] = colors["Main.Text"];
337 colors["Input.Focused"] = colorA;
338 colors["Input.Invalid"] = Color.Red;
339 colors["Input.Valid"] = Color.Lime;
340 colors["Input.Selection"] = colorA.To(Color.White, 0.7f) * 0.5f;
341 colors["Input.Caret"] = colorA.To(Color.White, 0.7f) * 0.5f;
342
343 foreach (var (key, cl) in colors)
344 {
345 palette.Values[key] = CUIExtensions.ColorToString(cl);
346 }
347
348 palette.SaveTo(Path.Combine(CUI.PalettesPath, $"{name}.xml"));
349 LoadedPalettes[name] = palette;
350 CUI.Log($"Created {name} palette and saved it to {Path.Combine(CUI.PalettesPath, $"{name}.xml")}");
351
352 return palette;
353 }
354
355 /// <summary>
356 /// Packs 4 palettes into 1 set
357 /// </summary>
358 public static void SaveSet(string setName, string primary = "", string secondary = "", string tertiary = "", string quaternary = "")
359 {
360 if (setName == null || setName == "") return;
361
362 string savePath = Path.Combine(PaletteSetsPath, $"{setName}.xml");
363
364 try
365 {
366 XDocument xdoc = new XDocument(new XElement("PaletteSet"));
367 XElement root = xdoc.Root;
368
369 root.Add(new XAttribute("Name", setName));
370
371 root.Add((LoadedPalettes.GetValueOrDefault(primary ?? "") ?? Primary).ToXML());
372 root.Add((LoadedPalettes.GetValueOrDefault(secondary ?? "") ?? Secondary).ToXML());
373 root.Add((LoadedPalettes.GetValueOrDefault(tertiary ?? "") ?? Tertiary).ToXML());
374 root.Add((LoadedPalettes.GetValueOrDefault(quaternary ?? "") ?? Quaternary).ToXML());
375
376 xdoc.Save(savePath);
377
378 CUI.Log($"Created {setName} palette set and saved it to {savePath}");
379 LoadSet(savePath);
380 }
381 catch (Exception e)
382 {
383 CUI.Warning($"Failed to save palette set to {savePath}");
384 CUI.Warning(e);
385 }
386 }
387
388 public static void LoadSet(string path)
389 {
390 if (path == null || path == "")
391 {
392 CUI.Warning($"Can't load CUIPalette set: path is empty");
393 return;
394 }
395 if (!File.Exists(path))
396 {
397 CUI.Warning($"Couldn't find {Path.GetFileName(path)} CUIPalette set");
398 return;
399 }
400
401 try
402 {
403 XDocument xdoc = XDocument.Load(path);
404 XElement root = xdoc.Root;
405
406 List<CUIPalette> palettes = new();
407
408 foreach (XElement element in root.Elements("Palette"))
409 {
410 palettes.Add(CUIPalette.FromXML(element));
411 }
412
413 Primary = palettes.ElementAtOrDefault(0) ?? Empty;
414 Secondary = palettes.ElementAtOrDefault(1) ?? Empty;
415 Tertiary = palettes.ElementAtOrDefault(2) ?? Empty;
416 Quaternary = palettes.ElementAtOrDefault(3) ?? Empty;
417 }
418 catch (Exception e)
419 {
420 CUI.Warning($"Failed to load palette set from {path}");
421 CUI.Warning(e);
422 }
423 }
424
425 /// <summary>
426 /// I used it once to transform xml palette to hardcoded backup palette
427 /// </summary>
428 public static void TransformToBackup(CUIPalette palette)
429 {
430 using (StreamWriter writer = new StreamWriter(Path.Combine(CUI.PalettesPath, "backup.txt"), false))
431 {
432 writer.WriteLine("{");
433 foreach (var (key, value) in palette.Values)
434 {
435 writer.WriteLine($" [\"{key}\"] = \"{value}\",");
436 }
437 writer.WriteLine("}");
438 }
439 }
440
441 }
442
443
444}