ui2: port controls dialog

This commit is contained in:
Marcin Kurczewski 2025-04-16 11:04:41 +02:00
parent dfa3797b87
commit e3240b166c
34 changed files with 691 additions and 1091 deletions

View file

@ -341,15 +341,15 @@
}, },
"game_strings": { "game_strings": {
"CONTROL_BACKEND_CONTROLLER": "Controller", "CONTROLS_BACKEND_CONTROLLER": "Controller",
"CONTROL_BACKEND_KEYBOARD": "Keyboard", "CONTROLS_BACKEND_KEYBOARD": "Keyboard",
"CONTROL_CUSTOMIZE": "Customize Controls", "CONTROLS_CUSTOMIZE": "Customize Controls",
"CONTROL_CUSTOM_1": "User Keys 1", "CONTROLS_CUSTOM_1": "User Keys 1",
"CONTROL_CUSTOM_2": "User Keys 2", "CONTROLS_CUSTOM_2": "User Keys 2",
"CONTROL_CUSTOM_3": "User Keys 3", "CONTROLS_CUSTOM_3": "User Keys 3",
"CONTROL_DEFAULT_KEYS": "Default Keys", "CONTROLS_DEFAULT_KEYS": "Default Keys",
"CONTROL_RESET_DEFAULTS": "Reset All: Hold %s", "CONTROLS_RESET_DEFAULTS": "Reset All: Hold %s",
"CONTROL_UNBIND": "Unbind: Hold %s", "CONTROLS_UNBIND": "Unbind: Hold %s",
"DETAIL_BILINEAR": "Bilinear", "DETAIL_BILINEAR": "Bilinear",
"DETAIL_BRIGHTNESS": "Brightness", "DETAIL_BRIGHTNESS": "Brightness",
"DETAIL_FBO_FILTER": "FBO filter", "DETAIL_FBO_FILTER": "FBO filter",

View file

@ -464,13 +464,13 @@
}, },
"game_strings": { "game_strings": {
"CONTROL_BACKEND_CONTROLLER": "Controller", "CONTROLS_BACKEND_CONTROLLER": "Controller",
"CONTROL_BACKEND_KEYBOARD": "Keyboard", "CONTROLS_BACKEND_KEYBOARD": "Keyboard",
"CONTROL_CUSTOMIZE": "Customize Controls", "CONTROLS_CUSTOMIZE": "Customize Controls",
"CONTROL_CUSTOM_1": "User Keys 1", "CONTROLS_CUSTOM_1": "User Keys 1",
"CONTROL_CUSTOM_2": "User Keys 2", "CONTROLS_CUSTOM_2": "User Keys 2",
"CONTROL_CUSTOM_3": "User Keys 3", "CONTROLS_CUSTOM_3": "User Keys 3",
"CONTROL_DEFAULT_KEYS": "Default Keys", "CONTROLS_DEFAULT_KEYS": "Default Keys",
"DETAIL_FLOAT_FMT": "%.1f", "DETAIL_FLOAT_FMT": "%.1f",
"DETAIL_FOG_END": "Fog end", "DETAIL_FOG_END": "Fog end",
"DETAIL_FOG_START": "Fog start", "DETAIL_FOG_START": "Fog start",

View file

@ -53,10 +53,10 @@ static bool m_IsRoleHardcoded[INPUT_ROLE_NUMBER_OF] = {
}; };
static const GAME_STRING_ID m_LayoutMap[INPUT_LAYOUT_NUMBER_OF] = { static const GAME_STRING_ID m_LayoutMap[INPUT_LAYOUT_NUMBER_OF] = {
[INPUT_LAYOUT_DEFAULT] = GS_ID(CONTROL_DEFAULT_KEYS), [INPUT_LAYOUT_DEFAULT] = GS_ID(CONTROLS_DEFAULT_KEYS),
[INPUT_LAYOUT_CUSTOM_1] = GS_ID(CONTROL_CUSTOM_1), [INPUT_LAYOUT_CUSTOM_1] = GS_ID(CONTROLS_CUSTOM_1),
[INPUT_LAYOUT_CUSTOM_2] = GS_ID(CONTROL_CUSTOM_2), [INPUT_LAYOUT_CUSTOM_2] = GS_ID(CONTROLS_CUSTOM_2),
[INPUT_LAYOUT_CUSTOM_3] = GS_ID(CONTROL_CUSTOM_3), [INPUT_LAYOUT_CUSTOM_3] = GS_ID(CONTROLS_CUSTOM_3),
}; };
static INPUT_BACKEND_IMPL *M_GetBackend(INPUT_BACKEND backend); static INPUT_BACKEND_IMPL *M_GetBackend(INPUT_BACKEND backend);

View file

@ -0,0 +1,88 @@
#include "game/ui2/dialogs/controls.h"
#include "config.h"
#include "game/game_string.h"
#include "game/input.h"
#include "game/ui2/dialogs/controls_backend.h"
#include "game/ui2/dialogs/controls_editor.h"
#include "game/ui2/elements/requester.h"
typedef enum {
M_PHASE_BACKEND,
M_PHASE_EDITOR,
} M_PHASE;
void UI2_Controls_Init(UI2_CONTROLS_STATE *const s)
{
s->events = EventManager_Create();
s->phase = M_PHASE_BACKEND;
s->backend = INPUT_BACKEND_KEYBOARD;
s->active_layout = g_Config.input.keyboard_layout;
UI2_ControlsBackend_Init(&s->backend_state);
UI2_ControlsEditor_Init(&s->editor_state, s->events);
}
void UI2_Controls_Free(UI2_CONTROLS_STATE *const s)
{
UI2_ControlsEditor_Free(&s->editor_state);
UI2_ControlsBackend_Free(&s->backend_state);
EventManager_Free(s->events);
s->events = nullptr;
}
bool UI2_Controls_Control(UI2_CONTROLS_STATE *const s)
{
switch (s->phase) {
case M_PHASE_BACKEND: {
const int32_t choice = UI2_ControlsBackend_Control(&s->backend_state);
switch (choice) {
case UI2_REQUESTER_NO_CHOICE:
return false;
case UI2_REQUESTER_CANCEL:
return true;
case INPUT_BACKEND_KEYBOARD:
s->backend = choice;
s->phase = M_PHASE_EDITOR;
UI2_ControlsEditor_Reinit(
&s->editor_state, choice, g_Config.input.keyboard_layout);
break;
case INPUT_BACKEND_CONTROLLER:
s->backend = choice;
s->phase = M_PHASE_EDITOR;
UI2_ControlsEditor_Reinit(
&s->editor_state, choice, g_Config.input.controller_layout);
break;
}
break;
}
case M_PHASE_EDITOR: {
const UI2_CONTROLS_CHOICE choice =
UI2_ControlsEditor_Control(&s->editor_state);
switch (choice) {
case UI2_CONTROLS_CHOICE_NOOP:
break;
case UI2_CONTROLS_CHOICE_GO_BACK:
s->phase = M_PHASE_BACKEND;
break;
case UI2_CONTROLS_CHOICE_EXIT:
return true;
}
break;
}
}
return false;
}
void UI2_Controls(UI2_CONTROLS_STATE *const s)
{
switch (s->phase) {
case M_PHASE_BACKEND:
UI2_ControlsBackend(&s->backend_state);
break;
case M_PHASE_EDITOR:
UI2_ControlsEditor(&s->editor_state);
break;
}
}

View file

@ -0,0 +1,57 @@
#include "game/ui2/dialogs/controls_backend.h"
#include "game/game_string.h"
#include "game/input.h"
#include "game/ui2/elements/anchor.h"
#include "game/ui2/elements/label.h"
#include "game/ui2/elements/modal.h"
#include "game/ui2/elements/requester.h"
static const GAME_STRING_ID m_Options[] = {
GS_ID(CONTROLS_BACKEND_KEYBOARD),
GS_ID(CONTROLS_BACKEND_CONTROLLER),
nullptr,
};
void UI2_ControlsBackend_Init(UI2_CONTROLS_BACKEND_STATE *const s)
{
int32_t count = 0;
for (count = 0; m_Options[count] != nullptr; count++) { }
UI2_Requester_Init(&s->req, count, count, true);
}
void UI2_ControlsBackend_Free(UI2_CONTROLS_BACKEND_STATE *const s)
{
UI2_Requester_Free(&s->req);
}
int32_t UI2_ControlsBackend_Control(UI2_CONTROLS_BACKEND_STATE *const s)
{
const int32_t choice = UI2_Requester_Control(&s->req);
switch (choice) {
case 0:
return INPUT_BACKEND_KEYBOARD;
case 1:
return INPUT_BACKEND_CONTROLLER;
default:
return choice;
}
}
void UI2_ControlsBackend(UI2_CONTROLS_BACKEND_STATE *const s)
{
UI2_BeginModal(0.5f, 2.0f / 3.0f);
UI2_BeginRequester(&s->req, GS(CONTROLS_CUSTOMIZE));
for (int32_t i = UI2_Requester_GetFirstRow(&s->req);
i < UI2_Requester_GetLastRow(&s->req); i++) {
UI2_BeginRequesterRow(&s->req, i);
UI2_BeginAnchor(0.5f, 0.5f);
UI2_Label(GameString_Get(m_Options[i]));
UI2_EndAnchor();
UI2_EndRequesterRow(&s->req, i);
}
UI2_EndRequester(&s->req);
UI2_EndModal();
}

View file

@ -0,0 +1,377 @@
#include "game/ui2/dialogs/controls_editor.h"
#include "config.h"
#include "game/const.h"
#include "game/game_string.h"
#include "game/input.h"
#include "game/shell.h"
#include "game/ui2/elements/anchor.h"
#include "game/ui2/elements/frame.h"
#include "game/ui2/elements/label.h"
#include "game/ui2/elements/modal.h"
#include "game/ui2/elements/pad.h"
#include "game/ui2/elements/requester.h"
#include "game/ui2/elements/spacer.h"
#include "game/ui2/elements/stack.h"
#include "game/ui2/elements/window.h"
#include "utils.h"
typedef enum {
M_PHASE_NAVIGATE_LAYOUT,
M_PHASE_NAVIGATE_INPUTS,
M_PHASE_NAVIGATE_INPUTS_DEBOUNCE,
M_PHASE_LISTEN,
M_PHASE_LISTEN_DEBOUNCE,
M_PHASE_EXIT,
} M_PHASE;
static const INPUT_ROLE m_LeftRoles[] = {
// clang-format off
INPUT_ROLE_UP,
INPUT_ROLE_DOWN,
INPUT_ROLE_LEFT,
INPUT_ROLE_RIGHT,
INPUT_ROLE_STEP_L,
INPUT_ROLE_STEP_R,
INPUT_ROLE_SLOW,
INPUT_ROLE_ENTER_CONSOLE,
INPUT_ROLE_PAUSE,
INPUT_ROLE_TOGGLE_PHOTO_MODE,
INPUT_ROLE_TOGGLE_UI,
// INPUT_ROLE_CAMERA_RESET, // same as look, no need to configure
INPUT_ROLE_CAMERA_UP,
INPUT_ROLE_CAMERA_DOWN,
INPUT_ROLE_CAMERA_LEFT,
INPUT_ROLE_CAMERA_RIGHT,
INPUT_ROLE_CAMERA_FORWARD,
INPUT_ROLE_CAMERA_BACK,
(INPUT_ROLE)-1,
// clang-format on
};
static const INPUT_ROLE m_RightRoles_CheatsOff[] = {
// clang-format off
INPUT_ROLE_JUMP,
INPUT_ROLE_ACTION,
INPUT_ROLE_DRAW,
#if TR_VERSION == 2
INPUT_ROLE_USE_FLARE,
#endif
INPUT_ROLE_LOOK,
INPUT_ROLE_ROLL,
INPUT_ROLE_OPTION,
(INPUT_ROLE)-1,
// clang-format on
};
static const INPUT_ROLE m_RightRoles_CheatsOn[] = {
// clang-format off
INPUT_ROLE_JUMP,
INPUT_ROLE_ACTION,
INPUT_ROLE_DRAW,
#if TR_VERSION == 2
INPUT_ROLE_USE_FLARE,
#endif
INPUT_ROLE_LOOK,
INPUT_ROLE_ROLL,
INPUT_ROLE_OPTION,
INPUT_ROLE_FLY_CHEAT,
INPUT_ROLE_ITEM_CHEAT,
INPUT_ROLE_LEVEL_SKIP_CHEAT,
INPUT_ROLE_TURBO_CHEAT,
(INPUT_ROLE)-1,
// clang-format on
};
static const INPUT_ROLE *m_RightRoles = nullptr;
static INPUT_ROLE M_GetInputRole(int32_t col, int32_t row);
static int32_t M_GetInputRoleCount(int32_t col);
static void M_CycleLayout(UI2_CONTROLS_EDITOR_STATE *s, int32_t dir);
static UI2_CONTROLS_CHOICE M_NavigateLayout(UI2_CONTROLS_EDITOR_STATE *s);
static UI2_CONTROLS_CHOICE M_NavigateInputs(UI2_CONTROLS_EDITOR_STATE *s);
static UI2_CONTROLS_CHOICE M_NavigateInputsDebounce(
UI2_CONTROLS_EDITOR_STATE *s);
static UI2_CONTROLS_CHOICE M_Listen(UI2_CONTROLS_EDITOR_STATE *s);
static UI2_CONTROLS_CHOICE M_ListenDebounce(UI2_CONTROLS_EDITOR_STATE *s);
static void M_Title(const UI2_CONTROLS_EDITOR_STATE *s);
static void M_InputChoice(UI2_CONTROLS_EDITOR_STATE *s, INPUT_ROLE role);
static void M_InputLabel(const UI2_CONTROLS_EDITOR_STATE *s, INPUT_ROLE role);
static void M_Column(UI2_CONTROLS_EDITOR_STATE *s, const INPUT_ROLE *roles);
static INPUT_ROLE M_GetInputRole(const int32_t col, const int32_t row)
{
if (col == 0) {
return m_LeftRoles[row];
} else {
return m_RightRoles[row];
}
}
static int32_t M_GetInputRoleCount(const int32_t col)
{
int32_t row = 0;
while (M_GetInputRole(col, row) != (INPUT_ROLE)-1) {
row++;
}
return row;
}
static void M_CycleLayout(UI2_CONTROLS_EDITOR_STATE *const s, const int32_t dir)
{
s->active_layout += dir;
s->active_layout += INPUT_LAYOUT_NUMBER_OF;
s->active_layout %= INPUT_LAYOUT_NUMBER_OF;
const EVENT event = {
.name = "layout_change",
.sender = nullptr,
.data = nullptr,
};
EventManager_Fire(s->events, &event);
}
static UI2_CONTROLS_CHOICE M_NavigateLayout(UI2_CONTROLS_EDITOR_STATE *const s)
{
if (g_InputDB.menu_confirm) {
return UI2_CONTROLS_CHOICE_EXIT;
} else if (g_InputDB.menu_back) {
return UI2_CONTROLS_CHOICE_GO_BACK;
} else if (g_InputDB.menu_left) {
M_CycleLayout(s, -1);
} else if (g_InputDB.menu_right) {
M_CycleLayout(s, 1);
} else if (g_InputDB.menu_down && s->active_layout != 0) {
s->phase = M_PHASE_NAVIGATE_INPUTS;
s->active_col = 0;
s->active_row = 0;
} else if (g_InputDB.menu_up && s->active_layout != 0) {
s->phase = M_PHASE_NAVIGATE_INPUTS;
s->active_col = 1;
s->active_row = M_GetInputRoleCount(1) - 1;
} else {
return UI2_CONTROLS_CHOICE_NOOP;
}
s->active_role = M_GetInputRole(s->active_col, s->active_row);
return UI2_CONTROLS_CHOICE_NOOP;
}
static UI2_CONTROLS_CHOICE M_NavigateInputs(UI2_CONTROLS_EDITOR_STATE *const s)
{
if (g_InputDB.menu_confirm) {
s->phase = M_PHASE_NAVIGATE_INPUTS_DEBOUNCE;
} else if (g_InputDB.menu_back) {
return UI2_CONTROLS_CHOICE_GO_BACK;
} else if (g_InputDB.menu_left || g_InputDB.menu_right) {
s->active_col ^= 1;
CLAMP(s->active_row, 0, M_GetInputRoleCount(s->active_col) - 1);
} else if (g_InputDB.menu_up) {
s->active_row--;
if (s->active_row < 0) {
if (s->active_col == 0) {
s->phase = M_PHASE_NAVIGATE_LAYOUT;
} else {
s->active_col = 0;
s->active_row = M_GetInputRoleCount(0) - 1;
}
}
} else if (g_InputDB.menu_down) {
s->active_row++;
if (s->active_row >= M_GetInputRoleCount(s->active_col)) {
if (s->active_col == 0) {
s->active_col = 1;
s->active_row = 0;
} else {
s->phase = M_PHASE_NAVIGATE_LAYOUT;
}
}
} else {
return UI2_CONTROLS_CHOICE_NOOP;
}
s->active_role = M_GetInputRole(s->active_col, s->active_row);
return UI2_CONTROLS_CHOICE_NOOP;
}
static UI2_CONTROLS_CHOICE M_NavigateInputsDebounce(
UI2_CONTROLS_EDITOR_STATE *const s)
{
Shell_ProcessEvents();
Input_Update();
if (g_Input.any) {
return UI2_CONTROLS_CHOICE_NOOP;
}
Input_EnterListenMode();
s->phase = M_PHASE_LISTEN;
return UI2_CONTROLS_CHOICE_NOOP;
}
static UI2_CONTROLS_CHOICE M_Listen(UI2_CONTROLS_EDITOR_STATE *const s)
{
if (!Input_ReadAndAssignRole(
s->backend, s->active_layout, s->active_role)) {
return UI2_CONTROLS_CHOICE_NOOP;
}
Input_ExitListenMode();
const EVENT event = {
.name = "key_change",
.sender = nullptr,
.data = nullptr,
};
EventManager_Fire(s->events, &event);
s->phase = M_PHASE_LISTEN_DEBOUNCE;
return UI2_CONTROLS_CHOICE_NOOP;
}
static UI2_CONTROLS_CHOICE M_ListenDebounce(UI2_CONTROLS_EDITOR_STATE *const s)
{
if (!g_Input.any) {
s->phase = M_PHASE_NAVIGATE_INPUTS;
}
return UI2_CONTROLS_CHOICE_NOOP;
}
static void M_Title(const UI2_CONTROLS_EDITOR_STATE *const s)
{
UI2_BeginAnchor(0.5f, 0.5f);
if (s->phase == M_PHASE_NAVIGATE_LAYOUT) {
UI2_BeginFrame(UI2_FRAME_SELECTED_OPTION);
}
UI2_BeginPad(2.0f, 1.0f);
UI2_Label(Input_GetLayoutName(s->active_layout));
UI2_EndPad();
if (s->phase == M_PHASE_NAVIGATE_LAYOUT) {
UI2_EndFrame();
}
UI2_EndAnchor();
}
static void M_InputLabel(
const UI2_CONTROLS_EDITOR_STATE *const s, const INPUT_ROLE role)
{
const bool is_selected = s->active_role == role
&& (s->phase == M_PHASE_NAVIGATE_INPUTS
|| s->phase == M_PHASE_NAVIGATE_INPUTS_DEBOUNCE);
if (is_selected) {
UI2_BeginFrame(UI2_FRAME_SELECTED_OPTION);
}
UI2_Label(Input_GetRoleName(role));
if (is_selected) {
UI2_EndFrame();
}
}
static void M_InputChoice(
UI2_CONTROLS_EDITOR_STATE *const s, const INPUT_ROLE role)
{
const bool is_flashing =
Input_IsKeyConflicted(s->backend, s->active_layout, role);
const bool is_selected =
s->active_role == role && s->phase == M_PHASE_LISTEN;
if (is_flashing) {
UI2_BeginFlash(&s->flash);
}
if (is_selected) {
UI2_BeginFrame(UI2_FRAME_SELECTED_OPTION);
}
UI2_Label(Input_GetKeyName(s->backend, s->active_layout, role));
if (is_selected) {
UI2_EndFrame();
}
if (is_flashing) {
UI2_EndFlash();
}
}
static void M_Column(
UI2_CONTROLS_EDITOR_STATE *const s, const INPUT_ROLE *const roles)
{
UI2_BeginStack(UI2_STACK_HORIZONTAL);
UI2_BeginStack(UI2_STACK_VERTICAL);
for (const INPUT_ROLE *role = roles; *role != (INPUT_ROLE)-1; role++) {
M_InputChoice(s, *role);
}
UI2_EndStack();
UI2_Spacer(10.0f, 0.0f);
UI2_BeginStack(UI2_STACK_VERTICAL);
for (const INPUT_ROLE *role = roles; *role != (INPUT_ROLE)-1; role++) {
M_InputLabel(s, *role);
}
UI2_EndStack();
UI2_EndStack();
}
void UI2_ControlsEditor_Init(
UI2_CONTROLS_EDITOR_STATE *const s, EVENT_MANAGER *events)
{
m_RightRoles = g_Config.gameplay.enable_cheats ? m_RightRoles_CheatsOn
: m_RightRoles_CheatsOff;
s->events = events;
UI2_Flash_Init(&s->flash, LOGIC_FPS * 2 / 3);
}
void UI2_ControlsEditor_Free(UI2_CONTROLS_EDITOR_STATE *const s)
{
UI2_Flash_Free(&s->flash);
}
void UI2_ControlsEditor_Reinit(
UI2_CONTROLS_EDITOR_STATE *s, INPUT_BACKEND backend, int32_t layout)
{
s->backend = backend;
s->active_layout = layout;
s->active_row = 0;
s->active_col = 0;
s->active_role = M_GetInputRole(s->active_col, s->active_row);
s->phase = M_PHASE_NAVIGATE_LAYOUT;
}
UI2_CONTROLS_CHOICE UI2_ControlsEditor_Control(
UI2_CONTROLS_EDITOR_STATE *const s)
{
UI2_Flash_Control(&s->flash);
switch (s->phase) {
case M_PHASE_NAVIGATE_LAYOUT:
return M_NavigateLayout(s);
case M_PHASE_NAVIGATE_INPUTS:
return M_NavigateInputs(s);
case M_PHASE_NAVIGATE_INPUTS_DEBOUNCE:
return M_NavigateInputsDebounce(s);
case M_PHASE_LISTEN:
return M_Listen(s);
case M_PHASE_LISTEN_DEBOUNCE:
return M_ListenDebounce(s);
default:
return UI2_CONTROLS_CHOICE_NOOP;
}
}
void UI2_ControlsEditor(UI2_CONTROLS_EDITOR_STATE *const s)
{
UI2_BeginModal(0.5f, 0.5f);
UI2_BeginWindow();
UI2_WindowTitle(GS(CONTROLS_CUSTOMIZE));
UI2_BeginWindowBody();
UI2_BeginStackEx((UI2_STACK_SETTINGS) {
.orientation = UI2_STACK_VERTICAL,
.align = { .h = UI2_STACK_H_ALIGN_SPAN },
});
M_Title(s);
UI2_Spacer(0.0f, 5.0f);
UI2_BeginStack(UI2_STACK_HORIZONTAL);
M_Column(s, m_LeftRoles);
UI2_Spacer(10.0f, 0.0f);
M_Column(s, m_RightRoles);
UI2_EndStack();
UI2_EndStack();
UI2_EndWindowBody();
UI2_EndWindow();
UI2_EndModal();
}

View file

@ -61,13 +61,13 @@ GS_DEFINE(OSD_AMBIGUOUS_INPUT_2, "Ambiguous input: %s and %s")
GS_DEFINE(OSD_AMBIGUOUS_INPUT_3, "Ambiguous input: %s, %s, ...") GS_DEFINE(OSD_AMBIGUOUS_INPUT_3, "Ambiguous input: %s, %s, ...")
GS_DEFINE(OSD_UI_ON, "UI enabled") GS_DEFINE(OSD_UI_ON, "UI enabled")
GS_DEFINE(OSD_UI_OFF, "UI disabled") GS_DEFINE(OSD_UI_OFF, "UI disabled")
GS_DEFINE(CONTROL_DEFAULT_KEYS, "Default Keys") GS_DEFINE(CONTROLS_DEFAULT_KEYS, "Default Keys")
GS_DEFINE(CONTROL_CUSTOM_1, "User Keys 1") GS_DEFINE(CONTROLS_CUSTOM_1, "User Keys 1")
GS_DEFINE(CONTROL_CUSTOM_2, "User Keys 2") GS_DEFINE(CONTROLS_CUSTOM_2, "User Keys 2")
GS_DEFINE(CONTROL_CUSTOM_3, "User Keys 3") GS_DEFINE(CONTROLS_CUSTOM_3, "User Keys 3")
GS_DEFINE(CONTROL_BACKEND_KEYBOARD, "Keyboard") GS_DEFINE(CONTROLS_BACKEND_KEYBOARD, "Keyboard")
GS_DEFINE(CONTROL_BACKEND_CONTROLLER, "Controller") GS_DEFINE(CONTROLS_BACKEND_CONTROLLER, "Controller")
GS_DEFINE(CONTROL_CUSTOMIZE, "Customize Controls") GS_DEFINE(CONTROLS_CUSTOMIZE, "Customize Controls")
GS_DEFINE(KEYMAP_RUN, "Run") GS_DEFINE(KEYMAP_RUN, "Run")
GS_DEFINE(KEYMAP_BACK, "Back") GS_DEFINE(KEYMAP_BACK, "Back")
GS_DEFINE(KEYMAP_LEFT, "Left") GS_DEFINE(KEYMAP_LEFT, "Left")

View file

@ -101,3 +101,5 @@ bool Input_AssignToJSONObject(
INPUT_ROLE role); INPUT_ROLE role);
INPUT_STATE Input_GetDebounced(const INPUT_STATE input); INPUT_STATE Input_GetDebounced(const INPUT_STATE input);
extern const char *Input_GetRoleName(INPUT_ROLE role);

