1
0
Fork 0
mirror of https://github.com/TerryCavanagh/VVVVVV.git synced 2024-12-22 17:49:43 +01:00

Add RTL level property and print flag

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 <font> tag, they now also
have an <rtl> tag that sets this property.

Right now, we'll have to hardcode it so the menu option for the Arabic
font sets the <rtl> 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).
This commit is contained in:
Dav999 2024-01-03 20:09:23 +01:00 committed by Misa Elizabeth Kai
parent 37c4f76988
commit 29e2b19698
11 changed files with 83 additions and 20 deletions

View file

@ -227,6 +227,7 @@ TAG_FINDER(find_desc2, "Desc2")
TAG_FINDER(find_desc3, "Desc3") TAG_FINDER(find_desc3, "Desc3")
TAG_FINDER(find_website, "website") TAG_FINDER(find_website, "website")
TAG_FINDER(find_font, "font") TAG_FINDER(find_font, "font")
TAG_FINDER(find_rtl, "rtl")
/* For CliPlaytestArgs */ /* For CliPlaytestArgs */
TAG_FINDER(find_playtest, "Playtest") 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.level_main_font_idx = font::get_font_idx_8x8();
} }
_data.rtl = help.Int(find_rtl(buf).c_str());
if (pt_args != NULL) if (pt_args != NULL)
@ -1037,6 +1039,7 @@ bool customlevelclass::load(std::string _path)
version = 0; version = 0;
level_font_name = "font"; level_font_name = "font";
rtl = false;
for (pElem = hDoc for (pElem = hDoc
.FirstChildElement() .FirstChildElement()
@ -1104,6 +1107,11 @@ bool customlevelclass::load(std::string _path)
{ {
level_font_name = pText_; 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, "mapwidth", mapwidth);
xml::update_tag(data, "mapheight", mapheight); xml::update_tag(data, "mapheight", mapheight);

View file

@ -66,6 +66,7 @@ struct LevelMetaData
/* This is for the metadata in the levels list, /* This is for the metadata in the levels list,
* so it will only be a main font (no custom ones). */ * so it will only be a main font (no custom ones). */
uint8_t level_main_font_idx; uint8_t level_main_font_idx;
bool rtl;
}; };
struct CliPlaytestArgs struct CliPlaytestArgs
@ -161,6 +162,7 @@ public:
int mapwidth, mapheight; //Actual width and height of stage int mapwidth, mapheight; //Actual width and height of stage
std::string level_font_name; std::string level_font_name;
bool rtl;
int version; int version;

View file

@ -85,6 +85,7 @@ struct PrintFlags
bool align_right; bool align_right;
bool cjk_low; bool cjk_low;
bool cjk_high; bool cjk_high;
bool rtl;
}; };
static FontContainer fonts_main = {}; 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) 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.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) static void fill_map_name_idx(FontContainer* container)
@ -763,13 +767,19 @@ static Font* container_get(FontContainer* container, uint8_t idx)
return NULL; 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 /* Take font selection integer (0-31) and turn it into the correct Font
* 0: PR_FONT_INTERFACE - use interface font * 0: PR_FONT_INTERFACE - use interface font
* 1: PR_FONT_LEVEL - use level font * 1: PR_FONT_LEVEL - use level font
* 2: PR_FONT_8X8 - use 8x8 font no matter what * 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) if (sel < 0)
{ {
@ -782,6 +792,7 @@ static Font* fontsel_to_font(int sel)
case 1: case 1:
if (!font_level_is_interface) if (!font_level_is_interface)
{ {
*rtl = cl.rtl;
if (font_idx_level_is_custom) if (font_idx_level_is_custom)
{ {
return container_get(&fonts_custom, font_idx_level); return container_get(&fonts_custom, font_idx_level);
@ -793,6 +804,7 @@ static Font* fontsel_to_font(int sel)
} }
SDL_FALLTHROUGH; SDL_FALLTHROUGH;
case 0: case 0:
*rtl = loc::get_langmeta()->rtl;
return container_get(&fonts_main, loc::get_langmeta()->font_idx); return container_get(&fonts_main, loc::get_langmeta()->font_idx);
case 2: case 2:
return container_get(&fonts_main, font_idx_8x8); return container_get(&fonts_main, font_idx_8x8);
@ -806,7 +818,11 @@ static PrintFlags decode_print_flags(uint32_t flags)
{ {
PrintFlags pf; PrintFlags pf;
pf.scale = FLAG_PART(0, 3) + 1; 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.brightness = ~FLAG_PART(8, 8) & 0xff;
pf.border = flags & PR_BOR; pf.border = flags & PR_BOR;
pf.full_border = flags & PR_FULLBOR; 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); 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; int text_len = 0;
@ -1313,9 +1329,9 @@ void print(
y -= h_diff_8/2; 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; int position = 0;

View file

@ -42,7 +42,8 @@
#define PR_FONT_INTERFACE (0 << 3) /* default, use interface font */ #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_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_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) */\ #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)) (((~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) */ #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_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_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_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 namespace font

