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 ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
the zoom level. ([#14](https://github.com/davep/complexitty/issues/14))
- Added `CopyCommandLineToClipboard`.
([#20](https://github.com/davep/complexitty/pull/20))
- Added the ability to undo a change in the plot.
([#21](https://github.com/davep/complexitty/pull/21))

## v0.2.0

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ authors = [
]
dependencies = [
"textual>=3.1.0",
"textual-enhanced>=0.11.0",
"textual-enhanced>=0.13.0",
"textual-canvas>=0.3.0",
"xdg-base-dirs>=6.0.2",
]
Expand Down
14 changes: 7 additions & 7 deletions requirements-dev.lock
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
-e file:.
aiohappyeyeballs==2.6.1
# via aiohttp
aiohttp==3.11.16
aiohttp==3.11.18
# via aiohttp-jinja2
# via textual-dev
# via textual-serve
Expand Down Expand Up @@ -42,12 +42,12 @@ distlib==0.3.9
# via virtualenv
filelock==3.18.0
# via virtualenv
frozenlist==1.5.0
frozenlist==1.6.0
# via aiohttp
# via aiosignal
ghp-import==2.1.0
# via mkdocs
identify==2.6.9
identify==2.6.10
# via pre-commit
idna==3.10
# via requests
Expand Down Expand Up @@ -91,11 +91,11 @@ multidict==6.4.3
# via aiohttp
# via yarl
mypy==1.15.0
mypy-extensions==1.0.0
mypy-extensions==1.1.0
# via mypy
nodeenv==1.9.1
# via pre-commit
packaging==24.2
packaging==25.0
# via mkdocs
paginate==0.5.7
# via mkdocs-material
Expand Down Expand Up @@ -132,7 +132,7 @@ rich==14.0.0
# via textual-serve
six==1.17.0
# via python-dateutil
textual==3.1.0
textual==3.1.1
# via complexitty
# via textual-canvas
# via textual-dev
Expand All @@ -141,7 +141,7 @@ textual==3.1.0
textual-canvas==0.4.0
# via complexitty
textual-dev==1.7.0
textual-enhanced==0.12.0
textual-enhanced==0.13.0
# via complexitty
textual-serve==1.1.2
# via textual-dev
Expand Down
4 changes: 2 additions & 2 deletions requirements.lock
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ pygments==2.19.1
# via rich
rich==14.0.0
# via textual
textual==3.1.0
textual==3.1.1
# via complexitty
# via textual-canvas
# via textual-enhanced
textual-canvas==0.4.0
# via complexitty
textual-enhanced==0.12.0
textual-enhanced==0.13.0
# via complexitty
typing-extensions==4.13.2
# via textual
Expand Down
3 changes: 2 additions & 1 deletion src/complexitty/commands/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
SetColourToShadesOfGreen,
SetColourToShadesOfRed,
)
from .main import CopyCommandLineToClipboard, Quit
from .main import CopyCommandLineToClipboard, Quit, Undo
from .navigation import (
GoMiddle,
GoTo,
Expand Down Expand Up @@ -68,6 +68,7 @@
"SetColourToShadesOfBlue",
"SetColourToShadesOfGreen",
"SetColourToShadesOfRed",
"Undo",
"ZeroZero",
"ZoomIn",
"ZoomInFaster",
Expand Down
7 changes: 7 additions & 0 deletions src/complexitty/commands/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,11 @@ class CopyCommandLineToClipboard(Command):
BINDING_KEY = "c"


##############################################################################
class Undo(Command):
"""Undo the latest change"""

BINDING_KEY = "backspace"


### main.py ends here
2 changes: 2 additions & 0 deletions src/complexitty/providers/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
SetColourToShadesOfBlue,
SetColourToShadesOfGreen,
SetColourToShadesOfRed,
Undo,
ZeroZero,
ZoomIn,
ZoomInFaster,
Expand Down Expand Up @@ -84,6 +85,7 @@ def commands(self) -> CommandHits:
yield SetColourToShadesOfBlue()
yield SetColourToShadesOfGreen()
yield SetColourToShadesOfRed()
yield Undo()
yield ZeroZero()
yield ZoomIn()
yield ZoomInFaster()
Expand Down
62 changes: 61 additions & 1 deletion src/complexitty/screens/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from argparse import Namespace
from math import floor, log10
from re import Pattern, compile
from typing import Final
from typing import Final, NamedTuple, TypeAlias

##############################################################################
# Textual imports.
Expand All @@ -18,6 +18,7 @@
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 @@ -48,6 +49,7 @@
SetColourToShadesOfBlue,
SetColourToShadesOfGreen,
SetColourToShadesOfRed,
Undo,
ZeroZero,
ZoomIn,
ZoomInFaster,
Expand All @@ -58,6 +60,27 @@
from ..providers import MainCommands


##############################################################################
class Situation(NamedTuple):
"""A class to hold a particular situation we can undo to."""

x_position: float
"""The X position in the plot."""
y_position: float
"""The Y position in the plot."""
zoom: float
"""The zoom level."""
max_iteration: int
"""The maximum iteration."""
multibrot: float
"""The multibrot setting."""


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


##############################################################################
class Main(EnhancedScreen[None]):
"""The main screen for the application."""
Expand Down Expand Up @@ -101,6 +124,7 @@ class Main(EnhancedScreen[None]):
SetColourToShadesOfBlue,
SetColourToShadesOfGreen,
SetColourToShadesOfRed,
Undo,
ZeroZero,
ZoomIn,
ZoomInFaster,
Expand All @@ -120,6 +144,8 @@ def __init__(self, arguments: Namespace) -> None:
"""
self._arguments = arguments
"""The command line arguments passed to the application."""
self._history = PlotHistory()
"""The plot situation history."""
super().__init__()

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

@on(Mandelbrot.Plotted)
def _update_situation(self, message: Mandelbrot.Plotted) -> None:
Expand Down Expand Up @@ -168,13 +195,27 @@ def _update_situation(self, message: Mandelbrot.Plotted) -> None:
f"{message.elapsed:0.4f} seconds"
)

def _remember(self) -> None:
"""Remember the current situation."""
plot = self.query_one(Mandelbrot)
self._history.add(
Situation(
plot.x_position,
plot.y_position,
plot.zoom,
plot.max_iteration,
plot.multibrot,
)
)

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()

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

def action_iterate(self, change: int) -> None:
"""Change the maximum iteration.
Expand All @@ -192,6 +234,7 @@ def action_iterate(self, change: int) -> None:
change: The change to make to the maximum iterations.
"""
self.query_one(Mandelbrot).max_iteration += change
self._remember()

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

def action_goto(self, x: int, y: int) -> None:
"""Go to a specific location.
Expand All @@ -217,10 +261,12 @@ def action_goto(self, x: int, y: int) -> None:
y: The Y location to go to.
"""
self.query_one(Mandelbrot).goto(x, y)
self._remember()

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

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

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()


### main.py ends here
Loading