Skip to content

Commit fde6ae4

Browse files
committed
Modernize MouseWheel and make conversion instance checks generic
Add protocols for mouse-like events so that mouse conversions automatically handle new code
1 parent 34e7c3a commit fde6ae4

File tree

3 files changed

+82
-9
lines changed

3 files changed

+82
-9
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@ This project adheres to [Semantic Versioning](https://semver.org/) since version
66

77
## [Unreleased]
88

9+
### Added
10+
11+
- `tcod.event.MouseWheel` now has `which`, `window_id`, `position` and `integer_position` attributes.
12+
13+
## Fixed
14+
15+
- `tcod.event.convert_coordinates_from_window` was not converting all types of mouse events.
16+
917
## [21.1.0] - 2026-04-04
1018

1119
### Added

tcod/context.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -255,10 +255,11 @@ def convert_event(self, event: _Event) -> _Event:
255255
Now returns a new event with the coordinates converted into tiles.
256256
"""
257257
event_copy = copy.copy(event)
258-
if isinstance(event, (tcod.event.MouseState, tcod.event.MouseMotion, tcod.event.MouseButtonEvent)):
259-
assert isinstance(event_copy, (tcod.event.MouseState, tcod.event.MouseMotion, tcod.event.MouseButtonEvent))
258+
if isinstance(event, tcod.event._MouseEventWithPosition):
259+
assert isinstance(event_copy, tcod.event._MouseEventWithPosition)
260260
event_copy.position = tcod.event.Point(*self.pixel_to_tile(event.position[0], event.position[1]))
261-
event._tile = tcod.event.Point(floor(event_copy.position[0]), floor(event_copy.position[1]))
261+
if isinstance(event, tcod.event._MouseEventWithTile):
262+
event._tile = tcod.event.Point(floor(event_copy.position[0]), floor(event_copy.position[1]))
262263
if isinstance(event, tcod.event.MouseMotion):
263264
assert isinstance(event_copy, tcod.event.MouseMotion)
264265
assert event._tile is not None

tcod/event.py

Lines changed: 70 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,20 @@
8989
from collections.abc import Callable, Iterator, Mapping
9090
from math import floor
9191
from pathlib import Path
92-
from typing import TYPE_CHECKING, Any, Final, Generic, Literal, NamedTuple, TypeAlias, TypedDict, TypeVar, overload
92+
from typing import (
93+
TYPE_CHECKING,
94+
Any,
95+
Final,
96+
Generic,
97+
Literal,
98+
NamedTuple,
99+
Protocol,
100+
TypeAlias,
101+
TypedDict,
102+
TypeVar,
103+
overload,
104+
runtime_checkable,
105+
)
93106

94107
import attrs
95108
import numpy as np
@@ -630,7 +643,7 @@ class MouseButtonEvent(Event):
630643
"""
631644

632645
position: Point[float] = attrs.field(default=Point(0.0, 0.0))
633-
"""The pixel coordinates of the mouse."""
646+
"""The coordinates of the mouse."""
634647
_tile: Point[int] | None = attrs.field(default=Point(0, 0), alias="tile")
635648
"""The tile integer coordinates of the mouse on the screen. Deprecated."""
636649
button: MouseButton
@@ -709,13 +722,63 @@ class MouseWheel(Event):
709722
"""Vertical scrolling. A positive value means scrolling away from the user."""
710723
flipped: bool
711724
"""If True then the values of `x` and `y` are the opposite of their usual values.
712-
This depends on the settings of the Operating System.
725+
This depends on the operating system settings.
726+
"""
727+
728+
position: Point[float] = attrs.field(default=Point(0.0, 0.0))
729+
"""Coordinates of the mouse for this event.
730+
731+
.. versionadded:: Unreleased
732+
"""
733+
734+
which: int = 0
735+
"""Mouse device ID for this event.
736+
737+
.. versionadded:: Unreleased
738+
"""
739+
740+
window_id: int = 0
741+
"""Window ID with mouse focus.
742+
743+
.. versionadded:: Unreleased
713744
"""
714745

746+
@property
747+
def integer_position(self) -> Point[int]:
748+
"""Integer coordinates of this event.
749+
750+
.. versionadded:: Unreleased
751+
"""
752+
x, y = self.position
753+
return Point(floor(x), floor(y))
754+
715755
@classmethod
716756
def _from_sdl_event(cls, sdl_event: _C_SDL_Event) -> Self:
717757
wheel = sdl_event.wheel
718-
return cls(x=int(wheel.x), y=int(wheel.y), flipped=bool(wheel.direction), **_unpack_sdl_event(sdl_event))
758+
return cls(
759+
x=int(wheel.integer_x),
760+
y=int(wheel.integer_y),
761+
flipped=bool(wheel.direction),
762+
position=Point(float(wheel.mouse_x), float(wheel.mouse_y)),
763+
which=int(wheel.which),
764+
window_id=int(wheel.windowID),
765+
**_unpack_sdl_event(sdl_event),
766+
)
767+
768+
769+
@runtime_checkable
770+
class _MouseEventWithPosition(Protocol):
771+
"""Mouse event with position. Used internally to handle conversions."""
772+
773+
position: Point[float]
774+
775+
776+
@runtime_checkable
777+
class _MouseEventWithTile(Protocol):
778+
"""Mouse event with position and deprecated tile attribute. Used internally to handle conversions."""
779+
780+
position: Point[float]
781+
_tile: Point[int] | None
719782

720783

721784
@attrs.define(slots=True, kw_only=True)
@@ -1742,9 +1805,10 @@ def convert_coordinates_from_window(
17421805
event._tile_motion = Point(
17431806
floor(position[0]) - floor(previous_position[0]), floor(position[1]) - floor(previous_position[1])
17441807
)
1745-
if isinstance(event, (MouseState, MouseMotion)):
1808+
elif isinstance(event, _MouseEventWithPosition):
17461809
event.position = Point(*convert_coordinates_from_window(event.position, context, console, dest_rect))
1747-
event._tile = Point(floor(event.position[0]), floor(event.position[1]))
1810+
if isinstance(event, _MouseEventWithTile):
1811+
event._tile = Point(floor(event.position[0]), floor(event.position[1]))
17481812
return event
17491813

17501814

0 commit comments

Comments
 (0)