Skip to content

Commit 6d138e2

Browse files
authored
🔀 Merge pull request #22 from davep/truncate-undo
Simplify and tidy undo
2 parents 019ed21 + a2184ce commit 6d138e2

File tree

3 files changed

+55
-32
lines changed

3 files changed

+55
-32
lines changed

src/complexitty/commands/main.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class Undo(Command):
2626
"""Undo the latest change"""
2727

2828
BINDING_KEY = "backspace"
29+
SHOW_IN_FOOTER = True
2930

3031

3132
### main.py ends here

src/complexitty/providers/main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ def commands(self) -> CommandHits:
8585
yield SetColourToShadesOfBlue()
8686
yield SetColourToShadesOfGreen()
8787
yield SetColourToShadesOfRed()
88-
yield Undo()
88+
yield from self.maybe(Undo)
8989
yield ZeroZero()
9090
yield ZoomIn()
9191
yield ZoomInFaster()

src/complexitty/screens/main.py

Lines changed: 53 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
##############################################################################
44
# Python imports.
55
from argparse import Namespace
6+
from collections import deque
67
from math import floor, log10
78
from re import Pattern, compile
89
from typing import Final, NamedTuple, TypeAlias
@@ -18,7 +19,6 @@
1819
from textual_enhanced.commands import ChangeTheme, Command, Help
1920
from textual_enhanced.dialogs import ModalInput
2021
from textual_enhanced.screen import EnhancedScreen
21-
from textual_enhanced.tools import History
2222

2323
##############################################################################
2424
# Local imports.
@@ -77,7 +77,7 @@ class Situation(NamedTuple):
7777

7878

7979
##############################################################################
80-
PlotHistory: TypeAlias = History[Situation]
80+
PlotHistory: TypeAlias = deque[Situation]
8181
"""Type of the plot history."""
8282

8383

@@ -96,25 +96,28 @@ class Main(EnhancedScreen[None]):
9696
# Keep these together as they're bound to function keys and destined
9797
# for the footer.
9898
Help,
99-
ChangeTheme,
99+
MoveLeft,
100+
MoveRight,
101+
MoveUp,
102+
MoveDown,
103+
ZoomIn,
104+
ZoomOut,
105+
IncreaseMaximumIteration,
106+
DecreaseMaximumIteration,
107+
Undo,
100108
Quit,
101109
# Everything else.
110+
ChangeTheme,
102111
CopyCommandLineToClipboard,
103-
DecreaseMaximumIteration,
104112
DecreaseMultibrot,
105113
GoMiddle,
106114
GoTo,
107115
GreatlyDecreaseMaximumIteration,
108116
GreatlyIncreaseMaximumIteration,
109-
IncreaseMaximumIteration,
110117
IncreaseMultibrot,
111-
MoveDown,
112118
MoveDownSlowly,
113-
MoveLeft,
114119
MoveLeftSlowly,
115-
MoveRight,
116120
MoveRightSlowly,
117-
MoveUp,
118121
MoveUpSlowly,
119122
Reset,
120123
SetColourToBluesAndBrowns,
@@ -124,11 +127,8 @@ class Main(EnhancedScreen[None]):
124127
SetColourToShadesOfBlue,
125128
SetColourToShadesOfGreen,
126129
SetColourToShadesOfRed,
127-
Undo,
128130
ZeroZero,
129-
ZoomIn,
130131
ZoomInFaster,
131-
ZoomOut,
132132
ZoomOutFaster,
133133
)
134134