View file

@ -1,6 +1,8 @@
#pragma once #pragma once
#include "./ui2/common.h" #include "./ui2/common.h"
#include "./ui2/dialogs/controls.h"
#include "./ui2/dialogs/controls_backend.h"
#include "./ui2/dialogs/examine_item.h" #include "./ui2/dialogs/examine_item.h"
#include "./ui2/dialogs/new_game.h" #include "./ui2/dialogs/new_game.h"
#include "./ui2/dialogs/pause.h" #include "./ui2/dialogs/pause.h"

View file

@ -0,0 +1,27 @@
#pragma once
// A controls editor dialog.
#include "../../../event_manager.h"
#include "../../input.h"
#include "../common.h"
#include "./controls_backend.h"
#include "./controls_editor.h"
typedef struct {
int32_t phase;
INPUT_BACKEND backend;
int32_t active_layout;
EVENT_MANAGER *events;
UI2_CONTROLS_BACKEND_STATE backend_state;
UI2_CONTROLS_EDITOR_STATE editor_state;
} UI2_CONTROLS_STATE;
// state functions
void UI2_Controls_Init(UI2_CONTROLS_STATE *s);
void UI2_Controls_Free(UI2_CONTROLS_STATE *s);
bool UI2_Controls_Control(UI2_CONTROLS_STATE *s);
// draw functions
void UI2_Controls(UI2_CONTROLS_STATE *s);

