Dropdown test.

This commit is contained in:
2026-07-07 15:18:33 -05:00
parent 860c797c9c
commit 0bd2491ab7
14 changed files with 438 additions and 116 deletions
+9 -2
View File
@@ -11,8 +11,8 @@ msgstr ""
"Welcome"
#: src/dusk/ui/frame/settings/uisettings.c
msgid "ui.settings.tabs.gameplay"
msgstr "Gameplay"
msgid "ui.settings.tabs.general"
msgstr "General"
#: src/dusk/ui/frame/settings/uisettings.c
msgid "ui.settings.tabs.input"
@@ -29,6 +29,13 @@ msgstr "Audio"
msgid "ui.settings.input.deadzone"
msgstr "Deadzone"
#: src/dusk/ui/frame/settings/uisettingsgeneral.c
msgid "ui.settings.general.language"
msgstr "Language"
msgid "ui.settings.general.language_detail"
msgstr "Takes effect after restarting the application."
#: src/dusk/ui/frame/settings/uisettings.c
msgid "ui.settings.apply"
msgstr "Apply"
+6 -2
View File
@@ -11,8 +11,8 @@ msgstr ""
"Bienvenido"
#: src/dusk/ui/frame/settings/uisettings.c
msgid "ui.settings.tabs.gameplay"
msgstr "Jugabilidad"
msgid "ui.settings.tabs.general"
msgstr "General"
#: src/dusk/ui/frame/settings/uisettings.c
msgid "ui.settings.tabs.input"
@@ -29,6 +29,10 @@ msgstr "Audio"
msgid "ui.settings.input.deadzone"
msgstr "Zona muerta"
#: src/dusk/ui/frame/settings/uisettingsgeneral.c
msgid "ui.settings.general.language"
msgstr "Idioma"
#: src/dusk/ui/frame/settings/uisettings.c
msgid "ui.settings.apply"
msgstr "Aplicar"
+6 -2
View File
@@ -11,8 +11,8 @@ msgstr ""
"歓迎"
#: src/dusk/ui/frame/settings/uisettings.c
msgid "ui.settings.tabs.gameplay"
msgstr "ゲームプレイ"
msgid "ui.settings.tabs.general"
msgstr "一般"
#: src/dusk/ui/frame/settings/uisettings.c
msgid "ui.settings.tabs.input"
@@ -29,6 +29,10 @@ msgstr "オーディオ"
msgid "ui.settings.input.deadzone"
msgstr "デッドゾーン"
#: src/dusk/ui/frame/settings/uisettingsgeneral.c
msgid "ui.settings.general.language"
msgstr "言語"
#: src/dusk/ui/frame/settings/uisettings.c
msgid "ui.settings.apply"
msgstr "適用"
+1 -1
View File
@@ -6,7 +6,7 @@
target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
uisettings.c
uisettingsgameplay.c
uisettingsgeneral.c
uisettingsinput.c
uisettingsdisplay.c
uisettingsaudio.c
+7 -7
View File
@@ -6,7 +6,7 @@
*/
#include "uisettings.h"
#include "uisettingsgameplay.h"
#include "uisettingsgeneral.h"
#include "uisettingsinput.h"
#include "uisettingsdisplay.h"
#include "uisettingsaudio.h"
@@ -24,12 +24,12 @@ uisettings_t UI_SETTINGS;
uisettingstabdef_t UI_SETTINGS_TAB_DEFS[UI_SETTINGS_TAB_COUNT] = {
{
.localeId = "ui.settings.tabs.gameplay",
.init = uiSettingsGameplayInit,
.menu = &UI_SETTINGS_GAMEPLAY.menu,
.load = uiSettingsGameplayLoad,
.apply = uiSettingsGameplayApply,
.hasChanges = uiSettingsGameplayHasChanges
.localeId = "ui.settings.tabs.general",
.init = uiSettingsGeneralInit,
.menu = &UI_SETTINGS_GENERAL.menu,
.load = uiSettingsGeneralLoad,
.apply = uiSettingsGeneralApply,
.hasChanges = uiSettingsGeneralHasChanges
},
{
@@ -1,54 +0,0 @@
/**
* 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"
#include "ui/frame/uiconfirm.h"
#include "console/console.h"
uisettingsgameplay_t UI_SETTINGS_GAMEPLAY;
void uiSettingsGameplaySelected(
const uimenu_t *menu,
const uint8_t index,
const uimenuitem_t *item
) {
switch(index) {
case UI_SETTINGS_GAMEPLAY_INDEX_APPLY:
uiSettingsGameplayApply();
break;
default:
break;
}
}
errorret_t uiSettingsGameplayInit(void) {
memoryZero(&UI_SETTINGS_GAMEPLAY, sizeof(uisettingsgameplay_t));
MENU_BEGIN(
&UI_SETTINGS_GAMEPLAY.menu, UI_SETTINGS_GAMEPLAY.items,
uiSettingsGameplaySelected, uiSettingsDiscardMenuClosed, NULL
);
MENU_BUTTON(UI_SETTINGS.applyLabel);
MENU_END(UI_SETTINGS_GAMEPLAY.items, 1);
errorOk();
}
void uiSettingsGameplayLoad(void) {
}
void uiSettingsGameplayApply(void) {
uiMenuClose(&UI_SETTINGS_GAMEPLAY.menu);
}
bool_t uiSettingsGameplayHasChanges(void) {
return false;
}
@@ -1,48 +0,0 @@
/**
* 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"
#include "uisettings.h"
#define UI_SETTINGS_GAMEPLAY_ITEM_COUNT 6
#define UI_SETTINGS_GAMEPLAY_INDEX_ENABLE_THING 2
#define UI_SETTINGS_GAMEPLAY_INDEX_TEST_SLIDER 3
#define UI_SETTINGS_GAMEPLAY_INDEX_SHOW_CONFIRM 4
#define UI_SETTINGS_GAMEPLAY_INDEX_APPLY 5
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);
/**
* Loads the page's widgets from the current engine gameplay settings.
*/
void uiSettingsGameplayLoad(void);
/**
* Applies the page's widget values to the engine gameplay settings.
*/
void uiSettingsGameplayApply(void);
/**
* Returns whether any widget's value differs from what was loaded.
*
* @returns True if there are unsaved changes.
*/
bool_t uiSettingsGameplayHasChanges(void);
@@ -0,0 +1,112 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "uisettingsgeneral.h"
#include "assert/assert.h"
#include "util/memory.h"
#include "util/string.h"
#include "locale/localemanager.h"
#include "locale/localeinfo.h"
#include "asset/loader/locale/assetlocaleloader.h"
static const localeinfo_t *const UI_SETTINGS_GENERAL_LOCALES[
UI_SETTINGS_GENERAL_LOCALE_COUNT
] = {
&LOCALE_EN_US,
&LOCALE_JP_JP,
&LOCALE_ES_MX
};
uisettingsgeneral_t UI_SETTINGS_GENERAL;
void uiSettingsGeneralSelected(
const uimenu_t *menu,
const uint8_t index,
const uimenuitem_t *item
) {
switch(index) {
case UI_SETTINGS_GENERAL_INDEX_APPLY:
uiSettingsGeneralApply();
break;
default:
break;
}
}
errorret_t uiSettingsGeneralInit(void) {
memoryZero(&UI_SETTINGS_GENERAL, sizeof(uisettingsgeneral_t));
for(uint8_t i = 0; i < UI_SETTINGS_GENERAL_LOCALE_COUNT; i++) {
UI_SETTINGS_GENERAL.localeNames[i] = UI_SETTINGS_GENERAL_LOCALES[i]->name;
}
errorChain(assetLocaleGetString(
&LOCALE.entry->data.locale,
"ui.settings.general.language",
0,
UI_SETTINGS_GENERAL.languageLabel,
UI_SETTINGS_GENERAL_LABEL_MAX
));
errorChain(assetLocaleGetString(
&LOCALE.entry->data.locale,
"ui.settings.general.language_detail",
0,
UI_SETTINGS_GENERAL.labelInfo,
UI_SETTINGS_GENERAL_INFO_MAX
));
MENU_BEGIN(
&UI_SETTINGS_GENERAL.menu, UI_SETTINGS_GENERAL.items,
uiSettingsGeneralSelected, uiSettingsDiscardMenuClosed, NULL
);
MENU_DROPDOWN(
UI_SETTINGS_GENERAL.languageLabel, UI_SETTINGS_GENERAL.localeNames,
UI_SETTINGS_GENERAL_LOCALE_COUNT, 0
);
MENU_LABEL(UI_SETTINGS_GENERAL.labelInfo);
MENU_BUTTON(UI_SETTINGS.applyLabel);
MENU_END(UI_SETTINGS_GENERAL.items, 1);
errorOk();
}
void uiSettingsGeneralLoad(void) {
uint8_t index = 0;
for(uint8_t i = 0; i < UI_SETTINGS_GENERAL_LOCALE_COUNT; i++) {
if(stringCompare(LOCALE.locale->file, UI_SETTINGS_GENERAL_LOCALES[i]->file) != 0) {
continue;
}
index = i;
break;
}
uiDropdownSetSelectedIndex(
&UI_SETTINGS_GENERAL.items[UI_SETTINGS_GENERAL_INDEX_LANGUAGE].dropdown,
index
);
}
void uiSettingsGeneralApply(void) {
uint8_t index = uiDropdownGetSelectedIndex(
&UI_SETTINGS_GENERAL.items[UI_SETTINGS_GENERAL_INDEX_LANGUAGE].dropdown
);
errorCatch(localeManagerSetLocale(UI_SETTINGS_GENERAL_LOCALES[index]));
uiMenuClose(&UI_SETTINGS_GENERAL.menu);
}
bool_t uiSettingsGeneralHasChanges(void) {
uint8_t index = uiDropdownGetSelectedIndex(
&UI_SETTINGS_GENERAL.items[UI_SETTINGS_GENERAL_INDEX_LANGUAGE].dropdown
);
return stringCompare(
LOCALE.locale->file, UI_SETTINGS_GENERAL_LOCALES[index]->file
) != 0;
}
@@ -0,0 +1,55 @@
/**
* 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"
#include "uisettings.h"
#define UI_SETTINGS_GENERAL_ITEM_COUNT 3
#define UI_SETTINGS_GENERAL_INDEX_LANGUAGE 0
#define UI_SETTINGS_GENERAL_INDEX_LANGUAGE_DETAIL 1
#define UI_SETTINGS_GENERAL_INDEX_APPLY 2
#define UI_SETTINGS_GENERAL_LOCALE_COUNT 3
#define UI_SETTINGS_GENERAL_LABEL_MAX 32
#define UI_SETTINGS_GENERAL_INFO_MAX 64
typedef struct {
uimenu_t menu;
uimenuitem_t items[UI_SETTINGS_GENERAL_ITEM_COUNT];
const char_t *localeNames[UI_SETTINGS_GENERAL_LOCALE_COUNT];
char_t languageLabel[UI_SETTINGS_GENERAL_LABEL_MAX];
char_t labelInfo[UI_SETTINGS_GENERAL_INFO_MAX];
} uisettingsgeneral_t;
extern uisettingsgeneral_t UI_SETTINGS_GENERAL;
/**
* Initializes the general settings page.
*
* @return Any error that occurs.
*/
errorret_t uiSettingsGeneralInit(void);
/**
* Loads the page's widgets from the current engine general settings.
*/
void uiSettingsGeneralLoad(void);
/**
* Applies the page's widget values to the engine general settings.
*/
void uiSettingsGeneralApply(void);
/**
* Returns whether any widget's value differs from what was loaded.
*
* @returns True if there are unsaved changes.
*/
bool_t uiSettingsGeneralHasChanges(void);
+1
View File
@@ -9,5 +9,6 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
uicheckbox.c
uitab.c
uislider.c
uidropdown.c
uimenu.c
)
+100
View File
@@ -0,0 +1,100 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "uidropdown.h"
#include "assert/assert.h"
#include "util/memory.h"
#include "util/string.h"
#include "display/text/text.h"
#include "display/color.h"
void uiDropdownInit(
uidropdown_t *dropdown,
const char_t *label,
const char_t *const *options,
const uint8_t optionCount,
const uint8_t selectedIndex
) {
assertNotNull(dropdown, "Dropdown cannot be NULL");
assertNotNull(label, "Label cannot be NULL");
assertNotNull(options, "Options cannot be NULL");
assertTrue(optionCount > 0, "Dropdown must have at least one option");
memoryZero(dropdown, sizeof(uidropdown_t));
dropdown->label = label;
dropdown->options = options;
dropdown->optionCount = optionCount;
dropdown->selectedIndex = selectedIndex < optionCount ? selectedIndex : 0;
}
uint8_t uiDropdownGetSelectedIndex(const uidropdown_t *dropdown) {
assertNotNull(dropdown, "Dropdown cannot be NULL");
return dropdown->selectedIndex;
}
const char_t *uiDropdownGetSelectedOption(const uidropdown_t *dropdown) {
assertNotNull(dropdown, "Dropdown cannot be NULL");
return dropdown->options[dropdown->selectedIndex];
}
void uiDropdownSetSelectedIndex(uidropdown_t *dropdown, const uint8_t index) {
assertNotNull(dropdown, "Dropdown cannot be NULL");
assertTrue(index < dropdown->optionCount, "Dropdown index out of range");
dropdown->selectedIndex = index;
}
void uiDropdownStepNext(uidropdown_t *dropdown) {
assertNotNull(dropdown, "Dropdown cannot be NULL");
dropdown->selectedIndex =
(dropdown->selectedIndex + 1) % dropdown->optionCount;
}
void uiDropdownStepPrev(uidropdown_t *dropdown) {
assertNotNull(dropdown, "Dropdown cannot be NULL");
dropdown->selectedIndex = dropdown->selectedIndex == 0 ?
dropdown->optionCount - 1 : dropdown->selectedIndex - 1;
}
bool_t uiDropdownIsHighlighted(const uidropdown_t *dropdown) {
assertNotNull(dropdown, "Dropdown cannot be NULL");
return dropdown->highlighted;
}
void uiDropdownSetHighlighted(
uidropdown_t *dropdown,
const bool_t highlighted
) {
assertNotNull(dropdown, "Dropdown cannot be NULL");
dropdown->highlighted = highlighted;
}
errorret_t uiDropdownDraw(
const uidropdown_t *dropdown,
const float_t x,
const float_t y
) {
assertNotNull(dropdown, "Dropdown cannot be NULL");
color_t color = dropdown->highlighted ? COLOR_RED : COLOR_WHITE;
errorChain(textDraw(x, y, dropdown->label, color, &FONT_DEFAULT));
int32_t labelW, labelH;
textMeasure(dropdown->label, &FONT_DEFAULT, &labelW, &labelH);
char_t valueText[UI_DROPDOWN_VALUE_TEXT_MAX];
stringFormat(
valueText, UI_DROPDOWN_VALUE_TEXT_MAX - 1, "< %s >",
uiDropdownGetSelectedOption(dropdown)
);
errorChain(textDraw(
x + (float_t)labelW + UI_DROPDOWN_GAP, y, valueText, color, &FONT_DEFAULT
));
errorOk();
}
+113
View File
@@ -0,0 +1,113 @@
/**
* 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_DROPDOWN_GAP 4.0f
#define UI_DROPDOWN_VALUE_TEXT_MAX 64
typedef struct {
const char_t *label;
const char_t *const *options;
uint8_t optionCount;
uint8_t selectedIndex;
bool_t highlighted;
} uidropdown_t;
/**
* Initializes a dropdown.
*
* @param dropdown The dropdown to initialize.
* @param label Display label.
* @param options Array of option display strings; caller-owned, must
* outlive the dropdown.
* @param optionCount Number of entries in options. Must be > 0.
* @param selectedIndex Initial selected option index, clamped to
* [0, optionCount - 1].
*/
void uiDropdownInit(
uidropdown_t *dropdown,
const char_t *label,
const char_t *const *options,
const uint8_t optionCount,
const uint8_t selectedIndex
);
/**
* Returns the index of the currently selected option.
*
* @param dropdown The dropdown to query.
* @returns The selected option index.
*/
uint8_t uiDropdownGetSelectedIndex(const uidropdown_t *dropdown);
/**
* Returns the currently selected option's display string.
*
* @param dropdown The dropdown to query.
* @returns The selected option string.
*/
const char_t *uiDropdownGetSelectedOption(const uidropdown_t *dropdown);
/**
* Sets the selected option by index.
*
* @param dropdown The dropdown to update.
* @param index The new selected option index. Must be < optionCount.
*/
void uiDropdownSetSelectedIndex(uidropdown_t *dropdown, const uint8_t index);
/**
* Selects the next option, wrapping around to the first option past
* the last.
*
* @param dropdown The dropdown to update.
*/
void uiDropdownStepNext(uidropdown_t *dropdown);
/**
* Selects the previous option, wrapping around to the last option
* before the first.
*
* @param dropdown The dropdown to update.
*/
void uiDropdownStepPrev(uidropdown_t *dropdown);
/**
* Returns whether the dropdown is highlighted.
*
* @param dropdown The dropdown to query.
* @returns True if highlighted.
*/
bool_t uiDropdownIsHighlighted(const uidropdown_t *dropdown);
/**
* Sets the highlighted state of the dropdown.
*
* @param dropdown The dropdown to update.
* @param highlighted The new highlighted state.
*/
void uiDropdownSetHighlighted(
uidropdown_t *dropdown,
const bool_t highlighted
);
/**
* Draws the dropdown at the given screen position: label, then the
* currently selected option surrounded by cycle arrows.
*
* @param dropdown The dropdown to draw.
* @param x Screen x position.
* @param y Screen y position.
* @return Any error that occurs.
*/
errorret_t uiDropdownDraw(
const uidropdown_t *dropdown,
const float_t x,
const float_t y
);
+16
View File
@@ -13,6 +13,7 @@
#include "ui/widget/uibutton.h"
#include "ui/widget/uitab.h"
#include "ui/widget/uislider.h"
#include "ui/widget/uidropdown.h"
void uiMenuInit(
uimenu_t *menu,
@@ -138,6 +139,10 @@ errorret_t uiMenuDraw(
errorChain(uiSliderDraw(&item->slider, ix, iy));
break;
case UI_MENU_WIDGET_TYPE_DROPDOWN:
errorChain(uiDropdownDraw(&item->dropdown, ix, iy));
break;
default:
break;
}
@@ -235,6 +240,15 @@ bool_t uiMenuFocusDirection(
return true;
}
if(item->type == UI_MENU_WIDGET_TYPE_DROPDOWN) {
if(right) {
uiDropdownStepNext(&item->dropdown);
} else {
uiDropdownStepPrev(&item->dropdown);
}
return true;
}
return false;
}
@@ -269,5 +283,7 @@ void uiMenuItemSetHighlighted(uimenuitem_t *item, const bool_t highlighted) {
uiTabSetActive(&item->tab, highlighted);
} else if(item->type == UI_MENU_WIDGET_TYPE_SLIDER) {
uiSliderSetHighlighted(&item->slider, highlighted);
} else if(item->type == UI_MENU_WIDGET_TYPE_DROPDOWN) {
uiDropdownSetHighlighted(&item->dropdown, highlighted);
}
}
+12
View File
@@ -12,6 +12,7 @@
#include "ui/widget/uicheckbox.h"
#include "ui/widget/uitab.h"
#include "ui/widget/uislider.h"
#include "ui/widget/uidropdown.h"
#define UI_MENU_LABEL_MAX UI_CHECKBOX_LABEL_MAX
@@ -25,6 +26,7 @@ typedef enum {
UI_MENU_WIDGET_TYPE_BUTTON,
UI_MENU_WIDGET_TYPE_TAB,
UI_MENU_WIDGET_TYPE_SLIDER,
UI_MENU_WIDGET_TYPE_DROPDOWN,
} uimenuwidgettype_t;
typedef struct {
@@ -35,6 +37,7 @@ typedef struct {
uibutton_t button;
uitab_t tab;
uislider_t slider;
uidropdown_t dropdown;
};
} uimenuitem_t;
@@ -266,6 +269,15 @@ bool_t uiMenuFocusDirection(
); \
++menuIndex
#define MENU_DROPDOWN(label, options, optionCount, selectedIndex) \
assertTrue(menuIndex < menuCapacity, "Menu item count exceeds capacity"); \
menu->items[menuIndex].type = UI_MENU_WIDGET_TYPE_DROPDOWN; \
uiDropdownInit( \
&menu->items[menuIndex].dropdown, label, options, optionCount, \
selectedIndex \
); \
++menuIndex
#define MENU_END(itemsArray, columns) \
uiMenuSetItems(menu, (itemsArray), menuIndex, (columns))