Tooltip following mousecursor

f95zoneuser463

Member
Game Developer
Aug 14, 2017
219
1,017
What I want:
  • Tooltip must follow the mousecursor all the time and should never move off-screen near the sides/top/bottom
  • Text should be inside a frame for readability (high contrast with dark background frame)
  • Style easy customizable (textsize, bold, textcolor, background-color)
  • I'd highly prefer a method without a custom displayable and screen-based instead
  • I'd prefer a method that uses GetTooltip()
  • Avoid dependency on the legacy "Tooltip"-class if a custom displayable is needed (which I guess is the case since screen code does not have an event for "while hovering" to update the mouse x/y)
What I have:
That's two different methods in one file. One must be deleted or uncommented.
You don't have permission to view the spoiler content. Log in or register now.
  1. A modified custom displayable based on
(I have no idea what the author of the original source did with the padding code, it caused exceptions for me and I removed it)
pro:
  • works the way I want
contra:
  • hard to customize (I took me forever to find out how to add a background-rectangle)
  • hard to use and high chance to f*ck things
  • based on legacy Tooltip-class
2. Screen based tooltips:
pro:
  • easy to customize
  • easy to use
contra:
  • does not work how I want, I'm not aware of a way to update the tooltip position while hovering, the tooltip only appears at the position where the mouse entered the tooltip-object
So the question is either for method 1:
How to get rid of the Tooltip-class dependency and how do I customize this in an easy way?

Or for method 2:
How can I update the tooltip position while hovering?
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Respected User
Donor
Jun 10, 2017
10,180
14,904
Wow...

So the question is either for method 1:
How to get rid of the Tooltip-class dependency and how do I customize this in an easy way?
Well, you can't. Here the dependency come from the fact that he made his class inherit from the old tooltip one, but it's just to ease his works. He could have build it from scratch with the same result. But the new tooltip works in a totally different way... different and incompatible.
There's no real easy way to do it like this. It's possible, but like you need to hardcode the whole display, it's not easy.

Or for method 2:
How can I update the tooltip position while hovering?
Probably like this :
[Note: I can't test right now, so it's a guess reusing part of my code]
Code:
init python
    class MousePos(renpy.Displayable):
        x = 0
        y = 0

        def __init__(self, **kwargs):
            super(MousePos, self).__init__(**kwargs)

        def event(self, ev, x, y, st):
            if (x < 0 or y < 0): return None
            import pygame_sdl2 as pygame
            if ev.type != pygame.MOUSEMOTION: return None
            self.x = x
            self.y = y
            renpy.redraw(self, 0)

define mousePos = MousePos()

screen myScreen:

    add mousePos

    textbutton "blabla":
        action NullAction()
        tooltip "blabla"

    $ tooltip = GetTooltip()
   
    if tooltip:
       frame:
         xpos mousePos.x
         ypos mousePos.y
         text "[tooltip]"
MousePos just give you the position of the mouse. So you have to play with mousePos.x and mousePos.y to ensure that it don't goes over the screen. Something like :
Code:
    if tooltip:
       frame:
           if mousePos.x > config.screen_width - 200:
              xpos = config.screen_width - 200
           else:
              xpos mousePos.x
           if mousePos.y > config.screen_height - 100:
              ypos = config.screen_height - 100
           else:
              ypos mousePos.y
/!\ WARNING /!\ like I said, I can't test right now. So I guess that this should works, but it's not 100% guaranty.

Like the custom displayable just take care of the position of the mouse, styling the tooltip is easy, it's just basic Ren'py screen statements. The problem is that you need to have a fixed maximal width and height that will apply for every tooltip in the screen.
There's probably a way to merge the class you tried and MousePos, making it return not only the position of the mouse, but also the width and height of the text, but it'll need way more works/test that what I can do actually, sorry.
 
  • Like
Reactions: f95zoneuser463

f95zoneuser463

Member
Game Developer
Aug 14, 2017
219
1,017
Found this with similar code:


I've tried a mix of displayable and screen. This is how far I got:
You don't have permission to view the spoiler content. Log in or register now.
I can't figure out why the Text doesn't move to the x/y position. Maybe something with screen updates ...

