blob: d4602b59517724e7c56b2297f4bd22616fde0aa1 [file] [log] [blame]
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include <functional>
#include <string>
#include <string_view>
#include <imgui.h>
extern "C" struct GLFWwindow;
namespace wpi::gui {
/**
* Creates GUI context. Must be called prior to calling any other functions.
*/
void CreateContext();
/**
* Destroys GUI context.
*/
void DestroyContext();
/**
* Initializes the GUI.
*
* @param title main application window title
* @param width main application window width
* @param height main application window height
*/
bool Initialize(const char* title, int width, int height);
/**
* Runs main GUI loop. On some OS'es this must be called from the main thread.
* Does not return until Exit() is called.
*/
void Main();
/**
* Exits main GUI loop when current loop iteration finishes.
* Safe to call from any thread, including from within main GUI loop.
*/
void Exit();
/**
* Adds initializer to GUI. The passed function is called once, immediately
* after the GUI (both GLFW and Dear ImGui) are initialized in Initialize().
* To have any effect, must be called prior to Initialize().
*
* @param initialize initialization function
*/
void AddInit(std::function<void()> initialize);
/**
* Adds window scaler function. The passed function is called once during
* Initialize() if the window scale is not 1.0. To have any effect, must
* be called prior to Initialize().
*
* @param windowScaler window scaler function
*/
void AddWindowScaler(std::function<void(float scale)> windowScaler);
/**
* Adds per-frame executor to GUI. The passed function is called on each
* Dear ImGui frame prior to any of the late execute functions.
*
* @param execute frame execution function
*/
void AddEarlyExecute(std::function<void()> execute);
/**
* Adds per-frame executor to GUI. The passed function is called on each
* Dear ImGui frame after all of the early execute functions.
*
* @param execute frame execution function
*/
void AddLateExecute(std::function<void()> execute);
/**
* Customizes save/load behavior.
*
* By default, the integrated ImGui functions are used for this;
* ImGui::LoadIniSettingsFromDisk(io.IniFilename) is called at startup, and
* ImGui default automatic save file handling is used via io.IniFilename.
*
* Calling this function results in the load function being called at startup,
* io.IniFilename set to null (which disables ImGui's integrated file saving),
* and the save function being called when io.WantSaveIniSettings is true.
* The loadIni function should call ImGui::LoadIniSettingsFromMemory() to load
* ImGui save data, and the save function should call
* ImGui::SaveIniSettingsToMemory() to get ImGui save data.
*
* The load function is called PRIOR to AddInit() functions, and the loadIni
* function is called AFTER to AddInit() functions. This allows initialize
* functions that use custom storage to handle the loaded values, and initialize
* functions that use INI storage to add hooks prior to the load INI occurring.
*
* This must be called prior to Initialize().
*
* @param load load function
* @param loadIni load INI function
* @param save save function; false is passed periodically, true is passed once
* when the main loop is exiting
*/
void ConfigureCustomSaveSettings(std::function<void()> load,
std::function<void()> loadIni,
std::function<void(bool exiting)> save);
/**
* Gets GLFW window handle.
*/
GLFWwindow* GetSystemWindow();
/**
* Adds an application icon. Multiple icons (of different sizes) may be
* set. This must be called prior to initialization to have an effect.
*
* @param data image data
* @param len image data length
* @return False if image data could not be read
*/
bool AddIcon(const unsigned char* data, int len);
inline bool AddIcon(std::string_view data) {
return AddIcon(reinterpret_cast<const unsigned char*>(data.data()),
data.size());
}
/**
* Adds a font to the GUI. The passed function is called during
* initialization as many times as necessary to create a range of sizes.
*
* @param name font name
* @param makeFont font creation / loader function
* @return Font index for later use with GetFont()
*/
int AddFont(
const char* name,
std::function<ImFont*(ImGuiIO& io, float size, const ImFontConfig* cfg)>
makeFont);
/**
* Gets a font added with AddFont() with the appropriate font size for
* the current scaling of the GUI.
*
* @param font font index returned by AddFont()
* @return Font pointer
*/
ImFont* GetFont(int font);
enum Style { kStyleClassic = 0, kStyleDark, kStyleLight };
/**
* Sets the ImGui style. Using this function makes this setting persistent.
*
* @param style Style
*/
void SetStyle(Style style);
/**
* Sets the clear (background) color.
*
* @param color Color
*/
void SetClearColor(ImVec4 color);
/**
* Gets the (platform-specific) absolute directory for save files.
*
* @return Absolute path, including trailing "/". Empty string if directory
* could not be determined.
*/
std::string GetPlatformSaveFileDir();
/**
* Configures a save file (.ini) in a platform specific location. On Windows,
* the .ini is saved in %APPDATA%; on macOS the .ini is saved in
* ~/Library/Preferences; on Linux the .ini is stored in $XDG_CONFIG_HOME or
* ~/.config if the former is not defined. This must be called before
* gui::Initialize().
*/
void ConfigurePlatformSaveFile(const std::string& name);
/**
* Emits a View menu (e.g. for a main menu bar) that allows setting of
* style and zoom. Internally starts with ImGui::BeginMenu("View").
*/
void EmitViewMenu();
/**
* Pixel formats for texture pixel data.
*/
enum PixelFormat { kPixelRGBA, kPixelBGRA };
/**
* Creates a texture from pixel data.
*
* @param format pixel format
* @param width image width
* @param height image height
* @param data pixel data
* @return Texture
*/
ImTextureID CreateTexture(PixelFormat format, int width, int height,
const unsigned char* data);
/**
* Updates a texture from pixel data.
* The passed-in width and height must match the width and height of the
* texture.
*
* @param texture texture
* @param format pixel format
* @param width texture width
* @param height texture height
* @param data pixel data
*/
void UpdateTexture(ImTextureID texture, PixelFormat format, int width,
int height, const unsigned char* data);
/**
* Updates a texture from image data.
* The pixel format of the texture must be RGBA. The passed-in width and
* height must match the width and height of the texture. If the width and
* height of the image differ from the passed-in width and height, a new
* texture is created (note this may be inefficient).
*
* @param texture texture (pointer, may be updated)
* @param width texture width
* @param height texture height
* @param data image data
* @param len image data length
*
* @return True on success, false on failure.
*/
bool UpdateTextureFromImage(ImTextureID* texture, int width, int height,
const unsigned char* data, int len);
/**
* Creates a texture from an image file.
*
* @param filename filename
* @param out_texture texture (output)
* @param out_width image width (output)
* @param out_height image height (output)
* @return True on success, false on failure.
*/
bool CreateTextureFromFile(const char* filename, ImTextureID* out_texture,
int* out_width, int* out_height);
/**
* Creates a texture from image data.
*
* @param data image data
* @param len image data length
* @param out_texture texture (output)
* @param out_width image width (output)
* @param out_height image height (output)
* @return True on success, false on failure.
*/
bool CreateTextureFromImage(const unsigned char* data, int len,
ImTextureID* out_texture, int* out_width,
int* out_height);
/**
* Deletes a texture.
*
* @param texture texture
*/
void DeleteTexture(ImTextureID texture);
/**
* RAII wrapper around ImTextureID. Also keeps track of width, height, and
* pixel format.
*/
class Texture {
public:
Texture() = default;
/**
* Constructs a texture from pixel data.
*
* @param format pixel format
* @param width image width
* @param height image height
* @param data pixel data
*/
Texture(PixelFormat format, int width, int height, const unsigned char* data)
: m_format{format}, m_width{width}, m_height{height} {
m_texture = CreateTexture(format, width, height, data);
}
Texture(const Texture&) = delete;
Texture(Texture&& oth)
: m_texture{oth.m_texture},
m_format{oth.m_format},
m_width{oth.m_width},
m_height{oth.m_height} {
oth.m_texture = 0; // NOLINT
}
Texture& operator=(const Texture&) = delete;
Texture& operator=(Texture&& oth) {
if (m_texture) {
DeleteTexture(m_texture);
}
m_texture = oth.m_texture;
oth.m_texture = 0; // NOLINT
m_format = oth.m_format;
m_width = oth.m_width;
m_height = oth.m_height;
return *this;
}
~Texture() {
if (m_texture) {
DeleteTexture(m_texture);
}
}
/**
* Evaluates to true if the texture is valid.
*/
explicit operator bool() const { return m_texture; }
/**
* Implicit conversion to ImTextureID.
*/
operator ImTextureID() const { return m_texture; } // NOLINT
/**
* Gets the texture pixel format.
*
* @return pixel format
*/
PixelFormat GetFormat() const { return m_format; }
/**
* Gets the texture width.
*
* @return width
*/
int GetWidth() const { return m_width; }
/**
* Gets the texture height.
*
* @return height
*/
int GetHeight() const { return m_height; }
/**
* Updates the texture from pixel data.
* The image data size and format is assumed to match that of the texture.
*
* @param format pixel format
* @param data pixel data
*/
void Update(const unsigned char* data) {
UpdateTexture(m_texture, m_format, m_width, m_height, data);
}
/**
* Updates the texture from image data.
* The pixel format of the texture must be RGBA. If the width and height of
* the image differ from the texture width and height, a new texture is
* created (note this may be inefficient).
*
* @param data image data
* @param len image data length
*
* @return True on success, false on failure.
*/
bool UpdateFromImage(const unsigned char* data, int len) {
return UpdateTextureFromImage(&m_texture, m_width, m_height, data, len);
}
/**
* Creates a texture by loading an image file.
*
* @param filename filename
*
* @return Texture, or invalid (empty) texture on failure.
*/
static Texture CreateFromFile(const char* filename) {
Texture texture;
if (!CreateTextureFromFile(filename, &texture.m_texture, &texture.m_width,
&texture.m_height)) {
return {};
}
return texture;
}
/**
* Creates a texture from image data.
*
* @param data image data
* @param len image data length
*
* @return Texture, or invalid (empty) texture on failure.
*/
static Texture CreateFromImage(const unsigned char* data, int len) {
Texture texture;
if (!CreateTextureFromImage(data, len, &texture.m_texture, &texture.m_width,
&texture.m_height)) {
return {};
}
return texture;
}
private:
ImTextureID m_texture = nullptr;
PixelFormat m_format = kPixelRGBA;
int m_width = 0;
int m_height = 0;
};
/**
* Get square of distance between two ImVec2's.
*
* @param a first ImVec
* @param b second ImVec
*
* @return Distance^2 between a and b.
*/
inline float GetDistSquared(const ImVec2& a, const ImVec2& b) {
float deltaX = b.x - a.x;
float deltaY = b.y - a.y;
return deltaX * deltaX + deltaY * deltaY;
}
/**
* Maximize fit in "window" while preserving aspect ratio. Min and max
* passed-in values are modified such that the object maximally fits.
*
* @param min upper left corner of window (modified to fit)
* @param max lower right corner of window (modified to fit)
* @param width width of object to fit
* @param height height of object to fit
*/
void MaxFit(ImVec2* min, ImVec2* max, float width, float height);
} // namespace wpi::gui