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
2 changes: 2 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ so you can switch between them. A legend next to the floor shows the room types
and how often they appear on the current floor.
The status bar shows the seed and floor id of the shown floor
and its position in the history.
Resting the mouse on a room shows its type, id, position, doors and teleport connection,
and the controls show what they do and their keyboard shortcut.
All actions are also available in the File, View and Floor menus.

| Key | Action |
Expand Down
8 changes: 4 additions & 4 deletions src/floors/floor.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,11 +232,11 @@ def is_within_border(self, coordinates: Tuple[int, int]) -> bool:
"""
return 0 <= coordinates[0] < self._width and 0 <= coordinates[1] < self._height

def _get_room(self, coordinates: Tuple[int, int]) -> Optional[Room]:
def get_room(self, coordinates: Tuple[int, int]) -> Optional[Room]:
"""
Returns the room at the given coordinates.
@param coordinates: coordinates of the room
@return: room
@return: room or None if there is no room
"""
for room in self._rooms:
if room[0] == coordinates[0] and room[1] == coordinates[1]:
Expand All @@ -256,7 +256,7 @@ def has_boss_room_as_neighbour(self, coordinates: Tuple[int, int]) -> bool:
if self.is_within_border(new_coordinates) and self.contains_room(
new_coordinates
):
room = self._get_room(new_coordinates)
room = self.get_room(new_coordinates)
if room is not None and room.get_type() == RoomType.BOSS_ROOM:
return True
return False
Expand All @@ -280,7 +280,7 @@ def has_special_room_as_neighbour(self, coordinates: Tuple[int, int]) -> bool:
if self.is_within_border(new_coordinates) and self.contains_room(
new_coordinates
):
room = self._get_room(new_coordinates)
room = self.get_room(new_coordinates)
if room is not None and room.get_type().is_special():
return True
return False
Expand Down
75 changes: 39 additions & 36 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 Callable, Optional
from typing import Callable, List, Optional, Union
from tkinter import filedialog, messagebox
from tkinter import ttk

Expand All @@ -15,6 +15,7 @@
from generators.generator import Generator
from generators.theme_handler import ThemeHandler
from rooms.tkinter.legend_panel import LegendPanel
from rooms.tkinter.tooltip import RoomToolTip, ToolTip
from utils import util_functions, globals as my_globals

# Highest floor id that can be entered in the floor field
Expand Down Expand Up @@ -63,6 +64,8 @@ def __init__(
self._tk, self._canvas, on_change=self._redraw_floor
)
self._legend = LegendPanel(self._content)
self._room_tooltip = RoomToolTip(self._canvas, self._get_shown_floor)
self._tooltips: List[ToolTip] = []
self.show_legend = tk.BooleanVar(self._tk, value=True)
self._floor_manager = FloorManager(output_file_name)
# Floor id for the next generated floor, edited in the floor field
Expand Down Expand Up @@ -221,10 +224,30 @@ def generate(self, new_seed=None) -> None:
self._floor_manager.add_floor(self._floor)
self._update_legend()

def _add_tooltip(
self, widget: tk.Widget, text: Union[str, tk.StringVar, Callable[[], str]]
) -> None:
"""
Adds a tooltip to a widget and keeps a reference to it.
@param widget: widget that shows the tooltip
@param text: text, variable or function that returns the text
"""
self._tooltips.append(ToolTip(widget, text))

def _get_shown_floor(self) -> Optional[TkinterFloor]:
"""
Returns the floor that is shown on the canvas or None before the first floor.
"""
if self._floor_manager.current_floor_index < 0:
return None
return self._floor_manager.get_current_floor()

def _update_legend(self) -> None:
"""
Shows the room counts of the current floor in the legend.
Shows the room counts of the current floor in the legend
and hides the room tooltip of the previous floor.
"""
self._room_tooltip.hide()
self._legend.update_counts(self._floor_manager.get_current_floor())

def toggle_legend(self) -> None:
Expand Down Expand Up @@ -264,7 +287,7 @@ def add_information_frame(self) -> None:
font=("DejaVu Sans", 10, "bold"),
)
name_label.pack(side=tk.LEFT)
_ToolTip(name_label, self._path)
self._add_tooltip(name_label, self._path)
ttk.Label(status, text=" · Seed ", style="Muted.TLabel").pack(side=tk.LEFT)
ttk.Label(
status,
Expand All @@ -282,9 +305,7 @@ def add_information_frame(self) -> None:
status, textvariable=self._floor_manager.position, style="Muted.TLabel"
)
position_label.pack(side=tk.LEFT)
_ToolTip(
position_label, tk.StringVar(self._tk, value="Position in the history")
)
self._add_tooltip(position_label, "Position in the history of generated floors")

def _increase_floor(self) -> None:
"""
Expand All @@ -310,12 +331,14 @@ def add_buttons(self) -> None:
button_frame, text="◀", width=3, command=self._decrease_floor
)
button_pre.pack(side=tk.LEFT)
self._add_tooltip(button_pre, "Back to the previous floor (←)")
# Left arrow to decrease
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)
self._add_tooltip(button_gen, "Generate a new floor (Space)")
# Space to generate
self._tk.bind(
"<space>", self._shortcut(lambda: self._generate_and_draw_floor())
Expand All @@ -324,6 +347,7 @@ def add_buttons(self) -> None:
button_frame, text="▶", width=3, command=self._increase_floor
)
button_next.pack(side=tk.LEFT)
self._add_tooltip(button_next, "Forward to the next floor (→)")
# Right arrow to increase
self._tk.bind("<Right>", self._shortcut(lambda: self._increase_floor()))
# Floor id for the next floor
Expand All @@ -342,6 +366,11 @@ def add_buttons(self) -> None:
self._floor_id_entry.bind("<Return>", lambda event: self._submit_seed())
self._floor_id_entry.bind("<Escape>", lambda event: self._tk.focus_set())
self._floor_id_entry.pack(side=tk.LEFT)
self._add_tooltip(
self._floor_id_entry,
"Floor id for the next floor, higher ids get more rooms\n"
"(Page Up / Page Down)",
)
self._tk.bind("<Prior>", self._shortcut(lambda: self.change_floor_id(1)))
self._tk.bind("<Next>", self._shortcut(lambda: self.change_floor_id(-1)))
# Next seed entry
Expand All @@ -358,6 +387,10 @@ def add_buttons(self) -> None:
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)
self._add_tooltip(
self._next_seed_entry,
"Seed for the next floor, press Enter to generate it",
)

