diff --git a/src/dusk/rpg/entity/player.c b/src/dusk/rpg/entity/player.c index 01b703e4..8a8b8265 100644 --- a/src/dusk/rpg/entity/player.c +++ b/src/dusk/rpg/entity/player.c @@ -11,6 +11,7 @@ #include "util/memory.h" #include "time/time.h" #include "ui/focus/uifocus.h" +#include "ui/frame/uisettings.h" void playerInit(entity_t *entity) { assertNotNull(entity, "Entity pointer cannot be NULL"); @@ -19,11 +20,19 @@ void playerInit(entity_t *entity) { void playerInput(entity_t *entity) { assertNotNull(entity, "Entity pointer cannot be NULL"); - // Can player act? - if(UI_FOCUS.count > 0) { - return; + // Toggle settings on pause + if(uiSettingsIsOpen() && inputPressed(INPUT_ACTION_PAUSE)) { + uiSettingsClose(); + } else if( + inputPressed(INPUT_ACTION_PAUSE) && + entity->animation == ENTITY_ANIM_IDLE + ) { + uiSettingsOpen(); } + // Can player act? + if(UI_FOCUS.count > 0) return; + // Turn const playerinputdirmap_t *dirMap = PLAYER_INPUT_DIR_MAP; do { diff --git a/src/dusk/ui/CMakeLists.txt b/src/dusk/ui/CMakeLists.txt index 524c4a35..0c3b196e 100644 --- a/src/dusk/ui/CMakeLists.txt +++ b/src/dusk/ui/CMakeLists.txt @@ -7,6 +7,7 @@ add_subdirectory(debug) add_subdirectory(frame) add_subdirectory(focus) add_subdirectory(overlay) +add_subdirectory(widget) # Sources target_sources(${DUSK_LIBRARY_TARGET_NAME} diff --git a/src/dusk/ui/focus/uifocus.c b/src/dusk/ui/focus/uifocus.c index 69e2e8be..73b548b4 100644 --- a/src/dusk/ui/focus/uifocus.c +++ b/src/dusk/ui/focus/uifocus.c @@ -47,6 +47,7 @@ uifocusitem_t * uiFocusPush( item->changed = changed; item->closed = closed; UI_FOCUS.count++; + if(item->changed != NULL) item->changed(item); return item; } diff --git a/src/dusk/ui/frame/uisettings.c b/src/dusk/ui/frame/uisettings.c index a27f4966..46058a0c 100644 --- a/src/dusk/ui/frame/uisettings.c +++ b/src/dusk/ui/frame/uisettings.c @@ -7,27 +7,31 @@ #include "uisettings.h" #include "ui/frame/uiframe.h" +#include "ui/widget/uicheckbox.h" #include "util/memory.h" #include "display/spritebatch/spritebatch.h" #include "display/text/text.h" -#include "input/input.h" uisettings_t UI_SETTINGS; -errorret_t uiSettingsInit(void) { - memoryZero(&UI_SETTINGS, sizeof(uisettings_t)); - errorOk(); +bool_t uiSettingsSelected(const uifocusitem_t *item) { + if(item->y == 3) uiCheckboxToggle(&UI_SETTINGS.checkboxTest); + return true; } -errorret_t uiSettingsUpdate(void) { - if(!inputPressedIfDynamic(INPUT_ACTION_PAUSE)) errorOk(); - - if(UI_SETTINGS.item == NULL) { - uiSettingsOpen(); - } else { - uiSettingsClose(); - } +bool_t uiSettingsChanged(const uifocusitem_t *item) { + uiCheckboxSetHighlighted(&UI_SETTINGS.checkboxTest, item->y == 3); + return true; +} +bool_t uiSettingsClosed(const uifocusitem_t *item) { + UI_SETTINGS.item = NULL; + return true; +} + +errorret_t uiSettingsInit(void) { + memoryZero(&UI_SETTINGS, sizeof(uisettings_t)); + uiCheckboxInit(&UI_SETTINGS.checkboxTest, "Enable thing?"); errorOk(); } @@ -40,30 +44,41 @@ errorret_t uiSettingsDraw(void) { "Settings three" }; const uint8_t textCount = sizeof(texts) / sizeof(texts[0]); + const uint8_t totalRows = textCount + 1; errorChain(uiFrameDraw( - 0.0f, 0.0f, 100.0f, FONT_DEFAULT.tileset->tileHeight * textCount + (UIFRAME_BORDER_HEIGHT * 2) + 0.0f, 0.0f, 100.0f, + FONT_DEFAULT.tileset->tileHeight * totalRows + (UIFRAME_BORDER_HEIGHT * 2) )); for(uint8_t i = 0; i < textCount; i++) { - const char_t *c = texts[i]; errorChain(textDraw( UIFRAME_BORDER_WIDTH, UIFRAME_BORDER_HEIGHT + (i * FONT_DEFAULT.tileset->tileHeight), - c, + texts[i], UI_SETTINGS.item->y == i ? COLOR_RED : COLOR_WHITE, NULL )); } + errorChain(uiCheckboxDraw( + &UI_SETTINGS.checkboxTest, + UIFRAME_BORDER_WIDTH, + UIFRAME_BORDER_HEIGHT + (textCount * FONT_DEFAULT.tileset->tileHeight) + )); + errorChain(spriteBatchFlush()); errorOk(); } +bool_t uiSettingsIsOpen(void) { + return UI_SETTINGS.item != NULL; +} + void uiSettingsOpen() { if(UI_SETTINGS.item != NULL) return; UI_SETTINGS.item = uiFocusPush( - 1, 3, NULL, NULL, NULL + 1, 4, uiSettingsSelected, uiSettingsChanged, uiSettingsClosed ); } diff --git a/src/dusk/ui/frame/uisettings.h b/src/dusk/ui/frame/uisettings.h index 6ba12013..7b2ef2f9 100644 --- a/src/dusk/ui/frame/uisettings.h +++ b/src/dusk/ui/frame/uisettings.h @@ -8,9 +8,11 @@ #pragma once #include "error/error.h" #include "ui/focus/uifocus.h" +#include "ui/widget/uicheckbox.h" typedef struct { uifocusitem_t *item; + uicheckbox_t checkboxTest; } uisettings_t; extern uisettings_t UI_SETTINGS; @@ -22,13 +24,6 @@ extern uisettings_t UI_SETTINGS; */ errorret_t uiSettingsInit(void); -/** - * Updates the settings panel. Handles input when visible. - * - * @return Any error that occurs. - */ -errorret_t uiSettingsUpdate(void); - /** * Draws the settings panel. No-op when not visible. * @@ -36,6 +31,13 @@ errorret_t uiSettingsUpdate(void); */ errorret_t uiSettingsDraw(void); +/** + * Returns true when the settings panel is currently open. + * + * @returns True if open. + */ +bool_t uiSettingsIsOpen(void); + /** * Opens the settings panel. No-op when already open. */ diff --git a/src/dusk/ui/uielement.c b/src/dusk/ui/uielement.c index a5dd6e31..577bcf9e 100644 --- a/src/dusk/ui/uielement.c +++ b/src/dusk/ui/uielement.c @@ -35,7 +35,6 @@ uielement_t UI_ELEMENTS[] = { { .init = uiSettingsInit, - .update = uiSettingsUpdate, .draw = uiSettingsDraw, .dispose = uiSettingsDispose }, diff --git a/src/dusk/ui/widget/CMakeLists.txt b/src/dusk/ui/widget/CMakeLists.txt new file mode 100644 index 00000000..70f9d666 --- /dev/null +++ b/src/dusk/ui/widget/CMakeLists.txt @@ -0,0 +1,9 @@ +# Copyright (c) 2026 Dominic Masters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +target_sources(${DUSK_LIBRARY_TARGET_NAME} + PUBLIC + uicheckbox.c +) diff --git a/src/dusk/ui/widget/uicheckbox.c b/src/dusk/ui/widget/uicheckbox.c new file mode 100644 index 00000000..813cce1a --- /dev/null +++ b/src/dusk/ui/widget/uicheckbox.c @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "uicheckbox.h" +#include "util/memory.h" +#include "util/string.h" +#include "display/text/text.h" +#include "display/color.h" + +void uiCheckboxInit( + uicheckbox_t *checkbox, + const char_t *label +) { + memoryZero(checkbox, sizeof(uicheckbox_t)); + stringCopy(checkbox->label, label, UI_CHECKBOX_LABEL_MAX); +} + +bool_t uiCheckboxIsChecked(const uicheckbox_t *checkbox) { + return checkbox->checked; +} + +void uiCheckboxSetChecked(uicheckbox_t *checkbox, const bool_t checked) { + checkbox->checked = checked; +} + +void uiCheckboxToggle(uicheckbox_t *checkbox) { + uiCheckboxSetChecked(checkbox, !uiCheckboxIsChecked(checkbox)); +} + +bool_t uiCheckboxIsHighlighted(const uicheckbox_t *checkbox) { + return checkbox->highlighted; +} + +void uiCheckboxSetHighlighted(uicheckbox_t *checkbox, const bool_t highlighted) { + checkbox->highlighted = highlighted; +} + +errorret_t uiCheckboxDraw( + const uicheckbox_t *checkbox, + const float_t x, + const float_t y +) { + color_t color = checkbox->highlighted ? COLOR_RED : COLOR_WHITE; + const char_t *mark = checkbox->checked ? "Y " : "N "; + errorChain(textDraw(x, y, mark, color, &FONT_DEFAULT)); + + int32_t markW, markH; + textMeasure(mark, &FONT_DEFAULT, &markW, &markH); + errorChain(textDraw( + x + (float_t)markW, y, checkbox->label, color, &FONT_DEFAULT + )); + + errorOk(); +} diff --git a/src/dusk/ui/widget/uicheckbox.h b/src/dusk/ui/widget/uicheckbox.h new file mode 100644 index 00000000..2d06b619 --- /dev/null +++ b/src/dusk/ui/widget/uicheckbox.h @@ -0,0 +1,81 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "error/error.h" + +#define UI_CHECKBOX_LABEL_MAX 32 + +typedef struct uicheckbox_s { + char_t label[UI_CHECKBOX_LABEL_MAX]; + bool_t checked; + bool_t highlighted; +} uicheckbox_t; + +/** + * Initializes a checkbox. + * + * @param checkbox The checkbox to initialize. + * @param label Display label, truncated to UI_CHECKBOX_LABEL_MAX - 1 chars. + */ +void uiCheckboxInit( + uicheckbox_t *checkbox, + const char_t *label +); + +/** + * Returns whether the checkbox is checked. + * + * @param checkbox The checkbox to query. + * @returns True if checked. + */ +bool_t uiCheckboxIsChecked(const uicheckbox_t *checkbox); + +/** + * Sets the checked state of the checkbox. + * + * @param checkbox The checkbox to update. + * @param checked The new checked state. + */ +void uiCheckboxSetChecked(uicheckbox_t *checkbox, const bool_t checked); + +/** + * Toggles the checked state of the checkbox. + * + * @param checkbox The checkbox to toggle. + */ +void uiCheckboxToggle(uicheckbox_t *checkbox); + +/** + * Returns whether the checkbox is highlighted. + * + * @param checkbox The checkbox to query. + * @returns True if highlighted. + */ +bool_t uiCheckboxIsHighlighted(const uicheckbox_t *checkbox); + +/** + * Sets the highlighted state of the checkbox. + * + * @param checkbox The checkbox to update. + * @param highlighted The new highlighted state. + */ +void uiCheckboxSetHighlighted(uicheckbox_t *checkbox, const bool_t highlighted); + +/** + * Draws the checkbox at the given screen position. + * + * @param checkbox The checkbox to draw. + * @param x Screen x position. + * @param y Screen y position. + * @return Any error that occurs. + */ +errorret_t uiCheckboxDraw( + const uicheckbox_t *checkbox, + const float_t x, + const float_t y +);