blob: 7860f0debd0f1d44529ea8e80adb7009159fd308 [file] [log] [blame]
Austin Schuh812d0d12021-11-04 20:16:48 -07001// Copyright (c) FIRST and other WPILib contributors.
2// Open Source Software; you can modify and/or share it under the terms of
3// the WPILib BSD license file in the root directory of this project.
Austin Schuh1e69f942020-11-14 15:06:14 -08004
5#pragma once
6
7#include <functional>
Austin Schuh812d0d12021-11-04 20:16:48 -07008#include <string>
9#include <string_view>
Austin Schuh1e69f942020-11-14 15:06:14 -080010
11#include <imgui.h>
12
13extern "C" struct GLFWwindow;
14
15namespace wpi::gui {
16
17/**
18 * Creates GUI context. Must be called prior to calling any other functions.
19 */
20void CreateContext();
21
22/**
23 * Destroys GUI context.
24 */
25void DestroyContext();
26
27/**
28 * Initializes the GUI.
29 *
30 * @param title main application window title
31 * @param width main application window width
32 * @param height main application window height
33 */
34bool Initialize(const char* title, int width, int height);
35
36/**
37 * Runs main GUI loop. On some OS'es this must be called from the main thread.
38 * Does not return until Exit() is called.
39 */
40void Main();
41
42/**
43 * Exits main GUI loop when current loop iteration finishes.
44 * Safe to call from any thread, including from within main GUI loop.
45 */
46void Exit();
47
48/**
49 * Adds initializer to GUI. The passed function is called once, immediately
50 * after the GUI (both GLFW and Dear ImGui) are initialized in Initialize().
51 * To have any effect, must be called prior to Initialize().
52 *
53 * @param initialize initialization function
54 */
55void AddInit(std::function<void()> initialize);
56
57/**
58 * Adds window scaler function. The passed function is called once during
59 * Initialize() if the window scale is not 1.0. To have any effect, must
60 * be called prior to Initialize().
61 *
62 * @param windowScaler window scaler function
63 */
64void AddWindowScaler(std::function<void(float scale)> windowScaler);
65
66/**
67 * Adds per-frame executor to GUI. The passed function is called on each
68 * Dear ImGui frame prior to any of the late execute functions.
69 *
70 * @param execute frame execution function
71 */
72void AddEarlyExecute(std::function<void()> execute);
73
74/**
75 * Adds per-frame executor to GUI. The passed function is called on each
76 * Dear ImGui frame after all of the early execute functions.
77 *
78 * @param execute frame execution function
79 */
80void AddLateExecute(std::function<void()> execute);
81
82/**
83 * Gets GLFW window handle.
84 */
85GLFWwindow* GetSystemWindow();
86
87/**
Austin Schuh812d0d12021-11-04 20:16:48 -070088 * Adds an application icon. Multiple icons (of different sizes) may be
89 * set. This must be called prior to initialization to have an effect.
90 *
91 * @param data image data
92 * @param len image data length
93 * @return False if image data could not be read
94 */
95bool AddIcon(const unsigned char* data, int len);
96
97inline bool AddIcon(std::string_view data) {
98 return AddIcon(reinterpret_cast<const unsigned char*>(data.data()),
99 data.size());
100}
101
102/**
Austin Schuh1e69f942020-11-14 15:06:14 -0800103 * Adds a font to the GUI. The passed function is called during
104 * initialization as many times as necessary to create a range of sizes.
105 *
106 * @param name font name
107 * @param makeFont font creation / loader function
108 * @return Font index for later use with GetFont()
109 */
110int AddFont(
111 const char* name,
112 std::function<ImFont*(ImGuiIO& io, float size, const ImFontConfig* cfg)>
113 makeFont);
114
115/**
116 * Gets a font added with AddFont() with the appropriate font size for
117 * the current scaling of the GUI.
118 *
119 * @param font font index returned by AddFont()
120 * @return Font pointer
121 */
122ImFont* GetFont(int font);
123
124enum Style { kStyleClassic = 0, kStyleDark, kStyleLight };
125
126/**
127 * Sets the ImGui style. Using this function makes this setting persistent.
128 *
129 * @param style Style
130 */
131void SetStyle(Style style);
132
133/**
134 * Sets the clear (background) color.
135 *
136 * @param color Color
137 */
138void SetClearColor(ImVec4 color);
139
140/**
Austin Schuh812d0d12021-11-04 20:16:48 -0700141 * Configures a save file (.ini) in a platform specific location. On Windows,
142 * the .ini is saved in %APPDATA%; on macOS the .ini is saved in
143 * ~/Library/Preferences; on Linux the .ini is stored in $XDG_CONFIG_HOME or
144 * ~/.config if the former is not defined. This must be called before
145 * gui::Initialize().
146 */
147void ConfigurePlatformSaveFile(const std::string& name);
148
149/**
Austin Schuh1e69f942020-11-14 15:06:14 -0800150 * Emits a View menu (e.g. for a main menu bar) that allows setting of
151 * style and zoom. Internally starts with ImGui::BeginMenu("View").
152 */
153void EmitViewMenu();
154
155/**
156 * Pixel formats for texture pixel data.
157 */
158enum PixelFormat { kPixelRGBA, kPixelBGRA };
159
160/**
161 * Creates a texture from pixel data.
162 *
163 * @param format pixel format
164 * @param width image width
165 * @param height image height
166 * @param data pixel data
167 * @return Texture
168 */
169ImTextureID CreateTexture(PixelFormat format, int width, int height,
170 const unsigned char* data);
171
172/**
173 * Updates a texture from pixel data.
174 * The passed-in width and height must match the width and height of the
175 * texture.
176 *
177 * @param texture texture
178 * @param format pixel format
179 * @param width texture width
180 * @param height texture height
181 * @param data pixel data
182 */
183void UpdateTexture(ImTextureID texture, PixelFormat format, int width,
184 int height, const unsigned char* data);
185
186/**
187 * Updates a texture from image data.
188 * The pixel format of the texture must be RGBA. The passed-in width and
189 * height must match the width and height of the texture. If the width and
190 * height of the image differ from the passed-in width and height, a new
191 * texture is created (note this may be inefficient).
192 *
193 * @param texture texture (pointer, may be updated)
194 * @param width texture width
195 * @param height texture height
196 * @param data image data
197 * @param len image data length
198 *
199 * @return True on success, false on failure.
200 */
201bool UpdateTextureFromImage(ImTextureID* texture, int width, int height,
202 const unsigned char* data, int len);
203
204/**
205 * Creates a texture from an image file.
206 *
207 * @param filename filename
208 * @param out_texture texture (output)
209 * @param out_width image width (output)
210 * @param out_height image height (output)
211 * @return True on success, false on failure.
212 */
213bool CreateTextureFromFile(const char* filename, ImTextureID* out_texture,
214 int* out_width, int* out_height);
215
216/**
217 * Creates a texture from image data.
218 *
219 * @param data image data
220 * @param len image data length
221 * @param out_texture texture (output)
222 * @param out_width image width (output)
223 * @param out_height image height (output)
224 * @return True on success, false on failure.
225 */
226bool CreateTextureFromImage(const unsigned char* data, int len,
227 ImTextureID* out_texture, int* out_width,
228 int* out_height);
229
230/**
231 * Deletes a texture.
232 *
233 * @param texture texture
234 */
235void DeleteTexture(ImTextureID texture);
236
237/**
238 * RAII wrapper around ImTextureID. Also keeps track of width, height, and
239 * pixel format.
240 */
241class Texture {
242 public:
243 Texture() = default;
244
245 /**
246 * Constructs a texture from pixel data.
247 *
248 * @param format pixel format
249 * @param width image width
250 * @param height image height
251 * @param data pixel data
252 */
253 Texture(PixelFormat format, int width, int height, const unsigned char* data)
254 : m_format{format}, m_width{width}, m_height{height} {
255 m_texture = CreateTexture(format, width, height, data);
256 }
257
258 Texture(const Texture&) = delete;
259 Texture(Texture&& oth)
260 : m_texture{oth.m_texture},
261 m_format{oth.m_format},
262 m_width{oth.m_width},
263 m_height{oth.m_height} {
Austin Schuh812d0d12021-11-04 20:16:48 -0700264 oth.m_texture = 0; // NOLINT
Austin Schuh1e69f942020-11-14 15:06:14 -0800265 }
266
267 Texture& operator=(const Texture&) = delete;
268 Texture& operator=(Texture&& oth) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700269 if (m_texture) {
270 DeleteTexture(m_texture);
271 }
Austin Schuh1e69f942020-11-14 15:06:14 -0800272 m_texture = oth.m_texture;
Austin Schuh812d0d12021-11-04 20:16:48 -0700273 oth.m_texture = 0; // NOLINT
Austin Schuh1e69f942020-11-14 15:06:14 -0800274 m_format = oth.m_format;
275 m_width = oth.m_width;
276 m_height = oth.m_height;
277 return *this;
278 }
279
280 ~Texture() {
Austin Schuh812d0d12021-11-04 20:16:48 -0700281 if (m_texture) {
282 DeleteTexture(m_texture);
283 }
Austin Schuh1e69f942020-11-14 15:06:14 -0800284 }
285
286 /**
287 * Evaluates to true if the texture is valid.
288 */
289 explicit operator bool() const { return m_texture; }
290
291 /**
292 * Implicit conversion to ImTextureID.
293 */
Austin Schuh812d0d12021-11-04 20:16:48 -0700294 operator ImTextureID() const { return m_texture; } // NOLINT
Austin Schuh1e69f942020-11-14 15:06:14 -0800295
296 /**
297 * Gets the texture pixel format.
298 *
299 * @return pixel format
300 */
301 PixelFormat GetFormat() const { return m_format; }
302
303 /**
304 * Gets the texture width.
305 *
306 * @return width
307 */
308 int GetWidth() const { return m_width; }
309
310 /**
311 * Gets the texture height.
312 *
313 * @return height
314 */
315 int GetHeight() const { return m_height; }
316
317 /**
318 * Updates the texture from pixel data.
319 * The image data size and format is assumed to match that of the texture.
320 *
321 * @param format pixel format
322 * @param data pixel data
323 */
324 void Update(const unsigned char* data) {
325 UpdateTexture(m_texture, m_format, m_width, m_height, data);
326 }
327
328 /**
329 * Updates the texture from image data.
330 * The pixel format of the texture must be RGBA. If the width and height of
331 * the image differ from the texture width and height, a new texture is
332 * created (note this may be inefficient).
333 *
334 * @param data image data
335 * @param len image data length
336 *
337 * @return True on success, false on failure.
338 */
339 bool UpdateFromImage(const unsigned char* data, int len) {
340 return UpdateTextureFromImage(&m_texture, m_width, m_height, data, len);
341 }
342
343 /**
344 * Creates a texture by loading an image file.
345 *
346 * @param filename filename
347 *
348 * @return Texture, or invalid (empty) texture on failure.
349 */
350 static Texture CreateFromFile(const char* filename) {
351 Texture texture;
352 if (!CreateTextureFromFile(filename, &texture.m_texture, &texture.m_width,
Austin Schuh812d0d12021-11-04 20:16:48 -0700353 &texture.m_height)) {
Austin Schuh1e69f942020-11-14 15:06:14 -0800354 return {};
Austin Schuh812d0d12021-11-04 20:16:48 -0700355 }
Austin Schuh1e69f942020-11-14 15:06:14 -0800356 return texture;
357 }
358
359 /**
360 * Creates a texture from image data.
361 *
362 * @param data image data
363 * @param len image data length
364 *
365 * @return Texture, or invalid (empty) texture on failure.
366 */
367 static Texture CreateFromImage(const unsigned char* data, int len) {
368 Texture texture;
369 if (!CreateTextureFromImage(data, len, &texture.m_texture, &texture.m_width,
Austin Schuh812d0d12021-11-04 20:16:48 -0700370 &texture.m_height)) {
Austin Schuh1e69f942020-11-14 15:06:14 -0800371 return {};
Austin Schuh812d0d12021-11-04 20:16:48 -0700372 }
Austin Schuh1e69f942020-11-14 15:06:14 -0800373 return texture;
374 }
375
376 private:
377 ImTextureID m_texture = nullptr;
378 PixelFormat m_format = kPixelRGBA;
379 int m_width = 0;
380 int m_height = 0;
381};
382
383/**
384 * Get square of distance between two ImVec2's.
385 *
386 * @param a first ImVec
387 * @param b second ImVec
388 *
389 * @return Distance^2 between a and b.
390 */
391inline float GetDistSquared(const ImVec2& a, const ImVec2& b) {
392 float deltaX = b.x - a.x;
393 float deltaY = b.y - a.y;
394 return deltaX * deltaX + deltaY * deltaY;
395}
396
397/**
398 * Maximize fit in "window" while preserving aspect ratio. Min and max
399 * passed-in values are modified such that the object maximally fits.
400 *
401 * @param min upper left corner of window (modified to fit)
402 * @param max lower right corner of window (modified to fit)
403 * @param width width of object to fit
404 * @param height height of object to fit
405 */
406void MaxFit(ImVec2* min, ImVec2* max, float width, float height);
407
408} // namespace wpi::gui