Skip to content

use_hovered

use_hovered tells a component whether the mouse is over the element it returns, as CSS's :hover does, so it can highlight itself under the pointer:

@component
def menu_item(label: str) -> Text:
    hovered = use_hovered()

    return Text(
        content=f"{'>' if hovered.border else ' '} {label}",
        style=pad_right(1) | (text_bg("sky", 800) if hovered.border else text_color("slate", 300)),
    )


@component
def menu() -> Div:
    return Div(
        style=col | border | border_lightrounded | fit_content_width,
        children=tuple(menu_item(label) for label in ("New file", "Open...", "Save", "Save as...", "Quit")),
    )

A menu with the pointer over Save, which is highlighted

╭─────────────╮         
│  New file   │         
│  Open...    │         
│> Save       │         
│  Save as... │         
│  Quit       │         
╰─────────────╯         

The highlight follows the pointer down the menu, and goes away when the pointer leaves it:

Which box the mouse is in

Hovered reports each of the element's boxes, from its content box out to its margin box, so a component can tell the mouse over its border from the mouse over its content. Each box holds the ones inside it, so the mouse in the content box is in all four:

@component
def boxes() -> Div:
    hovered = use_hovered()

    return Div(
        style=col
        | margin(1)
        | margin_color("slate", 800)
        | border
        | border_lightrounded
        | (border_color("amber", 400) if hovered.border else None)
        | pad(1)
        | padding_color("slate", 700)
        | content_color("slate", 900)
        | fit_content_width,
        children=tuple(
            Text(
                content=f"{name:<7} {'yes' if is_hovered else 'no'}",
                style=text_color("emerald" if is_hovered else "slate", 400),
            )
            for name, is_hovered in (
                ("content", hovered.content),
                ("padding", hovered.padding),
                ("border", hovered.border),
                ("margin", hovered.margin),
            )
        ),
    )

The pointer in the padding, so padding, border and margin say yes and content says no

 ╭─────────────╮        
 │             │        
 │ content no  │        
 │ padding yes │        
 │ border  yes │        
 │ margin  yes │        
 │             │        
 ╰─────────────╯        

Here the pointer crosses the margin, the border and the padding into the content, then leaves through the bottom:

Only the parts of the element that are painted count: the mouse over a part of it that an ancestor clips away, such as a row scrolled out of a scroll container, is outside all four boxes (see Clipping and the mouse).

When it renders again

use_hovered is built on use_mouse and use_rects, so a component that calls it renders again on every move of the mouse, wherever it is on the screen, and whenever its boxes move. It checks the mouse against the boxes as the last layout placed them, and renders again before a frame that moves them is painted, as use_rects does, so it checks against where the boxes are drawn.

API

counterweight.hooks.use_hovered

use_hovered() -> Hovered
RETURNS DESCRIPTION
Hovered

A record describing which of the calling component's top-level element's content, padding, border, and margin rectangles the mouse is currently inside, counting only the parts inside its clip, so the mouse over a part its ancestors clip away is outside all of them.

counterweight.hooks.Hovered dataclass

Hovered(
    content: bool, padding: bool, border: bool, margin: bool
)

Which of an element's boxes the mouse is over, as use_hovered returns it. Each box holds the ones inside it, so the mouse over the content box is over all four.

content instance-attribute

content: bool

Whether the mouse is over the content box.

padding instance-attribute

padding: bool

Whether the mouse is over the padding box: the content and the padding around it.

border instance-attribute

border: bool

Whether the mouse is over the border box: the padding box and the border around it.

margin instance-attribute

margin: bool

Whether the mouse is over the margin box: the border box and the margin around it.