How to create a search box function with imagebuttons?

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
Galex
Newbie
Posts: 3
Joined: Mon Jan 23, 2023 8:39 pm
Contact:

How to create a search box function with imagebuttons?

#1 Post by Galex »

In my game, I am planning to have a search bar to go to certain character stories filtering out all of the imagebuttons. But upon attempting to create it I learned that I cant use python to create a search function due to some functions not being available. I would appreciate some assistance to guide me on the right path of creating a search box/bar function with the renpy limitations!
Attachments
Screenshot_20230125_103007.png

User avatar
m_from_space
Miko-Class Veteran
Posts: 975
Joined: Sun Feb 21, 2021 3:36 am
Contact:

Re: How to create a search box function with imagebuttons?

#2 Post by m_from_space »

What do you mean by "filtering out all the imagebuttons"? How do imagebuttons relate to character stories?

Wouldn't it be enough to provide the player with a list of possible chapters/stories they can choose from?

Galex
Newbie
Posts: 3
Joined: Mon Jan 23, 2023 8:39 pm
Contact:

Re: How to create a search box function with imagebuttons?

#3 Post by Galex »

By filtering out I mean by typing out the character name in the searchbox (as a searchbox is a filtering system). Imagebuttons don't necessarily relate but its just for the visual appeal and to easily identify the character. I plan on having alot of separate character stories in the long run so a search functionality would make things alot easier for the player rather then scrolling and missing the character

User avatar
Kaji
Regular
Posts: 87
Joined: Thu Nov 17, 2022 10:17 pm
Github: Kaji01
Discord: Kaji#7767
Contact:

Re: How to create a search box function with imagebuttons?

#4 Post by Kaji »

Having set up a similar type of search system for an educational app I'm working on in RenPy, here are a couple insights that I've found helpful.

1. The screen refreshes with every event. So when the user clicks on something or types something into the box a refresh occurs.

2. Conditional logic can still be put into screen code. In the case of IF statements, raw Python code does work in most cases, assuming the functions were properly defined in advance in a python: or init python: block.

Based on this, the process I use is to create a library that acts as a registry for things I intend to be searchable. Something like the following:

Code: Select all

init python:
    import math

    # Define our story list here
    stories = [
        {
            "title": _("Test Script{#title_for_story_list}"),
            "prefix": "test_script",
            "summary": _("Temporary Script for Feature Testing{#description_for_story_list}"),
            "image": "_test_script.png"
        },
        {
            "title": _("This Dumb Programmer{#title_for_story_list}"),
            "prefix": "dumb_programmer",
            "summary": _("Pardon our dust...{#description_for_story_list}"),
            "image": "_dumb_programmer.png"
        },
        {
            "title": _("Effects Test{#title_for_story_list}"),
            "prefix": "effects_test",
            "summary": _("Temporary Script for Effects Testing{#description_for_story_list}"),
            "image": "_effects_test.png"
        },
    }
    
    story_grid_rows = math.ceil(len(stories) / gui.file_slot_cols)
    story_grid_cells = math.ceil(story_grid_rows * gui.file_slot_cols)
The grid cell rows/columns calculations at the bottom are useful if you're using a grid view, since it (...rather inconveniently) wants to know up front how many cells to expect (you'll also have to add dummy cells to the end to pad the final row to the proper length).

Back to the meat of things though, in your case you may want to add a "character": "Jason{#character_name_for_search}" type of field to each registry entry. From there when building the cells showing things you can put the code building it behind an if statement. Something like the following:

Code: Select all

    $ current_grid_cells = story_grid_cells ## Creating a copy so we can adjust the number of anticipated cells during a search

    for i in range(current_grid_cells):
        if (i < len(stories)):
            if (stories[i]["character"] == search_term):
                button:
                    action Start(stories[i]["prefix"] + "_start") ## Start telling story from Label in string

                    has vbox

                    add im.Scale("images/title_cards/" + stories[i]["image"], config.thumbnail_width, config.thumbnail_height) xalign 0.5

                    text stories[i]["title"]:
                        style "slot_time_text"

                    text stories[i]["summary"]:
                        style "slot_name_text"
            else: ## Story not about the character you're searching for, skip it
                $ current_grid_cells = current_grid_cells + 1 ## Add a dummy button to the end so that the count lines up
        else: ## Create an empty button
            button:
                has vbox

                add im.Scale("images/title_cards/_title_card_placeholder.jpg", config.thumbnail_width, config.thumbnail_height) xalign 0.5

                text "":
                    style "slot_time_text"

                text "":
                    style "slot_name_text"
If you want to be even more slick and not have a ton of empty cells at the end you could do a quick check before the FOR loop where you build the grid and count the number of stories that meet the criteria in advance; then you wouldn't need to worry about the first ELSE branch at all.

