From ba5f2fbe78d7fe00eb096070071b373b8af11e7e Mon Sep 17 00:00:00 2001 From: EmeraldLockdown <86802223+EmeraldLoc@users.noreply.github.com> Date: Fri, 13 Mar 2026 19:59:15 -0500 Subject: [PATCH] Add missing files, add lang support --- lang/English.ini | 70 +- src/pc/djui/djui_panel_themes.c | 613 ++++++++++++++++++ src/pc/djui/djui_panel_themes.h | 4 + src/pc/djui/djui_theme.c | 2 +- .../custom_font_classic.rgba32.png | Bin 0 -> 11256 bytes 5 files changed, 685 insertions(+), 4 deletions(-) create mode 100644 src/pc/djui/djui_panel_themes.c create mode 100644 src/pc/djui/djui_panel_themes.h create mode 100644 textures/custom_font/custom_font_classic.rgba32.png diff --git a/lang/English.ini b/lang/English.ini index e1b713275..49faf6443 100644 --- a/lang/English.ini +++ b/lang/English.ini @@ -174,6 +174,7 @@ SHOW_FPS = "Show FPS" SHOW_PING = "Show Ping" [DJUI_THEMES] +THEMES_TITLE = "THEMES" DJUI_THEME = "DJUI Theme" DJUI_SCALE = "DJUI Scale" DJUI_FONT = "Font" @@ -188,9 +189,72 @@ FONT_SPECIAL = "Special" FONT_CLASSIC = "Classic" LIGHT_THEME = "Light" DARK_THEME = "Dark" -MARIO_THEME = "Mario" -ODYSSEY_THEME = "Odyssey" -FILE_SELECT_THEME = "File Select" +ELEMENT = "Element" +DJUI_THEME_ELEMENT_PRIMARY = "Primary" +DJUI_THEME_ELEMENT_PRIMARY_HOVER = "Primary (Hover)" +DJUI_THEME_ELEMENT_PRIMARY_DOWN = "Primary (Pressed)" +DJUI_THEME_ELEMENT_PRIMARY_DISABLED = "Primary (Disabled)" +DJUI_THEME_ELEMENT_PRIMARY_TEXT = "Primary Text" +DJUI_THEME_ELEMENT_PRIMARY_TEXT_DISABLED = "Primary Text (Disabled)" +DJUI_THEME_ELEMENT_PRIMARY_BORDER = "Primary Border" +DJUI_THEME_ELEMENT_PRIMARY_BORDER_HOVER = "Primary Border (Hover)" +DJUI_THEME_ELEMENT_PRIMARY_BORDER_DOWN = "Primary Border (Pressed)" +DJUI_THEME_ELEMENT_PRIMARY_BORDER_DISABLED = "Primary Border (Disabled)" +DJUI_THEME_ELEMENT_SECONDARY = "Secondary" +DJUI_THEME_ELEMENT_SECONDARY_HOVER = "Secondary (Hover)" +DJUI_THEME_ELEMENT_SECONDARY_DOWN = "Secondary (Pressed)" +DJUI_THEME_ELEMENT_SECONDARY_DISABLED = "Secondary (Disabled)" +DJUI_THEME_ELEMENT_SECONDARY_TEXT = "Secondary Text" +DJUI_THEME_ELEMENT_SECONDARY_TEXT_DISABLED = "Secondary Text (Disabled)" +DJUI_THEME_ELEMENT_SECONDARY_BORDER = "Secondary Border" +DJUI_THEME_ELEMENT_SECONDARY_BORDER_HOVER = "Secondary Border (Hover)" +DJUI_THEME_ELEMENT_SECONDARY_BORDER_DOWN = "Secondary Border (Pressed)" +DJUI_THEME_ELEMENT_SECONDARY_BORDER_DISABLED = "Secondary Border (Disabled)" +DJUI_THEME_ELEMENT_INPUTBOX = "Input Box" +DJUI_THEME_ELEMENT_INPUTBOX_HOVER = "Input Box (Hover)" +DJUI_THEME_ELEMENT_INPUTBOX_DOWN = "Input Box (Pressed)" +DJUI_THEME_ELEMENT_INPUTBOX_DISABLED = "Input Box (Disabled)" +DJUI_THEME_ELEMENT_INPUTBOX_TEXT = "Input Box Text" +DJUI_THEME_ELEMENT_INPUTBOX_TEXT_PLACEHOLDER = "Input Box Placeholder" +DJUI_THEME_ELEMENT_INPUTBOX_BORDER = "Input Box Border" +DJUI_THEME_ELEMENT_INPUTBOX_BORDER_HOVER = "Input Box Border (Hover)" +DJUI_THEME_ELEMENT_INPUTBOX_BORDER_DOWN = "Input Box Border (Pressed)" +DJUI_THEME_ELEMENT_INPUTBOX_BORDER_DISABLED = "Input Box Border (Disabled)" +DJUI_THEME_ELEMENT_CHECKBOX = "Checkbox" +DJUI_THEME_ELEMENT_CHECKBOX_HOVER = "Checkbox (Hover)" +DJUI_THEME_ELEMENT_CHECKBOX_DOWN = "Checkbox (Pressed)" +DJUI_THEME_ELEMENT_CHECKBOX_DISABLED = "Checkbox (Disabled)" +DJUI_THEME_ELEMENT_CHECKBOX_BORDER = "Checkbox Border" +DJUI_THEME_ELEMENT_CHECKBOX_BORDER_HOVER = "Checkbox Border (Hover)" +DJUI_THEME_ELEMENT_CHECKBOX_BORDER_DOWN = "Checkbox Border (Pressed)" +DJUI_THEME_ELEMENT_CHECKBOX_BORDER_DISABLED = "Checkbox Border (Disabled)" +DJUI_THEME_ELEMENT_SLIDER = "Slider" +DJUI_THEME_ELEMENT_SLIDER_HOVER = "Slider (Hover)" +DJUI_THEME_ELEMENT_SLIDER_DOWN = "Slider (Pressed)" +DJUI_THEME_ELEMENT_SLIDER_DISABLED = "Slider (Disabled)" +DJUI_THEME_ELEMENT_SLIDER_BORDER = "Slider Border" +DJUI_THEME_ELEMENT_SLIDER_BORDER_HOVER = "Slider Border (Hover)" +DJUI_THEME_ELEMENT_SLIDER_BORDER_DOWN = "Slider Border (Pressed)" +DJUI_THEME_ELEMENT_SLIDER_BORDER_DISABLED = "Slider Border (Disabled)" +DJUI_THEME_ELEMENT_TEXT = "Text" +DJUI_THEME_ELEMENT_TEXT_DISABLED = "Text (Disabled)" +DJUI_THEME_ELEMENT_SELECTIONBOX_IMAGE = "Selection Box Image" +DJUI_THEME_ELEMENT_SELECTIONBOX_IMAGE_DISABLED = "Selection Box Image (Disabled)" +DJUI_THEME_ELEMENT_THREE_PANEL = "Three Panel" +DJUI_THEME_ELEMENT_THREE_PANEL_BORDER = "Three Panel Border" +DJUI_THEME_ELEMENT_PANEL_HEADER_COLOR = "Panel Header Color" +HEX_CODE = "Hex Code" +RED = "Red" +GREEN = "Green" +BLUE = "Blue" +ALPHA = "Alpha" +HEADER_FONT = "Header Font" +USE_RAINBOW = "Use Rainbow" +THEME_NAME = "Theme Name" +DELETE_THEME = "Delete Theme" +DELETE_THEME_TITLE = "DELETE THEME" +WARN_DELETE_THEME = "Are you sure you want to delete this theme?" +EXPORT_THEME = "Save Theme" [DYNOS] DYNOS = "DYNOS" diff --git a/src/pc/djui/djui_panel_themes.c b/src/pc/djui/djui_panel_themes.c new file mode 100644 index 000000000..143ffa686 --- /dev/null +++ b/src/pc/djui/djui_panel_themes.c @@ -0,0 +1,613 @@ +#include "djui.h" +#include "djui_theme.h" +#include "djui_three_panel.h" +#include "djui_panel.h" +#include "djui_panel_confirm.h" +#include "djui_panel_menu.h" +#include "djui_panel_main.h" +#include "djui_panel_options.h" +#include "djui_panel_misc.h" +#include "djui_panel_pause.h" +#include "djui_panel_menu_options.h" +#include "djui_panel_themes.h" +#include "djui_panel_modlist.h" +#include "djui_panel_playerlist.h" +#include "djui_hud_utils.h" +#include "djui_unicode.h" +#include "pc/utils/misc.h" +#include "pc/configfile.h" +#include "pc/lua/smlua_hooks.h" +#include "game/level_update.h" +#include "seq_ids.h" + +typedef struct ThemeColor { + unsigned int r; + unsigned int g; + unsigned int b; + unsigned int a; +} ThemeColor; + +static unsigned int sDjuiFontSelected = 0; +static unsigned int sDjuiHeaderFontSelected = 0; +static unsigned int sDjuiThemeSelected = 0; +static unsigned int sElementChoice = 0; + +static ThemeColor sSliderChannels = { 0 }; +static struct DjuiSelectionbox* sThemeSelectionbox = NULL; +static struct DjuiRect* sColorRect = NULL; +static struct DjuiInputbox* sHexColorTextBox = NULL; +static struct DjuiInputbox* sThemeNameTextBox = NULL; +static struct DjuiSlider *sSliderR = NULL; +static struct DjuiSlider *sSliderG = NULL; +static struct DjuiSlider *sSliderB = NULL; +static struct DjuiSlider *sSliderA = NULL; + +static struct DjuiText* sTexts[16] = { NULL }; +static unsigned int sTextsCount = 0; +static struct DjuiButton* sButtons[16] = { NULL }; +static unsigned int sButtonsCount = 0; +static struct DjuiCheckbox* sCheckboxes[16] = { NULL }; +static unsigned int sCheckboxesCount = 0; +static struct DjuiSelectionbox* sSelectionboxes[16] = { NULL }; +static unsigned int sSelectionboxesCount = 0; + +char* themeElementChoices[DJUI_THEME_ELEMENT_COUNT] = { + [DJUI_THEME_ELEMENT_PRIMARY] = "DJUI_THEME_ELEMENT_PRIMARY", + [DJUI_THEME_ELEMENT_PRIMARY_HOVER] = "DJUI_THEME_ELEMENT_PRIMARY_HOVER", + [DJUI_THEME_ELEMENT_PRIMARY_DOWN] = "DJUI_THEME_ELEMENT_PRIMARY_DOWN", + [DJUI_THEME_ELEMENT_PRIMARY_DISABLED] = "DJUI_THEME_ELEMENT_PRIMARY_DISABLED", + [DJUI_THEME_ELEMENT_PRIMARY_TEXT] = "DJUI_THEME_ELEMENT_PRIMARY_TEXT", + [DJUI_THEME_ELEMENT_PRIMARY_TEXT_DISABLED] = "DJUI_THEME_ELEMENT_PRIMARY_TEXT_DISABLED", + + [DJUI_THEME_ELEMENT_PRIMARY_BORDER] = "DJUI_THEME_ELEMENT_PRIMARY_BORDER", + [DJUI_THEME_ELEMENT_PRIMARY_BORDER_HOVER] = "DJUI_THEME_ELEMENT_PRIMARY_BORDER_HOVER", + [DJUI_THEME_ELEMENT_PRIMARY_BORDER_DOWN] = "DJUI_THEME_ELEMENT_PRIMARY_BORDER_DOWN", + [DJUI_THEME_ELEMENT_PRIMARY_BORDER_DISABLED] = "DJUI_THEME_ELEMENT_PRIMARY_BORDER_DISABLED", + + [DJUI_THEME_ELEMENT_SECONDARY] = "DJUI_THEME_ELEMENT_SECONDARY", + [DJUI_THEME_ELEMENT_SECONDARY_HOVER] = "DJUI_THEME_ELEMENT_SECONDARY_HOVER", + [DJUI_THEME_ELEMENT_SECONDARY_DOWN] = "DJUI_THEME_ELEMENT_SECONDARY_DOWN", + [DJUI_THEME_ELEMENT_SECONDARY_DISABLED] = "DJUI_THEME_ELEMENT_SECONDARY_DISABLED", + [DJUI_THEME_ELEMENT_SECONDARY_TEXT] = "DJUI_THEME_ELEMENT_SECONDARY_TEXT", + [DJUI_THEME_ELEMENT_SECONDARY_TEXT_DISABLED] = "DJUI_THEME_ELEMENT_SECONDARY_TEXT_DISABLED", + + [DJUI_THEME_ELEMENT_SECONDARY_BORDER] = "DJUI_THEME_ELEMENT_SECONDARY_BORDER", + [DJUI_THEME_ELEMENT_SECONDARY_BORDER_HOVER] = "DJUI_THEME_ELEMENT_SECONDARY_BORDER_HOVER", + [DJUI_THEME_ELEMENT_SECONDARY_BORDER_DOWN] = "DJUI_THEME_ELEMENT_SECONDARY_BORDER_DOWN", + [DJUI_THEME_ELEMENT_SECONDARY_BORDER_DISABLED] = "DJUI_THEME_ELEMENT_SECONDARY_BORDER_DISABLED", + + [DJUI_THEME_ELEMENT_INPUTBOX] = "DJUI_THEME_ELEMENT_INPUTBOX", + [DJUI_THEME_ELEMENT_INPUTBOX_HOVER] = "DJUI_THEME_ELEMENT_INPUTBOX_HOVER", + [DJUI_THEME_ELEMENT_INPUTBOX_DOWN] = "DJUI_THEME_ELEMENT_INPUTBOX_DOWN", + [DJUI_THEME_ELEMENT_INPUTBOX_DISABLED] = "DJUI_THEME_ELEMENT_INPUTBOX_DISABLED", + [DJUI_THEME_ELEMENT_INPUTBOX_TEXT] = "DJUI_THEME_ELEMENT_INPUTBOX_TEXT", + [DJUI_THEME_ELEMENT_INPUTBOX_TEXT_PLACEHOLDER] = "DJUI_THEME_ELEMENT_INPUTBOX_TEXT_PLACEHOLDER", + + [DJUI_THEME_ELEMENT_INPUTBOX_BORDER] = "DJUI_THEME_ELEMENT_INPUTBOX_BORDER", + [DJUI_THEME_ELEMENT_INPUTBOX_BORDER_HOVER] = "DJUI_THEME_ELEMENT_INPUTBOX_BORDER_HOVER", + [DJUI_THEME_ELEMENT_INPUTBOX_BORDER_DOWN] = "DJUI_THEME_ELEMENT_INPUTBOX_BORDER_DOWN", + [DJUI_THEME_ELEMENT_INPUTBOX_BORDER_DISABLED] = "DJUI_THEME_ELEMENT_INPUTBOX_BORDER_DISABLED", + + [DJUI_THEME_ELEMENT_CHECKBOX] = "DJUI_THEME_ELEMENT_CHECKBOX", + [DJUI_THEME_ELEMENT_CHECKBOX_HOVER] = "DJUI_THEME_ELEMENT_CHECKBOX_HOVER", + [DJUI_THEME_ELEMENT_CHECKBOX_DOWN] = "DJUI_THEME_ELEMENT_CHECKBOX_DOWN", + [DJUI_THEME_ELEMENT_CHECKBOX_DISABLED] = "DJUI_THEME_ELEMENT_CHECKBOX_DISABLED", + + [DJUI_THEME_ELEMENT_CHECKBOX_BORDER] = "DJUI_THEME_ELEMENT_CHECKBOX_BORDER", + [DJUI_THEME_ELEMENT_CHECKBOX_BORDER_HOVER] = "DJUI_THEME_ELEMENT_CHECKBOX_BORDER_HOVER", + [DJUI_THEME_ELEMENT_CHECKBOX_BORDER_DOWN] = "DJUI_THEME_ELEMENT_CHECKBOX_BORDER_DOWN", + [DJUI_THEME_ELEMENT_CHECKBOX_BORDER_DISABLED] = "DJUI_THEME_ELEMENT_CHECKBOX_BORDER_DISABLED", + + [DJUI_THEME_ELEMENT_SLIDER] = "DJUI_THEME_ELEMENT_SLIDER", + [DJUI_THEME_ELEMENT_SLIDER_HOVER] = "DJUI_THEME_ELEMENT_SLIDER_HOVER", + [DJUI_THEME_ELEMENT_SLIDER_DOWN] = "DJUI_THEME_ELEMENT_SLIDER_DOWN", + [DJUI_THEME_ELEMENT_SLIDER_DISABLED] = "DJUI_THEME_ELEMENT_SLIDER_DISABLED", + + [DJUI_THEME_ELEMENT_SLIDER_BORDER] = "DJUI_THEME_ELEMENT_SLIDER_BORDER", + [DJUI_THEME_ELEMENT_SLIDER_BORDER_HOVER] = "DJUI_THEME_ELEMENT_SLIDER_BORDER_HOVER", + [DJUI_THEME_ELEMENT_SLIDER_BORDER_DOWN] = "DJUI_THEME_ELEMENT_SLIDER_BORDER_DOWN", + [DJUI_THEME_ELEMENT_SLIDER_BORDER_DISABLED] = "DJUI_THEME_ELEMENT_SLIDER_BORDER_DISABLED", + + [DJUI_THEME_ELEMENT_TEXT] = "DJUI_THEME_ELEMENT_TEXT", + [DJUI_THEME_ELEMENT_TEXT_DISABLED] = "DJUI_THEME_ELEMENT_TEXT_DISABLED", + + [DJUI_THEME_ELEMENT_SELECTIONBOX_IMAGE] = "DJUI_THEME_ELEMENT_SELECTIONBOX_IMAGE", + [DJUI_THEME_ELEMENT_SELECTIONBOX_IMAGE_DISABLED] = "DJUI_THEME_ELEMENT_SELECTIONBOX_IMAGE_DISABLED", + + [DJUI_THEME_ELEMENT_THREE_PANEL] = "DJUI_THEME_ELEMENT_THREE_PANEL", + [DJUI_THEME_ELEMENT_THREE_PANEL_BORDER] = "DJUI_THEME_ELEMENT_THREE_PANEL_BORDER", + + [DJUI_THEME_ELEMENT_PANEL_HEADER_COLOR] = "DJUI_THEME_ELEMENT_PANEL_HEADER_COLOR", +}; + +static void djui_panel_themes_reload(UNUSED struct DjuiBase* caller); + +static void djui_panel_theme_reset_djui_tree() { + if (gDjuiInMainMenu) { + djui_panel_shutdown(); + gDjuiInMainMenu = true; + djui_panel_playerlist_create(NULL); + djui_panel_modlist_create(NULL); + djui_panel_main_create(NULL); + djui_panel_options_create(NULL); + djui_panel_misc_create(NULL); + djui_panel_main_menu_create(NULL); + djui_panel_themes_create(NULL); + } else if (gDjuiPanelPauseCreated) { + djui_panel_shutdown(); + djui_panel_playerlist_create(NULL); + djui_panel_modlist_create(NULL); + djui_panel_pause_create(NULL); + djui_panel_options_create(NULL); + djui_panel_misc_create(NULL); + djui_panel_main_menu_create(NULL); + djui_panel_themes_create(NULL); + + djui_text_set_font(gDjuiPauseOptions, gDjuiFonts[configDjuiThemeFont]); + djui_text_set_text(gDjuiPauseOptions, DLANG(MISC, R_BUTTON)); + + djui_text_set_font(gDjuiModReload, gDjuiFonts[configDjuiThemeFont]); + djui_text_set_text(gDjuiModReload, DLANG(MISC, L_BUTTON)); + } +} + +static bool djui_panel_theme_apply(UNUSED struct DjuiBase* caller) { + djui_panel_theme_reset_djui_tree(); + return false; +} + +static void djui_panel_theme_font_setting_change(UNUSED struct DjuiBase* caller) { + u8 djuiFontChoices[4] = { + FONT_NORMAL, + FONT_ALIASED, + FONT_SPECIAL, + FONT_CLASSIC + }; + configDjuiThemeFont = djuiFontChoices[sDjuiFontSelected]; + djui_panel_themes_reload(NULL); +} + +static void djui_panel_theme_header_font_setting_change(UNUSED struct DjuiBase* caller) { + u8 djuiHeaderFontChoices[5] = { + FONT_NORMAL, + FONT_MENU, + FONT_ALIASED, + FONT_SPECIAL, + FONT_CLASSIC + }; + configDjuiTheme.headerFont = djuiHeaderFontChoices[sDjuiHeaderFontSelected]; + djui_panel_themes_reload(NULL); +} + +static void djui_panel_theme_theme_changed(UNUSED struct DjuiBase* caller) { + if (sDjuiThemeSelected <= 0 || !gDjuiThemes[sDjuiThemeSelected - 1]) return; + configDjuiTheme = *gDjuiThemes[sDjuiThemeSelected - 1]; + snprintf(sThemeNameTextBox->buffer, sThemeNameTextBox->bufferSize, "%s", configDjuiTheme.name); + djui_panel_themes_reload(NULL); +} + +static void djui_panel_theme_edit_theme_update_hex_code_box(void) { + char buf[9]; + static const char digitToChar[] = "0123456789abcdef"; + + + u8 rgba[4] = { 0 }; + rgba[0] = configDjuiTheme.elements[sElementChoice].r; + rgba[1] = configDjuiTheme.elements[sElementChoice].g; + rgba[2] = configDjuiTheme.elements[sElementChoice].b; + rgba[3] = configDjuiTheme.elements[sElementChoice].a; + for (size_t i = 0; i < 4; i++) { + buf[2*i] = digitToChar[rgba[i] >> 4]; + buf[2*i+1] = digitToChar[rgba[i] & 0xF]; + } + + buf[8] = '\0'; + + djui_inputbox_set_text(sHexColorTextBox, buf); +} + +static void djui_panel_theme_update_color_rect(void) { + if (sColorRect) { + djui_base_set_color(&sColorRect->base, sSliderChannels.r, sSliderChannels.g, sSliderChannels.b, sSliderChannels.a); + } +} + +static void djui_panel_theme_edit_theme_update_sliders(void) { + struct DjuiColor c = configDjuiTheme.elements[sElementChoice]; + sSliderChannels.r = c.r; + sSliderChannels.g = c.g; + sSliderChannels.b = c.b; + sSliderChannels.a = c.a; + + djui_slider_update_value(&sSliderR->base); + djui_slider_update_style(&sSliderR->base); + djui_text_set_font(sSliderR->text, gDjuiFonts[configDjuiThemeFont]); + djui_base_set_color_with_color(&sSliderR->text->base, configDjuiTheme.elements[DJUI_THEME_ELEMENT_TEXT]); + djui_slider_update_value(&sSliderG->base); + djui_slider_update_style(&sSliderG->base); + djui_text_set_font(sSliderG->text, gDjuiFonts[configDjuiThemeFont]); + djui_base_set_color_with_color(&sSliderG->text->base, configDjuiTheme.elements[DJUI_THEME_ELEMENT_TEXT]); + djui_slider_update_value(&sSliderB->base); + djui_slider_update_style(&sSliderB->base); + djui_text_set_font(sSliderB->text, gDjuiFonts[configDjuiThemeFont]); + djui_base_set_color_with_color(&sSliderB->text->base, configDjuiTheme.elements[DJUI_THEME_ELEMENT_TEXT]); + djui_slider_update_value(&sSliderA->base); + djui_slider_update_style(&sSliderA->base); + djui_text_set_font(sSliderA->text, gDjuiFonts[configDjuiThemeFont]); + djui_base_set_color_with_color(&sSliderA->text->base, configDjuiTheme.elements[DJUI_THEME_ELEMENT_TEXT]); + djui_base_set_gradient(&sSliderA->base, configDjuiTheme.gradients); + djui_base_set_gradient(&sSliderA->rect->base, configDjuiTheme.gradients); + djui_base_set_gradient(&sSliderA->rectValue->base, configDjuiTheme.gradients); + djui_panel_theme_update_color_rect(); +} + +static void djui_panel_theme_edit_theme_slider_changed(UNUSED struct DjuiBase* caller) { + ThemeColor c = sSliderChannels; + configDjuiTheme.elements[sElementChoice].r = c.r; + configDjuiTheme.elements[sElementChoice].g = c.g; + configDjuiTheme.elements[sElementChoice].b = c.b; + configDjuiTheme.elements[sElementChoice].a = c.a; + + djui_panel_theme_edit_theme_update_hex_code_box(); + djui_panel_theme_update_color_rect(); + djui_panel_themes_reload(NULL); +} + +static int char_to_hex_digit(char c) { + return (c >= '0' && c <= '9') ? c - '0' : c - 'a' + 10; +} + +static void djui_panel_theme_edit_theme_hex_code_changed(struct DjuiBase* caller) { + struct DjuiInputbox* input = (struct DjuiInputbox*) caller; + + for (int i = 0; i < 8; i++) { + char c = input->buffer[i]; + if (c == '\0') return; // all 8 characters must be filled + + if (c >= 'A' && c <= 'Z') { + input->buffer[i] = c - 'A' + 'a'; // convert all characters to lowercase + } + + if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'))) return; + } + + u8 rgba[4] = { 0 }; + for (int i = 0; i < 4; i++) { + rgba[i] = (char_to_hex_digit(input->buffer[2 * i]) << 4) | char_to_hex_digit(input->buffer[2 * i + 1]); + } + configDjuiTheme.elements[sElementChoice].r = rgba[0]; + configDjuiTheme.elements[sElementChoice].g = rgba[1]; + configDjuiTheme.elements[sElementChoice].b = rgba[2]; + configDjuiTheme.elements[sElementChoice].a = rgba[3]; + + djui_panel_theme_edit_theme_update_sliders(); + djui_panel_themes_reload(NULL); +} + +static void djui_panel_theme_theme_element_changed(UNUSED struct DjuiBase* caller) { + struct DjuiColor c = configDjuiTheme.elements[sElementChoice]; + sSliderChannels.r = c.r; + sSliderChannels.g = c.g; + sSliderChannels.b = c.b; + sSliderChannels.a = c.a; + + djui_panel_theme_edit_theme_update_sliders(); + djui_panel_theme_edit_theme_update_hex_code_box(); + djui_panel_theme_update_color_rect(); +} + +static bool djui_panel_theme_edit_theme_name_valid(char* buffer) { + if (buffer[0] == '\0') { return false; } + char* c = buffer; + while (*c != '\0') { +#if defined(_WIN32) || defined(_WIN64) + if (*c == '/' || *c == '\\' || *c == ':' || *c == '<' || *c == '>' || *c == '"' || *c == '|' || *c == '?' || *c == '*') { return false; } +#else + if (*c == '/' || *c == '\\') { return false; } +#endif + if (!djui_unicode_valid_char(c)) { return false; } + c = djui_unicode_next_char(c); + } + return true; +} + +static char* djui_panel_theme_edit_theme_name_get_text(void) { + char* name = "Unnamed"; + for (int i = 0; i < MAX_DJUI_THEMES; i++) { + if (gDjuiThemes[i]) { + if (memcmp(&configDjuiTheme, gDjuiThemes[i], sizeof(struct DjuiTheme)) == 0) { + name = gPresetPalettes[i].name; + } + } + } + return name; +} + +static void djui_panel_theme_edit_theme_name_text_change(struct DjuiBase* caller) { + struct DjuiInputbox* inputbox1 = (struct DjuiInputbox*)caller; + if (djui_panel_theme_edit_theme_name_valid(inputbox1->buffer)) { + djui_inputbox_reset_text_color(inputbox1); + } else { + djui_inputbox_set_text_color(inputbox1, 255, 0, 0, 255); + } +} + +static void djui_panel_theme_edit_theme_name_on_focus_end(struct DjuiBase* caller) { + struct DjuiInputbox* inputbox1 = (struct DjuiInputbox*)caller; + if (!djui_panel_theme_edit_theme_name_valid(inputbox1->buffer)) { + djui_inputbox_set_text(inputbox1, djui_panel_theme_edit_theme_name_get_text()); + } else { + djui_inputbox_reset_text_color(inputbox1); + } + + djui_inputbox_on_focus_end(&inputbox1->base); +} + +static void djui_panel_theme_reset_theme_selectionbox() { + char* themeChoices[MAX_DJUI_THEMES + 1] = { "Custom" }; + int themeChoicesCount = 1; + for (int i = 0; i < MAX_DJUI_THEMES; i++) { + if (!gDjuiThemes[i]) continue; + themeChoices[themeChoicesCount++] = i < DJUI_THEME_COUNT ? djui_language_get("DJUI_THEMES", gDjuiThemes[i]->name) : gDjuiThemes[i]->name; + if (memcmp(&configDjuiTheme, gDjuiThemes[i], sizeof(struct DjuiTheme)) == 0) { + sDjuiThemeSelected = themeChoicesCount - 1; + } + } + if (sDjuiThemeSelected <= 0 || !gDjuiThemes[sDjuiThemeSelected - 1]) sDjuiThemeSelected = 0; + for (int i = 0; i < sThemeSelectionbox->choiceCount; i++) { + free(sThemeSelectionbox->choices[i]); + } + free(sThemeSelectionbox->choices); + sThemeSelectionbox->choices = calloc(themeChoicesCount, sizeof(char*)); + for (int i = 0; i < themeChoicesCount; i++) { + u32 length = strlen(themeChoices[i]); + sThemeSelectionbox->choices[i] = calloc((length + 1), sizeof(char)); + snprintf(sThemeSelectionbox->choices[i], length + 1, "%s", themeChoices[i]); + } + sThemeSelectionbox->choiceCount = themeChoicesCount; + djui_selectionbox_update_value(&sThemeSelectionbox->base); +} + +static void djui_panel_theme_edit_theme_delete(UNUSED struct DjuiBase* caller) { + djui_theme_delete(&configDjuiTheme); + djui_panel_theme_reset_theme_selectionbox(); + djui_panel_themes_reload(NULL); + djui_panel_menu_back(caller); +} + +static void djui_panel_theme_edit_theme_delete_confirm(UNUSED struct DjuiBase* caller) { + if (sDjuiThemeSelected <= DJUI_THEME_COUNT || sDjuiThemeSelected >= MAX_DJUI_THEMES || !gDjuiThemes[sDjuiThemeSelected - 1]) return; + djui_panel_confirm_create(NULL, DLANG(DJUI_THEMES, DELETE_THEME_TITLE), DLANG(DJUI_THEMES, WARN_DELETE_THEME), djui_panel_theme_edit_theme_delete); +} + +static void djui_panel_theme_edit_theme_export(UNUSED struct DjuiBase* caller) { + if (!djui_panel_theme_edit_theme_name_valid(sThemeNameTextBox->buffer)) return; + snprintf(configDjuiTheme.name, MAX_DJUI_THEME_NAME_LEN, "%s", sThemeNameTextBox->buffer); + bool setThemeArray = true; + for (int i = DJUI_THEME_COUNT; i < MAX_DJUI_THEMES; i++) { + if (!gDjuiThemes[i]) continue; + if (strcmp(gDjuiThemes[i]->name, sThemeNameTextBox->buffer) == 0) { + setThemeArray = false; + memcpy(gDjuiThemes[i], &configDjuiTheme, sizeof(struct DjuiTheme)); + break; + } + } + djui_themes_save_current(setThemeArray); + djui_panel_theme_reset_theme_selectionbox(); + djui_panel_themes_reload(NULL); +} + +static bool djui_panel_theme_renderer(struct DjuiBase* base) { + struct DjuiThreePanel* threePanel = (struct DjuiThreePanel*)base; + djui_base_set_color_with_color(&threePanel->base, configDjuiTheme.elements[DJUI_THEME_ELEMENT_THREE_PANEL]); + djui_base_set_border_color_with_color(&threePanel->base, configDjuiTheme.elements[DJUI_THEME_ELEMENT_THREE_PANEL_BORDER]); + struct DjuiBase* headerBase = djui_three_panel_get_header(threePanel); + struct DjuiText* headerText = (struct DjuiText*)headerBase; + djui_text_set_font(headerText, gDjuiFonts[configDjuiTheme.headerFont]); + if (!configDjuiTheme.useRainbowColor) { + djui_text_set_text(headerText, DLANG(DJUI_THEMES, THEMES_TITLE)); + djui_base_set_color_with_color(headerBase, configDjuiTheme.elements[DJUI_THEME_ELEMENT_PANEL_HEADER_COLOR]); + } else { + djui_text_set_text(headerText, generate_rainbow_text(DLANG(DJUI_THEMES, THEMES_TITLE))); + djui_base_set_color(headerBase, 255, 8, 0, 255); + } + return djui_three_panel_render(base); +} + +static void djui_panel_themes_reload(UNUSED struct DjuiBase* caller) { + djui_inputbox_update_style(&sHexColorTextBox->base); + djui_inputbox_reset_text_color(sHexColorTextBox); + djui_base_set_gradient(&sHexColorTextBox->base, configDjuiTheme.gradients); + + djui_inputbox_update_style(&sThemeNameTextBox->base); + djui_inputbox_reset_text_color(sThemeNameTextBox); + djui_base_set_gradient(&sThemeNameTextBox->base, configDjuiTheme.gradients); + + for (unsigned int i = 0; i < sTextsCount; i++) { + if (!sTexts[i]) continue; + djui_base_set_color_with_color(&sTexts[i]->base, configDjuiTheme.elements[DJUI_THEME_ELEMENT_TEXT]); + djui_text_set_font(sTexts[i], gDjuiFonts[configDjuiThemeFont]); + } + + for (unsigned int i = 0; i < sButtonsCount; i++) { + if (!sButtons[i]) continue; + djui_button_update_style(&sButtons[i]->base); + djui_base_set_gradient(&sButtons[i]->rect->base, configDjuiTheme.gradients); + djui_text_set_font(sButtons[i]->text, gDjuiFonts[configDjuiThemeFont]); + } + + for (unsigned int i = 0; i < sCheckboxesCount; i++) { + if (!sCheckboxes[i]) continue; + djui_checkbox_update_style(&sCheckboxes[i]->base); + djui_base_set_gradient(&sCheckboxes[i]->rect->base, configDjuiTheme.gradients); + djui_base_set_gradient(&sCheckboxes[i]->rectValue->base, configDjuiTheme.gradients); + djui_text_set_font(sCheckboxes[i]->text, gDjuiFonts[configDjuiThemeFont]); + } + + for (unsigned int i = 0; i < sSelectionboxesCount; i++) { + if (!sSelectionboxes[i]) continue; + djui_selectionbox_update_style(&sSelectionboxes[i]->base); + djui_base_set_gradient(&sSelectionboxes[i]->rect->base, configDjuiTheme.gradients); + djui_text_set_font(sSelectionboxes[i]->text, gDjuiFonts[configDjuiThemeFont]); + djui_text_set_font(sSelectionboxes[i]->rectText, gDjuiFonts[configDjuiThemeFont]); + } + + if (sDjuiThemeSelected <= 0 || !gDjuiThemes[sDjuiThemeSelected - 1] || memcmp(&configDjuiTheme, gDjuiThemes[sDjuiThemeSelected - 1], sizeof(struct DjuiTheme)) != 0) { + sDjuiThemeSelected = 0; + djui_selectionbox_update_value(&sThemeSelectionbox->base); + } + + djui_panel_theme_edit_theme_update_sliders(); + djui_panel_theme_update_color_rect(); + smlua_call_event_hooks(HOOK_ON_DJUI_THEME_CHANGED); +} + +void djui_panel_themes_create(struct DjuiBase* caller) { + sTextsCount = 0; + sButtonsCount = 0; + sCheckboxesCount = 0; + sSelectionboxesCount = 0; + sDjuiThemeSelected = 0; + struct DjuiThreePanel* panel = djui_panel_menu_create(DLANG(DJUI_THEMES, THEMES_TITLE), false); + panel->base.render = djui_panel_theme_renderer; + struct DjuiBase* body = djui_three_panel_get_body(panel); + { + u8 djuiFontSelectIndexes[FONT_COUNT] = { + [FONT_NORMAL] = 0, + [FONT_ALIASED] = 1, + [FONT_SPECIAL] = 2, + [FONT_CLASSIC] = 3, + }; + sDjuiFontSelected = djuiFontSelectIndexes[configDjuiThemeFont]; + char* djuiFontChoices[4] = { + DLANG(DJUI_THEMES, FONT_NORMAL), + DLANG(DJUI_THEMES, FONT_ALIASED), + DLANG(DJUI_THEMES, FONT_SPECIAL), + DLANG(DJUI_THEMES, FONT_CLASSIC) + }; + sSelectionboxes[sSelectionboxesCount++] = djui_selectionbox_create(body, DLANG(DJUI_THEMES, DJUI_FONT), djuiFontChoices, 4, &sDjuiFontSelected, djui_panel_theme_font_setting_change); + + char* themeChoices[MAX_DJUI_THEMES + 1] = { "Custom" }; + int themeChoicesCount = 1; + for (int i = 0; i < MAX_DJUI_THEMES; i++) { + if (!gDjuiThemes[i]) continue; + themeChoices[themeChoicesCount++] = i < DJUI_THEME_COUNT ? djui_language_get("DJUI_THEMES", gDjuiThemes[i]->name) : gDjuiThemes[i]->name; + if (memcmp(&configDjuiTheme, gDjuiThemes[i], sizeof(struct DjuiTheme)) == 0) { + sDjuiThemeSelected = themeChoicesCount - 1; + } + } + sThemeSelectionbox = djui_selectionbox_create(body, DLANG(DJUI_THEMES, DJUI_THEME), themeChoices, themeChoicesCount, &sDjuiThemeSelected, djui_panel_theme_theme_changed); + sSelectionboxes[sSelectionboxesCount++] = sThemeSelectionbox; + + char* translatedThemeElementChoices[DJUI_THEME_ELEMENT_COUNT]; + for (int i = 0; i < DJUI_THEME_ELEMENT_COUNT; i++) { + translatedThemeElementChoices[i] = djui_language_get("DJUI_THEMES", themeElementChoices[i]); + } + struct DjuiSelectionbox* selectionbox = djui_selectionbox_create(body, DLANG(DJUI_THEMES, ELEMENT), translatedThemeElementChoices, DJUI_THEME_ELEMENT_COUNT, &sElementChoice, djui_panel_theme_theme_element_changed); + djui_base_set_size(&selectionbox->rect->base, 0.65f, 1.0f); + sSelectionboxes[sSelectionboxesCount++] = selectionbox; + + struct DjuiRect* rect1 = djui_rect_container_create(body, 32); + { + struct DjuiText* hexText = djui_text_create(&rect1->base, DLANG(DJUI_THEMES, HEX_CODE)); + djui_base_set_size_type(&hexText->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); + djui_base_set_color_with_color(&hexText->base, configDjuiTheme.elements[DJUI_THEME_ELEMENT_TEXT]); + djui_base_set_size(&hexText->base, 0.585f, 64); + djui_base_set_alignment(&hexText->base, DJUI_HALIGN_LEFT, DJUI_VALIGN_TOP); + djui_text_set_drop_shadow(hexText, 64, 64, 64, 100); + sTexts[sTextsCount++] = hexText; + + sHexColorTextBox = djui_inputbox_create(&rect1->base, 9); + djui_base_set_size_type(&sHexColorTextBox->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); + djui_base_set_size(&sHexColorTextBox->base, 0.45f, 32); + djui_base_set_alignment(&sHexColorTextBox->base, DJUI_HALIGN_RIGHT, DJUI_VALIGN_TOP); + djui_panel_theme_edit_theme_update_hex_code_box(); + djui_interactable_hook_value_change(&sHexColorTextBox->base, djui_panel_theme_edit_theme_hex_code_changed); + } + + sSliderR = djui_slider_create(body, DLANG(DJUI_THEMES, RED), &sSliderChannels.r, 0, 255, djui_panel_theme_edit_theme_slider_changed); + djui_base_set_color(&sSliderR->rectValue->base, 255, 0, 0, 255); + djui_base_set_gradient(&sSliderR->rectValue->base, false); + djui_base_set_gradient(&sSliderR->rect->base, false); + sSliderR->updateRectValueColor = false; + djui_slider_update_style(&sSliderR->base); + sSliderG = djui_slider_create(body, DLANG(DJUI_THEMES, GREEN), &sSliderChannels.g, 0, 255, djui_panel_theme_edit_theme_slider_changed); + djui_base_set_color(&sSliderG->rectValue->base, 0, 255, 0, 255); + djui_base_set_gradient(&sSliderG->rectValue->base, false); + djui_base_set_gradient(&sSliderG->rect->base, false); + sSliderG->updateRectValueColor = false; + djui_slider_update_style(&sSliderG->base); + sSliderB = djui_slider_create(body, DLANG(DJUI_THEMES, BLUE), &sSliderChannels.b, 0, 255, djui_panel_theme_edit_theme_slider_changed); + djui_base_set_color(&sSliderB->rectValue->base, 0, 0, 255, 255); + djui_base_set_gradient(&sSliderB->rectValue->base, false); + djui_base_set_gradient(&sSliderB->rect->base, false); + sSliderB->updateRectValueColor = false; + djui_slider_update_style(&sSliderB->base); + sSliderA = djui_slider_create(body, DLANG(DJUI_THEMES, ALPHA), &sSliderChannels.a, 0, 255, djui_panel_theme_edit_theme_slider_changed); + djui_base_set_color(&sSliderA->rectValue->base, 220, 220, 220, 255); + djui_slider_update_style(&sSliderA->base); + djui_panel_theme_edit_theme_update_sliders(); + + sColorRect = djui_rect_create(body); + djui_base_set_size_type(&sColorRect->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); + djui_base_set_size(&sColorRect->base, 1.0f, 32); + djui_base_set_color(&sColorRect->base, sSliderChannels.r, sSliderChannels.g, sSliderChannels.b, sSliderChannels.a); + djui_base_set_gradient(&sColorRect->base, false); + djui_base_set_border_width(&sColorRect->base, 2); + djui_base_set_border_color(&sColorRect->base, 173, 173, 173, 255); + + u8 djuiHeaderFontSelectIndexes[FONT_COUNT] = { + [FONT_NORMAL] = 0, + [FONT_MENU] = 1, + [FONT_ALIASED] = 2, + [FONT_SPECIAL] = 3, + [FONT_CLASSIC] = 4, + }; + sDjuiHeaderFontSelected = djuiHeaderFontSelectIndexes[configDjuiTheme.headerFont]; + char* djuiHeaderFontChoices[5] = { + DLANG(DJUI_THEMES, FONT_NORMAL), + DLANG(DJUI_THEMES, FONT_MENU), + DLANG(DJUI_THEMES, FONT_ALIASED), + DLANG(DJUI_THEMES, FONT_SPECIAL), + DLANG(DJUI_THEMES, FONT_CLASSIC) + }; + sSelectionboxes[sSelectionboxesCount++] = djui_selectionbox_create(body, DLANG(DJUI_THEMES, HEADER_FONT), djuiHeaderFontChoices, 5, &sDjuiHeaderFontSelected, djui_panel_theme_header_font_setting_change); + + sCheckboxes[sCheckboxesCount++] = djui_checkbox_create(body, DLANG(DJUI_THEMES, USE_RAINBOW), &configDjuiTheme.useRainbowColor, djui_panel_themes_reload); + sCheckboxes[sCheckboxesCount++] = djui_checkbox_create(body, DLANG(DJUI_THEMES, GRADIENTS), &configDjuiTheme.gradients, djui_panel_themes_reload); + + struct DjuiRect* rect2 = djui_rect_container_create(body, 32); + { + struct DjuiText* text1 = djui_text_create(&rect2->base, DLANG(DJUI_THEMES, THEME_NAME)); + djui_base_set_size_type(&text1->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); + djui_base_set_color_with_color(&text1->base, configDjuiTheme.elements[DJUI_THEME_ELEMENT_TEXT]); + djui_base_set_size(&text1->base, 0.585f, 64); + djui_base_set_alignment(&text1->base, DJUI_HALIGN_LEFT, DJUI_VALIGN_TOP); + djui_text_set_drop_shadow(text1, 64, 64, 64, 100); + sTexts[sTextsCount++] = text1; + + sThemeNameTextBox = djui_inputbox_create(&rect2->base, 32); + djui_inputbox_set_text(sThemeNameTextBox, djui_panel_theme_edit_theme_name_get_text()); + djui_base_set_size_type(&sThemeNameTextBox->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); + djui_base_set_size(&sThemeNameTextBox->base, 0.45f, 32); + djui_base_set_alignment(&sThemeNameTextBox->base, DJUI_HALIGN_RIGHT, DJUI_VALIGN_TOP); + djui_interactable_hook_value_change(&sThemeNameTextBox->base, djui_panel_theme_edit_theme_name_text_change); + djui_interactable_hook_focus(&sThemeNameTextBox->base, djui_inputbox_on_focus_begin, NULL, djui_panel_theme_edit_theme_name_on_focus_end); + } + + struct DjuiRect* rect3 = djui_rect_container_create(body, 32); + { + struct DjuiButton* button1 = djui_button_left_create(&rect3->base, DLANG(DJUI_THEMES, DELETE_THEME), DJUI_BUTTON_STYLE_PRIMARY, djui_panel_theme_edit_theme_delete_confirm); + djui_base_set_size(&button1->base, 0.485f, 32); + sButtons[sButtonsCount++] = button1; + struct DjuiButton* button2 = djui_button_right_create(&rect3->base, DLANG(DJUI_THEMES, EXPORT_THEME), DJUI_BUTTON_STYLE_PRIMARY, djui_panel_theme_edit_theme_export); + djui_base_set_size(&button2->base, 0.485f, 32); + sButtons[sButtonsCount++] = button2; + } + + sButtons[sButtonsCount++] = djui_button_create(body, DLANG(MENU, BACK), DJUI_BUTTON_STYLE_SECONDARY, djui_panel_menu_back); + } + panel->on_back = djui_panel_theme_apply; + + djui_panel_add(caller, panel, NULL); +} \ No newline at end of file diff --git a/src/pc/djui/djui_panel_themes.h b/src/pc/djui/djui_panel_themes.h new file mode 100644 index 000000000..5a1595fd7 --- /dev/null +++ b/src/pc/djui/djui_panel_themes.h @@ -0,0 +1,4 @@ +#pragma once +#include "djui.h" + +void djui_panel_themes_create(struct DjuiBase* caller); \ No newline at end of file diff --git a/src/pc/djui/djui_theme.c b/src/pc/djui/djui_theme.c index 0834fa155..30c900b28 100644 --- a/src/pc/djui/djui_theme.c +++ b/src/pc/djui/djui_theme.c @@ -222,7 +222,7 @@ void djui_themes_save(struct DjuiTheme* theme, bool setThemeArray) { snprintf(ppath, SYS_MAX_PATH, "%s/%s.ini", themesPath, theme->name); fs_sys_mkdir(themesPath); - LOG_INFO("Saving theme as '%s.ini'\n", theme->name); + LOG_INFO("Saving theme as '%s.ini'", theme->name); FILE* file = fopen(ppath, "w"); if (!file) { LOG_ERROR("Failed to save theme %s", theme->name); diff --git a/textures/custom_font/custom_font_classic.rgba32.png b/textures/custom_font/custom_font_classic.rgba32.png new file mode 100644 index 0000000000000000000000000000000000000000..fa02a747ebb1e31c1f4665a3d10be42194988507 GIT binary patch literal 11256 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