Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 35 additions & 8 deletions src/generators/tkinter_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import os
import secrets
import tkinter as tk
from typing import Optional
from typing import Callable, Optional
from tkinter import filedialog, messagebox
from tkinter import ttk

Expand Down Expand Up @@ -263,19 +263,21 @@ def add_buttons(self) -> None:
)
button_pre.pack(side=tk.LEFT)
# Left arrow to decrease
self._tk.bind("<Left>", lambda event: self._decrease_floor())
self._tk.bind("<Left>", self._shortcut(lambda: self._decrease_floor()))
button_gen = ttk.Button(
button_frame, text="Generate", command=self._generate_and_draw_floor
)
button_gen.pack(side=tk.LEFT, padx=6)
# Space to generate
self._tk.bind("<space>", lambda event: self._generate_and_draw_floor())
self._tk.bind(
"<space>", self._shortcut(lambda: self._generate_and_draw_floor())
)
button_next = ttk.Button(
button_frame, text="▶", width=3, command=self._increase_floor
)
button_next.pack(side=tk.LEFT)
# Right arrow to increase
self._tk.bind("<Right>", lambda event: self._increase_floor())
self._tk.bind("<Right>", self._shortcut(lambda: self._increase_floor()))
# Next seed entry
next_seed_label = ttk.Label(
button_frame, text="Next seed", style="Muted.TLabel"
Expand All @@ -284,12 +286,37 @@ def add_buttons(self) -> None:
self._next_seed_entry = ttk.Entry(
button_frame, textvariable=self._floor_manager.next_seed, width=34
)
# Enter generates a floor with the entered seed
self._next_seed_entry.bind(
"<Return>", lambda event: self._generate_and_draw_floor()
)
# Enter generates a floor with the entered seed, Escape leaves the field.
# Both give the focus back so that the shortcuts work again.
self._next_seed_entry.bind("<Return>", lambda event: self._submit_seed())
self._next_seed_entry.bind("<Escape>", lambda event: self._tk.focus_set())
self._canvas.bind("<Button-1>", lambda event: self._tk.focus_set())
self._next_seed_entry.pack(side=tk.LEFT)

def _submit_seed(self) -> None:
"""
Generates a floor with the entered seed and leaves the seed field.
"""
self._tk.focus_set()
self._generate_and_draw_floor()

@staticmethod
def _shortcut(action: Callable[[], None]) -> Callable[[tk.Event], None]:
"""
Creates a handler for a single key shortcut that is ignored while an
entry or a button has the focus, so typing a seed or pressing a button
with the keyboard does not trigger it.
@param action: action of the shortcut
@return: event handler
"""

def handler(event: tk.Event) -> None:
if isinstance(event.widget, (tk.Entry, ttk.Entry, tk.Button, ttk.Button)):
return
action()

return handler

def create_menu_bar(self) -> None:
"""
Creates the menu bar with the file, view and floor menus.
Expand Down
47 changes: 47 additions & 0 deletions test/tkinter_generator_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,53 @@ def test_dark_theme_check_mark_follows_the_shortcut(self):
self._press("<Control-t>")
self.assertFalse(generator._theme_handler.dark.get())

def _type_in_seed_field(self, key: str) -> None:
tk = self._generator._tk
entry = self._generator._next_seed_entry
tk.update()
entry.focus_force()
tk.update()
entry.event_generate(key, when="now")
tk.update()

def test_space_in_the_seed_field_does_not_generate_a_floor(self):
generator = self._generator
generator.add_buttons()
generator._floor_manager.next_seed.set("my")
generator._next_seed_entry.icursor("end")

with mock.patch.object(generator, "_generate_and_draw_floor") as generate:
self._type_in_seed_field("<space>")

generate.assert_not_called()

def test_arrow_keys_in_the_seed_field_do_not_switch_the_floor(self):
generator = self._generator
generator.add_buttons()
generator.generate("2")
current = generator._floor

self._type_in_seed_field("<Left>")
self._type_in_seed_field("<Right>")

self.assertIs(current, generator._floor)

def test_shortcuts_work_again_after_leaving_the_seed_field(self):
generator = self._generator
generator.add_buttons()
for key in ("<Escape>", "<Return>"):
with self.subTest(key=key), mock.patch(
"floors.tkinter_floor.TkinterFloor.draw_step_by_step"
):
self._type_in_seed_field(key)
self.assertIs(generator._tk, generator._tk.focus_get())

with mock.patch.object(
generator, "_generate_and_draw_floor"
) as generate:
self._press("<space>")
generate.assert_called_once()


if __name__ == "__main__":
unittest.main()
Loading