View file

@ -0,0 +1,18 @@
#pragma once
// A control backend (keyboard/controller) choice dialog.
#include "../common.h"
#include "../elements/requester.h"
typedef struct {
UI2_REQUESTER_STATE req;
} UI2_CONTROLS_BACKEND_STATE;
// state functions
void UI2_ControlsBackend_Init(UI2_CONTROLS_BACKEND_STATE *s);
void UI2_ControlsBackend_Free(UI2_CONTROLS_BACKEND_STATE *s);
int32_t UI2_ControlsBackend_Control(UI2_CONTROLS_BACKEND_STATE *s);
// draw functions
void UI2_ControlsBackend(UI2_CONTROLS_BACKEND_STATE *s);

View file

@ -0,0 +1,36 @@
#pragma once
// A controls remapper dialog.
#include "../../input.h"
#include "../common.h"
#include "../elements/flash.h"
#include "../elements/requester.h"
typedef struct {
int32_t phase;
INPUT_BACKEND backend;
int32_t active_layout;
INPUT_ROLE active_role;
int32_t active_col;
int32_t active_row;
UI2_FLASH_STATE flash;
EVENT_MANAGER *events;
} UI2_CONTROLS_EDITOR_STATE;
typedef enum {
UI2_CONTROLS_CHOICE_EXIT,
UI2_CONTROLS_CHOICE_GO_BACK,
UI2_CONTROLS_CHOICE_NOOP,
} UI2_CONTROLS_CHOICE;
// state functions
void UI2_ControlsEditor_Init(
UI2_CONTROLS_EDITOR_STATE *s, EVENT_MANAGER *events);
void UI2_ControlsEditor_Free(UI2_CONTROLS_EDITOR_STATE *s);
void UI2_ControlsEditor_Reinit(
UI2_CONTROLS_EDITOR_STATE *s, INPUT_BACKEND backend, int32_t layout);
UI2_CONTROLS_CHOICE UI2_ControlsEditor_Control(UI2_CONTROLS_EDITOR_STATE *s);
// draw functions
void UI2_ControlsEditor(UI2_CONTROLS_EDITOR_STATE *s);

View file

