Reference Manual: (offline | online) Quickstart: (offline | online)

# Customizing the Interface

Ren'Py is extremely customizable. Below is information on the many ways available to customize Ren'Py.

## 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. These colors, and the background images used, can be changed by supplying parameters to the theme functions.

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

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", mm_root=..., gm_root=..., centered=False, button_menu=True, 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.

centered - If True, the buttons and sliders will be centered in the frames or windows that contain them. If False, the default, they will be pushed to the right side.

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

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.

Function: theme.roundrect_red( | **params)

This sets up a red/pink variant of the roundrect theme.

The arguments are the same as for theme.roundrect .

## Customizing the Keymap

The variable config.keymap contains a map from functions that can be performed by the various user-interface elements of Ren'Py to a list of keysyms (a keysym is a key symbol) that actually perform those functions. Modifying the contents of the keymap can change the keys and mouse buttons that cause things to happen.

While this functionality has been added to Ren'Py at a user's request (and because it simplifies the Ren'Py code), it's not altogether clear that it should be used. Having a common set of keybindings makes games easier to play by reducing the learning curve of users. It's probably better to build consensus around a change in keybindings, rather than unilaterally making one game different from every other game.

Anyway, in Ren'Py keysyms are strings. The first kind of keysym is of the form 'mouseup_#' or 'mousedown_#', for a number between 1 and 5. These keysyms are generated by mouse button presses, releases, or turns of the mouse wheel. For example, "mousedown_1" is generally a press of the left mouse button, "mouseup_1" is a release of that button, and "mousedown_4" is a turn of the the mouse wheel to the top.

A second kind of keysym is a joystick keysym. These begin with joy_. They are defined in config.joystick_keys , and mapped to actual joystick events by the user.

A third kind of keysym is a string containing a character that is generated when a key is pressed. This is useful for binding alphabetic keys and numbers. Examples of these keysyms include "a", "A", and "7".

The final kind of keysym is the symbolic name for the key. This can be any of the K_ constants taken from pygame.constants This type of keysym looks like "K_BACKSPACE", "K_RETURN", and "K_TAB".

To change a binding, update the appropriate list in config.keymap . The following code adds the 't' key to the list of keys that dismiss a say statement, and removes the space key from that list.

    init:
        $ config.keymap['dismiss'].append('t')
        $ config.keymap['dismiss'].remove('K_SPACE')

The default keymap is contained inside the python code implementing Ren'Py, and as of version 5.6.4 is as follows:

    config.keymap = dict(

        # Bindings present almost everywhere, unless explicitly
        # disabled.
        rollback = [ 'K_PAGEUP', 'mousedown_4', 'joy_rollback' ],
        screenshot = [ 's' ],
        toggle_fullscreen = [ 'f' ],
        toggle_music = [ 'm' ],
        game_menu = [ 'K_ESCAPE', 'mouseup_3', 'joy_menu' ],
        hide_windows = [ 'mouseup_2', 'h' ],
        launch_editor = [ 'E' ],
        dump_styles = [ 'Y' ],
        debugger = [ 'D' ],

        # Say.
        rollforward = [ 'mousedown_5', 'K_PAGEDOWN' ],
        dismiss = [ 'mouseup_1', 'K_RETURN', 'K_SPACE', 'K_KP_ENTER', 'joy_dismiss' ],

        # Focus.
        focus_left = [ 'K_LEFT', 'joy_left' ],
        focus_right = [ 'K_RIGHT', 'joy_right' ],
        focus_up = [ 'K_UP', 'joy_up' ],
        focus_down = [ 'K_DOWN', 'joy_down' ],

        # Button.
        button_select = [ 'mouseup_1', 'K_RETURN', 'K_KP_ENTER', 'joy_dismiss' ],

        # Input.
        input_backspace = [ 'K_BACKSPACE' ],
        input_enter = [ 'K_RETURN', 'K_KP_ENTER' ],

        # These keys control skipping.
        skip = [ 'K_LCTRL', 'K_RCTRL' ],
        toggle_skip = [ 'K_TAB', 'joy_skip' ],
        fast_skip = [ '>' ],

        # These control the bar.
        bar_activate = [ 'mousedown_1', 'K_RETURN', 'K_KP_ENTER', 'joy_dismiss' ],
        bar_deactivate = [ 'mouseup_1', 'K_RETURN', 'K_KP_ENTER', 'joy_dismiss' ],
        bar_decrease = [ 'K_LEFT', 'joy_left' ],
        bar_increase = [ 'K_RIGHT', 'joy_right' ],
        )

## Customizing the 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 styles. To change just the text of menu items, consider using config.translations . To change just the position of buttons in a menu, use config.game_menu_positions and config.main_menu_positions .

### 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.

If one wants to change the main menu each time it is invoked, but keep the look of it the same, then the thing to do is to provide a main_menu label. The code after this main_menu label should set config.main_menu, and then jump to _library_main_menu. This is useful, for example, if there are some items in the main menu that should be absent or disabled until some condition is met. (Say, the user reaching an ending for the first time.)

#### From Scratch

Finally, it may be desirable to write your own menu from scratch. This is done by creating the menu using ui functions called from code invoked through the `main_menu` label. To start the game, the code should jump out of the context, using ui.jumpsoutofcontext or renpy.jump_out_of_context . The code can jump to game menu screens, using the jump statement, renpy.jump , or ui.jumps . When interacting with the user, ui.interact should be called with suppress_overlay=True, suppress_underlay=True, and perhaps mouse="mainmenu".

The functions that are used to make a game menu screen, _game_nav() and _game_interact(), should also work with the main menu screen. When necessary, renpy.context().main_menu can be checked to see if we are in the main menu (it is True) or game menu (it is False). (see renpy.context )

### 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 _game_nav(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 actually draw the screen. Third, it should call _game_interact, instead of ui.interact , to interact with the user. This ensures that the game menu interaction is handled properly. 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 ui.jumps ('label'), where label is the name of the label under which your screen was defined above. Finally, the fourth parameter is a string containing a python expression. If the expression is not true, the button is insensitive.

To customize the game menu navigation buttons, one can replace the _game_nav() function.

Function: _game_nav( | screen)

Called by Ren'Py to display navigation for a game menu screen.

screen- the screen we're at. Default parameters for this are "restart", "load", "save", "prefs", "quit", or None. When None, navigation buttons should't be shown.

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.

There is a convenience function for prompting the user for their response to a yes and no question.

Function: _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. The load screen, save screen, and preferences screen are define at the `_load_screen`, `_save_screen` and `_prefs_screen` labels, respectively.

#### From Scratch

To write your own game menu entirely from scratch, set _game_menu_screen to point to the screen you would like to access when the user right-clicks. You can then jump between game menu screens using the jump statement, renpy.jump or ui.jumps .

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.

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

## Customizing the Preferences Screen

Customizing the preferences screen is accomplished by modifying config.preferences, which is a dictionary mapping from vbox style to a list of preferences to be placed in that vbox. Modifying this dictionary and lists can move preferences around, and eliminate preferences entirely. It may be desirable to create new vbox styles, by using style.create .

Alternative, one can position preferences using config.preference_positions .

To create an entirely new preference, create it with _Preference, _SliderPreference, or _VolumePreference, and then add it to an appropriate list in config.preferences, with a command like `library.preferences['prefs_left'].insert(3, MyNewPreference)`

Function: _Preference( | name, field, values, base=...)

This is a class that's used to represent a multiple-choice preference.

name - The name of this preference. It will be displayed to the user.

field - The field on the base object that will be assigned the selected value. This field must exist.

values - A list of value name, value, condition triples. The value name is the name of this value that will be shown to the user. The value is the literal python value that will be assigned if this value is selected. The condition is a condition that will be evaluated to determine if this is a legal value. If no conditions are true, this preference will not be displayed to the user. A condition of None is always considered to be True.

base - The base object on which the variable is read from and set. This defaults to _preferences, the user preferences object.

Function: _SliderPreference( | name, range, get, set, enable='True')

A class that represents a preference that is controlled by a slider.

set - The name of this preference, that is shown to the user.

range - An integer giving the maximum value of this slider. The slider goes from 0 to range.

get - A function that's called to get the initial value of the slider. It's called with no arguments, and should return an integet between 0 and range, inclusive.

set - A function that's called when the value of the slider is set by the user. It is called with a single integer, in the range 0 to range, inclusive.

enable - A string giving a python expression. If the expression is evaluates to false, this preference is not shown.

Function: _VolumePreference( | name, mixer, enable='True', sound='None', channel=0)

This represents a preference that controls one of the volumes in the system. It is represented as a slider bar, and a button that can be pushed to play a sample sound on a channel.

name - The name of this preference, as shown to the user.

mixer - The mixer this preference controls.

enable - A string giving a python expression. If the expression is evaluates to false, this preference is not shown.

sound - A string that is evaluated to yield another string. The yielded string is expected to give a sound file, which is played as the sample sound. (We apologize for the convolution of this.)

channel - The number of the channel the sample sound is played on.

Function: _remove_preference( | name)

Removes the preference with the given name from the preferences menu.

## From Scratch

One can also write their own preference screen from scratch, as one of the game menu screens. The preferences screen can use the following variables and functions to update the preferences. Preferences take effect as soon as they are set.

Variable: _preferences.fullscreen = | False

True if Ren'Py should run in fullscreen mode, False otherwise.

Variable: _preferences.transitions = | 2

The kinds of transitions to show. 0 for no transitons, 2 for all transitions. A lesser-used value is 1, which shows all transitions except default transitions.

Variable: _preferences.text_cps = | 0

The number of characters per second to show slow text at. 0 means to show infinitely fast. A sensible range is between 1 and 150.

Variable: _preferences.afm_time = | 0

The number of seconds to delay after showing config.afm_characters (default 250) characters in auto-forward mode. A sensible range is between 1 and 40 seconds. If set to 0, auto-forward mode is disabled.

Variable: _preferences.skip_unseen = | False

If True, skipping will skip unseen messages. If False, only seen messages will be skipped.

Variable: _preferences.skip_after_choices = | False

If True, skipping continues past menus. If False, skipping stops a menus.

Function: _preferences.set_volume( | mixer, volume)

Sets the volume of the given mixer to the given volume. mixer is usually 'sfx', 'voice', or 'music', volume ranges between 0 and 1.

Function: _preferences.get_volume( | mixer)

Gets the volume of mixer, which is usually 'sfx', 'voice', or 'music'. The returned volume ranges between 0 and 1.0.

### Joystick Preferences

There are three functions that help in customizing the joystick preferences screen.

Function: _joystick_select_binding( | )

Generates a number of buttons, one for each possible joystick binding. Clicking on these buttons causes ui.interact to return a (label, key) pair, where label is a label indicating the type of binding being customized, and key is the keysym associated with it.

Function: _joystick_get_binding( | )

Calling this prepares Ren'Py to accept a joystick binding. The next call to ui.interact returns the binding when one is selected.

Function: _joystick_take_binding( | binding, key)

This takes a key and binding, and sets Ren'Py up such that that the binding will be translated into the given key.

Skeleton code for the joystick screen looks like:

    label joystick_screen:
       python hide:
           ui.vbox(xalign=0.5, yalign=0.5)
           _joystick_select_binding()
           ui.close()

           label, key = ui.interact()

           ui.text("Setting binding for " + label + ". Click to unset binding.", xalign=0.5, yalign=0.5)
           _joystick_get_binding()

           binding = ui.interact()

           _joystick_take_binding(binding, key)

        jump joystick_screen

Reference Manual: (offline | online) Quickstart: (offline | online)