documentation indexreference manualfunction index

Contents

Customizing the Interface

In Ren'Py, there are four ways that one can customize the out-of-game menus:

Of these, the only mandatory customization is the selection of a theme.

Order of Customizations

We expect customization of the game to occur in the following order:

  1. If necessary, setting the script version (config.script_version), screen width (config.screen_width). and screen height (config.screen_height).
  2. If necessary, selecting one or more layouts.
  3. Selecting a master theme.
  4. If necessary, selecting one or more theme components that override portions of the master theme.
  5. If necessary, customize the main and game menus, and using the style system to further customize the interface.

Layouts

Layouts control the feel of the out-of-game menus. There are five main kinds of layouts:

There are also standalone layouts which do not fall into any of these categories. While a game needs exactly one of each of the five main kinds of layouts to function properly, it can have as many standalone layouts as it needs.

Each kind of layout provides certain functionality to the interface. The functionality provided by each kind of layout is described below. The selection of layouts is expected to occur before the selection of a theme. If no layout of a given kind is selected, then the classic variant is used. For example, if no yesno_prompt layout has been selected, then layout.classic_yesno_prompt is used.

Note that there are combinations of layouts and themes that will not look reasonable together. This is not a bug... it's impossible to have every layout work with every other layout and every theme, and still have an unlimited range of layouts and themes. Caveat factor.


main_menu layouts

The main_menu layouts are responsible for defining the look of the main menu. This includes the background of the main menu, and the buttons that are used to go to the game menu or start a game.


Function: layout.classic_main_menu ():

This displays the classic main menu, which stacks the main menu buttons vertically.



Function: layout.grouped_main_menu ():

This defines a main menu layout that groups buttons into rows horizontally, and then stacks those rows vertically.

Variable: config.main_menu_per_group = 2

The number of buttons placed in to each horizontal row.



navigation layouts

The navigation layouts are responsible for defining the navigation through the game menu. This includes the background of the game menus and the buttons that are used to go between game menu screens.


Function: layout.classic_navigation ():

This displays the navigation buttons in a vertical box.



Function: layout.grouped_navigation ():

This displays the navigation buttons in horizontal groups, which are then stacked vertically.

Variable: config.navigation_per_group = 2

The number of navigation buttons per horizontal group.



load_save layouts

The load_save layouts are responsible for defining the load and save screens.


Function: layout.classic_load_save ():

This layout displays the the load and save screens as having a certain number of rows and columns of slots, with a row of navigation buttons on top.

Variable: config.file_page_rows = 5

The number of rows of file slots to display.

Variable: config.file_page_cols = 2

The number of columns of file slots to display.

Variable: config.file_quick_access_pages = 8

The number of pages of files to provide quick access buttons for.

Variable: config.disable_file_pager = False

If True, the pager is disabled. This prevents access to pages other than the first page of files.

Variable: config.disable_thumbnails = False

If True, thumbnails are not shown.

Variable: config.time_format = "%b %d, %H:%M"

The format used for file times in the file entry slots.

Variable: config.file_entry_format = "%(time)s\n%(save_name)s"

The format of file entries in the file entry slots.



Function: layout.scrolling_load_save ():

This uses a scrolling area that contains file picker entries. The user picks one of these entries to load or save a file. There is one big thumbnail to the right of the screen, which corresponds to the currently hovered entry. (config.thumbnail_width and config.thumbnail_height control the size of this thumbnail.)

Variable: config.load_save_slots = 50

The number of normal slots to show.

Variable: config.load_save_auto_slots = 5

The number of autosave slots to show.

Variable: config.load_save_quick_slots = 5

The number of quicksave slots to show.

Variable: config.load_save_empty_thumbnail = None

When not None, this should be a displayable that will be shown in the thumbnail frame when no save slot has been hovered.

Variable: config.time_format = "%b %d, %H:%M"

The format used for file times in the file entry slots.

Variable: config.file_entry_format = "%(time)s\n%(save_name)s"

The format of file entries in the file entry slots.



yesno_prompt layouts

The yesno_prompt layouts are responsible for defining yes/no prompt screens.


Function: layout.classic_yesno_prompt ():

This displays the classic yes/no prompt, which is just a prompt above two buttons.