One final thought: Using buttons (image or text) to have people choose which character to search for will save you a world of headaches. Users mistype things and capitalize things differently, and if you get into translating it then they may search for different versions of the character's name. It'd be better to have a button they can click on to set the search_term used in the filter, which you can then programmatically ensure matches with the possible values in the registry. With some light tweaking on this you can even allow people to do things like tap on multiple buttons to find stories featuring all of the chosen characters (e.g. by storing the selections in a set and then ensuring that the story registry entry contains all of the characters in the set).

Using some of the tricks here you can also use screen variables to show and hide pieces of the screen when building things so that, for example, you can have a "Show Filters" button that displays the buttons, along with another to hide them.

Anyway...I know that's a whole lot to drop, so I'll leave it there for now. Hope it helps!

User avatar
_ticlock_
Miko-Class Veteran
Posts: 910
Joined: Mon Oct 26, 2020 5:41 pm
Contact:

Re: How to create a search box function with imagebuttons?

#5 Post by _ticlock_ »

Galex wrote: Thu Jan 26, 2023 9:50 am
In addition to Kaji's post:

Here is a simplified example:

Code: Select all

screen filtered_search:
    default search = ""
    default input_search = ScreenVariableInputValue("search")

    default character_list = [_("Alice"), _("The White Rabbit")]
    default image_list = ["alice.png", "rabbit.png"]

    input:
        value input_search
        xalign 0.5 yalign 0.0

    vbox:
        xalign 0.5 ypos 0.1
        for i, name in enumerate(character_list):
            if search.lower() in name.lower():
                hbox:
                    imagebutton:
                        idle Transform(image_list[i], matrixcolor=SaturationMatrix(0))
                        hover image_list[i]
                        action Return(name)
                    label name yalign 0.5
You can add another list that stores label names. And use

Code: Select all

                        action Jump(label_list[i])
                        #or
                        # action Replay(label_list[i])
to jump or replay the specified label.
PS:
1) line if search.lower() in name.lower() is for case insensitive search. If you are using RePy 8, it is better to use casefold():

Code: Select all

            if search.casefold() in name.casefold():
2) If you want the search filter to work with translated strings, you can use renpy.translate_string and translating substitutions:

Code: Select all

            if search.casefold() in renpy.translate_string(name).casefold():

Code: Select all

label "[name!t]" yalign 0.5

Galex
Newbie
Posts: 3
Joined: Mon Jan 23, 2023 8:39 pm
Contact:

Re: How to create a search box function with imagebuttons?

#6 Post by Galex »

Thank you very much for the help!

User avatar
Andredron
Miko-Class Veteran
Posts: 719
Joined: Thu Dec 28, 2017 2:37 pm
Location: Russia
Contact:

Re: How to create a search box function with imagebuttons?

#7 Post by Andredron »

_ticlock_ wrote: Thu Jan 26, 2023 3:23 pm
Galex wrote: Thu Jan 26, 2023 9:50 am
In addition to Kaji's post:

Here is a simplified example:

Code: Select all

screen filtered_search:
    default search = ""
    default input_search = ScreenVariableInputValue("search")

    default character_list = [_("Alice"), _("The White Rabbit")]
    default image_list = ["alice.png", "rabbit.png"]

    input:
        value input_search
        xalign 0.5 yalign 0.0

    vbox:
        xalign 0.5 ypos 0.1
        for i, name in enumerate(character_list):
            if search.lower() in name.lower():
                hbox:
                    imagebutton:
                        idle Transform(image_list[i], matrixcolor=SaturationMatrix(0))
                        hover image_list[i]
                        action Return(name)
                    label name yalign 0.5
You can add another list that stores label names. And use

Code: Select all

                        action Jump(label_list[i])
                        #or
                        # action Replay(label_list[i])
to jump or replay the specified label.
PS:
1) line if search.lower() in name.lower() is for case insensitive search. If you are using RePy 8, it is better to use casefold():

Code: Select all

            if search.casefold() in name.casefold():
2) If you want the search filter to work with translated strings, you can use renpy.translate_string and translating substitutions:

Code: Select all

            if search.casefold() in renpy.translate_string(name).casefold():

Code: Select all

label "[name!t]" yalign 0.5
Good afternoon, is it possible to upgrade this code, so that when you find a partial name, it would give a result?

User avatar
plastiekk
Regular
Posts: 112
Joined: Wed Sep 29, 2021 4:08 am
Contact:

Re: How to create a search box function with imagebuttons?

#8 Post by plastiekk »

Andredron wrote: Fri Apr 07, 2023 1:27 am Good afternoon, is it possible to upgrade this code, so that when you find a partial name, it would give a result?
Hi,
_ticlock_ simplified example does literally what you want to modify. No regular expression needed.
Why on earth did I put the bread in the fridge?

User avatar
Andredron
Miko-Class Veteran
Posts: 719
Joined: Thu Dec 28, 2017 2:37 pm
Location: Russia
Contact:

Re: How to create a search box function with imagebuttons?

#9 Post by Andredron »

