From 29e2b19698455a3ccd777a336d0156e300492549 Mon Sep 17 00:00:00 2001 From: Dav999 Date: Wed, 3 Jan 2024 20:09:23 +0100 Subject: [PATCH] Add RTL level property and print flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Again, the RTL property controls whether textboxes will be right-aligned, and that kind of stuff. It can't be font-bound, since Space Station supports Hebrew characters and we want to be able to support, say, a Hebrew translation or Hebrew levels in the future without having to make a dedicated (or duplicated) font for it. Therefore it's a property of both the language pack as well as custom levels - like custom levels already had a tag, they now also have an tag that sets this property. Right now, we'll have to hardcode it so the menu option for the Arabic font sets the property to 1, and all the other options set it to 0. But it's future-proof in that we can later decide to split the option for Space Station into an LTR option and an RTL option (so both "english/..." and "עברית" would select Space Station, but one sets the RTL property to 0 and the other sets it to 1). --- desktop_version/src/CustomLevels.cpp | 22 +++++++++++++++ desktop_version/src/CustomLevels.h | 2 ++ desktop_version/src/Font.cpp | 30 ++++++++++++++++----- desktop_version/src/Font.h | 4 ++- desktop_version/src/FontBidi.cpp | 16 ++++++++--- desktop_version/src/FontBidi.h | 4 +-- desktop_version/src/Game.cpp | 14 +++++++--- desktop_version/src/LocalizationMaint.cpp | 2 +- desktop_version/src/LocalizationStorage.cpp | 2 +- desktop_version/src/Render.cpp | 5 +++- desktop_version/src/main.cpp | 2 +- 11 files changed, 83 insertions(+), 20 deletions(-) diff --git a/desktop_version/src/CustomLevels.cpp b/desktop_version/src/CustomLevels.cpp index feaaa500..7d882e1d 100644 --- a/desktop_version/src/CustomLevels.cpp +++ b/desktop_version/src/CustomLevels.cpp @@ -227,6 +227,7 @@ TAG_FINDER(find_desc2, "Desc2") TAG_FINDER(find_desc3, "Desc3") TAG_FINDER(find_website, "website") TAG_FINDER(find_font, "font") +TAG_FINDER(find_rtl, "rtl") /* For CliPlaytestArgs */ TAG_FINDER(find_playtest, "Playtest") @@ -315,6 +316,7 @@ bool customlevelclass::getLevelMetaDataAndPlaytestArgs(const std::string& _path, { _data.level_main_font_idx = font::get_font_idx_8x8(); } + _data.rtl = help.Int(find_rtl(buf).c_str()); if (pt_args != NULL) @@ -1037,6 +1039,7 @@ bool customlevelclass::load(std::string _path) version = 0; level_font_name = "font"; + rtl = false; for (pElem = hDoc .FirstChildElement() @@ -1104,6 +1107,11 @@ bool customlevelclass::load(std::string _path) { level_font_name = pText_; } + + if(SDL_strcmp(pKey_, "rtl") == 0) + { + rtl = help.Int(pText_); + } } } @@ -1524,6 +1532,20 @@ bool customlevelclass::save(const std::string& _path) } } + if (rtl) + { + xml::update_tag(msg, "rtl", rtl); + } + else + { + // Also get rid of this one... + tinyxml2::XMLElement* element; + while ((element = msg->FirstChildElement("rtl")) != NULL) + { + doc.DeleteNode(element); + } + } + xml::update_tag(data, "mapwidth", mapwidth); xml::update_tag(data, "mapheight", mapheight); diff --git a/desktop_version/src/CustomLevels.h b/desktop_version/src/CustomLevels.h index fe3aa9d6..ac9fcdf4 100644 --- a/desktop_version/src/CustomLevels.h +++ b/desktop_version/src/CustomLevels.h @@ -66,6 +66,7 @@ struct LevelMetaData /* This is for the metadata in the levels list, * so it will only be a main font (no custom ones). */ uint8_t level_main_font_idx; + bool rtl; }; struct CliPlaytestArgs @@ -161,6 +162,7 @@ public: int mapwidth, mapheight; //Actual width and height of stage std::string level_font_name; + bool rtl; int version; diff --git a/desktop_version/src/Font.cpp b/desktop_version/src/Font.cpp index b0009d40..abffcb6a 100644 --- a/desktop_version/src/Font.cpp +++ b/desktop_version/src/Font.cpp @@ -85,6 +85,7 @@ struct PrintFlags bool align_right; bool cjk_low; bool cjk_high; + bool rtl; }; static FontContainer fonts_main = {}; @@ -558,6 +559,8 @@ void set_level_font(const char* name) } } } + + cl.rtl = SDL_strcmp(name, "font_ar") == 0; // FIXME: make different menu options for choosing LTR/RTL of the same font } void set_level_font_interface(void) @@ -590,6 +593,7 @@ void set_level_font_new(void) } cl.level_font_name = get_main_font_name(font_idx_level); + cl.rtl = cl.level_font_name == "font_ar"; // FIXME: make different menu options for choosing LTR/RTL of the same font } static void fill_map_name_idx(FontContainer* container) @@ -763,13 +767,19 @@ static Font* container_get(FontContainer* container, uint8_t idx) return NULL; } -static Font* fontsel_to_font(int sel) +static Font* fontsel_to_font(int sel, bool* rtl) { /* Take font selection integer (0-31) and turn it into the correct Font * 0: PR_FONT_INTERFACE - use interface font * 1: PR_FONT_LEVEL - use level font * 2: PR_FONT_8X8 - use 8x8 font no matter what - * 3-31: - use (main) font index 0-28 */ + * 3-31: - use (main) font index 0-28 + * + * rtl will be set depending on whether we're requesting the interface + * font (take it from the lang attributes) or level font (take it from + * the level attributes), or false otherwise. */ + + *rtl = false; if (sel < 0) { @@ -782,6 +792,7 @@ static Font* fontsel_to_font(int sel) case 1: if (!font_level_is_interface) { + *rtl = cl.rtl; if (font_idx_level_is_custom) { return container_get(&fonts_custom, font_idx_level); @@ -793,6 +804,7 @@ static Font* fontsel_to_font(int sel) } SDL_FALLTHROUGH; case 0: + *rtl = loc::get_langmeta()->rtl; return container_get(&fonts_main, loc::get_langmeta()->font_idx); case 2: return container_get(&fonts_main, font_idx_8x8); @@ -806,7 +818,11 @@ static PrintFlags decode_print_flags(uint32_t flags) { PrintFlags pf; pf.scale = FLAG_PART(0, 3) + 1; - pf.font_sel = fontsel_to_font(FLAG_PART(3, 5)); + pf.font_sel = fontsel_to_font(FLAG_PART(3, 5), &pf.rtl); + if (flags & PR_RTL_FORCE) + { + pf.rtl = true; + } pf.brightness = ~FLAG_PART(8, 8) & 0xff; pf.border = flags & PR_BOR; pf.full_border = flags & PR_FULLBOR; @@ -1206,9 +1222,9 @@ int len(const uint32_t flags, const char* text) { PrintFlags pf = decode_print_flags(flags); - if (bidi_should_transform(text)) + if (bidi_should_transform(pf.rtl, text)) { - text = bidi_transform(text); + text = bidi_transform(pf.rtl, text); } int text_len = 0; @@ -1313,9 +1329,9 @@ void print( y -= h_diff_8/2; } - if (bidi_should_transform(text)) + if (bidi_should_transform(pf.rtl, text)) { - text = bidi_transform(text); + text = bidi_transform(pf.rtl, text); } int position = 0; diff --git a/desktop_version/src/Font.h b/desktop_version/src/Font.h index afdebdbd..8b6c86d6 100644 --- a/desktop_version/src/Font.h +++ b/desktop_version/src/Font.h @@ -42,7 +42,8 @@ #define PR_FONT_INTERFACE (0 << 3) /* default, use interface font */ #define PR_FONT_LEVEL (1 << 3) /* use level-specific font (room names, cutscene dialogue, etc) */ #define PR_FONT_8X8 (2 << 3) /* use 8x8 font no matter what */ -#define PR_FONT_IDX(idx) ((SDL_clamp(idx, 0, 28) + 3) << 3) /* use given font index */ +#define PR_FONT_IDX(idx, rtl) /* use given font index */\ + (((SDL_clamp(idx, 0, 28) + 3) << 3) | (rtl ? PR_RTL_FORCE : 0)) #define PR_BRIGHTNESS(value) /* use this brightness 0-255 for the text (accounts for button glyphs correctly) */\ (((~SDL_clamp((int)(value), 0, 255) & 0xff) << 8)) #define PR_FULLBOR (1 << 16) /* draw a black border around the text, filling in the corners (for the map legend) */ @@ -53,6 +54,7 @@ #define PR_CJK_CEN (0 << 20) /* default, larger fonts should stick out on top and bottom compared to 8x8 font */ #define PR_CJK_LOW (1 << 20) /* larger fonts should stick out fully on the bottom (draw at Y) */ #define PR_CJK_HIGH (2 << 20) /* larger fonts should stick out fully on the top */ +#define PR_RTL_FORCE (1 << 22) /* force the RTL flag, not needed if the font is set to INTERFACE or LEVEL */ namespace font diff --git a/desktop_version/src/FontBidi.cpp b/desktop_version/src/FontBidi.cpp index 199c1797..582bc032 100644 --- a/desktop_version/src/FontBidi.cpp +++ b/desktop_version/src/FontBidi.cpp @@ -282,11 +282,16 @@ bool is_joiner(const uint32_t codepoint) return codepoint == 0x200C || codepoint == 0x200D; } -bool bidi_should_transform(const char* text) +bool bidi_should_transform(const bool rtl, const char* text) { /* Just as an optimization, only run the whole bidi machinery if the * language is actually an RTL one, _or_ if an RTL character is found. */ + if (rtl) + { + return true; + } + const char* text_ptr = text; uint32_t ch; while ((ch = UTF8_next(&text_ptr))) @@ -313,7 +318,7 @@ bool bidi_should_transform(const char* text) return false; } -const char* bidi_transform(const char* text) +const char* bidi_transform(const bool rtl, const char* text) { uint32_t utf32_in[1024]; int n_codepoints = 0; @@ -345,7 +350,12 @@ const char* bidi_transform(const char* text) { return text; } - SBParagraphRef paragraph = SBAlgorithmCreateParagraph(algorithm, 0, INT32_MAX, SBLevelDefaultRTL); + SBParagraphRef paragraph = SBAlgorithmCreateParagraph( + algorithm, + 0, + INT32_MAX, + rtl ? SBLevelDefaultRTL : SBLevelDefaultLTR + ); SDL_assert(paragraph != NULL); SBUInteger paragraph_len = SBParagraphGetLength(paragraph); SBLineRef paragraph_line = SBParagraphCreateLine(paragraph, 0, paragraph_len); diff --git a/desktop_version/src/FontBidi.h b/desktop_version/src/FontBidi.h index 6824dd51..bad7ed09 100644 --- a/desktop_version/src/FontBidi.h +++ b/desktop_version/src/FontBidi.h @@ -10,8 +10,8 @@ void bidi_init(void); void bidi_destroy(void); bool is_directional_character(uint32_t codepoint); bool is_joiner(uint32_t codepoint); -bool bidi_should_transform(const char* text); -const char* bidi_transform(const char* text); +bool bidi_should_transform(bool rtl, const char* text); +const char* bidi_transform(bool rtl, const char* text); } // namespace font diff --git a/desktop_version/src/Game.cpp b/desktop_version/src/Game.cpp index e3017e25..2a64171e 100644 --- a/desktop_version/src/Game.cpp +++ b/desktop_version/src/Game.cpp @@ -6357,7 +6357,7 @@ void Game::createmenu( enum Menu::MenuName t, bool samemenu/*= false*/ ) text, true, cl.ListOfMetaData[i].title_is_gettext ? PR_FONT_INTERFACE : PR_FONT_IDX( - cl.ListOfMetaData[i].level_main_font_idx + cl.ListOfMetaData[i].level_main_font_idx, cl.ListOfMetaData[i].rtl ) ); } @@ -6487,7 +6487,7 @@ void Game::createmenu( enum Menu::MenuName t, bool samemenu/*= false*/ ) for (uint8_t i = 0; i < font::font_idx_options_n; i++) { uint8_t idx = font::font_idx_options[i]; - option(font::get_main_font_display_name(idx), true, PR_FONT_IDX(idx)); + option(font::get_main_font_display_name(idx), true, PR_FONT_IDX(idx, false)); if (font::level_font_is_main_idx(idx)) { option_match = i; @@ -6586,9 +6586,17 @@ void Game::createmenu( enum Menu::MenuName t, bool samemenu/*= false*/ ) for (size_t i = 0; i < loc::languagelist.size(); i++) { if (loc::languagelist[i].nativename.empty()) + { option(loc::languagelist[i].code.c_str()); + } else - option(loc::languagelist[i].nativename.c_str(), true, PR_FONT_IDX(loc::languagelist[i].font_idx)); + { + option( + loc::languagelist[i].nativename.c_str(), + true, + PR_FONT_IDX(loc::languagelist[i].font_idx, loc::languagelist[i].rtl) + ); + } } menuyoff = 70-(menuoptions.size()*10); diff --git a/desktop_version/src/LocalizationMaint.cpp b/desktop_version/src/LocalizationMaint.cpp index 926ba86b..88c8a872 100644 --- a/desktop_version/src/LocalizationMaint.cpp +++ b/desktop_version/src/LocalizationMaint.cpp @@ -64,7 +64,7 @@ static void sync_lang_file(const std::string& langcode) loadtext(false); uint8_t glyph_w = 8, glyph_h = 8; - font::glyph_dimensions(PR_FONT_IDX(langmeta.font_idx), &glyph_w, &glyph_h); + font::glyph_dimensions(PR_FONT_IDX(langmeta.font_idx, langmeta.rtl), &glyph_w, &glyph_h); bool max_local_needed = glyph_w != 8 || glyph_h != 8; tinyxml2::XMLDocument doc; diff --git a/desktop_version/src/LocalizationStorage.cpp b/desktop_version/src/LocalizationStorage.cpp index 57783b73..9632f46d 100644 --- a/desktop_version/src/LocalizationStorage.cpp +++ b/desktop_version/src/LocalizationStorage.cpp @@ -311,7 +311,7 @@ static bool max_check_string(const char* str, const char* max) } uint8_t font_idx = get_langmeta()->font_idx; - uint32_t print_flags = PR_FONT_IDX(font_idx) | PR_CJK_LOW; + uint32_t print_flags = PR_FONT_IDX(font_idx, get_langmeta()->rtl) | PR_CJK_LOW; uint8_t font_w = 8; uint8_t font_h = 8; font::glyph_dimensions(print_flags, &font_w, &font_h); diff --git a/desktop_version/src/Render.cpp b/desktop_version/src/Render.cpp index 52f40b1b..ae8f7eea 100644 --- a/desktop_version/src/Render.cpp +++ b/desktop_version/src/Render.cpp @@ -266,7 +266,10 @@ static void menurender(void) } else { - uint32_t level_flags = PR_FONT_IDX(cl.ListOfMetaData[tmp].level_main_font_idx); + uint32_t level_flags = PR_FONT_IDX( + cl.ListOfMetaData[tmp].level_main_font_idx, + cl.ListOfMetaData[tmp].rtl + ); uint32_t title_flags = cl.ListOfMetaData[tmp].title_is_gettext ? PR_FONT_INTERFACE : level_flags; uint32_t creator_flags = cl.ListOfMetaData[tmp].creator_is_gettext ? PR_FONT_INTERFACE : level_flags; diff --git a/desktop_version/src/main.cpp b/desktop_version/src/main.cpp index 6c928cf7..52674f0a 100644 --- a/desktop_version/src/main.cpp +++ b/desktop_version/src/main.cpp @@ -937,7 +937,7 @@ static void unfocused_run(void) #define FLIP_PR_CJK_HIGH (graphics.flipmode ? PR_CJK_LOW : PR_CJK_HIGH) /* The pause screen can also appear on the language screen, where highlighting * a language changes the used language metadata but not the loaded strings... */ - uint32_t flags = PR_CEN | PR_BOR | PR_FONT_IDX(loc::langmeta.font_idx); + uint32_t flags = PR_CEN | PR_BOR | PR_FONT_IDX(loc::langmeta.font_idx, loc::langmeta.rtl); font::print(flags | FLIP_PR_CJK_HIGH, -1, FLIP(110), loc::gettext("Game paused"), 196 - help.glow, 255 - help.glow, 196 - help.glow); if (BUTTONGLYPHS_keyboard_is_available())