# Mouse area

> Makes any widget react to clicks, hover, drags and the wheel. A native Pyzed component for Python desktop GUIs, with a live preview, code examples and its API.

Source: https://albakore.github.io/pyzed/library/mouse-area/

:::tip[In plain words]
An invisible layer that lets anything (a picture, a card, a drawing) notice clicks and the mouse.
:::

`pz.mouse_area` wraps any widget and reports what the mouse does over it: presses (left, right, middle, double click), entering and leaving, moving (`on_move(x, y)`) and the wheel (`on_scroll(dx, dy)`); `cursor` changes the pointer. `pz.hover(base, top)` shows `top` over `base` only while the pointer is over it, and `pz.opaque` stops clicks from reaching what is under a widget in a `stack`.

`pz.mouse_area` · Category: [Actions](https://albakore.github.io/pyzed/library/#actions) · [Full reference →](https://albakore.github.io/pyzed/reference/widgets/#mouse_area)

**Code:**

```python
pz.mouse_area(
    ui.card(pz.row([pz.icon("folder", size=20), ui.label("Open project…")], spacing=10, align="center"),
            width=280),
    on_press=on_open, on_right_press=on_menu, cursor="pointer",
)
```

## When to use

- To make a card, an image or a custom drawing clickable without turning it into a button.
- For right-click menus (`on_right_press`), drag gestures (`on_move`) and zoom with the wheel.
- `pz.hover` for actions that appear on hover, like the close button of a tab.

## Usage

```python
import pyzed as pz

class Board(pz.App):
    def __init__(self):
        self.pointer = (0, 0)
        self.zoom = 1.0

    def view(self):
        return pz.mouse_area(
            self.drawing(),
            on_move=lambda x, y: setattr(self, "pointer", (x, y)),
            on_scroll=lambda dx, dy: setattr(self, "zoom", max(0.1, self.zoom + dy * 0.1)),
            on_double_click=self.reset, cursor="crosshair",
        )
```

## Variants

:::note[About these captures]
Each capture is recorded by pyzed itself, off-screen at 2x, in One Dark or One Light to match this site's theme, and cropped to the component so it shows at its real size. The pointer hovers, presses or types into it; a component that does not react to the pointer cycles through a few themes. With reduced motion you see the first frame. Open **Code** under a capture to see the snippet that draws it; free names such as `on_save` stand for your own callbacks.
:::

### Cursor

The pointer shape is only visible with a real mouse; the shot shows the wrapped widget.

```python
pz.mouse_area(ui.card(ui.label("Click me"), width=180), on_press=on_open, cursor="pointer")
```

```python
pz.mouse_area(ui.card(ui.label("Drag me"), width=180), on_move=on_drag, cursor="grab")
```

```python
pz.mouse_area(pz.canvas([draw.circle(60, 40, 24, fill="#74ade8")], width=120, height=80),
              on_press=on_pick, cursor="crosshair")
```

### Hover content

`pz.hover` only draws its `top` while the pointer is over it: here, the close button of a row.

```python
pz.hover(ui.list_item("notes.py", on_open, icon="file-text"),
         pz.container(ui.icon_button("x", on_close, size="xs"), align_x="end", width="fill"))
```

```python
pz.hover(ui.list_item("notes.py", on_open, icon="file-text"),
         pz.container(ui.icon_button("x", on_close, size="xs"), align_x="end", width="fill"))
```

## Events

Mouse area has **10 events of its own**, listed below. Like every widget it also has the **18 common events** (`click`, `double_click`, `right_click`, `mouse_enter`, `drag`, `key_down`, `resize`…), attached with `.on(...)`: see [Events](https://albakore.github.io/pyzed/guides/events/) and [Events of every component](https://albakore.github.io/pyzed/library/events/).

| Event | Handler gets | When |
| --- | --- | --- |
| `on_press` | `()` | Left click (on press). |
| `on_release` | `()` | On releasing the left button. |
| `on_right_press` | `()` | Right click (context menus). |
| `on_double_click` | `()` | Double click. |
| `on_enter` | `()` | The mouse enters. |
| `on_exit` | `()` | The mouse leaves. |
| `on_move` | `(x, y)` | Called with `(x, y)` when the mouse moves. |
| `on_scroll` | `(dx, dy)` | Callable that receives `(dx, dy)` in lines when the wheel is used. |
| `on_middle_press` | `()` | Middle-button click. |
| `on_middle_release` | `()` | Middle button released (pan gestures, for example). |

**Handler gets** is what Pyzed passes to your callable: `()` means nothing. Any handler can also be a message (a value that is not callable), which goes to `update()`.

### Handling the events

The same events written three ways. **Imperative**: methods of the app change `self`. **Declarative**: the handlers produce messages and one `update()` decides. **Functional**: a pure function draws an immutable state and asks for changes with `set_state(...)`. See [Imperative, declarative and functional](https://albakore.github.io/pyzed/guides/code-styles/).

**Imperative:**

```python
class Board(pz.App):
    def __init__(self):
        self.pointer, self.hovering, self.zoom = (0, 0), False, 1.0

    def view(self):
        return pz.mouse_area(
            ui.card(ui.label(f"{self.pointer} · zoom {self.zoom:.1f}"), width=320),
            on_enter=lambda: setattr(self, "hovering", True),
            on_exit=lambda: setattr(self, "hovering", False),
            on_move=lambda x, y: setattr(self, "pointer", (round(x), round(y))),
            on_scroll=lambda dx, dy: setattr(self, "zoom", max(0.1, self.zoom + dy * 0.1)),
            on_double_click=lambda: setattr(self, "zoom", 1.0),
            on_right_press=lambda: print("context menu"),
            cursor="crosshair",
        )
```

**Declarative:**

```python
from dataclasses import dataclass

@dataclass
class Moved:
    x: float
    y: float

class Board(pz.App):
    pointer = (0, 0)

    def update(self, message):
        match message:
            case Moved(x, y):
                self.pointer = (round(x), round(y))
            case "reset":
                self.pointer = (0, 0)

    def view(self):
        return pz.mouse_area(ui.card(ui.label(str(self.pointer)), width=320),
                             on_move=Moved, on_double_click="reset")
```

**Functional:**

```python
from dataclasses import dataclass

@dataclass(frozen=True)
class State:
    hovering: bool = False

def view(state, set_state):
    return pz.mouse_area(ui.card(ui.label("Hovering" if state.hovering else "Point at me"), width=320),
                         on_enter=lambda: set_state(hovering=True), on_exit=lambda: set_state(hovering=False))
```

## Tips

:::tip
Hover and pointer states only exist with a real mouse, so the previews draw them as they look; see the animated clips in [Showcase](https://albakore.github.io/pyzed/showcase/) for the real thing.
:::

## Properties and API

### `pz.mouse_area`

```python
mouse_area(
    content,
    *,
    on_press=None,
    on_release=None,
    on_right_press=None,
    on_double_click=None,
    on_enter=None,
    on_exit=None,
    on_move=None,
    on_scroll=None,
    on_middle_press=None,
    on_middle_release=None,
    cursor=None,
)
```

Detects mouse events over its content.

| Property | Default | Description |
| --- | --- | --- |
| `content` | *required* | Content: a `Widget`, a string or a number (converted to text). |
| `cursor` | `None` | `"pointer"`, `"text"`, `"grab"`, `"grabbing"`, `"crosshair"`, `"not-allowed"`, `"resize-horizontal"`, `"resize-vertical"`, `"move"` or `"default"`. |

Events: `on_press`, `on_release`, `on_right_press`, `on_double_click`, `on_enter`, `on_exit`, `on_move`, `on_scroll`, `on_middle_press`, `on_middle_release` ([events](#events)).

[Full reference →](https://albakore.github.io/pyzed/reference/widgets/#mouse_area)

### `pz.hover`

```python
hover(base, top)
```

Shows `top` over `base` only while the mouse is over it (for example, a tab's close button).

| Property | Default | Description |
| --- | --- | --- |
| `base` | *required* | Widget underneath. |
| `top` | *required* | Widget that appears on hover. |

[Full reference →](https://albakore.github.io/pyzed/reference/widgets/#hover)

### `pz.opaque`

```python
opaque(content)
```

Makes a widget block the mouse for whatever is below it in a `stack`.

| Property | Default | Description |
| --- | --- | --- |
| `content` | *required* | Content: a `Widget`, a string or a number (converted to text). |

[Full reference →](https://albakore.github.io/pyzed/reference/widgets/#opaque)

## Related

- [Button](https://albakore.github.io/pyzed/library/button/): The themed button: variants, sizes, icons and shortcuts.
- [Popover](https://albakore.github.io/pyzed/library/popover/): Floating content at a point: context menus, color pickers.
- [Stack](https://albakore.github.io/pyzed/library/stack/): Layers children on top of each other; `pin` places one at x, y.
