Skip to content

Commit 0a400e6

Browse files
Fix #345 - add scroll wheel support (#394)
1 parent ce7523b commit 0a400e6

File tree

5 files changed

+55
-5
lines changed

5 files changed

+55
-5
lines changed

asciimatics/event.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ class MouseEvent(Event):
4646
LEFT_CLICK = 1
4747
RIGHT_CLICK = 2
4848
DOUBLE_CLICK = 4
49+
# Note: older versions of curses (e.g. in pypy) do not support up/down
50+
SCROLL_UP = 8
51+
SCROLL_DOWN = 16
4952

5053
def __init__(self, x: int, y: int, buttons: int):
5154
"""

asciimatics/screen.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2213,8 +2213,15 @@ def get_event(self):
22132213
button |= MouseEvent.LEFT_CLICK
22142214
if event.ButtonState & win32con.RIGHTMOST_BUTTON_PRESSED != 0:
22152215
button |= MouseEvent.RIGHT_CLICK
2216-
elif event.EventFlags & win32con.DOUBLE_CLICK != 0:
2217-
button |= MouseEvent.DOUBLE_CLICK
2216+
else:
2217+
if event.EventFlags & win32con.MOUSE_WHEELED:
2218+
direction = (event.ButtonState & 0xFFFF0000) // 0x10000
2219+
if direction & 0x8000 == 0:
2220+
button |= MouseEvent.SCROLL_UP
2221+
else:
2222+
button |= MouseEvent.SCROLL_DOWN
2223+
if event.EventFlags & win32con.DOUBLE_CLICK != 0:
2224+
button |= MouseEvent.DOUBLE_CLICK
22182225

22192226
return MouseEvent(event.MousePosition.X, event.MousePosition.Y, button)
22202227

@@ -2599,6 +2606,11 @@ def get_event(self) -> Optional[Event]:
25992606
buttons |= MouseEvent.LEFT_CLICK
26002607
if (bstate & curses.BUTTON3_PRESSED != 0 or bstate & curses.BUTTON3_CLICKED != 0):
26012608
buttons |= MouseEvent.RIGHT_CLICK
2609+
if hasattr(curses, "BUTTON5_PRESSED"):
2610+
if (bstate & curses.BUTTON4_PRESSED != 0 or bstate & curses.BUTTON4_CLICKED != 0):
2611+
buttons |= MouseEvent.SCROLL_UP
2612+
if (bstate & curses.BUTTON5_PRESSED != 0 or bstate & curses.BUTTON5_CLICKED != 0):
2613+
buttons |= MouseEvent.SCROLL_DOWN
26022614
if bstate & curses.BUTTON1_DOUBLE_CLICKED != 0:
26032615
buttons |= MouseEvent.DOUBLE_CLICK
26042616
return MouseEvent(x, y, buttons)

asciimatics/widgets/frame.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -231,8 +231,8 @@ def add_effect(self, effect: Effect):
231231
232232
:param effect: The Effect to be added.
233233
"""
234-
assert self._scene is not None
235-
effect.register_scene(self._scene)
234+
if self._scene:
235+
effect.register_scene(self._scene)
236236
self._effects.append(effect)
237237

238238
def fix(self):

samples/interactive.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@ def process_event(self, event):
5555
elif event.buttons & MouseEvent.RIGHT_CLICK != 0:
5656
# Try to whack the other sprites when mouse is clicked
5757
self._sprite.whack("CRASH!")
58+
elif event.buttons & MouseEvent.SCROLL_UP != 0:
59+
# Try to whack the other sprites when mouse is clicked
60+
self._sprite.whack("OOOOH!")
61+
elif event.buttons & MouseEvent.SCROLL_DOWN != 0:
62+
# Try to whack the other sprites when mouse is clicked
63+
self._sprite.whack("AAAAH!")
5864
else:
5965
return event
6066

tests/test_screen.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -698,19 +698,30 @@ def _inject_mouse(screen, x, y, button):
698698
event.ButtonState |= win32con.FROM_LEFT_1ST_BUTTON_PRESSED
699699
if button & MouseEvent.RIGHT_CLICK != 0:
700700
event.ButtonState |= win32con.RIGHTMOST_BUTTON_PRESSED
701+
if button & MouseEvent.SCROLL_UP != 0:
702+
event.EventFlags |= win32con.MOUSE_WHEELED
703+
event.ButtonState = 120
704+
if button & MouseEvent.SCROLL_DOWN != 0:
705+
event.EventFlags |= win32con.MOUSE_WHEELED
706+
event.ButtonState = -120
701707
if button & MouseEvent.DOUBLE_CLICK != 0:
702708
event.EventFlags |= win32con.DOUBLE_CLICK
703709
screen._stdin.WriteConsoleInput([event])
704710
else:
705711
# Curses doesn't like no value in some cases - use a dummy button
706712
# click which we don't use instead.
707-
bstate = curses.BUTTON4_CLICKED
713+
bstate = curses.BUTTON2_CLICKED
708714
if button & MouseEvent.LEFT_CLICK != 0:
709715
bstate |= curses.BUTTON1_CLICKED
710716
if button & MouseEvent.RIGHT_CLICK != 0:
711717
bstate |= curses.BUTTON3_CLICKED
712718
if button & MouseEvent.DOUBLE_CLICK != 0:
713719
bstate |= curses.BUTTON1_DOUBLE_CLICKED
720+
if sys.platform != "win32" and hasattr(curses, "BUTTON5_PRESSED"):
721+
if button & MouseEvent.SCROLL_UP != 0:
722+
bstate |= curses.BUTTON4_CLICKED
723+
if button & MouseEvent.SCROLL_DOWN != 0:
724+
bstate |= curses.BUTTON5_CLICKED
714725
curses.ungetmouse(0, x, y, 0, bstate)
715726

716727
def test_key_input(self):
@@ -787,6 +798,24 @@ def internal_checks(screen):
787798
self.assertEqual(ev.buttons, MouseEvent.DOUBLE_CLICK)
788799
self.assertIsNone(screen.get_event())
789800

801+
# Not all curses versions support scrolling...
802+
if sys.platform == "win32" or hasattr(curses, "BUTTON5_PRESSED"):
803+
# Check scroll up
804+
self._inject_mouse(screen, 1, 1, MouseEvent.SCROLL_UP)
805+
ev = screen.get_event()
806+
self.assertEqual(ev.x, 1)
807+
self.assertEqual(ev.y, 1)
808+
self.assertEqual(ev.buttons, MouseEvent.SCROLL_UP)
809+
self.assertIsNone(screen.get_event())
810+
811+
# Check scroll up
812+
self._inject_mouse(screen, 1, 1, MouseEvent.SCROLL_DOWN)
813+
ev = screen.get_event()
814+
self.assertEqual(ev.x, 1)
815+
self.assertEqual(ev.y, 1)
816+
self.assertEqual(ev.buttons, MouseEvent.SCROLL_DOWN)
817+
self.assertIsNone(screen.get_event())
818+
790819
Screen.wrapper(internal_checks, height=15)
791820

792821
def test_windows_input(self):

0 commit comments

Comments
 (0)