@ -209,11 +209,14 @@ sources = [
'game/ui/widgets/stack.c', 'game/ui/widgets/stack.c',
'game/ui/widgets/window.c', 'game/ui/widgets/window.c',
'game/ui2/common.c', 'game/ui2/common.c',
'game/ui2/dialogs/controls.c',
'game/ui2/dialogs/controls_backend.c',
'game/ui2/dialogs/controls_editor.c',
'game/ui2/dialogs/examine_item.c', 'game/ui2/dialogs/examine_item.c',
'game/ui2/dialogs/stats.c',
'game/ui2/dialogs/new_game.c', 'game/ui2/dialogs/new_game.c',
'game/ui2/dialogs/pause.c', 'game/ui2/dialogs/pause.c',
'game/ui2/dialogs/photo_mode.c', 'game/ui2/dialogs/photo_mode.c',
'game/ui2/dialogs/stats.c',
'game/ui2/elements/anchor.c', 'game/ui2/elements/anchor.c',
'game/ui2/elements/fade.c', 'game/ui2/elements/fade.c',
'game/ui2/elements/flash.c', 'game/ui2/elements/flash.c',

View file

@ -18,8 +18,8 @@ GS_DEFINE(DETAIL_RENDER_MODE_FBO, "Framebuffer")
GS_DEFINE(DETAIL_RESOLUTION, "Resolution") GS_DEFINE(DETAIL_RESOLUTION, "Resolution")
GS_DEFINE(DETAIL_STRING_FMT, "%s") GS_DEFINE(DETAIL_STRING_FMT, "%s")
GS_DEFINE(DETAIL_RESOLUTION_FMT, "%dx%d") GS_DEFINE(DETAIL_RESOLUTION_FMT, "%dx%d")
GS_DEFINE(CONTROL_RESET_DEFAULTS, "Reset All: Hold %s") GS_DEFINE(CONTROLS_RESET_DEFAULTS, "Reset All: Hold %s")
GS_DEFINE(CONTROL_UNBIND, "Unbind: Hold %s") GS_DEFINE(CONTROLS_UNBIND, "Unbind: Hold %s")
GS_DEFINE(KEYMAP_CHANGE_TARGET, "Change Target") GS_DEFINE(KEYMAP_CHANGE_TARGET, "Change Target")
GS_DEFINE(KEYMAP_EQUIP_PISTOLS, "Equip Pistols") GS_DEFINE(KEYMAP_EQUIP_PISTOLS, "Equip Pistols")
GS_DEFINE(KEYMAP_EQUIP_SHOTGUN, "Equip Shotgun") GS_DEFINE(KEYMAP_EQUIP_SHOTGUN, "Equip Shotgun")

View file

@ -129,3 +129,9 @@ void Input_Update(void)
g_InputDB.any = 0; g_InputDB.any = 0;
} }
} }
const char *Input_GetRoleName(const INPUT_ROLE role)
{
// TODO: implement me
return nullptr;
}

View file

@ -295,10 +295,10 @@ static void M_InitText(INPUT_BACKEND backend, INPUT_LAYOUT layout)
Text_AddOutline(m_Text[TEXT_TITLE_BORDER], TS_BACKGROUND); Text_AddOutline(m_Text[TEXT_TITLE_BORDER], TS_BACKGROUND);
sprintf( sprintf(
m_ResetGS, GS(CONTROL_RESET_DEFAULTS), m_ResetGS, GS(CONTROLS_RESET_DEFAULTS),
Input_GetKeyName(backend, layout, INPUT_ROLE_RESET_BINDINGS)); Input_GetKeyName(backend, layout, INPUT_ROLE_RESET_BINDINGS));
sprintf( sprintf(
m_UnbindGS, GS(CONTROL_UNBIND), m_UnbindGS, GS(CONTROLS_UNBIND),
Input_GetKeyName(backend, layout, INPUT_ROLE_UNBIND_KEY)); Input_GetKeyName(backend, layout, INPUT_ROLE_UNBIND_KEY));
m_Text[TEXT_RESET] = m_Text[TEXT_RESET] =
@ -340,11 +340,11 @@ static void M_UpdateText(INPUT_BACKEND backend, INPUT_LAYOUT layout)
} }
sprintf( sprintf(
m_ResetGS, GS(CONTROL_RESET_DEFAULTS), m_ResetGS, GS(CONTROLS_RESET_DEFAULTS),
Input_GetKeyName(backend, layout, INPUT_ROLE_RESET_BINDINGS)); Input_GetKeyName(backend, layout, INPUT_ROLE_RESET_BINDINGS));
Text_ChangeText(m_Text[TEXT_RESET], m_ResetGS); Text_ChangeText(m_Text[TEXT_RESET], m_ResetGS);
sprintf( sprintf(
m_UnbindGS, GS(CONTROL_UNBIND), m_UnbindGS, GS(CONTROLS_UNBIND),
Input_GetKeyName(backend, layout, INPUT_ROLE_UNBIND_KEY)); Input_GetKeyName(backend, layout, INPUT_ROLE_UNBIND_KEY));
Text_ChangeText(m_Text[TEXT_UNBIND], m_UnbindGS); Text_ChangeText(m_Text[TEXT_UNBIND], m_UnbindGS);

View file

@ -29,13 +29,13 @@ static void M_InitText(void)
Text_AddBackground(m_Text[TEXT_TITLE_BORDER], 180, 85, 0, 0, TS_BACKGROUND); Text_AddBackground(m_Text[TEXT_TITLE_BORDER], 180, 85, 0, 0, TS_BACKGROUND);
Text_AddOutline(m_Text[TEXT_TITLE_BORDER], TS_BACKGROUND); Text_AddOutline(m_Text[TEXT_TITLE_BORDER], TS_BACKGROUND);
m_Text[TEXT_TITLE] = Text_Create(0, -30, GS(CONTROL_CUSTOMIZE)); m_Text[TEXT_TITLE] = Text_Create(0, -30, GS(CONTROLS_CUSTOMIZE));
Text_AddBackground(m_Text[TEXT_TITLE], 176, 0, 0, 0, TS_HEADING); Text_AddBackground(m_Text[TEXT_TITLE], 176, 0, 0, 0, TS_HEADING);
Text_AddOutline(m_Text[TEXT_TITLE], TS_HEADING); Text_AddOutline(m_Text[TEXT_TITLE], TS_HEADING);
m_Text[TEXT_KEYBOARD] = Text_Create(0, 0, GS(CONTROL_BACKEND_KEYBOARD)); m_Text[TEXT_KEYBOARD] = Text_Create(0, 0, GS(CONTROLS_BACKEND_KEYBOARD));
m_Text[TEXT_CONTROLLER] = m_Text[TEXT_CONTROLLER] =
Text_Create(0, 25, GS(CONTROL_BACKEND_CONTROLLER)); Text_Create(0, 25, GS(CONTROLS_BACKEND_CONTROLLER));
Text_AddBackground(m_Text[g_OptionSelected], 128, 0, 0, 0, TS_REQUESTED); Text_AddBackground(m_Text[g_OptionSelected], 128, 0, 0, 0, TS_REQUESTED);
Text_AddOutline(m_Text[g_OptionSelected], TS_REQUESTED); Text_AddOutline(m_Text[g_OptionSelected], TS_REQUESTED);

View file

@ -1,5 +1,3 @@
#pragma once #pragma once
#include <libtrx/game/input.h> #include <libtrx/game/input.h>
const char *Input_GetRoleName(INPUT_ROLE role);

View file

