Skip to content

Commit

Permalink
mouse motion support
Browse files Browse the repository at this point in the history
  • Loading branch information
Dliwk committed Apr 4, 2024
1 parent 5ee5c46 commit 1472620
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 42 deletions.
52 changes: 37 additions & 15 deletions labyrinths/ui/mainwindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import sys
import weakref
from abc import abstractmethod
from typing import Any, Callable

import pygame

Expand Down Expand Up @@ -70,7 +71,10 @@ def close(self):
if self.parent:
self.parent.children.remove(self)

def on_mouse_left_click(self) -> None:
def on_mouse_left_button_down(self) -> None:
pass

def on_mouse_left_button_up(self) -> None:
pass

Check warning on line 78 in labyrinths/ui/mainwindow.py

View check run for this annotation

Codecov / codecov/patch

labyrinths/ui/mainwindow.py#L78

Added line #L78 was not covered by tests

def on_mouse_hover(self) -> None:
Expand All @@ -79,7 +83,13 @@ def on_mouse_hover(self) -> None:
def on_mouse_hover_end(self) -> None:
pass

Check warning on line 84 in labyrinths/ui/mainwindow.py

View check run for this annotation

Codecov / codecov/patch

labyrinths/ui/mainwindow.py#L84

Added line #L84 was not covered by tests

def on_mouse_right_click(self) -> None:
def on_mouse_right_button_down(self) -> None:
pass

Check warning on line 87 in labyrinths/ui/mainwindow.py

View check run for this annotation

Codecov / codecov/patch

labyrinths/ui/mainwindow.py#L87

Added line #L87 was not covered by tests

def on_mouse_right_button_up(self) -> None:
pass

Check warning on line 90 in labyrinths/ui/mainwindow.py

View check run for this annotation

Codecov / codecov/patch

labyrinths/ui/mainwindow.py#L90

Added line #L90 was not covered by tests

def on_mouse_motion(self, dx: int, dy: int) -> None:
pass

Check warning on line 93 in labyrinths/ui/mainwindow.py

View check run for this annotation

Codecov / codecov/patch

labyrinths/ui/mainwindow.py#L93

Added line #L93 was not covered by tests

def get_widget_at(self, x: int, y: int) -> weakref.ref[Widget] | None:
Expand Down Expand Up @@ -118,23 +128,14 @@ def render(self) -> None:
"""Render everything."""
self.root_widget.render_self_and_children(self.screen, (0, 0))

Check warning on line 129 in labyrinths/ui/mainwindow.py

View check run for this annotation

Codecov / codecov/patch

labyrinths/ui/mainwindow.py#L129

Added line #L129 was not covered by tests

def on_mouse_left_click(self, x: int, y: int) -> None:
ref = self.root_widget.get_widget_at(x, y)
if ref is None:
return
result = ref()
if result is None:
return
result.on_mouse_left_click()

def on_mouse_right_click(self, x: int, y: int) -> None:
def widget_action(self, x: int, y: int, action: Callable, *args, **kwargs) -> None:
ref = self.root_widget.get_widget_at(x, y)
if ref is None:
return

Check warning on line 134 in labyrinths/ui/mainwindow.py

View check run for this annotation

Codecov / codecov/patch

labyrinths/ui/mainwindow.py#L134

Added line #L134 was not covered by tests
result = ref()
if result is None:
return

Check warning on line 137 in labyrinths/ui/mainwindow.py

View check run for this annotation

Codecov / codecov/patch

labyrinths/ui/mainwindow.py#L137

Added line #L137 was not covered by tests
result.on_mouse_right_click()
action(result, *args, **kwargs)

def on_mouse_hover(self, x: int, y: int) -> None:
result = self.root_widget.get_widget_at(x, y)

Check warning on line 141 in labyrinths/ui/mainwindow.py

View check run for this annotation

Codecov / codecov/patch

labyrinths/ui/mainwindow.py#L141

