3
3
##############################################################################
4
4
# Python imports.
5
5
from argparse import Namespace
6
+ from collections import deque
6
7
from math import floor , log10
7
8
from re import Pattern , compile
8
9
from typing import Final , NamedTuple , TypeAlias
18
19
from textual_enhanced .commands import ChangeTheme , Command , Help
19
20
from textual_enhanced .dialogs import ModalInput
20
21
from textual_enhanced .screen import EnhancedScreen
21
- from textual_enhanced .tools import History
22
22
23
23
##############################################################################
24
24
# Local imports.
@@ -77,7 +77,7 @@ class Situation(NamedTuple):
77
77
78
78
79
79
##############################################################################
80
- PlotHistory : TypeAlias = History [Situation ]
80
+ PlotHistory : TypeAlias = deque [Situation ]
81
81
"""Type of the plot history."""
82
82
83
83
@@ -96,25 +96,28 @@ class Main(EnhancedScreen[None]):
96
96
# Keep these together as they're bound to function keys and destined
97
97
# for the footer.
98
98
Help ,
99
- ChangeTheme ,
99
+ MoveLeft ,
100
+ MoveRight ,
101
+ MoveUp ,
102
+ MoveDown ,
103
+ ZoomIn ,
104
+ ZoomOut ,
105
+ IncreaseMaximumIteration ,
106
+ DecreaseMaximumIteration ,
107
+ Undo ,
100
108
Quit ,
101
109
# Everything else.
110
+ ChangeTheme ,
102
111
CopyCommandLineToClipboard ,
103
- DecreaseMaximumIteration ,
104
112
DecreaseMultibrot ,
105
113
GoMiddle ,
106
114
GoTo ,
107
115
GreatlyDecreaseMaximumIteration ,
108
116
GreatlyIncreaseMaximumIteration ,
109
- IncreaseMaximumIteration ,
110
117
IncreaseMultibrot ,
111
- MoveDown ,
112
118
MoveDownSlowly ,
113
- MoveLeft ,
114
119
MoveLeftSlowly ,
115
- MoveRight ,
116
120
MoveRightSlowly ,
117
- MoveUp ,
118
121
MoveUpSlowly ,
119
122
Reset ,
120
123
SetColourToBluesAndBrowns ,
@@ -124,11 +127,8 @@ class Main(EnhancedScreen[None]):
124
127
SetColourToShadesOfBlue ,
125
128
SetColourToShadesOfGreen ,
126
129
SetColourToShadesOfRed ,
127
- Undo ,
128
130
ZeroZero ,
129
- ZoomIn ,
130
131
ZoomInFaster ,
131
- ZoomOut ,
132
132
ZoomOutFaster ,
133
133
)
134
134
@@ -144,7 +144,7 @@ def __init__(self, arguments: Namespace) -> None:
144
144
"""
145
145
self ._arguments = arguments
146
146
"""The command line arguments passed to the application."""
147
- self ._history = PlotHistory ()
147
+ self ._history = PlotHistory (maxlen = 128 )
148
148
"""The plot situation history."""
149
149
super ().__init__ ()
150
150
@@ -166,7 +166,27 @@ def on_mount(self) -> None:
166
166
if self ._arguments .colour_map is None
167
167
else get_colour_map (self ._arguments .colour_map ),
168
168
)
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
170
190
171
191
@on (Mandelbrot .Plotted )
172
192
def _update_situation (self , message : Mandelbrot .Plotted ) -> None :
@@ -198,7 +218,7 @@ def _update_situation(self, message: Mandelbrot.Plotted) -> None:
198
218
def _remember (self ) -> None :
199
219
"""Remember the current situation."""
200
220
plot = self .query_one (Mandelbrot )
201
- self ._history .add (
221
+ self ._history .append (
202
222
Situation (
203
223
plot .x_position ,
204
224
plot .y_position ,
@@ -207,15 +227,16 @@ def _remember(self) -> None:
207
227
plot .multibrot ,
208
228
)
209
229
)
230
+ self .refresh_bindings ()
210
231
211
232
def action_zoom (self , change : float ) -> None :
212
233
"""Change the zoom value.
213
234
214
235
Args:
215
236
change: The amount to change the zoom by.
216
237
"""
217
- self .query_one (Mandelbrot ).zoom *= change
218
238
self ._remember ()
239
+ self .query_one (Mandelbrot ).zoom *= change
219
240
220
241
def action_move (self , x : int , y : int ) -> None :
221
242
"""Move the plot in the indicated direction.
@@ -224,17 +245,17 @@ def action_move(self, x: int, y: int) -> None:
224
245
x: The number of pixels to move in the X direction.
225
246
y: The number of pixels to move in the Y direction.
226
247
"""
227
- self .query_one (Mandelbrot ).move (x , y )
228
248
self ._remember ()
249
+ self .query_one (Mandelbrot ).move (x , y )
229
250
230
251
def action_iterate (self , change : int ) -> None :
231
252
"""Change the maximum iteration.
232
253
233
254
Args:
234
255
change: The change to make to the maximum iterations.
235
256
"""
236
- self .query_one (Mandelbrot ).max_iteration += change
237
257
self ._remember ()
258
+ self .query_one (Mandelbrot ).max_iteration += change
238
259
239
260
def action_set_colour (self , colour_map : str ) -> None :
240
261
"""Set the colour map for the plot.
@@ -250,8 +271,8 @@ def action_multibrot(self, change: int) -> None:
250
271
Args:
251
272
change: The change to make to the 'multibrot' value.
252
273
"""
253
- self .query_one (Mandelbrot ).multibrot += change
254
274
self ._remember ()
275
+ self .query_one (Mandelbrot ).multibrot += change
255
276
256
277
def action_goto (self , x : int , y : int ) -> None :
257
278
"""Go to a specific location.
@@ -265,8 +286,8 @@ def action_goto(self, x: int, y: int) -> None:
265
286
266
287
def action_reset_command (self ) -> None :
267
288
"""Reset the plot to its default values."""
268
- self .query_one (Mandelbrot ).reset ()
269
289
self ._remember ()
290
+ self .query_one (Mandelbrot ).reset ()
270
291
271
292
_VALID_LOCATION : Final [Pattern [str ]] = compile (
272
293
r"(?P<x>[^, ]+) *[, ] *(?P<y>[^, ]+)"
@@ -318,17 +339,18 @@ def action_copy_command_line_to_clipboard_command(self) -> None:
318
339
319
340
def action_undo_command (self ) -> None :
320
341
"""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 ()
332
354
333
355
334
356
### main.py ends here
0 commit comments