View file

@ -282,11 +282,16 @@ bool is_joiner(const uint32_t codepoint)
return codepoint == 0x200C || codepoint == 0x200D; 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 /* 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. */ * language is actually an RTL one, _or_ if an RTL character is found. */
if (rtl)
{
return true;
}
const char* text_ptr = text; const char* text_ptr = text;
uint32_t ch; uint32_t ch;
while ((ch = UTF8_next(&text_ptr))) while ((ch = UTF8_next(&text_ptr)))
@ -313,7 +318,7 @@ bool bidi_should_transform(const char* text)
return false; return false;
} }
const char* bidi_transform(const char* text) const char* bidi_transform(const bool rtl, const char* text)
{ {
uint32_t utf32_in[1024]; uint32_t utf32_in[1024];
int n_codepoints = 0; int n_codepoints = 0;
@ -345,7 +350,12 @@ const char* bidi_transform(const char* text)
{ {
return 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); SDL_assert(paragraph != NULL);
SBUInteger paragraph_len = SBParagraphGetLength(paragraph); SBUInteger paragraph_len = SBParagraphGetLength(paragraph);
SBLineRef paragraph_line = SBParagraphCreateLine(paragraph, 0, paragraph_len); SBLineRef paragraph_line = SBParagraphCreateLine(paragraph, 0, paragraph_len);

View file

@ -10,8 +10,8 @@ void bidi_init(void);
void bidi_destroy(void); void bidi_destroy(void);
bool is_directional_character(uint32_t codepoint); bool is_directional_character(uint32_t codepoint);
bool is_joiner(uint32_t codepoint); bool is_joiner(uint32_t codepoint);
bool bidi_should_transform(const char* text); bool bidi_should_transform(bool rtl, const char* text);
const char* bidi_transform(const char* text); const char* bidi_transform(bool rtl, const char* text);
} // namespace font } // namespace font

View file

@ -6357,7 +6357,7 @@ void Game::createmenu( enum Menu::MenuName t, bool samemenu/*= false*/ )
text, text,
true, true,
cl.ListOfMetaData[i].title_is_gettext ? PR_FONT_INTERFACE : PR_FONT_IDX( 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++) for (uint8_t i = 0; i < font::font_idx_options_n; i++)
{ {
uint8_t idx = font::font_idx_options[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)) if (font::level_font_is_main_idx(idx))
{ {
option_match = i; 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++) for (size_t i = 0; i < loc::languagelist.size(); i++)
{ {
if (loc::languagelist[i].nativename.empty()) if (loc::languagelist[i].nativename.empty())
{
option(loc::languagelist[i].code.c_str()); option(loc::languagelist[i].code.c_str());
}
else 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); menuyoff = 70-(menuoptions.size()*10);

View file

@ -64,7 +64,7 @@ static void sync_lang_file(const std::string& langcode)
loadtext(false); loadtext(false);
uint8_t glyph_w = 8, glyph_h = 8; 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; bool max_local_needed = glyph_w != 8 || glyph_h != 8;
tinyxml2::XMLDocument doc; tinyxml2::XMLDocument doc;

View file

@ -311,7 +311,7 @@ static bool max_check_string(const char* str, const char* max)
} }
uint8_t font_idx = get_langmeta()->font_idx; 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_w = 8;
uint8_t font_h = 8; uint8_t font_h = 8;
font::glyph_dimensions(print_flags, &font_w, &font_h); font::glyph_dimensions(print_flags, &font_w, &font_h);

View file

@ -266,7 +266,10 @@ static void menurender(void)
} }
else 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 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; uint32_t creator_flags = cl.ListOfMetaData[tmp].creator_is_gettext ? PR_FONT_INTERFACE : level_flags;

View file

@ -937,7 +937,7 @@ static void unfocused_run(void)
#define FLIP_PR_CJK_HIGH (graphics.flipmode ? PR_CJK_LOW : PR_CJK_HIGH) #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 /* The pause screen can also appear on the language screen, where highlighting
* a language changes the used language metadata but not the loaded strings... */ * 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); 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()) if (BUTTONGLYPHS_keyboard_is_available())