preferences layouts

The preferences layouts are used to define the preferences screen.


Function: layout.classic_preferences ():

This uses a three-column preferences layout.

Variable: config.sample_sound = None

A sound file used to test the sound volume.

Variable: config.sample_voice=None = None

A sound file used to test the voice volume.

Variable: config.has_transitions = True

If True, the option to enable or disable transitions is shown.

Variable: config.has_cps = True

If True, the option to control text speed is shown.

Variable: config.has_afm = True

If True, the option to control auto-forward mode is shown.

Variable: config.has_skipping = True

If True, the option to control skipping read messages or not is shown

Variable: config.has_skip_after_choice = True

If True, the option to control skipping after choices is shown

Variable: config.always_has_joystick = False

If True, the link to the joystick page is always active.

Variable: config.has_joystick = True

If True, the link to the joystick page is shown.



Function: layout.two_column_preferences ():

This uses a two-column preferences layout. Configuration variables are the same as for layout.classic_preferences.



Function: layout.one_column_preferences ():

This uses a one-column preferences layout. Configuration variables are the same as for layout.classic_preferences.



joystick_preferences layouts

The joystick_preferences layout are used to define the joystick preferences screen.


Function: layout.classic_joystick_preferences ():

The standard joystick preferences layout.



standalone layouts

These provide interesting functionality to Ren'Py games, and they stand alone, so it's possible to call as many of them as you want.


Function: layout.button_menu ():

This changes the in-game menus to use buttons defined in the current theme.



Defining New Layouts

This section contains some notes on how to define your own layouts. If you're not interested in defining your own labels, then you can safely ignore this section.

When it comes to defining layouts, there are two important principles you should follow:

  1. layout.provides must be called with the type of layout being defined, and it must be called before any theme function is called. For example, if a navigation layout is being defined, then layout.provides("navigation") should be called.
  2. The layout needs to implement the contract of it's layout kind. These contracts are described below.

While the default layouts are enable through the use of functions on the layout object, this is by no means the only way to supply a layout. For a user-defined layout, it should be enough to place the call to layout.provides in an init -2 python block, with the rest of the code residing in init blocks or labels as appropriate.

If your layout defines new configuration variables, you should set config.locked to False before creating them, and then back to True when you're done creating them. We suggest that configuration variables should be prefixed with the type of layout they're used by. Load/save variables should be prefixed by config.load_save_, navigation variables with config.navigation_ and so on. (For compatibility reasons, the default layouts do not conform to this standard.)

main_menu layouts

Main_menu layouts need to call:

    layout.provides('main_menu')

Main menu layouts are expected to define a main_menu_screen label. This label is expected to:

    python:
        ui.window(style='mm_root')
        ui.null()
    python:
        ui.keymap(game_menu=ui.returns(None))

navigation layouts

Navigation layouts need to call:

    layout.provides('navigation')

Navigation layouts are expected to redefine the layout.navigation function. The way to do this is to first define a function in an init python block, and then assign that function to layout.navigation.


Function: layout.navigation (screen):

This function is intended to be set by a navigation layout. It's legitimate to replace this function with one of your choosing, provided the signature remains the same.

This function displays the navigation on the game menu. It's expected to set the background of the game menu. If screen is not None, it's also expected to display the navigation buttons defined in config.game_menu, highlighting the one named in screen.


It's suggested that your navigation function use config.game_menu to determine the game menu buttons to show to the user. (But note that the game menu is less often extended then the main menu, so this is correspondingly less important.)


load_save layouts

Load/save layouts need to call:

    layout.provides('load_save')

Load/save layouts are expected to provide two labels: load_screen and save_screen, which are used to load and save, respectively. These screens should call layout.navigation with "load" or "save" as an argument, and are otherwise unconstrained in how they provide loading and saving functionality.

Please see the section on load/save functions for the functions that would be used to actually implement these screens.

yesno_prompt layouts

Yes/no prompt layouts need to call:

    layout.provides('yesno_prompt')

Navigation layouts are expected to redefine the layout.yesno_prompt function. The way to do this is to first define a function in an init python block, and then assign that function to layout.yesno_prompt.


Function: layout.yesno_prompt (screen, message):

