8787import sys
8888import warnings
8989from collections .abc import Callable , Iterator , Mapping
90+ from math import floor
9091from typing import TYPE_CHECKING , Any , Final , Generic , Literal , NamedTuple , TypeAlias , TypeVar , overload
9192
9293import attrs
@@ -149,16 +150,16 @@ def _describe_bitmask(bits: int, table: Mapping[int, str], default: str = "0") -
149150 return "|" .join (result )
150151
151152
152- def _pixel_to_tile (x : float , y : float ) -> tuple [ float , float ] | None :
153+ def _pixel_to_tile (xy : tuple [ float , float ], / ) -> Point [ float ] | None :
153154 """Convert pixel coordinates to tile coordinates."""
154155 if not lib .TCOD_ctx .engine :
155156 return None
156- xy = ffi .new ("double[2]" , ( x , y ) )
157- lib .TCOD_sys_pixel_to_tile (xy , xy + 1 )
158- return xy [0 ], xy [1 ]
157+ xy_out = ffi .new ("double[2]" , xy )
158+ lib .TCOD_sys_pixel_to_tile (xy_out , xy_out + 1 )
159+ return Point ( float ( xy_out [0 ]), float ( xy_out [1 ]))
159160
160161
161- class Point (NamedTuple ):
162+ class Point (NamedTuple , Generic [ T ] ):
162163 """A 2D position used for events with mouse coordinates.
163164
164165 .. seealso::
@@ -168,13 +169,13 @@ class Point(NamedTuple):
168169 Now uses floating point coordinates due to the port to SDL3.
169170 """
170171
171- x : float
172+ x : T
172173 """A pixel or tile coordinate starting with zero as the left-most position."""
173- y : float
174+ y : T
174175 """A pixel or tile coordinate starting with zero as the top-most position."""
175176
176177
177- def _verify_tile_coordinates (xy : Point | None ) -> Point :
178+ def _verify_tile_coordinates (xy : Point [ int ] | None ) -> Point [ int ] :
178179 """Check if an events tile coordinate is initialized and warn if not.
179180
180181 Always returns a valid Point object for backwards compatibility.
@@ -399,36 +400,50 @@ class MouseState(Event):
399400 Renamed `pixel` attribute to `position`.
400401 """
401402
402- position : Point = attrs .field (default = Point (0 , 0 ))
403+ position : Point [ float ] = attrs .field (default = Point (0.0 , 0. 0 ))
403404 """The position coordinates of the mouse."""
404- _tile : Point | None = attrs .field (default = Point (0 , 0 ), alias = "tile" )
405- """The integer tile coordinates of the mouse on the screen."""
405+ _tile : Point [ int ] | None = attrs .field (default = Point (0 , 0 ), alias = "tile" )
406+
406407 state : MouseButtonMask = attrs .field (default = MouseButtonMask (0 ))
407408 """A bitmask of which mouse buttons are currently held."""
408409
410+ @property
411+ def integer_position (self ) -> Point [int ]:
412+ """Integer coordinates of this event.
413+
414+ .. versionadded:: Unreleased
415+ """
416+ x , y = self .position
417+ return Point (floor (x ), floor (y ))
418+
409419 @property
410420 @deprecated ("The mouse.pixel attribute is deprecated. Use mouse.position instead." )
411- def pixel (self ) -> Point :
421+ def pixel (self ) -> Point [ float ] :
412422 return self .position
413423
414424 @pixel .setter
415- def pixel (self , value : Point ) -> None :
425+ def pixel (self , value : Point [ float ] ) -> None :
416426 self .position = value
417427
418428 @property
419429 @deprecated (
420430 "The mouse.tile attribute is deprecated."
421- " Use mouse.position of the event returned by context.convert_event instead."
431+ " Use mouse.integer_position of the event returned by context.convert_event instead."
422432 )
423- def tile (self ) -> Point :
433+ def tile (self ) -> Point [int ]:
434+ """The integer tile coordinates of the mouse on the screen.
435+
436+ .. deprecated:: Unreleased
437+ Use :any:`integer_position` of the event returned by :any:`Context.convert_event` instead.
438+ """
424439 return _verify_tile_coordinates (self ._tile )
425440
426441 @tile .setter
427442 @deprecated (
428443 "The mouse.tile attribute is deprecated."
429- " Use mouse.position of the event returned by context.convert_event instead."
444+ " Use mouse.integer_position of the event returned by context.convert_event instead."
430445 )
431- def tile (self , xy : tuple [float , float ]) -> None :
446+ def tile (self , xy : tuple [int , int ]) -> None :
432447 self ._tile = Point (* xy )
433448
434449
@@ -444,52 +459,67 @@ class MouseMotion(MouseState):
444459 `position` and `motion` now use floating point coordinates.
445460 """
446461
447- motion : Point = attrs .field (default = Point (0 , 0 ))
462+ motion : Point [ float ] = attrs .field (default = Point (0.0 , 0. 0 ))
448463 """The pixel delta."""
449- _tile_motion : Point | None = attrs .field (default = Point (0 , 0 ), alias = "tile_motion" )
450- """The tile delta."""
464+ _tile_motion : Point [int ] | None = attrs .field (default = Point (0 , 0 ), alias = "tile_motion" )
465+
466+ @property
467+ def integer_motion (self ) -> Point [int ]:
468+ """Integer motion of this event.
469+
470+ .. versionadded:: Unreleased
471+ """
472+ x , y = self .position
473+ dx , dy = self .motion
474+ prev_x , prev_y = x - dx , y - dy
475+ return Point (floor (x ) - floor (prev_x ), floor (y ) - floor (prev_y ))
451476
452477 @property
453478 @deprecated ("The mouse.pixel_motion attribute is deprecated. Use mouse.motion instead." )
454- def pixel_motion (self ) -> Point :
479+ def pixel_motion (self ) -> Point [ float ] :
455480 return self .motion
456481
457482 @pixel_motion .setter
458483 @deprecated ("The mouse.pixel_motion attribute is deprecated. Use mouse.motion instead." )
459- def pixel_motion (self , value : Point ) -> None :
484+ def pixel_motion (self , value : Point [ float ] ) -> None :
460485 self .motion = value
461486
462487 @property
463488 @deprecated (
464489 "The mouse.tile_motion attribute is deprecated."
465- " Use mouse.motion of the event returned by context.convert_event instead."
490+ " Use mouse.integer_motion of the event returned by context.convert_event instead."
466491 )
467- def tile_motion (self ) -> Point :
492+ def tile_motion (self ) -> Point [int ]:
493+ """The tile delta.
494+
495+ .. deprecated:: Unreleased
496+ Use :any:`integer_motion` of the event returned by :any:`Context.convert_event` instead.
497+ """
468498 return _verify_tile_coordinates (self ._tile_motion )
469499
470500 @tile_motion .setter
471501 @deprecated (
472502 "The mouse.tile_motion attribute is deprecated."
473- " Use mouse.motion of the event returned by context.convert_event instead."
503+ " Use mouse.integer_motion of the event returned by context.convert_event instead."
474504 )
475- def tile_motion (self , xy : tuple [float , float ]) -> None :
505+ def tile_motion (self , xy : tuple [int , int ]) -> None :
476506 self ._tile_motion = Point (* xy )
477507
478508 @classmethod
479509 def _from_sdl_event (cls , sdl_event : _C_SDL_Event ) -> Self :
480510 motion = sdl_event .motion
481511 state = MouseButtonMask (motion .state )
482512
483- pixel = Point (motion .x , motion .y )
484- pixel_motion = Point (motion .xrel , motion .yrel )
485- subtile = _pixel_to_tile (* pixel )
513+ pixel = Point (float ( motion .x ), float ( motion .y ) )
514+ pixel_motion = Point (float ( motion .xrel ), float ( motion .yrel ) )
515+ subtile = _pixel_to_tile (pixel )
486516 if subtile is None :
487517 self = cls (position = pixel , motion = pixel_motion , tile = None , tile_motion = None , state = state )
488518 else :
489- tile = Point (int (subtile [0 ]), int (subtile [1 ]))
490- prev_pixel = pixel [0 ] - pixel_motion [0 ], pixel [1 ] - pixel_motion [1 ]
491- prev_subtile = _pixel_to_tile (* prev_pixel ) or (0 , 0 )
492- prev_tile = int (prev_subtile [0 ]), int (prev_subtile [1 ])
519+ tile = Point (floor (subtile [0 ]), floor (subtile [1 ]))
520+ prev_pixel = ( pixel [0 ] - pixel_motion [0 ], pixel [1 ] - pixel_motion [1 ])
521+ prev_subtile = _pixel_to_tile (prev_pixel ) or (0 , 0 )
522+ prev_tile = floor (prev_subtile [0 ]), floor (prev_subtile [1 ])
493523 tile_motion = Point (tile [0 ] - prev_tile [0 ], tile [1 ] - prev_tile [1 ])
494524 self = cls (position = pixel , motion = pixel_motion , tile = tile , tile_motion = tile_motion , state = state )
495525 self .sdl_event = sdl_event
@@ -507,10 +537,10 @@ class MouseButtonEvent(Event):
507537 No longer a subclass of :any:`MouseState`.
508538 """
509539
510- position : Point = attrs .field (default = Point (0 , 0 ))
540+ position : Point [ float ] = attrs .field (default = Point (0.0 , 0. 0 ))
511541 """The pixel coordinates of the mouse."""
512- _tile : Point | None = attrs .field (default = Point (0 , 0 ), alias = "tile" )
513- """The tile coordinates of the mouse on the screen."""
542+ _tile : Point [ int ] | None = attrs .field (default = Point (0 , 0 ), alias = "tile" )
543+ """The tile integer coordinates of the mouse on the screen. Deprecated ."""
514544 button : MouseButton
515545 """Which mouse button index was pressed or released in this event.
516546
@@ -521,12 +551,12 @@ class MouseButtonEvent(Event):
521551 @classmethod
522552 def _from_sdl_event (cls , sdl_event : _C_SDL_Event ) -> Self :
523553 button = sdl_event .button
524- pixel = Point (button .x , button .y )
525- subtile = _pixel_to_tile (* pixel )
554+ pixel = Point (float ( button .x ), float ( button .y ) )
555+ subtile = _pixel_to_tile (pixel )
526556 if subtile is None :
527- tile : Point | None = None
557+ tile : Point [ int ] | None = None
528558 else :
529- tile = Point (float (subtile [0 ]), float (subtile [1 ]))
559+ tile = Point (floor (subtile [0 ]), floor (subtile [1 ]))
530560 self = cls (position = pixel , tile = tile , button = MouseButton (button .button ))
531561 self .sdl_event = sdl_event
532562 return self
@@ -1362,12 +1392,12 @@ def get_mouse_state() -> MouseState:
13621392
13631393 .. versionadded:: 9.3
13641394 """
1365- xy = ffi .new ("int [2]" )
1395+ xy = ffi .new ("float [2]" )
13661396 buttons = lib .SDL_GetMouseState (xy , xy + 1 )
1367- tile = _pixel_to_tile (* xy )
1397+ tile = _pixel_to_tile (tuple ( xy ) )
13681398 if tile is None :
13691399 return MouseState (position = Point (xy [0 ], xy [1 ]), tile = None , state = buttons )
1370- return MouseState (position = Point (xy [0 ], xy [1 ]), tile = Point (int (tile [0 ]), int (tile [1 ])), state = buttons )
1400+ return MouseState (position = Point (xy [0 ], xy [1 ]), tile = Point (floor (tile [0 ]), floor (tile [1 ])), state = buttons )
13711401
13721402
13731403@overload
@@ -1431,14 +1461,13 @@ def convert_coordinates_from_window(
14311461 ((event .position [0 ] - event .motion [0 ]), (event .position [1 ] - event .motion [1 ])), context , console , dest_rect
14321462 )
14331463 position = convert_coordinates_from_window (event .position , context , console , dest_rect )
1434- event .motion = tcod . event . Point (position [0 ] - previous_position [0 ], position [1 ] - previous_position [1 ])
1435- event ._tile_motion = tcod . event . Point (
1436- int (position [0 ]) - int (previous_position [0 ]), int (position [1 ]) - int (previous_position [1 ])
1464+ event .motion = Point (position [0 ] - previous_position [0 ], position [1 ] - previous_position [1 ])
1465+ event ._tile_motion = Point (
1466+ floor (position [0 ]) - floor (previous_position [0 ]), floor (position [1 ]) - floor (previous_position [1 ])
14371467 )
14381468 if isinstance (event , (MouseState , MouseMotion )):
1439- event .position = event ._tile = tcod .event .Point (
1440- * convert_coordinates_from_window (event .position , context , console , dest_rect )
1441- )
1469+ event .position = Point (* convert_coordinates_from_window (event .position , context , console , dest_rect ))
1470+ event ._tile = Point (floor (event .position [0 ]), floor (event .position [1 ]))
14421471 return event
14431472
14441473
0 commit comments