plastiekk wrote: Fri Apr 07, 2023 3:45 am
Andredron wrote: Fri Apr 07, 2023 1:27 am Good afternoon, is it possible to upgrade this code, so that when you find a partial name, it would give a result?
Hi,
_ticlock_ simplified example does literally what you want to modify. No regular expression needed.
If so, that's great, because I thought case was when you spell it with a small or capital letter. I thought it would not work, for example, the keyword Apple, and the user entered app

User avatar
plastiekk
Regular
Posts: 112
Joined: Wed Sep 29, 2021 4:08 am
Contact:

Re: How to create a search box function with imagebuttons?

#10 Post by plastiekk »

Andredron wrote: Fri Apr 07, 2023 6:45 am ... for example, the keyword Apple, and the user entered app

Code: Select all

if search.lower() in name.lower():
This line uses search as regex and should work as intended. In your example you could just type "ple" to get your Apple listed.

edit: And no it doesn't matter if upper or lower case, because it is always compared with lower case.
Why on earth did I put the bread in the fridge?

jeffster
Veteran
Posts: 409
Joined: Wed Feb 03, 2021 9:55 pm
Contact:

Re: How to create a search box function with imagebuttons?

#11 Post by jeffster »

You also can search "on the fly", filtering searched strings while user types, starting maybe from 3 symbols.
There's "changed" property of "input" that allows doing so:
https://www.renpy.org/dev-doc/html/screens.html#input

Here is a test script (function "search_fly" would be invoked every time the input value changes):

Code: Select all

# We are going to search star_systems' entries by their "name"

default star_systems = {
        'tatooine': {
            'name': 'Tatooine',
            'x': 300,
            'y': 400,
            'description': 'A desert planet on the Outer Rim Territories.',
            'image': 'images/tatooine.png',
        },
        'dantooine': {
            'name': 'Dantooine',
            'x': 350,
            'y': 420,
            'description': 'Some planet on the Outer Rim Territories.',
            'image': 'images/dantooine.png',
        },
        'alderaan': {
            'name': 'Alderaan',
            'x': 700,
            'y': 200,
            'description': 'A peaceful planet in the Core Worlds, destroyed by the Death Star.',
            'image': 'images/alderaan.png',
        },
    }

default filtered_names = []

init python:
    def search_fly(searching):
        if len(searching) > 2:
            store.filtered_names = [
                star_systems[x]['name'] for x in star_systems \
                if searching.casefold() in \
                renpy.translate_string(star_systems[x]['name']).casefold()]
            renpy.restart_interaction()

screen searched():
    input:
        default ""
        align (0.5, 0.0)
        changed search_fly

    vbox:
        align (1.0, 0.0)
        for n in filtered_names:
            textbutton [n!t] action Return(n)

label start:
    call screen searched
    "[_return]"
    $ del filtered_names[:]
    jump start
(Try to search Tatooine, Dantooine and Alderaan with any string of 3 or more symbols).

User avatar
Andredron
Miko-Class Veteran
Posts: 719
Joined: Thu Dec 28, 2017 2:37 pm
Location: Russia
Contact:

Re: How to create a search box function with imagebuttons?

#12 Post by Andredron »

jeffster wrote: Tue Feb 27, 2024 7:27 am You also can search "on the fly", filtering searched strings while user types, starting maybe from 3 symbols.
There's "changed" property of "input" that allows doing so:
https://www.renpy.org/dev-doc/html/screens.html#input

Here is a test script (function "search_fly" would be invoked every time the input value changes):

Code: Select all

# We are going to search star_systems' entries by their "name"

default star_systems = {
        'tatooine': {
            'name': 'Tatooine',
            'x': 300,
            'y': 400,
            'description': 'A desert planet on the Outer Rim Territories.',
            'image': 'images/tatooine.png',
        },
        'dantooine': {
            'name': 'Dantooine',
            'x': 350,
            'y': 420,
            'description': 'Some planet on the Outer Rim Territories.',
            'image': 'images/dantooine.png',
        },
        'alderaan': {
            'name': 'Alderaan',
            'x': 700,
            'y': 200,
            'description': 'A peaceful planet in the Core Worlds, destroyed by the Death Star.',
            'image': 'images/alderaan.png',
        },
    }

default filtered_names = []

init python:
    def search_fly(searching):
        if len(searching) > 2:
            store.filtered_names = [
                star_systems[x]['name'] for x in star_systems \
                if searching.casefold() in \
                renpy.translate_string(star_systems[x]['name']).casefold()]
            renpy.restart_interaction()

screen searched():
    input:
        default ""
        align (0.5, 0.0)
        changed search_fly

    vbox:
        align (1.0, 0.0)
        for n in filtered_names:
            textbutton [n!t] action Return(n)

label start:
    call screen searched
    "[_return]"
    $ del filtered_names[:]
    jump start
(Try to search Tatooine, Dantooine and Alderaan with any string of 3 or more symbols).
Thanks, your code gave me the nudge I needed to solve the problem! When I get home I'll check if the code is correct. Thank you

Post Reply

Who is online

Users browsing this forum: Google [Bot], Semrush [Bot]