Slider widget and some cleanup
This commit is contained in:
@@ -11,7 +11,7 @@
|
|||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
#include "time/time.h"
|
#include "time/time.h"
|
||||||
#include "ui/focus/uifocus.h"
|
#include "ui/focus/uifocus.h"
|
||||||
#include "ui/frame/uisettings.h"
|
#include "ui/frame/game/uigamemenu.h"
|
||||||
#include "rpg/cutscene/cutscenesystem.h"
|
#include "rpg/cutscene/cutscenesystem.h"
|
||||||
|
|
||||||
void playerInit(entity_t *entity) {
|
void playerInit(entity_t *entity) {
|
||||||
@@ -27,14 +27,14 @@ void playerInput(entity_t *entity) {
|
|||||||
assertNotNull(entity, "Entity pointer cannot be NULL");
|
assertNotNull(entity, "Entity pointer cannot be NULL");
|
||||||
if(CUTSCENE_SYSTEM.pause & CUTSCENE_PAUSE_PLAYER) return;
|
if(CUTSCENE_SYSTEM.pause & CUTSCENE_PAUSE_PLAYER) return;
|
||||||
|
|
||||||
// Toggle settings on pause
|
// Toggle game menu on pause
|
||||||
if(uiSettingsIsOpen() && inputPressed(INPUT_ACTION_PAUSE)) {
|
if(uiGameMenuIsOpen() && inputPressed(INPUT_ACTION_PAUSE)) {
|
||||||
uiSettingsClose();
|
uiGameMenuClose();
|
||||||
} else if(
|
} else if(
|
||||||
inputPressed(INPUT_ACTION_PAUSE) &&
|
inputPressed(INPUT_ACTION_PAUSE) &&
|
||||||
entity->animation == ENTITY_ANIM_IDLE
|
entity->animation == ENTITY_ANIM_IDLE
|
||||||
) {
|
) {
|
||||||
uiSettingsOpen();
|
uiGameMenuOpen();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Can player act?
|
// Can player act?
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ uifocusitem_t * uiFocusPush(
|
|||||||
uifocusitemcallback_t selected,
|
uifocusitemcallback_t selected,
|
||||||
uifocusitemcallback_t changed,
|
uifocusitemcallback_t changed,
|
||||||
uifocusitemcallback_t closed,
|
uifocusitemcallback_t closed,
|
||||||
|
uifocusitemdirectioncallback_t direction,
|
||||||
void *user
|
void *user
|
||||||
) {
|
) {
|
||||||
assertTrue(
|
assertTrue(
|
||||||
@@ -47,6 +48,7 @@ uifocusitem_t * uiFocusPush(
|
|||||||
item->selected = selected;
|
item->selected = selected;
|
||||||
item->changed = changed;
|
item->changed = changed;
|
||||||
item->closed = closed;
|
item->closed = closed;
|
||||||
|
item->direction = direction;
|
||||||
item->user = user;
|
item->user = user;
|
||||||
UI_FOCUS.count++;
|
UI_FOCUS.count++;
|
||||||
UI_FOCUS.pushedThisTick = true;
|
UI_FOCUS.pushedThisTick = true;
|
||||||
@@ -118,6 +120,14 @@ void uiFocusMoveDirection(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void uiFocusHandleDirection(
|
||||||
|
uifocusitem_t *item,
|
||||||
|
const uifocusdirection_t dir
|
||||||
|
) {
|
||||||
|
if(item->direction != NULL && item->direction(item, dir)) return;
|
||||||
|
uiFocusMoveDirection(item, dir);
|
||||||
|
}
|
||||||
|
|
||||||
void uiFocusUpdate(void) {
|
void uiFocusUpdate(void) {
|
||||||
if(UI_FOCUS.count == 0) return;
|
if(UI_FOCUS.count == 0) return;
|
||||||
|
|
||||||
@@ -150,7 +160,7 @@ void uiFocusUpdate(void) {
|
|||||||
if(!inputPressed(m->action)) continue;
|
if(!inputPressed(m->action)) continue;
|
||||||
UI_FOCUS.direction = m->direction;
|
UI_FOCUS.direction = m->direction;
|
||||||
UI_FOCUS.timeHeld = 0.0f;
|
UI_FOCUS.timeHeld = 0.0f;
|
||||||
uiFocusMoveDirection(item, m->direction);
|
uiFocusHandleDirection(item, m->direction);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,5 +186,5 @@ void uiFocusUpdate(void) {
|
|||||||
if(UI_FOCUS.timeHeld < UI_FOCUS_HOLD_DELAY) return;
|
if(UI_FOCUS.timeHeld < UI_FOCUS_HOLD_DELAY) return;
|
||||||
if(UI_FOCUS.timeHeld < UI_FOCUS_HOLD_DELAY + UI_FOCUS_HOLD_REPEAT) return;
|
if(UI_FOCUS.timeHeld < UI_FOCUS_HOLD_DELAY + UI_FOCUS_HOLD_REPEAT) return;
|
||||||
UI_FOCUS.timeHeld = UI_FOCUS_HOLD_DELAY;
|
UI_FOCUS.timeHeld = UI_FOCUS_HOLD_DELAY;
|
||||||
uiFocusMoveDirection(item, UI_FOCUS.direction);
|
uiFocusHandleDirection(item, UI_FOCUS.direction);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,14 +22,6 @@
|
|||||||
*/
|
*/
|
||||||
#define UI_FOCUS_HOLD_REPEAT 0.1f
|
#define UI_FOCUS_HOLD_REPEAT 0.1f
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
UI_FOCUS_DIRECTION_NONE,
|
|
||||||
UI_FOCUS_DIRECTION_UP,
|
|
||||||
UI_FOCUS_DIRECTION_DOWN,
|
|
||||||
UI_FOCUS_DIRECTION_LEFT,
|
|
||||||
UI_FOCUS_DIRECTION_RIGHT
|
|
||||||
} uifocusdirection_t;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
inputaction_t action;
|
inputaction_t action;
|
||||||
uifocusdirection_t direction;
|
uifocusdirection_t direction;
|
||||||
@@ -72,6 +64,8 @@ void uiFocusInit(void);
|
|||||||
* @param selected Called when the user selects the focused cell.
|
* @param selected Called when the user selects the focused cell.
|
||||||
* @param changed Called when the focused cell position changes.
|
* @param changed Called when the focused cell position changes.
|
||||||
* @param closed Called when this focus item is popped.
|
* @param closed Called when this focus item is popped.
|
||||||
|
* @param direction Called on a direction press/hold before the default
|
||||||
|
* cell-to-cell movement is applied; may be NULL.
|
||||||
* @param user Arbitrary pointer stored on the item before changed fires.
|
* @param user Arbitrary pointer stored on the item before changed fires.
|
||||||
* @returns Pointer to the newly pushed focus item.
|
* @returns Pointer to the newly pushed focus item.
|
||||||
*/
|
*/
|
||||||
@@ -81,6 +75,7 @@ uifocusitem_t * uiFocusPush(
|
|||||||
uifocusitemcallback_t selected,
|
uifocusitemcallback_t selected,
|
||||||
uifocusitemcallback_t changed,
|
uifocusitemcallback_t changed,
|
||||||
uifocusitemcallback_t closed,
|
uifocusitemcallback_t closed,
|
||||||
|
uifocusitemdirectioncallback_t direction,
|
||||||
void *user
|
void *user
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -118,6 +113,19 @@ void uiFocusMoveDirection(
|
|||||||
const uifocusdirection_t dir
|
const uifocusdirection_t dir
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles a direction press/hold for the given item: gives the item's
|
||||||
|
* direction callback (if any) first refusal, falling back to the
|
||||||
|
* default cell-to-cell movement if it's unset or returns false.
|
||||||
|
*
|
||||||
|
* @param item The focus item to move.
|
||||||
|
* @param dir Direction that was pressed or held.
|
||||||
|
*/
|
||||||
|
void uiFocusHandleDirection(
|
||||||
|
uifocusitem_t *item,
|
||||||
|
const uifocusdirection_t dir
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the focus system. Handles first-press movement and
|
* Updates the focus system. Handles first-press movement and
|
||||||
* held-direction repeating. Called once per game tick.
|
* held-direction repeating. Called once per game tick.
|
||||||
|
|||||||
@@ -10,6 +10,14 @@
|
|||||||
|
|
||||||
typedef struct uifocusitem_s uifocusitem_t;
|
typedef struct uifocusitem_s uifocusitem_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
UI_FOCUS_DIRECTION_NONE,
|
||||||
|
UI_FOCUS_DIRECTION_UP,
|
||||||
|
UI_FOCUS_DIRECTION_DOWN,
|
||||||
|
UI_FOCUS_DIRECTION_LEFT,
|
||||||
|
UI_FOCUS_DIRECTION_RIGHT
|
||||||
|
} uifocusdirection_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback invoked when a focus item's selected cell changes.
|
* Callback invoked when a focus item's selected cell changes.
|
||||||
*
|
*
|
||||||
@@ -17,6 +25,21 @@ typedef struct uifocusitem_s uifocusitem_t;
|
|||||||
*/
|
*/
|
||||||
typedef bool_t (*uifocusitemcallback_t)(const uifocusitem_t *item);
|
typedef bool_t (*uifocusitemcallback_t)(const uifocusitem_t *item);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback invoked when a direction is pressed or held-repeated while
|
||||||
|
* this item is focused, before the default cell-to-cell movement is
|
||||||
|
* applied.
|
||||||
|
*
|
||||||
|
* @param item The focus item that is currently focused.
|
||||||
|
* @param direction The direction that was pressed.
|
||||||
|
* @returns True if the direction was fully handled and the default
|
||||||
|
* grid movement should be skipped; false to fall through to it.
|
||||||
|
*/
|
||||||
|
typedef bool_t (*uifocusitemdirectioncallback_t)(
|
||||||
|
const uifocusitem_t *item,
|
||||||
|
const uifocusdirection_t direction
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A single entry on the UI focus stack. Tracks the focused cell
|
* A single entry on the UI focus stack. Tracks the focused cell
|
||||||
* within a grid of cols x rows, and the current x/y position.
|
* within a grid of cols x rows, and the current x/y position.
|
||||||
@@ -29,5 +52,6 @@ struct uifocusitem_s {
|
|||||||
uifocusitemcallback_t selected;
|
uifocusitemcallback_t selected;
|
||||||
uifocusitemcallback_t changed;
|
uifocusitemcallback_t changed;
|
||||||
uifocusitemcallback_t closed;
|
uifocusitemcallback_t closed;
|
||||||
|
uifocusitemdirectioncallback_t direction;
|
||||||
void *user;
|
void *user;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,5 +6,7 @@
|
|||||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||||
PUBLIC
|
PUBLIC
|
||||||
uiframe.c
|
uiframe.c
|
||||||
uisettings.c
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
add_subdirectory(game)
|
||||||
|
add_subdirectory(settings)
|
||||||
|
|||||||
@@ -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
|
||||||
|
uigamemenu.c
|
||||||
|
)
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "uigamemenu.h"
|
||||||
|
#include "ui/frame/uiframe.h"
|
||||||
|
#include "ui/frame/settings/uisettings.h"
|
||||||
|
#include "util/memory.h"
|
||||||
|
#include "display/spritebatch/spritebatch.h"
|
||||||
|
#include "display/screen/screen.h"
|
||||||
|
#include "assert/assert.h"
|
||||||
|
|
||||||
|
#define UI_GAME_MENU_INDEX_CHARACTERS 0
|
||||||
|
#define UI_GAME_MENU_INDEX_SETTINGS 1
|
||||||
|
|
||||||
|
uigamemenu_t UI_GAME_MENU;
|
||||||
|
|
||||||
|
void uiGameMenuSelected(
|
||||||
|
const uimenu_t *menu,
|
||||||
|
const uint8_t index,
|
||||||
|
const uimenuitem_t *item
|
||||||
|
) {
|
||||||
|
if(index == UI_GAME_MENU_INDEX_SETTINGS) uiSettingsOpen();
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t uiGameMenuInit(void) {
|
||||||
|
memoryZero(&UI_GAME_MENU, sizeof(uigamemenu_t));
|
||||||
|
|
||||||
|
uimenu_t *menu = &UI_GAME_MENU.menu;
|
||||||
|
uiMenuInit(menu, uiGameMenuSelected, NULL, NULL);
|
||||||
|
menu->items = UI_GAME_MENU.items;
|
||||||
|
|
||||||
|
MENU_BEGIN
|
||||||
|
MENU_BUTTON("Characters");
|
||||||
|
MENU_BUTTON("Settings");
|
||||||
|
|
||||||
|
assertTrue(
|
||||||
|
menuIndex <= UI_GAME_MENU_ITEM_COUNT,
|
||||||
|
"Game menu item count mismatch"
|
||||||
|
);
|
||||||
|
|
||||||
|
uiMenuSetItems(menu, UI_GAME_MENU.items, menuIndex, 1);
|
||||||
|
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t uiGameMenuDraw(void) {
|
||||||
|
if(!uiMenuIsActive(&UI_GAME_MENU.menu)) errorOk();
|
||||||
|
|
||||||
|
const float_t width = UI_GAME_MENU_WIDTH;
|
||||||
|
const float_t height = (float_t)SCREEN.scanHeight;
|
||||||
|
const float_t x = (float_t)(SCREEN.scanX + SCREEN.scanWidth) - width;
|
||||||
|
const float_t y = (float_t)SCREEN.scanY;
|
||||||
|
|
||||||
|
errorChain(uiFrameDraw(x, y, width, height));
|
||||||
|
errorChain(uiMenuDraw(
|
||||||
|
&UI_GAME_MENU.menu,
|
||||||
|
x + UI_FRAME_START_X,
|
||||||
|
y + UI_FRAME_START_Y,
|
||||||
|
width - (UI_FRAME_START_X * 2),
|
||||||
|
height - (UI_FRAME_START_Y * 2)
|
||||||
|
));
|
||||||
|
|
||||||
|
errorChain(spriteBatchFlush());
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool_t uiGameMenuIsOpen(void) {
|
||||||
|
return uiMenuIsActive(&UI_GAME_MENU.menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiGameMenuOpen() {
|
||||||
|
uiMenuOpen(&UI_GAME_MENU.menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiGameMenuClose() {
|
||||||
|
uiMenuClose(&UI_GAME_MENU.menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t uiGameMenuDispose(void) {
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "error/error.h"
|
||||||
|
#include "ui/widget/uimenu.h"
|
||||||
|
|
||||||
|
#define UI_GAME_MENU_ITEM_COUNT 2
|
||||||
|
#define UI_GAME_MENU_WIDTH 150.0f
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uimenu_t menu;
|
||||||
|
uimenuitem_t items[UI_GAME_MENU_ITEM_COUNT];
|
||||||
|
} uigamemenu_t;
|
||||||
|
|
||||||
|
extern uigamemenu_t UI_GAME_MENU;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the game menu panel.
|
||||||
|
*
|
||||||
|
* @return Any error that occurs.
|
||||||
|
*/
|
||||||
|
errorret_t uiGameMenuInit(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws the game menu panel. No-op when not visible.
|
||||||
|
*
|
||||||
|
* @return Any error that occurs.
|
||||||
|
*/
|
||||||
|
errorret_t uiGameMenuDraw(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true when the game menu panel is currently open.
|
||||||
|
*
|
||||||
|
* @returns True if open.
|
||||||
|
*/
|
||||||
|
bool_t uiGameMenuIsOpen(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the game menu panel. No-op when already open.
|
||||||
|
*/
|
||||||
|
void uiGameMenuOpen();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the game menu panel. No-op when already closed.
|
||||||
|
*/
|
||||||
|
void uiGameMenuClose();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disposes of the game menu panel.
|
||||||
|
*
|
||||||
|
* @return Any error that occurs.
|
||||||
|
*/
|
||||||
|
errorret_t uiGameMenuDispose(void);
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# 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
|
||||||
|
uisettings.c
|
||||||
|
uisettingsgameplay.c
|
||||||
|
uisettingsinput.c
|
||||||
|
uisettingsdisplay.c
|
||||||
|
uisettingsaudio.c
|
||||||
|
)
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "uisettings.h"
|
||||||
|
#include "uisettingsgameplay.h"
|
||||||
|
#include "uisettingsinput.h"
|
||||||
|
#include "uisettingsdisplay.h"
|
||||||
|
#include "uisettingsaudio.h"
|
||||||
|
#include "ui/frame/uiframe.h"
|
||||||
|
#include "util/memory.h"
|
||||||
|
#include "display/spritebatch/spritebatch.h"
|
||||||
|
#include "display/text/text.h"
|
||||||
|
#include "display/screen/screen.h"
|
||||||
|
#include "assert/assert.h"
|
||||||
|
|
||||||
|
uisettings_t UI_SETTINGS;
|
||||||
|
|
||||||
|
void uiSettingsTabSelected(
|
||||||
|
const uimenu_t *menu,
|
||||||
|
const uint8_t index,
|
||||||
|
const uimenuitem_t *item
|
||||||
|
) {
|
||||||
|
switch(index) {
|
||||||
|
case UI_SETTINGS_TAB_GAMEPLAY:
|
||||||
|
uiSettingsGameplayOpen();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case UI_SETTINGS_TAB_INPUT:
|
||||||
|
uiSettingsInputOpen();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case UI_SETTINGS_TAB_DISPLAY:
|
||||||
|
uiSettingsDisplayOpen();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case UI_SETTINGS_TAB_AUDIO:
|
||||||
|
uiSettingsAudioOpen();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t uiSettingsInit(void) {
|
||||||
|
memoryZero(&UI_SETTINGS, sizeof(uisettings_t));
|
||||||
|
|
||||||
|
uimenu_t *menu = &UI_SETTINGS.tabsMenu;
|
||||||
|
uiMenuInit(menu, uiSettingsTabSelected, NULL, NULL);
|
||||||
|
menu->items = UI_SETTINGS.tabs;
|
||||||
|
|
||||||
|
MENU_BEGIN
|
||||||
|
MENU_TAB("Gameplay");
|
||||||
|
MENU_TAB("Input");
|
||||||
|
MENU_TAB("Display");
|
||||||
|
MENU_TAB("Audio");
|
||||||
|
|
||||||
|
assertTrue(
|
||||||
|
menuIndex <= UI_SETTINGS_TAB_COUNT,
|
||||||
|
"Settings tab count mismatch"
|
||||||
|
);
|
||||||
|
|
||||||
|
uiMenuSetItems(menu, UI_SETTINGS.tabs, menuIndex, menuIndex);
|
||||||
|
|
||||||
|
errorChain(uiSettingsGameplayInit());
|
||||||
|
errorChain(uiSettingsInputInit());
|
||||||
|
errorChain(uiSettingsDisplayInit());
|
||||||
|
errorChain(uiSettingsAudioInit());
|
||||||
|
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t uiSettingsDraw(void) {
|
||||||
|
if(!uiMenuIsActive(&UI_SETTINGS.tabsMenu)) errorOk();
|
||||||
|
|
||||||
|
const float_t width = 220.0f;
|
||||||
|
const float_t height = 150.0f;
|
||||||
|
const float_t x = (float_t)SCREEN.scanX +
|
||||||
|
((float_t)SCREEN.scanWidth - width) * 0.5f;
|
||||||
|
const float_t y = (float_t)SCREEN.scanY +
|
||||||
|
((float_t)SCREEN.scanHeight - height) * 0.5f;
|
||||||
|
|
||||||
|
errorChain(uiFrameDraw(x, y, width, height));
|
||||||
|
|
||||||
|
const float_t contentX = x + UI_FRAME_START_X;
|
||||||
|
const float_t contentY = y + UI_FRAME_START_Y;
|
||||||
|
const float_t contentWidth = width - (UI_FRAME_START_X * 2);
|
||||||
|
const float_t contentHeight = height - (UI_FRAME_START_Y * 2);
|
||||||
|
|
||||||
|
errorChain(uiMenuDraw(
|
||||||
|
&UI_SETTINGS.tabsMenu,
|
||||||
|
contentX,
|
||||||
|
contentY,
|
||||||
|
contentWidth,
|
||||||
|
contentHeight
|
||||||
|
));
|
||||||
|
|
||||||
|
const float_t tabsRowHeight = (float_t)FONT_DEFAULT.tileset->tileHeight;
|
||||||
|
const float_t pageY = contentY + tabsRowHeight + UI_FRAME_PADDING_Y;
|
||||||
|
const float_t pageHeight = contentHeight - tabsRowHeight - UI_FRAME_PADDING_Y;
|
||||||
|
|
||||||
|
if(uiTabIsActive(&UI_SETTINGS.tabs[UI_SETTINGS_TAB_GAMEPLAY].tab)) {
|
||||||
|
errorChain(uiSettingsGameplayDraw(contentX, pageY, contentWidth, pageHeight));
|
||||||
|
} else if(uiTabIsActive(&UI_SETTINGS.tabs[UI_SETTINGS_TAB_INPUT].tab)) {
|
||||||
|
errorChain(uiSettingsInputDraw(contentX, pageY, contentWidth, pageHeight));
|
||||||
|
} else if(uiTabIsActive(&UI_SETTINGS.tabs[UI_SETTINGS_TAB_DISPLAY].tab)) {
|
||||||
|
errorChain(uiSettingsDisplayDraw(contentX, pageY, contentWidth, pageHeight));
|
||||||
|
} else if(uiTabIsActive(&UI_SETTINGS.tabs[UI_SETTINGS_TAB_AUDIO].tab)) {
|
||||||
|
errorChain(uiSettingsAudioDraw(contentX, pageY, contentWidth, pageHeight));
|
||||||
|
}
|
||||||
|
|
||||||
|
errorChain(spriteBatchFlush());
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool_t uiSettingsIsOpen(void) {
|
||||||
|
return uiMenuIsActive(&UI_SETTINGS.tabsMenu);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiSettingsOpen() {
|
||||||
|
uiMenuOpen(&UI_SETTINGS.tabsMenu);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiSettingsClose() {
|
||||||
|
uiMenuClose(&UI_SETTINGS.tabsMenu);
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t uiSettingsDispose(void) {
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
@@ -9,17 +9,21 @@
|
|||||||
#include "error/error.h"
|
#include "error/error.h"
|
||||||
#include "ui/widget/uimenu.h"
|
#include "ui/widget/uimenu.h"
|
||||||
|
|
||||||
#define UI_SETTINGS_ITEM_COUNT 6
|
#define UI_SETTINGS_TAB_GAMEPLAY 0
|
||||||
|
#define UI_SETTINGS_TAB_INPUT 1
|
||||||
|
#define UI_SETTINGS_TAB_DISPLAY 2
|
||||||
|
#define UI_SETTINGS_TAB_AUDIO 3
|
||||||
|
#define UI_SETTINGS_TAB_COUNT 4
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uimenu_t menu;
|
uimenu_t tabsMenu;
|
||||||
uimenuitem_t items[UI_SETTINGS_ITEM_COUNT];
|
uimenuitem_t tabs[UI_SETTINGS_TAB_COUNT];
|
||||||
} uisettings_t;
|
} uisettings_t;
|
||||||
|
|
||||||
extern uisettings_t UI_SETTINGS;
|
extern uisettings_t UI_SETTINGS;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the settings panel.
|
* Initializes the settings panel and all of its category pages.
|
||||||
*
|
*
|
||||||
* @return Any error that occurs.
|
* @return Any error that occurs.
|
||||||
*/
|
*/
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "uisettingsaudio.h"
|
||||||
|
#include "util/memory.h"
|
||||||
|
|
||||||
|
uisettingsaudio_t UI_SETTINGS_AUDIO;
|
||||||
|
|
||||||
|
errorret_t uiSettingsAudioInit(void) {
|
||||||
|
memoryZero(&UI_SETTINGS_AUDIO, sizeof(uisettingsaudio_t));
|
||||||
|
|
||||||
|
uimenu_t *menu = &UI_SETTINGS_AUDIO.menu;
|
||||||
|
uiMenuInit(menu, NULL, NULL, NULL);
|
||||||
|
menu->items = UI_SETTINGS_AUDIO.items;
|
||||||
|
|
||||||
|
MENU_BEGIN
|
||||||
|
MENU_LABEL("No audio settings yet");
|
||||||
|
|
||||||
|
uiMenuSetItems(menu, UI_SETTINGS_AUDIO.items, menuIndex, 1);
|
||||||
|
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t uiSettingsAudioDraw(
|
||||||
|
const float_t x,
|
||||||
|
const float_t y,
|
||||||
|
const float_t width,
|
||||||
|
const float_t height
|
||||||
|
) {
|
||||||
|
return uiMenuDraw(&UI_SETTINGS_AUDIO.menu, x, y, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool_t uiSettingsAudioIsOpen(void) {
|
||||||
|
return uiMenuIsActive(&UI_SETTINGS_AUDIO.menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiSettingsAudioOpen(void) {
|
||||||
|
uiMenuOpen(&UI_SETTINGS_AUDIO.menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiSettingsAudioClose(void) {
|
||||||
|
uiMenuClose(&UI_SETTINGS_AUDIO.menu);
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "error/error.h"
|
||||||
|
#include "ui/widget/uimenu.h"
|
||||||
|
|
||||||
|
#define UI_SETTINGS_AUDIO_ITEM_COUNT 1
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uimenu_t menu;
|
||||||
|
uimenuitem_t items[UI_SETTINGS_AUDIO_ITEM_COUNT];
|
||||||
|
} uisettingsaudio_t;
|
||||||
|
|
||||||
|
extern uisettingsaudio_t UI_SETTINGS_AUDIO;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the audio settings page.
|
||||||
|
*
|
||||||
|
* @return Any error that occurs.
|
||||||
|
*/
|
||||||
|
errorret_t uiSettingsAudioInit(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws the audio settings page at the given content rect.
|
||||||
|
*
|
||||||
|
* @param x Screen x position.
|
||||||
|
* @param y Screen y position.
|
||||||
|
* @param width Content width.
|
||||||
|
* @param height Content height.
|
||||||
|
* @return Any error that occurs.
|
||||||
|
*/
|
||||||
|
errorret_t uiSettingsAudioDraw(
|
||||||
|
const float_t x,
|
||||||
|
const float_t y,
|
||||||
|
const float_t width,
|
||||||
|
const float_t height
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true when the audio settings page is currently open.
|
||||||
|
*
|
||||||
|
* @returns True if open.
|
||||||
|
*/
|
||||||
|
bool_t uiSettingsAudioIsOpen(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the audio settings page. No-op when already open.
|
||||||
|
*/
|
||||||
|
void uiSettingsAudioOpen(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the audio settings page. No-op when already closed.
|
||||||
|
*/
|
||||||
|
void uiSettingsAudioClose(void);
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "uisettingsdisplay.h"
|
||||||
|
#include "util/memory.h"
|
||||||
|
|
||||||
|
uisettingsdisplay_t UI_SETTINGS_DISPLAY;
|
||||||
|
|
||||||
|
errorret_t uiSettingsDisplayInit(void) {
|
||||||
|
memoryZero(&UI_SETTINGS_DISPLAY, sizeof(uisettingsdisplay_t));
|
||||||
|
|
||||||
|
uimenu_t *menu = &UI_SETTINGS_DISPLAY.menu;
|
||||||
|
uiMenuInit(menu, NULL, NULL, NULL);
|
||||||
|
menu->items = UI_SETTINGS_DISPLAY.items;
|
||||||
|
|
||||||
|
MENU_BEGIN
|
||||||
|
MENU_LABEL("No display settings yet");
|
||||||
|
|
||||||
|
uiMenuSetItems(menu, UI_SETTINGS_DISPLAY.items, menuIndex, 1);
|
||||||
|
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t uiSettingsDisplayDraw(
|
||||||
|
const float_t x,
|
||||||
|
const float_t y,
|
||||||
|
const float_t width,
|
||||||
|
const float_t height
|
||||||
|
) {
|
||||||
|
return uiMenuDraw(&UI_SETTINGS_DISPLAY.menu, x, y, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool_t uiSettingsDisplayIsOpen(void) {
|
||||||
|
return uiMenuIsActive(&UI_SETTINGS_DISPLAY.menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiSettingsDisplayOpen(void) {
|
||||||
|
uiMenuOpen(&UI_SETTINGS_DISPLAY.menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiSettingsDisplayClose(void) {
|
||||||
|
uiMenuClose(&UI_SETTINGS_DISPLAY.menu);
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "error/error.h"
|
||||||
|
#include "ui/widget/uimenu.h"
|
||||||
|
|
||||||
|
#define UI_SETTINGS_DISPLAY_ITEM_COUNT 1
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uimenu_t menu;
|
||||||
|
uimenuitem_t items[UI_SETTINGS_DISPLAY_ITEM_COUNT];
|
||||||
|
} uisettingsdisplay_t;
|
||||||
|
|
||||||
|
extern uisettingsdisplay_t UI_SETTINGS_DISPLAY;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the display settings page.
|
||||||
|
*
|
||||||
|
* @return Any error that occurs.
|
||||||
|
*/
|
||||||
|
errorret_t uiSettingsDisplayInit(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws the display settings page at the given content rect.
|
||||||
|
*
|
||||||
|
* @param x Screen x position.
|
||||||
|
* @param y Screen y position.
|
||||||
|
* @param width Content width.
|
||||||
|
* @param height Content height.
|
||||||
|
* @return Any error that occurs.
|
||||||
|
*/
|
||||||
|
errorret_t uiSettingsDisplayDraw(
|
||||||
|
const float_t x,
|
||||||
|
const float_t y,
|
||||||
|
const float_t width,
|
||||||
|
const float_t height
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true when the display settings page is currently open.
|
||||||
|
*
|
||||||
|
* @returns True if open.
|
||||||
|
*/
|
||||||
|
bool_t uiSettingsDisplayIsOpen(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the display settings page. No-op when already open.
|
||||||
|
*/
|
||||||
|
void uiSettingsDisplayOpen(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the display settings page. No-op when already closed.
|
||||||
|
*/
|
||||||
|
void uiSettingsDisplayClose(void);
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "uisettingsgameplay.h"
|
||||||
|
#include "assert/assert.h"
|
||||||
|
#include "util/memory.h"
|
||||||
|
|
||||||
|
uisettingsgameplay_t UI_SETTINGS_GAMEPLAY;
|
||||||
|
|
||||||
|
errorret_t uiSettingsGameplayInit(void) {
|
||||||
|
memoryZero(&UI_SETTINGS_GAMEPLAY, sizeof(uisettingsgameplay_t));
|
||||||
|
|
||||||
|
uimenu_t *menu = &UI_SETTINGS_GAMEPLAY.menu;
|
||||||
|
uiMenuInit(menu, NULL, NULL, NULL);
|
||||||
|
menu->items = UI_SETTINGS_GAMEPLAY.items;
|
||||||
|
|
||||||
|
MENU_BEGIN
|
||||||
|
MENU_BUTTON("Do thing");
|
||||||
|
MENU_BUTTON("Do other thing");
|
||||||
|
MENU_CHECKBOX("Enable thing?");
|
||||||
|
MENU_SLIDER_FLOAT("test", 1.0f, 1.0f, 4.0f, 0.5f);
|
||||||
|
|
||||||
|
assertTrue(
|
||||||
|
menuIndex <= UI_SETTINGS_GAMEPLAY_ITEM_COUNT,
|
||||||
|
"Gameplay settings item count mismatch"
|
||||||
|
);
|
||||||
|
|
||||||
|
uiMenuSetItems(menu, UI_SETTINGS_GAMEPLAY.items, menuIndex, 1);
|
||||||
|
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t uiSettingsGameplayDraw(
|
||||||
|
const float_t x,
|
||||||
|
const float_t y,
|
||||||
|
const float_t width,
|
||||||
|
const float_t height
|
||||||
|
) {
|
||||||
|
return uiMenuDraw(&UI_SETTINGS_GAMEPLAY.menu, x, y, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool_t uiSettingsGameplayIsOpen(void) {
|
||||||
|
return uiMenuIsActive(&UI_SETTINGS_GAMEPLAY.menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiSettingsGameplayOpen(void) {
|
||||||
|
uiMenuOpen(&UI_SETTINGS_GAMEPLAY.menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiSettingsGameplayClose(void) {
|
||||||
|
uiMenuClose(&UI_SETTINGS_GAMEPLAY.menu);
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "error/error.h"
|
||||||
|
#include "ui/widget/uimenu.h"
|
||||||
|
|
||||||
|
#define UI_SETTINGS_GAMEPLAY_ITEM_COUNT 4
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uimenu_t menu;
|
||||||
|
uimenuitem_t items[UI_SETTINGS_GAMEPLAY_ITEM_COUNT];
|
||||||
|
} uisettingsgameplay_t;
|
||||||
|
|
||||||
|
extern uisettingsgameplay_t UI_SETTINGS_GAMEPLAY;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the gameplay settings page.
|
||||||
|
*
|
||||||
|
* @return Any error that occurs.
|
||||||
|
*/
|
||||||
|
errorret_t uiSettingsGameplayInit(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws the gameplay settings page at the given content rect.
|
||||||
|
*
|
||||||
|
* @param x Screen x position.
|
||||||
|
* @param y Screen y position.
|
||||||
|
* @param width Content width.
|
||||||
|
* @param height Content height.
|
||||||
|
* @return Any error that occurs.
|
||||||
|
*/
|
||||||
|
errorret_t uiSettingsGameplayDraw(
|
||||||
|
const float_t x,
|
||||||
|
const float_t y,
|
||||||
|
const float_t width,
|
||||||
|
const float_t height
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true when the gameplay settings page is currently open.
|
||||||
|
*
|
||||||
|
* @returns True if open.
|
||||||
|
*/
|
||||||
|
bool_t uiSettingsGameplayIsOpen(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the gameplay settings page. No-op when already open.
|
||||||
|
*/
|
||||||
|
void uiSettingsGameplayOpen(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the gameplay settings page. No-op when already closed.
|
||||||
|
*/
|
||||||
|
void uiSettingsGameplayClose(void);
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "uisettingsinput.h"
|
||||||
|
#include "util/memory.h"
|
||||||
|
|
||||||
|
uisettingsinput_t UI_SETTINGS_INPUT;
|
||||||
|
|
||||||
|
errorret_t uiSettingsInputInit(void) {
|
||||||
|
memoryZero(&UI_SETTINGS_INPUT, sizeof(uisettingsinput_t));
|
||||||
|
|
||||||
|
uimenu_t *menu = &UI_SETTINGS_INPUT.menu;
|
||||||
|
uiMenuInit(menu, NULL, NULL, NULL);
|
||||||
|
menu->items = UI_SETTINGS_INPUT.items;
|
||||||
|
|
||||||
|
MENU_BEGIN
|
||||||
|
MENU_LABEL("No input settings yet");
|
||||||
|
|
||||||
|
uiMenuSetItems(menu, UI_SETTINGS_INPUT.items, menuIndex, 1);
|
||||||
|
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t uiSettingsInputDraw(
|
||||||
|
const float_t x,
|
||||||
|
const float_t y,
|
||||||
|
const float_t width,
|
||||||
|
const float_t height
|
||||||
|
) {
|
||||||
|
return uiMenuDraw(&UI_SETTINGS_INPUT.menu, x, y, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool_t uiSettingsInputIsOpen(void) {
|
||||||
|
return uiMenuIsActive(&UI_SETTINGS_INPUT.menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiSettingsInputOpen(void) {
|
||||||
|
uiMenuOpen(&UI_SETTINGS_INPUT.menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiSettingsInputClose(void) {
|
||||||
|
uiMenuClose(&UI_SETTINGS_INPUT.menu);
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "error/error.h"
|
||||||
|
#include "ui/widget/uimenu.h"
|
||||||
|
|
||||||
|
#define UI_SETTINGS_INPUT_ITEM_COUNT 1
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uimenu_t menu;
|
||||||
|
uimenuitem_t items[UI_SETTINGS_INPUT_ITEM_COUNT];
|
||||||
|
} uisettingsinput_t;
|
||||||
|
|
||||||
|
extern uisettingsinput_t UI_SETTINGS_INPUT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the input settings page.
|
||||||
|
*
|
||||||
|
* @return Any error that occurs.
|
||||||
|
*/
|
||||||
|
errorret_t uiSettingsInputInit(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws the input settings page at the given content rect.
|
||||||
|
*
|
||||||
|
* @param x Screen x position.
|
||||||
|
* @param y Screen y position.
|
||||||
|
* @param width Content width.
|
||||||
|
* @param height Content height.
|
||||||
|
* @return Any error that occurs.
|
||||||
|
*/
|
||||||
|
errorret_t uiSettingsInputDraw(
|
||||||
|
const float_t x,
|
||||||
|
const float_t y,
|
||||||
|
const float_t width,
|
||||||
|
const float_t height
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true when the input settings page is currently open.
|
||||||
|
*
|
||||||
|
* @returns True if open.
|
||||||
|
*/
|
||||||
|
bool_t uiSettingsInputIsOpen(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the input settings page. No-op when already open.
|
||||||
|
*/
|
||||||
|
void uiSettingsInputOpen(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the input settings page. No-op when already closed.
|
||||||
|
*/
|
||||||
|
void uiSettingsInputClose(void);
|
||||||
@@ -19,8 +19,8 @@ errorret_t uiFrameInit(void) {
|
|||||||
memoryZero(&UI_FRAME, sizeof(uiframe_t));
|
memoryZero(&UI_FRAME, sizeof(uiframe_t));
|
||||||
|
|
||||||
// Init texture.
|
// Init texture.
|
||||||
color_t border = color4b(0, 100, 220, 255);
|
color_t border = color4b(0, 50, 255, 255);
|
||||||
color_t center = color4b(0, 100, 220, 200);
|
color_t center = color4b(0, 100, 220, 240);
|
||||||
|
|
||||||
for(uint8_t y = 0; y < UI_FRAME_TEXTURE_HEIGHT_POW2; y++) {
|
for(uint8_t y = 0; y < UI_FRAME_TEXTURE_HEIGHT_POW2; y++) {
|
||||||
for(uint8_t x = 0; x < UI_FRAME_TEXTURE_WIDTH_POW2; x++) {
|
for(uint8_t x = 0; x < UI_FRAME_TEXTURE_WIDTH_POW2; x++) {
|
||||||
|
|||||||
@@ -12,6 +12,10 @@
|
|||||||
|
|
||||||
#define UI_FRAME_BORDER_WIDTH 6
|
#define UI_FRAME_BORDER_WIDTH 6
|
||||||
#define UI_FRAME_BORDER_HEIGHT 6
|
#define UI_FRAME_BORDER_HEIGHT 6
|
||||||
|
#define UI_FRAME_PADDING_X 4
|
||||||
|
#define UI_FRAME_PADDING_Y 4
|
||||||
|
#define UI_FRAME_START_X (UI_FRAME_BORDER_WIDTH + UI_FRAME_PADDING_X)
|
||||||
|
#define UI_FRAME_START_Y (UI_FRAME_BORDER_HEIGHT + UI_FRAME_PADDING_Y)
|
||||||
#define UI_FRAME_TILE_WIDTH 1
|
#define UI_FRAME_TILE_WIDTH 1
|
||||||
#define UI_FRAME_TILE_HEIGHT 1
|
#define UI_FRAME_TILE_HEIGHT 1
|
||||||
#define UI_FRAME_TEXTURE_WIDTH (UI_FRAME_TILE_WIDTH * 3)
|
#define UI_FRAME_TEXTURE_WIDTH (UI_FRAME_TILE_WIDTH * 3)
|
||||||
|
|||||||
@@ -1,113 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "uisettings.h"
|
|
||||||
#include "ui/frame/uiframe.h"
|
|
||||||
#include "util/memory.h"
|
|
||||||
#include "display/spritebatch/spritebatch.h"
|
|
||||||
#include "display/text/text.h"
|
|
||||||
#include "display/screen/screen.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
|
|
||||||
uisettings_t UI_SETTINGS;
|
|
||||||
|
|
||||||
void uiSettingsSelected(
|
|
||||||
const uimenu_t *menu,
|
|
||||||
const uint8_t index,
|
|
||||||
const uimenuitem_t *item
|
|
||||||
) {
|
|
||||||
if(item->type == UI_MENU_WIDGET_TYPE_CHECKBOX) {
|
|
||||||
uiCheckboxToggle(&UI_SETTINGS.items[index].checkbox);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void uiSettingsClosed(const uimenu_t *menu) {
|
|
||||||
// nothing extra needed; menu clears focusItem itself
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t uiSettingsInit(void) {
|
|
||||||
memoryZero(&UI_SETTINGS, sizeof(uisettings_t));
|
|
||||||
|
|
||||||
uint8_t i = 0;
|
|
||||||
|
|
||||||
UI_SETTINGS.items[i] = (uimenuitem_t){
|
|
||||||
.type = UI_MENU_WIDGET_TYPE_LABEL,
|
|
||||||
.label = "Settings"
|
|
||||||
};
|
|
||||||
++i;
|
|
||||||
|
|
||||||
UI_SETTINGS.items[i].type = UI_MENU_WIDGET_TYPE_BUTTON;
|
|
||||||
uiButtonInit(&UI_SETTINGS.items[i].button, "Do thing");
|
|
||||||
++i;
|
|
||||||
|
|
||||||
UI_SETTINGS.items[i].type = UI_MENU_WIDGET_TYPE_BUTTON;
|
|
||||||
uiButtonInit(&UI_SETTINGS.items[i].button, "Do other thing");
|
|
||||||
++i;
|
|
||||||
|
|
||||||
UI_SETTINGS.items[i].type = UI_MENU_WIDGET_TYPE_SPACER;
|
|
||||||
++i;
|
|
||||||
|
|
||||||
UI_SETTINGS.items[i] = (uimenuitem_t){
|
|
||||||
.type = UI_MENU_WIDGET_TYPE_LABEL,
|
|
||||||
.label = "Options"
|
|
||||||
};
|
|
||||||
++i;
|
|
||||||
|
|
||||||
UI_SETTINGS.items[i].type = UI_MENU_WIDGET_TYPE_CHECKBOX;
|
|
||||||
uiCheckboxInit(&UI_SETTINGS.items[i].checkbox, "Enable thing?");
|
|
||||||
++i;
|
|
||||||
|
|
||||||
assertTrue(i <= UI_SETTINGS_ITEM_COUNT, "Settings item count mismatch");
|
|
||||||
|
|
||||||
uiMenuInit(&UI_SETTINGS.menu, uiSettingsSelected, uiSettingsClosed, NULL);
|
|
||||||
uiMenuSetItems(
|
|
||||||
&UI_SETTINGS.menu,
|
|
||||||
UI_SETTINGS.items,
|
|
||||||
i, 1
|
|
||||||
);
|
|
||||||
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t uiSettingsDraw(void) {
|
|
||||||
if(!uiMenuIsActive(&UI_SETTINGS.menu)) errorOk();
|
|
||||||
|
|
||||||
const float_t width = 300.0f;
|
|
||||||
const float_t height = 300.0f;
|
|
||||||
const float_t x = (float_t)SCREEN.scanX +
|
|
||||||
((float_t)SCREEN.scanWidth - width) * 0.5f;
|
|
||||||
const float_t y = (float_t)SCREEN.scanY +
|
|
||||||
((float_t)SCREEN.scanHeight - height) * 0.5f;
|
|
||||||
|
|
||||||
errorChain(uiFrameDraw(x, y, width, height));
|
|
||||||
errorChain(uiMenuDraw(
|
|
||||||
&UI_SETTINGS.menu,
|
|
||||||
x + UI_FRAME_BORDER_WIDTH,
|
|
||||||
y + UI_FRAME_BORDER_HEIGHT,
|
|
||||||
width - (UI_FRAME_BORDER_WIDTH * 2),
|
|
||||||
height - (UI_FRAME_BORDER_HEIGHT * 2)
|
|
||||||
));
|
|
||||||
|
|
||||||
errorChain(spriteBatchFlush());
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool_t uiSettingsIsOpen(void) {
|
|
||||||
return uiMenuIsActive(&UI_SETTINGS.menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
void uiSettingsOpen() {
|
|
||||||
uiMenuOpen(&UI_SETTINGS.menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
void uiSettingsClose() {
|
|
||||||
uiMenuClose(&UI_SETTINGS.menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t uiSettingsDispose(void) {
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
@@ -144,12 +144,12 @@ errorret_t uiTextboxDraw(
|
|||||||
) {
|
) {
|
||||||
assertNotNull(box, "Textbox cannot be NULL");
|
assertNotNull(box, "Textbox cannot be NULL");
|
||||||
|
|
||||||
float_t borderW = (float_t)UI_FRAME_BORDER_WIDTH;
|
float_t startX = (float_t)UI_FRAME_START_X;
|
||||||
float_t borderH = (float_t)UI_FRAME_BORDER_HEIGHT;
|
float_t startY = (float_t)UI_FRAME_START_Y;
|
||||||
float_t contentX = x + borderW;
|
float_t contentX = x + startX;
|
||||||
float_t contentY = y + borderH;
|
float_t contentY = y + startY;
|
||||||
float_t contentW = width - 2.0f * borderW;
|
float_t contentW = width - 2.0f * startX;
|
||||||
float_t contentH = height - 2.0f * borderH;
|
float_t contentH = height - 2.0f * startY;
|
||||||
|
|
||||||
if(contentW != box->layoutWidth || contentH != box->layoutHeight) {
|
if(contentW != box->layoutWidth || contentH != box->layoutHeight) {
|
||||||
uiTextboxBuildLayout(box, contentW, contentH);
|
uiTextboxBuildLayout(box, contentW, contentH);
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ void uiTextboxMainSetText(const char_t *text) {
|
|||||||
uiTextboxMainFocusSelected,
|
uiTextboxMainFocusSelected,
|
||||||
NULL,
|
NULL,
|
||||||
uiTextboxMainFocusClosed,
|
uiTextboxMainFocusClosed,
|
||||||
|
NULL,
|
||||||
NULL
|
NULL
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -41,7 +42,7 @@ errorret_t uiTextboxMainDraw(void) {
|
|||||||
float_t fontH = (float_t)FONT_DEFAULT.tileset->tileHeight;
|
float_t fontH = (float_t)FONT_DEFAULT.tileset->tileHeight;
|
||||||
float_t h = (float_t)UI_TEXTBOX_MAIN_LINES * fontH +
|
float_t h = (float_t)UI_TEXTBOX_MAIN_LINES * fontH +
|
||||||
(float_t)(UI_TEXTBOX_MAIN_LINES - 1) * UI_TEXTBOX_LINE_SPACING +
|
(float_t)(UI_TEXTBOX_MAIN_LINES - 1) * UI_TEXTBOX_LINE_SPACING +
|
||||||
2.0f * (float_t)UI_FRAME_BORDER_HEIGHT;
|
2.0f * (float_t)UI_FRAME_START_Y;
|
||||||
float_t w = (float_t)SCREEN.scanWidth;
|
float_t w = (float_t)SCREEN.scanWidth;
|
||||||
float_t x = (float_t)SCREEN.scanX;
|
float_t x = (float_t)SCREEN.scanX;
|
||||||
float_t y = (float_t)(SCREEN.scanY + SCREEN.scanHeight) - h;
|
float_t y = (float_t)(SCREEN.scanY + SCREEN.scanHeight) - h;
|
||||||
|
|||||||
@@ -16,7 +16,8 @@
|
|||||||
#include "ui/overlay/uicrop.h"
|
#include "ui/overlay/uicrop.h"
|
||||||
#include "ui/transition/uitransition.h"
|
#include "ui/transition/uitransition.h"
|
||||||
#include "ui/debug/uiconsole.h"
|
#include "ui/debug/uiconsole.h"
|
||||||
#include "ui/frame/uisettings.h"
|
#include "ui/frame/settings/uisettings.h"
|
||||||
|
#include "ui/frame/game/uigamemenu.h"
|
||||||
#include "ui/rpg/uitextboxmain.h"
|
#include "ui/rpg/uitextboxmain.h"
|
||||||
|
|
||||||
uielement_t UI_ELEMENTS[] = {
|
uielement_t UI_ELEMENTS[] = {
|
||||||
@@ -33,6 +34,12 @@ uielement_t UI_ELEMENTS[] = {
|
|||||||
.draw = uiFullboxUnderDraw
|
.draw = uiFullboxUnderDraw
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
.init = uiGameMenuInit,
|
||||||
|
.draw = uiGameMenuDraw,
|
||||||
|
.dispose = uiGameMenuDispose
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
.init = uiSettingsInit,
|
.init = uiSettingsInit,
|
||||||
.draw = uiSettingsDraw,
|
.draw = uiSettingsDraw,
|
||||||
|
|||||||
@@ -7,5 +7,7 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
|||||||
PUBLIC
|
PUBLIC
|
||||||
uibutton.c
|
uibutton.c
|
||||||
uicheckbox.c
|
uicheckbox.c
|
||||||
|
uitab.c
|
||||||
|
uislider.c
|
||||||
uimenu.c
|
uimenu.c
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
#include "uibutton.h"
|
#include "uibutton.h"
|
||||||
#include "assert/assert.h"
|
#include "assert/assert.h"
|
||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
#include "util/string.h"
|
|
||||||
#include "display/text/text.h"
|
#include "display/text/text.h"
|
||||||
#include "display/color.h"
|
#include "display/color.h"
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
#include "uicheckbox.h"
|
#include "uicheckbox.h"
|
||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
#include "util/string.h"
|
|
||||||
#include "display/text/text.h"
|
#include "display/text/text.h"
|
||||||
#include "display/color.h"
|
#include "display/color.h"
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,8 @@
|
|||||||
#include "display/text/text.h"
|
#include "display/text/text.h"
|
||||||
#include "display/color.h"
|
#include "display/color.h"
|
||||||
#include "ui/widget/uibutton.h"
|
#include "ui/widget/uibutton.h"
|
||||||
|
#include "ui/widget/uitab.h"
|
||||||
|
#include "ui/widget/uislider.h"
|
||||||
|
|
||||||
void uiMenuInit(
|
void uiMenuInit(
|
||||||
uimenu_t *menu,
|
uimenu_t *menu,
|
||||||
@@ -61,6 +63,7 @@ void uiMenuOpen(uimenu_t *menu) {
|
|||||||
uiMenuFocusSelected,
|
uiMenuFocusSelected,
|
||||||
uiMenuFocusChanged,
|
uiMenuFocusChanged,
|
||||||
uiMenuFocusClosed,
|
uiMenuFocusClosed,
|
||||||
|
uiMenuFocusDirection,
|
||||||
menu
|
menu
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -127,6 +130,14 @@ errorret_t uiMenuDraw(
|
|||||||
errorChain(uiButtonDraw(&item->button, ix, iy));
|
errorChain(uiButtonDraw(&item->button, ix, iy));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case UI_MENU_WIDGET_TYPE_TAB:
|
||||||
|
errorChain(uiTabDraw(&item->tab, ix, iy));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case UI_MENU_WIDGET_TYPE_SLIDER:
|
||||||
|
errorChain(uiSliderDraw(&item->slider, ix, iy));
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -166,12 +177,7 @@ bool_t uiMenuFocusChanged(const uifocusitem_t *focusItem) {
|
|||||||
item->type == UI_MENU_WIDGET_TYPE_LABEL ||
|
item->type == UI_MENU_WIDGET_TYPE_LABEL ||
|
||||||
item->type == UI_MENU_WIDGET_TYPE_SPACER
|
item->type == UI_MENU_WIDGET_TYPE_SPACER
|
||||||
) continue;
|
) continue;
|
||||||
bool_t isActive = (slot == focusSlot);
|
uiMenuItemSetHighlighted(item, slot == focusSlot);
|
||||||
if(item->type == UI_MENU_WIDGET_TYPE_CHECKBOX) {
|
|
||||||
uiCheckboxSetHighlighted(&item->checkbox, isActive);
|
|
||||||
} else if(item->type == UI_MENU_WIDGET_TYPE_BUTTON) {
|
|
||||||
uiButtonSetHighlighted(&item->button, isActive);
|
|
||||||
}
|
|
||||||
slot++;
|
slot++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,10 +194,50 @@ bool_t uiMenuFocusClosed(const uifocusitem_t *focusItem) {
|
|||||||
assertNotNull(focusItem->user, "Focus item user cannot be NULL");
|
assertNotNull(focusItem->user, "Focus item user cannot be NULL");
|
||||||
uimenu_t *menu = (uimenu_t *)focusItem->user;
|
uimenu_t *menu = (uimenu_t *)focusItem->user;
|
||||||
menu->focusItem = NULL;
|
menu->focusItem = NULL;
|
||||||
|
|
||||||
|
for(uint8_t i = 0; i < menu->itemCount; i++) {
|
||||||
|
uiMenuItemSetHighlighted(&menu->items[i], false);
|
||||||
|
}
|
||||||
|
|
||||||
if(menu->closed != NULL) menu->closed(menu);
|
if(menu->closed != NULL) menu->closed(menu);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool_t uiMenuFocusDirection(
|
||||||
|
const uifocusitem_t *focusItem,
|
||||||
|
const uifocusdirection_t direction
|
||||||
|
) {
|
||||||
|
assertNotNull(focusItem, "Focus item cannot be NULL");
|
||||||
|
assertNotNull(focusItem->user, "Focus item user cannot be NULL");
|
||||||
|
if(direction != UI_FOCUS_DIRECTION_LEFT && direction != UI_FOCUS_DIRECTION_RIGHT) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uimenu_t *menu = (uimenu_t *)focusItem->user;
|
||||||
|
uint8_t slot = focusItem->y * menu->columns + focusItem->x;
|
||||||
|
uint8_t index = uiMenuFocusSlotToIndex(menu, slot);
|
||||||
|
if(index == 0xFF) return false;
|
||||||
|
|
||||||
|
uimenuitem_t *item = &menu->items[index];
|
||||||
|
bool_t right = direction == UI_FOCUS_DIRECTION_RIGHT;
|
||||||
|
|
||||||
|
if(item->type == UI_MENU_WIDGET_TYPE_SLIDER) {
|
||||||
|
if(right) {
|
||||||
|
uiSliderStepUp(&item->slider);
|
||||||
|
} else {
|
||||||
|
uiSliderStepDown(&item->slider);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(item->type == UI_MENU_WIDGET_TYPE_CHECKBOX) {
|
||||||
|
uiCheckboxToggle(&item->checkbox);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t uiMenuFocusableCount(const uimenu_t *menu) {
|
uint8_t uiMenuFocusableCount(const uimenu_t *menu) {
|
||||||
assertNotNull(menu, "Menu cannot be NULL");
|
assertNotNull(menu, "Menu cannot be NULL");
|
||||||
uint8_t count = 0;
|
uint8_t count = 0;
|
||||||
@@ -213,3 +259,15 @@ uint8_t uiMenuFocusSlotToIndex(const uimenu_t *menu, const uint8_t slot) {
|
|||||||
}
|
}
|
||||||
return 0xFF;
|
return 0xFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void uiMenuItemSetHighlighted(uimenuitem_t *item, const bool_t highlighted) {
|
||||||
|
if(item->type == UI_MENU_WIDGET_TYPE_CHECKBOX) {
|
||||||
|
uiCheckboxSetHighlighted(&item->checkbox, highlighted);
|
||||||
|
} else if(item->type == UI_MENU_WIDGET_TYPE_BUTTON) {
|
||||||
|
uiButtonSetHighlighted(&item->button, highlighted);
|
||||||
|
} else if(item->type == UI_MENU_WIDGET_TYPE_TAB) {
|
||||||
|
uiTabSetActive(&item->tab, highlighted);
|
||||||
|
} else if(item->type == UI_MENU_WIDGET_TYPE_SLIDER) {
|
||||||
|
uiSliderSetHighlighted(&item->slider, highlighted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
#include "ui/focus/uifocus.h"
|
#include "ui/focus/uifocus.h"
|
||||||
#include "ui/widget/uibutton.h"
|
#include "ui/widget/uibutton.h"
|
||||||
#include "ui/widget/uicheckbox.h"
|
#include "ui/widget/uicheckbox.h"
|
||||||
|
#include "ui/widget/uitab.h"
|
||||||
|
#include "ui/widget/uislider.h"
|
||||||
|
|
||||||
#define UI_MENU_LABEL_MAX UI_CHECKBOX_LABEL_MAX
|
#define UI_MENU_LABEL_MAX UI_CHECKBOX_LABEL_MAX
|
||||||
|
|
||||||
@@ -21,6 +23,8 @@ typedef enum {
|
|||||||
UI_MENU_WIDGET_TYPE_SPACER,
|
UI_MENU_WIDGET_TYPE_SPACER,
|
||||||
UI_MENU_WIDGET_TYPE_CHECKBOX,
|
UI_MENU_WIDGET_TYPE_CHECKBOX,
|
||||||
UI_MENU_WIDGET_TYPE_BUTTON,
|
UI_MENU_WIDGET_TYPE_BUTTON,
|
||||||
|
UI_MENU_WIDGET_TYPE_TAB,
|
||||||
|
UI_MENU_WIDGET_TYPE_SLIDER,
|
||||||
} uimenuwidgettype_t;
|
} uimenuwidgettype_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -29,6 +33,8 @@ typedef struct {
|
|||||||
char_t *label;
|
char_t *label;
|
||||||
uicheckbox_t checkbox;
|
uicheckbox_t checkbox;
|
||||||
uibutton_t button;
|
uibutton_t button;
|
||||||
|
uitab_t tab;
|
||||||
|
uislider_t slider;
|
||||||
};
|
};
|
||||||
} uimenuitem_t;
|
} uimenuitem_t;
|
||||||
|
|
||||||
@@ -157,6 +163,16 @@ uint8_t uiMenuFocusableCount(const uimenu_t *menu);
|
|||||||
*/
|
*/
|
||||||
uint8_t uiMenuFocusSlotToIndex(const uimenu_t *menu, const uint8_t slot);
|
uint8_t uiMenuFocusSlotToIndex(const uimenu_t *menu, const uint8_t slot);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the highlighted/active state on a menu item, dispatching to the
|
||||||
|
* matching widget's setter based on its type. No-op for label/spacer
|
||||||
|
* items and any other type with no highlight concept.
|
||||||
|
*
|
||||||
|
* @param item The item to update.
|
||||||
|
* @param highlighted The new highlighted/active state.
|
||||||
|
*/
|
||||||
|
void uiMenuItemSetHighlighted(uimenuitem_t *item, const bool_t highlighted);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal focus callback - forwards selection to the menu's selected handler.
|
* Internal focus callback - forwards selection to the menu's selected handler.
|
||||||
*
|
*
|
||||||
@@ -179,4 +195,59 @@ bool_t uiMenuFocusChanged(const uifocusitem_t *focusItem);
|
|||||||
* @param focusItem The active focus item; user field must point to uimenu_t.
|
* @param focusItem The active focus item; user field must point to uimenu_t.
|
||||||
* @returns True.
|
* @returns True.
|
||||||
*/
|
*/
|
||||||
bool_t uiMenuFocusClosed(const uifocusitem_t *focusItem);
|
bool_t uiMenuFocusClosed(const uifocusitem_t *focusItem);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal focus callback - gives the focused item's widget a chance to
|
||||||
|
* handle LEFT/RIGHT itself (e.g. a slider adjusting its value) before
|
||||||
|
* falling back to the default cell-to-cell movement.
|
||||||
|
*
|
||||||
|
* @param focusItem The active focus item; user field must point to uimenu_t.
|
||||||
|
* @param direction The direction that was pressed or held.
|
||||||
|
* @returns True if the focused widget handled the direction.
|
||||||
|
*/
|
||||||
|
bool_t uiMenuFocusDirection(
|
||||||
|
const uifocusitem_t *focusItem,
|
||||||
|
const uifocusdirection_t direction
|
||||||
|
);
|
||||||
|
|
||||||
|
// Helper macros
|
||||||
|
#define MENU_BEGIN uint8_t menuIndex = 0;
|
||||||
|
|
||||||
|
#define MENU_LABEL(text) \
|
||||||
|
menu->items[menuIndex].type = UI_MENU_WIDGET_TYPE_LABEL; \
|
||||||
|
menu->items[menuIndex].label = text; \
|
||||||
|
++menuIndex;
|
||||||
|
|
||||||
|
#define MENU_SPACER() \
|
||||||
|
menu->items[menuIndex].type = UI_MENU_WIDGET_TYPE_SPACER; \
|
||||||
|
++menuIndex;
|
||||||
|
|
||||||
|
#define MENU_CHECKBOX(label) \
|
||||||
|
menu->items[menuIndex].type = UI_MENU_WIDGET_TYPE_CHECKBOX; \
|
||||||
|
uiCheckboxInit(&menu->items[menuIndex].checkbox, label); \
|
||||||
|
++menuIndex;
|
||||||
|
|
||||||
|
#define MENU_BUTTON(label) \
|
||||||
|
menu->items[menuIndex].type = UI_MENU_WIDGET_TYPE_BUTTON; \
|
||||||
|
uiButtonInit(&menu->items[menuIndex].button, label); \
|
||||||
|
++menuIndex;
|
||||||
|
|
||||||
|
#define MENU_TAB(label) \
|
||||||
|
menu->items[menuIndex].type = UI_MENU_WIDGET_TYPE_TAB; \
|
||||||
|
uiTabInit(&menu->items[menuIndex].tab, label); \
|
||||||
|
++menuIndex;
|
||||||
|
|
||||||
|
#define MENU_SLIDER_FLOAT(label, value, min, max, step) \
|
||||||
|
menu->items[menuIndex].type = UI_MENU_WIDGET_TYPE_SLIDER; \
|
||||||
|
uiSliderInitFloat( \
|
||||||
|
&menu->items[menuIndex].slider, label, value, min, max, step \
|
||||||
|
); \
|
||||||
|
++menuIndex;
|
||||||
|
|
||||||
|
#define MENU_SLIDER_INT(label, value, min, max, step) \
|
||||||
|
menu->items[menuIndex].type = UI_MENU_WIDGET_TYPE_SLIDER; \
|
||||||
|
uiSliderInitInt( \
|
||||||
|
&menu->items[menuIndex].slider, label, value, min, max, step \
|
||||||
|
); \
|
||||||
|
++menuIndex;
|
||||||
|
|||||||
@@ -0,0 +1,242 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "uislider.h"
|
||||||
|
#include "assert/assert.h"
|
||||||
|
#include "util/memory.h"
|
||||||
|
#include "util/math.h"
|
||||||
|
#include "util/string.h"
|
||||||
|
#include "display/text/text.h"
|
||||||
|
#include "display/color.h"
|
||||||
|
#include "display/spritebatch/spritebatch.h"
|
||||||
|
#include "display/texture/texture.h"
|
||||||
|
#include "display/shader/shaderunlit.h"
|
||||||
|
|
||||||
|
void uiSliderInitFloat(
|
||||||
|
uislider_t *slider,
|
||||||
|
const char_t *label,
|
||||||
|
const float_t value,
|
||||||
|
const float_t min,
|
||||||
|
const float_t max,
|
||||||
|
const float_t step
|
||||||
|
) {
|
||||||
|
assertNotNull(slider, "Slider cannot be NULL");
|
||||||
|
assertNotNull(label, "Label cannot be NULL");
|
||||||
|
assertTrue(max > min, "Slider max must be greater than min");
|
||||||
|
assertTrue(step > 0.0f, "Slider step must be greater than zero");
|
||||||
|
|
||||||
|
memoryZero(slider, sizeof(uislider_t));
|
||||||
|
slider->label = label;
|
||||||
|
slider->type = UI_SLIDER_TYPE_FLOAT;
|
||||||
|
slider->min.f = min;
|
||||||
|
slider->max.f = max;
|
||||||
|
slider->step.f = step;
|
||||||
|
slider->value.f = mathClamp(value, min, max);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiSliderInitInt(
|
||||||
|
uislider_t *slider,
|
||||||
|
const char_t *label,
|
||||||
|
const int32_t value,
|
||||||
|
const int32_t min,
|
||||||
|
const int32_t max,
|
||||||
|
const int32_t step
|
||||||
|
) {
|
||||||
|
assertNotNull(slider, "Slider cannot be NULL");
|
||||||
|
assertNotNull(label, "Label cannot be NULL");
|
||||||
|
assertTrue(max > min, "Slider max must be greater than min");
|
||||||
|
assertTrue(step > 0, "Slider step must be greater than zero");
|
||||||
|
|
||||||
|
memoryZero(slider, sizeof(uislider_t));
|
||||||
|
slider->label = label;
|
||||||
|
slider->type = UI_SLIDER_TYPE_INT;
|
||||||
|
slider->min.i = min;
|
||||||
|
slider->max.i = max;
|
||||||
|
slider->step.i = step;
|
||||||
|
slider->value.i = mathClamp(value, min, max);
|
||||||
|
}
|
||||||
|
|
||||||
|
float_t uiSliderGetFloat(const uislider_t *slider) {
|
||||||
|
assertNotNull(slider, "Slider cannot be NULL");
|
||||||
|
return slider->type == UI_SLIDER_TYPE_INT ?
|
||||||
|
(float_t)slider->value.i : slider->value.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t uiSliderGetInt(const uislider_t *slider) {
|
||||||
|
assertNotNull(slider, "Slider cannot be NULL");
|
||||||
|
assertTrue(
|
||||||
|
slider->type == UI_SLIDER_TYPE_INT, "Slider is not an int slider"
|
||||||
|
);
|
||||||
|
return slider->value.i;
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiSliderSetFloat(uislider_t *slider, const float_t value) {
|
||||||
|
assertNotNull(slider, "Slider cannot be NULL");
|
||||||
|
assertTrue(
|
||||||
|
slider->type == UI_SLIDER_TYPE_FLOAT, "Slider is not a float slider"
|
||||||
|
);
|
||||||
|
slider->value.f = mathClamp(value, slider->min.f, slider->max.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiSliderSetInt(uislider_t *slider, const int32_t value) {
|
||||||
|
assertNotNull(slider, "Slider cannot be NULL");
|
||||||
|
assertTrue(
|
||||||
|
slider->type == UI_SLIDER_TYPE_INT, "Slider is not an int slider"
|
||||||
|
);
|
||||||
|
slider->value.i = mathClamp(value, slider->min.i, slider->max.i);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiSliderStepUp(uislider_t *slider) {
|
||||||
|
assertNotNull(slider, "Slider cannot be NULL");
|
||||||
|
if(slider->type == UI_SLIDER_TYPE_INT) {
|
||||||
|
int32_t next = slider->value.i + slider->step.i;
|
||||||
|
uiSliderSetInt(slider, next > slider->max.i ? slider->min.i : next);
|
||||||
|
} else {
|
||||||
|
float_t next = slider->value.f + slider->step.f;
|
||||||
|
uiSliderSetFloat(slider, next > slider->max.f ? slider->min.f : next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiSliderStepDown(uislider_t *slider) {
|
||||||
|
assertNotNull(slider, "Slider cannot be NULL");
|
||||||
|
if(slider->type == UI_SLIDER_TYPE_INT) {
|
||||||
|
int32_t next = slider->value.i - slider->step.i;
|
||||||
|
uiSliderSetInt(slider, next < slider->min.i ? slider->max.i : next);
|
||||||
|
} else {
|
||||||
|
float_t next = slider->value.f - slider->step.f;
|
||||||
|
uiSliderSetFloat(slider, next < slider->min.f ? slider->max.f : next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float_t uiSliderGetRatio(const uislider_t *slider) {
|
||||||
|
assertNotNull(slider, "Slider cannot be NULL");
|
||||||
|
if(slider->type == UI_SLIDER_TYPE_INT) {
|
||||||
|
if(slider->max.i == slider->min.i) return 0.0f;
|
||||||
|
return (float_t)(slider->value.i - slider->min.i) /
|
||||||
|
(float_t)(slider->max.i - slider->min.i);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(slider->max.f == slider->min.f) return 0.0f;
|
||||||
|
return (slider->value.f - slider->min.f) / (slider->max.f - slider->min.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t uiSliderGetStepCount(const uislider_t *slider) {
|
||||||
|
assertNotNull(slider, "Slider cannot be NULL");
|
||||||
|
if(slider->type != UI_SLIDER_TYPE_INT || slider->step.i <= 0) return 0;
|
||||||
|
return (slider->max.i - slider->min.i) / slider->step.i;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool_t uiSliderIsHighlighted(const uislider_t *slider) {
|
||||||
|
assertNotNull(slider, "Slider cannot be NULL");
|
||||||
|
return slider->highlighted;
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiSliderSetHighlighted(uislider_t *slider, const bool_t highlighted) {
|
||||||
|
assertNotNull(slider, "Slider cannot be NULL");
|
||||||
|
slider->highlighted = highlighted;
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t uiSliderDraw(
|
||||||
|
const uislider_t *slider,
|
||||||
|
const float_t x,
|
||||||
|
const float_t y
|
||||||
|
) {
|
||||||
|
assertNotNull(slider, "Slider cannot be NULL");
|
||||||
|
|
||||||
|
color_t color = slider->highlighted ? COLOR_RED : COLOR_WHITE;
|
||||||
|
|
||||||
|
errorChain(textDraw(x, y, slider->label, color, &FONT_DEFAULT));
|
||||||
|
|
||||||
|
int32_t labelW, labelH;
|
||||||
|
textMeasure(slider->label, &FONT_DEFAULT, &labelW, &labelH);
|
||||||
|
|
||||||
|
float_t trackX = x + (float_t)labelW + UI_SLIDER_GAP;
|
||||||
|
float_t trackY = y + ((float_t)labelH - UI_SLIDER_TRACK_HEIGHT) * 0.5f;
|
||||||
|
|
||||||
|
spritebatchsprite_t trackSprite = {
|
||||||
|
.min = { trackX, trackY, 0.0f },
|
||||||
|
.max = { trackX + UI_SLIDER_TRACK_WIDTH, trackY + UI_SLIDER_TRACK_HEIGHT, 0.0f },
|
||||||
|
.uvMin = { 0.0f, 0.0f },
|
||||||
|
.uvMax = { 1.0f, 1.0f }
|
||||||
|
};
|
||||||
|
shadermaterial_t trackMaterial = {
|
||||||
|
.unlit = {
|
||||||
|
.color = COLOR_DARK_GRAY,
|
||||||
|
.texture = &TEXTURE_WHITE
|
||||||
|
}
|
||||||
|
};
|
||||||
|
errorChain(spriteBatchBuffer(&trackSprite, 1, &SHADER_UNLIT, trackMaterial));
|
||||||
|
errorChain(spriteBatchFlush());
|
||||||
|
|
||||||
|
float_t fillWidth = UI_SLIDER_TRACK_WIDTH * uiSliderGetRatio(slider);
|
||||||
|
if(fillWidth > 0.0f) {
|
||||||
|
spritebatchsprite_t fillSprite = {
|
||||||
|
.min = { trackX, trackY, 0.0f },
|
||||||
|
.max = { trackX + fillWidth, trackY + UI_SLIDER_TRACK_HEIGHT, 0.0f },
|
||||||
|
.uvMin = { 0.0f, 0.0f },
|
||||||
|
.uvMax = { 1.0f, 1.0f }
|
||||||
|
};
|
||||||
|
shadermaterial_t fillMaterial = {
|
||||||
|
.unlit = {
|
||||||
|
.color = color,
|
||||||
|
.texture = &TEXTURE_WHITE
|
||||||
|
}
|
||||||
|
};
|
||||||
|
errorChain(spriteBatchBuffer(&fillSprite, 1, &SHADER_UNLIT, fillMaterial));
|
||||||
|
errorChain(spriteBatchFlush());
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t stepCount = uiSliderGetStepCount(slider);
|
||||||
|
if(stepCount > 0 && stepCount < UI_SLIDER_STEP_MARKERS_MAX) {
|
||||||
|
for(int32_t i = 0; i <= stepCount; i++) {
|
||||||
|
float_t markerX = trackX +
|
||||||
|
UI_SLIDER_TRACK_WIDTH * (float_t)i / (float_t)stepCount;
|
||||||
|
spritebatchsprite_t markerSprite = {
|
||||||
|
.min = {
|
||||||
|
markerX - UI_SLIDER_STEP_MARKER_WIDTH * 0.5f,
|
||||||
|
trackY - UI_SLIDER_STEP_MARKER_OVERHANG,
|
||||||
|
0.0f
|
||||||
|
},
|
||||||
|
.max = {
|
||||||
|
markerX + UI_SLIDER_STEP_MARKER_WIDTH * 0.5f,
|
||||||
|
trackY + UI_SLIDER_TRACK_HEIGHT + UI_SLIDER_STEP_MARKER_OVERHANG,
|
||||||
|
0.0f
|
||||||
|
},
|
||||||
|
.uvMin = { 0.0f, 0.0f },
|
||||||
|
.uvMax = { 1.0f, 1.0f }
|
||||||
|
};
|
||||||
|
shadermaterial_t markerMaterial = {
|
||||||
|
.unlit = {
|
||||||
|
.color = COLOR_WHITE,
|
||||||
|
.texture = &TEXTURE_WHITE
|
||||||
|
}
|
||||||
|
};
|
||||||
|
errorChain(
|
||||||
|
spriteBatchBuffer(&markerSprite, 1, &SHADER_UNLIT, markerMaterial)
|
||||||
|
);
|
||||||
|
errorChain(spriteBatchFlush());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char_t valueText[UI_SLIDER_VALUE_TEXT_MAX];
|
||||||
|
if(slider->type == UI_SLIDER_TYPE_INT) {
|
||||||
|
stringFormat(
|
||||||
|
valueText, UI_SLIDER_VALUE_TEXT_MAX - 1, "%d", slider->value.i
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
stringFormat(
|
||||||
|
valueText, UI_SLIDER_VALUE_TEXT_MAX - 1, "%.2f", slider->value.f
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
errorChain(textDraw(
|
||||||
|
trackX + UI_SLIDER_TRACK_WIDTH + UI_SLIDER_GAP, y,
|
||||||
|
valueText, color, &FONT_DEFAULT
|
||||||
|
));
|
||||||
|
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
@@ -0,0 +1,185 @@
|
|||||||
|
/**
|
||||||
|
* 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_SLIDER_TRACK_WIDTH 80.0f
|
||||||
|
#define UI_SLIDER_TRACK_HEIGHT 4.0f
|
||||||
|
#define UI_SLIDER_STEP_MARKER_WIDTH 2.0f
|
||||||
|
#define UI_SLIDER_STEP_MARKER_OVERHANG 1.0f
|
||||||
|
#define UI_SLIDER_GAP 4.0f
|
||||||
|
#define UI_SLIDER_VALUE_TEXT_MAX 16
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of discrete steps below which an int slider renders individual
|
||||||
|
* step markers on its track instead of a smooth fill.
|
||||||
|
*/
|
||||||
|
#define UI_SLIDER_STEP_MARKERS_MAX 10
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
UI_SLIDER_TYPE_FLOAT,
|
||||||
|
UI_SLIDER_TYPE_INT,
|
||||||
|
|
||||||
|
UI_SLIDER_TYPE_COUNT
|
||||||
|
} uislidertype_t;
|
||||||
|
|
||||||
|
typedef union {
|
||||||
|
float_t f;
|
||||||
|
int32_t i;
|
||||||
|
} uislidervalue_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char_t *label;
|
||||||
|
uislidertype_t type;
|
||||||
|
uislidervalue_t value;
|
||||||
|
uislidervalue_t min;
|
||||||
|
uislidervalue_t max;
|
||||||
|
uislidervalue_t step;
|
||||||
|
bool_t highlighted;
|
||||||
|
} uislider_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes a floating point slider.
|
||||||
|
*
|
||||||
|
* @param slider The slider to initialize.
|
||||||
|
* @param label Display label.
|
||||||
|
* @param value Initial value, clamped to [min, max].
|
||||||
|
* @param min Minimum value.
|
||||||
|
* @param max Maximum value.
|
||||||
|
* @param step The amount a single step moves the value by.
|
||||||
|
*/
|
||||||
|
void uiSliderInitFloat(
|
||||||
|
uislider_t *slider,
|
||||||
|
const char_t *label,
|
||||||
|
const float_t value,
|
||||||
|
const float_t min,
|
||||||
|
const float_t max,
|
||||||
|
const float_t step
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes an integer slider.
|
||||||
|
*
|
||||||
|
* @param slider The slider to initialize.
|
||||||
|
* @param label Display label.
|
||||||
|
* @param value Initial value, clamped to [min, max].
|
||||||
|
* @param min Minimum value.
|
||||||
|
* @param max Maximum value.
|
||||||
|
* @param step The amount a single step moves the value by.
|
||||||
|
*/
|
||||||
|
void uiSliderInitInt(
|
||||||
|
uislider_t *slider,
|
||||||
|
const char_t *label,
|
||||||
|
const int32_t value,
|
||||||
|
const int32_t min,
|
||||||
|
const int32_t max,
|
||||||
|
const int32_t step
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the slider's current value as a float. Works for both slider
|
||||||
|
* types; int values are widened to float.
|
||||||
|
*
|
||||||
|
* @param slider The slider to query.
|
||||||
|
* @returns The current value.
|
||||||
|
*/
|
||||||
|
float_t uiSliderGetFloat(const uislider_t *slider);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the slider's current value as an int. Only valid for
|
||||||
|
* UI_SLIDER_TYPE_INT sliders.
|
||||||
|
*
|
||||||
|
* @param slider The slider to query.
|
||||||
|
* @returns The current value.
|
||||||
|
*/
|
||||||
|
int32_t uiSliderGetInt(const uislider_t *slider);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the slider's value, clamped to [min, max]. Only valid for
|
||||||
|
* UI_SLIDER_TYPE_FLOAT sliders.
|
||||||
|
*
|
||||||
|
* @param slider The slider to update.
|
||||||
|
* @param value The new value.
|
||||||
|
*/
|
||||||
|
void uiSliderSetFloat(uislider_t *slider, const float_t value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the slider's value, clamped to [min, max]. Only valid for
|
||||||
|
* UI_SLIDER_TYPE_INT sliders.
|
||||||
|
*
|
||||||
|
* @param slider The slider to update.
|
||||||
|
* @param value The new value.
|
||||||
|
*/
|
||||||
|
void uiSliderSetInt(uislider_t *slider, const int32_t value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves the slider's value up by one step, wrapping around to its min
|
||||||
|
* if the step would exceed its max.
|
||||||
|
*
|
||||||
|
* @param slider The slider to update.
|
||||||
|
*/
|
||||||
|
void uiSliderStepUp(uislider_t *slider);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves the slider's value down by one step, wrapping around to its
|
||||||
|
* max if the step would go below its min.
|
||||||
|
*
|
||||||
|
* @param slider The slider to update.
|
||||||
|
*/
|
||||||
|
void uiSliderStepDown(uislider_t *slider);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the slider's current value normalized to a 0..1 range based
|
||||||
|
* on its min/max.
|
||||||
|
*
|
||||||
|
* @param slider The slider to query.
|
||||||
|
* @returns The normalized value.
|
||||||
|
*/
|
||||||
|
float_t uiSliderGetRatio(const uislider_t *slider);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of discrete steps between min and max. Always 0
|
||||||
|
* for float sliders.
|
||||||
|
*
|
||||||
|
* @param slider The slider to query.
|
||||||
|
* @returns The step count.
|
||||||
|
*/
|
||||||
|
int32_t uiSliderGetStepCount(const uislider_t *slider);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the slider is highlighted.
|
||||||
|
*
|
||||||
|
* @param slider The slider to query.
|
||||||
|
* @returns True if highlighted.
|
||||||
|
*/
|
||||||
|
bool_t uiSliderIsHighlighted(const uislider_t *slider);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the highlighted state of the slider.
|
||||||
|
*
|
||||||
|
* @param slider The slider to update.
|
||||||
|
* @param highlighted The new highlighted state.
|
||||||
|
*/
|
||||||
|
void uiSliderSetHighlighted(uislider_t *slider, const bool_t highlighted);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws the slider at the given screen position: label, then track,
|
||||||
|
* then the current value as text. Int sliders with fewer than
|
||||||
|
* UI_SLIDER_STEP_MARKERS_MAX discrete steps render a marker per step
|
||||||
|
* instead of a plain track.
|
||||||
|
*
|
||||||
|
* @param slider The slider to draw.
|
||||||
|
* @param x Screen x position.
|
||||||
|
* @param y Screen y position.
|
||||||
|
* @return Any error that occurs.
|
||||||
|
*/
|
||||||
|
errorret_t uiSliderDraw(
|
||||||
|
const uislider_t *slider,
|
||||||
|
const float_t x,
|
||||||
|
const float_t y
|
||||||
|
);
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "uitab.h"
|
||||||
|
#include "assert/assert.h"
|
||||||
|
#include "util/memory.h"
|
||||||
|
#include "display/text/text.h"
|
||||||
|
#include "display/color.h"
|
||||||
|
#include "display/spritebatch/spritebatch.h"
|
||||||
|
#include "display/texture/texture.h"
|
||||||
|
#include "display/shader/shaderunlit.h"
|
||||||
|
|
||||||
|
void uiTabInit(uitab_t *tab, const char_t *label) {
|
||||||
|
assertNotNull(tab, "Tab cannot be NULL");
|
||||||
|
assertNotNull(label, "Label cannot be NULL");
|
||||||
|
memoryZero(tab, sizeof(uitab_t));
|
||||||
|
tab->label = label;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool_t uiTabIsActive(const uitab_t *tab) {
|
||||||
|
assertNotNull(tab, "Tab cannot be NULL");
|
||||||
|
return tab->active;
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiTabSetActive(uitab_t *tab, const bool_t active) {
|
||||||
|
assertNotNull(tab, "Tab cannot be NULL");
|
||||||
|
tab->active = active;
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t uiTabDraw(
|
||||||
|
const uitab_t *tab,
|
||||||
|
const float_t x,
|
||||||
|
const float_t y
|
||||||
|
) {
|
||||||
|
assertNotNull(tab, "Tab cannot be NULL");
|
||||||
|
|
||||||
|
int32_t labelW, labelH;
|
||||||
|
textMeasure(tab->label, &FONT_DEFAULT, &labelW, &labelH);
|
||||||
|
|
||||||
|
spritebatchsprite_t sprite = {
|
||||||
|
.min = { x, y, 0.0f },
|
||||||
|
.max = { x + (float_t)labelW, y + (float_t)labelH, 0.0f },
|
||||||
|
.uvMin = { 0.0f, 0.0f },
|
||||||
|
.uvMax = { 1.0f, 1.0f }
|
||||||
|
};
|
||||||
|
shadermaterial_t material = {
|
||||||
|
.unlit = {
|
||||||
|
.color = tab->active ? COLOR_GREEN : COLOR_RED,
|
||||||
|
.texture = &TEXTURE_WHITE
|
||||||
|
}
|
||||||
|
};
|
||||||
|
errorChain(spriteBatchBuffer(&sprite, 1, &SHADER_UNLIT, material));
|
||||||
|
errorChain(spriteBatchFlush());
|
||||||
|
|
||||||
|
errorChain(textDraw(x, y, tab->label, COLOR_WHITE, &FONT_DEFAULT));
|
||||||
|
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "error/error.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char_t *label;
|
||||||
|
bool_t active;
|
||||||
|
} uitab_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes a tab.
|
||||||
|
*
|
||||||
|
* @param tab The tab to initialize.
|
||||||
|
* @param label Display label.
|
||||||
|
*/
|
||||||
|
void uiTabInit(uitab_t *tab, const char_t *label);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the tab is active.
|
||||||
|
*
|
||||||
|
* @param tab The tab to query.
|
||||||
|
* @returns True if active.
|
||||||
|
*/
|
||||||
|
bool_t uiTabIsActive(const uitab_t *tab);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the active state of the tab.
|
||||||
|
*
|
||||||
|
* @param tab The tab to update.
|
||||||
|
* @param active The new active state.
|
||||||
|
*/
|
||||||
|
void uiTabSetActive(uitab_t *tab, const bool_t active);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws the tab at the given screen position. Active tabs draw with a
|
||||||
|
* green background; inactive tabs draw with a red background.
|
||||||
|
*
|
||||||
|
* @param tab The tab to draw.
|
||||||
|
* @param x Screen x position.
|
||||||
|
* @param y Screen y position.
|
||||||
|
* @return Any error that occurs.
|
||||||
|
*/
|
||||||
|
errorret_t uiTabDraw(
|
||||||
|
const uitab_t *tab,
|
||||||
|
const float_t x,
|
||||||
|
const float_t y
|
||||||
|
);
|
||||||
Reference in New Issue
Block a user