@ -1,61 +1,57 @@
#include "game/option/option.h" #include "game/option/option.h"
#include "game/ui/widgets/controls_dialog.h"
#include "global/vars.h" #include "global/vars.h"
#include <libtrx/config.h> #include <libtrx/config.h>
#include <libtrx/game/ui/events.h> #include <libtrx/game/ui2.h>
static UI_WIDGET *m_Dialog; typedef struct {
static UI_CONTROLS_CONTROLLER m_Controller; int32_t listeners[2];
static int32_t m_Listener1; struct {
static int32_t m_Listener2; bool is_ready;
UI2_CONTROLS_STATE state;
} ui;
} M_PRIV;
static void M_Init(void); static M_PRIV m_Priv = {};
static void M_Shutdown(void);
static void M_Init(M_PRIV *p);
static void M_Shutdown(M_PRIV *p);
static void M_HandleLayoutChange(const EVENT *event, void *user_data); static void M_HandleLayoutChange(const EVENT *event, void *user_data);
static void M_HandleKeyChange(const EVENT *event, void *user_data); static void M_HandleKeyChange(const EVENT *event, void *user_data);
static void M_Init(void) static void M_Init(M_PRIV *const p)
{ {
UI_ControlsController_Init(&m_Controller); UI2_Controls_Init(&p->ui.state);
m_Controller.active_layout = g_Config.input.keyboard_layout; p->ui.is_ready = true;
p->listeners[0] = EventManager_Subscribe(
m_Dialog = UI_ControlsDialog_Create(&m_Controller); p->ui.state.events, "layout_change", nullptr, M_HandleLayoutChange, p);
m_Listener1 = EventManager_Subscribe( p->listeners[1] = EventManager_Subscribe(
m_Controller.events, "layout_change", nullptr, M_HandleLayoutChange, p->ui.state.events, "key_change", nullptr, M_HandleKeyChange, p);
nullptr);
m_Listener2 = EventManager_Subscribe(
m_Controller.events, "key_change", nullptr, M_HandleKeyChange, nullptr);
} }
static void M_Shutdown(void) static void M_Shutdown(M_PRIV *const p)
{ {
if (m_Dialog == nullptr) { if (p->ui.is_ready) {
return; EventManager_Unsubscribe(p->ui.state.events, p->listeners[0]);
EventManager_Unsubscribe(p->ui.state.events, p->listeners[1]);
UI2_Controls_Free(&p->ui.state);
p->ui.is_ready = false;
} }
m_Dialog->free(m_Dialog);
m_Dialog = nullptr;
EventManager_Unsubscribe(m_Controller.events, m_Listener1);
EventManager_Unsubscribe(m_Controller.events, m_Listener2);
UI_ControlsController_Shutdown(&m_Controller);
} }
static void M_HandleLayoutChange(const EVENT *event, void *user_data) static void M_HandleLayoutChange(const EVENT *event, void *user_data)
{ {
switch (m_Controller.backend) { const M_PRIV *const p = user_data;
switch (p->ui.state.backend) {
case INPUT_BACKEND_KEYBOARD: case INPUT_BACKEND_KEYBOARD:
g_Config.input.keyboard_layout = m_Controller.active_layout; g_Config.input.keyboard_layout = p->ui.state.active_layout;
break; break;
case INPUT_BACKEND_CONTROLLER: case INPUT_BACKEND_CONTROLLER:
g_Config.input.controller_layout = m_Controller.active_layout; g_Config.input.controller_layout = p->ui.state.active_layout;
break; break;
default: default:
break; break;
} }
Config_Write(); Config_Write();
} }
@ -66,22 +62,22 @@ static void M_HandleKeyChange(const EVENT *event, void *user_data)
void Option_Controls_Shutdown(void) void Option_Controls_Shutdown(void)
{ {
M_Shutdown(); M_Shutdown(&m_Priv);
} }
void Option_Controls_Control(INVENTORY_ITEM *const item, const bool is_busy) void Option_Controls_Control(INVENTORY_ITEM *const item, const bool is_busy)
{ {
M_PRIV *const p = &m_Priv;
if (is_busy) { if (is_busy) {
return; return;
} }
if (m_Dialog == nullptr) { if (!p->ui.is_ready) {
M_Init(); M_Init(p);
} }
m_Dialog->control(m_Dialog); if (UI2_Controls_Control(&p->ui.state)) {
if (m_Controller.state == UI_CONTROLS_STATE_EXIT) { M_Shutdown(p);
Option_Controls_Shutdown();
} else { } else {
g_Input = (INPUT_STATE) {}; g_Input = (INPUT_STATE) {};
g_InputDB = (INPUT_STATE) {}; g_InputDB = (INPUT_STATE) {};
@ -90,7 +86,8 @@ void Option_Controls_Control(INVENTORY_ITEM *const item, const bool is_busy)
void Option_Controls_Draw(INVENTORY_ITEM *const item) void Option_Controls_Draw(INVENTORY_ITEM *const item)
{ {
if (m_Dialog != nullptr) { M_PRIV *const p = &m_Priv;
m_Dialog->draw(m_Dialog); if (p->ui.is_ready) {
UI2_Controls(&p->ui.state);
} }
} }

View file

@ -1,285 +0,0 @@
#include "game/ui/controllers/controls.h"
#include "game/input.h"
#include "game/shell.h"
#include "global/vars.h"
#include <libtrx/config.h>
#include <libtrx/debug.h>
#include <libtrx/game/ui/events.h>
#include <libtrx/utils.h>
static const INPUT_ROLE m_LeftRoles[] = {
// clang-format off
INPUT_ROLE_UP,
INPUT_ROLE_DOWN,
INPUT_ROLE_LEFT,
INPUT_ROLE_RIGHT,
INPUT_ROLE_STEP_L,
INPUT_ROLE_STEP_R,
INPUT_ROLE_SLOW,
INPUT_ROLE_ENTER_CONSOLE,
INPUT_ROLE_PAUSE,
INPUT_ROLE_TOGGLE_PHOTO_MODE,
INPUT_ROLE_TOGGLE_UI,
// INPUT_ROLE_CAMERA_RESET, // same as look, no need to configure
INPUT_ROLE_CAMERA_UP,
INPUT_ROLE_CAMERA_DOWN,
INPUT_ROLE_CAMERA_LEFT,
INPUT_ROLE_CAMERA_RIGHT,
INPUT_ROLE_CAMERA_FORWARD,
INPUT_ROLE_CAMERA_BACK,
(INPUT_ROLE)-1,
// clang-format on
};
static const INPUT_ROLE m_RightRoles_CheatsOff[] = {
// clang-format off
INPUT_ROLE_JUMP,
INPUT_ROLE_ACTION,
INPUT_ROLE_DRAW,
INPUT_ROLE_USE_FLARE,
INPUT_ROLE_LOOK,
INPUT_ROLE_ROLL,
INPUT_ROLE_OPTION,
(INPUT_ROLE)-1,
// clang-format on
};
static const INPUT_ROLE m_RightRoles_CheatsOn[] = {
// clang-format off
INPUT_ROLE_JUMP,
INPUT_ROLE_ACTION,
INPUT_ROLE_DRAW,
INPUT_ROLE_USE_FLARE,
INPUT_ROLE_LOOK,
INPUT_ROLE_ROLL,
INPUT_ROLE_OPTION,
INPUT_ROLE_FLY_CHEAT,
INPUT_ROLE_ITEM_CHEAT,
INPUT_ROLE_LEVEL_SKIP_CHEAT,
INPUT_ROLE_TURBO_CHEAT,
(INPUT_ROLE)-1,
// clang-format on
};
static INPUT_ROLE M_GetInputRole(int32_t col, int32_t row);
static void M_CycleLayout(UI_CONTROLS_CONTROLLER *controller, int32_t dir);
static bool M_NavigateLayout(UI_CONTROLS_CONTROLLER *controller);
static bool M_NavigateInputs(UI_CONTROLS_CONTROLLER *controller);
static bool M_NavigateInputsDebounce(UI_CONTROLS_CONTROLLER *controller);
static bool M_Listen(UI_CONTROLS_CONTROLLER *controller);
static bool M_ListenDebounce(UI_CONTROLS_CONTROLLER *controller);
static INPUT_ROLE M_GetInputRole(const int32_t col, const int32_t row)
{
if (col == 0) {
return m_LeftRoles[row];
} else if (g_Config.gameplay.enable_cheats) {
return m_RightRoles_CheatsOn[row];
} else {
return m_RightRoles_CheatsOff[row];
}
}
static void M_CycleLayout(
UI_CONTROLS_CONTROLLER *const controller, const int32_t dir)
{
controller->active_layout += dir;
controller->active_layout += INPUT_LAYOUT_NUMBER_OF;
controller->active_layout %= INPUT_LAYOUT_NUMBER_OF;
const EVENT event = {
.name = "layout_change",
.sender = nullptr,
.data = nullptr,
};
EventManager_Fire(controller->events, &event);
}
static bool M_NavigateBackend(UI_CONTROLS_CONTROLLER *const controller)
{
if (g_InputDB.menu_down && controller->backend == INPUT_BACKEND_KEYBOARD) {
controller->backend = INPUT_BACKEND_CONTROLLER;
return true;
}
if (g_InputDB.menu_up && controller->backend == INPUT_BACKEND_CONTROLLER) {
controller->backend = INPUT_BACKEND_KEYBOARD;
return true;
}
if (g_InputDB.menu_back) {
controller->state = UI_CONTROLS_STATE_EXIT;
return true;
}
if (g_InputDB.menu_confirm) {
controller->state = UI_CONTROLS_STATE_NAVIGATE_LAYOUT;
return true;
}
return false;
}
static bool M_NavigateLayout(UI_CONTROLS_CONTROLLER *const controller)
{
if (g_InputDB.menu_confirm) {
controller->state = UI_CONTROLS_STATE_EXIT;
} else if (g_InputDB.menu_back) {
controller->state = UI_CONTROLS_STATE_NAVIGATE_BACKEND;
} else if (g_InputDB.menu_left) {
M_CycleLayout(controller, -1);
} else if (g_InputDB.menu_right) {
M_CycleLayout(controller, 1);
} else if (g_InputDB.menu_down && controller->active_layout != 0) {
controller->state = UI_CONTROLS_STATE_NAVIGATE_INPUTS;
controller->active_col = 0;
controller->active_row = 0;
} else if (g_InputDB.menu_up && controller->active_layout != 0) {
controller->state = UI_CONTROLS_STATE_NAVIGATE_INPUTS;
controller->active_col = 1;
controller->active_row = UI_ControlsController_GetInputRoleCount(1) - 1;
} else {
return false;
}
controller->active_role =
M_GetInputRole(controller->active_col, controller->active_row);
return true;
}
static bool M_NavigateInputs(UI_CONTROLS_CONTROLLER *const controller)
{
if (g_InputDB.menu_back) {
controller->state = UI_CONTROLS_STATE_NAVIGATE_BACKEND;
} else if (g_InputDB.menu_left || g_InputDB.menu_right) {
controller->active_col ^= 1;
CLAMP(
controller->active_row, 0,
UI_ControlsController_GetInputRoleCount(controller->active_col)
- 1);
} else if (g_InputDB.menu_up) {
controller->active_row--;
if (controller->active_row < 0) {
if (controller->active_col == 0) {
controller->state = UI_CONTROLS_STATE_NAVIGATE_LAYOUT;
} else {
controller->active_col = 0;
controller->active_row =
UI_ControlsController_GetInputRoleCount(0) - 1;
}
}
} else if (g_InputDB.menu_down) {
controller->active_row++;
if (controller->active_row >= UI_ControlsController_GetInputRoleCount(
controller->active_col)) {
if (controller->active_col == 0) {
controller->active_col = 1;
controller->active_row = 0;
} else {
controller->state = UI_CONTROLS_STATE_NAVIGATE_LAYOUT;
}
}
} else if (g_InputDB.menu_confirm) {
controller->state = UI_CONTROLS_STATE_NAVIGATE_INPUTS_DEBOUNCE;
} else {
return false;
}
controller->active_role =
M_GetInputRole(controller->active_col, controller->active_row);
return true;
}
static bool M_NavigateInputsDebounce(UI_CONTROLS_CONTROLLER *const controller)
{
Shell_ProcessEvents();
Input_Update();
if (g_Input.any) {
return false;
}
Input_EnterListenMode();
controller->state = UI_CONTROLS_STATE_LISTEN;
return true;
}
static bool M_Listen(UI_CONTROLS_CONTROLLER *const controller)
{
if (!Input_ReadAndAssignRole(
controller->backend, controller->active_layout,
controller->active_role)) {
return false;
}
Input_ExitListenMode();
const EVENT event = {
.name = "key_change",
.sender = nullptr,
.data = nullptr,
};
EventManager_Fire(controller->events, &event);
controller->state = UI_CONTROLS_STATE_LISTEN_DEBOUNCE;
return true;
}
static bool M_ListenDebounce(UI_CONTROLS_CONTROLLER *const controller)
{
if (g_Input.any) {
return false;
}
controller->state = UI_CONTROLS_STATE_NAVIGATE_INPUTS;
return true;
}
void UI_ControlsController_Init(UI_CONTROLS_CONTROLLER *const controller)
{
ASSERT(controller->events == nullptr);
controller->backend = INPUT_BACKEND_KEYBOARD;
controller->state = UI_CONTROLS_STATE_NAVIGATE_BACKEND;
controller->events = EventManager_Create();
}
void UI_ControlsController_Shutdown(UI_CONTROLS_CONTROLLER *const controller)
{
EventManager_Free(controller->events);
controller->events = nullptr;
}
bool UI_ControlsController_Control(UI_CONTROLS_CONTROLLER *const controller)
{
switch (controller->state) {
case UI_CONTROLS_STATE_NAVIGATE_BACKEND:
return M_NavigateBackend(controller);
case UI_CONTROLS_STATE_NAVIGATE_LAYOUT:
return M_NavigateLayout(controller);
case UI_CONTROLS_STATE_NAVIGATE_INPUTS:
return M_NavigateInputs(controller);
case UI_CONTROLS_STATE_NAVIGATE_INPUTS_DEBOUNCE:
return M_NavigateInputsDebounce(controller);
case UI_CONTROLS_STATE_LISTEN:
return M_Listen(controller);
case UI_CONTROLS_STATE_LISTEN_DEBOUNCE:
return M_ListenDebounce(controller);
default:
return false;
}
return false;
}
INPUT_ROLE UI_ControlsController_GetInputRole(
const int32_t col, const int32_t row)
{
return M_GetInputRole(col, row);
}
int32_t UI_ControlsController_GetInputRoleCount(const int32_t col)
{
int32_t row = 0;
while (M_GetInputRole(col, row) != (INPUT_ROLE)-1) {
row++;
}
return row;
}

View file

@ -1,33 +0,0 @@
#pragma once
#include "game/input.h"
#include <libtrx/event_manager.h>
typedef enum {
UI_CONTROLS_STATE_NAVIGATE_BACKEND,
UI_CONTROLS_STATE_NAVIGATE_LAYOUT,
UI_CONTROLS_STATE_NAVIGATE_INPUTS,
UI_CONTROLS_STATE_NAVIGATE_INPUTS_DEBOUNCE,
UI_CONTROLS_STATE_LISTEN,
UI_CONTROLS_STATE_LISTEN_DEBOUNCE,
UI_CONTROLS_STATE_EXIT,
} UI_CONTROLS_STATE;
typedef struct {
INPUT_BACKEND backend;
UI_CONTROLS_STATE state;
int32_t active_layout;
INPUT_ROLE active_role;
int32_t active_col;
int32_t active_row;
EVENT_MANAGER *events;
} UI_CONTROLS_CONTROLLER;
void UI_ControlsController_Init(UI_CONTROLS_CONTROLLER *controller);
void UI_ControlsController_Shutdown(UI_CONTROLS_CONTROLLER *controller);
bool UI_ControlsController_Control(UI_CONTROLS_CONTROLLER *controller);
INPUT_ROLE UI_ControlsController_GetInputRole(int32_t col, int32_t row);
int32_t UI_ControlsController_GetInputRoleCount(int32_t col);

View file

@ -1,111 +0,0 @@
#include "game/ui/widgets/controls_backend_selector.h"
#include "game/game_string.h"
#include <libtrx/game/ui/widgets/label.h>
#include <libtrx/game/ui/widgets/stack.h>
#include <libtrx/memory.h>
typedef struct {
UI_WIDGET_VTABLE vtable;
UI_WIDGET *keyboard_label;
UI_WIDGET *controller_label;
UI_WIDGET *container;
UI_CONTROLS_CONTROLLER *controller;
} UI_CONTROLS_BACKEND_SELECTOR;
static void M_SyncOutlines(UI_CONTROLS_BACKEND_SELECTOR *self);
static void M_UpdateText(UI_CONTROLS_BACKEND_SELECTOR *self);
static int32_t M_GetWidth(const UI_CONTROLS_BACKEND_SELECTOR *self);
static int32_t M_GetHeight(const UI_CONTROLS_BACKEND_SELECTOR *self);
static void M_SetPosition(
UI_CONTROLS_BACKEND_SELECTOR *self, int32_t x, int32_t y);
static void M_Control(UI_CONTROLS_BACKEND_SELECTOR *self);
static void M_Draw(UI_CONTROLS_BACKEND_SELECTOR *self);
static void M_Free(UI_CONTROLS_BACKEND_SELECTOR *self);
static void M_SyncOutlines(UI_CONTROLS_BACKEND_SELECTOR *const self)
{
UI_Label_RemoveFrame(self->keyboard_label);
UI_Label_RemoveFrame(self->controller_label);
switch (self->controller->backend) {
case INPUT_BACKEND_KEYBOARD:
UI_Label_AddFrame(self->keyboard_label);
break;
case INPUT_BACKEND_CONTROLLER:
UI_Label_AddFrame(self->controller_label);
break;
default:
break;
}
}
static int32_t M_GetWidth(const UI_CONTROLS_BACKEND_SELECTOR *const self)
{
return self->container->get_width(self->container);
}
static int32_t M_GetHeight(const UI_CONTROLS_BACKEND_SELECTOR *const self)
{
return self->container->get_height(self->container);
}
static void M_SetPosition(
UI_CONTROLS_BACKEND_SELECTOR *const self, const int32_t x, const int32_t y)
{
self->container->set_position(self->container, x, y);
}
static void M_Control(UI_CONTROLS_BACKEND_SELECTOR *const self)
{
M_SyncOutlines(self);
}
static void M_Draw(UI_CONTROLS_BACKEND_SELECTOR *const self)
{
if (self->container->draw != nullptr) {
self->container->draw(self->container);
}
}
static void M_Free(UI_CONTROLS_BACKEND_SELECTOR *const self)
{
self->keyboard_label->free(self->keyboard_label);
self->controller_label->free(self->controller_label);
self->container->free(self->container);
Memory_Free(self);
}
UI_WIDGET *UI_ControlsBackendSelector_Create(
UI_CONTROLS_CONTROLLER *const controller)
{
UI_CONTROLS_BACKEND_SELECTOR *const self =
Memory_Alloc(sizeof(UI_CONTROLS_BACKEND_SELECTOR));
self->vtable = (UI_WIDGET_VTABLE) {
.get_width = (UI_WIDGET_GET_WIDTH)M_GetWidth,
.get_height = (UI_WIDGET_GET_HEIGHT)M_GetHeight,
.set_position = (UI_WIDGET_SET_POSITION)M_SetPosition,
.control = (UI_WIDGET_CONTROL)M_Control,
.draw = (UI_WIDGET_DRAW)M_Draw,
.free = (UI_WIDGET_FREE)M_Free,
};
self->controller = controller;
self->keyboard_label =
UI_Label_Create(GS(CONTROL_BACKEND_KEYBOARD), UI_LABEL_AUTO_SIZE, 16);
self->controller_label =
UI_Label_Create(GS(CONTROL_BACKEND_CONTROLLER), UI_LABEL_AUTO_SIZE, 16);
self->container = UI_Stack_Create(
UI_STACK_LAYOUT_VERTICAL, UI_STACK_AUTO_SIZE, UI_STACK_AUTO_SIZE);
UI_Stack_AddChild(self->container, self->keyboard_label);
UI_Stack_AddChild(self->container, self->controller_label);
UI_Stack_SetHAlign(self->container, UI_STACK_H_ALIGN_CENTER);
M_SyncOutlines(self);
return (UI_WIDGET *)self;
}

View file

@ -1,8 +0,0 @@
#pragma once
#include "game/ui/controllers/controls.h"
#include <libtrx/game/ui/widgets/base.h>
UI_WIDGET *UI_ControlsBackendSelector_Create(
UI_CONTROLS_CONTROLLER *controller);

View file

@ -1,87 +0,0 @@
#include "game/ui/widgets/controls_column.h"
#include "game/ui/widgets/controls_input_selector.h"
#include <libtrx/game/ui/widgets/stack.h>
#include <libtrx/memory.h>
typedef struct {
UI_WIDGET_VTABLE vtable;
UI_WIDGET *container;
int32_t selector_count;
UI_WIDGET **selectors;
} UI_CONTROLS_COLUMN;
static int32_t M_GetWidth(const UI_CONTROLS_COLUMN *self);
static int32_t M_GetHeight(const UI_CONTROLS_COLUMN *self);
static void M_SetPosition(UI_CONTROLS_COLUMN *self, int32_t x, int32_t y);
static void M_Control(UI_CONTROLS_COLUMN *self);
static void M_Draw(UI_CONTROLS_COLUMN *self);
static void M_Free(UI_CONTROLS_COLUMN *self);
static int32_t M_GetWidth(const UI_CONTROLS_COLUMN *const self)
{
return self->container->get_width(self->container);
}
static int32_t M_GetHeight(const UI_CONTROLS_COLUMN *const self)
{
return self->container->get_height(self->container);
}
static void M_SetPosition(
UI_CONTROLS_COLUMN *const self, const int32_t x, const int32_t y)
{
self->container->set_position(self->container, x, y);
}
static void M_Control(UI_CONTROLS_COLUMN *const self)
{
if (self->container->control != nullptr) {
self->container->control(self->container);
}
}
static void M_Draw(UI_CONTROLS_COLUMN *const self)
{
if (self->container->draw != nullptr) {
self->container->draw(self->container);
}
}
static void M_Free(UI_CONTROLS_COLUMN *const self)
{
for (int32_t i = 0; i < self->selector_count; i++) {
self->selectors[i]->free(self->selectors[i]);
}
self->container->free(self->container);
Memory_FreePointer(&self->selectors);
Memory_Free(self);
}
UI_WIDGET *UI_ControlsColumn_Create(
const int32_t column, UI_CONTROLS_CONTROLLER *const controller)
{
UI_CONTROLS_COLUMN *const self = Memory_Alloc(sizeof(UI_CONTROLS_COLUMN));
self->vtable = (UI_WIDGET_VTABLE) {
.get_width = (UI_WIDGET_GET_WIDTH)M_GetWidth,
.get_height = (UI_WIDGET_GET_HEIGHT)M_GetHeight,
.set_position = (UI_WIDGET_SET_POSITION)M_SetPosition,
.control = (UI_WIDGET_CONTROL)M_Control,
.draw = (UI_WIDGET_DRAW)M_Draw,
.free = (UI_WIDGET_FREE)M_Free,
};
self->selector_count = UI_ControlsController_GetInputRoleCount(column);
self->container = UI_Stack_Create(
UI_STACK_LAYOUT_VERTICAL, UI_STACK_AUTO_SIZE, UI_STACK_AUTO_SIZE);
self->selectors = Memory_Alloc(sizeof(UI_WIDGET *) * self->selector_count);
for (int32_t i = 0; i < self->selector_count; i++) {
self->selectors[i] = UI_ControlsInputSelector_Create(
UI_ControlsController_GetInputRole(column, i), controller);
UI_Stack_AddChild(self->container, self->selectors[i]);
}
return (UI_WIDGET *)self;
}

View file

@ -1,8 +0,0 @@
#pragma once
#include "game/ui/controllers/controls.h"
#include <libtrx/game/ui/widgets/base.h>
UI_WIDGET *UI_ControlsColumn_Create(
int32_t column, UI_CONTROLS_CONTROLLER *controller);

View file

@ -1,122 +0,0 @@
#include "game/ui/widgets/controls_dialog.h"
#include "game/game_string.h"
#include "game/ui/widgets/controls_backend_selector.h"
#include "game/ui/widgets/controls_column.h"
#include "game/ui/widgets/controls_layout_editor.h"
#include <libtrx/game/ui/common.h>
#include <libtrx/game/ui/widgets/window.h>
#include <libtrx/memory.h>
typedef struct {
UI_WIDGET_VTABLE vtable;
UI_CONTROLS_CONTROLLER *controller;
UI_WIDGET *window;
UI_WIDGET *backend_selector;
UI_WIDGET *layout_editor;
int32_t listener;
} UI_CONTROLS_DIALOG;
static void M_DoLayout(UI_CONTROLS_DIALOG *self);
static void M_HandleCanvasResize(const EVENT *event, void *data);
static int32_t M_GetWidth(const UI_CONTROLS_DIALOG *self);
static int32_t M_GetHeight(const UI_CONTROLS_DIALOG *self);
static void M_SetPosition(UI_CONTROLS_DIALOG *self, int32_t x, int32_t y);
static void M_Control(UI_CONTROLS_DIALOG *self);
static void M_Draw(UI_CONTROLS_DIALOG *self);
static void M_Free(UI_CONTROLS_DIALOG *self);
static void M_DoLayout(UI_CONTROLS_DIALOG *const self)
{
M_SetPosition(
self, (UI_GetCanvasWidth() - M_GetWidth(self)) / 2,
(UI_GetCanvasHeight() - M_GetHeight(self)) * 2 / 3);
}
static void M_HandleCanvasResize(const EVENT *event, void *data)
{
UI_CONTROLS_DIALOG *const self = (UI_CONTROLS_DIALOG *)data;
M_DoLayout(self);
}
static int32_t M_GetWidth(const UI_CONTROLS_DIALOG *const self)
{
return self->window->get_width(self->window);
}
static int32_t M_GetHeight(const UI_CONTROLS_DIALOG *const self)
{
return self->window->get_height(self->window);
}
static void M_SetPosition(
UI_CONTROLS_DIALOG *const self, const int32_t x, const int32_t y)
{
self->window->set_position(self->window, x, y);
}
static void M_Control(UI_CONTROLS_DIALOG *const self)
{
// Trigger the UI updates only if anything has changed.
if (UI_ControlsController_Control(self->controller)) {
// Set the root widget - backend selector modal or the inputs modal
if (self->controller->state == UI_CONTROLS_STATE_NAVIGATE_BACKEND
|| self->controller->state == UI_CONTROLS_STATE_EXIT) {
UI_Window_SetTitle(self->window, GS(CONTROL_CUSTOMIZE));
UI_Window_SetRootWidget(self->window, self->backend_selector);
} else {
UI_Window_SetTitle(self->window, nullptr);
UI_Window_SetRootWidget(self->window, self->layout_editor);
}
M_DoLayout(self);
if (self->window->control != nullptr) {
self->window->control(self->window);
}
}
}
static void M_Draw(UI_CONTROLS_DIALOG *const self)
{
if (self->window->draw != nullptr) {
self->window->draw(self->window);
}
}
static void M_Free(UI_CONTROLS_DIALOG *const self)
{
self->layout_editor->free(self->layout_editor);
self->backend_selector->free(self->backend_selector);
self->window->free(self->window);
UI_Events_Unsubscribe(self->listener);
Memory_Free(self);
}
UI_WIDGET *UI_ControlsDialog_Create(UI_CONTROLS_CONTROLLER *const controller)
{
UI_CONTROLS_DIALOG *const self = Memory_Alloc(sizeof(UI_CONTROLS_DIALOG));
self->vtable = (UI_WIDGET_VTABLE) {
.get_width = (UI_WIDGET_GET_WIDTH)M_GetWidth,
.get_height = (UI_WIDGET_GET_HEIGHT)M_GetHeight,
.set_position = (UI_WIDGET_SET_POSITION)M_SetPosition,
.control = (UI_WIDGET_CONTROL)M_Control,
.draw = (UI_WIDGET_DRAW)M_Draw,
.free = (UI_WIDGET_FREE)M_Free,
};
self->controller = controller;
self->layout_editor = UI_ControlsLayoutEditor_Create(self->controller);
self->backend_selector =
UI_ControlsBackendSelector_Create(self->controller);
self->window = UI_Window_Create(self->backend_selector, 5, 5, 10, 5);
UI_Window_SetTitle(self->window, GS(CONTROL_CUSTOMIZE));
self->listener = UI_Events_Subscribe(
"canvas_resize", nullptr, M_HandleCanvasResize, self);
M_DoLayout(self);
return (UI_WIDGET *)self;
}

View file

@ -1,7 +0,0 @@
#pragma once
#include "game/ui/controllers/controls.h"
#include <libtrx/game/ui/widgets/base.h>
UI_WIDGET *UI_ControlsDialog_Create(UI_CONTROLS_CONTROLLER *controller);

View file

@ -1,135 +0,0 @@
#include "game/ui/widgets/controls_input_selector.h"
#include <libtrx/game/ui/widgets/label.h>
#include <libtrx/game/ui/widgets/stack.h>
#include <libtrx/memory.h>
#include <stdio.h>
#include <string.h>
typedef struct {
UI_WIDGET_VTABLE vtable;
INPUT_ROLE input_role;
UI_WIDGET *label;
UI_WIDGET *choice;
UI_WIDGET *container;
UI_CONTROLS_CONTROLLER *controller;
} UI_CONTROLS_INPUT_SELECTOR;
static void M_UpdateText(UI_CONTROLS_INPUT_SELECTOR *self);
static int32_t M_GetWidth(const UI_CONTROLS_INPUT_SELECTOR *self);
static int32_t M_GetHeight(const UI_CONTROLS_INPUT_SELECTOR *self);
static void M_SetPosition(
UI_CONTROLS_INPUT_SELECTOR *self, int32_t x, int32_t y);
static void M_Control(UI_CONTROLS_INPUT_SELECTOR *self);
static void M_Draw(UI_CONTROLS_INPUT_SELECTOR *self);
static void M_Free(UI_CONTROLS_INPUT_SELECTOR *self);
static void M_UpdateText(UI_CONTROLS_INPUT_SELECTOR *const self)
{
const char *const key_name = Input_GetKeyName(
self->controller->backend, self->controller->active_layout,
self->input_role);
UI_Label_ChangeText(self->choice, key_name);
const char *const role_name = Input_GetRoleName(self->input_role);
char role_name_padded[strlen(role_name) + 2];
sprintf(role_name_padded, "%s ", role_name);
UI_Label_ChangeText(self->label, role_name_padded);
}
static int32_t M_GetWidth(const UI_CONTROLS_INPUT_SELECTOR *const self)
{
return self->container->get_width(self->container);
}
static int32_t M_GetHeight(const UI_CONTROLS_INPUT_SELECTOR *const self)
{
return self->container->get_height(self->container);
}
static void M_SetPosition(
UI_CONTROLS_INPUT_SELECTOR *const self, const int32_t x, const int32_t y)
{
self->container->set_position(self->container, x, y);
}
static void M_Control(UI_CONTROLS_INPUT_SELECTOR *const self)
{
if (self->label->control != nullptr) {
self->label->control(self->label);
}
if (self->choice->control != nullptr) {
self->choice->control(self->choice);
}
// Sync outlines
UI_Label_RemoveFrame(self->label);
UI_Label_RemoveFrame(self->choice);
if (self->controller->active_role == self->input_role) {
if (self->controller->state == UI_CONTROLS_STATE_NAVIGATE_INPUTS
|| self->controller->state
== UI_CONTROLS_STATE_NAVIGATE_INPUTS_DEBOUNCE) {
UI_Label_AddFrame(self->label);
} else if (self->controller->state == UI_CONTROLS_STATE_LISTEN) {
UI_Label_AddFrame(self->choice);
}
}
M_UpdateText(self);
// Flash conflicts
UI_Label_Flash(self->choice, false, 0);
if (Input_IsKeyConflicted(
self->controller->backend, self->controller->active_layout,
self->input_role)) {
UI_Label_Flash(self->choice, true, 20);
}
}
static void M_Draw(UI_CONTROLS_INPUT_SELECTOR *const self)
{
if (self->label->draw != nullptr) {
self->label->draw(self->label);
}
if (self->choice->draw != nullptr) {
self->choice->draw(self->choice);
}
}
static void M_Free(UI_CONTROLS_INPUT_SELECTOR *const self)
{
self->label->free(self->label);
self->choice->free(self->choice);
self->container->free(self->container);
Memory_Free(self);
}
UI_WIDGET *UI_ControlsInputSelector_Create(
const INPUT_ROLE input_role, UI_CONTROLS_CONTROLLER *const controller)
{
UI_CONTROLS_INPUT_SELECTOR *const self =
Memory_Alloc(sizeof(UI_CONTROLS_INPUT_SELECTOR));
self->vtable = (UI_WIDGET_VTABLE) {
.get_width = (UI_WIDGET_GET_WIDTH)M_GetWidth,
.get_height = (UI_WIDGET_GET_HEIGHT)M_GetHeight,
.set_position = (UI_WIDGET_SET_POSITION)M_SetPosition,
.control = (UI_WIDGET_CONTROL)M_Control,
.draw = (UI_WIDGET_DRAW)M_Draw,
.free = (UI_WIDGET_FREE)M_Free,
};
self->controller = controller;
self->input_role = input_role;
self->label = UI_Label_Create("", UI_LABEL_AUTO_SIZE, 15);
self->choice = UI_Label_Create("", 70, 15);
self->container = UI_Stack_Create(
UI_STACK_LAYOUT_HORIZONTAL, UI_STACK_AUTO_SIZE, UI_STACK_AUTO_SIZE);
UI_Stack_AddChild(self->container, self->choice);
UI_Stack_AddChild(self->container, self->label);
// update the text on init
M_UpdateText(self);
return (UI_WIDGET *)self;
}

View file

@ -1,8 +0,0 @@
#pragma once
#include "game/ui/controllers/controls.h"
#include <libtrx/game/ui/widgets/base.h>
UI_WIDGET *UI_ControlsInputSelector_Create(
INPUT_ROLE input_role, UI_CONTROLS_CONTROLLER *controller);

View file

@ -1,103 +0,0 @@
#include "game/ui/widgets/controls_layout_editor.h"
#include "game/ui/widgets/controls_column.h"
#include "game/ui/widgets/controls_layout_selector.h"
#include <libtrx/game/ui/common.h>
#include <libtrx/game/ui/widgets/stack.h>
#include <libtrx/memory.h>
typedef struct {
UI_WIDGET_VTABLE vtable;
UI_CONTROLS_CONTROLLER *controller;
UI_WIDGET *layout_selector;
UI_WIDGET *outer_stack;
UI_WIDGET *column_stack;
UI_WIDGET *left_column;
UI_WIDGET *right_column;
} UI_CONTROLS_LAYOUT_EDITOR;
static void M_DoLayout(UI_CONTROLS_LAYOUT_EDITOR *self);
static int32_t M_GetWidth(const UI_CONTROLS_LAYOUT_EDITOR *self);
static int32_t M_GetHeight(const UI_CONTROLS_LAYOUT_EDITOR *self);
static void M_SetPosition(
UI_CONTROLS_LAYOUT_EDITOR *self, int32_t x, int32_t y);
static void M_Control(UI_CONTROLS_LAYOUT_EDITOR *self);
static void M_Draw(UI_CONTROLS_LAYOUT_EDITOR *self);
static void M_Free(UI_CONTROLS_LAYOUT_EDITOR *self);
static int32_t M_GetWidth(const UI_CONTROLS_LAYOUT_EDITOR *const self)
{
return self->outer_stack->get_width(self->outer_stack);
}
static int32_t M_GetHeight(const UI_CONTROLS_LAYOUT_EDITOR *const self)
{
return self->outer_stack->get_height(self->outer_stack);
}
static void M_SetPosition(
UI_CONTROLS_LAYOUT_EDITOR *const self, const int32_t x, const int32_t y)
{
self->outer_stack->set_position(self->outer_stack, x, y);
}
static void M_Control(UI_CONTROLS_LAYOUT_EDITOR *const self)
{
if (self->outer_stack->control != nullptr) {
self->outer_stack->control(self->outer_stack);
}
// Reposition the header.
UI_Stack_DoLayout(self->outer_stack);
}
static void M_Draw(UI_CONTROLS_LAYOUT_EDITOR *const self)
{
if (self->outer_stack->draw != nullptr) {
self->outer_stack->draw(self->outer_stack);
}
}
static void M_Free(UI_CONTROLS_LAYOUT_EDITOR *const self)
{
self->left_column->free(self->left_column);
self->right_column->free(self->right_column);
self->column_stack->free(self->column_stack);
self->outer_stack->free(self->outer_stack);
self->layout_selector->free(self->layout_selector);
Memory_Free(self);
}
UI_WIDGET *UI_ControlsLayoutEditor_Create(
UI_CONTROLS_CONTROLLER *const controller)
{
UI_CONTROLS_LAYOUT_EDITOR *const self =
Memory_Alloc(sizeof(UI_CONTROLS_LAYOUT_EDITOR));
self->vtable = (UI_WIDGET_VTABLE) {
.get_width = (UI_WIDGET_GET_WIDTH)M_GetWidth,
.get_height = (UI_WIDGET_GET_HEIGHT)M_GetHeight,
.set_position = (UI_WIDGET_SET_POSITION)M_SetPosition,
.control = (UI_WIDGET_CONTROL)M_Control,
.draw = (UI_WIDGET_DRAW)M_Draw,
.free = (UI_WIDGET_FREE)M_Free,
};
self->controller = controller;
self->layout_selector = UI_ControlsLayoutSelector_Create(self->controller);
self->left_column = UI_ControlsColumn_Create(0, self->controller);
self->right_column = UI_ControlsColumn_Create(1, self->controller);
self->column_stack = UI_Stack_Create(
UI_STACK_LAYOUT_HORIZONTAL, UI_STACK_AUTO_SIZE, UI_STACK_AUTO_SIZE);
UI_Stack_AddChild(self->column_stack, self->left_column);
UI_Stack_AddChild(self->column_stack, self->right_column);
self->outer_stack = UI_Stack_Create(
UI_STACK_LAYOUT_VERTICAL, UI_STACK_AUTO_SIZE, UI_STACK_AUTO_SIZE);
UI_Stack_SetHAlign(self->outer_stack, UI_STACK_H_ALIGN_CENTER);
UI_Stack_AddChild(self->outer_stack, self->layout_selector);
UI_Stack_AddChild(self->outer_stack, self->column_stack);
return (UI_WIDGET *)self;
}

View file

@ -1,7 +0,0 @@
#pragma once
#include "game/ui/controllers/controls.h"
#include <libtrx/game/ui/widgets/base.h>
UI_WIDGET *UI_ControlsLayoutEditor_Create(UI_CONTROLS_CONTROLLER *controller);

View file

@ -1,83 +0,0 @@
#include "game/ui/widgets/controls_layout_selector.h"
#include <libtrx/game/ui/widgets/label.h>
#include <libtrx/memory.h>
typedef struct {
UI_WIDGET_VTABLE vtable;
UI_WIDGET *label;
UI_CONTROLS_CONTROLLER *controller;
} UI_CONTROLS_LAYOUT_SELECTOR;
static int32_t M_GetWidth(const UI_CONTROLS_LAYOUT_SELECTOR *self);
static int32_t M_GetHeight(const UI_CONTROLS_LAYOUT_SELECTOR *self);
static void M_SetPosition(
UI_CONTROLS_LAYOUT_SELECTOR *self, int32_t x, int32_t y);
static void M_Control(UI_CONTROLS_LAYOUT_SELECTOR *self);
static void M_Draw(UI_CONTROLS_LAYOUT_SELECTOR *self);
static void M_Free(UI_CONTROLS_LAYOUT_SELECTOR *self);
static int32_t M_GetWidth(const UI_CONTROLS_LAYOUT_SELECTOR *const self)
{
return self->label->get_width(self->label);
}
static int32_t M_GetHeight(const UI_CONTROLS_LAYOUT_SELECTOR *const self)
{
return self->label->get_height(self->label);
}
static void M_SetPosition(
UI_CONTROLS_LAYOUT_SELECTOR *const self, const int32_t x, const int32_t y)
{
self->label->set_position(self->label, x, y);
}
static void M_Control(UI_CONTROLS_LAYOUT_SELECTOR *const self)
{
if (self->controller->state == UI_CONTROLS_STATE_NAVIGATE_LAYOUT) {
UI_Label_AddFrame(self->label);
UI_Label_ChangeText(
self->label, Input_GetLayoutName(self->controller->active_layout));
} else {
UI_Label_RemoveFrame(self->label);
}
if (self->label->control != nullptr) {
self->label->control(self->label);
}
}
static void M_Draw(UI_CONTROLS_LAYOUT_SELECTOR *const self)
{
if (self->label->draw != nullptr) {
self->label->draw(self->label);
}
}
static void M_Free(UI_CONTROLS_LAYOUT_SELECTOR *const self)
{
self->label->free(self->label);
Memory_Free(self);
}
UI_WIDGET *UI_ControlsLayoutSelector_Create(
UI_CONTROLS_CONTROLLER *const controller)
{
UI_CONTROLS_LAYOUT_SELECTOR *self =
Memory_Alloc(sizeof(UI_CONTROLS_LAYOUT_SELECTOR));
self->vtable = (UI_WIDGET_VTABLE) {
.get_width = (UI_WIDGET_GET_WIDTH)M_GetWidth,
.get_height = (UI_WIDGET_GET_HEIGHT)M_GetHeight,
.set_position = (UI_WIDGET_SET_POSITION)M_SetPosition,
.control = (UI_WIDGET_CONTROL)M_Control,
.draw = (UI_WIDGET_DRAW)M_Draw,
.free = (UI_WIDGET_FREE)M_Free,
};
self->controller = controller;
self->label = UI_Label_Create(
Input_GetLayoutName(self->controller->active_layout),
UI_LABEL_AUTO_SIZE, 25);
UI_Label_AddFrame(self->label);
return (UI_WIDGET *)self;
}

View file

@ -1,7 +0,0 @@
#pragma once
#include "game/ui/controllers/controls.h"
#include <libtrx/game/ui/widgets/base.h>
UI_WIDGET *UI_ControlsLayoutSelector_Create(UI_CONTROLS_CONTROLLER *controller);

View file

@ -270,13 +270,6 @@ sources = [
'game/stats.c', 'game/stats.c',
'game/text.c', 'game/text.c',
'game/ui/common.c', 'game/ui/common.c',
'game/ui/controllers/controls.c',
'game/ui/widgets/controls_backend_selector.c',
'game/ui/widgets/controls_column.c',
'game/ui/widgets/controls_dialog.c',
'game/ui/widgets/controls_input_selector.c',
'game/ui/widgets/controls_layout_editor.c',
'game/ui/widgets/controls_layout_selector.c',
'game/ui/widgets/graphics_dialog.c', 'game/ui/widgets/graphics_dialog.c',
'game/ui2/dialogs/stats.c', 'game/ui2/dialogs/stats.c',
'game/viewport.c', 'game/viewport.c',