Skip to content

Commit 019ed21

Browse files
authored
🔀 Merge pull request #21 from davep/undo
Add history/undo
2 parents f2ae8f1 + 23f75f3 commit 019ed21

File tree

8 files changed

+84
-12
lines changed

8 files changed

+84
-12
lines changed

ChangeLog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
the zoom level. ([#14](https://github.com/davep/complexitty/issues/14))
1313
- Added `CopyCommandLineToClipboard`.
1414
([#20](https://github.com/davep/complexitty/pull/20))
15+
- Added the ability to undo a change in the plot.
16+
([#21](https://github.com/davep/complexitty/pull/21))
1517

1618
## v0.2.0
1719

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ authors = [
77
]
88
dependencies = [
99
"textual>=3.1.0",
10-
"textual-enhanced>=0.11.0",
10+
"textual-enhanced>=0.13.0",
1111
"textual-canvas>=0.3.0",
1212
"xdg-base-dirs>=6.0.2",
1313
]

requirements-dev.lock

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
-e file:.
1313
aiohappyeyeballs==2.6.1
1414
# via aiohttp
15-
aiohttp==3.11.16
15+
aiohttp==3.11.18
1616
# via aiohttp-jinja2
1717
# via textual-dev
1818
# via textual-serve
@@ -42,12 +42,12 @@ distlib==0.3.9
4242
# via virtualenv
4343
filelock==3.18.0
4444
# via virtualenv
45-
frozenlist==1.5.0
45+
frozenlist==1.6.0
4646
# via aiohttp
4747
# via aiosignal
4848
ghp-import==2.1.0
4949
# via mkdocs
50-
identify==2.6.9
50+
identify==2.6.10
5151
# via pre-commit
5252
idna==3.10
5353
# via requests
@@ -91,11 +91,11 @@ multidict==6.4.3
9191
# via aiohttp
9292
# via yarl
9393
mypy==1.15.0
94-
mypy-extensions==1.0.0
94+
mypy-extensions==1.1.0
9595
# via mypy
9696
nodeenv==1.9.1
9797
# via pre-commit
98-
packaging==24.2
98+
packaging==25.0
9999
# via mkdocs
100100
paginate==0.5.7
101101
# via mkdocs-material
@@ -132,7 +132,7 @@ rich==14.0.0
132132
# via textual-serve
133133
six==1.17.0
134134
# via python-dateutil
135-
textual==3.1.0
135+
textual==3.1.1
136136
# via complexitty
137137
# via textual-canvas
138138
# via textual-dev
@@ -141,7 +141,7 @@ textual==3.1.0
141141
textual-canvas==0.4.0
142142
# via complexitty
143143
textual-dev==1.7.0
144-
textual-enhanced==0.12.0
144+
textual-enhanced==0.13.0
145145
# via complexitty
146146
textual-serve==1.1.2
147147
# via textual-dev

requirements.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@ pygments==2.19.1
2626
# via rich
2727
rich==14.0.0
2828
# via textual
29-
textual==3.1.0
29+
textual==3.1.1
3030
# via complexitty
3131
# via textual-canvas
3232
# via textual-enhanced
3333
textual-canvas==0.4.0
3434
# via complexitty
35-
textual-enhanced==0.12.0
35+
textual-enhanced==0.13.0
3636
# via complexitty
3737
typing-extensions==4.13.2
3838
# via textual

src/complexitty/commands/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
SetColourToShadesOfGreen,
1212
SetColourToShadesOfRed,
1313
)
14-
from .main import CopyCommandLineToClipboard, Quit
14+
from .main import CopyCommandLineToClipboard, Quit, Undo
1515
from .navigation import (
1616
GoMiddle,
1717
GoTo,
@@ -68,6 +68,7 @@
6868
"SetColourToShadesOfBlue",
6969
"SetColourToShadesOfGreen",
7070
"SetColourToShadesOfRed",
71+
"Undo",
7172
"ZeroZero",
7273
"ZoomIn",
7374
"ZoomInFaster",

src/complexitty/commands/main.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,11 @@ class CopyCommandLineToClipboard(Command):
2121
BINDING_KEY = "c"
2222

2323

24+
##############################################################################
25+
class Undo(Command):
26+
"""Undo the latest change"""
27+
28+
BINDING_KEY = "backspace"
29+
30+
2431
### main.py ends here

src/complexitty/providers/main.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
SetColourToShadesOfBlue,
3939
SetColourToShadesOfGreen,
4040
SetColourToShadesOfRed,
41+
Undo,
4142
ZeroZero,
4243
ZoomIn,
4344
ZoomInFaster,
@@ -84,6 +85,7 @@ def commands(self) -> CommandHits:
8485
yield SetColourToShadesOfBlue()
8586
yield SetColourToShadesOfGreen()
8687
yield SetColourToShadesOfRed()
88+
yield Undo()
8789
yield ZeroZero()
8890
yield ZoomIn()
8991
yield ZoomInFaster()

src/complexitty/screens/main.py

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from argparse import Namespace
66
from math import floor, log10
77
from re import Pattern, compile
8-
from typing import Final
8+
from typing import Final, NamedTuple, TypeAlias
99

1010
##############################################################################
1111
# Textual imports.
@@ -18,6 +18,7 @@
1818
from textual_enhanced.commands import ChangeTheme, Command, Help
1919
from textual_enhanced.dialogs import ModalInput
2020
from textual_enhanced.screen import EnhancedScreen
21+
from textual_enhanced.tools import History
2122

2223
##############################################################################
2324
# Local imports.
@@ -48,6 +49,7 @@
4849
SetColourToShadesOfBlue,
4950
SetColourToShadesOfGreen,
5051
SetColourToShadesOfRed,
52+
Undo,
5153
ZeroZero,
5254
ZoomIn,
5355
ZoomInFaster,
@@ -58,6 +60,27 @@
5860
from ..providers import MainCommands
5961

6062

63+
##############################################################################
64+
class Situation(NamedTuple):
65+
"""A class to hold a particular situation we can undo to."""
66+
67+
x_position: float
68+
"""The X position in the plot."""
69+
y_position: float
70+
"""The Y position in the plot."""
71+
zoom: float
72+
"""The zoom level."""
73+
max_iteration: int
74+
"""The maximum iteration."""
75+
multibrot: float
76+
"""The multibrot setting."""
77+
78+
79+
##############################################################################
80+
PlotHistory: TypeAlias = History[Situation]
81+
"""Type of the plot history."""
82+
83+
6184
##############################################################################
6285
class Main(EnhancedScreen[None]):
6386
"""The main screen for the application."""
@@ -101,6 +124,7 @@ class Main(EnhancedScreen[None]):
101124
SetColourToShadesOfBlue,
102125
SetColourToShadesOfGreen,
103126
SetColourToShadesOfRed,
127+
Undo,
104128
ZeroZero,
105129
ZoomIn,
106130
ZoomInFaster,
@@ -120,6 +144,8 @@ def __init__(self, arguments: Namespace) -> None:
120144
"""
121145
self._arguments = arguments
122146
"""The command line arguments passed to the application."""
147+
self._history = PlotHistory()
148+
"""The plot situation history."""
123149
super().__init__()
124150

125151
def compose(self) -> ComposeResult:
@@ -140,6 +166,7 @@ def on_mount(self) -> None:
140166
if self._arguments.colour_map is None
141167
else get_colour_map(self._arguments.colour_map),
142168
)
169+
self._remember()
143170

144171
@on(Mandelbrot.Plotted)
145172
def _update_situation(self, message: Mandelbrot.Plotted) -> None:
@@ -168,13 +195,27 @@ def _update_situation(self, message: Mandelbrot.Plotted) -> None:
168195
f"{message.elapsed:0.4f} seconds"
169196
)
170197

198+
def _remember(self) -> None:
199+
"""Remember the current situation."""
200+
plot = self.query_one(Mandelbrot)
201+
self._history.add(
202+
Situation(
203+
plot.x_position,
204+
plot.y_position,
205+
plot.zoom,
206+
plot.max_iteration,
207+
plot.multibrot,
208+
)
209+
)
210+
171211
def action_zoom(self, change: float) -> None:
172212
"""Change the zoom value.
173213
174214
Args:
175215
change: The amount to change the zoom by.
176216
"""
177217
self.query_one(Mandelbrot).zoom *= change
218+
self._remember()
178219

179220
def action_move(self, x: int, y: int) -> None:
180221
"""Move the plot in the indicated direction.
@@ -184,6 +225,7 @@ def action_move(self, x: int, y: int) -> None:
184225
y: The number of pixels to move in the Y direction.
185226
"""
186227
self.query_one(Mandelbrot).move(x, y)
228+
self._remember()
187229

188230
def action_iterate(self, change: int) -> None:
189231
"""Change the maximum iteration.
@@ -192,6 +234,7 @@ def action_iterate(self, change: int) -> None:
192234
change: The change to make to the maximum iterations.
193235
"""
194236
self.query_one(Mandelbrot).max_iteration += change
237+
self._remember()
195238

196239
def action_set_colour(self, colour_map: str) -> None:
197240
"""Set the colour map for the plot.
@@ -208,6 +251,7 @@ def action_multibrot(self, change: int) -> None:
208251
change: The change to make to the 'multibrot' value.
209252
"""
210253
self.query_one(Mandelbrot).multibrot += change
254+
self._remember()
211255

212256
def action_goto(self, x: int, y: int) -> None:
213257
"""Go to a specific location.
@@ -217,10 +261,12 @@ def action_goto(self, x: int, y: int) -> None:
217261
y: The Y location to go to.
218262
"""
219263
self.query_one(Mandelbrot).goto(x, y)
264+
self._remember()
220265

221266
def action_reset_command(self) -> None:
222267
"""Reset the plot to its default values."""
223268
self.query_one(Mandelbrot).reset()
269+
self._remember()
224270

225271
_VALID_LOCATION: Final[Pattern[str]] = compile(
226272
r"(?P<x>[^, ]+) *[, ] *(?P<y>[^, ]+)"
@@ -270,5 +316,19 @@ def action_copy_command_line_to_clipboard_command(self) -> None:
270316
self.app.copy_to_clipboard(command)
271317
self.notify(command, title="Copied")
272318

319+
def action_undo_command(self) -> None:
320+
"""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()
332+
273333

274334
### main.py ends here

0 commit comments

Comments
 (0)