diff --git a/samples/main.cpp b/samples/main.cpp index 09edaf3..c175e82 100644 --- a/samples/main.cpp +++ b/samples/main.cpp @@ -1,7 +1,7 @@ /* MIT License -Copyright (c) 2022-2025 Kim Kulling +Copyright (c) 2022-2024 Kim Kulling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -21,12 +21,13 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include #include "widgets.h" using namespace tinyui; -static constexpr Id RootPanelId = 1; /// The id for the root panel +static constexpr Id RootPanelId = 1; + +static constexpr Id NextPanelId = 100; int quit(uint32_t, void *instance) { if (instance == nullptr) { @@ -61,7 +62,7 @@ int main(int argc, char *argv[]) { return ErrorCode; } - Widgets::panel(ctx, RootPanelId, 0, "Sample-Dialog", Rect(90, 5, 120, 400), nullptr); + Widgets::panel(ctx, RootPanelId, 0, "Sample-Dialog", Rect(90, 5, 120, 500), nullptr); Widgets::label(ctx, 2, RootPanelId, "Title", Rect(100, 10, 100, 20), Alignment::Center); Widgets::button(ctx, 3, RootPanelId, "Test 1", nullptr, Rect(100, 50, 100, 40), nullptr); Widgets::button(ctx, 4, RootPanelId, "Test 2", nullptr, Rect(100, 100, 100, 40), nullptr); @@ -74,6 +75,8 @@ int main(int argc, char *argv[]) { CallbackI updateProgressBarCallback(updateProgressbar, nullptr, Events::UpdateEvent); Widgets::progressBar(ctx, 8, RootPanelId, Rect(100, 300, 100, 40), 50, &updateProgressBarCallback); + Widgets::textfield(ctx, 9, RootPanelId, Rect(100, 350, 100, 40), Alignment::Left); + while (TinyUi::run(ctx)) { TinyUi::beginRender(ctx, style.mClearColor); Widgets::renderWidgets(ctx); diff --git a/src/backends/sdl2_iodevice.cpp b/src/backends/sdl2_iodevice.cpp index 9a4d0cc..0c13c11 100644 --- a/src/backends/sdl2_iodevice.cpp +++ b/src/backends/sdl2_iodevice.cpp @@ -1,7 +1,7 @@ /* MIT License -Copyright (c) 2022-2025 Kim Kulling +Copyright (c) 2022-2024 Kim Kulling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/backends/sdl2_iodevice.h b/src/backends/sdl2_iodevice.h index 7507725..211f76e 100644 --- a/src/backends/sdl2_iodevice.h +++ b/src/backends/sdl2_iodevice.h @@ -1,7 +1,7 @@ /* MIT License -Copyright (c) 2022-2025 Kim Kulling +Copyright (c) 2022-2024 Kim Kulling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -31,12 +31,7 @@ namespace tinyui { /// /// IO-Devices are used to contrl any kind of input / output operations. struct IODevice { - /// @brief The class destructor. ~IODevice() = default; - - /// @brief will do the update for the input states. - /// @param[out] event The new event. - /// @return true, if successful, false if not. static bool update(SDL_Event &event); }; diff --git a/src/backends/sdl2_renderer.cpp b/src/backends/sdl2_renderer.cpp index db733d6..8580be4 100644 --- a/src/backends/sdl2_renderer.cpp +++ b/src/backends/sdl2_renderer.cpp @@ -1,7 +1,7 @@ /* MIT License -Copyright (c) 2022-2025 Kim Kulling +Copyright (c) 2022-2024 Kim Kulling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,7 +27,6 @@ SOFTWARE. #include #include -#include namespace tinyui { namespace { @@ -63,8 +62,8 @@ void showDriverInUse(const Context &ctx) { printDriverInfo(info); } -int queryDriver(const Context &ctx, const std::string_view &driverType) { - if (driverType.empty()) { +int queryDriver(const Context &ctx, const char *driverType, size_t maxLen) { + if (driverType == nullptr) { ctx.mLogger(LogSeverity::Error, "Driver type is a nullptr."); return -1; } @@ -74,7 +73,11 @@ int queryDriver(const Context &ctx, const std::string_view &driverType) { for (int i = 0; i < numRenderDrivers; ++i) { SDL_RendererInfo info; SDL_GetRenderDriverInfo(i, &info); - if (driverType == std::string(info.name)) { + size_t len = strlen(driverType); + if (len > maxLen) { + len = maxLen; + } + if (strncmp(driverType, info.name, len) == 0) { found = i; break; } @@ -264,7 +267,7 @@ ret_code Renderer::initScreen(Context &ctx, int32_t x, int32_t y, int32_t w, int return ErrorCode; } - const int driverIndex = queryDriver(ctx, std::string("opengl")); + const int driverIndex = queryDriver(ctx, "opengl", 256); if (driverIndex == -1) { ctx.mLogger(LogSeverity::Error, "Cannot open opengl driver"); return ErrorCode; diff --git a/src/backends/sdl2_renderer.h b/src/backends/sdl2_renderer.h index 7aec610..be75d08 100644 --- a/src/backends/sdl2_renderer.h +++ b/src/backends/sdl2_renderer.h @@ -1,7 +1,7 @@ /* MIT License -Copyright (c) 2022-2025 Kim Kulling +Copyright (c) 2022-2024 Kim Kulling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -36,10 +36,9 @@ struct SDL_Renderer; struct SDL_Texture; namespace tinyui { - -/// @brief Implementation of a SDL2 surface. + struct SurfaceImpl { - SDL_Surface *mSurface = nullptr; + SDL_Surface *mSurface; ~SurfaceImpl() { clear(); @@ -54,7 +53,6 @@ struct SurfaceImpl { } }; -/// @brief Implementation of a SDL2 TTF font. struct FontImpl { TTF_Font *mFontImpl; diff --git a/src/tinyui.cpp b/src/tinyui.cpp index c712696..4fb0247 100644 --- a/src/tinyui.cpp +++ b/src/tinyui.cpp @@ -1,7 +1,7 @@ /* MIT License -Copyright (c) 2022-2025 Kim Kulling +Copyright (c) 2022-2024 Kim Kulling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -57,7 +57,6 @@ void log_message(LogSeverity severity, const char *message) { ret_code TinyUi::init(Context &ctx) { if (ctx.mCreated) { - printf("Error: Context is already inited\n"); return ErrorCode; } @@ -78,7 +77,6 @@ ret_code TinyUi::initScreen(Context &ctx, int32_t x, int32_t y, int32_t w, int32 ret_code TinyUi::getSurfaceInfo(Context &ctx, int32_t &w, int32_t &h) { w = h = -1; if (!ctx.mCreated) { - printf("Error: Context is not inited\n"); return ErrorCode; } @@ -111,7 +109,6 @@ ret_code TinyUi::endRender(Context &ctx) { ret_code TinyUi::release(Context &ctx) { if (!ctx.mCreated) { - printf("Error: Context is not inited\n"); return ErrorCode; } Renderer::releaseRenderer(ctx); diff --git a/src/tinyui.h b/src/tinyui.h index be40376..a9bb76f 100644 --- a/src/tinyui.h +++ b/src/tinyui.h @@ -1,7 +1,7 @@ /* MIT License -Copyright (c) 2022-2025 Kim Kulling +Copyright (c) 2022-2024 Kim Kulling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -250,10 +250,17 @@ struct Events { static constexpr int32_t MouseButtonUpEvent = 2; static constexpr int32_t MouseMoveEvent = 3; static constexpr int32_t MouseHoverEvent = 4; - static constexpr int32_t UpdateEvent = 5; + static constexpr int32_t KeyDownEvent = 5; + static constexpr int32_t KeyUpEvent = 6; + static constexpr int32_t UpdateEvent = 7; static constexpr int32_t NumEvents = UpdateEvent + 1; }; +struct EventData { + uint8_t data[16] = {}; + +}; + /// @brief This interface is used to store all neede message handlers. struct CallbackI { /// The function callback @@ -326,6 +333,7 @@ struct Context { SDLContext mSDLContext; ///< The SDL context. Style mStyle; ///< The active style. Widget *mRoot; ///< The root widget. + Widget *mFocus; ///< The widget which is in focus. tui_log_func mLogger = nullptr; ///< The logger function. EventDispatchMap mEventDispatchMap; ///< The event dispatch map. FontCache mFontCache; ///< The font cache. @@ -351,7 +359,8 @@ struct Context { mWindowsTitle(nullptr), mSDLContext(), mStyle(), - mRoot(nullptr) { + mRoot(nullptr), + mFocus(nullptr) { // empty } }; diff --git a/src/widgets.cpp b/src/widgets.cpp index ce398c3..1d65151 100644 --- a/src/widgets.cpp +++ b/src/widgets.cpp @@ -1,7 +1,7 @@ /* MIT License -Copyright (c) 2022-2025 Kim Kulling +Copyright (c) 2022-2024 Kim Kulling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -56,16 +56,19 @@ static Image *loadIntoImageCache(Context &ctx, const char *filename) { return image; } - int w = 0; - int h = 0; - int bytesPerPixel = 0; + image = new Image; + if (image == nullptr) { + return nullptr; + } + + int w, h, bytesPerPixel; unsigned char *data = stbi_load(filename, &w, &h, &bytesPerPixel, 0); if (data == nullptr) { return nullptr; } - - image = new Image; - int pitch = w * bytesPerPixel; + + + int32_t pitch = w * bytesPerPixel; pitch = (pitch + 3) & ~3; image->mSurfaceImpl = Renderer::createSurfaceImpl(data, w, h, bytesPerPixel, pitch); image->mX = w; @@ -110,8 +113,18 @@ static Widget *createWidget(Context &ctx, Id id, Id parentId, const Rect &rect, return widget; } -void eventDispatcher() { +void eventDispatcher(Context &ctx, int32_t eventId, EventData *eventData) { + if (ctx.mFocus == nullptr) { + return; + } + if (eventId == Events::KeyDownEvent || eventId == Events::KeyUpEvent) { + if (eventData != nullptr) { + if (ctx.mFocus->mType == WidgetType::TextField) { + ctx.mFocus->mText.append((const char*)eventData->data[0]); + } + } + } } ret_code Widgets::container(Context &ctx, Id id, Id parentId, const char *text, const Rect &rect) { @@ -190,6 +203,22 @@ ret_code Widgets::label(Context &ctx, Id id, Id parentId, const char *text, cons return ResultOk; } +ret_code Widgets::textfield(Context &ctx, Id id, Id parentId, const Rect &rect, Alignment alignment) { + if (ctx.mRoot == nullptr) { + return ErrorCode; + } + + Widget *widget = createWidget(ctx, id, parentId, rect, WidgetType::TextField); + if (widget == nullptr) { + return ErrorCode; + } + + widget->mAlignment = alignment; + + return ResultOk; +} + + ret_code Widgets::button(Context &ctx, Id id, Id parentId, const char *text, const char *image, const Rect &rect, CallbackI *callback) { if (ctx.mSDLContext.mRenderer == nullptr) { return ErrorCode; @@ -227,20 +256,6 @@ ret_code Widgets::box(Context &ctx, Id id, Id parentId, const Rect &rect, bool f return ResultOk; } -ret_code Widgets::toolbar(Context &ctx, Id id, Id parentId, const Rect &rect) { - if (ctx.mSDLContext.mRenderer == nullptr) { - ctx.mLogger(LogSeverity::Error, "TUI-Renderer is nullptr."); - return ErrorCode; - } - - Widget *child = createWidget(ctx, id, parentId, rect, WidgetType::ToolBar); - if (child == nullptr) { - return ErrorCode; - } - - return ResultOk; -} - ret_code Widgets::panel(Context &ctx, Id id, Id parentId, const char *title, const Rect &rect, CallbackI *callback) { if (ctx.mSDLContext.mRenderer == nullptr) { ctx.mLogger(LogSeverity::Error, "TUI-Renderer is nullptr."); @@ -302,7 +317,6 @@ ret_code Widgets::progressBar(Context &ctx, Id id, Id parentId, const Rect &rect ctx.mUpdateCallbackList.push_back(callback); } - return ResultOk; } @@ -360,7 +374,15 @@ static void render(Context &ctx, Widget *currentWidget) { const uint32_t fillRate = state->filledState; const uint32_t width = r.width * fillRate / 100; Renderer::drawRect(ctx, r.x1, r.y1, width, r.height, true, ctx.mStyle.mTextColor); - } break; + } + break; + + case WidgetType::TextField: + { + Renderer::drawRect(ctx, r.x1, r.y1, r.width, r.height, true, ctx.mStyle.mFg); + Renderer::drawRect(ctx, r.x1+2, r.y1+2, r.width-4, r.height-4, true, ctx.mStyle.mBorder); + } + break; case WidgetType::Container: case WidgetType::Box: @@ -428,7 +450,12 @@ void Widgets::onKey(Context &ctx, const char *key, bool isDown) { return; } - eventDispatcher(); + if (isDown) { + EventData eventData; + eventData.data[0] = *key; + eventDispatcher(ctx, Events::KeyDownEvent, &eventData); + + } } void recursiveClear(Widget *current) { @@ -475,6 +502,17 @@ bool Widgets::isEnabled(Context &ctx, Id id) { return false; } +ret_code Widgets::setFocus(Context &ctx, Id id) { + Widget *widget = findWidget(id, ctx.mRoot); + if (widget == nullptr) { + return ErrorCode; + } + + ctx.mFocus = widget; + + return ResultOk; +} + Widget *Widgets::getWidgetById(Context &ctx, Id id) { return findWidget(id, ctx.mRoot); } diff --git a/src/widgets.h b/src/widgets.h index 76b0b5a..02d5c17 100644 --- a/src/widgets.h +++ b/src/widgets.h @@ -1,7 +1,7 @@ /* MIT License -Copyright (c) 2022-2025 Kim Kulling +Copyright (c) 2022-2024 Kim Kulling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -36,11 +36,11 @@ enum class WidgetType { Container = 0, ///< A container widget Button, ///< A button widget Label, ///< A label widget + TextField, ///< Panel, ///< A panel widget Box, ///< A box widget TreeView, ///< A treeeview widget - ProgressBar, ///< A status bar widget - ToolBar, ///< A toolbar, will contain a list of buttons + ProgressBar, ///< A status bar widget Count ///< The number of widgets }; @@ -145,7 +145,7 @@ struct Widget { /// This class is used to create and manage widgets. struct Widgets { /// @brief The root item id. - static const Id RootItem = 0; + static constexpr Id RootItem = 0; /// @brief The default class constructor. Widgets() = default; @@ -171,9 +171,6 @@ struct Widgets { /// @return ResultOk if the widget was created, ErrorCode if not. static ret_code box(Context &ctx, Id id, Id parentId, const Rect &rect, bool filled); - - static ret_code toolbar(Context &ctx, Id id, Id parentId, const Rect &rect); - /// @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. @@ -197,6 +194,8 @@ struct Widgets { /// @return ResultOk if the widget was created, ErrorCode if not. static ret_code label(Context &ctx, Id id, Id parentId, const char *text, const Rect &rect, Alignment alignment); + static ret_code textfield(Context &ctx, Id id, Id parentId, const Rect &rect, Alignment alignment); + /// @brief Create a new widget from the type button. /// @param ctx The context to create the widget in. /// @param id The unique id of the widget. @@ -281,6 +280,8 @@ struct Widgets { /// @return true if the widget is enabled, false if not. static bool isEnabled(Context &ctx, Id id); + static ret_code setFocus(Context &ctx, Id 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.