Eto.Forms.Control.ToolTip - How To Style To Match Rhino

Hello,

How can I style my Eto Tooltips to the Rhino theme?

Here’s what my tooltip looks like when adding it to a control via the .ToolTip method like so:

my_eto_control.ToolTip = "helpful user information here"

image

And here’s what the Rhino tooltips look like in Dark Mode:

image

Thank you for your help!

Are you using the this.UseRhinoStyle() extension for Eto Controls?

Hi @CallumSykes, I’m not because all my controls are custom drawables but sounds like maybe if I set that on my drawables anyways then the tooltip may inherit the method to style it accordingly?

I think it should, try it out and let me know :slight_smile:

Will do, thanks for the suggestion!

Alright I gave it a try and saw that the form background changed to the default “dark theme gray” but it had no effect on the tooltips themselves unfortunately.

Any other ideas? Thanks!

import Eto.Forms as forms
import Eto.Drawing as drawing
import Rhino

class CustomToolTip(forms.FloatingForm):
    def __init__(self, text):
        super().__init__()
        self.WindowStyle = forms.WindowStyle.NONE
        Rhino.UI.EtoExtensions.UseRhinoStyle(self)
        self.Resizable=False
        label = forms.Label()
        label.Text = text
        self.Padding = drawing.Padding(10)
        layout = forms.DynamicLayout()
        layout.AddRow(label)
        self.Content = layout

class FloatingForm(forms.FloatingForm):
    def __init__(self):
        super().__init__()
        self.Title = "Floating Form with Custom Tooltip"
        self.Size = drawing.Size(300, 100)
        self.Topmost = True
        Rhino.UI.EtoExtensions.UseRhinoStyle(self)
        # Create a button that will show the tooltip when hovered over
        button = forms.Button()
        button.Text="Hover over me"
        button.MouseEnter += self.show_tooltip
        button.MouseLeave += self.hide_tooltip

        # Layout setup
        layout = forms.DynamicLayout()
        layout.Padding = drawing.Padding(5)
        layout.AddRow(button)
        self.Content = layout

        # Tooltip attribute to keep track of the active tooltip
        self.tooltip = None

    def show_tooltip(self, sender, e):
        if self.tooltip is None or not self.tooltip.Visible:
            self.tooltip = CustomToolTip("This is a Rhino style tooltip")
            mouse_position = forms.Mouse.Position
            self.tooltip.Location = drawing.Point(mouse_position.X + 10, mouse_position.Y + 10) 
            self.tooltip.Show()

    def hide_tooltip(self, sender, e):
        if self.tooltip and self.tooltip.Visible:
            self.tooltip.Close()
            self.tooltip = None #clear the tooltip

form = FloatingForm()
form.Show()

2 Likes

This works great, thank you so much @Gijs !

EDIT:
Actually this is even better because now I can use drawable code within the CustomTooltip as well to add things like “icons” or “shortcut symbols”.

Cheers!

EDIT 2:
I noticed the tooltip spawns instantly OnMouseEnter so I added an Eto UI Timer to add a variable delay. Here’s the code:

import Eto.Forms
import Eto.Drawing
import Rhino


class CustomToolTip(Eto.Forms.FloatingForm):
    def __init__(self, text):
        super().__init__()
        self.WindowStyle = Eto.Forms.WindowStyle.NONE
        Rhino.UI.EtoExtensions.UseRhinoStyle(self)
        self.Resizable = False
        label = Eto.Forms.Label()
        label.Text = text
        self.Padding = Eto.Drawing.Padding(10)
        layout = Eto.Forms.DynamicLayout()
        layout.AddRow(label)
        self.Content = layout


