diff --git a/samples/demo/main.cpp b/samples/demo/main.cpp index 48a44d4..6ae09de 100644 --- a/samples/demo/main.cpp +++ b/samples/demo/main.cpp @@ -105,7 +105,7 @@ int main(int argc, char *argv[]) { Widgets::button(7, RootPanelId, "Quit", Rect(100, 250, 100, ButtonHeight), dynamicQuitCallback); Widgets::progressBar(8, RootPanelId, Rect(100, 300, 100, ButtonHeight), 50, dynamicUpdateProgressBarCallback); - Widgets::inputText(9, RootPanelId, Rect(100, 350, 100, ButtonHeight), Alignment::Left); + Widgets::inputText(9, RootPanelId, 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"); diff --git a/src/backends/sdl2_iodevice.cpp b/src/backends/sdl2_iodevice.cpp index 7a04144..8637a14 100644 --- a/src/backends/sdl2_iodevice.cpp +++ b/src/backends/sdl2_iodevice.cpp @@ -34,4 +34,8 @@ uint32_t IODevice::getTicks() { return SDL_GetTicks(); } +void IODevice::delay(uint32_t ms) { + SDL_Delay(ms); +} + } // namespace tinyui diff --git a/src/backends/sdl2_iodevice.h b/src/backends/sdl2_iodevice.h index 6f6ca73..a2af0a8 100644 --- a/src/backends/sdl2_iodevice.h +++ b/src/backends/sdl2_iodevice.h @@ -42,6 +42,10 @@ struct IODevice { /// @brief Get the current ticks from the io-device. /// @return The current ticks. static uint32_t getTicks(); + + /// @brief Delay the execution for a given amount of milliseconds. + /// @param ms The amount of milliseconds to delay. + static void delay(uint32_t ms); }; } // namespace tinyui diff --git a/src/backends/sdl2_renderer.cpp b/src/backends/sdl2_renderer.cpp index 94890bc..83a8763 100644 --- a/src/backends/sdl2_renderer.cpp +++ b/src/backends/sdl2_renderer.cpp @@ -205,12 +205,14 @@ ret_code Renderer::drawText(Context &ctx, const char *string, Font *font, const if (ctx.mStyle.mFont.mName != nullptr) { loadFont(ctx); if (font == nullptr) { + const std::string msg = "Cannot load font: " + std::string(ctx.mStyle.mFont.mName) + ", using the default font."; + ctx.mLogger(LogSeverity::Error, msg.c_str()); font = ctx.mDefaultFont; } } } + font = ctx.mDefaultFont; - if (font == nullptr) { return InvalidHandle; } @@ -446,7 +448,7 @@ ret_code Renderer::createRenderTexture(Context &ctx, int w, int h, SDL_Texture * } bool Renderer::update(Context &ctx) { - if (!ctx.mCreated) { + if (!ctx.mCreated) { return false; } diff --git a/src/widgets.cpp b/src/widgets.cpp index ec2647e..7f6e2b5 100644 --- a/src/widgets.cpp +++ b/src/widgets.cpp @@ -145,16 +145,35 @@ static Widget *createWidget(Context &ctx, Id id, Id parentId, const Rect &rect, return widget; } +static void deleteKeyFromText(Context &ctx) { + ctx.mFocus->mText.erase(ctx.mFocus->mText.size() - 1); +} + +static void appendKeyToText(Context &ctx, char *buffer) { + ctx.mFocus->mText.append(buffer); +} + +static void handleInputField(Context &ctx, EventPayload *eventPayload) { + char buffer[2] = { + static_cast(eventPayload->payload[0]), + '\0' + }; + if (buffer[0] == SDLK_BACKSPACE) { + deleteKeyFromText(ctx); + } else { + appendKeyToText(ctx, buffer); + } +} + void eventDispatcher(Context &ctx, int32_t eventId, EventPayload *eventPayload) { if (ctx.mFocus == nullptr) { return; } - if (eventId == Events::KeyDownEvent || eventId == Events::KeyUpEvent) { + if (eventId == Events::KeyDownEvent) { if (eventPayload != nullptr) { if (ctx.mFocus->mType == WidgetType::InputField) { - char buffer[2] = { eventPayload->payload[0], '\0' }; - ctx.mFocus->mText.append(buffer); + handleInputField(ctx, eventPayload); } } } @@ -242,7 +261,18 @@ ret_code Widgets::label(Id id, Id parentId, const char *text, const Rect &rect, return ResultOk; } -ret_code Widgets::inputText(Id id, Id parentId, const Rect &rect, Alignment alignment) { +static int inputHandler(Id id, void *instance) { + if (instance == nullptr) { + return ErrorCode; + } + + Context *ctx = static_cast(instance); + ctx->mFocus = Widgets::findWidget(id, ctx->mRoot); + + return ResultOk; +} + +ret_code Widgets::inputText(Id id, Id parentId, const Rect &rect, Alignment alignment, KeyInputType type, const char *defaultText) { auto &ctx = TinyUi::getContext(); if (ctx.mBackendCtx == nullptr) { return InvalidRenderHandle; @@ -258,6 +288,12 @@ ret_code Widgets::inputText(Id id, Id parentId, const Rect &rect, Alignment alig } widget->mAlignment = alignment; + widget->mKeyInputType = type; + if (defaultText != nullptr) { + widget->mText.assign(defaultText); + } + + widget->mCallback = new CallbackI(inputHandler, (void *)&ctx, Events::MouseButtonDownEvent); return ResultOk; } @@ -402,7 +438,6 @@ ret_code Widgets::treeView(Id id, Id parentId, const char *title, const Rect &re return ErrorCode; } - if (title != nullptr) { widget->mText.assign(title); } @@ -437,7 +472,7 @@ ret_code Widgets::treeItem(Id id, Id parentItemId, const char *text) { const int32_t w = parentRect.width; const int32_t h = parentRect.height; size_t numChildren = parentWidget->mChildren.size() + 1; - const Rect rect(parentRect.top.x + margin, parentRect.top.y + numChildren * margin + + 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); if (child == nullptr) { @@ -558,11 +593,17 @@ static void render(Context &ctx, const Widget *currentWidget) { break; case WidgetType::InputField: - { - Renderer::drawRect(ctx, r.top.x, r.top.y, r.width, r.height, true, ctx.mStyle.mFg); - Renderer::drawRect(ctx, r.top.x+2, r.top.y+2, r.width-4, r.height-4, true, ctx.mStyle.mBorder); - } - break; + { + Renderer::drawRect(ctx, r.top.x, r.top.y, r.width, r.height, true, ctx.mStyle.mFg); + Renderer::drawRect(ctx, r.top.x+2, r.top.y+2, r.width-4, r.height-4, true, ctx.mStyle.mBorder); + if (!currentWidget->mText.empty()) { + const Color4 fg = ctx.mStyle.mTextColor; + const Color4 bg = ctx.mStyle.mBg; + Renderer::drawText(ctx, currentWidget->mText.c_str(), ctx.mDefaultFont, + currentWidget->mRect, fg, bg, currentWidget->mAlignment); + } + } + break; case WidgetType::Container: case WidgetType::Box: diff --git a/src/widgets.h b/src/widgets.h index 88eca0c..4e06b82 100644 --- a/src/widgets.h +++ b/src/widgets.h @@ -53,6 +53,14 @@ enum class LayoutPolicy { Count ///< The number of layouts }; +enum class KeyInputType { + Invalid = -1, ///< Not initialized + Character, ///< A character input + Password, ///< A special key input + Numeric, ///< A numeric input + Count ///< The number of key input types +}; + /// @brief This struct is used to describe the filled state as a payload. struct FilledState { uint32_t filledState{0}; ///< The filled state in percent (0-100) @@ -70,20 +78,21 @@ 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 - WidgetType mType{WidgetType::Invalid}; ///< The type of the widget - Widget *mParent{nullptr}; ///< The parent widget - bool mEnabled{true}; ///< The enabled state of the widget - Rect mRect{}; ///< The rectangle of the widget - bool mFilledRect{true}; ///< The filled rectangle state - uint32_t mStyles{0u}; ///< The style of the widget - Alignment mAlignment{Alignment::Left}; ///< The alignment of the widget - std::string mText{}; ///< The label text - Image *mImage{nullptr}; ///< The image of the widget - WidgetArray mChildren{}; ///< The children of the widget - CallbackI *mCallback{nullptr}; ///< The callback of the widget - uint8_t *mContent{nullptr}; ///< The content of the widget - uint32_t mIntention{0}; ///< The interaction intention. + Id mId{0}; ///< 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 + Rect mRect{}; ///< The rectangle of the widget + bool mFilledRect{true}; ///< The filled rectangle state + uint32_t mStyles{0u}; ///< The style of the widget + Alignment mAlignment{Alignment::Left}; ///< The alignment of the widget + KeyInputType mKeyInputType{ KeyInputType::Invalid }; ///< The type of the key input + std::string mText{}; ///< The label text + Image *mImage{nullptr}; ///< The image of the widget + WidgetArray mChildren{}; ///< The children of the widget + CallbackI *mCallback{nullptr}; ///< The callback of the widget + uint8_t *mContent{nullptr}; ///< The content of the widget + uint32_t mIntention{0}; ///< The interaction intention. /// @brief The default class constructor. Widget() = default; @@ -161,6 +170,14 @@ struct Widgets { /// @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); + + /// @brief Create a new widget from the type image box. + /// @param id The unique id of the widget. + /// @param parentId The parent id of the widget. + /// @param image The image of the widget. + /// @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); /// @brief Will look for a widget by its id. @@ -187,13 +204,15 @@ struct Widgets { static ret_code label(Id id, Id 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. - /// @param id The unique id of the widget. - /// @param parentId The parent id of the widget. - /// @param rect The rect of the widget. - /// @param alignment The alignment of the widget. + /// @param ctx The context to create the widget in. + /// @param id The unique id of the widget. + /// @param parentId The parent id of the widget. + /// @param rect The rect of the widget. + /// @param alignment The alignment of the widget. + /// @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); + static ret_code inputText(Id id, Id 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.