This function is intended to be customized by yesno_prompt layouts. It can be replaced by another function, provided the signature remains the same.

screen - The screen button that should be highlighted when this prompt is shown. If None, then no game menu navigation is shown.

message - The message that is shown to the user to prompt them to answer yes or no.

This function returns True if the user clicks Yes, or False if the user clicks No.


preferences layouts

Preferences layouts need to call:

    layout.provides('preferences')

The preferences layout should define a preferences_screen label, which contains code to set the preferences. This screen should call layout.navigation("preferences"). It then needs to allow the user to alter the preferences, using the variables and functions defined in the preferences section. Finally, it should:

joystick_preferences layouts

Joystick preferences layouts need to call:

    layout.provides('joystick_preferences')

The preferences layout should define a joystick_preferences_screen label, which contains code to set the joystick preferences. This screen should call layout.navigation("joystick_preferences"). Unfortunately, the joystick preferences are undocumented at this time.

Themes

Themes provide a simple way of changing the look of the main and game menus. A single function call applies styles to many of the elements of the main and game menus, giving a consistent look to the interface.

Theme functions should be called after the config.screen_width, config.screen_height, and library.script_version variables have been set, and after any layout functions have been called. They should be called before any style is changed by hand.

Theme Functions

These theme functions are


Function: theme.roundrect (widget="#003c78", widget_hover="#0050a0", widget_text="#c8e1ff", widget_selected="#ffffc8", disabled="#404040", disabled_text="#c8c8c8", label=,"#ffffff" frame="#6496c8", window="#000000c0", text_size=None, small_text_size=None, mm_root=..., gm_root=..., less_rounded=False):

This enables the use of the roundrect theme. By default, this theme styles the game in a blue color scheme. However, by supplying one or more of the parameters given below, the color scheme can be changed.

widget - The background color of non-focued buttons and sliders.

widget_hover - The background color of focused buttons and sliders.

widget_text - The text color of non-selected buttons.

widget_selected - The text color of selected buttons.

disabled - The background color of disabled buttons.

disabled_text - The text color of disabled buttons.

label - The text color of non-selected labels.

frame - The background color of frames.

mm_root - A displayable (such as an Image or Solid) that will be used as the background for the main menu.

gm_root - A displayable (such as an Image or Solid) that will be used as the background for the game menu.

less_rounded - If True, causes the buttons to appear less rounded in 800x600 mode (has no effect in 640x480 mode).

text_size - The size of text, such as button captions, labels, and prompts. Defaults to 18 if the screen is 640 pixels wide or less, and 22 otherwise.

small_text_size - The size of the text on large buttons. Defaults to 12 if the screen is 640 pixels wide or less, and 16 otherwise.

widget, widget_hover, disabled, and frame may either be single colors, or tuples containing two colors. In the latter case, a vertical gradient is used.

Component functions

The following functions exist to allow you to add elements of the roundrect theme to another theme. The other theme must have been set up before these functions can be used. Arguments are as for roundrect, except that all must be specified (no defaulting occurs).



Function: theme.outline (inside="#fff", idle="#e66", hover="#48f", selected="#84f", insensitive="#ccc", label="#484", prompt="#484", background="#fee", large_button="#fff8f8", text_size=22, small_text_size=16):

This function selects a theme that is based on outlining text in different colors.

inside - The color of text inside the various outlines.

idle - The outline color of the text of an idle button or bar.

hover - The outline color of a hovered button or bar.

selected - The outline color of a selected button.

insensitive - The outline color of an insensitive button.

label - The outline color of a label.

prompt - The outline color of a prompt.

background - A displayable used for the game and main menu backgrounds.

large_button - The background color of large backgrounds.

text_size - The size of large text. (Used for buttons, labels, and prompts.)

small_text_size - The size of small text. (Used in large buttons.)

Component functions

The following functions exist to allow you to add elements of the outline theme to another theme. The other theme must have been set up before these functions can be used. Arguments are as for theme.outline, except that all must be specified (no defaulting occurs).




Function: theme.ancient ():

This is a theme that attempts to emulate the theme used by Ren'Py 6.5.0 when no theme was explicitly specified.



Theme Modifiers

These are functions that can be called after a theme function, allowing you to change a portion of a theme.


