Some label fixes
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
uibutton.c
|
||||
uicheckbox.c
|
||||
uimenu.c
|
||||
)
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
|
||||
);
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user