Added line #L141 was not covered by tests
Expand All @@ -149,6 +150,21 @@ def on_mouse_hover(self, x: int, y: int) -> None:
widget.on_mouse_hover()
self.hovered_widget = result

Check warning on line 151 in labyrinths/ui/mainwindow.py

View check run for this annotation

Codecov / codecov/patch

labyrinths/ui/mainwindow.py#L150-L151

Added lines #L150 - L151 were not covered by tests

def on_mouse_left_button_down(self, x: int, y: int) -> None:
self.widget_action(x, y, lambda this: this.on_mouse_left_button_down())

def on_mouse_right_button_down(self, x: int, y: int) -> None:
self.widget_action(x, y, lambda this: this.on_mouse_right_button_down())

def on_mouse_left_button_up(self, x: int, y: int) -> None:
self.widget_action(x, y, lambda this: this.on_mouse_left_button_up())

def on_mouse_right_button_up(self, x: int, y: int) -> None:
self.widget_action(x, y, lambda this: this.on_mouse_right_button_up())

def on_mouse_motion(self, x: int, y: int, dx: int, dy: int) -> None:
self.widget_action(x, y, lambda this, dx2, dy2: this.on_mouse_motion(dx2, dy2), dx, dy)

def run(self) -> None:
"""Run the event loop."""

Expand All @@ -166,11 +182,17 @@ def run(self) -> None:
self.root_widget.on_keyup_propagate(event.key)

Check warning on line 182 in labyrinths/ui/mainwindow.py

View check run for this annotation

Codecov / codecov/patch

labyrinths/ui/mainwindow.py#L182

Added line #L182 was not covered by tests
case pygame.MOUSEBUTTONDOWN:
if event.button == 1:
self.on_mouse_left_click(*event.pos)
self.on_mouse_left_button_down(*event.pos)

Check warning on line 185 in labyrinths/ui/mainwindow.py

View check run for this annotation

Codecov / codecov/patch

labyrinths/ui/mainwindow.py#L185

Added line #L185 was not covered by tests
elif event.button == 2:
self.on_mouse_right_button_down(*event.pos)

Check warning on line 187 in labyrinths/ui/mainwindow.py

View check run for this annotation

Codecov / codecov/patch

labyrinths/ui/mainwindow.py#L187

Added line #L187 was not covered by tests
case pygame.MOUSEBUTTONUP:
if event.button == 1:
self.on_mouse_left_button_up(*event.pos)

Check warning on line 190 in labyrinths/ui/mainwindow.py

View check run for this annotation

Codecov / codecov/patch

labyrinths/ui/mainwindow.py#L190

Added line #L190 was not covered by tests
elif event.button == 2:
self.on_mouse_right_click(*event.pos)
self.on_mouse_right_button_up(*event.pos)

Check warning on line 192 in labyrinths/ui/mainwindow.py

View check run for this annotation

Codecov / codecov/patch

labyrinths/ui/mainwindow.py#L192

Added line #L192 was not covered by tests
case pygame.MOUSEMOTION:
self.on_mouse_hover(*event.pos)
self.on_mouse_motion(*event.pos, *event.rel)
self.render()
pygame.display.flip()

Check warning on line 197 in labyrinths/ui/mainwindow.py

View check run for this annotation

Codecov / codecov/patch

labyrinths/ui/mainwindow.py#L194-L197

Added lines #L194 - L197 were not covered by tests