Function: theme.image_buttons (d):

Used to define buttons in terms of 5-tuples of image filenames. This expects its single parameter, d, to be a dictionary mapping untranslated button names to 5-tuples. Each 5-tuple should contain 5 filenames, giving the image used for the button when:

in that order.


Function: theme.image_labels (d):

Replaces labels with images. This takes a single parameter, d, which is expected to be a map from the text of a label to an image file.


Custom Theme

It's also possible to define your own Ren'Py theme. A custom theme consists of Ren'Py code that does the following.

Often, the base styles come in name/name_text pairs. In these cases, name represents a Button or Window with style name, in which a Text with style name_text lives.

The base styles are:

style.frame (inherits from style.default)
Used for frames on top of which the rest of the interface can comfortably sit.
style.button (inherits from style.default)
style.button_text (inherits from style.default)
Used for buttons, especially buttons whose primary purpose is navigating through the interface. (Like the main menu and the game menu navigation buttons.)
style.small_button (inherits from style.button)
style.small_button_text (inherits from style.button_text)
Used for smaller navigation buttons. It might make sense to set a minimum width on buttons, but small_buttons should be allowed to shrink as small as possible.
style.radio_button (inherits from style.button)
style.radio_button_text (inherits from style.button_text)
Used for buttons that are arranged in a group, such that only one of the buttons in the group can be selected at a time.
style.check_button (inherits from style.button)
style.check_button_text (inherits from style.button_text)
Used for buttons that toggle their selected state to indicate if an option is set or not. (These aren't used in any of the pre-defined layouts.)
style.large_button (inherits from style.default)
style.large_button_text (inherits from style.default)
Used for large buttons, such as file picker entries, that can contain a large amount of text and other information.
style.label (inherits from style.default)
style.label_text (inherits from style.default)
Used for labels, which are small text messages that never change.
style.prompt (inherits from style.default)
style.prompt_text (inherits from style.default)
Used for prompts, longer text messages which may change at runtime.
style.bar (inherits from style.default)
style.vbar (inherits from style.default)
Used for horizontal and vertical bars, respectively. Bars are generally intended to indicate a quantity or an amount of progress, but aren't expected to be adjusted by the user.
style.slider (inherits from style.default)
style.vslider (inherits from style.default)
Used for horizontal and vertical sliders, respectively. Sliders are bars that are used to adjust a value.
style.scrollbar (inherits from style.default)
style.vscrollbar (inherits from style.default)
Used for horizontal and vertical scrollbars, respectively.
style.mm_root (inherits from style.default)
style.gm_root (inherits from style.default)
Used for the backgrounds of the main and game menus, respectively.

Generally, themes should not adjust the margins, positioning properties, or maximum sizes of these styles. An exception is that the bars are expected to set a maximum size in a direction perpendicular to the orientation of the bar (ymaximum for bar and scrollbar; xmaximum for vbar and vscrollbar). No limitations apply to the _text styles.

Main and Game Menus

This section describes how the content of the various menus can be customized. If you just want to change the look of the menus (to the extent made possible by the style system), use themes or styles. To change just the text of menu items, consider using config.translations. To change the screens on the menus, you'll need to change the contents of the config.main_menu and config.game_menu variables. Otherwise, you'll want to use a layout that changes the look of the menus, or write your own layout.

Main Menu

The main menu can be customized by setting the config.main_menu variable. This variable should be a list of pairs. The first element of each pair is the name of the button on the main menu. The second item can be one of three things. It can be a string, in which case it is a label at which the game execution starts (after a jump out of the menu context). It can be a function, in which case the function is called when the button is clicked. Finally, it can be None, which causes the button to be insensitive.

To jump from the main menu to one of the screens in the game menu, then one should use the _intra_jumps function to specify the appropriate transition.

Function: _intra_jumps (label, transition):

Jumps to label while remaining in the same context.

transition should be a string giving a field on the config object. When this function is run, that field is accessed, and is used to specify the transition that is used.

The default value of config.main_menu is:

    config.main_menu = [
        (u"Start Game", "start", "True"),
        (u"Continue Game", _intra_jumps("load_screen", "main_game_transition"), "True"),
        (u"Preferences", _intra_jumps("preferences_screen", "main_game_transition"), "True"),
        (u"Quit", ui.jumps("_quit"), "True")
        ]


Game Menu

The first thing one may wish to do when modifying the game menu is to add a screen to it. This is done in two steps. The first is to create the screen, and the second is to add it to the config.game_menu list so that it can be reached from the game menu.

Each screen is represented in the code as a label that is jumped to to display the screen. There are five steps that this label should do in order to display the screen. First, it should call layout.navigation(screen), where screen is the name of the screen (the first component of the tuples in config.game_menu, described below). Second, it should call the ui functions to add components to the the screen. Third, it should call ui.interact to interact with the user. Fourth, it should examine the results of _game_interact, and react appropriately. Finally, it should jump back up to the screen label, showing the screen again after each interaction.

So that the user can see it, the screen should be added to config.game_menu. This is a list of four-component tuples. The first is the name of the screen. It's used to determine if the button used to reach that screen should be indicated as selected. The second component is the text used for that button. The third component is a function that executes when the button is clicked. Normally, an appropriate function is _intra_jumps(label, transition), where label is the name of the label of your screen. Finally, the fourth parameter is a string containing a python expression. If the expression is not true, the button is insensitive.

The default value of config.game_menu is:

    config.game_menu = [
        ( None, u"Return", ui.jumps("_return"), 'True'),
        ( "preferences", u"Preferences", _intra_jumps("preferences_screen", "intra_transition"), 'True' ),
        ( "save", u"Save Game", _intra_jumps("save_screen", "intra_transition"), 'not main_menu' ),
        ( "load", u"Load Game", _intra_jumps("load_screen", "intra_transition"), 'True'),
        ( None, u"Main Menu", ui.callsinnewcontext("_main_menu_prompt"), 'not main_menu' ),
        ( None, u"Quit", ui.callsinnewcontext("_quit_prompt"), 'True' ),
        ]


Variable: _game_menu_screen = "_load_screen"

One can customize the screen the game menu jumps to by default by changing the value of _game_menu_screen. For example, one could set this to "load_screen" for the first few interactions, and then set it to "save_screen" for the rest of the game. This is especially useful for a game with no main menu.

If set to None, the user cannot enter the game menu. This is set to None at the start of the splashscreen, and reset to its old value when the splashscreen completes.

Use the layout.yesno_prompt function to ask yes/no questions of the user.

Function: layout.yesno_prompt (screen, message):

screen - The screen button that should be highlighted when this prompt is shown. If None, then no game menu navigation is shown.

message - The message that is shown to the user to prompt them to answer yes or no.

This function returns True if the user clicks Yes, or False if the user clicks No.

Pre-defined Screens. Layouts define the load screen, save screen, and preferences screen are define at the load_screen, save_screen, and preferences_screen, respectively.

Please see the page on Saving, Loading, and Rollback for a list of functions that can be used in the load and save screens, and the page on the Preferences for a list of preferences that can be customized.

Jump to _return to return to the game or the main menu, as appropriate. Jump to _noisy_return to play config.exit_sound before returning.

Entering the Game Menu

To enter the game menu from game code, use renpy.call_in_new_context or ui.callsinnewcontext to call the _game_menu label with a parameter, the name of the screen you wish to enter. This parameter can be omitted to enter the default screen.

This can be omitted to use the default screen.

init python:
    def overlay():
         # We can assume that "save_screen" is the default.
         ui.textbutton("Save", clicked=ui.callsinnewcontext("_game_menu"), xalign=0.0, yalign=0.0)
         ui.textbutton("Prefs", clicked=ui.callsinnewcontext("_game_menu", "preferences_screen"), xalign=1.0, yalign=0.0)

    config.overlay_functions.append(overlay)

Compatibility Mode

Ren'Py also supports a compatibility mode that makes layouts and themes more compatible with how they were used in Ren'Py 6.5.0. This compatibility mode is automatically enabled when config.script_version is set to (6, 5, 0) or less. It can also be enabled explicitly by calling layout.compat().


Function: layout.compat ():

This enables a compatibility mode that sets up themes and styles as they were used in Ren'Py 6.5.0 and prior versions.


documentation indexreference manualfunction index