class FloatingForm(Eto.Forms.FloatingForm):
    def __init__(self):
        super().__init__()
        self.Title = "Floating Form with Custom Tooltip"
        self.Size = Eto.Drawing.Size(300, 100)
        self.Topmost = True
        Rhino.UI.EtoExtensions.UseRhinoStyle(self)

        # Create a button that will show the tooltip when hovered over
        button = Eto.Forms.Button()
        button.Text = "Hover over me"
        button.MouseEnter += self.ToggleTooltip
        button.MouseLeave += self.ToggleTooltip

        # Layout setup
        layout = Eto.Forms.DynamicLayout()
        layout.Padding = Eto.Drawing.Padding(5)
        layout.AddRow(button)
        self.Content = layout

        # Tooltip and timer attributes
        self.tooltip = None
        self.enabled = False
        self.hover_timer = Eto.Forms.UITimer()
        self.hover_timer.Interval = 0.75  # add a delay
        self.hover_timer.Elapsed += self.ShowTooltip

    def ToggleTooltip(self, sender, e):
        self.enabled = not self.enabled
        if self.enabled:
            # Start the UITimer to show the tooltip after delay
            self.hover_timer.Start()
        else:
            # Stop the timer and hide the tooltip if the mouse leaves before the delay
            self.hover_timer.Stop()
            self.HideTooltip()

    def ShowTooltip(self, sender=None, e=None):
        # Stop the timer to avoid repeated execution
        self.hover_timer.Stop()

        # Check if tooltip should be shown and display it
        if self.tooltip is None or not self.tooltip.Visible:
            self.tooltip = CustomToolTip("This is a Rhino style tooltip")
            mouse_position = Eto.Forms.Mouse.Position
            self.tooltip.Location = Eto.Drawing.Point(mouse_position.X + 10, mouse_position.Y + 10)
            self.tooltip.Show()

    def HideTooltip(self):
        # Hide the tooltip if it's visible
        if self.tooltip and self.tooltip.Visible:
            self.tooltip.Close()
            self.tooltip = None  # clear the tooltip


form = FloatingForm()
form.Show()

EDIT 3:
Unfortunately it seems that Eto.Forms.FloatingForm does not support transparent window styling like so?

It works when using Eto.Forms.Form but not with Eto.Forms.Floating.Form.

@clement is this something you have come across before?

    class CustomTooltip(Eto.Forms.Form):
        def __init__(self, text, shortcut=None, toolbar=None):
            super().__init__()
            try:
                self.has_shortcut = True if shortcut is not None else False

                self.WindowStyle = Eto.Forms.WindowStyle.NONE

                # Rhino.UI.EtoExtensions.UseRhinoStyle(self)

                # Add transparent style
                self.Styles.Add[Eto.Forms.Panel]("transparent", UI.TransparentFormStyler)
                self.Style = "transparent"

                self.Resizable = False
                self.TopMost = True
                self.MovableByWindowBackground = False

TransparentStyler:

class UI():
    @staticmethod
    def TransparentFormStyler(control):  # Set the form background to transparent
        control.BackgroundColor = Eto.Drawing.Colors.Transparent
        window = control.ControlObject
        if hasattr(window, "AllowsTransparency"):
            window.AllowsTransparency = True
        if hasattr(window, "Background"):
            brush = window.Background.Clone()
            brush.Opacity = 0  # Adjust opacity as needed
            window.Background = brush
        else:
            color = window.BackgroundColor
            window.BackgroundColor = color.FromRgba(0, 0, 0, 0)

Thank you all!

EDIT 4:

My current workaround is (instead of drawing the tooltip, background shape, and border path on a form with a transparent background via drawable onpaint code) I actually draw the text only on a FloatingForm with the background color I want and WindowStyle set to None:

image

This has the advantage of still allowing me to add icons, text, and other graphics while allowing the tooltip to be on top of any other eto.form but is severely limiting in how I can “style” the border/shape itself. I happen to like the current Rhino windows implementation but am I bit uneasy of how it will translate to Mac.

Anyways, thanks for reading if you’ve made it this far

5 Likes

Hi @michaelvollrath,

sorry for the late reply, i’ve never used or even heard about Eto.Forms.FloatingForm

_
c.

1 Like

No worries at all and thanks for letting me know!

1 Like