Expand Down
2 changes: 1 addition & 1 deletion labyrinths/ui/widgets/button.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def __init__(
self.normal_color = color
self.hovered_color = hovered_color

def on_mouse_left_click(self) -> None:
def on_mouse_left_button_down(self) -> None:
self.onclick()

def on_mouse_hover(self) -> None:
Expand Down
33 changes: 25 additions & 8 deletions labyrinths/ui/widgets/mazewindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,28 +27,29 @@ def __init__(self, parent: Widget, width: int, height: int, x: int, y: int) -> N
self.current_maze: MazeData | None = None
self.maze_viewport = (-self.cellsize * 3, -self.cellsize * 3)
self.solution: Solution | None = None

self.generators = [
(KruskalGenerator, "Kruskal MST"),
(DepthFirstSearchGenerator, "DFS"),
]
self.gen_id = 0

self.mouse_pressed = False

self.help_widget = TextLabel(
self,
300,
300,
400,
200,
30,
30,
# fmt: off
text=("Use arrow keys to move.\n"
"WASD to look around.\n"
"WASD or left-click&drag to look around.\n"
"By default, mazes\n"
"are saved into\n"
"are saved to\n"
"maze.json.gz"),
# fmt: on
)
self.help_widget.hide()
# self.help_widget.hide()

Button(self, 100, 30, 0, self.height - 30, onclick=self.new_maze, text="new maze")
self.next_gen_button = Button(self, 120, 30, 0, self.height - 60, onclick=self.next_gen, text="")
Expand All @@ -57,7 +58,7 @@ def __init__(self, parent: Widget, width: int, height: int, x: int, y: int) -> N
Button(self, 30, 30, self.width - 60, 0, onclick=self.scale_down, text="-")
Button(self, 60, 30, self.width - 60, self.height // 2, onclick=self.save_maze, text="save")
Button(self, 60, 30, self.width - 60, self.height // 2 - 30, onclick=self.load_maze, text="load")
Button(self, 30, 30, 0, 0, onclick=self.toggle_help, text="?")
self.help_button = Button(self, 30, 30, 0, 0, onclick=self.toggle_help, text="x")

self.next_gen()

Expand All @@ -72,7 +73,12 @@ def next_gen(self):

def toggle_help(self):
"""Show/hide help message."""
self.help_widget.toggle()
if self.help_widget.hidden:
self.help_widget.show()
self.help_button.text = "x"

Check warning on line 78 in labyrinths/ui/widgets/mazewindow.py

View check run for this annotation

Codecov / codecov/patch

labyrinths/ui/widgets/mazewindow.py#L77-L78

Added lines #L77 - L78 were not covered by tests
else:
self.help_widget.hide()
self.help_button.text = "?"

Check warning on line 81 in labyrinths/ui/widgets/mazewindow.py

View check run for this annotation

Codecov / codecov/patch

labyrinths/ui/widgets/mazewindow.py#L80-L81

Added lines #L80 - L81 were not covered by tests

def new_maze(self) -> None:
"""Generate new maze."""
Expand Down Expand Up @@ -165,6 +171,17 @@ def on_keydown(self, key: int) -> None:
case pygame.K_RIGHT:
self.try_move_player(1, 0)

Check warning on line 172 in labyrinths/ui/widgets/mazewindow.py

View check run for this annotation

Codecov / codecov/patch

labyrinths/ui/widgets/mazewindow.py#L172

Added line #L172 was not covered by tests

def on_mouse_left_button_down(self) -> None:
self.mouse_pressed = True

Check warning on line 175 in labyrinths/ui/widgets/mazewindow.py

View check run for this annotation

Codecov / codecov/patch

labyrinths/ui/widgets/mazewindow.py#L175

Added line #L175 was not covered by tests

def on_mouse_left_button_up(self) -> None:
self.mouse_pressed = False

Check warning on line 178 in labyrinths/ui/widgets/mazewindow.py

View check run for this annotation

Codecov / codecov/patch

labyrinths/ui/widgets/mazewindow.py#L178

Added line #L178 was not covered by tests

def on_mouse_motion(self, dx: int, dy: int) -> None:
if self.mouse_pressed:
x, y = self.maze_viewport
self.maze_viewport = x - dx, y - dy

Check warning on line 183 in labyrinths/ui/widgets/mazewindow.py

View check run for this annotation

Codecov / codecov/patch

labyrinths/ui/widgets/mazewindow.py#L182-L183

Added lines #L182 - L183 were not covered by tests

def draw_maze(self) -> None:
maze = self.current_maze
assert maze is not None

Check warning on line 187 in labyrinths/ui/widgets/mazewindow.py

View check run for this annotation

Codecov / codecov/patch

labyrinths/ui/widgets/mazewindow.py#L186-L187

Added lines #L186 - L187 were not covered by tests
Expand Down
16 changes: 8 additions & 8 deletions test/test_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,27 +50,27 @@ def test_keyup(mocker, pygame_headless) -> None:
def test_mouse_click(mocker, pygame_headless) -> None:
mainwindow = MainWindow(800, 600)
widget = EmptyWidget(mainwindow.root_widget, 30, 40, 10, 20)
spy = mocker.spy(widget, "on_mouse_left_click")
spy = mocker.spy(widget, "on_mouse_left_button_down")

mainwindow.on_mouse_left_click(9, 19)
mainwindow.on_mouse_left_button_down(9, 19)
assert spy.call_count == 0

mainwindow.on_mouse_left_click(10, 20)
mainwindow.on_mouse_left_button_down(10, 20)
assert spy.call_count == 1

mainwindow.on_mouse_left_click(20, 30)
mainwindow.on_mouse_left_button_down(20, 30)
assert spy.call_count == 2

mainwindow.on_mouse_left_click(0, 80)
mainwindow.on_mouse_left_button_down(0, 80)
assert spy.call_count == 2

mainwindow.on_mouse_left_click(90, 0)
mainwindow.on_mouse_left_button_down(90, 0)
assert spy.call_count == 2

mainwindow.on_mouse_left_click(40, 50)
mainwindow.on_mouse_left_button_down(40, 50)
assert spy.call_count == 2

mainwindow.on_mouse_left_click(30, 60)
mainwindow.on_mouse_left_button_down(30, 60)
assert spy.call_count == 2


Expand Down
20 changes: 10 additions & 10 deletions test/test_ui_button.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,22 @@ def test_button_click(pygame_headless) -> None:
mainwindow = MainWindow(800, 600)
Button(mainwindow.root_widget, 40, 40, 80, 80, callback)

mainwindow.on_mouse_left_click(80, 80)
mainwindow.on_mouse_left_button_down(80, 80)
assert callback.call_count == 1

mainwindow.on_mouse_left_click(120, 100)
mainwindow.on_mouse_left_button_down(120, 100)
assert callback.call_count == 1

mainwindow.on_mouse_left_click(100, 120)
mainwindow.on_mouse_left_button_down(100, 120)
assert callback.call_count == 1

mainwindow.on_mouse_left_click(100, 100)
mainwindow.on_mouse_left_button_down(100, 100)
assert callback.call_count == 2

mainwindow.on_mouse_left_click(100, 20)
mainwindow.on_mouse_left_button_down(100, 20)
assert callback.call_count == 2

mainwindow.on_mouse_left_click(20, 100)
mainwindow.on_mouse_left_button_down(20, 100)
assert callback.call_count == 2


Expand All @@ -59,14 +59,14 @@ def call_counts():
# 55..65
Button(mainwindow.root_widget, 10, 10, 55, 55, callback3)

mainwindow.on_mouse_left_click(60, 60)
mainwindow.on_mouse_left_button_down(60, 60)
assert call_counts() == (0, 0, 1)

mainwindow.on_mouse_left_click(50, 50)
mainwindow.on_mouse_left_button_down(50, 50)
assert call_counts() == (0, 1, 1)

mainwindow.on_mouse_left_click(67, 67)
mainwindow.on_mouse_left_button_down(67, 67)
assert call_counts() == (0, 2, 1)

mainwindow.on_mouse_left_click(45, 45)
mainwindow.on_mouse_left_button_down(45, 45)
assert call_counts() == (1, 2, 1)

0 comments on commit 1472620

Please sign in to comment.