Some label fixes

This commit is contained in:
2026-06-25 19:56:14 -05:00
parent d85737cc08
commit 722fe2ccfb
30 changed files with 290 additions and 105 deletions
+29 -14
View File
@@ -18,7 +18,7 @@ void uiSettingsSelected(
const uint8_t index,
const uimenuitem_t *item
) {
if(item->type == UIMENU_WIDGET_TYPE_CHECKBOX) {
if(item->type == UI_MENU_WIDGET_TYPE_CHECKBOX) {
uiCheckboxToggle(&UI_SETTINGS.items[index].checkbox);
}
}
@@ -30,27 +30,42 @@ void uiSettingsClosed(const uimenu_t *menu) {
errorret_t uiSettingsInit(void) {
memoryZero(&UI_SETTINGS, sizeof(uisettings_t));
UI_SETTINGS.items[0] = (uimenuitem_t){
.type = UIMENU_WIDGET_TYPE_LABEL,
uint8_t i = 0;
UI_SETTINGS.items[i] = (uimenuitem_t){
.type = UI_MENU_WIDGET_TYPE_LABEL,
.label = "Settings"
};
UI_SETTINGS.items[1] = (uimenuitem_t){
.type = UIMENU_WIDGET_TYPE_LABEL,
.label = "Settings two"
++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"
};
UI_SETTINGS.items[2] = (uimenuitem_t){
.type = UIMENU_WIDGET_TYPE_LABEL,
.label = "Settings three"
};
UI_SETTINGS.items[3].type = UIMENU_WIDGET_TYPE_CHECKBOX;
uiCheckboxInit(&UI_SETTINGS.items[3].checkbox, "Enable thing?");
++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,
UI_SETTINGS_ITEM_COUNT,
2
i, 1
);
errorOk();
+1 -1
View File
@@ -9,7 +9,7 @@
#include "error/error.h"
#include "ui/widget/uimenu.h"
#define UI_SETTINGS_ITEM_COUNT 4
#define UI_SETTINGS_ITEM_COUNT 6
typedef struct {
uimenu_t menu;
+1
View File
@@ -5,6 +5,7 @@
target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
uibutton.c
uicheckbox.c
uimenu.c
)
+44
View File
@@ -0,0 +1,44 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "uibutton.h"
#include "assert/assert.h"
#include "util/memory.h"
#include "util/string.h"
#include "display/text/text.h"
#include "display/color.h"
void uiButtonInit(uibutton_t *button, const char_t *label) {
assertNotNull(button, "Button cannot be NULL");
assertNotNull(label, "Label cannot be NULL");
memoryZero(button, sizeof(uibutton_t));
button->label = label;
}
bool_t uiButtonIsHighlighted(const uibutton_t *button) {
assertNotNull(button, "Button cannot be NULL");
return button->highlighted;
}
void uiButtonSetHighlighted(uibutton_t *button, const bool_t highlighted) {
assertNotNull(button, "Button cannot be NULL");
button->highlighted = highlighted;
}
errorret_t uiButtonDraw(
const uibutton_t *button,
const float_t x,
const float_t y
) {
assertNotNull(button, "Button cannot be NULL");
errorChain(textDraw(
x, y, button->label,
button->highlighted ? COLOR_RED : COLOR_WHITE,
&FONT_DEFAULT
));
errorOk();
}
+54
View File
@@ -0,0 +1,54 @@
/**
* 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_BUTTON_LABEL_MAX 32
typedef struct {
const char_t *label;
bool_t highlighted;
} uibutton_t;
/**
* Initializes a button.
*
* @param button The button to initialize.
* @param label Display label.
*/
void uiButtonInit(uibutton_t *button, const char_t *label);
/**
* Returns whether the button is highlighted.
*
* @param button The button to query.
* @returns True if highlighted.
*/
bool_t uiButtonIsHighlighted(const uibutton_t *button);
/**
* Sets the highlighted state of the button.
*
* @param button The button to update.
* @param highlighted The new highlighted state.
*/
void uiButtonSetHighlighted(uibutton_t *button, const bool_t highlighted);
/**
* Draws the button at the given screen position.
*
* @param button The button to draw.
* @param x Screen x position.
* @param y Screen y position.
* @return Any error that occurs.
*/
errorret_t uiButtonDraw(
const uibutton_t *button,
const float_t x,
const float_t y
);
+1 -1
View File
@@ -16,7 +16,7 @@ void uiCheckboxInit(
const char_t *label
) {
memoryZero(checkbox, sizeof(uicheckbox_t));
stringCopy(checkbox->label, label, UI_CHECKBOX_LABEL_MAX);
checkbox->label = label;
}
bool_t uiCheckboxIsChecked(const uicheckbox_t *checkbox) {
+2 -4
View File
@@ -8,10 +8,8 @@
#pragma once
#include "error/error.h"
#define UI_CHECKBOX_LABEL_MAX 32
typedef struct uicheckbox_s {
char_t label[UI_CHECKBOX_LABEL_MAX];
const char_t *label;
bool_t checked;
bool_t highlighted;
} uicheckbox_t;
@@ -20,7 +18,7 @@ typedef struct uicheckbox_s {
* Initializes a checkbox.
*
* @param checkbox The checkbox to initialize.
* @param label Display label, truncated to UI_CHECKBOX_LABEL_MAX - 1 chars.
* @param label Display label.
*/
void uiCheckboxInit(
uicheckbox_t *checkbox,
+73 -21
View File
@@ -10,6 +10,7 @@
#include "util/memory.h"
#include "display/text/text.h"
#include "display/color.h"
#include "ui/widget/uibutton.h"
void uiMenuInit(
uimenu_t *menu,
@@ -52,7 +53,9 @@ void uiMenuOpen(uimenu_t *menu) {
assertTrue(menu->columns > 0, "Menu columns must be > 0");
if(menu->focusItem != NULL) return;
uint8_t rows = (menu->itemCount + menu->columns - 1) / menu->columns;
uint8_t focusable = uiMenuFocusableCount(menu);
uint8_t rows = focusable > 0 ?
(focusable + menu->columns - 1) / menu->columns : 1;
menu->focusItem = uiFocusPush(
menu->columns, rows,
uiMenuFocusSelected,
@@ -87,34 +90,49 @@ errorret_t uiMenuDraw(
float_t colStep = width / (float_t)menu->columns;
float_t rowHeight = (float_t)FONT_DEFAULT.tileset->tileHeight;
uint8_t focusIndex = menu->focusItem != NULL ?
menu->focusItem->y * menu->columns + menu->focusItem->x :
0xFF;
uint8_t col = 0;
uint8_t row = 0;
for(uint8_t i = 0; i < menu->itemCount; i++) {
const uimenuitem_t *item = &menu->items[i];
uint8_t col = i % menu->columns;
uint8_t row = i / menu->columns;
if(item->type == UI_MENU_WIDGET_TYPE_LABEL) {
if(col > 0) { row++; col = 0; }
errorChain(textDraw(
x,
y + (float_t)row * rowHeight,
item->label,
COLOR_WHITE,
&FONT_DEFAULT
));
row++;
continue;
}
if(item->type == UI_MENU_WIDGET_TYPE_SPACER) {
if(col > 0) { row++; col = 0; }
row++;
continue;
}
float_t ix = x + (float_t)col * colStep;
float_t iy = y + (float_t)row * rowHeight;
bool_t highlighted = i == focusIndex;
switch(item->type) {
case UIMENU_WIDGET_TYPE_LABEL:
errorChain(textDraw(
ix, iy, item->label,
highlighted ? COLOR_RED : COLOR_WHITE,
&FONT_DEFAULT
));
case UI_MENU_WIDGET_TYPE_CHECKBOX:
errorChain(uiCheckboxDraw(&item->checkbox, ix, iy));
break;
case UIMENU_WIDGET_TYPE_CHECKBOX:
errorChain(uiCheckboxDraw(&item->checkbox, ix, iy));
case UI_MENU_WIDGET_TYPE_BUTTON:
errorChain(uiButtonDraw(&item->button, ix, iy));
break;
default:
break;
}
col++;
if(col >= menu->columns) { col = 0; row++; }
}
errorOk();
@@ -126,8 +144,9 @@ bool_t uiMenuFocusSelected(const uifocusitem_t *focusItem) {
uimenu_t *menu = (uimenu_t *)focusItem->user;
if(menu->selected == NULL) return true;
uint8_t index = focusItem->y * menu->columns + focusItem->x;
if(index >= menu->itemCount) return true;
uint8_t slot = focusItem->y * menu->columns + focusItem->x;
uint8_t index = uiMenuFocusSlotToIndex(menu, slot);
if(index == 0xFF) return true;
menu->selected(menu, index, &menu->items[index]);
return true;
@@ -138,16 +157,27 @@ bool_t uiMenuFocusChanged(const uifocusitem_t *focusItem) {
assertNotNull(focusItem->user, "Focus item user cannot be NULL");
uimenu_t *menu = (uimenu_t *)focusItem->user;
uint8_t index = focusItem->y * menu->columns + focusItem->x;
uint8_t focusSlot = focusItem->y * menu->columns + focusItem->x;
uint8_t slot = 0;
for(uint8_t i = 0; i < menu->itemCount; i++) {
uimenuitem_t *item = &menu->items[i];
if(item->type != UIMENU_WIDGET_TYPE_CHECKBOX) continue;
uiCheckboxSetHighlighted(&item->checkbox, i == index);
if(
item->type == UI_MENU_WIDGET_TYPE_LABEL ||
item->type == UI_MENU_WIDGET_TYPE_SPACER
) continue;
bool_t isActive = (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++;
}
if(menu->changed == NULL) return true;
if(index >= menu->itemCount) return true;
uint8_t index = uiMenuFocusSlotToIndex(menu, focusSlot);
if(index == 0xFF) return true;
menu->changed(menu, index, &menu->items[index]);
return true;
@@ -161,3 +191,25 @@ bool_t uiMenuFocusClosed(const uifocusitem_t *focusItem) {
if(menu->closed != NULL) menu->closed(menu);
return true;
}
uint8_t uiMenuFocusableCount(const uimenu_t *menu) {
assertNotNull(menu, "Menu cannot be NULL");
uint8_t count = 0;
for(uint8_t i = 0; i < menu->itemCount; i++) {
uimenuwidgettype_t t = menu->items[i].type;
if(t != UI_MENU_WIDGET_TYPE_LABEL && t != UI_MENU_WIDGET_TYPE_SPACER) count++;
}
return count;
}
uint8_t uiMenuFocusSlotToIndex(const uimenu_t *menu, const uint8_t slot) {
assertNotNull(menu, "Menu cannot be NULL");
uint8_t focusable = 0;
for(uint8_t i = 0; i < menu->itemCount; i++) {
uimenuwidgettype_t t = menu->items[i].type;
if(t == UI_MENU_WIDGET_TYPE_LABEL || t == UI_MENU_WIDGET_TYPE_SPACER) continue;
if(focusable == slot) return i;
focusable++;
}
return 0xFF;
}
+29 -7
View File
@@ -8,6 +8,7 @@
#pragma once
#include "error/error.h"
#include "ui/focus/uifocus.h"
#include "ui/widget/uibutton.h"
#include "ui/widget/uicheckbox.h"
#define UI_MENU_LABEL_MAX UI_CHECKBOX_LABEL_MAX
@@ -15,16 +16,19 @@
typedef struct uimenu_s uimenu_t;
typedef enum {
UIMENU_WIDGET_TYPE_NONE,
UIMENU_WIDGET_TYPE_LABEL,
UIMENU_WIDGET_TYPE_CHECKBOX,
UI_MENU_WIDGET_TYPE_NONE,
UI_MENU_WIDGET_TYPE_LABEL,
UI_MENU_WIDGET_TYPE_SPACER,
UI_MENU_WIDGET_TYPE_CHECKBOX,
UI_MENU_WIDGET_TYPE_BUTTON,
} uimenuwidgettype_t;
typedef struct {
uimenuwidgettype_t type;
union {
char_t label[UI_MENU_LABEL_MAX];
char_t *label;
uicheckbox_t checkbox;
uibutton_t button;
};
} uimenuitem_t;
@@ -136,7 +140,25 @@ errorret_t uiMenuDraw(
);
/**
* Internal focus callback — forwards selection to the menu's selected handler.
* Returns the number of focusable (non-label) items in the menu.
*
* @param menu The menu to query.
* @returns Count of non-label items.
*/
uint8_t uiMenuFocusableCount(const uimenu_t *menu);
/**
* Maps a flat focus slot index to the corresponding item array index,
* skipping over label items which are not focusable.
*
* @param menu The menu to query.
* @param slot The focus slot index (y * columns + x).
* @returns The item array index, or 0xFF if out of range.
*/
uint8_t uiMenuFocusSlotToIndex(const uimenu_t *menu, const uint8_t slot);
/**
* Internal focus callback - forwards selection to the menu's selected handler.
*
* @param focusItem The active focus item; user field must point to uimenu_t.
* @returns True.
@@ -144,7 +166,7 @@ errorret_t uiMenuDraw(
bool_t uiMenuFocusSelected(const uifocusitem_t *focusItem);
/**
* Internal focus callback updates checkbox highlights and fires changed.
* Internal focus callback - updates widget highlights and fires changed.
*
* @param focusItem The active focus item; user field must point to uimenu_t.
* @returns True.
@@ -152,7 +174,7 @@ bool_t uiMenuFocusSelected(const uifocusitem_t *focusItem);
bool_t uiMenuFocusChanged(const uifocusitem_t *focusItem);
/**
* Internal focus callback clears focusItem and fires the closed handler.
* Internal focus callback - clears focusItem and fires the closed handler.
*
* @param focusItem The active focus item; user field must point to uimenu_t.
* @returns True.