Autopositioning, custom fonts
This commit is contained in:
162
src/quip.cpp
162
src/quip.cpp
@@ -8,21 +8,38 @@
|
||||
|
||||
namespace Quip {
|
||||
int windowCount = 0;
|
||||
bool ttfInitialized = false;
|
||||
|
||||
namespace Widget {
|
||||
TextLabel::TextLabel() = default;
|
||||
TextLabel::TextLabel(std::string text, int xpos, int ypos) : text(std::move(text)), xpos(xpos), ypos(ypos) {}
|
||||
void TextLabel::render(const Window &window) const {
|
||||
TextLabel::TextLabel(std::string text, Font font) : text(std::move(text)), font(std::move(font)) {}
|
||||
int TextLabel::getVertSize() const {
|
||||
SDL_Surface* surface = TTF_RenderText_Blended(font.getFont(), text.c_str(), 0, {255, 255, 255, 255});
|
||||
if (!surface) {
|
||||
return 20; // Default height if rendering fails
|
||||
}
|
||||
int height = surface->h;
|
||||
SDL_DestroySurface(surface);
|
||||
return height;
|
||||
}
|
||||
void TextLabel::render(Window &window, int customY) const {
|
||||
constexpr SDL_Color color = {255, 255, 255, 255};
|
||||
SDL_Surface* surface = TTF_RenderText_Blended(window.font, text.c_str(), 0, color);
|
||||
SDL_Surface* surface = TTF_RenderText_Blended(font.getFont(), text.c_str(), 0, color);
|
||||
if (!surface) {
|
||||
return; // Skip rendering if font fails
|
||||
}
|
||||
SDL_Texture* texture = SDL_CreateTextureFromSurface(window.renderer, surface);
|
||||
SDL_FRect destRect = {static_cast<float>(xpos), static_cast<float>(ypos), static_cast<float>(surface->w), static_cast<float>(surface->h)};
|
||||
SDL_FRect destRect;
|
||||
destRect = {static_cast<float>(10), static_cast<float>(customY), static_cast<float>(surface->w), static_cast<float>(surface->h)};
|
||||
if (customY == -1) {
|
||||
destRect.y += static_cast<float>(window.getYPos(this->getVertSize()));
|
||||
}
|
||||
SDL_RenderTexture(window.renderer, texture, nullptr, &destRect);
|
||||
SDL_DestroyTexture(texture);
|
||||
SDL_DestroySurface(surface);
|
||||
}
|
||||
|
||||
Image::Image(const std::string& filepath, int xpos, int ypos, int width, int height) : width(width), height(height), xpos(xpos), ypos(ypos) {
|
||||
Image::Image(const std::string& filepath, int width, int height) : width(width), height(height) {
|
||||
surface = IMG_Load(filepath.c_str());
|
||||
if (surface == nullptr) {
|
||||
Window().error(4, "Couldn't load image " + filepath);
|
||||
@@ -35,31 +52,97 @@ namespace Quip {
|
||||
}
|
||||
}
|
||||
|
||||
void Image::render(const Window &window) const {
|
||||
void Image::render(Window &window) const {
|
||||
SDL_Texture* texture = SDL_CreateTextureFromSurface(window.renderer, surface);
|
||||
SDL_FRect destRect = {static_cast<float>(xpos), static_cast<float>(ypos), static_cast<float>(width), static_cast<float>(height)};
|
||||
SDL_FRect destRect = {static_cast<float>(10), static_cast<float>(window.getYPos(height)), static_cast<float>(width), static_cast<float>(height)};
|
||||
SDL_RenderTexture(window.renderer, texture, nullptr, &destRect);
|
||||
SDL_DestroyTexture(texture);
|
||||
}
|
||||
|
||||
Button::Button(std::string text, std::function<void()> callback, int xpos, int ypos) : text(std::move(text)), onCLick(std::move(callback)), xpos(xpos), ypos(ypos) {}
|
||||
void Button::render(const Window &window) const {
|
||||
SDL_SetRenderDrawColor(window.renderer, 40, 60, 80, 255); // Dark gray background
|
||||
SDL_FRect backgroundRect = {static_cast<float>(xpos), static_cast<float>(ypos), static_cast<float>(width), static_cast<float>(height)};
|
||||
Button::Button(TextLabel text, std::function<void()> callback) : text(std::move(text)), onCLick(std::move(callback)) {}
|
||||
void Button::render(Window &window) {
|
||||
SDL_SetRenderDrawColor(window.renderer, 40, 60, 80, 255);
|
||||
const int renderYPos = window.getYPos(height);
|
||||
xpos = 10;
|
||||
ypos = renderYPos;
|
||||
SDL_FRect backgroundRect = {static_cast<float>(10), static_cast<float>(renderYPos), static_cast<float>(width), static_cast<float>(height)};
|
||||
SDL_RenderFillRect(window.renderer, &backgroundRect);
|
||||
constexpr SDL_Color color = {255, 255, 255, 255};
|
||||
SDL_Surface* surface = TTF_RenderText_Blended(window.font, text.c_str(), 0, color);
|
||||
SDL_Texture* texture = SDL_CreateTextureFromSurface(window.renderer, surface);
|
||||
SDL_FRect destRect = {static_cast<float>(xpos), static_cast<float>(ypos), static_cast<float>(surface->w), static_cast<float>(surface->h)};
|
||||
SDL_RenderTexture(window.renderer, texture, nullptr, &destRect);
|
||||
SDL_DestroyTexture(texture);
|
||||
SDL_DestroySurface(surface);
|
||||
text.render(window, renderYPos);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Font::Font() {
|
||||
if (!ttfInitialized) {
|
||||
if (!TTF_Init()) {
|
||||
Font::error(2, "Couldn't initialize TTF");
|
||||
}
|
||||
ttfInitialized = true;
|
||||
}
|
||||
TTF_Font* rawFont = TTF_OpenFont("/usr/share/fonts/noto/NotoSans-Regular.ttf", 16);
|
||||
if (!rawFont) {
|
||||
std::cerr << "Failed to load default font: " << SDL_GetError() << std::endl;
|
||||
Font::error(4, SDL_GetError());
|
||||
return; // This won't execute if error throws
|
||||
}
|
||||
font = std::shared_ptr<TTF_Font>(rawFont, [](TTF_Font* f) {
|
||||
if (f) TTF_CloseFont(f);
|
||||
});
|
||||
}
|
||||
Font::Font(const std::string& path, const float ptsize) {
|
||||
if (!ttfInitialized) {
|
||||
if (!TTF_Init()) {
|
||||
Font::error(2, "Couldn't initialize TTF");
|
||||
}
|
||||
ttfInitialized = true;
|
||||
}
|
||||
TTF_Font* rawFont = TTF_OpenFont(path.c_str(), ptsize);
|
||||
if (!rawFont) {
|
||||
std::cerr << "Failed to load font from " << path << ": " << SDL_GetError() << std::endl;
|
||||
Font::error(2, SDL_GetError());
|
||||
return;
|
||||
}
|
||||
font = std::shared_ptr<TTF_Font>(rawFont, [](TTF_Font* f) {
|
||||
if (f) TTF_CloseFont(f);
|
||||
});
|
||||
}
|
||||
void Font::setFont(const std::string& path, const float ptsize) {
|
||||
TTF_Font* rawFont = TTF_OpenFont(path.c_str(), ptsize);
|
||||
if (!rawFont) {
|
||||
error(4, SDL_GetError());
|
||||
}
|
||||
font = std::shared_ptr<TTF_Font>(rawFont, TTF_CloseFont);
|
||||
}
|
||||
TTF_Font* Font::getFont() const {
|
||||
return font.get();
|
||||
}
|
||||
void Font::error(int errCode, const std::string& context) {
|
||||
std::cerr << "Quip::Font() error " << errCode << ": ";
|
||||
switch (errCode) {
|
||||
default:
|
||||
case 0:
|
||||
std::cerr << "Generic error (please raise an issue on Chookspace)" << std::endl;
|
||||
break;
|
||||
case 1:
|
||||
std::cerr << "Font file is inaccessible or unable to be read" << std::endl;
|
||||
break;
|
||||
}
|
||||
if (!context.empty()) {
|
||||
std::cerr << "Context: " << context << std::endl;
|
||||
}
|
||||
switch (errCode) {
|
||||
default:
|
||||
case 0:
|
||||
throw std::runtime_error("Quip::Font() generic error");
|
||||
break;
|
||||
case 1:
|
||||
throw std::runtime_error("Quip::Font() file error");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Window::Window() = default;
|
||||
Window::Window(std::string title, int width, int height, const std::function<void()>& onFrame) : title(std::move(title)), width(width), height(height), onFrame(onFrame) {
|
||||
Window::Window(std::string title, const int width, const int height, const std::function<void()>& onFrame) : title(std::move(title)), width(width), height(height), onFrame(onFrame) {
|
||||
windowCount ++;
|
||||
if (width <= 0 || height <= 0) {
|
||||
error(2, "width is " + std::to_string(width) + " and height is " + std::to_string(height));
|
||||
@@ -72,16 +155,16 @@ namespace Quip {
|
||||
if (!SDL_CreateWindowAndRenderer(title.c_str(), width, height, SDL_WINDOW_RESIZABLE, &window, &renderer)) {
|
||||
error(1, SDL_GetError());
|
||||
}
|
||||
if (!TTF_Init()) {
|
||||
error(1, SDL_GetError());
|
||||
}
|
||||
font = TTF_OpenFont("/usr/share/fonts/noto/NotoSans-Regular.ttf", 16);
|
||||
if (!font) {
|
||||
error(1, SDL_GetError());
|
||||
}
|
||||
font = Font("/usr/share/fonts/noto/NotoSans-Regular.ttf", 16);
|
||||
}
|
||||
|
||||
void Window::error(int errCode, const std::string& context) const {
|
||||
int Window::getYPos(const int vertsize) {
|
||||
const int retval = nextYPos;
|
||||
nextYPos += vertsize + 10;
|
||||
return retval;
|
||||
}
|
||||
|
||||
void Window::error(const int errCode, const std::string& context) const {
|
||||
SDL_DestroyWindow(window);
|
||||
SDL_DestroyRenderer(renderer);
|
||||
SDL_Quit();
|
||||
@@ -130,33 +213,36 @@ namespace Quip {
|
||||
if (id.empty()) {
|
||||
error(3, "Widget ID is empty");
|
||||
}
|
||||
widgets[id] = widget;
|
||||
widgets.emplace_back(widget);
|
||||
widgetIds.emplace(id, widgets.size() - 1);
|
||||
}
|
||||
|
||||
Widget::Widget* Window::getChild(const std::string& id) {
|
||||
if (widgets.find(id) == widgets.end()) {
|
||||
if (widgetIds.find(id) == widgetIds.end()) {
|
||||
error(3, "Cannot find widget with id " + id);
|
||||
return nullptr;
|
||||
}
|
||||
return &widgets[id];
|
||||
return &widgets[widgetIds[id]];
|
||||
}
|
||||
|
||||
int Window::run() {
|
||||
while (true) {
|
||||
onFrame();
|
||||
if (onFrame) {
|
||||
onFrame();
|
||||
}
|
||||
while (SDL_PollEvent(&event)) {
|
||||
switch (event.type) {
|
||||
case SDL_EVENT_QUIT:
|
||||
goto cleanup;
|
||||
break;
|
||||
case SDL_EVENT_MOUSE_BUTTON_DOWN: {
|
||||
int mouseX = event.button.x;
|
||||
int mouseY = event.button.y;
|
||||
const int mouseX = event.button.x;
|
||||
const int mouseY = event.button.y;
|
||||
|
||||
// Check if click is inside any button
|
||||
for (auto& [id, widget] : widgets) {
|
||||
for (auto& widget : widgets) {
|
||||
if (std::holds_alternative<Widget::Button>(widget)) {
|
||||
Widget::Button& button = std::get<Widget::Button>(widget);
|
||||
auto& button = std::get<Widget::Button>(widget);
|
||||
// Check if click is inside button bounds
|
||||
if (mouseX >= button.xpos && mouseX <= button.xpos + button.width &&
|
||||
mouseY >= button.ypos && mouseY <= button.ypos + button.height) {
|
||||
@@ -167,11 +253,14 @@ namespace Quip {
|
||||
}
|
||||
}
|
||||
}
|
||||
default: ;
|
||||
}
|
||||
}
|
||||
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
|
||||
SDL_RenderClear(renderer);
|
||||
for (auto const& [id, widget] : widgets) {
|
||||
nextXpos = 10;
|
||||
nextYPos = 10;
|
||||
for (auto &widget : widgets) {
|
||||
if (std::holds_alternative<Widget::TextLabel>(widget)) {
|
||||
std::get<Widget::TextLabel>(widget).render(*this);
|
||||
} else if (std::holds_alternative<Widget::Button>(widget)) {
|
||||
@@ -187,7 +276,6 @@ namespace Quip {
|
||||
cleanup:
|
||||
windowCount --;
|
||||
if (windowCount == 0) {
|
||||
TTF_CloseFont(font);
|
||||
SDL_DestroyRenderer(renderer);
|
||||
SDL_DestroyWindow(window);
|
||||
SDL_Quit();
|
||||
|
||||
252
src/quip.h
252
src/quip.h
@@ -2,60 +2,286 @@
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3_ttf/SDL_ttf.h>
|
||||
#include <SDL3_image/SDL_image.h>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
/**
|
||||
* @brief Main namespace for the Quip GUI library.
|
||||
*
|
||||
* Quip provides a simple, easy-to-use GUI framework built on top of SDL3.
|
||||
* It supports text labels, images, buttons, and window management.
|
||||
*/
|
||||
namespace Quip {
|
||||
/** @brief Flag indicating whether TTF (TrueType Font) has been initialized. */
|
||||
extern bool ttfInitialized;
|
||||
|
||||
class Window;
|
||||
|
||||
/**
|
||||
* @brief Font class for managing TrueType fonts.
|
||||
*
|
||||
* Handles font loading, initialization, and provides access to the underlying
|
||||
* SDL TTF font object. Automatically initializes TTF subsystem on first use.
|
||||
*/
|
||||
class Font {
|
||||
std::shared_ptr<TTF_Font> font;
|
||||
public:
|
||||
/**
|
||||
* @brief Sets the font from a file path.
|
||||
* @param path Path to the TrueType font file.
|
||||
* @param ptsize Point size for the font.
|
||||
* @throws std::runtime_error if the font cannot be loaded.
|
||||
*/
|
||||
void setFont(const std::string& path, float ptsize);
|
||||
|
||||
/**
|
||||
* @brief Gets the underlying SDL TTF font pointer.
|
||||
* @return Pointer to the TTF_Font object, or nullptr if not initialized.
|
||||
*/
|
||||
[[nodiscard]] TTF_Font* getFont() const;
|
||||
|
||||
/**
|
||||
* @brief Static error handler for font-related errors.
|
||||
* @param errCode Error code (0 = generic, 1 = file error, 2 = TTF init error, 4 = file error).
|
||||
* @param context Additional context string for the error.
|
||||
* @throws std::runtime_error with appropriate error message.
|
||||
*/
|
||||
static void error(int errCode = 0, const std::string& context = "") ;
|
||||
|
||||
/**
|
||||
* @brief Constructs a Font from a file path.
|
||||
* @param path Path to the TrueType font file.
|
||||
* @param ptsize Point size for the font.
|
||||
* @throws std::runtime_error if the font cannot be loaded.
|
||||
*/
|
||||
Font(const std::string& path, float ptsize);
|
||||
|
||||
/**
|
||||
* @brief Default constructor that loads the system default font.
|
||||
*
|
||||
* Attempts to load NotoSans-Regular from /usr/share/fonts/noto/NotoSans-Regular.ttf
|
||||
* with a default size of 16 points.
|
||||
* @throws std::runtime_error if the default font cannot be loaded.
|
||||
*/
|
||||
Font();
|
||||
};
|
||||
|
||||
/** @brief Global counter tracking the number of active Window instances. */
|
||||
extern int windowCount;
|
||||
|
||||
/**
|
||||
* @brief Namespace containing widget classes for UI elements.
|
||||
*/
|
||||
namespace Widget {
|
||||
/**
|
||||
* @brief Text label widget for displaying text in a window.
|
||||
*
|
||||
* Renders text using a specified font. Text is positioned automatically
|
||||
* based on the window's layout system, or can be positioned manually.
|
||||
*/
|
||||
class TextLabel {
|
||||
public:
|
||||
/** @brief The text content to display. */
|
||||
std::string text;
|
||||
int xpos = 0, ypos = 0;
|
||||
void render(const Window &window) const;
|
||||
explicit TextLabel(std::string text, int xpos, int ypos);
|
||||
|
||||
/** @brief The font to use for rendering the text. */
|
||||
Font font;
|
||||
|
||||
/**
|
||||
* @brief Renders the text label in the specified window.
|
||||
* @param window Reference to the Window to render into.
|
||||
* @param customY Custom Y position. If -1 (default), uses automatic positioning.
|
||||
*/
|
||||
void render(Window &window, int customY = -1) const;
|
||||
|
||||
/**
|
||||
* @brief Gets the vertical size (height) of the rendered text.
|
||||
* @return Height in pixels of the text when rendered.
|
||||
*/
|
||||
[[nodiscard]] int getVertSize() const;
|
||||
|
||||
/**
|
||||
* @brief Constructs a TextLabel with specified text and font.
|
||||
* @param text The text content to display.
|
||||
* @param font The font to use for rendering.
|
||||
*/
|
||||
TextLabel(std::string text, Font font);
|
||||
|
||||
/**
|
||||
* @brief Default constructor for TextLabel.
|
||||
*
|
||||
* Creates an empty text label with default font.
|
||||
*/
|
||||
TextLabel();
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Image widget for displaying image files in a window.
|
||||
*
|
||||
* Supports loading images from file paths and rendering them at
|
||||
* specified or auto-calculated dimensions.
|
||||
*/
|
||||
class Image {
|
||||
SDL_Surface* surface;
|
||||
public:
|
||||
/** @brief Width of the image in pixels. */
|
||||
int width = 0, height = 0;
|
||||
int xpos = 0, ypos = 0;
|
||||
void render(const Window &window) const;
|
||||
Image(const std::string& filepath, int xpos, int ypos, int width = -1, int height = -1);
|
||||
|
||||
/**
|
||||
* @brief Renders the image in the specified window.
|
||||
* @param window Reference to the Window to render into.
|
||||
*/
|
||||
void render(Window &window) const;
|
||||
|
||||
/**
|
||||
* @brief Constructs an Image from a file path.
|
||||
* @param filepath Path to the image file to load.
|
||||
* @param width Desired width in pixels. If -1, uses image's natural width.
|
||||
* @param height Desired height in pixels. If -1, uses image's natural height.
|
||||
* @throws std::runtime_error if the image cannot be loaded.
|
||||
*/
|
||||
explicit Image(const std::string& filepath, int width = -1, int height = -1);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Button widget for interactive clickable buttons.
|
||||
*
|
||||
* Displays a button with text and executes a callback function when clicked.
|
||||
* Button dimensions and position can be customized.
|
||||
*/
|
||||
class Button {
|
||||
public:
|
||||
std::string text;
|
||||
/** @brief Text label displayed on the button. */
|
||||
TextLabel text;
|
||||
|
||||
/** @brief Callback function executed when the button is clicked. */
|
||||
std::function<void()> onCLick;
|
||||
int xpos = 0, ypos = 0;
|
||||
|
||||
/** @brief Width of the button in pixels. Default: 100. */
|
||||
int width = 100, height = 30;
|
||||
void render(const Window &window) const;
|
||||
explicit Button(std::string text, std::function<void()> callback, int xpos, int ypos);
|
||||
|
||||
/** @brief X position of the button in pixels. Set automatically during rendering. */
|
||||
int xpos = 0, ypos = 0;
|
||||
|
||||
/**
|
||||
* @brief Renders the button in the specified window.
|
||||
* @param window Reference to the Window to render into.
|
||||
*/
|
||||
void render(Window &window);
|
||||
|
||||
/**
|
||||
* @brief Constructs a Button with text and callback.
|
||||
* @param text TextLabel to display on the button.
|
||||
* @param callback Function to call when the button is clicked.
|
||||
*/
|
||||
explicit Button(TextLabel text, std::function<void()> callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Variant type that can hold any widget type.
|
||||
*
|
||||
* Used for storing heterogeneous collections of widgets in a Window.
|
||||
*/
|
||||
typedef std::variant<TextLabel, Button, Image> Widget;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Window class representing a GUI window.
|
||||
*
|
||||
* Manages an SDL window, renderer, and collection of widgets. Handles
|
||||
* event processing, rendering, and widget layout. Supports custom frame
|
||||
* callbacks for per-frame logic.
|
||||
*/
|
||||
class Window {
|
||||
public:
|
||||
/** @brief Pointer to the underlying SDL window. */
|
||||
SDL_Window* window;
|
||||
|
||||
/** @brief Pointer to the SDL renderer for drawing operations. */
|
||||
SDL_Renderer* renderer;
|
||||
|
||||
/** @brief SDL event structure for processing window events. */
|
||||
SDL_Event event;
|
||||
TTF_Font* font;
|
||||
|
||||
/** @brief Default font used by the window. */
|
||||
Font font;
|
||||
|
||||
/** @brief Window title displayed in the title bar. */
|
||||
std::string title;
|
||||
|
||||
/** @brief Window width in pixels. */
|
||||
int width, height;
|
||||
std::map<std::string, Widget::Widget> widgets;
|
||||
|
||||
/** @brief Next X position for automatic widget layout. */
|
||||
int nextXpos = 0, nextYPos = 0;
|
||||
|
||||
/** @brief Map of widget IDs to their indices in the widgets vector. */
|
||||
std::map<std::string, int> widgetIds;
|
||||
|
||||
/** @brief Vector containing all widgets added to this window. */
|
||||
std::vector<Widget::Widget> widgets;
|
||||
|
||||
/** @brief Optional callback function called each frame before rendering. */
|
||||
std::function<void()> onFrame;
|
||||
|
||||
/**
|
||||
* @brief Error handler for window-related errors.
|
||||
* @param errCode Error code (0 = generic, 1 = SDL error, 2 = size error, 3 = widget name error, 4 = file error).
|
||||
* @param context Additional context string for the error.
|
||||
* @throws std::runtime_error with appropriate error message.
|
||||
*/
|
||||
void error(int errCode = 0, const std::string& context = "") const;
|
||||
|
||||
/**
|
||||
* @brief Gets the next Y position for automatic widget layout.
|
||||
* @param vertsize Vertical size of the widget to be positioned.
|
||||
* @return Y position in pixels for the widget.
|
||||
*/
|
||||
int getYPos(int vertsize);
|
||||
|
||||
/**
|
||||
* @brief Runs the window's main event loop.
|
||||
*
|
||||
* Processes events, renders widgets, and handles window updates.
|
||||
* Blocks until the window is closed.
|
||||
* @return 0 on successful completion.
|
||||
*/
|
||||
int run();
|
||||
|
||||
/**
|
||||
* @brief Adds a widget to the window with a unique identifier.
|
||||
* @param id Unique string identifier for the widget.
|
||||
* @param widget The widget to add (TextLabel, Button, or Image).
|
||||
* @throws std::runtime_error if the ID is empty or invalid.
|
||||
*/
|
||||
void addChild(const std::string& id, const Widget::Widget& widget);
|
||||
|
||||
/**
|
||||
* @brief Retrieves a widget by its ID.
|
||||
* @param id The unique identifier of the widget.
|
||||
* @return Pointer to the widget, or nullptr if not found.
|
||||
* @throws std::runtime_error if the widget ID is not found.
|
||||
*/
|
||||
Widget::Widget* getChild(const std::string& id);
|
||||
|
||||
/**
|
||||
* @brief Constructs a Window with specified parameters.
|
||||
* @param title Window title displayed in the title bar.
|
||||
* @param width Window width in pixels. Default: 800.
|
||||
* @param height Window height in pixels. Default: 600.
|
||||
* @param onFrame Optional callback function called each frame. Default: nullptr.
|
||||
* @throws std::runtime_error if window creation fails or dimensions are invalid.
|
||||
*/
|
||||
explicit Window(std::string title, int width = 800, int height = 600, const std::function<void()>& onFrame = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Default constructor for Window.
|
||||
*
|
||||
* Creates an uninitialized window. Must be properly initialized before use.
|
||||
*/
|
||||
Window();
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user