@@ -144,7 +144,7 @@ def __init__(self, arguments: Namespace) -> None:
144144
"""
145145
self._arguments = arguments
146146
"""The command line arguments passed to the application."""
147-
self._history = PlotHistory()
147+
self._history = PlotHistory(maxlen=128)
148148
"""The plot situation history."""
149149
super().__init__()
150150

@@ -166,7 +166,27 @@ def on_mount(self) -> None:
166166
if self._arguments.colour_map is None
167167
else get_colour_map(self._arguments.colour_map),
168168
)
169-
self._remember()
169+
170+
def check_action(self, action: str, parameters: tuple[object, ...]) -> bool | None:
171+
"""Check if an action is possible to perform right now.
172+
173+
Args:
174+
action: The action to perform.
175+
parameters: The parameters of the action.
176+
177+
Returns:
178+
`True` if it can perform, `False` or `None` if not.
179+
"""
180+
if not self.is_mounted:
181+
# Surprisingly it seems that Textual's "dynamic bindings" can
182+
# cause this method to be called before the DOM is up and
183+
# running. This breaks the rule of least astonishment, I'd say,
184+
# but okay let's be defensive... (when I can come up with a nice
185+
# little MRE I'll report it).
186+
return True
187+
if action == Undo.action_name():
188+
return bool(self._history) or None
189+
return True
170190

171191
@on(Mandelbrot.Plotted)
172192
def _update_situation(self, message: Mandelbrot.Plotted) -> None:
@@ -198,7 +218,7 @@ def _update_situation(self, message: Mandelbrot.Plotted) -> None:
198218
def _remember(self) -> None:
199219
"""Remember the current situation."""
200220
plot = self.query_one(Mandelbrot)
201-
self._history.add(
221+
self._history.append(
202222
Situation(
203223
plot.x_position,
204224
plot.y_position,
@@ -207,15 +227,16 @@ def _remember(self) -> None:
207227
plot.multibrot,
208228
)
209229
)
230+
self.refresh_bindings()
210231

211232
def action_zoom(self, change: float) -> None:
212233
"""Change the zoom value.
213234
214235
Args:
215236
change: The amount to change the zoom by.
216237
"""
217-
self.query_one(Mandelbrot).zoom *= change
218238
self._remember()
239+
self.query_one(Mandelbrot).zoom *= change
219240

220241
def action_move(self, x: int, y: int) -> None:
221242
"""Move the plot in the indicated direction.
@@ -224,17 +245,17 @@ def action_move(self, x: int, y: int) -> None:
224245
x: The number of pixels to move in the X direction.
225246
y: The number of pixels to move in the Y direction.
226247
"""
227-
self.query_one(Mandelbrot).move(x, y)
228248
self._remember()
249+
self.query_one(Mandelbrot).move(x, y)
229250

230251
def action_iterate(self, change: int) -> None:
231252
"""Change the maximum iteration.
232253
233254
Args:
234255
change: The change to make to the maximum iterations.
235256
"""
236-
self.query_one(Mandelbrot).max_iteration += change
237257
self._remember()
258+
self.query_one(Mandelbrot).max_iteration += change
238259

239260
def action_set_colour(self, colour_map: str) -> None:
240261
"""Set the colour map for the plot.
@@ -250,8 +271,8 @@ def action_multibrot(self, change: int) -> None:
250271
Args:
251272
change: The change to make to the 'multibrot' value.
252273
"""
253-
self.query_one(Mandelbrot).multibrot += change
254274
self._remember()
275+
self.query_one(Mandelbrot).multibrot += change
255276

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

266287
def action_reset_command(self) -> None:
267288
"""Reset the plot to its default values."""
268-
self.query_one(Mandelbrot).reset()
269289
self._remember()
290+
self.query_one(Mandelbrot).reset()
270291

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

319340
def action_undo_command(self) -> None:
320341
"""Undo through the history."""
321-
if (
322-
self._history.backward()
323-
and (situation := self._history.current_item) is not None
324-
):
325-
self.query_one(Mandelbrot).set(
326-
x_position=situation.x_position,
327-
y_position=situation.y_position,
328-
zoom=situation.zoom,
329-
max_iteration=situation.max_iteration,
330-
multibrot=situation.multibrot,
331-
).plot()
342+
try:
343+
situation = self._history.pop()
344+
except IndexError:
345+
return
346+
self.refresh_bindings()
347+
self.query_one(Mandelbrot).set(
348+
x_position=situation.x_position,
349+
y_position=situation.y_position,
350+
zoom=situation.zoom,
351+
max_iteration=situation.max_iteration,
352+
multibrot=situation.multibrot,
353+
).plot()
332354

333355

334356
### main.py ends here

0 commit comments

Comments
 (0)