WKLib 0.2.3
A modding library for White Knuckle
Loading...
Searching...
No Matches
UIUtility.cs
Go to the documentation of this file.
1using System;
2using System.Collections.Generic;
3using System.Drawing;
4using System.Text;
5using BepInEx;
6using BepInEx.Configuration;
7using Imui.Controls;
8using Imui.Core;
9using Imui.IO.Events;
10using Imui.Rendering;
11using UnityEngine;
12using UnityEngine.InputSystem;
13using UnityEngine.TextCore.Text;
14using WKLib.API.Input;
15using WKLib.Core.UI;
16
17namespace WKLib.API.UI;
18
19public static class UIUtility
20{
21 public struct LabeledScope : IDisposable
22 {
23 private ImGui gui;
24
25 public LabeledScope(ImGui gui, ReadOnlySpan<char> label, float firstRectSize = 0.4f)
26 {
27 this.gui = gui;
28
29 gui.PushId(label);
30
31 gui.AddSpacingIfLayoutFrameNotEmpty();
32 gui.BeginHorizontal();
33 var rect = gui.AddLayoutRect(gui.GetLayoutWidth() * firstRectSize, gui.GetRowHeight());
34 gui.Text(label, rect, overflow: ImTextOverflow.Ellipsis);
35 gui.BeginVertical();
36 }
37
38 public void Dispose()
39 {
40 gui.EndVertical();
41 gui.EndHorizontal();
42 gui.PopId();
43 }
44 }
45
46 public static bool Keybind(this ImGui gui, string label, ref KeyCode keyCode)
47 {
48 var changed = false;
49
50 var id = gui.GetControlId(label);
51 gui.PushId(id);
52
53 gui.AddSpacingIfLayoutFrameNotEmpty();
54 gui.BeginHorizontal();
55 var rect = gui.AddLayoutRect(gui.GetLayoutWidth() * 0.8f, gui.GetRowHeight());
56 gui.Text(label, rect, overflow: ImTextOverflow.Ellipsis);
57 gui.BeginVertical();
58
59 if (gui.GetActiveControl() == id)
60 {
61 gui.Button("...");
62
63 if (SetToPressedKey(gui, ref keyCode))
64 {
65 changed = true;
66 gui.ResetActiveControl();
67 }
68 }
69 else if (gui.Button(keyCode.ToString()))
70 {
71 gui.SetActiveControl(id);
72 }
73
74 gui.EndVertical();
75 gui.EndHorizontal();
76 gui.PopId();
77
78 return changed;
79
80 bool SetToPressedKey(ImGui gui, ref KeyCode keyCode)
81 {
83 if (key == null)
84 return false;
85
86 if (key == KeyCode.Mouse0 || key == KeyCode.Mouse1)
87 return true;
88
89 if (key == KeyCode.Escape)
90 {
91 keyCode = KeyCode.None;
92 return true;
93 }
94
95 keyCode = key.Value;
96 return true;
97 }
98 }
99
100 public static void DrawConfigEntry(ImGui gui, ConfigEntryBase configEntry)
101 {
102 string label = configEntry.Definition.Key;
103 string description = configEntry.Description.Description;
104 description = InsertLineBreaks(description, 40);
105
106 object value = configEntry.BoxedValue;
107 Type type = configEntry.SettingType;
108
109 var width = gui.Layout.GetAvailableWidth();
110 var height = gui.GetRowHeight();
111 var nextPosition = gui.Layout.GetNextPosition(height);
112 ImRect clickableArea = new ImRect(nextPosition.x, nextPosition.y, width, height);
113 var groupId = gui.GetNextControlId();
114 gui.RegisterGroup(groupId, clickableArea);
115
116 if (type == typeof(bool))
117 {
118 bool v = (bool)value;
119
120 if (gui.Checkbox(ref v, label))
121 configEntry.BoxedValue = v;
122 }
123 else if (type == typeof(byte))
124 {
125 byte v = (byte)value;
126
127 using (new UIUtility.LabeledScope(gui, label))
128 {
129 if (gui.NumericEdit(ref v, flags: ImNumericEditFlag.Slider))
130 configEntry.BoxedValue = (byte)v;
131 }
132 }
133 else if (type == typeof(sbyte))
134 {
135 int v = (sbyte)value;
136
137 using (new UIUtility.LabeledScope(gui, label))
138 {
139 if (gui.NumericEdit(ref v, flags: ImNumericEditFlag.Slider, min: sbyte.MinValue, max: sbyte.MaxValue))
140 configEntry.BoxedValue = (sbyte)v;
141 }
142 }
143 else if (type == typeof(short))
144 {
145 short v = (short)value;
146
147 using (new UIUtility.LabeledScope(gui, label))
148 {
149 if (gui.NumericEdit(ref v, flags: ImNumericEditFlag.Slider))
150 configEntry.BoxedValue = (short)v;
151 }
152 }
153 else if (type == typeof(ushort))
154 {
155 int v = (ushort)value;
156
157 using (new UIUtility.LabeledScope(gui, label))
158 {
159 if (gui.NumericEdit(ref v, flags: ImNumericEditFlag.Slider, min: ushort.MinValue, max: ushort.MaxValue))
160 configEntry.BoxedValue = (ushort)v;
161 }
162 }
163 else if (type == typeof(int))
164 {
165 int v = (int)value;
166
167 using (new UIUtility.LabeledScope(gui, label))
168 {
169 if (gui.NumericEdit(ref v, flags: ImNumericEditFlag.Slider))
170 configEntry.BoxedValue = (int)v;
171 }
172 }
173 else if (type == typeof(uint))
174 {
175 long v = (uint)value;
176
177 using (new UIUtility.LabeledScope(gui, label))
178 {
179 if (gui.NumericEdit(ref v, flags: ImNumericEditFlag.Slider, min: uint.MinValue, max: uint.MaxValue))
180 configEntry.BoxedValue = (uint)v;
181 }
182 }
183 else if (type == typeof(long))
184 {
185 long v = (long)value;
186
187 using (new UIUtility.LabeledScope(gui, label))
188 {
189 if (gui.NumericEdit(ref v, flags: ImNumericEditFlag.Slider))
190 configEntry.BoxedValue = v;
191 }
192 }
193 else if (type == typeof(ulong))
194 {
195 long v = (long)value;
196
197 using (new UIUtility.LabeledScope(gui, label))
198 {
199 if (gui.NumericEdit(ref v, flags: ImNumericEditFlag.Slider, min: 0)) // Compromise: Use Long limits on ULong since Imui doesnt handle ULong
200 configEntry.BoxedValue = (ulong)v;
201 }
202 }
203 else if (type == typeof(float))
204 {
205 float v = (float)value;
206
207 using (new UIUtility.LabeledScope(gui, label))
208 {
209 if (gui.NumericEdit(ref v, flags: ImNumericEditFlag.Slider))
210 configEntry.BoxedValue = v;
211 }
212 }
213 else if (type == typeof(double))
214 {
215 double v = (double)value;
216
217 using (new UIUtility.LabeledScope(gui, label))
218 {
219 if (gui.NumericEdit(ref v, flags: ImNumericEditFlag.Slider))
220 configEntry.BoxedValue = v;
221 }
222 }
223 else if (type == typeof(decimal))
224 {
225 double v = Convert.ToDouble((decimal)value);
226
227 using (new UIUtility.LabeledScope(gui, label))
228 {
229 if (gui.NumericEdit(ref v, flags: ImNumericEditFlag.Slider))
230 configEntry.BoxedValue = (decimal)v;
231 }
232 }
233 else if (type == typeof(string))
234 {
235 string v = (string)value ?? "";
236
237 using (new UIUtility.LabeledScope(gui, label))
238 {
239 if (gui.TextEdit(ref v))
240 configEntry.BoxedValue = v;
241 }
242 }
243 else if (type == typeof(Vector2))
244 {
245 Vector2 v = (Vector2)value;
246
247 using (new UIUtility.LabeledScope(gui, label))
248 {
249 if (gui.Vector(ref v))
250 configEntry.BoxedValue = v;
251 }
252 }
253 else if (type == typeof(Vector3))
254 {
255 Vector3 v = (Vector3)value;
256
257 using (new UIUtility.LabeledScope(gui, label))
258 {
259 if (gui.Vector(ref v))
260 configEntry.BoxedValue = v;
261 }
262 }
263 else if (type == typeof(Vector4))
264 {
265 Vector4 v = (Vector4)value;
266
267 using (new UIUtility.LabeledScope(gui, label))
268 {
269 if (gui.Vector(ref v))
270 configEntry.BoxedValue = v;
271 }
272 }
273 else if (type == typeof(Quaternion))
274 {
275 var quat = (Quaternion)value;
276 Vector4 v = new Vector4(quat.x, quat.y, quat.z, quat.w);
277
278 using (new UIUtility.LabeledScope(gui, label))
279 {
280 if (gui.Vector(ref v))
281 configEntry.BoxedValue = new Quaternion(v.x, v.y, v.z, v.w);
282 }
283 }
284 else if (type == typeof(UnityEngine.Color))
285 {
286 using (new UIUtility.LabeledScope(gui, label))
287 {
288 var tempValue = (UnityEngine.Color)configEntry.BoxedValue;
289 if (gui.ColorEdit(ref tempValue))
290 configEntry.BoxedValue = tempValue;
291 }
292 }
293 else if (type == typeof(KeyboardShortcut))
294 {
295 // Ignore
296 return;
297 }
298 else if (type == typeof(KeyCode))
299 {
300 var tempValue = (KeyCode)configEntry.BoxedValue;
301 if (gui.Keybind(label, ref tempValue))
302 configEntry.BoxedValue = tempValue;
303 }
304 else if (type.IsEnum)
305 {
306 string[] names = Enum.GetNames(type);
307
308 int currentIndex = Array.IndexOf(names, value.ToString());
309
310 using (new UIUtility.LabeledScope(gui, label))
311 {
312 if (gui.Dropdown(ref currentIndex, names))
313 {
314 object enumValue = Enum.Parse(type, names[currentIndex]);
315 configEntry.BoxedValue = enumValue;
316 }
317 }
318 }
319 // UNSUPPORTED
320 else
321 {
322 gui.Text($"{label}: Unsupported type ({type.Name})");
323 }
324
325 if (!description.IsNullOrWhiteSpace() && gui.IsGroupHovered(groupId))
326 gui.Tooltip(description, gui.Input.MousePosition + gui.Style.Tooltip.OffsetPixels / gui.Canvas.ScreenScale);
327
328 string InsertLineBreaks(string text, int maxLineLength)
329 {
330 if (string.IsNullOrWhiteSpace(text) || maxLineLength <= 0)
331 return text;
332
333 var words = text.Split(' ');
334 var sb = new StringBuilder();
335
336 int currentLineLength = 0;
337
338 foreach (var word in words)
339 {
340 if (currentLineLength + word.Length + 1 > maxLineLength)
341 {
342 sb.AppendLine();
343 currentLineLength = 0;
344 }
345 else if (currentLineLength > 0)
346 {
347 sb.Append(' ');
348 currentLineLength++;
349 }
350
351 sb.Append(word);
352 currentLineLength += word.Length;
353 }
354
355 return sb.ToString();
356 }
357 }
358
359 public static void ShowPopupForTime(string text, float seconds = 2.5f)
360 {
361 OverlayState.Popups.Add(new PopupSettings(text, seconds));
362 }
363}
static ? KeyCode GetFirstActiveKey()
static bool Keybind(this ImGui gui, string label, ref KeyCode keyCode)
Definition UIUtility.cs:46
static void DrawConfigEntry(ImGui gui, ConfigEntryBase configEntry)
Definition UIUtility.cs:100
static void ShowPopupForTime(string text, float seconds=2.5f)
Definition UIUtility.cs:359
LabeledScope(ImGui gui, ReadOnlySpan< char > label, float firstRectSize=0.4f)
Definition UIUtility.cs:25