[Solved] Manually set a textbutton to a state (e.g., idle)

Discuss how to use the Ren'Py engine to create visual novels and story-based games. New releases are announced in this section.
Forum rules
This is the right place for Ren'Py help. Please ask one question per thread, use a descriptive subject like 'NotFound error in option.rpy' , and include all the relevant information - especially any relevant code and traceback messages. Use the code tag to format scripts.
Post Reply
Message
Author
Phagocytosis
Newbie
Posts: 7
Joined: Mon Jun 21, 2021 1:27 pm
Projects: Polymath – a simulation-heavy life sim about learning
Location: Netherlands
Discord: Babelismoj#4758
Contact:

[Solved] Manually set a textbutton to a state (e.g., idle)

#1 Post by Phagocytosis »

Hi!

In short:
I am looking for a way to manually set a textbutton to "idle" based on some (external?) condition. I don't much mind whether it is to be written as some sort of conditional inside the block or line for the button (similar to SensitiveIf, but for idle rather than insensitive) or it can be called somewhere outside the block (perhaps referencing the button by a variable name?
Although no documentation or tutorial I find seems to do it this way; even if there is a block rather than a line, the button does not get a variable for external reference.
).

More context:
I am writing code for a nested menu for the player to select among tasks to perform that are grouped in categories and subcategories. I made it so that if you click on a category, the menu for the subcategories opens, and then the actual task down below, and finally the location where to perform the task once a task is selected (so there are four levels in the nested menu). I'm using a main (top-level) screen that uses separate screens for each (other) level of the menu, and I set a variable for each level to decide whether to show that level.

However, when I added a style to the buttons with different colors to make it clearer when a button was selected, I noticed that when I close and re-open a particular submenu level (the menu collapses when you go back to a higher level or re-select the same option; see the code in the while loop), a button that was previously selected in that level might still be highlighted. So, I would like to be able to set that button to idle manually—because since there are separate levels involved, it doesn't seem possible to just use a single radio-button style menu where only one can be active at a time (in fact, multiple should be allowed to be active; just only one per level; but setting idle should also be possible between the levels).

Code for illustration (slightly simplified):

Code: Select all

# This example uses only three levels (top, task, loc) rather than the four from my code, for simplicity.
style basicButton:
    color "#888888"
    hover_color "#0066cc"
    selected_color "#cccccc"
    insensitive_color "#444444"


label start:
    $ playing = True
    while playing:
        # It remembers the previous selections to decide whether the same option is clicked twice
        # (which would close the lower-level menu that was just opened).
        $ previousSelectedTop = selectedTop
        $ previousTask = task
        $ UIReturn = renpy.call_screen("TopMenu", _layer="screens")
        if UIReturn == "topMenuClick":
            # If you click back to the top level after having reached further down, collapse the menu.
            $ showLocMenu = False
            $ locChosen = None  # This variable is used later in the program, as is the variable 'task'.
            # Clicking top menu shows the next level, unless already shown and the same top choice clicked again.
            if not selectedTop == previousSelectedTop or not showTaskMenu:
                $ showTaskMenu = True
            else:
                $ showTaskMenu = False
                # Ideally, here is where I would set all buttons from the task and location menus to idle.
                # That way, if I open up the same category in the top menu again, the task I may have previously selected
                # in the lower-level task or location menus would no longer be misleadingly highlighted as if selected
                # (even though the game has forgotten the associated data, like locChosen).
        if UIReturn == "taskMenuClick":
            # Clicking task menu shows the next level, unless already shown and the same task choice clicked again.
            if not task == previousTask or not showLocMenu:
                $ showLocMenu = True
            else:
                $ showLocMenu = False
                $ locChosen = None
                # Here too I would like to set the location menu buttons to idle to avoid highlighting upon re-opening.
        if UIReturn == "locMenuClick":
            pass  # This no longer does anything because I added a confirmation button instead.
        if UIReturn == "confirmButtonClick":
            jump newDay  # I did not include this label, as it is outside the scope of my question. The jump works fine.
    return