4th attempt:
A displayable renders the tooltip, it does not use the legacy Tooltip-class, has no action and GetTooltip() instead. I just would like to have the background rectangle somewhat bigger and can't find a way to do this in the displayable. I've tried a bunch of style-variable on the 'Fixed' and 'Text' ... most things don't have any effect.
You don't have permission to view the spoiler content. Log in or register now.
 
  • Like
Reactions: Lewd Tracker

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Respected User
Donor
Jun 10, 2017
10,180
14,904
I just would like to have the background rectangle somewhat bigger [...]
Code:
            fixed = Fixed(Color("#7777"), size=(200,100))
If this don't works, perhaps that :
Code:
            fixed = Fixed( Color("#7777"), background=Frame( Solid( "#7777" ), size=(200,100) ))
will do it. But well, it's really messed since you add a background over the initial one.
 
  • Like
Reactions: f95zoneuser463

f95zoneuser463

Member
Game Developer
Aug 14, 2017
219
1,017
Finally got it working the way I want ... your post helped, I was so focused on trying to change styles on the 'Fixed'- and 'Text'-objects AFTER I created them ... did not even realize I can do it WHILE creating them using the parameters ... yeah I know ... facepalm ... I need sleep *insert homer simpson doh here* :rolleyes:

Code:
init python:
    class MouseTooltip(renpy.Displayable):

        def __init__(self, **kwargs):
            super(renpy.Displayable, self).__init__(**kwargs)
            self.text = Text("")
            self.textsize = 36
            self.textcolor = Color("#FFF")
            self.padding = 10
            self.bold = True
            self.backgroundcolor = Color("#7777") # use something like #0007 for real VN
            self.x = 0
            self.y = 0

        def render(self, width, height, st, at):
            # avoid to render an empty rectangle when the text got cleared
            if self.text.text != [""]:
                w, h = self.text.size()
                render = renpy.Render(w, h)

                # if tooltip is close to the top or right side make sure it stays on the screen
                x = self.x + self.padding
                if x > config.screen_width - w - self.padding:
                    x = config.screen_width - w - self.padding
                
                y = self.y-h-self.padding*2 # -h to place text above cursor
                if y < 0:
                    y = 0


                # fixed = transparent background rectangle
                fixed = Fixed(self.backgroundcolor, xpos=-self.padding, xsize=int(w)+self.padding*2, ysize=int(h)+self.padding*2)
                fixed.add(self.text)
                render.place(fixed, x, y)
                return render
            return renpy.Render(1, 1)

        def event(self, ev, x, y, st):
            import pygame
            # ignore all events except MOUSEMOTION
            # not sure whether events like MOUSEBUTTONDOWN update the position too and should therefore be added here?!
            if ev.type != pygame.MOUSEMOTION:
                return None
            self.x = x
            self.y = y
            tooltip = GetTooltip()
            if tooltip:
                self.text = Text(tooltip, size=self.textsize, color=self.textcolor, xpos=self.padding, ypos=self.padding, bold=self.bold)
                renpy.redraw(self, 0)
            elif self.text.text != [""]: # avoid unnecessary redraw calls
                self.text = Text("") # if there is no tooltip clear the text just once
                renpy.redraw(self, 0)

define mousetooltip = MouseTooltip()
Edit:
There is a minor bug with this mousetooltip: It's not being cleared if the player doesn't move the mouse.
 

Kakimito

Newbie
Jan 25, 2022
85
49
Finally got it working the way I want ... your post helped, I was so focused on trying to change styles on the 'Fixed'- and 'Text'-objects AFTER I created them ... did not even realize I can do it WHILE creating them using the parameters ... yeah I know ... facepalm ... I need sleep *insert homer simpson doh here* :rolleyes:

