mirror of
https://github.com/TerryCavanagh/VVVVVV.git
synced 2024-12-22 09:39: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:
parent
37c4f76988
commit
29e2b19698
11 changed files with 83 additions and 20 deletions
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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())
|
||||
|
|
Loading…
Reference in a new issue