screen TopMenu():
    # Show the menu for the basic category of tasks to choose.
    frame:
        xalign 0.0
        vbox:
            for skill in skills:  # These are objects from a Skill class (not shown here) that are in a global list 'skills'.
                textbutton skill.name:
                    text_style "basicButton"
                    action SetVariable("selectedTop", skill), Return("topMenuClick")

    if showTaskMenu:
        use TaskMenuScreen
    if showLocMenu:
        use LocMenuScreen

    # Confirmation button.
    frame:
        xpos 1200
        textbutton "Confirm":
            text_style "basicButton"
            # Only able to confirm if a selection is made on all levels of the menu.
            action [Return("confirmButtonClick", SensitiveIf(locChosen is not None)]


screen TaskMenu():
    # Show the menu for the subcategory of tasks to choose.
    xpos 400
    frame:
        for skill in selectedTop.subSkills:
            textbutton skill.name:
                text_style "basicButton"
                action SetVariable("task", skill), Return("taskMenuClick")


screen LocMenu():
    # Show the menu for the location choice.
    xpos 800
    frame:
        for place in task.places:
            textbutton place.name:
                text_style "basicButton"
                action SetVariable("locChosen", place), Return("locMenuClick")
Final thought:
It occurs to me that if I could destroy the buttons in the lower-level menus upon collapse, that would also work, if they could be recreated when the lower level is reopened, as when another higher-level option is selected. That way too, they would all start as idle again. However, since I was unable to find this information after searching for quite a while, I would prefer to have my original question answered, for future Ren'Py users with a similar question.
Last edited by Phagocytosis on Mon Jun 21, 2021 6:00 pm, edited 1 time in total.

User avatar
Ocelot
Lemma-Class Veteran
Posts: 2384
Joined: Tue Aug 23, 2016 10:35 am
Github: MiiNiPaa
Discord: MiiNiPaa#4384
Contact:

Re: Manually set a textbutton to a state (e.g., idle)

#2 Post by Ocelot »

By default button sensitive state is chosen by its Actions. SensitiveIf action provides one way to override it, other one is sensitive property:
https://www.renpy.org/doc/html/screens.html#button

selected state is, by default, chosen by its Actions too. For example, if button sets some variable to some value, button will be drawn selected if that variable already contains that value. You can override it by using either SelectedIf action ( https://www.renpy.org/doc/html/screen_a ... SelectedIf ) or by using selected button property.
< < insert Rick Cook quote here > >

User avatar
Alex
Lemma-Class Veteran
Posts: 3090
Joined: Fri Dec 11, 2009 5:25 pm
Contact:

Re: Manually set a textbutton to a state (e.g., idle)

#3 Post by Alex »

Some more explanations about button's behavior - viewtopic.php?f=51&t=60864

Phagocytosis
Newbie
Posts: 7
Joined: Mon Jun 21, 2021 1:27 pm
Projects: Polymath – a simulation-heavy life sim about learning
Location: Netherlands
Discord: Babelismoj#4758
Contact:

Re: Manually set a textbutton to a state (e.g., idle)

#4 Post by Phagocytosis »

Wow, I can't believe I completely glanced over the SelectedIf section in the documentation. I worked it out now, after some more tinkering, and it seemed to work with both SelectedIf as suggested by Ocelot (cool name!) and the approach described by Asceai in the extra explanation linked by Alex. So thank you very much, to both of you!

However, upon closer consideration, it looks like what actually ended up solving my problem was to reset the other variables. I was never able to get SelectedIf to work directly. (Did not test for the other approach if it worked even without resetting the other variables involved.) So, what I did was to set, for example,

Code: Select all

task = None
or

Code: Select all

locChosen = None
when I wanted the buttons for the relevant menu to get deselected. When I went into the console and tested out the selectedInTaskMenu and selectedInLocMenu variables that I had tested out, they were always False, even when the buttons were shown as selected. So that part, I don't fully understand, but in any event, I did get it to work now. Just to clarify, this is how I had it set up:

Code: Select all

action [SetVariable("task", skill),
SelectedIf(setVariable("selectedInTaskMenu", True)),
Return("taskMenuClick")]

Code: Select all

action [SetVariable("task", skill),
If(selectedInTaskMenu, true=SetVariable("dummyTrueValue", True), false=NullAction()),
Return("taskMenuClick")]
But when I commented out the middle action (the SelectedIf or If lines), there was no change; it worked either way, if I set the task and locChosen variables to None.

For those who come after me, here are the changes I made to the code that made it work:

Code: Select all

if UIReturn == "topMenuClick":
    # If you click back to the top level after having reached down, collapse the menu.
    $ task = None  # This was the important part.
    $ showLocMenu = False
    $ locChosen = None  # This was the important part.
    # Clicking top menu shows the next level, unless already shown and the same top choice clicked again.
    if not selectedTop == previousSelectedTop or not showTaskMenu:
        $ showTaskMenu = True
    else:
        $ showTaskMenu = False
if UIReturn == "taskMenuClick":
    # Clicking task menu shows the next level, unless already shown and the same task choice clicked again.
    $ locChosen = None  # This was the important part.
    if not task == previousTask or not showLocMenu:
        $ showLocMenu = True
    else:
        $ showLocMenu = False
The code outside these two if statements was left unchanged, I believe. I hope this helps future users of these forums! Once more, thank you, Ocelot and Alex, and Asceai as well.

Post Reply

Who is online

Users browsing this forum: No registered users