From ee54f0a284b827c3e8c94066936b47d5d26fad10 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 28 Apr 2026 23:21:26 +0200 Subject: [PATCH 1/7] Introduce new concept. --- contrib/vcpkg | 2 +- samples/demo/main.cpp | 30 +++--- samples/hello_world/main.cpp | 8 +- src/tinyui.cpp | 3 +- src/tinyui.h | 23 ++++- src/widgets.cpp | 181 ++++++++++++++++++----------------- src/widgets.h | 52 ++++------ 7 files changed, 154 insertions(+), 145 deletions(-) diff --git a/contrib/vcpkg b/contrib/vcpkg index 594ad88..f1fe3ac 160000 --- a/contrib/vcpkg +++ b/contrib/vcpkg @@ -1 +1 @@ -Subproject commit 594ad8871e1e8e45f8e626c015fd611163430207 +Subproject commit f1fe3acb62b7aba476e48e3395c40d88478ac444 diff --git a/samples/demo/main.cpp b/samples/demo/main.cpp index e486a23..16dec67 100644 --- a/samples/demo/main.cpp +++ b/samples/demo/main.cpp @@ -31,7 +31,7 @@ static constexpr Id RootPanelId = 1; static constexpr Id NextPanelId = 100; -int quit(Id, void *instance) { +int quit(WidgetHandle, void *instance) { if (instance == nullptr) { return ErrorCode; } @@ -46,7 +46,7 @@ static uint32_t LastTick = 0; static uint32_t Diff = 0; static constexpr uint32_t TimeDiff = 10; -int updateProgressbar(Id, void *instance) { +int updateProgressbar(WidgetHandle, void *instance) { if (instance == nullptr) { return ErrorCode; } @@ -87,12 +87,12 @@ int main(int argc, char *argv[]) { } constexpr int32_t ButtonHeight = 25; - Widgets::panel(RootPanelId, 0, "Sample-Dialog", Rect(90, 5, 120, 600), nullptr); - Widgets::label(2, RootPanelId, "Title", Rect(100, 10, 100, 20), Alignment::Center); - Widgets::textButton(3, RootPanelId, "Test 1", Rect(100, 50, 100, ButtonHeight), Alignment::Center, nullptr); - Widgets::textButton(4, RootPanelId, "Test 2", Rect(100, 100, 100, ButtonHeight), Alignment::Center, nullptr); - Widgets::textButton(5, RootPanelId, "Test 3", Rect(100, 150, 100, ButtonHeight), Alignment::Center, nullptr); - Widgets::imageButton(6, RootPanelId, "button_test.png", Rect(100, 200, 100, ButtonHeight), nullptr); + WidgetHandle panel = Widgets::panel(WidgetHandle::getRootHandle(), "Sample-Dialog", Rect(90, 5, 120, 600), nullptr); + Widgets::label(panel, "Title", Rect(100, 10, 100, 20), Alignment::Center); + Widgets::textButton(panel, "Test 1", Rect(100, 50, 100, ButtonHeight), Alignment::Center, nullptr); + Widgets::textButton(panel, "Test 2", Rect(100, 100, 100, ButtonHeight), Alignment::Center, nullptr); + Widgets::textButton(panel, "Test 3", Rect(100, 150, 100, ButtonHeight), Alignment::Center, nullptr); + Widgets::imageButton(panel, "button_test.png", Rect(100, 200, 100, ButtonHeight), nullptr); auto &ctx = TinyUi::getContext(); @@ -100,16 +100,16 @@ int main(int argc, char *argv[]) { CallbackI *dynamicQuitCallback = new CallbackI(quit, (void*) &ctx); CallbackI *dynamicUpdateProgressBarCallback = new CallbackI(updateProgressbar, nullptr, Events::UpdateEvent); - Widgets::textButton(7, RootPanelId, "Quit", Rect(100, 250, 100, ButtonHeight), Alignment::Center, dynamicQuitCallback); - Widgets::progressBar(8, RootPanelId, Rect(100, 300, 100, ButtonHeight), 50, dynamicUpdateProgressBarCallback); + Widgets::textButton(panel, "Quit", Rect(100, 250, 100, ButtonHeight), Alignment::Center, dynamicQuitCallback); + Widgets::progressBar(panel, Rect(100, 300, 100, ButtonHeight), 50, dynamicUpdateProgressBarCallback); - Widgets::inputText(9, RootPanelId, Rect(100, 350, 100, ButtonHeight), Alignment::Left, KeyInputType::Character, ""); + Widgets::inputText(panel, Rect(100, 350, 100, ButtonHeight), Alignment::Left, KeyInputType::Character, ""); - Widgets::treeView(10, RootPanelId, "tree", Rect(100, 400, 100, ButtonHeight)); - Widgets::treeItem(11, 10, "Item 1"); + WidgetHandle tree = Widgets::treeView(panel, "tree", Rect(100, 400, 100, ButtonHeight)); + Widgets::treeItem(tree, "Item 1"); //Widgets::treeItem(12, 11, "Item 1.1"); - Widgets::treeItem(13, 10, "Item 2"); - Widgets::treeItem(14, 13, "Item 2.1"); + WidgetHandle view = Widgets::treeItem(tree, "Item 2"); + Widgets::treeItem(view, "Item 2.1"); while (TinyUi::run()) { TinyUi::render(); diff --git a/samples/hello_world/main.cpp b/samples/hello_world/main.cpp index 5657578..e5f2414 100644 --- a/samples/hello_world/main.cpp +++ b/samples/hello_world/main.cpp @@ -27,7 +27,7 @@ using namespace tinyui; static constexpr Id RootPanelId = 1; -int quit(Id, void *instance) { +int quit(WidgetHandle, void *instance) { if (instance == nullptr) { return ErrorCode; } @@ -50,12 +50,12 @@ int main(int argc, char *argv[]) { } constexpr int32_t ButtonHeight = 18; - Widgets::panel(RootPanelId, 0, "Sample-Dialog", Rect(90, 5, 220, 60), nullptr); + Widgets::panel(WidgetHandle::getRootHandle(), "Sample-Dialog", Rect(90, 5, 220, 60), nullptr); auto &ctx = TinyUi::getContext(); auto *dynamicQuitCallback = new CallbackI(quit, (void*) &ctx); - Widgets::label(2, RootPanelId, "Hi, World!", Rect(100, 10, 200, ButtonHeight), Alignment::Center); - Widgets::textButton(3, RootPanelId, "Quit", Rect(100, 30, 200, ButtonHeight), Alignment::Center, dynamicQuitCallback); + Widgets::label(WidgetHandle::getRootHandle(), "Hi, World!", Rect(100, 10, 200, ButtonHeight), Alignment::Center); + Widgets::textButton(WidgetHandle::getRootHandle(), "Quit", Rect(100, 30, 200, ButtonHeight), Alignment::Center, dynamicQuitCallback); while (TinyUi::run()) { TinyUi::render(); } diff --git a/src/tinyui.cpp b/src/tinyui.cpp index eb33308..6daf8b9 100644 --- a/src/tinyui.cpp +++ b/src/tinyui.cpp @@ -145,7 +145,8 @@ bool TinyUi::run() { const auto &ctx = getContext(); if (!ctx.mUpdateCallbackList.empty()) { for (auto it = ctx.mUpdateCallbackList.begin(); it != ctx.mUpdateCallbackList.end(); ++it) { - (*it)->mfuncCallback[Events::UpdateEvent](1, (*it)->mInstance); + WidgetHandle handle{1}; + (*it)->mfuncCallback[Events::UpdateEvent](handle, (*it)->mInstance); } } return Renderer::update(ctx); diff --git a/src/tinyui.h b/src/tinyui.h index 95534d7..9f73fbe 100644 --- a/src/tinyui.h +++ b/src/tinyui.h @@ -96,6 +96,27 @@ using Id = uint64_t; /// @brief The return code type used in the ui library. using ret_code = int32_t; +/// @brief The widget handle struct. +struct WidgetHandle { + static constexpr Id RootItem = 0; ///< The root item id. + static constexpr Id InvalidId = 999999; ///< The invalid id of the widget handle. + Id mId{InvalidId}; ///< The unique id of the widget. + + /// @brief Check if the widget handle is valid. + /// @return true if the widget handle is valid, false if not. + bool isValid() const { + return mId != InvalidId; + } + + /// @brief Will return the root item; + /// @return Thhe root item. + static WidgetHandle getRootHandle() { + static WidgetHandle root; + root.mId = RootItem; + return root; + } +}; + /// @brief The operation was cancelled. static constexpr ret_code OpCancelled = -5; /// @brief The context is invalid. @@ -350,7 +371,7 @@ struct EventPayload { /// @brief This interface is used to store all neede message handlers. struct CallbackI { /// The function callback - typedef int (*funcCallback) (Id id, void *data); + typedef int (*funcCallback) (WidgetHandle id, void *data); /// The function callback array, not handled callbacks are marked as a nullptr. funcCallback mfuncCallback[Events::NumEvents]; /// The data instance. diff --git a/src/widgets.cpp b/src/widgets.cpp index 178ea40..4e30031 100644 --- a/src/widgets.cpp +++ b/src/widgets.cpp @@ -108,9 +108,9 @@ static Widget *getValidRoot(Context &ctx) { return ctx.mRoot; } -static Widget *setParent(Context &ctx, Widget *child, Id parentId) { +static Widget *setParent(Context &ctx, Widget *child, WidgetHandle parentId) { Widget *parent{nullptr}; - if (parentId == 0) { + if (parentId.mId == 0) { parent = getValidRoot(ctx); } else { parent = Widgets::findWidget(parentId, ctx.mRoot); @@ -126,8 +126,8 @@ static Widget *setParent(Context &ctx, Widget *child, Id parentId) { return parent; } -static Widget *createWidget(Context &ctx, Id id, Id parentId, const Rect &rect, WidgetType type) { - Widget *widget = Widgets::findWidget(id, ctx.mRoot); +static Widget *createWidget(Context &ctx, WidgetHandle parentId, const Rect &rect, WidgetType type) { + /*Widget *widget = Widgets::findWidget(id, ctx.mRoot); if (widget != nullptr) { if (widget->mType == type) { return widget; @@ -135,15 +135,15 @@ static Widget *createWidget(Context &ctx, Id id, Id parentId, const Rect &rect, ctx.mLogger(LogSeverity::Error, "A widget with the same id but different type already exists."); return nullptr; - } + }*/ - widget = new Widget; + Widget *widget = new Widget; if (widget == nullptr) { ctx.mLogger(LogSeverity::Error, "TUI-Widget cannot be created."); return nullptr; } - widget->mId = id; + widget->mHandle = WidgetHandle{createHandle()}; widget->mType = type; widget->mRect = rect; widget->mParent = setParent(ctx, widget, parentId); @@ -185,27 +185,27 @@ void eventDispatcher(Context &ctx, int32_t eventId, EventPayload *eventPayload) } } -ret_code Widgets::container(Id id, Id parentId, const char *text, const Rect &rect) { +WidgetHandle Widgets::container(WidgetHandle parentId, const char *text, const Rect &rect) { auto &ctx = TinyUi::getContext(); if (ctx.mRoot != nullptr) { - return ErrorCode; + return WidgetHandle{WidgetHandle::InvalidId}; } - Widget *widget = createWidget(ctx, id, parentId, rect, WidgetType::Container); + Widget *widget = createWidget(ctx, parentId, rect, WidgetType::Container); ctx.mRoot = widget; if (text != nullptr) { widget->mText.assign(text); } - return ResultOk; + return widget->mHandle; } -Widget *Widgets::findWidget(Id id, Widget *root) { +Widget *Widgets::findWidget(WidgetHandle id, Widget *root) { if (root == nullptr) { return nullptr; } - if (root->mId == id) { + if (root->mHandle.mId == id.mId) { return root; } @@ -214,7 +214,7 @@ Widget *Widgets::findWidget(Id id, Widget *root) { if (child == nullptr) { continue; } - if (child->mId == id) { + if (child->mHandle.mId == id.mId) { return child; } Widget *foundWidget = findWidget(id, child); @@ -245,29 +245,29 @@ void Widgets::findSelectedWidget(int x, int y, Widget *currentChild, Widget **fo } } -ret_code Widgets::label(Id id, Id parentId, const char *text, const Rect &rect, Alignment alignment) { +WidgetHandle Widgets::label(WidgetHandle parentId, const char *text, const Rect &rect, Alignment alignment) { auto &ctx = TinyUi::getContext(); - if (ctx.mBackendCtx == nullptr) { - return InvalidRenderHandle; + if (ctx.mRoot != nullptr) { + return WidgetHandle{WidgetHandle::InvalidId}; } - if (ctx.mRoot == nullptr) { - return ErrorCode; + if (ctx.mRoot != nullptr) { + return WidgetHandle{WidgetHandle::InvalidId}; } - Widget *widget = createWidget(ctx, id, parentId, rect, WidgetType::Label); + Widget *widget = createWidget(ctx, parentId, rect, WidgetType::Label); if (widget == nullptr) { - return ErrorCode; + return WidgetHandle{WidgetHandle::InvalidId}; } widget->mAlignment = alignment; if (text != nullptr) { widget->mText.assign(text); } - return ResultOk; + return widget->mHandle; } -static int inputHandler(Id id, void *instance) { +static int inputHandler(WidgetHandle id, void *instance) { if (instance == nullptr) { return ErrorCode; } @@ -278,19 +278,19 @@ static int inputHandler(Id id, void *instance) { return ResultOk; } -ret_code Widgets::inputText(Id id, Id parentId, const Rect &rect, Alignment alignment, KeyInputType type, const char *defaultText) { +WidgetHandle Widgets::inputText(WidgetHandle parentId, const Rect &rect, Alignment alignment, KeyInputType type, const char *defaultText) { auto &ctx = TinyUi::getContext(); - if (ctx.mBackendCtx == nullptr) { - return InvalidRenderHandle; + if (ctx.mRoot != nullptr) { + return WidgetHandle{WidgetHandle::InvalidId}; } - if (ctx.mRoot == nullptr) { - return InvalidRenderHandle; + if (ctx.mRoot != nullptr) { + return WidgetHandle{WidgetHandle::InvalidId}; } - Widget *widget = createWidget(ctx, id, parentId, rect, WidgetType::InputField); + Widget *widget = createWidget(ctx, parentId, rect, WidgetType::InputField); if (widget == nullptr) { - return ErrorCode; + return WidgetHandle{WidgetHandle::InvalidId}; } widget->mAlignment = alignment; @@ -301,22 +301,22 @@ ret_code Widgets::inputText(Id id, Id parentId, const Rect &rect, Alignment alig widget->mCallback = new CallbackI(inputHandler, (void *)&ctx, Events::MouseButtonDownEvent); - return ResultOk; + return widget->mHandle; } -ret_code Widgets::textButton(Id id, Id parentId, const char *text, const Rect &rect, Alignment alignment, CallbackI *callback) { +WidgetHandle Widgets::textButton(WidgetHandle parentId, const char *text, const Rect &rect, Alignment alignment, CallbackI *callback) { auto &ctx = TinyUi::getContext(); if (ctx.mBackendCtx == nullptr) { - return InvalidRenderHandle; + return WidgetHandle{WidgetHandle::InvalidId}; } if (ctx.mRoot == nullptr) { - return InvalidRenderHandle; + return WidgetHandle{WidgetHandle::InvalidId}; } - Widget *child = createWidget(ctx, id, parentId, rect, WidgetType::Button); + Widget *child = createWidget(ctx, parentId, rect, WidgetType::Button); if (child == nullptr) { - return ErrorCode; + return WidgetHandle{WidgetHandle::InvalidId}; } child->mCallback = callback; @@ -328,22 +328,22 @@ ret_code Widgets::textButton(Id id, Id parentId, const char *text, const Rect &r child->mAlignment = alignment; } - return ResultOk; + return child->mHandle; } -ret_code Widgets::imageButton(Id id, Id parentId, const char *image, const Rect &rect, CallbackI *callback) { +WidgetHandle Widgets::imageButton(WidgetHandle parentId, const char *image, const Rect &rect, CallbackI *callback) { auto &ctx = TinyUi::getContext(); if (ctx.mBackendCtx == nullptr) { - return InvalidRenderHandle; + return WidgetHandle{WidgetHandle::InvalidId}; } if (ctx.mRoot == nullptr) { - return InvalidRenderHandle; + return WidgetHandle{WidgetHandle::InvalidId}; } - Widget *child = createWidget(ctx, id, parentId, rect, WidgetType::Button); + Widget *child = createWidget(ctx, parentId, rect, WidgetType::Button); if (child == nullptr) { - return ErrorCode; + return WidgetHandle{WidgetHandle::InvalidId}; } child->mCallback = callback; @@ -355,41 +355,41 @@ ret_code Widgets::imageButton(Id id, Id parentId, const char *image, const Rect child->mImage = loadIntoImageCache(ctx, image); } - return ResultOk; + return child->mHandle; } -ret_code Widgets::box(Id id, Id parentId, const Rect &rect, bool filled) { +WidgetHandle Widgets::box(WidgetHandle parentId, const Rect &rect, bool filled) { auto &ctx = TinyUi::getContext(); if (ctx.mBackendCtx == nullptr) { - return InvalidRenderHandle; + return WidgetHandle{WidgetHandle::InvalidId}; } if (ctx.mRoot == nullptr) { - return InvalidRenderHandle; + return WidgetHandle{WidgetHandle::InvalidId}; } - Widget *child = createWidget(ctx, id, parentId, rect, WidgetType::Box); + Widget *child = createWidget(ctx, parentId, rect, WidgetType::Box); if (child == nullptr) { - return ErrorCode; + return WidgetHandle{WidgetHandle::InvalidId}; } child->mFilledRect = filled; - return ResultOk; + return child->mHandle; } -ret_code Widgets::imageBox(Id id, Id parentId, const char* image, const Rect& rect, bool filled) { +WidgetHandle Widgets::imageBox(WidgetHandle parentId, const char* image, const Rect& rect, bool filled) { auto &ctx = TinyUi::getContext(); if (ctx.mBackendCtx == nullptr) { - return InvalidRenderHandle; + return WidgetHandle{WidgetHandle::InvalidId}; } if (ctx.mRoot == nullptr) { - return InvalidRenderHandle; + return WidgetHandle{WidgetHandle::InvalidId}; } - Widget *child = createWidget(ctx, id, parentId, rect, WidgetType::ImageBox); + Widget *child = createWidget(ctx, parentId, rect, WidgetType::ImageBox); if (child == nullptr) { - return ErrorCode; + return WidgetHandle{WidgetHandle::InvalidId}; } child->mFilledRect = filled; @@ -397,23 +397,24 @@ ret_code Widgets::imageBox(Id id, Id parentId, const char* image, const Rect& re child->mImage = loadIntoImageCache(ctx, image); } - return ResultOk; + return child->mHandle; } -ret_code Widgets::panel(Id id, Id parentId, const char *title, const Rect &rect, CallbackI *callback) { +WidgetHandle Widgets::panel(WidgetHandle parentId, const char *title, const Rect &rect, CallbackI *callback) { auto &ctx = TinyUi::getContext(); if (ctx.mBackendCtx == nullptr) { - return InvalidRenderHandle; + return WidgetHandle{WidgetHandle::InvalidId}; } - if (const Widget *child = createWidget(ctx, id, parentId, rect, WidgetType::Panel); child == nullptr) { - return ErrorCode; + const Widget *child = createWidget(ctx, parentId, rect, WidgetType::Panel); + if ( child == nullptr) { + return WidgetHandle{WidgetHandle::InvalidId}; } - return ResultOk; + return child->mHandle; } -static int onTreeViewItemClicked(Id id, void *data) { +static int onTreeViewItemClicked(WidgetHandle id, void *data) { Widget *treeView = Widgets::findWidget(id, TinyUi::getContext().mRoot); if (treeView == nullptr) { return ErrorCode; @@ -427,24 +428,24 @@ static int onTreeViewItemClicked(Id id, void *data) { child->mEnabled = !child->mEnabled; } - std::cout << "TreeView item clicked: " << id << std::endl; + std::cout << "TreeView item clicked: " << id.mId << std::endl; return 0; } -ret_code Widgets::treeView(Id id, Id parentId, const char *title, const Rect &rect) { +WidgetHandle Widgets::treeView(WidgetHandle parentId, const char *title, const Rect &rect) { auto &ctx = TinyUi::getContext(); if (ctx.mBackendCtx == nullptr) { - return InvalidRenderHandle; + return WidgetHandle{WidgetHandle::InvalidId}; } if (ctx.mRoot == nullptr) { - return InvalidRenderHandle; + return WidgetHandle{WidgetHandle::InvalidId}; } - Widget *widget = createWidget(ctx, id, parentId, rect, WidgetType::TreeView); + Widget *widget = createWidget(ctx, parentId, rect, WidgetType::TreeView); if (widget == nullptr) { - return ErrorCode; + return WidgetHandle{WidgetHandle::InvalidId}; } if (title != nullptr) { @@ -457,22 +458,22 @@ ret_code Widgets::treeView(Id id, Id parentId, const char *title, const Rect &re callback->incRef(); } - return ResultOk; + return widget->mHandle; } -ret_code Widgets::treeItem(Id id, Id parentItemId, const char *text) { +WidgetHandle Widgets::treeItem(WidgetHandle parentItemId, const char *text) { auto &ctx = TinyUi::getContext(); if (ctx.mBackendCtx == nullptr) { - return InvalidRenderHandle; + return WidgetHandle{WidgetHandle::InvalidId}; } if (ctx.mRoot == nullptr) { - return InvalidRenderHandle; + return WidgetHandle{WidgetHandle::InvalidId}; } const Widget *parentWidget = findWidget(parentItemId, ctx.mRoot); if (parentWidget == nullptr) { - return ErrorCode; + return WidgetHandle{WidgetHandle::InvalidId}; } const auto &parentRect = parentWidget->mRect; @@ -483,9 +484,9 @@ ret_code Widgets::treeItem(Id id, Id parentItemId, const char *text) { size_t numChildren = parentWidget->mChildren.size() + 1; const Rect rect(parentRect.top.x + margin, parentRect.top.y + static_cast(numChildren) * margin + static_cast(numChildren) * h, w, h); - Widget *child = createWidget(ctx, id, parentItemId, rect, WidgetType::Label); + Widget *child = createWidget(ctx, parentItemId, rect, WidgetType::Label); if (child == nullptr) { - return ErrorCode; + return WidgetHandle{WidgetHandle::InvalidId}; } child->mIntention = parentWidget->mIntention + 1; @@ -493,22 +494,22 @@ ret_code Widgets::treeItem(Id id, Id parentItemId, const char *text) { child->mText.assign(text); } - return ResultOk; + return child->mHandle; } -ret_code Widgets::progressBar(Id id, Id parentId, const Rect &rect, int fillRate, CallbackI *callback) { +WidgetHandle Widgets::progressBar(WidgetHandle parentId, const Rect &rect, int fillRate, CallbackI *callback) { auto &ctx = TinyUi::getContext(); if (ctx.mBackendCtx == nullptr) { - return InvalidRenderHandle; + return WidgetHandle{WidgetHandle::InvalidId}; } if (ctx.mRoot == nullptr) { - return InvalidRenderHandle; + return WidgetHandle{WidgetHandle::InvalidId}; } - Widget *child = createWidget(ctx, id, parentId, rect, WidgetType::ProgressBar); + Widget *child = createWidget(ctx, parentId, rect, WidgetType::ProgressBar); if (child == nullptr) { - return ErrorCode; + return WidgetHandle{WidgetHandle::InvalidId}; } clamp(0, 100, fillRate); @@ -524,7 +525,7 @@ ret_code Widgets::progressBar(Id id, Id parentId, const Rect &rect, int fillRate ctx.mUpdateCallbackList.push_back(callback); } - return ResultOk; + return child->mHandle; } static void render(Context &ctx, const Widget *currentWidget) { @@ -658,14 +659,14 @@ void Widgets::onMouseButton(int x, int y, int eventType, MouseState state) { if (found != nullptr) { #ifdef _DEBUG if (eventType == Events::MouseButtonDownEvent) { - std::cout << "Clicked " << found->mId << "\n"; + std::cout << "Clicked " << found->mHandle.mId << "\n"; } else { - std::cout << "Released " << found->mId << "\n"; + std::cout << "Released " << found->mHandle.mId << "\n"; } #endif // _DEBUG if (found->mCallback != nullptr) { if (found->mCallback->mfuncCallback[eventType] != nullptr) { - found->mCallback->mfuncCallback[eventType](found->mId, found->mCallback->mInstance); + found->mCallback->mfuncCallback[eventType](found->mHandle, found->mCallback->mInstance); } } } @@ -688,7 +689,7 @@ void Widgets::onMouseMove(int x, int y, int eventType, MouseState state) { if (found->mCallback != nullptr) { if (found->mCallback->mfuncCallback[eventType] != nullptr) { - found->mCallback->mfuncCallback[eventType](found->mId, found->mCallback->mInstance); + found->mCallback->mfuncCallback[eventType](found->mHandle, found->mCallback->mInstance); } } } @@ -740,7 +741,7 @@ void Widgets::clear() { releaseImageCache(ctx); } -bool Widgets::clearItem(Id id, bool recursive) { +bool Widgets::clearItem(WidgetHandle id, bool recursive) { auto &ctx = TinyUi::getContext(); Widget *widget = findWidget(id, ctx.mRoot); if (widget == nullptr) { @@ -766,7 +767,7 @@ bool Widgets::clearItem(Id id, bool recursive) { return result; } -void Widgets::setEnableState(Id id, bool enabled) { +void Widgets::setEnableState(WidgetHandle id, bool enabled) { auto &ctx = TinyUi::getContext(); Widget *widget = findWidget(id, ctx.mRoot); if (widget != nullptr) { @@ -778,7 +779,7 @@ void Widgets::setEnableState(Id id, bool enabled) { } } -bool Widgets::isEnabled(Id id) { +bool Widgets::isEnabled(WidgetHandle id) { auto &ctx = TinyUi::getContext(); const Widget *widget = findWidget(id, ctx.mRoot); if (widget != nullptr) { @@ -788,7 +789,7 @@ bool Widgets::isEnabled(Id id) { return false; } -ret_code Widgets::setFocus(Id id) { +ret_code Widgets::setFocus(WidgetHandle id) { auto &ctx = TinyUi::getContext(); Widget *widget = findWidget(id, ctx.mRoot); if (widget == nullptr) { @@ -800,7 +801,7 @@ ret_code Widgets::setFocus(Id id) { return ResultOk; } -Widget *Widgets::getWidgetById(Id id) { +Widget *Widgets::getWidgetById(WidgetHandle id) { auto &ctx = TinyUi::getContext(); return findWidget(id, ctx.mRoot); } diff --git a/src/widgets.h b/src/widgets.h index 36f5257..f02d50d 100644 --- a/src/widgets.h +++ b/src/widgets.h @@ -53,6 +53,7 @@ enum class LayoutPolicy { Count ///< The number of layouts }; +/// @brief This enum is used to describe the input type from the keyboard. enum class KeyInputType { Invalid = -1, ///< Not initialized Character, ///< A character input @@ -78,7 +79,7 @@ using WidgetArray = std::vector; /// @brief This struct contains all the data which is needed to describe a widget. struct Widget { - Id mId{0}; ///< The unique id of the widget + WidgetHandle mHandle{}; ///< The unique id of the widget WidgetType mType{WidgetType::Invalid}; ///< The type of the widget Widget *mParent{nullptr}; ///< The parent widget bool mEnabled{true}; ///< The enabled state of the widget @@ -141,25 +142,10 @@ struct Widget { Widget &operator=(const Widget &) = delete; }; -/// @brief The widget handle struct. -struct WidgetHandle { - static constexpr Id InvalidId = 999999; ///< The invalid id of the widget handle. - Id mId{InvalidId}; ///< The unique id of the widget. - - /// @brief Check if the widget handle is valid. - /// @return true if the widget handle is valid, false if not. - bool isValid() const { - return mId != InvalidId; - } -}; - /// @brief The widgets access interface. /// /// This class is used to create and manage widgets. struct Widgets { - /// @brief The root item id. - static constexpr Id RootItem = 0; - /// @brief The default class constructor. Widgets() = default; @@ -173,7 +159,7 @@ struct Widgets { /// @param text The text of the widget. /// @param rect The rect of the widget. /// @return ResultOk if the widget was created, ErrorCode if not. - static ret_code container(Id id, Id parentId, const char *text, const Rect &rect); + static WidgetHandle container(WidgetHandle parentId, const char *text, const Rect &rect); /// @brief Create a new widget from the type box. /// @param ctx The context to create the widget in. @@ -182,7 +168,7 @@ struct Widgets { /// @param rect The rect of the widget. /// @param filled The filled state of the widget. /// @return ResultOk if the widget was created, ErrorCode if not. - static ret_code box(Id id, Id parentId, const Rect &rect, bool filled); + static WidgetHandle box(WidgetHandle parentId, const Rect &rect, bool filled); /// @brief Create a new widget from the type image box. /// @param id The unique id of the widget. @@ -191,13 +177,13 @@ struct Widgets { /// @param rect The rect of the widget. /// @param filled The filled state of the widget. /// @return ResultOk if the widget was created, ErrorCode if not. - static ret_code imageBox(Id id, Id parentId, const char *image, const Rect &rect, bool filled); + static WidgetHandle imageBox(WidgetHandle parentId, const char *image, const Rect &rect, bool filled); /// @brief Will look for a widget by its id. /// @param id The id of the widget to look for. /// @param root The root widget to start the search. /// @return The found widget or nullptr if not found. - static Widget *findWidget(Id id, Widget *root); + static Widget *findWidget(WidgetHandle id, Widget *root); /// @brief Will look for a widget by its id. /// @param x The x-coordinate of the point. @@ -214,7 +200,7 @@ struct Widgets { /// @param rect The rect of the widget. /// @param alignment The alignment of the widget. /// @return ResultOk if the widget was created, ErrorCode if not. - static ret_code label(Id id, Id parentId, const char *text, const Rect &rect, Alignment alignment); + static WidgetHandle label(WidgetHandle parentId, const char *text, const Rect &rect, Alignment alignment); /// @brief Creates a new widget from the type textfield. /// @param ctx The context to create the widget in. @@ -225,7 +211,7 @@ struct Widgets { /// @param type The type of the key input. /// @param defaultText The default text of the widget. /// @return ResultOk if the widget was created, ErrorCode if not. - static ret_code inputText(Id id, Id parentId, const Rect &rect, Alignment alignment, KeyInputType type, const char *defaultText); + static WidgetHandle inputText(WidgetHandle parentId, const Rect &rect, Alignment alignment, KeyInputType type, const char *defaultText); /// @brief Creates a new text button. /// @param ctx The context to create the widget in. @@ -236,7 +222,7 @@ struct Widgets { /// @param alignment The alignment of the widget. /// @param callback The callback for the widget. /// @return ResultOk if the widget was created, ErrorCode if not. - static ret_code textButton(Id id, Id parentId, const char *text, const Rect &rect, Alignment alignment, CallbackI *callback); + static WidgetHandle textButton(WidgetHandle parentId, const char *text, const Rect &rect, Alignment alignment, CallbackI *callback); /// @brief Creates a new image button. /// @param ctx The context to create the widget in. @@ -246,7 +232,7 @@ struct Widgets { /// @param rect The rect of the widget. /// @param callback The callback for the widget. /// @return ResultOk if the widget was created, ErrorCode if not. - static ret_code imageButton(Id id, Id parentId, const char *image, const Rect &rect, CallbackI *callback); + static WidgetHandle imageButton(WidgetHandle parentId, const char *image, const Rect &rect, CallbackI *callback); /// @brief Creates a new widget from the type panel. /// @param ctx The context to create the widget in. @@ -256,7 +242,7 @@ struct Widgets { /// @param rect The rect of the widget. /// @param callback The callback of the widget. /// @return ResultOk if the widget was created, ErrorCode if not. - static ret_code panel(Id id, Id parentId, const char *title, const Rect &rect, CallbackI *callback); + static WidgetHandle panel(WidgetHandle parentId, const char *title, const Rect &rect, CallbackI *callback); /// @brief Creates a new widget from the type treeview. /// @param ctx The context to create the widget in. @@ -266,13 +252,13 @@ struct Widgets { /// @param rect The rect of the widget. /// @param callback The callback of the widget. /// @return ResultOk if the widget was created, ErrorCode if not. - static ret_code treeView(Id id, Id parentId, const char *title, const Rect &rect); + static WidgetHandle treeView(WidgetHandle parentId, const char *title, const Rect &rect); /// @brief Creates a new tree item. /// @param id The unique id of the tree item. /// @param parentItemId The parent item id of the tree item. /// @param title The title of the tree item. - static ret_code treeItem(Id id, Id parentItemId, const char *title); + static WidgetHandle treeItem(WidgetHandle parentItemId, const char *title); /// @brief Creates a new widget from the type status bar. /// @param ctx The context to create the widget in. @@ -283,7 +269,7 @@ struct Widgets { /// @param fillRate The fill rate of the widget. /// @param callback The callback of the widget. /// @return ResultOk if the widget was created, ErrorCode if not. - static ret_code progressBar(Id id, Id parentId, const Rect &rect, int fillRate, CallbackI *callback); + static WidgetHandle progressBar(WidgetHandle parentId, const Rect &rect, int fillRate, CallbackI *callback); /// @brief Will render all widgets. /// @param ctx The context to render the widgets in. @@ -318,31 +304,31 @@ struct Widgets { /// @param id The id of the widget to clear. /// @param recursive If true, all child widgets will also be cleared. /// @return true if the widget was cleared, false if not. - static bool clearItem(Id id, bool recursive); + static bool clearItem(WidgetHandle id, bool recursive); /// @brief The widget enabler /// @param ctx The context to enable the widget in. /// @param id The id of the widget to enable. /// @param enabled The enabled state of the widget. - static void setEnableState(Id id, bool enabled); + static void setEnableState(WidgetHandle id, bool enabled); /// @brief Will return true if the widget is enabled. /// @param ctx The context to check the widget in. /// @param id The id of the widget to check. /// @return true if the widget is enabled, false if not. - static bool isEnabled(Id id); + static bool isEnabled(WidgetHandle id); /// @brief Will set the focus to the widget by its id. /// @param ctx The context to set the focus in. /// @param id The id of the widget to set the focus to. /// @return ResultOk if the focus was set, ErrorCode if not. - static ret_code setFocus(Id id); + static ret_code setFocus(WidgetHandle id); /// @brief Will return the widget by its id. /// @param ctx The context to get the widget from. /// @param id The id of the widget to get. /// @return The widget or nullptr if not found. - static Widget *getWidgetById(Id id); + static Widget *getWidgetById(WidgetHandle id); /// @brief Will open a child window. /// @return true if successful. From f724275900fdb4144643d2d2e7d70993524b9cbc Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 28 Apr 2026 23:56:02 +0200 Subject: [PATCH 2/7] Fix label creation --- samples/hello_world/main.cpp | 6 +++--- src/widgets.cpp | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/samples/hello_world/main.cpp b/samples/hello_world/main.cpp index e5f2414..ef9f553 100644 --- a/samples/hello_world/main.cpp +++ b/samples/hello_world/main.cpp @@ -50,12 +50,12 @@ int main(int argc, char *argv[]) { } constexpr int32_t ButtonHeight = 18; - Widgets::panel(WidgetHandle::getRootHandle(), "Sample-Dialog", Rect(90, 5, 220, 60), nullptr); + WidgetHandle panel = Widgets::panel(WidgetHandle::getRootHandle(), "Sample-Dialog", Rect(90, 5, 220, 60), nullptr); auto &ctx = TinyUi::getContext(); auto *dynamicQuitCallback = new CallbackI(quit, (void*) &ctx); - Widgets::label(WidgetHandle::getRootHandle(), "Hi, World!", Rect(100, 10, 200, ButtonHeight), Alignment::Center); - Widgets::textButton(WidgetHandle::getRootHandle(), "Quit", Rect(100, 30, 200, ButtonHeight), Alignment::Center, dynamicQuitCallback); + Widgets::label(panel, "Hi, World!", Rect(100, 10, 200, ButtonHeight), Alignment::Center); + Widgets::textButton(panel, "Quit", Rect(100, 30, 200, ButtonHeight), Alignment::Center, dynamicQuitCallback); while (TinyUi::run()) { TinyUi::render(); } diff --git a/src/widgets.cpp b/src/widgets.cpp index 4e30031..9eeacea 100644 --- a/src/widgets.cpp +++ b/src/widgets.cpp @@ -247,11 +247,11 @@ void Widgets::findSelectedWidget(int x, int y, Widget *currentChild, Widget **fo WidgetHandle Widgets::label(WidgetHandle parentId, const char *text, const Rect &rect, Alignment alignment) { auto &ctx = TinyUi::getContext(); - if (ctx.mRoot != nullptr) { + if (ctx.mBackendCtx == nullptr) { return WidgetHandle{WidgetHandle::InvalidId}; } - if (ctx.mRoot != nullptr) { + if (ctx.mRoot == nullptr) { return WidgetHandle{WidgetHandle::InvalidId}; } @@ -280,11 +280,11 @@ static int inputHandler(WidgetHandle id, void *instance) { WidgetHandle Widgets::inputText(WidgetHandle parentId, const Rect &rect, Alignment alignment, KeyInputType type, const char *defaultText) { auto &ctx = TinyUi::getContext(); - if (ctx.mRoot != nullptr) { + if (ctx.mBackendCtx == nullptr) { return WidgetHandle{WidgetHandle::InvalidId}; } - if (ctx.mRoot != nullptr) { + if (ctx.mRoot == nullptr) { return WidgetHandle{WidgetHandle::InvalidId}; } From 64f10ab7cdcc54d46a0eea9b3b46122ca57bb865 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 28 Apr 2026 23:57:47 +0200 Subject: [PATCH 3/7] Remove unused ids --- samples/demo/main.cpp | 4 ---- samples/hello_world/main.cpp | 2 -- 2 files changed, 6 deletions(-) diff --git a/samples/demo/main.cpp b/samples/demo/main.cpp index 16dec67..bd47458 100644 --- a/samples/demo/main.cpp +++ b/samples/demo/main.cpp @@ -27,10 +27,6 @@ SOFTWARE. using namespace tinyui; -static constexpr Id RootPanelId = 1; - -static constexpr Id NextPanelId = 100; - int quit(WidgetHandle, void *instance) { if (instance == nullptr) { return ErrorCode; diff --git a/samples/hello_world/main.cpp b/samples/hello_world/main.cpp index ef9f553..c459d0a 100644 --- a/samples/hello_world/main.cpp +++ b/samples/hello_world/main.cpp @@ -25,8 +25,6 @@ SOFTWARE. using namespace tinyui; -static constexpr Id RootPanelId = 1; - int quit(WidgetHandle, void *instance) { if (instance == nullptr) { return ErrorCode; From 9e1fb44229871a4c80b5c01eec46fcfe5f05b47c Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 29 Apr 2026 00:00:47 +0200 Subject: [PATCH 4/7] Remove unused ids --- src/widgets.cpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/widgets.cpp b/src/widgets.cpp index 9eeacea..f1e0ce9 100644 --- a/src/widgets.cpp +++ b/src/widgets.cpp @@ -127,16 +127,6 @@ static Widget *setParent(Context &ctx, Widget *child, WidgetHandle parentId) { } static Widget *createWidget(Context &ctx, WidgetHandle parentId, const Rect &rect, WidgetType type) { - /*Widget *widget = Widgets::findWidget(id, ctx.mRoot); - if (widget != nullptr) { - if (widget->mType == type) { - return widget; - } - - ctx.mLogger(LogSeverity::Error, "A widget with the same id but different type already exists."); - return nullptr; - }*/ - Widget *widget = new Widget; if (widget == nullptr) { ctx.mLogger(LogSeverity::Error, "TUI-Widget cannot be created."); From 3005b4e7ad8076a0a00b5e9b214c0d0c3878acbd Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 29 Apr 2026 00:07:27 +0200 Subject: [PATCH 5/7] Fix sonarqube findings --- src/widgets.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/widgets.cpp b/src/widgets.cpp index f1e0ce9..f33920f 100644 --- a/src/widgets.cpp +++ b/src/widgets.cpp @@ -127,12 +127,7 @@ static Widget *setParent(Context &ctx, Widget *child, WidgetHandle parentId) { } static Widget *createWidget(Context &ctx, WidgetHandle parentId, const Rect &rect, WidgetType type) { - Widget *widget = new Widget; - if (widget == nullptr) { - ctx.mLogger(LogSeverity::Error, "TUI-Widget cannot be created."); - return nullptr; - } - + auto *widget = new Widget; widget->mHandle = WidgetHandle{createHandle()}; widget->mType = type; widget->mRect = rect; From 77029f2523792b77bd6d396facfee2d735febcf0 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 29 Apr 2026 08:44:15 +0200 Subject: [PATCH 6/7] Fix possible leak in widget creation --- contrib/vcpkg | 2 +- src/widgets.cpp | 42 ++++-------------------------------------- 2 files changed, 5 insertions(+), 39 deletions(-) diff --git a/contrib/vcpkg b/contrib/vcpkg index f1fe3ac..594ad88 160000 --- a/contrib/vcpkg +++ b/contrib/vcpkg @@ -1 +1 @@ -Subproject commit f1fe3acb62b7aba476e48e3395c40d88478ac444 +Subproject commit 594ad8871e1e8e45f8e626c015fd611163430207 diff --git a/src/widgets.cpp b/src/widgets.cpp index f33920f..d00a85b 100644 --- a/src/widgets.cpp +++ b/src/widgets.cpp @@ -241,9 +241,6 @@ WidgetHandle Widgets::label(WidgetHandle parentId, const char *text, const Rect } Widget *widget = createWidget(ctx, parentId, rect, WidgetType::Label); - if (widget == nullptr) { - return WidgetHandle{WidgetHandle::InvalidId}; - } widget->mAlignment = alignment; if (text != nullptr) { widget->mText.assign(text); @@ -274,10 +271,6 @@ WidgetHandle Widgets::inputText(WidgetHandle parentId, const Rect &rect, Alignme } Widget *widget = createWidget(ctx, parentId, rect, WidgetType::InputField); - if (widget == nullptr) { - return WidgetHandle{WidgetHandle::InvalidId}; - } - widget->mAlignment = alignment; widget->mKeyInputType = type; if (defaultText != nullptr) { @@ -300,10 +293,6 @@ WidgetHandle Widgets::textButton(WidgetHandle parentId, const char *text, const } Widget *child = createWidget(ctx, parentId, rect, WidgetType::Button); - if (child == nullptr) { - return WidgetHandle{WidgetHandle::InvalidId}; - } - child->mCallback = callback; if (callback != nullptr) { callback->incRef(); @@ -326,11 +315,7 @@ WidgetHandle Widgets::imageButton(WidgetHandle parentId, const char *image, cons return WidgetHandle{WidgetHandle::InvalidId}; } - Widget *child = createWidget(ctx, parentId, rect, WidgetType::Button); - if (child == nullptr) { - return WidgetHandle{WidgetHandle::InvalidId}; - } - + Widget *child = createWidget(ctx, parentId, rect, WidgetType::Button); child->mCallback = callback; if (callback != nullptr) { callback->incRef(); @@ -354,10 +339,6 @@ WidgetHandle Widgets::box(WidgetHandle parentId, const Rect &rect, bool filled) } Widget *child = createWidget(ctx, parentId, rect, WidgetType::Box); - if (child == nullptr) { - return WidgetHandle{WidgetHandle::InvalidId}; - } - child->mFilledRect = filled; return child->mHandle; @@ -373,10 +354,6 @@ WidgetHandle Widgets::imageBox(WidgetHandle parentId, const char* image, const R return WidgetHandle{WidgetHandle::InvalidId}; } Widget *child = createWidget(ctx, parentId, rect, WidgetType::ImageBox); - if (child == nullptr) { - return WidgetHandle{WidgetHandle::InvalidId}; - } - child->mFilledRect = filled; if (image != nullptr) { child->mImage = loadIntoImageCache(ctx, image); @@ -392,9 +369,6 @@ WidgetHandle Widgets::panel(WidgetHandle parentId, const char *title, const Rect } const Widget *child = createWidget(ctx, parentId, rect, WidgetType::Panel); - if ( child == nullptr) { - return WidgetHandle{WidgetHandle::InvalidId}; - } return child->mHandle; } @@ -413,8 +387,9 @@ static int onTreeViewItemClicked(WidgetHandle id, void *data) { child->mEnabled = !child->mEnabled; } +#ifdef _DEBUG std::cout << "TreeView item clicked: " << id.mId << std::endl; - +#endif // _DEBUG return 0; } @@ -429,10 +404,6 @@ WidgetHandle Widgets::treeView(WidgetHandle parentId, const char *title, const R } Widget *widget = createWidget(ctx, parentId, rect, WidgetType::TreeView); - if (widget == nullptr) { - return WidgetHandle{WidgetHandle::InvalidId}; - } - if (title != nullptr) { widget->mText.assign(title); } @@ -461,8 +432,7 @@ WidgetHandle Widgets::treeItem(WidgetHandle parentItemId, const char *text) { return WidgetHandle{WidgetHandle::InvalidId}; } - const auto &parentRect = parentWidget->mRect; - + const auto &parentRect = parentWidget->mRect; const int32_t margin = ctx.mStyle.mMargin; const int32_t w = parentRect.width; const int32_t h = parentRect.height; @@ -493,10 +463,6 @@ WidgetHandle Widgets::progressBar(WidgetHandle parentId, const Rect &rect, int f } Widget *child = createWidget(ctx, parentId, rect, WidgetType::ProgressBar); - if (child == nullptr) { - return WidgetHandle{WidgetHandle::InvalidId}; - } - clamp(0, 100, fillRate); FilledState state; state.filledState = fillRate; From cdc75992029678f9a3a9ca3a84d5e041a729bf99 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 29 Apr 2026 09:39:37 +0200 Subject: [PATCH 7/7] Add widget-handle validation to demos. --- samples/demo/main.cpp | 17 ++++++++++++++++- samples/hello_world/main.cpp | 5 +++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/samples/demo/main.cpp b/samples/demo/main.cpp index bd47458..7964120 100644 --- a/samples/demo/main.cpp +++ b/samples/demo/main.cpp @@ -84,6 +84,12 @@ int main(int argc, char *argv[]) { constexpr int32_t ButtonHeight = 25; WidgetHandle panel = Widgets::panel(WidgetHandle::getRootHandle(), "Sample-Dialog", Rect(90, 5, 120, 600), nullptr); + if (!panel.isValid()) { + const auto &ctx = TinyUi::getContext(); + ctx.mLogger(LogSeverity::Error, "Cannot create panel"); + return ErrorCode; + } + Widgets::label(panel, "Title", Rect(100, 10, 100, 20), Alignment::Center); Widgets::textButton(panel, "Test 1", Rect(100, 50, 100, ButtonHeight), Alignment::Center, nullptr); Widgets::textButton(panel, "Test 2", Rect(100, 100, 100, ButtonHeight), Alignment::Center, nullptr); @@ -102,9 +108,18 @@ int main(int argc, char *argv[]) { Widgets::inputText(panel, Rect(100, 350, 100, ButtonHeight), Alignment::Left, KeyInputType::Character, ""); WidgetHandle tree = Widgets::treeView(panel, "tree", Rect(100, 400, 100, ButtonHeight)); + if (!tree.isValid()) { + ctx.mLogger(LogSeverity::Error, "Cannot create tree view"); + return ErrorCode; + } + Widgets::treeItem(tree, "Item 1"); - //Widgets::treeItem(12, 11, "Item 1.1"); + WidgetHandle view = Widgets::treeItem(tree, "Item 2"); + if (!view.isValid()) { + ctx.mLogger(LogSeverity::Error, "Cannot create tree view item"); + return ErrorCode; + } Widgets::treeItem(view, "Item 2.1"); while (TinyUi::run()) { diff --git a/samples/hello_world/main.cpp b/samples/hello_world/main.cpp index c459d0a..338a3f0 100644 --- a/samples/hello_world/main.cpp +++ b/samples/hello_world/main.cpp @@ -49,6 +49,11 @@ int main(int argc, char *argv[]) { constexpr int32_t ButtonHeight = 18; WidgetHandle panel = Widgets::panel(WidgetHandle::getRootHandle(), "Sample-Dialog", Rect(90, 5, 220, 60), nullptr); + if (!panel.isValid()) { + const auto &ctx = TinyUi::getContext(); + ctx.mLogger(LogSeverity::Error, "Cannot create panel"); + return ErrorCode; + } auto &ctx = TinyUi::getContext(); auto *dynamicQuitCallback = new CallbackI(quit, (void*) &ctx);