Code:
init python:
    class MouseTooltip(renpy.Displayable):

        def __init__(self, **kwargs):
            super(renpy.Displayable, self).__init__(**kwargs)
            self.text = Text("")
            self.textsize = 36
            self.textcolor = Color("#FFF")
            self.padding = 10
            self.bold = True
            self.backgroundcolor = Color("#7777") # use something like #0007 for real VN
            self.x = 0
            self.y = 0

        def render(self, width, height, st, at):
            # avoid to render an empty rectangle when the text got cleared
            if self.text.text != [""]:
                w, h = self.text.size()
                render = renpy.Render(w, h)

                # if tooltip is close to the top or right side make sure it stays on the screen
                x = self.x + self.padding
                if x > config.screen_width - w - self.padding:
                    x = config.screen_width - w - self.padding
                
                y = self.y-h-self.padding*2 # -h to place text above cursor
                if y < 0:
                    y = 0


                # fixed = transparent background rectangle
                fixed = Fixed(self.backgroundcolor, xpos=-self.padding, xsize=int(w)+self.padding*2, ysize=int(h)+self.padding*2)
                fixed.add(self.text)
                render.place(fixed, x, y)
                return render
            return renpy.Render(1, 1)

        def event(self, ev, x, y, st):
            import pygame
            # ignore all events except MOUSEMOTION
            # not sure whether events like MOUSEBUTTONDOWN update the position too and should therefore be added here?!
            if ev.type != pygame.MOUSEMOTION:
                return None
            self.x = x
            self.y = y
            tooltip = GetTooltip()
            if tooltip:
                self.text = Text(tooltip, size=self.textsize, color=self.textcolor, xpos=self.padding, ypos=self.padding, bold=self.bold)
                renpy.redraw(self, 0)
            elif self.text.text != [""]: # avoid unnecessary redraw calls
                self.text = Text("") # if there is no tooltip clear the text just once
                renpy.redraw(self, 0)

define mousetooltip = MouseTooltip()
Edit:
There is a minor bug with this mousetooltip: It's not being cleared if the player doesn't move the mouse.
Im "kinda" late but not cleared tooltip on mouse click can be fixed by changing event function to this
Code:
def event(self, ev, x, y, st):
            import pygame
            if ev.type == pygame.MOUSEBUTTONUP:
                self.text = Text("")
                renpy.redraw(self, 0)
            if ev.type != pygame.MOUSEMOTION:
                return None
            self.x = x
            self.y = y
            tooltip = GetTooltip()
            if tooltip:
                self.text = Text(tooltip, size=self.textsize, color=self.textcolor, xpos=self.padding, ypos=self.padding, bold=self.bold)
                renpy.redraw(self, 0)
            elif self.text.text != [""]:
                self.text = Text("")
                renpy.redraw(self, 0)
it simply detects any mouse click. if you want it to detect certain button clicks you can replace third line with something like this
Code:
if (ev.type == pygame.MOUSEBUTTONUP) and (ev.button == 7):
where 7 is any mouse button (LMB is 1 and RMB is 3 for example)
there may be some other bugs tho
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Respected User
Donor
Jun 10, 2017
10,180
14,904
Im "kinda" late
Just four years, a trifle.


but not cleared tooltip on mouse click can be fixed by changing event function to this
Except that at this time Ren'Py still only had what is now the legacy version of the tooltip ; therefore a class generally declared in the screen, and then only available from it.
 

Kakimito

Newbie
Jan 25, 2022
85
49
Just four years, a trifle.




Except that at this time Ren'Py still only had what is now the legacy version of the tooltip ; therefore a class generally declared in the screen, and then only available from it.
Uh... What do you mean? I'm talking about the second part of your message
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Respected User
Donor
Jun 10, 2017
10,180
14,904
Uh... What do you mean? I'm talking about the second part of your message
When this discussion happened, GetTooltip don't existed yet. At this time, to have tooltips, you needed to use what is now the "legacy method". Therefore you had to create a Tooltip object, and set it's content through the hovered button property.
And usually this object what created directly in the screen, with default tt = Tooltip(""), what mean that it wasn't available outside of the screen scope ; scope unavailable from a custom displayable.
 

Kakimito

Newbie
Jan 25, 2022
85
49
When this discussion happened, GetTooltip don't existed yet. At this time, to have tooltips, you needed to use what is now the "legacy method". Therefore you had to create a Tooltip object, and set it's content through the hovered button property.
And usually this object what created directly in the screen, with default tt = Tooltip(""), what mean that it wasn't available outside of the screen scope ; scope unavailable from a custom displayable.
do you remember which version of renpy was latest one back then?