Settings improvements

This commit is contained in:
2026-07-07 14:00:37 -05:00
parent d83a953e2d
commit fae191d8fe
20 changed files with 666 additions and 136 deletions
+7
View File
@@ -25,3 +25,10 @@ msgstr "Display"
#: src/dusk/ui/frame/settings/uisettings.c #: src/dusk/ui/frame/settings/uisettings.c
msgid "ui.settings.tabs.audio" msgid "ui.settings.tabs.audio"
msgstr "Audio" msgstr "Audio"
msgid "ui.settings.input.deadzone"
msgstr "Deadzone"
#: src/dusk/ui/frame/uiconfirm.c
msgid "ui.confirm.discard_changes"
msgstr "Discard unsaved changes?"
+1
View File
@@ -17,6 +17,7 @@ input_t INPUT;
errorret_t inputInit(void) { errorret_t inputInit(void) {
memoryZero(&INPUT, sizeof(input_t)); memoryZero(&INPUT, sizeof(input_t));
INPUT.deadzone = INPUT_DEADZONE_DEFAULT;
for(uint8_t i = 0; i < INPUT_ACTION_COUNT; i++) { for(uint8_t i = 0; i < INPUT_ACTION_COUNT; i++) {
INPUT.actions[i].action = (inputaction_t)i; INPUT.actions[i].action = (inputaction_t)i;
+4
View File
@@ -12,11 +12,15 @@
#define INPUT_LISTENER_PRESSED_MAX 16 #define INPUT_LISTENER_PRESSED_MAX 16
#define INPUT_LISTENER_RELEASED_MAX INPUT_LISTENER_PRESSED_MAX #define INPUT_LISTENER_RELEASED_MAX INPUT_LISTENER_PRESSED_MAX
#define INPUT_DEADZONE_DEFAULT 0.1f
typedef struct { typedef struct {
inputactiondata_t actions[INPUT_ACTION_COUNT]; inputactiondata_t actions[INPUT_ACTION_COUNT];
inputplatform_t platform; inputplatform_t platform;
/** User-configured gamepad axis deadzone (0.0f to 1.0f). */
float_t deadzone;
} input_t; } input_t;
extern input_t INPUT; extern input_t INPUT;
+4 -11
View File
@@ -29,20 +29,13 @@ void uiGameMenuSelected(
errorret_t uiGameMenuInit(void) { errorret_t uiGameMenuInit(void) {
memoryZero(&UI_GAME_MENU, sizeof(uigamemenu_t)); memoryZero(&UI_GAME_MENU, sizeof(uigamemenu_t));
uimenu_t *menu = &UI_GAME_MENU.menu; MENU_BEGIN(
uiMenuInit(menu, uiGameMenuSelected, NULL, NULL); &UI_GAME_MENU.menu, UI_GAME_MENU.items, uiGameMenuSelected, NULL, NULL
menu->items = UI_GAME_MENU.items; );
MENU_BEGIN();
MENU_BUTTON("Characters"); MENU_BUTTON("Characters");
MENU_BUTTON("Settings"); MENU_BUTTON("Settings");
assertTrue( MENU_END(UI_GAME_MENU.items, 1);
menuIndex <= UI_GAME_MENU_ITEM_COUNT,
"Game menu item count mismatch"
);
uiMenuSetItems(menu, UI_GAME_MENU.items, menuIndex, 1);
errorOk(); errorOk();
} }
@@ -6,6 +6,7 @@
target_sources(${DUSK_LIBRARY_TARGET_NAME} target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC PUBLIC
uisettings.c uisettings.c
uisettingsdiscard.c
uisettingsgameplay.c uisettingsgameplay.c
uisettingsinput.c uisettingsinput.c
uisettingsdisplay.c uisettingsdisplay.c
+126 -75
View File
@@ -19,6 +19,68 @@
#include "locale/localemanager.h" #include "locale/localemanager.h"
#include "asset/loader/locale/assetlocaleloader.h" #include "asset/loader/locale/assetlocaleloader.h"
typedef struct {
const char_t *localeId;
errorret_t (*init)(void);
errorret_t (*draw)(
const float_t x,
const float_t y,
const float_t width,
const float_t height
);
errorret_t (*update)(void);
void (*open)(void);
void (*load)(void);
void (*apply)(void);
bool_t (*hasChanges)(void);
} uisettingstabdef_t;
static const uisettingstabdef_t UI_SETTINGS_TAB_DEFS[UI_SETTINGS_TAB_COUNT] = {
[UI_SETTINGS_TAB_GAMEPLAY] = {
.localeId = "ui.settings.tabs.gameplay",
.init = uiSettingsGameplayInit,
.draw = uiSettingsGameplayDraw,
.update = uiSettingsGameplayUpdate,
.open = uiSettingsGameplayOpen,
.load = uiSettingsGameplayLoad,
.apply = uiSettingsGameplayApply,
.hasChanges = uiSettingsGameplayHasChanges
},
[UI_SETTINGS_TAB_INPUT] = {
.localeId = "ui.settings.tabs.input",
.init = uiSettingsInputInit,
.draw = uiSettingsInputDraw,
.update = uiSettingsInputUpdate,
.open = uiSettingsInputOpen,
.load = uiSettingsInputLoad,
.apply = uiSettingsInputApply,
.hasChanges = uiSettingsInputHasChanges
},
[UI_SETTINGS_TAB_DISPLAY] = {
.localeId = "ui.settings.tabs.display",
.init = uiSettingsDisplayInit,
.draw = uiSettingsDisplayDraw,
.update = uiSettingsDisplayUpdate,
.open = uiSettingsDisplayOpen,
.load = uiSettingsDisplayLoad,
.apply = uiSettingsDisplayApply,
.hasChanges = uiSettingsDisplayHasChanges
},
[UI_SETTINGS_TAB_AUDIO] = {
.localeId = "ui.settings.tabs.audio",
.init = uiSettingsAudioInit,
.draw = uiSettingsAudioDraw,
.update = uiSettingsAudioUpdate,
.open = uiSettingsAudioOpen,
.load = uiSettingsAudioLoad,
.apply = uiSettingsAudioApply,
.hasChanges = uiSettingsAudioHasChanges
}
};
uisettings_t UI_SETTINGS; uisettings_t UI_SETTINGS;
void uiSettingsTabSelected( void uiSettingsTabSelected(
@@ -26,81 +88,44 @@ void uiSettingsTabSelected(
const uint8_t index, const uint8_t index,
const uimenuitem_t *item const uimenuitem_t *item
) { ) {
switch(index) { if(index >= UI_SETTINGS_TAB_COUNT) return;
case UI_SETTINGS_TAB_GAMEPLAY: UI_SETTINGS_TAB_DEFS[index].open();
uiSettingsGameplayOpen(); }
break;
case UI_SETTINGS_TAB_INPUT: void uiSettingsTabsClosed(const uimenu_t *menu) {
uiSettingsInputOpen(); uiSettingsDiscardClosed(&UI_SETTINGS.discard);
break;
case UI_SETTINGS_TAB_DISPLAY:
uiSettingsDisplayOpen();
break;
case UI_SETTINGS_TAB_AUDIO:
uiSettingsAudioOpen();
break;
default:
break;
}
} }
errorret_t uiSettingsInit(void) { errorret_t uiSettingsInit(void) {
memoryZero(&UI_SETTINGS, sizeof(uisettings_t)); memoryZero(&UI_SETTINGS, sizeof(uisettings_t));
errorChain(assetLocaleGetString( for(uint8_t i = 0; i < UI_SETTINGS_TAB_COUNT; i++) {
&LOCALE.entry->data.locale, errorChain(assetLocaleGetString(
"ui.settings.tabs.gameplay", &LOCALE.entry->data.locale,
0, UI_SETTINGS_TAB_DEFS[i].localeId,
UI_SETTINGS.tabLabels[UI_SETTINGS_TAB_GAMEPLAY], 0,
UI_SETTINGS_TAB_LABEL_MAX UI_SETTINGS.tabLabels[i],
)); UI_SETTINGS_TAB_LABEL_MAX
errorChain(assetLocaleGetString( ));
&LOCALE.entry->data.locale, }
"ui.settings.tabs.input",
0,
UI_SETTINGS.tabLabels[UI_SETTINGS_TAB_INPUT],
UI_SETTINGS_TAB_LABEL_MAX
));
errorChain(assetLocaleGetString(
&LOCALE.entry->data.locale,
"ui.settings.tabs.display",
0,
UI_SETTINGS.tabLabels[UI_SETTINGS_TAB_DISPLAY],
UI_SETTINGS_TAB_LABEL_MAX
));
errorChain(assetLocaleGetString(
&LOCALE.entry->data.locale,
"ui.settings.tabs.audio",
0,
UI_SETTINGS.tabLabels[UI_SETTINGS_TAB_AUDIO],
UI_SETTINGS_TAB_LABEL_MAX
));
uimenu_t *menu = &UI_SETTINGS.tabsMenu; MENU_BEGIN(
uiMenuInit(menu, uiSettingsTabSelected, NULL, NULL); &UI_SETTINGS.tabsMenu, UI_SETTINGS.tabs, uiSettingsTabSelected,
menu->items = UI_SETTINGS.tabs; uiSettingsTabsClosed, NULL
);
for(uint8_t i = 0; i < UI_SETTINGS_TAB_COUNT; i++) {
MENU_TAB(UI_SETTINGS.tabLabels[i]);
}
MENU_END(UI_SETTINGS.tabs, menuIndex);
MENU_BEGIN(); uiSettingsDiscardInit(
MENU_TAB(UI_SETTINGS.tabLabels[UI_SETTINGS_TAB_GAMEPLAY]); &UI_SETTINGS.discard, &UI_SETTINGS.tabsMenu, uiSettingsHasChanges,
MENU_TAB(UI_SETTINGS.tabLabels[UI_SETTINGS_TAB_INPUT]); uiSettingsLoad
MENU_TAB(UI_SETTINGS.tabLabels[UI_SETTINGS_TAB_DISPLAY]);
MENU_TAB(UI_SETTINGS.tabLabels[UI_SETTINGS_TAB_AUDIO]);
assertTrue(
menuIndex <= UI_SETTINGS_TAB_COUNT,
"Settings tab count mismatch"
); );
uiMenuSetItems(menu, UI_SETTINGS.tabs, menuIndex, menuIndex); for(uint8_t i = 0; i < UI_SETTINGS_TAB_COUNT; i++) {
errorChain(UI_SETTINGS_TAB_DEFS[i].init());
errorChain(uiSettingsGameplayInit()); }
errorChain(uiSettingsInputInit());
errorChain(uiSettingsDisplayInit());
errorChain(uiSettingsAudioInit());
errorOk(); errorOk();
} }
@@ -108,8 +133,8 @@ errorret_t uiSettingsInit(void) {
errorret_t uiSettingsDraw(void) { errorret_t uiSettingsDraw(void) {
if(!uiMenuIsActive(&UI_SETTINGS.tabsMenu)) errorOk(); if(!uiMenuIsActive(&UI_SETTINGS.tabsMenu)) errorOk();
const float_t width = 220.0f; const float_t width = SCREEN.width;
const float_t height = 150.0f; const float_t height = SCREEN.height;
const float_t x = (float_t)SCREEN.scanX + const float_t x = (float_t)SCREEN.scanX +
((float_t)SCREEN.scanWidth - width) * 0.5f; ((float_t)SCREEN.scanWidth - width) * 0.5f;
const float_t y = (float_t)SCREEN.scanY + const float_t y = (float_t)SCREEN.scanY +
@@ -134,25 +159,32 @@ errorret_t uiSettingsDraw(void) {
const float_t pageY = contentY + tabsRowHeight + UI_FRAME_PADDING_Y; const float_t pageY = contentY + tabsRowHeight + UI_FRAME_PADDING_Y;
const float_t pageHeight = contentHeight - 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)) { for(uint8_t i = 0; i < UI_SETTINGS_TAB_COUNT; i++) {
errorChain(uiSettingsGameplayDraw(contentX, pageY, contentWidth, pageHeight)); if(!uiTabIsActive(&UI_SETTINGS.tabs[i].tab)) continue;
} else if(uiTabIsActive(&UI_SETTINGS.tabs[UI_SETTINGS_TAB_INPUT].tab)) { errorChain(UI_SETTINGS_TAB_DEFS[i].draw(
errorChain(uiSettingsInputDraw(contentX, pageY, contentWidth, pageHeight)); contentX, pageY, contentWidth, pageHeight
} else if(uiTabIsActive(&UI_SETTINGS.tabs[UI_SETTINGS_TAB_DISPLAY].tab)) { ));
errorChain(uiSettingsDisplayDraw(contentX, pageY, contentWidth, pageHeight)); break;
} else if(uiTabIsActive(&UI_SETTINGS.tabs[UI_SETTINGS_TAB_AUDIO].tab)) {
errorChain(uiSettingsAudioDraw(contentX, pageY, contentWidth, pageHeight));
} }
errorChain(spriteBatchFlush()); errorChain(spriteBatchFlush());
errorOk(); errorOk();
} }
errorret_t uiSettingsUpdate(void) {
for(uint8_t i = 0; i < UI_SETTINGS_TAB_COUNT; i++) {
errorChain(UI_SETTINGS_TAB_DEFS[i].update());
}
return uiSettingsDiscardUpdate(&UI_SETTINGS.discard);
}
bool_t uiSettingsIsOpen(void) { bool_t uiSettingsIsOpen(void) {
return uiMenuIsActive(&UI_SETTINGS.tabsMenu); return uiMenuIsActive(&UI_SETTINGS.tabsMenu);
} }
void uiSettingsOpen() { void uiSettingsOpen() {
uiSettingsLoad();
uiMenuOpen(&UI_SETTINGS.tabsMenu); uiMenuOpen(&UI_SETTINGS.tabsMenu);
} }
@@ -163,3 +195,22 @@ void uiSettingsClose() {
errorret_t uiSettingsDispose(void) { errorret_t uiSettingsDispose(void) {
errorOk(); errorOk();
} }
void uiSettingsLoad(void) {
for(uint8_t i = 0; i < UI_SETTINGS_TAB_COUNT; i++) {
UI_SETTINGS_TAB_DEFS[i].load();
}
}
void uiSettingsApply(void) {
for(uint8_t i = 0; i < UI_SETTINGS_TAB_COUNT; i++) {
UI_SETTINGS_TAB_DEFS[i].apply();
}
}
bool_t uiSettingsHasChanges(void) {
for(uint8_t i = 0; i < UI_SETTINGS_TAB_COUNT; i++) {
if(UI_SETTINGS_TAB_DEFS[i].hasChanges()) return true;
}
return false;
}
+30
View File
@@ -8,6 +8,7 @@
#pragma once #pragma once
#include "error/error.h" #include "error/error.h"
#include "ui/widget/uimenu.h" #include "ui/widget/uimenu.h"
#include "uisettingsdiscard.h"
#define UI_SETTINGS_TAB_GAMEPLAY 0 #define UI_SETTINGS_TAB_GAMEPLAY 0
#define UI_SETTINGS_TAB_INPUT 1 #define UI_SETTINGS_TAB_INPUT 1
@@ -20,6 +21,7 @@ typedef struct {
uimenu_t tabsMenu; uimenu_t tabsMenu;
uimenuitem_t tabs[UI_SETTINGS_TAB_COUNT]; uimenuitem_t tabs[UI_SETTINGS_TAB_COUNT];
char_t tabLabels[UI_SETTINGS_TAB_COUNT][UI_SETTINGS_TAB_LABEL_MAX]; char_t tabLabels[UI_SETTINGS_TAB_COUNT][UI_SETTINGS_TAB_LABEL_MAX];
uisettingsdiscard_t discard;
} uisettings_t; } uisettings_t;
extern uisettings_t UI_SETTINGS; extern uisettings_t UI_SETTINGS;
@@ -38,6 +40,14 @@ errorret_t uiSettingsInit(void);
*/ */
errorret_t uiSettingsDraw(void); errorret_t uiSettingsDraw(void);
/**
* Updates the settings panel. Shows a discard-changes confirmation if
* the panel was just closed with unsaved changes.
*
* @return Any error that occurs.
*/
errorret_t uiSettingsUpdate(void);
/** /**
* Returns true when the settings panel is currently open. * Returns true when the settings panel is currently open.
* *
@@ -61,3 +71,23 @@ void uiSettingsClose();
* @return Any error that occurs. * @return Any error that occurs.
*/ */
errorret_t uiSettingsDispose(void); errorret_t uiSettingsDispose(void);
/**
* Loads every category page's widgets from the current engine settings.
* Called automatically by uiSettingsOpen.
*/
void uiSettingsLoad(void);
/**
* Applies every category page's widget values to the engine settings.
* Called automatically when a page's own Apply button is pressed.
*/
void uiSettingsApply(void);
/**
* Returns whether any category page has a widget value that differs
* from what was loaded.
*
* @returns True if there are unsaved changes.
*/
bool_t uiSettingsHasChanges(void);
+39 -6
View File
@@ -6,21 +6,40 @@
*/ */
#include "uisettingsaudio.h" #include "uisettingsaudio.h"
#include "assert/assert.h"
#include "util/memory.h" #include "util/memory.h"
uisettingsaudio_t UI_SETTINGS_AUDIO; uisettingsaudio_t UI_SETTINGS_AUDIO;
void uiSettingsAudioSelected(
const uimenu_t *menu,
const uint8_t index,
const uimenuitem_t *item
) {
if(index != UI_SETTINGS_AUDIO_INDEX_APPLY) return;
uiSettingsAudioApply();
}
void uiSettingsAudioClosed(const uimenu_t *menu) {
uiSettingsDiscardClosed(&UI_SETTINGS_AUDIO.discard);
}
errorret_t uiSettingsAudioInit(void) { errorret_t uiSettingsAudioInit(void) {
memoryZero(&UI_SETTINGS_AUDIO, sizeof(uisettingsaudio_t)); memoryZero(&UI_SETTINGS_AUDIO, sizeof(uisettingsaudio_t));
uimenu_t *menu = &UI_SETTINGS_AUDIO.menu; MENU_BEGIN(
uiMenuInit(menu, NULL, NULL, NULL); &UI_SETTINGS_AUDIO.menu, UI_SETTINGS_AUDIO.items,
menu->items = UI_SETTINGS_AUDIO.items; uiSettingsAudioSelected, uiSettingsAudioClosed, NULL
);
MENU_BEGIN();
MENU_LABEL("No audio settings yet"); MENU_LABEL("No audio settings yet");
MENU_BUTTON("Apply");
uiMenuSetItems(menu, UI_SETTINGS_AUDIO.items, menuIndex, 1); MENU_END(UI_SETTINGS_AUDIO.items, 1);
uiSettingsDiscardInit(
&UI_SETTINGS_AUDIO.discard, &UI_SETTINGS_AUDIO.menu,
uiSettingsAudioHasChanges, uiSettingsAudioLoad
);
errorOk(); errorOk();
} }
@@ -45,3 +64,17 @@ void uiSettingsAudioOpen(void) {
void uiSettingsAudioClose(void) { void uiSettingsAudioClose(void) {
uiMenuClose(&UI_SETTINGS_AUDIO.menu); uiMenuClose(&UI_SETTINGS_AUDIO.menu);
} }
errorret_t uiSettingsAudioUpdate(void) {
return uiSettingsDiscardUpdate(&UI_SETTINGS_AUDIO.discard);
}
void uiSettingsAudioLoad(void) {
}
void uiSettingsAudioApply(void) {
}
bool_t uiSettingsAudioHasChanges(void) {
return false;
}
+29 -1
View File
@@ -8,12 +8,15 @@
#pragma once #pragma once
#include "error/error.h" #include "error/error.h"
#include "ui/widget/uimenu.h" #include "ui/widget/uimenu.h"
#include "uisettingsdiscard.h"
#define UI_SETTINGS_AUDIO_ITEM_COUNT 1 #define UI_SETTINGS_AUDIO_ITEM_COUNT 2
#define UI_SETTINGS_AUDIO_INDEX_APPLY 1
typedef struct { typedef struct {
uimenu_t menu; uimenu_t menu;
uimenuitem_t items[UI_SETTINGS_AUDIO_ITEM_COUNT]; uimenuitem_t items[UI_SETTINGS_AUDIO_ITEM_COUNT];
uisettingsdiscard_t discard;
} uisettingsaudio_t; } uisettingsaudio_t;
extern uisettingsaudio_t UI_SETTINGS_AUDIO; extern uisettingsaudio_t UI_SETTINGS_AUDIO;
@@ -41,6 +44,14 @@ errorret_t uiSettingsAudioDraw(
const float_t height const float_t height
); );
/**
* Updates the audio settings page. Shows a discard-changes
* confirmation if the page was just backed out of with unsaved changes.
*
* @return Any error that occurs.
*/
errorret_t uiSettingsAudioUpdate(void);
/** /**
* Returns true when the audio settings page is currently open. * Returns true when the audio settings page is currently open.
* *
@@ -57,3 +68,20 @@ void uiSettingsAudioOpen(void);
* Closes the audio settings page. No-op when already closed. * Closes the audio settings page. No-op when already closed.
*/ */
void uiSettingsAudioClose(void); void uiSettingsAudioClose(void);
/**
* Loads the page's widgets from the current engine audio settings.
*/
void uiSettingsAudioLoad(void);
/**
* Applies the page's widget values to the engine audio settings.
*/
void uiSettingsAudioApply(void);
/**
* Returns whether any widget's value differs from what was loaded.
*
* @returns True if there are unsaved changes.
*/
bool_t uiSettingsAudioHasChanges(void);
@@ -0,0 +1,59 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "uisettingsdiscard.h"
#include "ui/frame/uiconfirm.h"
#include "locale/localemanager.h"
#include "asset/loader/locale/assetlocaleloader.h"
void uiSettingsDiscardInit(
uisettingsdiscard_t *discard,
uimenu_t *menu,
uisettingshaschangesfn_t hasChanges,
uisettingsloadfn_t load
) {
discard->menu = menu;
discard->hasChanges = hasChanges;
discard->load = load;
discard->pending = false;
}
void uiSettingsDiscardClosed(uisettingsdiscard_t *discard) {
if(discard->hasChanges()) discard->pending = true;
}
errorret_t uiSettingsDiscardUpdate(uisettingsdiscard_t *discard) {
if(!discard->pending) errorOk();
discard->pending = false;
char_t text[UI_SETTINGS_DISCARD_TEXT_MAX];
errorChain(assetLocaleGetString(
&LOCALE.entry->data.locale,
"ui.confirm.discard_changes",
0,
text,
UI_SETTINGS_DISCARD_TEXT_MAX
));
uiConfirmOpen(text, uiSettingsDiscardConfirmResult, discard);
errorOk();
}
void uiSettingsDiscardConfirmResult(const bool_t result, void *user) {
uisettingsdiscard_t *discard = (uisettingsdiscard_t *)user;
if(!result) {
// Cancel means "keep editing" - reopen without reloading so the
// unsaved widget values the user had are preserved.
uiMenuOpen(discard->menu);
return;
}
// Confirmed - reload the widgets back to the actual engine values.
discard->load();
}
@@ -0,0 +1,77 @@
/**
* 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_DISCARD_TEXT_MAX 128
/**
* Returns true if the owning settings page/panel has unsaved changes.
*
* @returns True if there are unsaved changes.
*/
typedef bool_t (*uisettingshaschangesfn_t)(void);
/**
* Reloads the owning settings page/panel's widgets from the current
* engine settings.
*/
typedef void (*uisettingsloadfn_t)(void);
typedef struct {
uimenu_t *menu;
uisettingshaschangesfn_t hasChanges;
uisettingsloadfn_t load;
bool_t pending;
} uisettingsdiscard_t;
/**
* Initializes a discard-changes confirmation helper for a settings
* page or panel.
*
* @param discard The helper state to initialize.
* @param menu The menu to reopen if the user cancels discarding.
* @param hasChanges Callback returning true if there are unsaved
* changes.
* @param load Callback that reloads the widgets from the current
* engine settings, called if the user confirms discarding changes.
*/
void uiSettingsDiscardInit(
uisettingsdiscard_t *discard,
uimenu_t *menu,
uisettingshaschangesfn_t hasChanges,
uisettingsloadfn_t load
);
/**
* Call from the owning menu's closed callback. Flags a pending
* confirmation if hasChanges() reports unsaved changes.
*
* @param discard The helper state.
*/
void uiSettingsDiscardClosed(uisettingsdiscard_t *discard);
/**
* Call every frame from the owning page/panel's update function. Shows
* the discard-changes confirmation dialog if one is pending.
*
* @param discard The helper state.
* @return Any error that occurs.
*/
errorret_t uiSettingsDiscardUpdate(uisettingsdiscard_t *discard);
/**
* Callback passed to uiConfirmOpen by uiSettingsDiscardUpdate. Reopens
* the menu if the user chose to keep editing.
*
* @param result True if the user confirmed discarding changes, false
* to keep editing. On true, the widgets are reloaded via load().
* @param user The uisettingsdiscard_t* this confirmation belongs to.
*/
void uiSettingsDiscardConfirmResult(const bool_t result, void *user);
+39 -6
View File
@@ -6,21 +6,40 @@
*/ */
#include "uisettingsdisplay.h" #include "uisettingsdisplay.h"
#include "assert/assert.h"
#include "util/memory.h" #include "util/memory.h"
uisettingsdisplay_t UI_SETTINGS_DISPLAY; uisettingsdisplay_t UI_SETTINGS_DISPLAY;
void uiSettingsDisplaySelected(
const uimenu_t *menu,
const uint8_t index,
const uimenuitem_t *item
) {
if(index != UI_SETTINGS_DISPLAY_INDEX_APPLY) return;
uiSettingsDisplayApply();
}
void uiSettingsDisplayClosed(const uimenu_t *menu) {
uiSettingsDiscardClosed(&UI_SETTINGS_DISPLAY.discard);
}
errorret_t uiSettingsDisplayInit(void) { errorret_t uiSettingsDisplayInit(void) {
memoryZero(&UI_SETTINGS_DISPLAY, sizeof(uisettingsdisplay_t)); memoryZero(&UI_SETTINGS_DISPLAY, sizeof(uisettingsdisplay_t));
uimenu_t *menu = &UI_SETTINGS_DISPLAY.menu; MENU_BEGIN(
uiMenuInit(menu, NULL, NULL, NULL); &UI_SETTINGS_DISPLAY.menu, UI_SETTINGS_DISPLAY.items,
menu->items = UI_SETTINGS_DISPLAY.items; uiSettingsDisplaySelected, uiSettingsDisplayClosed, NULL
);
MENU_BEGIN();
MENU_LABEL("No display settings yet"); MENU_LABEL("No display settings yet");
MENU_BUTTON("Apply");
uiMenuSetItems(menu, UI_SETTINGS_DISPLAY.items, menuIndex, 1); MENU_END(UI_SETTINGS_DISPLAY.items, 1);
uiSettingsDiscardInit(
&UI_SETTINGS_DISPLAY.discard, &UI_SETTINGS_DISPLAY.menu,
uiSettingsDisplayHasChanges, uiSettingsDisplayLoad
);
errorOk(); errorOk();
} }
@@ -45,3 +64,17 @@ void uiSettingsDisplayOpen(void) {
void uiSettingsDisplayClose(void) { void uiSettingsDisplayClose(void) {
uiMenuClose(&UI_SETTINGS_DISPLAY.menu); uiMenuClose(&UI_SETTINGS_DISPLAY.menu);
} }
errorret_t uiSettingsDisplayUpdate(void) {
return uiSettingsDiscardUpdate(&UI_SETTINGS_DISPLAY.discard);
}
void uiSettingsDisplayLoad(void) {
}
void uiSettingsDisplayApply(void) {
}
bool_t uiSettingsDisplayHasChanges(void) {
return false;
}
+29 -1
View File
@@ -8,12 +8,15 @@
#pragma once #pragma once
#include "error/error.h" #include "error/error.h"
#include "ui/widget/uimenu.h" #include "ui/widget/uimenu.h"
#include "uisettingsdiscard.h"
#define UI_SETTINGS_DISPLAY_ITEM_COUNT 1 #define UI_SETTINGS_DISPLAY_ITEM_COUNT 2
#define UI_SETTINGS_DISPLAY_INDEX_APPLY 1
typedef struct { typedef struct {
uimenu_t menu; uimenu_t menu;
uimenuitem_t items[UI_SETTINGS_DISPLAY_ITEM_COUNT]; uimenuitem_t items[UI_SETTINGS_DISPLAY_ITEM_COUNT];
uisettingsdiscard_t discard;
} uisettingsdisplay_t; } uisettingsdisplay_t;
extern uisettingsdisplay_t UI_SETTINGS_DISPLAY; extern uisettingsdisplay_t UI_SETTINGS_DISPLAY;
@@ -41,6 +44,14 @@ errorret_t uiSettingsDisplayDraw(
const float_t height const float_t height
); );
/**
* Updates the display settings page. Shows a discard-changes
* confirmation if the page was just backed out of with unsaved changes.
*
* @return Any error that occurs.
*/
errorret_t uiSettingsDisplayUpdate(void);
/** /**
* Returns true when the display settings page is currently open. * Returns true when the display settings page is currently open.
* *
@@ -57,3 +68,20 @@ void uiSettingsDisplayOpen(void);
* Closes the display settings page. No-op when already closed. * Closes the display settings page. No-op when already closed.
*/ */
void uiSettingsDisplayClose(void); void uiSettingsDisplayClose(void);
/**
* Loads the page's widgets from the current engine display settings.
*/
void uiSettingsDisplayLoad(void);
/**
* Applies the page's widget values to the engine display settings.
*/
void uiSettingsDisplayApply(void);
/**
* Returns whether any widget's value differs from what was loaded.
*
* @returns True if there are unsaved changes.
*/
bool_t uiSettingsDisplayHasChanges(void);
+63 -14
View File
@@ -22,33 +22,44 @@ void uiSettingsGameplaySelected(
const uint8_t index, const uint8_t index,
const uimenuitem_t *item const uimenuitem_t *item
) { ) {
if(index != UI_SETTINGS_GAMEPLAY_INDEX_SHOW_CONFIRM) return; if(index == UI_SETTINGS_GAMEPLAY_INDEX_SHOW_CONFIRM) {
uiConfirmOpen( uiConfirmOpen(
"Are you sure?", uiSettingsGameplayShowConfirmResult, NULL "Are you sure?", uiSettingsGameplayShowConfirmResult, NULL
); );
return;
}
if(index == UI_SETTINGS_GAMEPLAY_INDEX_APPLY) {
uiSettingsGameplayApply();
return;
}
}
void uiSettingsGameplayClosed(const uimenu_t *menu) {
uiSettingsDiscardClosed(&UI_SETTINGS_GAMEPLAY.discard);
} }
errorret_t uiSettingsGameplayInit(void) { errorret_t uiSettingsGameplayInit(void) {
memoryZero(&UI_SETTINGS_GAMEPLAY, sizeof(uisettingsgameplay_t)); memoryZero(&UI_SETTINGS_GAMEPLAY, sizeof(uisettingsgameplay_t));
uimenu_t *menu = &UI_SETTINGS_GAMEPLAY.menu; MENU_BEGIN(
uiMenuInit(menu, uiSettingsGameplaySelected, NULL, NULL); &UI_SETTINGS_GAMEPLAY.menu, UI_SETTINGS_GAMEPLAY.items,
menu->items = UI_SETTINGS_GAMEPLAY.items; uiSettingsGameplaySelected, uiSettingsGameplayClosed, NULL
);
MENU_BEGIN();
MENU_BUTTON("Do thing"); MENU_BUTTON("Do thing");
MENU_BUTTON("Do other thing"); MENU_BUTTON("Do other thing");
MENU_CHECKBOX("Enable thing?"); MENU_CHECKBOX("Enable thing?");
MENU_SLIDER_FLOAT("test", 1.0f, 1.0f, 4.0f, 0.5f); MENU_SLIDER_FLOAT("test", 1.0f, 1.0f, 4.0f, 0.5f);
MENU_BUTTON("Show confirm"); MENU_BUTTON("Show confirm");
MENU_BUTTON("Apply");
assertTrue( MENU_END(UI_SETTINGS_GAMEPLAY.items, 1);
menuIndex <= UI_SETTINGS_GAMEPLAY_ITEM_COUNT,
"Gameplay settings item count mismatch" uiSettingsDiscardInit(
&UI_SETTINGS_GAMEPLAY.discard, &UI_SETTINGS_GAMEPLAY.menu,
uiSettingsGameplayHasChanges, uiSettingsGameplayLoad
); );
uiMenuSetItems(menu, UI_SETTINGS_GAMEPLAY.items, menuIndex, 1);
errorOk(); errorOk();
} }
@@ -72,3 +83,41 @@ void uiSettingsGameplayOpen(void) {
void uiSettingsGameplayClose(void) { void uiSettingsGameplayClose(void) {
uiMenuClose(&UI_SETTINGS_GAMEPLAY.menu); uiMenuClose(&UI_SETTINGS_GAMEPLAY.menu);
} }
errorret_t uiSettingsGameplayUpdate(void) {
return uiSettingsDiscardUpdate(&UI_SETTINGS_GAMEPLAY.discard);
}
void uiSettingsGameplayLoad(void) {
UI_SETTINGS_GAMEPLAY.loadedEnableThing = uiCheckboxIsChecked(
&UI_SETTINGS_GAMEPLAY.items[UI_SETTINGS_GAMEPLAY_INDEX_ENABLE_THING].checkbox
);
UI_SETTINGS_GAMEPLAY.loadedTestValue = uiSliderGetFloat(
&UI_SETTINGS_GAMEPLAY.items[UI_SETTINGS_GAMEPLAY_INDEX_TEST_SLIDER].slider
);
}
void uiSettingsGameplayApply(void) {
UI_SETTINGS_GAMEPLAY.loadedEnableThing = uiCheckboxIsChecked(
&UI_SETTINGS_GAMEPLAY.items[UI_SETTINGS_GAMEPLAY_INDEX_ENABLE_THING].checkbox
);
UI_SETTINGS_GAMEPLAY.loadedTestValue = uiSliderGetFloat(
&UI_SETTINGS_GAMEPLAY.items[UI_SETTINGS_GAMEPLAY_INDEX_TEST_SLIDER].slider
);
}
bool_t uiSettingsGameplayHasChanges(void) {
if(
uiCheckboxIsChecked(
&UI_SETTINGS_GAMEPLAY.items[UI_SETTINGS_GAMEPLAY_INDEX_ENABLE_THING].checkbox
) != UI_SETTINGS_GAMEPLAY.loadedEnableThing
) return true;
if(
uiSliderGetFloat(
&UI_SETTINGS_GAMEPLAY.items[UI_SETTINGS_GAMEPLAY_INDEX_TEST_SLIDER].slider
) != UI_SETTINGS_GAMEPLAY.loadedTestValue
) return true;
return false;
}
@@ -8,13 +8,20 @@
#pragma once #pragma once
#include "error/error.h" #include "error/error.h"
#include "ui/widget/uimenu.h" #include "ui/widget/uimenu.h"
#include "uisettingsdiscard.h"
#define UI_SETTINGS_GAMEPLAY_ITEM_COUNT 5 #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_SHOW_CONFIRM 4
#define UI_SETTINGS_GAMEPLAY_INDEX_APPLY 5
typedef struct { typedef struct {
uimenu_t menu; uimenu_t menu;
uimenuitem_t items[UI_SETTINGS_GAMEPLAY_ITEM_COUNT]; uimenuitem_t items[UI_SETTINGS_GAMEPLAY_ITEM_COUNT];
uisettingsdiscard_t discard;
bool_t loadedEnableThing;
float_t loadedTestValue;
} uisettingsgameplay_t; } uisettingsgameplay_t;
extern uisettingsgameplay_t UI_SETTINGS_GAMEPLAY; extern uisettingsgameplay_t UI_SETTINGS_GAMEPLAY;
@@ -42,6 +49,14 @@ errorret_t uiSettingsGameplayDraw(
const float_t height const float_t height
); );
/**
* Updates the gameplay settings page. Shows a discard-changes
* confirmation if the page was just backed out of with unsaved changes.
*
* @return Any error that occurs.
*/
errorret_t uiSettingsGameplayUpdate(void);
/** /**
* Returns true when the gameplay settings page is currently open. * Returns true when the gameplay settings page is currently open.
* *
@@ -58,3 +73,20 @@ void uiSettingsGameplayOpen(void);
* Closes the gameplay settings page. No-op when already closed. * Closes the gameplay settings page. No-op when already closed.
*/ */
void uiSettingsGameplayClose(void); void uiSettingsGameplayClose(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);
+71 -7
View File
@@ -6,21 +6,55 @@
*/ */
#include "uisettingsinput.h" #include "uisettingsinput.h"
#include "assert/assert.h"
#include "util/memory.h" #include "util/memory.h"
#include "locale/localemanager.h"
#include "asset/loader/locale/assetlocaleloader.h"
#include "input/input.h"
uisettingsinput_t UI_SETTINGS_INPUT; uisettingsinput_t UI_SETTINGS_INPUT;
void uiSettingsInputSelected(
const uimenu_t *menu,
const uint8_t index,
const uimenuitem_t *item
) {
if(index != UI_SETTINGS_INPUT_INDEX_APPLY) return;
uiSettingsInputApply();
}
void uiSettingsInputClosed(const uimenu_t *menu) {
uiSettingsDiscardClosed(&UI_SETTINGS_INPUT.discard);
}
errorret_t uiSettingsInputInit(void) { errorret_t uiSettingsInputInit(void) {
memoryZero(&UI_SETTINGS_INPUT, sizeof(uisettingsinput_t)); memoryZero(&UI_SETTINGS_INPUT, sizeof(uisettingsinput_t));
uimenu_t *menu = &UI_SETTINGS_INPUT.menu; MENU_BEGIN(
uiMenuInit(menu, NULL, NULL, NULL); &UI_SETTINGS_INPUT.menu, UI_SETTINGS_INPUT.items,
menu->items = UI_SETTINGS_INPUT.items; uiSettingsInputSelected, uiSettingsInputClosed, NULL
);
#ifdef DUSK_INPUT_GAMEPAD
errorChain(assetLocaleGetString(
&LOCALE.entry->data.locale,
"ui.settings.input.deadzone",
0,
UI_SETTINGS_INPUT.deadzoneLabel,
UI_SETTINGS_INPUT_LABEL_MAX
));
MENU_SLIDER_FLOAT(
UI_SETTINGS_INPUT.deadzoneLabel, INPUT_DEADZONE_DEFAULT, 0.0f, 1.0f, 0.05f
);
#else
MENU_LABEL("No input settings yet");
#endif
MENU_BUTTON("Apply");
MENU_END(UI_SETTINGS_INPUT.items, 1);
MENU_BEGIN(); uiSettingsDiscardInit(
MENU_LABEL("No input settings yet"); &UI_SETTINGS_INPUT.discard, &UI_SETTINGS_INPUT.menu,
uiSettingsInputHasChanges, uiSettingsInputLoad
uiMenuSetItems(menu, UI_SETTINGS_INPUT.items, menuIndex, 1); );
errorOk(); errorOk();
} }
@@ -45,3 +79,33 @@ void uiSettingsInputOpen(void) {
void uiSettingsInputClose(void) { void uiSettingsInputClose(void) {
uiMenuClose(&UI_SETTINGS_INPUT.menu); uiMenuClose(&UI_SETTINGS_INPUT.menu);
} }
errorret_t uiSettingsInputUpdate(void) {
return uiSettingsDiscardUpdate(&UI_SETTINGS_INPUT.discard);
}
void uiSettingsInputLoad(void) {
#ifdef DUSK_INPUT_GAMEPAD
uiSliderSetFloat(
&UI_SETTINGS_INPUT.items[UI_SETTINGS_INPUT_INDEX_DEADZONE].slider,
INPUT.deadzone
);
#endif
}
void uiSettingsInputApply(void) {
#ifdef DUSK_INPUT_GAMEPAD
INPUT.deadzone = uiSliderGetFloat(
&UI_SETTINGS_INPUT.items[UI_SETTINGS_INPUT_INDEX_DEADZONE].slider
);
#endif
}
bool_t uiSettingsInputHasChanges(void) {
#ifdef DUSK_INPUT_GAMEPAD
if(uiSliderGetFloat(
&UI_SETTINGS_INPUT.items[UI_SETTINGS_INPUT_INDEX_DEADZONE].slider
) != INPUT.deadzone) return true;
#endif
return false;
}
+32 -1
View File
@@ -8,12 +8,18 @@
#pragma once #pragma once
#include "error/error.h" #include "error/error.h"
#include "ui/widget/uimenu.h" #include "ui/widget/uimenu.h"
#include "uisettingsdiscard.h"
#define UI_SETTINGS_INPUT_ITEM_COUNT 1 #define UI_SETTINGS_INPUT_ITEM_COUNT 2
#define UI_SETTINGS_INPUT_LABEL_MAX 32
#define UI_SETTINGS_INPUT_INDEX_DEADZONE 0
#define UI_SETTINGS_INPUT_INDEX_APPLY 1
typedef struct { typedef struct {
uimenu_t menu; uimenu_t menu;
uimenuitem_t items[UI_SETTINGS_INPUT_ITEM_COUNT]; uimenuitem_t items[UI_SETTINGS_INPUT_ITEM_COUNT];
uisettingsdiscard_t discard;
char_t deadzoneLabel[UI_SETTINGS_INPUT_LABEL_MAX];
} uisettingsinput_t; } uisettingsinput_t;
extern uisettingsinput_t UI_SETTINGS_INPUT; extern uisettingsinput_t UI_SETTINGS_INPUT;
@@ -41,6 +47,14 @@ errorret_t uiSettingsInputDraw(
const float_t height const float_t height
); );
/**
* Updates the input settings page. Shows a discard-changes confirmation
* if the page was just backed out of with unsaved changes.
*
* @return Any error that occurs.
*/
errorret_t uiSettingsInputUpdate(void);
/** /**
* Returns true when the input settings page is currently open. * Returns true when the input settings page is currently open.
* *
@@ -57,3 +71,20 @@ void uiSettingsInputOpen(void);
* Closes the input settings page. No-op when already closed. * Closes the input settings page. No-op when already closed.
*/ */
void uiSettingsInputClose(void); void uiSettingsInputClose(void);
/**
* Loads the page's widgets from the current engine input settings.
*/
void uiSettingsInputLoad(void);
/**
* Applies the page's widget values to the engine input settings.
*/
void uiSettingsInputApply(void);
/**
* Returns whether any widget's value differs from what was loaded.
*
* @returns True if there are unsaved changes.
*/
bool_t uiSettingsInputHasChanges(void);
+4 -11
View File
@@ -40,20 +40,13 @@ void uiConfirmClosed(const uimenu_t *menu) {
errorret_t uiConfirmInit(void) { errorret_t uiConfirmInit(void) {
memoryZero(&UI_CONFIRM, sizeof(uiconfirm_t)); memoryZero(&UI_CONFIRM, sizeof(uiconfirm_t));
uimenu_t *menu = &UI_CONFIRM.menu; MENU_BEGIN(
uiMenuInit(menu, uiConfirmSelected, uiConfirmClosed, NULL); &UI_CONFIRM.menu, UI_CONFIRM.items, uiConfirmSelected, uiConfirmClosed, NULL
menu->items = UI_CONFIRM.items; );
MENU_BEGIN();
MENU_BUTTON("Confirm"); MENU_BUTTON("Confirm");
MENU_BUTTON("Cancel"); MENU_BUTTON("Cancel");
assertTrue( MENU_END(UI_CONFIRM.items, menuIndex);
menuIndex <= UI_CONFIRM_ITEM_COUNT,
"Confirm item count mismatch"
);
uiMenuSetItems(menu, UI_CONFIRM.items, menuIndex, menuIndex);
errorOk(); errorOk();
} }
+1
View File
@@ -43,6 +43,7 @@ uielement_t UI_ELEMENTS[] = {
{ {
.init = uiSettingsInit, .init = uiSettingsInit,
.update = uiSettingsUpdate,
.draw = uiSettingsDraw, .draw = uiSettingsDraw,
.dispose = uiSettingsDispose .dispose = uiSettingsDispose
}, },
+16 -1
View File
@@ -212,33 +212,44 @@ bool_t uiMenuFocusDirection(
); );
// Helper macros // Helper macros
#define MENU_BEGIN() uint8_t menuIndex = 0 #define MENU_BEGIN(menuPtr, itemsArray, selected, closed, changed) \
uimenu_t *menu = (menuPtr); \
uiMenuInit(menu, selected, closed, changed); \
menu->items = (itemsArray); \
uint8_t menuIndex = 0; \
uint8_t menuCapacity = sizeof(itemsArray) / sizeof((itemsArray)[0])
#define MENU_LABEL(text) \ #define MENU_LABEL(text) \
assertTrue(menuIndex < menuCapacity, "Menu item count exceeds capacity"); \
menu->items[menuIndex].type = UI_MENU_WIDGET_TYPE_LABEL; \ menu->items[menuIndex].type = UI_MENU_WIDGET_TYPE_LABEL; \
menu->items[menuIndex].label = text; \ menu->items[menuIndex].label = text; \
++menuIndex ++menuIndex
#define MENU_SPACER() \ #define MENU_SPACER() \
assertTrue(menuIndex < menuCapacity, "Menu item count exceeds capacity"); \
menu->items[menuIndex].type = UI_MENU_WIDGET_TYPE_SPACER; \ menu->items[menuIndex].type = UI_MENU_WIDGET_TYPE_SPACER; \
++menuIndex ++menuIndex
#define MENU_CHECKBOX(label) \ #define MENU_CHECKBOX(label) \
assertTrue(menuIndex < menuCapacity, "Menu item count exceeds capacity"); \
menu->items[menuIndex].type = UI_MENU_WIDGET_TYPE_CHECKBOX; \ menu->items[menuIndex].type = UI_MENU_WIDGET_TYPE_CHECKBOX; \
uiCheckboxInit(&menu->items[menuIndex].checkbox, label); \ uiCheckboxInit(&menu->items[menuIndex].checkbox, label); \
++menuIndex ++menuIndex
#define MENU_BUTTON(label) \ #define MENU_BUTTON(label) \
assertTrue(menuIndex < menuCapacity, "Menu item count exceeds capacity"); \
menu->items[menuIndex].type = UI_MENU_WIDGET_TYPE_BUTTON; \ menu->items[menuIndex].type = UI_MENU_WIDGET_TYPE_BUTTON; \
uiButtonInit(&menu->items[menuIndex].button, label); \ uiButtonInit(&menu->items[menuIndex].button, label); \
++menuIndex ++menuIndex
#define MENU_TAB(label) \ #define MENU_TAB(label) \
assertTrue(menuIndex < menuCapacity, "Menu item count exceeds capacity"); \
menu->items[menuIndex].type = UI_MENU_WIDGET_TYPE_TAB; \ menu->items[menuIndex].type = UI_MENU_WIDGET_TYPE_TAB; \
uiTabInit(&menu->items[menuIndex].tab, label); \ uiTabInit(&menu->items[menuIndex].tab, label); \
++menuIndex ++menuIndex
#define MENU_SLIDER_FLOAT(label, value, min, max, step) \ #define MENU_SLIDER_FLOAT(label, value, min, max, step) \
assertTrue(menuIndex < menuCapacity, "Menu item count exceeds capacity"); \
menu->items[menuIndex].type = UI_MENU_WIDGET_TYPE_SLIDER; \ menu->items[menuIndex].type = UI_MENU_WIDGET_TYPE_SLIDER; \
uiSliderInitFloat( \ uiSliderInitFloat( \
&menu->items[menuIndex].slider, label, value, min, max, step \ &menu->items[menuIndex].slider, label, value, min, max, step \
@@ -246,10 +257,14 @@ bool_t uiMenuFocusDirection(
++menuIndex ++menuIndex
#define MENU_SLIDER_INT(label, value, min, max, step) \ #define MENU_SLIDER_INT(label, value, min, max, step) \
assertTrue(menuIndex < menuCapacity, "Menu item count exceeds capacity"); \
menu->items[menuIndex].type = UI_MENU_WIDGET_TYPE_SLIDER; \ menu->items[menuIndex].type = UI_MENU_WIDGET_TYPE_SLIDER; \
uiSliderInitInt( \ uiSliderInitInt( \
&menu->items[menuIndex].slider, label, value, min, max, step \ &menu->items[menuIndex].slider, label, value, min, max, step \
); \ ); \
++menuIndex ++menuIndex
#define MENU_END(itemsArray, columns) \
uiMenuSetItems(menu, (itemsArray), menuIndex, (columns))
//EOF //EOF