A Hacky approach to Ren'Py Text Accessibility
Tags: posts, from dreamwidth, accessibility, renpy, tutorial, coding,
Ren'Py All-In-One GUI Template is a much less hacky approach but I couldn't get it to do what I wanted without having the code break. So this is what I did instead.
What I wanted: The ability to have very large text in Renpy, including button and UI text etc, and having the text box resize to match.
And here's how I did it, if only for my own future reference, as well as some rambling about why I made the decisions I did.
I based my code on the in-built Renpy accessibility screen (found in 00accessibility.rpy in the main renpy folder) and GUI Preferences example code. I also drew on Ren'Py All-In-One GUI Template, which in turn draws on Ren'Py Accessibility Add-On.
I probably could have done a lot of this with styles, but styles confuse me, so I didn't.
The principles I decided upon after some thought and rereading the Game Accessibility Guidelines:
- Large text options should include all text necessary to play the game: dialogue, button text, and menus.
- The game should start with moderately large text by default. It's no good having a "change text size" button that people with poor vision can't see.
- the largest text size should be as large as possible without making the menus unreadable.
- Don't offer the player too many options they don't need.
- The small text size should be fairly readable, but is mostly there for The Aesthetic, since it looks cute and shows more of the sprites/background.
- Every text size should fit nicely in it's text box.
- "Dyslexic Fonts" don't actually offer any specific extra benefit to dyslexic people, as long as there's a nice clean option like Deja Vu Sans that's good enough.
- Including accessibility options in the general preferences screen normalises them and makes more sense. All preferences are accessibility preferences for someone!
So! I have two new additions to the Preferences screen: a font choice, and a text size choice.
Font
Overwrite/replace the following in gui.rpy:
define gui.text_font = gui.preference("font", "gui/IMFeDPrm28P.ttf") #the font used for dialogue, buttons etc. define gui.interface_text_font = gui.preference("UIfont", "gui/IMFeENsc28P.ttf") #the font used for user interfaces define gui.history_height = None
My default is IM Fell.
Since I wanted my preferences screen to look good with very large text, I made sure every line only had two columns. I also added "xsize 400" and "spacing 5" to each block to stop the text getting garbled.
So here's the row in screens.rpy with the font and text size choices in the Preferences menu:
hbox: box_wrap True null height (4 * gui.pref_spacing) vbox: {I have multiple versions of this, see below} vbox: xsize 400 spacing 5 style_prefix "radio" label _("Typeface") textbutton "{font=DejaVuSans.ttf}Sans-Serif{/font}" action [ gui.SetPreference("font","DejaVuSans.ttf"),gui.SetPreference("UIfont", "DejaVuSans.ttf")] textbutton "{font=gui/IMFeDPrm28P.ttf}Serif{/font}" action [ gui.SetPreference("font","gui/IMFeDPrm28P.ttf"),gui.SetPreference("UIfont", "gui/IMFeENsc28P.ttf")] null height (4 * gui.pref_spacing)
I decided the actual font names would just confuse people, but I'm not sure Sans Serif vs Serif is the best dichotomy. I might change it to simple vs fancy or something.
Text Size: Version One
This has three text size options, and three matching unscaled textboxes. I couldn't be bothered implementing text box transparency options, so just made the largest text size box entirely opaque. This is the only version that adjusts the history.
Overwrite/replace the following in gui.rpy:
define gui.textbox_height = gui.preference("text_height", 186) #the height of the text box image define gui.dialogue_xpos = gui.preference("text_start", 59) #how far right into the textbox the dialogue starts define gui.dialogue_width = gui.preference("text_width", 1160) #how wide the dialogue is allowed to get define gui.history_text_xpos = gui.preference("history_xpos", 170) #where history dialogue starts define gui.history_text_width = gui.preference("history_width", 740) #how wide history dialogue is
Add this to the Preferences screen in screens.rpy:
vbox: xsize 400 spacing 5 style_prefix "radio" label _("Font Size") textbutton "Small" action [Preference("font size", 0.8), gui.SetPreference("text_height", 185), gui.SetPreference("text_start", 165), gui.SetPreference("text_width", 950), gui.SetPreference("history_xpos", 170),gui.SetPreference("history_width", 740)] textbutton "Medium" action [Preference("font size", 1.0), gui.SetPreference("text_height", 186), gui.SetPreference("text_start", 59),gui.SetPreference("text_width", 1160), gui.SetPreference("history_xpos", 171),gui.SetPreference("history_width", 739)] textbutton "Large" action [Preference("font size", 1.25), gui.SetPreference("text_height", 240), gui.SetPreference("text_start", 60),gui.SetPreference("text_width", 1166), gui.SetPreference("history_xpos", 260),gui.SetPreference("history_width", 660)]
Note that I made some of the sizes one pixel different, this was to stop Renpy thinking the choices counted as the same (I said this was hacky >.>)
And here's the new code for the dialogue window:
style window: xalign 0.5 xfill True yalign gui.textbox_yalign ysize gui.textbox_height background ConditionSwitch("preferences.font_size<=0.8", Image("gui/textbox_small.png"), "preferences.font_size<=1.25", Image("gui/textbox_medium.png"), "True", Image("gui/textbox_large.png"))
These numbers were found via a lot of trial and error. I initially just used a frame for the textbox until I had the size right.
Text Size: Version Two
This has a text size slider and resizes the textbox to fit. Inspired by this approach which I couldn't quite get to work. I couldn't figure out how to stop long character names overlapping the text in the history.
Add/overwrite the following variables in gui.rpy:
define gui.dialogue_ypos = 0 define gui.textbox_height = None #resize automatically define gui.textbox_borders = Borders(10, 10, 10, 10) #size of corners define gui.textbox_min = 185 #minimum height
Here's some documentation on Frames and Borders.
In screens.rpy:
Add the line "has vbox" and some padding to the say screen:
window: id "window" has vbox if who is not None: window: id "namebox" style "namebox" text who id "who" else: text " " text what id "what" text " "
Edit the dialogue window:
style window: xalign 0.5 xfill True yalign gui.textbox_yalign ysize gui.textbox_height yminimum gui.textbox_min background Frame("gui/textbox.png", gui.textbox_borders)
And that should work automatically! You can either just link to the accessibility screen, or add the text size bar to your preferences screen:
vbox: style_prefix "slider" box_wrap True xsize 400 spacing 5 label _("Text Size Scaling") null height 10 bar value Preference("font size") textbutton _("Reset"): action Preference("font size", 1.0)
Text Size: Version 3
If you want a fancy unscaled textbox by default but a scaled one if the text size gets larger, use Version Two, but replace the window code with:
style window: xalign 0.5 xfill True yalign gui.textbox_yalign ysize gui.textbox_height yminimum gui.textbox_min background ConditionSwitch("preferences.font_size<=1", Image("gui/textbox_fancy.png"), "True", Frame("gui/textbox_scalable.png", gui.textbox_borders))