From ba8d1033e1adf0d205bce1b68a820006ec6065dc Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 18 Sep 2026 18:51:38 +0200 Subject: [PATCH 1/7] Assign image cache to tinyui context --- src/backends/sdl2_iodevice.h | 11 +++++++++-- src/backends/sdl2_renderer.h | 15 +++++++++++++++ src/tinyui.h | 30 ++++++++++++++++++++++++------ src/tinyui_config.h | 7 +++++++ src/widgets.h | 8 ++++++++ 5 files changed, 63 insertions(+), 8 deletions(-) diff --git a/src/backends/sdl2_iodevice.h b/src/backends/sdl2_iodevice.h index fabd151..b31bf8a 100644 --- a/src/backends/sdl2_iodevice.h +++ b/src/backends/sdl2_iodevice.h @@ -23,13 +23,20 @@ SOFTWARE. */ #pragma once +/** + * @file sdl2_iodevice.h + * @brief SDL2 I/O device implementation for TinyUI. + * + * This file contains the SDL2-based input/output device backend for the TinyUI library. + */ + #include namespace tinyui { -/// @brief the SDL2 implementation for an io-device. +/// @brief The SDL2 implementation for an io-device. /// -/// IO-Devices are used to contrl any kind of input / output operations. +/// IO-Devices are used to control any kind of input / output operations. struct IODevice { // No copying or moving allowed. IODevice(const IODevice &) = delete; diff --git a/src/backends/sdl2_renderer.h b/src/backends/sdl2_renderer.h index 22c8545..83c3163 100644 --- a/src/backends/sdl2_renderer.h +++ b/src/backends/sdl2_renderer.h @@ -23,6 +23,13 @@ SOFTWARE. */ #pragma once +/** + * @file sdl2_renderer.h + * @brief SDL2 renderer implementation for TinyUI. + * + * This file contains the SDL2-based rendering backend for the TinyUI library. + */ + #include "tinyui.h" #include @@ -55,9 +62,11 @@ struct SurfaceImpl { } }; +/// @brief The font implementation using the SDL2_ttf library. struct FontImpl { TTF_Font *mFontImpl{nullptr}; + /// @brief Clear the font implementation. void clear() { if (mFontImpl != nullptr) { TTF_CloseFont(mFontImpl); @@ -121,7 +130,13 @@ struct Renderer { ~Renderer() = default; // Render implementation functions. + /// @brief Initialize the renderer. + /// @param ctx The TinyUI context. + /// @return ResultOk if successful, ErrorCode otherwise. static ret_code initRenderer(Context &ctx); + /// @brief Release the renderer. + /// @param ctx The TinyUI context. + /// @return ResultOk if successful, ErrorCode otherwise. static ret_code releaseRenderer(Context &ctx); static ret_code initScreen(Context &ctx, int32_t x, int32_t y, int32_t w, int32_t h); static ret_code initScreen(Context &ctx, SDL_Window *mWindow, SDL_Renderer *mRenderer); diff --git a/src/tinyui.h b/src/tinyui.h index 782e245..b2870da 100644 --- a/src/tinyui.h +++ b/src/tinyui.h @@ -23,6 +23,14 @@ SOFTWARE. */ #pragma once +/** + * @file tinyui.h + * @brief Main header file for the TinyUI library. + * + * This file contains the core types, structures, and interfaces for the TinyUI library, + * including widget definitions, context management, and rendering interfaces. + */ + #include "tinyui_config.h" #include @@ -98,9 +106,11 @@ struct SDLContext; // Type declarations ---------------------------------------------------------- -/// @brief This enum is used to describe the alignment of a widget. +/// @brief The unique identifier type used for widgets. using Id = uint64_t; +/// @brief This enum is used to describe the alignment of a widget. + /// @brief The return code type used in the ui library. using ret_code = int32_t; @@ -462,7 +472,10 @@ struct BackendContext { void *mHandle{nullptr}; ///< The backend specific handle. }; -/// @brief The tiny ui context. +/// @brief The main context struct for the TinyUI library. +/// +/// This struct holds all the global state and configuration for a TinyUI instance, +/// including the widget tree, styles, fonts, images, and event handling. struct Context { bool mCreated{false}; ///< The created state. bool mRequestShutdown{false}; ///< The request shutdown state. @@ -500,7 +513,7 @@ struct Context { static void destroy(Context *ctx); /// @brief Will return the current tiny ui context. - /// @return The current tiny ui context. + /// @return The current tiny ui context, nullptr if not created. static Context *getCurrent(); /// @brief Will add an image to the image cache. @@ -508,8 +521,8 @@ struct Context { /// @param image The image to add. void addImage(const char *name, Image *image); - /// @brief Will lookfor for an imgae described by its name. - /// @param name The name of the image- + /// @brief Will look for an image described by its name. + /// @param name The name of the image. /// @return The image if found, nullptr otherwise. Image *getImage(const char *name); @@ -526,7 +539,7 @@ struct Context { ~Context() = default; }; -/// @brief The tiny ui app interface. +/// @brief The tiny ui application interface. /// /// The tiny ui interface is used to create and manage the tiny ui context and to run the tiny ui. struct TinyUi { @@ -536,6 +549,11 @@ struct TinyUi { /// @return true if successful. static bool createContext(const char *title, const Style &style); + /// @brief Will create the tinyui context with a custom logger. + /// @param[in] title The app title. + /// @param[in] style The style to use. + /// @param[in] logger The custom logger function. + /// @return true if successful. static bool createContext(const char *title, const Style &style, tui_log_func logger); /// @brief Will destroy the context. diff --git a/src/tinyui_config.h b/src/tinyui_config.h index 7953b12..a0d2d2a 100644 --- a/src/tinyui_config.h +++ b/src/tinyui_config.h @@ -23,5 +23,12 @@ SOFTWARE. */ #pragma once +/** + * @file tinyui_config.h + * @brief Configuration file for the TinyUI library. + * + * This file contains configuration macros and settings for the TinyUI library. + */ + // Enable this to get verbose logs. //#define TINYUI_TRACE_ENABLED diff --git a/src/widgets.h b/src/widgets.h index 0aabec6..125c593 100644 --- a/src/widgets.h +++ b/src/widgets.h @@ -23,6 +23,14 @@ SOFTWARE. */ #pragma once +/** + * @file widgets.h + * @brief Widget definitions for the TinyUI library. + * + * This file contains all widget types, widget creation functions, and widget management + * utilities for the TinyUI library. + */ + #include "tinyui.h" namespace tinyui { From 3f9ab4ba2a4cb73c4aaf6c508fb9c58cd9e8e975 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 18 Sep 2026 19:56:53 +0200 Subject: [PATCH 2/7] Add missing context getter + doc --- CMakeLists.txt | 6 +++--- src/tinyui.cpp | 35 +++++++++++++++++++++++++++++++++++ src/tinyui.h | 2 ++ src/widgets.cpp | 18 +++++++----------- 4 files changed, 47 insertions(+), 14 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e7faae3..fb92534 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,9 +25,9 @@ INCLUDE_DIRECTORIES( contrib/stb_image ) -find_package(SDL2 CONFIG REQUIRED) +find_package(SDL2 CONFIG REQUIRED) find_package(SDL2_image CONFIG REQUIRED) -find_package(SDL2_ttf CONFIG REQUIRED) +find_package(SDL2_ttf CONFIG REQUIRED) # Find GoogleTest for unit tests find_package(GTest QUIET) @@ -39,7 +39,7 @@ SET(tinyui_backends_src src/backends/sdl2_iodevice.cpp ) -SOURCE_GROUP( Backends FILES ${tinyui_backends_src} ) +SOURCE_GROUP(Backends FILES ${tinyui_backends_src} ) ADD_LIBRARY(tiny_ui src/tinyui.h diff --git a/src/tinyui.cpp b/src/tinyui.cpp index afd992a..e172775 100644 --- a/src/tinyui.cpp +++ b/src/tinyui.cpp @@ -96,6 +96,10 @@ void Context::destroy(Context *ctx) { delete ctx; } +Context *Context::getCurrent() { + return gCtx; +} + void Context::addImage(const char *name, Image *image) { if (name == nullptr || image == nullptr) { return; @@ -123,6 +127,37 @@ bool Context::removeImage(const char *name) { return mImageCache.erase(std::string(name)) > 0; } +Image *Context::loadIntoImageCache(const char *filename) { + if (filename == nullptr) { + return nullptr; + } + + Image *image = getImage(filename); + if (image != nullptr) { + return image; + } + + int w{ -1 }; + int h{ -1 }; + int bytesPerPixel{ -1 }; + unsigned char *data = stbi_load(filename, &w, &h, &bytesPerPixel, 0); + if (data == nullptr) { + return nullptr; + } + + image = new Image; + int32_t pitch = w * bytesPerPixel; + pitch = (pitch + 3) & ~3; + image->mSurfaceImpl = Renderer::createSurfaceImpl(data, w, h, bytesPerPixel, pitch); + image->mX = w; + image->mY = h; + image->mComp = bytesPerPixel; + mImageCache[filename] = image; + + return image; + +} + bool TinyUi::createContext(const char *title, const Style &style) { if (gCtx != nullptr) { return false; diff --git a/src/tinyui.h b/src/tinyui.h index b2870da..7c357b3 100644 --- a/src/tinyui.h +++ b/src/tinyui.h @@ -531,6 +531,8 @@ struct Context { /// @return true if successful. bool removeImage(const char *name); + Image *loadIntoImageCache(const char *filename); + private: /// @brief The default class constructor Context() = default; diff --git a/src/widgets.cpp b/src/widgets.cpp index 25980de..8326b5b 100644 --- a/src/widgets.cpp +++ b/src/widgets.cpp @@ -57,12 +57,12 @@ namespace { return it->second; } - Image *loadIntoImageCache(Context &ctx, const char *filename) { + /*Image *loadIntoImageCache(Context &ctx, const char *filename) { if (filename == nullptr) { return nullptr; } - Image *image = findImage(ctx, filename); + Image *image = ctx.getImage(filename); if (image != nullptr) { return image; } @@ -76,10 +76,6 @@ namespace { } image = new Image; - if (image == nullptr) { - return nullptr; - } - int32_t pitch = w * bytesPerPixel; pitch = (pitch + 3) & ~3; image->mSurfaceImpl = Renderer::createSurfaceImpl(data, w, h, bytesPerPixel, pitch); @@ -89,7 +85,7 @@ namespace { ctx.mImageCache[filename] = image; return image; - } + }*/ void releaseImageCache(Context &ctx) { for (auto it = ctx.mImageCache.begin(); it != ctx.mImageCache.end(); ++it) { @@ -345,7 +341,7 @@ WidgetHandle Widgets::textButton(WidgetHandle parentId, const char *text, const return child->mHandle; } -WidgetHandle Widgets::imageButton(WidgetHandle parentId, const char *image, const Rect &rect, CallbackI *callback) { +WidgetHandle Widgets::imageButton(WidgetHandle parentId, const char *imageName, const Rect &rect, CallbackI *callback) { auto &ctx = TinyUi::getContext(); if (ctx.mBackendCtx == nullptr) { return WidgetHandle{WidgetHandle::InvalidId}; @@ -361,8 +357,8 @@ WidgetHandle Widgets::imageButton(WidgetHandle parentId, const char *image, cons callback->incRef(); } - if (image != nullptr) { - child->mImage = loadIntoImageCache(ctx, image); + if (imageName != nullptr) { + child->mImage = ctx.loadIntoImageCache(imageName); } return child->mHandle; @@ -396,7 +392,7 @@ WidgetHandle Widgets::imageBox(WidgetHandle parentId, const char* image, const R Widget *child = createWidget(ctx, parentId, rect, WidgetType::ImageBox); child->mFilledRect = filled; if (image != nullptr) { - child->mImage = loadIntoImageCache(ctx, image); + child->mImage = ctx.loadIntoImageCache(image); } return child->mHandle; From 874d430c16ea277098fdcf51044fa2f176de1433 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 18 Sep 2026 19:58:40 +0200 Subject: [PATCH 3/7] Remove dead code. --- src/widgets.cpp | 32 +------------------------------- 1 file changed, 1 insertion(+), 31 deletions(-) diff --git a/src/widgets.cpp b/src/widgets.cpp index 8326b5b..f03b361 100644 --- a/src/widgets.cpp +++ b/src/widgets.cpp @@ -56,37 +56,7 @@ namespace { return it->second; } - - /*Image *loadIntoImageCache(Context &ctx, const char *filename) { - if (filename == nullptr) { - return nullptr; - } - - Image *image = ctx.getImage(filename); - if (image != nullptr) { - return image; - } - - int w{ -1 }; - int h{ -1 }; - int bytesPerPixel{ -1 }; - unsigned char *data = stbi_load(filename, &w, &h, &bytesPerPixel, 0); - if (data == nullptr) { - return nullptr; - } - - image = new Image; - int32_t pitch = w * bytesPerPixel; - pitch = (pitch + 3) & ~3; - image->mSurfaceImpl = Renderer::createSurfaceImpl(data, w, h, bytesPerPixel, pitch); - image->mX = w; - image->mY = h; - image->mComp = bytesPerPixel; - ctx.mImageCache[filename] = image; - - return image; - }*/ - + void releaseImageCache(Context &ctx) { for (auto it = ctx.mImageCache.begin(); it != ctx.mImageCache.end(); ++it) { if (Image *image = it->second; image != nullptr) { From 291d2f283f893f19cae9683cc6d554400f2b1ce7 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 18 Sep 2026 20:01:09 +0200 Subject: [PATCH 4/7] Update tinyui.h --- src/tinyui.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/tinyui.h b/src/tinyui.h index 7c357b3..1c4a32b 100644 --- a/src/tinyui.h +++ b/src/tinyui.h @@ -109,8 +109,6 @@ struct SDLContext; /// @brief The unique identifier type used for widgets. using Id = uint64_t; -/// @brief This enum is used to describe the alignment of a widget. - /// @brief The return code type used in the ui library. using ret_code = int32_t; From c1a8e1670833f0901b24733cf85f68b59c9a7635 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 18 Sep 2026 20:03:14 +0200 Subject: [PATCH 5/7] Add loadIntoImageCache method declaration Added loadIntoImageCache method to load images. --- src/tinyui.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tinyui.h b/src/tinyui.h index 1c4a32b..dd9b218 100644 --- a/src/tinyui.h +++ b/src/tinyui.h @@ -529,6 +529,9 @@ struct Context { /// @return true if successful. bool removeImage(const char *name); + /// @brief Will load an image into the image cache. + /// @param[in] filename The name of the image to load. + /// @return A pointer showing to the image or nullptr in case of an error. Image *loadIntoImageCache(const char *filename); private: From b7397b20742018f4d5581a782da052682361ebf5 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 18 Sep 2026 20:08:06 +0200 Subject: [PATCH 6/7] review finding: remove unused function --- src/widgets.cpp | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/widgets.cpp b/src/widgets.cpp index f03b361..27e1729 100644 --- a/src/widgets.cpp +++ b/src/widgets.cpp @@ -43,19 +43,6 @@ namespace { static Id id{ RootHandle }; return ++id; } - - Image *findImage(Context &ctx, const char *filename) { - if (filename == nullptr) { - return nullptr; - } - - auto it = ctx.mImageCache.find(filename); - if (it == ctx.mImageCache.end()) { - return nullptr; - } - - return it->second; - } void releaseImageCache(Context &ctx) { for (auto it = ctx.mImageCache.begin(); it != ctx.mImageCache.end(); ++it) { From 3b8fdc93dd0d4f6b0aea9a0c8cb8f65e0dc6187c Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 18 Sep 2026 20:40:03 +0200 Subject: [PATCH 7/7] Fix image cache leak --- src/tinyui.cpp | 21 ++++++++++++++++++++- src/tinyui.h | 4 +++- src/widgets.cpp | 11 ----------- 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/src/tinyui.cpp b/src/tinyui.cpp index e172775..56ffa2f 100644 --- a/src/tinyui.cpp +++ b/src/tinyui.cpp @@ -51,12 +51,18 @@ static constexpr char const *SeverityToken[] = { }; static void logVersion(const Context &ctx) { - const std::string msg ="TinyUI Version: " + std::to_string(ctx.mVersion.major) + "." + std::to_string(ctx.mVersion.minor) + "." + std::to_string(ctx.mVersion.patch); + const std::string msg ="TinyUI Version: " + + std::to_string(ctx.mVersion.major) + + "." + + std::to_string(ctx.mVersion.minor) + + "." + + std::to_string(ctx.mVersion.patch); ctx.mLogger(LogSeverity::Info, msg.c_str()); } void log_message(LogSeverity severity, const char *message) { assert(message != nullptr); + if (severity == LogSeverity::Message) { std::cout << message << "\n"; } else { @@ -66,6 +72,10 @@ void log_message(LogSeverity severity, const char *message) { Context *gCtx = nullptr; +Context::~Context() { + clearImageCache(); +} + Context *Context::create(const char *title, const Style &style) { auto *ctx = new Context; ctx->mLogger = log_message; @@ -155,7 +165,16 @@ Image *Context::loadIntoImageCache(const char *filename) { mImageCache[filename] = image; return image; +} +void Context::clearImageCache() { + for (auto it = mImageCache.begin(); it != mImageCache.end(); ++it) { + if (auto *image = it->second; image != nullptr) { + Renderer::releaseSurfaceImpl(image->mSurfaceImpl); + delete image; + } + } + mImageCache.clear(); } bool TinyUi::createContext(const char *title, const Style &style) { diff --git a/src/tinyui.h b/src/tinyui.h index dd9b218..27626e9 100644 --- a/src/tinyui.h +++ b/src/tinyui.h @@ -534,12 +534,14 @@ struct Context { /// @return A pointer showing to the image or nullptr in case of an error. Image *loadIntoImageCache(const char *filename); + void clearImageCache(); + private: /// @brief The default class constructor Context() = default; /// @brief The class destructor. - ~Context() = default; + ~Context(); }; /// @brief The tiny ui application interface. diff --git a/src/widgets.cpp b/src/widgets.cpp index 27e1729..a047570 100644 --- a/src/widgets.cpp +++ b/src/widgets.cpp @@ -44,16 +44,6 @@ namespace { return ++id; } - void releaseImageCache(Context &ctx) { - for (auto it = ctx.mImageCache.begin(); it != ctx.mImageCache.end(); ++it) { - if (Image *image = it->second; image != nullptr) { - Renderer::releaseSurfaceImpl(image->mSurfaceImpl); - delete image; - } - } - ctx.mImageCache.clear(); - } - Widget *getValidRoot(Context &ctx) { if (ctx.mRoot != nullptr) { return ctx.mRoot; @@ -729,7 +719,6 @@ void Widgets::clear() { Widget *current{ctx.mRoot}; recursiveClear(current); ctx.mRoot = nullptr; - releaseImageCache(ctx); } bool Widgets::clearItem(WidgetHandle id, bool recursive) {