[Solved!] Tooltip for dialogue text needed

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
kalesco
Regular
Posts: 25
Joined: Wed Apr 18, 2018 9:34 am
Contact:

[Solved!] Tooltip for dialogue text needed

#1 Post by kalesco »

Hi,

I would like to add definitions/"glossary terms" to my dialogue with tooltips showing the definition text on a hover. Is this possible with Ren'Py?

Image

Thank you!
Last edited by kalesco on Fri May 25, 2018 8:37 am, edited 3 times in total.

User avatar
Remix
Eileen-Class Veteran
Posts: 1628
Joined: Tue May 30, 2017 6:10 am
Completed: None... yet (as I'm still looking for an artist)
Projects: An un-named anime based trainer game
Contact:

Re: Tooltip for dialogue text needed

#2 Post by Remix »

Code: Select all

define e = Character("Eileen")

init python:

    lexicon = {
        ('Visual Novel', 'VN')  : """
            A visual novel (bijuaru noberu) is an interactive game genre, 
            which originated in Japan in the early 1990s, featuring mostly 
            static graphics, most often using anime-style art or occasionally 
            live-action stills.
            As the name might suggest, they resemble mixed-media novels.""",
        ('Ren\'Py', 'Renpy') : """
            The Ren'Py Visual Novel Engine is a free software engine which 
            facilitates the creation of visual novels, a form of 
            computer-mediated storytelling. Ren'Py is a portmanteau of ren'ai, 
            the Japanese word for 'romantic love' and Python, 
            the programming language that Ren'Py runs on. """
    }

    def hyperlink_lexicon( str_to_test ):

        for keys in lexicon:
        
            if isinstance(keys, basestring):
                keys = [keys]
        
            for phrase in keys:

                # preceded by a space
                str_to_test = str_to_test.replace(
                    " {0}".format(phrase),
                    " {{a=lexicon:{phrase}}}{phrase}{{/a}}".format(
                        phrase = phrase ) )
                
                # followed by a space
                str_to_test = str_to_test.replace(
                    "{0} ".format(phrase),
                    "{{a=lexicon:{phrase}}}{phrase}{{/a}} ".format(
                        phrase = phrase ) )
        
        return str_to_test

    config.say_menu_text_filter = hyperlink_lexicon


    def hyperlink_styler(*args):

        return style.hyperlink_text

    def hyperlink_hovered(*args):
        
        if not args[0]:
            # Ren'Py 7+ recent nightly only, see below
            renpy.hide_screen("lexicon_popup")
        
        elif args[0][:8] == "lexicon:":

            renpy.show_screen( "lexicon_popup", 
                               args[0][8:], 
                               renpy.get_mouse_pos() )
            
            renpy.restart_interaction()
        
        return

    def hyperlink_clicked(*args):

        if args[0] and args[0][:8] != 'lexicon:':

            # adapted from common/00defaults.rpy
            if args[0].startswith("http:") or args[0].startswith("https:"):
                try:
                    import webbrowser
                    webbrowser.open(args[0])
                except:
                    renpy.notify("Failed to open browser")

            elif args[0].startswith("jump:"):
                renpy.jump( args[0][5:] )

            else:
                renpy.call_in_new_context(args[0][args[0].index(':')+1:])

    
    style.default.hyperlink_functions = ( hyperlink_styler, 
                                          hyperlink_clicked, 
                                          hyperlink_hovered )


screen lexicon_popup(phrase=None, pos=(100,100)):

    if phrase:

        python:
            # get description
            d = [ lexicon[k] for k in lexicon if phrase in k ]
            description = d[0] if len(d) else "No description found."
            description = " ".join( [ k for k in description.split()
                                      if k not in [" ", "\t"] ] )
            
            # move the ypos up by a bit
            pos = ( pos[0], pos[1] - 25 )

            # reformat phrase
            p = [ k for k in lexicon if phrase in k ]
            primary_phrase = p[0][0] if len(p) else phrase
            if primary_phrase != phrase:
                phrase = "{0} ({1})".format(phrase, primary_phrase)

        frame:
            anchor (0.5, 1.0)
            pos pos
            xsize 340
            background Solid("#A9B")
            vbox:
                text "[phrase]" size 18
                text "[description]" size 14

    # Hacky workaround as hyperlink_hovered does not seem to nicely hide this
    # --- Fixed in Ren'Py 7.0 nightlies of May 23rd onwards apparently

    timer 0.5 action Hide("lexicon_popup")

label start:

    e "Start"
    e "Oh look, this VN is made with Ren'Py"
    e "Test phrases that should not make link... AVNX aRenpyx"
    e "Test phrases that should not make link... miniVN{#no-lex} {#no-lex}Renpy-esque"
    e "Mix and match VN with {a=call:label_2}call{/a} {a=jump:label_2}jump{/a} {a=https:www.renpy.org}browser{/a}"
    e "End"

label label_2:
    e "Hiding in different label"
    return
Edit: Fixed the restart_interaction so as not to affect other non lexicon anchors and added a=call: a=jump: and a=http(s): clicks
Last edited by Remix on Fri May 25, 2018 7:59 am, edited 1 time in total.
Frameworks & Scriptlets:

kalesco
Regular
Posts: 25
Joined: Wed Apr 18, 2018 9:34 am
Contact:

Re: Tooltip for dialogue text needed

#3 Post by kalesco »

Lexicon! Thank you that's perfect!
I was looking for glossary and text tooltip.

User avatar
Remix
Eileen-Class Veteran
Posts: 1628
Joined: Tue May 30, 2017 6:10 am
Completed: None... yet (as I'm still looking for an artist)
Projects: An un-named anime based trainer game
Contact:

Re: [Solved] Tooltip for dialogue text needed

#4 Post by Remix »

Glad it suits your purposes :)

Just for info:

Note keys are case sensitive ... ( 'Visual Novel', 'VN' ) would not match 'visual novel' or 'vn'

It tests the dialogue string for keys from the dictionary either with a space before them or after.
If you did get a false positive ( e.g. the word miniVN would match key VN ) you could just add a {#tag} to force ignore:

Code: Select all

    e "Test phrases that should not make link... miniVN{#no-lex} {#no-lex}Renpy-esque"
The popup uses the found key plus the first key in parenthesis (to expand abbreviations)
More than 2 words in a tuple/set key is also fine and maybe needed to cover cases ( 'Visual Novel', 'VN', 'vn', 'visual novel' )
Frameworks & Scriptlets:

kalesco
Regular
Posts: 25
Joined: Wed Apr 18, 2018 9:34 am
Contact:

Re: [Solved] Tooltip for dialogue text needed

#5 Post by kalesco »

The tooltips work now but I do have a couple of questions:
a) a 2nd string for the lexicon entry is needed, otherwise weird things happen. E.g.

Code: Select all

lexicon = {
        ('Schmutzschleuse'') : """
            Als Schmutzschleuse bezeichnen wir in einem Gebäude den Übergang zwischen dem Innen- und dem Außenbereich, oder zwischen Bereichen, die unterschiedlichen Anforderungen in Bezug auf Staub und Schmutz, an Reinigung und Hygiene gerecht werden müssen."""
    }
    
makes random characters into hyperlinks for the tooltip.
It worked after I added the 2nd (same) string.

Code: Select all

lexicon = {
        ('Schmutzschleuse', 'Schmutzschleuse') : """
            Als Schmutzschleuse bezeichnen wir in einem Gebäude den Übergang zwischen dem Innen- und dem Außenbereich, oder zwischen Bereichen, die unterschiedlichen Anforderungen in Bezug auf Staub und Schmutz, an Reinigung und Hygiene gerecht werden müssen."""
    }
    
b) It breaks the standard hyperlink functionality. :-(
I get a styled hyperlink for a text with {a=call:monitoring} for example, but the link doesn't work - it's not reacting to hover, and not clickable.
same for a=http oder a=file

Do you have any suggestions how to use tooltips like this and retain hyperlink functionality?
Thanks!

User avatar
Remix
Eileen-Class Veteran
Posts: 1628
Joined: Tue May 30, 2017 6:10 am
Completed: None... yet (as I'm still looking for an artist)
Projects: An un-named anime based trainer game
Contact:

Re: [Solved] Tooltip for dialogue text needed

#6 Post by Remix »

kalesco wrote: Fri May 25, 2018 6:52 am The tooltips work now but I do have a couple of questions:
a) a 2nd string for the lexicon entry is needed, otherwise weird things happen...makes random characters into hyperlinks for the tooltip.

Oops, yeah, slight tweak needed:

Code: Select all


    def hyperlink_lexicon( str_to_test ):
        for keys in lexicon:
            # add this bit
            if isinstance(keys, basestring):
                keys = [keys]
            for phrase in keys:
If only one item it interprets the string as a list so hyperlinks every letter, d'oh
b) It breaks the standard hyperlink functionality. :-(
I get a styled hyperlink for a text with {a=call:monitoring} for example, but the link doesn't work - it's not reacting to hover, and not clickable.
same for a=http oder a=file

Do you have any suggestions how to use tooltips like this and retain hyperlink functionality? Thanks!
Because we are using " style.default.hyperlink_functions = (...) " I *had* thought I could just define style.lexicon and tap the functions to that. After some failures I opted to just use default and extend the hyperlink_clicked function...

Edited initial reply with applied fixes...
Frameworks & Scriptlets:

kalesco
Regular
Posts: 25
Joined: Wed Apr 18, 2018 9:34 am
Contact:

Re: [reopened] Tooltip for dialogue text needed

#7 Post by kalesco »

That's great, thanks a lot! :!:

Minor addition:
if one wants to link to files or use call/show the lines need to be adapted/added.

Code: Select all

# adapted from common/00defaults.rpy
            if args[0].startswith("http:") or args[0].startswith("https:") or args[0].startswith("file:"):
                try:
                    import webbrowser
                    webbrowser.open(args[0])
                except:
                    renpy.notify("Failed to open browser")

            elif args[0].startswith("jump:"):
                renpy.jump( args[0][5:] )

            else:
                renpy.call_in_new_context(args[0][args[0].index(':')+1:])

Post Reply

Who is online

Users browsing this forum: Bing [Bot], Tomoyo Ichijouji