More Eto Examples please

Per request of @Tommy804,

Here’s a Python 3 example of an Eto.Forms.Drawable button that responds visually to mouse events:

default:

hovered:

clicked:

Hope that helps some people, cheers!

Code:

#! python 3

"""

Author: Michael Vollrath
Company: Toyblock
Website: https://www.toyblock.co

Version: 1.0
Date: 04.21.2025
Notes: Made with <3 in Dallas, TX

Creates a custom Eto.Form and a custom Drawable to be used
as a button that visually responds to mouse events.

"""

import Eto.Forms as ef
import Eto.Drawing as ed

from Rhino.UI import EtoExtensions


# Code Example for a Custom Eto Form (non-modal)
class CustomForm(ef.Form):
    # Intiliaze CustomForm Class
    def __init__(self):
        super(CustomForm, self).__init__()  # super needed for Python 3

        # Set Form General Settings
        self.Title = "My Custom Form"
        self.Size = ed.Size(300, 200)  # Set The Overall Form Size
        self.ShowInTaskbar = True

        EtoExtensions.UseRhinoStyle(self)

        # self.WindowStyle = ef.WindowStyle.NONE  # Uncomment this to remove the forms window title bar
        self.Resizable = True
        self.MovableByWindowBackground = False
        self.Topmost = False

        self.CreateLayout()

    def CreateLayout(self):
        """Create a layout for this form and add a custom drawable button to it"""
        self.custom_button = CustomButton(starting_state=False)

        self.layout = ef.PixelLayout()
        self.layout.Size = ed.Size(280, 180)

        self.layout.Add(
            self.custom_button,
            (self.layout.Size.Width - self.custom_button.Width) / 2,
            (self.layout.Size.Height - self.custom_button.Height) / 2)

        self.Content = self.layout  # Add the layout to the form


# Code Example for a Custom Drawable Button that visually responds to mouse events
class CustomButton(ef.Drawable):
    # Intiliaze CustomButton Class
    def __init__(self, starting_state: bool):
        super(CustomButton, self).__init__()  # super needed for Python 3

        # Size/Position variables
        self._padding = 5
        self.Size = ed.Size(100 + self._padding * 2, 40 + self._padding * 2)

        # Styling variables
        self.highlight_color = ed.Color.FromArgb(0, 111, 238, 255)

        self.button_enabled_color = self.highlight_color
        self.button_disabled_color = ed.Color.FromArgb(0, 111, 238, 60)

        self.border_enabled_color = self.highlight_color
        self.border_disabled_color = self.highlight_color

        self.defaultTextColor = self.highlight_color

        self.enabledPen = ed.Pen(self.border_enabled_color, 4)
        self.disabledPen = ed.Pen(self.border_disabled_color, 2)
        self.hoverPen = ed.Pen(self.highlight_color, 8)

        self.strokeWidth = 6

        self.radius = 14

        # Mouse state tracking variables
        self._hover = False
        self._mouse_down = False

        # Bonus stuff
        self._state = starting_state

        self.font = ed.Font("Arial", 12)

    # This is where everything gets dynamically drawn real-time
    def OnPaint(self, e):
        try:
            """Basic stuff, fill, border"""
            overall_rect = ed.RectangleF(0, 0, self.Size.Width, self.Size.Height)  # This is the overall "max size" of the button drawable graphics to be drawn within
            button_rect = ed.RectangleF.Inflate(overall_rect, -self._padding, -self._padding)  # "Deinflate" to ensure all graphics are drawn "within" the button rect otherwise some clipping may occur
            rounded_rect_path = ed.GraphicsPath.GetRoundRect(button_rect, min(self.radius, button_rect.Height * .5))  # Use min here to prevent "radius" from being larger than half the height of the button
            e.Graphics.FillPath(self.button_enabled_color if self._state or self._hover else self.button_disabled_color, rounded_rect_path)
            e.Graphics.DrawPath(self.hoverPen if self._hover else (self.enabledPen if self._state else self.disabledPen), rounded_rect_path)

            """Bonus stuff below"""
            # Set text
            text = "click me!" if not self._state else "thanks! ☺️"
            text_color = ed.Colors.White if self._state or self._hover else self.defaultTextColor

            # Calculate text position to center it in the button
            text_size = e.Graphics.MeasureString(self.font, text)
            x = (self.Size.Width - text_size.Width) / 2
            y = (self.Size.Height - text_size.Height) / 2

            # Draw the text
            e.Graphics.DrawText(self.font, text_color, x, y, text)

        except Exception as ex:
            print(f"OnPaint exception: {ex}")

    def OnMouseDown(self, e):
        try:
            self._mouse_down = True
            self._state = not self._state
            self.Invalidate()  # This will ensure the button is redrawn
            print("button clicked!")

        except Exception as ex:
            print(f"OnMouseDown exception: {ex}")

    def OnMouseUp(self, e):
        try:
            self._mouse_down = False
            self.Invalidate()  # This will ensure the button is redrawn
            print("button released...")

        except Exception as ex:
            print(f"OnMouseUp exception: {ex}")

    def OnMouseEnter(self, e):
        try:
            self._hover = True
            self.Invalidate()  # This will ensure the button is redrawn
            print("button hovered")

        except Exception as ex:
            print(f"OnMouseEnter exception: {ex}")

    def OnMouseLeave(self, e):
        try:
            self._hover = False
            self.Invalidate()  # This will ensure the button is redrawn
            print("button no longer hovered")

        except Exception as ex:
            print(f"OnMouseLeave exception: {ex}")

    # def OnMouseMove(self, e):
    #     try:
    #         print("mouse is moving!")
    #     except Exception as ex:
    #         print(f"OnMouseMove exception: {ex}")


def Main():
    """Create a form instance with the drawable, then show the form"""
    # Create an Eto.Forms.Form (This is non-modal, meaning it can exist while other operations/input are being used)
    form = CustomForm()
    # form.Owner = Rhino.UI.RhinoEtoApp.MainWindow
    form.Topmost = False
    form.Show()
    form.BringToFront()


# Execute Script If Script Is Being Run Directly
if __name__ == "__main__":
    Main()