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
1 change: 1 addition & 0 deletions src/complexitty/commands/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class Undo(Command):
"""Undo the latest change"""

BINDING_KEY = "backspace"
SHOW_IN_FOOTER = True


### main.py ends here
2 changes: 1 addition & 1 deletion src/complexitty/providers/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def commands(self) -> CommandHits:
yield SetColourToShadesOfBlue()
yield SetColourToShadesOfGreen()
yield SetColourToShadesOfRed()
yield Undo()
yield from self.maybe(Undo)
yield ZeroZero()
yield ZoomIn()
yield ZoomInFaster()
Expand Down
84 changes: 53 additions & 31 deletions src/complexitty/screens/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
##############################################################################
# Python imports.
from argparse import Namespace
from collections import deque
from math import floor, log10
from re import Pattern, compile
from typing import Final, NamedTuple, TypeAlias
Expand All @@ -18,7 +19,6 @@
from textual_enhanced.commands import ChangeTheme, Command, Help
from textual_enhanced.dialogs import ModalInput
from textual_enhanced.screen import EnhancedScreen
from textual_enhanced.tools import History

##############################################################################
# Local imports.
Expand Down Expand Up @@ -77,7 +77,7 @@ class Situation(NamedTuple):


##############################################################################
PlotHistory: TypeAlias = History[Situation]
PlotHistory: TypeAlias = deque[Situation]
"""Type of the plot history."""


Expand All @@ -96,25 +96,28 @@ class Main(EnhancedScreen[None]):
# Keep these together as they're bound to function keys and destined
# for the footer.
Help,
ChangeTheme,
MoveLeft,
MoveRight,
MoveUp,
MoveDown,
ZoomIn,
ZoomOut,
IncreaseMaximumIteration,
DecreaseMaximumIteration,
Undo,
Quit,
# Everything else.
ChangeTheme,
CopyCommandLineToClipboard,
DecreaseMaximumIteration,
DecreaseMultibrot,
GoMiddle,
GoTo,
GreatlyDecreaseMaximumIteration,
GreatlyIncreaseMaximumIteration,
IncreaseMaximumIteration,
IncreaseMultibrot,
MoveDown,
MoveDownSlowly,
MoveLeft,
MoveLeftSlowly,
MoveRight,
MoveRightSlowly,
MoveUp,
MoveUpSlowly,
Reset,
SetColourToBluesAndBrowns,
Expand All @@ -124,11 +127,8 @@ class Main(EnhancedScreen[None]):
SetColourToShadesOfBlue,
SetColourToShadesOfGreen,
SetColourToShadesOfRed,
Undo,
ZeroZero,
ZoomIn,
ZoomInFaster,
ZoomOut,
ZoomOutFaster,
)

Expand All @@ -144,7 +144,7 @@ def __init__(self, arguments: Namespace) -> None:
"""
self._arguments = arguments
"""The command line arguments passed to the application."""
self._history = PlotHistory()
self._history = PlotHistory(maxlen=128)
"""The plot situation history."""
super().__init__()

Expand All @@ -166,7 +166,27 @@ def on_mount(self) -> None:
if self._arguments.colour_map is None
else get_colour_map(self._arguments.colour_map),
)
self._remember()

def check_action(self, action: str, parameters: tuple[object, ...]) -> bool | None:
"""Check if an action is possible to perform right now.

Args:
action: The action to perform.
parameters: The parameters of the action.

Returns:
`True` if it can perform, `False` or `None` if not.
"""
if not self.is_mounted:
# Surprisingly it seems that Textual's "dynamic bindings" can
# cause this method to be called before the DOM is up and
# running. This breaks the rule of least astonishment, I'd say,
# but okay let's be defensive... (when I can come up with a nice
# little MRE I'll report it).
return True
if action == Undo.action_name():
return bool(self._history) or None
return True

@on(Mandelbrot.Plotted)
def _update_situation(self, message: Mandelbrot.Plotted) -> None:
Expand Down Expand Up @@ -198,7 +218,7 @@ def _update_situation(self, message: Mandelbrot.Plotted) -> None:
def _remember(self) -> None:
"""Remember the current situation."""
plot = self.query_one(Mandelbrot)
self._history.add(
self._history.append(
Situation(
plot.x_position,
plot.y_position,
Expand All @@ -207,15 +227,16 @@ def _remember(self) -> None:
plot.multibrot,
)
)
self.refresh_bindings()

def action_zoom(self, change: float) -> None:
"""Change the zoom value.

Args:
change: The amount to change the zoom by.
"""
self.query_one(Mandelbrot).zoom *= change
self._remember()
self.query_one(Mandelbrot).zoom *= change

def action_move(self, x: int, y: int) -> None:
"""Move the plot in the indicated direction.
Expand All @@ -224,17 +245,17 @@ def action_move(self, x: int, y: int) -> None:
x: The number of pixels to move in the X direction.
y: The number of pixels to move in the Y direction.
"""
self.query_one(Mandelbrot).move(x, y)
self._remember()
self.query_one(Mandelbrot).move(x, y)

def action_iterate(self, change: int) -> None:
"""Change the maximum iteration.

Args:
change: The change to make to the maximum iterations.
"""
self.query_one(Mandelbrot).max_iteration += change
self._remember()
self.query_one(Mandelbrot).max_iteration += change

def action_set_colour(self, colour_map: str) -> None:
"""Set the colour map for the plot.
Expand All @@ -250,8 +271,8 @@ def action_multibrot(self, change: int) -> None:
Args:
change: The change to make to the 'multibrot' value.
"""
self.query_one(Mandelbrot).multibrot += change
self._remember()
self.query_one(Mandelbrot).multibrot += change

def action_goto(self, x: int, y: int) -> None:
"""Go to a specific location.
Expand All @@ -265,8 +286,8 @@ def action_goto(self, x: int, y: int) -> None:

def action_reset_command(self) -> None:
"""Reset the plot to its default values."""
self.query_one(Mandelbrot).reset()
self._remember()
self.query_one(Mandelbrot).reset()

_VALID_LOCATION: Final[Pattern[str]] = compile(
r"(?P<x>[^, ]+) *[, ] *(?P<y>[^, ]+)"
Expand Down Expand Up @@ -318,17 +339,18 @@ def action_copy_command_line_to_clipboard_command(self) -> None:

def action_undo_command(self) -> None:
"""Undo through the history."""
if (
self._history.backward()
and (situation := self._history.current_item) is not None
):
self.query_one(Mandelbrot).set(
x_position=situation.x_position,
y_position=situation.y_position,
zoom=situation.zoom,
max_iteration=situation.max_iteration,
multibrot=situation.multibrot,
).plot()
try:
situation = self._history.pop()
except IndexError:
return
self.refresh_bindings()
self.query_one(Mandelbrot).set(
x_position=situation.x_position,
y_position=situation.y_position,
zoom=situation.zoom,
max_iteration=situation.max_iteration,
multibrot=situation.multibrot,
).plot()


### main.py ends here
Loading