def _submit_seed(self) -> None:
"""
Expand Down Expand Up @@ -459,33 +492,3 @@ def _save_current_floor(self) -> None:
Saves the current floor to the current output file.
"""
self.save(os.path.join(self._output_file_path, self._output_file_name))


class _ToolTip:
"""
Shows the value of a variable in a small window while the mouse is over a widget.
"""

def __init__(self, widget: tk.Widget, text: tk.StringVar) -> None:
self._widget = widget
self._text = text
self._window: Optional[tk.Toplevel] = None
widget.bind("<Enter>", lambda event: self._show())
widget.bind("<Leave>", lambda event: self._hide())

def _show(self) -> None:
if self._window is not None or not self._text.get():
return
self._window = tk.Toplevel(self._widget)
self._window.wm_overrideredirect(True)
x = self._widget.winfo_rootx()
y = self._widget.winfo_rooty() - 28
self._window.wm_geometry(f"+{x}+{y}")
ttk.Label(
self._window, textvariable=self._text, padding=(6, 2), relief=tk.SOLID
).pack()

def _hide(self) -> None:
if self._window is not None:
self._window.destroy()
self._window = None
56 changes: 56 additions & 0 deletions src/rooms/room_description.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
"""
Describes rooms of a floor in a short text, e.g. for tooltips.
"""

from typing import List

from floors.floor import Floor
from rooms.room import Room
from rooms.teleport_room import TeleportRoom
from utils.globals import DoorFace
from utils.room_type import RoomType, room_names

_DOOR_NAMES = {
DoorFace.TOP: "top",
DoorFace.EAST: "right",
DoorFace.BOTTOM: "bottom",
DoorFace.WEST: "left",
}
_BOSS_ROOM_SHAPES = {2: "1 × 2", 4: "2 × 2"}


def describe_room(floor: Floor, room: Room) -> str:
"""
Describes a room with its type, id, position, doors and connections.
@param floor: floor that contains the room
@param room: room to describe
@return: description with one fact per line
"""
lines: List[str] = [
f"{room_names[room.get_type()]} · #{room.get_id()}",
f"Position {room[0]}, {room[1]}",
]
doors = sorted(room.get_doors(), key=lambda door: door.value)
door_names = ", ".join(_DOOR_NAMES[door] for door in doors)
lines.append(f"Doors: {door_names or 'none'}")

rooms_by_id = {other.get_id(): other for other in floor.get_rooms()}
if isinstance(room, TeleportRoom):
target = rooms_by_id.get(room.get_connected_room_id())
if target is not None:
lines.append(
f"Teleports to #{target.get_id()} ({room_names[target.get_type()]})"
)
if room.get_type() == RoomType.BOSS_ROOM:
cells = sum(
other.get_type() == RoomType.BOSS_ROOM for other in floor.get_rooms()
)
if cells in _BOSS_ROOM_SHAPES:
lines.append(f"Part of a {_BOSS_ROOM_SHAPES[cells]} boss room")
for other in floor.get_rooms():
if (
isinstance(other, TeleportRoom)
and other.get_connected_room_id() == room.get_id()
):
lines.append(f"Reached through teleport #{other.get_id()}")
return "\n".join(lines)
Loading
Loading