diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4194ac0..0006b3a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Made radio buttons rounder in `--ascii-only` mode, using parentheses instead of square brackets.
+- Improved the appearance of the warning icon and question icon in `--ascii-only` mode with the dark theme, and made it update when toggling dark mode with Ctrl+D. (The question icon is used only when pasting content larger than the canvas.)
- Split up code files (especially the huge `paint.py`) into lots of smaller modules, and refactored a bunch of things.
### Added
diff --git a/src/textual_paint/icons.py b/src/textual_paint/icons.py
index 31ebba7..e15a412 100644
--- a/src/textual_paint/icons.py
+++ b/src/textual_paint/icons.py
@@ -16,15 +16,76 @@ Two nice things about embedding it are:
2. it's easier to dynamically modify them to remove the background color.
TODO: unify formats/authoring workflow?
-TODO: dynamic dark mode (I already have alternate versions of some icons)
"""
+from rich.console import RenderableType
+from rich.protocol import is_renderable
from rich.text import Text
+from textual.errors import RenderError
from textual.widgets import Static
from textual_paint.args import args
from textual_paint.localization.i18n import get as _
+
+def _check_renderable(renderable: object):
+ """Check if a renderable conforms to the Rich Console protocol
+ (https://rich.readthedocs.io/en/latest/protocol.html)
+
+ Args:
+ renderable: A potentially renderable object.
+
+ Raises:
+ RenderError: If the object can not be rendered.
+ """
+ if not is_renderable(renderable):
+ raise RenderError(
+ f"unable to render {renderable!r}; a string, Text, or other Rich renderable is required"
+ )
+
+class ThemedIcon(Static):
+ """A Static widget that changes its content based on the theme.
+
+ Args:
+ light_renderable: A Rich renderable, or string containing console markup, for the light theme.
+ dark_renderable: A Rich renderable, or string containing console markup, for the dark theme.
+ name: Name of widget.
+ id: ID of Widget.
+ classes: Space separated list of class names.
+ disabled: Whether the static is disabled or not.
+ """
+
+ def __init__(
+ self,
+ light_renderable: RenderableType,
+ dark_renderable: RenderableType,
+ *,
+ name: str | None = None,
+ id: str | None = None,
+ classes: str | None = None,
+ disabled: bool = False,
+ ):
+ """Initialize the icon."""
+ super().__init__("",
+ name=name,
+ id=id,
+ classes=classes,
+ disabled=disabled,
+ )
+ self.light_renderable = light_renderable
+ self.dark_renderable = dark_renderable
+ _check_renderable(light_renderable)
+ _check_renderable(dark_renderable)
+ self.watch(self.app, "dark", self._on_dark_changed, init=False)
+ self._on_dark_changed(False, self.app.dark)
+
+ def _on_dark_changed(self, old_value: bool, dark: bool) -> None:
+ if dark:
+ self.update(self.dark_renderable)
+ else:
+ self.update(self.light_renderable)
+
+
# ASCII line art version:
# get_warning_icon = lambda: Static("""[#ffff00]
# _
@@ -133,9 +194,10 @@ warning_icon_markup_ascii_dark_mode = """[#ffff00]
[/]"""
def get_warning_icon() -> Static:
- markup = warning_icon_markup_ascii if args.ascii_only else warning_icon_markup_unicode
- # TODO: Use warning_icon_markup_ascii_dark_mode for a less blocky looking outline in dark mode.
- return Static(markup, classes="warning_icon message_box_icon")
+ if args.ascii_only:
+ return ThemedIcon(warning_icon_markup_ascii, warning_icon_markup_ascii_dark_mode, classes="warning_icon message_box_icon")
+ else:
+ return Static(warning_icon_markup_unicode, classes="warning_icon message_box_icon")
# question_icon_ansi = ""
@@ -156,32 +218,6 @@ question_icon_console_markup = """
"""
# make background transparent
question_icon_console_markup = question_icon_console_markup.replace(" on rgb(128,128,128)", "")
-# class QuestionIcon(Static):
-# """A question mark icon."""
-#
-# def __init__(self) -> None:
-# """Initialize the icon."""
-# super().__init__("", classes="question_icon message_box_icon")
-# # This assertion fails.
-# # > type(self.app)
-# # .PaintApp'>
-# # > type(PaintApp())
-# #
-# # from paint import PaintApp
-# # assert isinstance(self.app, PaintApp), "QuestionIcon should be used in PaintApp, but got: " + repr(self.app)
-# self.watch(self.app, "dark", self._on_dark_changed, init=False)
-# self._on_dark_changed(False, self.app.dark)
-#
-# def _on_dark_changed(self, old_value: bool, dark: bool) -> None:
-# # tweak colors according to the theme
-# if dark:
-# # Never happens?
-# self.update(question_icon_console_markup.replace("rgb(0,0,0)", "rgb(255,0,255)"))
-# else:
-# self.update(question_icon_console_markup.replace("rgb(0,0,0)", "rgb(128,128,128)"))
-#
-# def get_question_icon() -> QuestionIcon:
-# return QuestionIcon()
# also the shadow is normally gray, I just drew it black because I was using gray as the background
@@ -201,9 +237,18 @@ question_icon_console_markup_ascii = question_icon_console_markup_ascii.replace(
# bold question mark
question_icon_console_markup_ascii = question_icon_console_markup_ascii.replace("?", "[b]?[/b]")
+# swap white and black, and brighten blue to cyan
+question_icon_console_markup_ascii_dark_mode = question_icon_console_markup_ascii.replace("rgb(0,0,0)", "rgb(255,0,255)").replace("rgb(255,255,255)", "rgb(0,0,0)").replace("rgb(255,0,255)", "rgb(255,255,255)").replace("rgb(0,0,255)", "rgb(0,255,255)")
+
def get_question_icon() -> Static:
- markup = question_icon_console_markup_ascii if args.ascii_only else question_icon_console_markup
- return Static(markup, classes="question_icon message_box_icon")
+ if args.ascii_only:
+ return ThemedIcon(
+ question_icon_console_markup_ascii,
+ question_icon_console_markup_ascii_dark_mode,
+ classes="question_icon message_box_icon",
+ )
+ else:
+ return Static(question_icon_console_markup, classes="question_icon message_box_icon")
paint_icon_console_markup = """
diff --git a/tests/__snapshots__/test_snapshots.ambr b/tests/__snapshots__/test_snapshots.ambr
index 61f69f6..a293169 100644
--- a/tests/__snapshots__/test_snapshots.ambr
+++ b/tests/__snapshots__/test_snapshots.ambr
@@ -4945,154 +4945,155 @@
font-weight: 700;
}
- .terminal-1235291690-matrix {
+ .terminal-1815055849-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1235291690-title {
+ .terminal-1815055849-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1235291690-r1 { fill: #c5c8c6 }
- .terminal-1235291690-r2 { fill: #608ab1;font-weight: bold }
- .terminal-1235291690-r3 { fill: #cc555a;font-weight: bold }
- .terminal-1235291690-r4 { fill: #d0b344;font-weight: bold }
- .terminal-1235291690-r5 { fill: #0d0d0d }
- .terminal-1235291690-r6 { fill: #dfdfdf }
- .terminal-1235291690-r7 { fill: #000000 }
- .terminal-1235291690-r8 { fill: #e3e3e3 }
- .terminal-1235291690-r9 { fill: #454a50 }
- .terminal-1235291690-r10 { fill: #e2e3e3 }
- .terminal-1235291690-r11 { fill: #000000;font-weight: bold }
- .terminal-1235291690-r12 { fill: #24292f;font-weight: bold }
- .terminal-1235291690-r13 { fill: #e5e5e5 }
- .terminal-1235291690-r14 { fill: #ff00ff;text-decoration: underline; }
- .terminal-1235291690-r15 { fill: #ff00ff }
- .terminal-1235291690-r16 { fill: #e2e3e3;font-style: italic;;text-decoration: underline; }
- .terminal-1235291690-r17 { fill: #cc555a;font-style: italic;;text-decoration: underline; }
- .terminal-1235291690-r18 { fill: #0080ff }
- .terminal-1235291690-r19 { fill: #00050f }
- .terminal-1235291690-r20 { fill: #e9e9ea }
- .terminal-1235291690-r21 { fill: #808040 }
- .terminal-1235291690-r22 { fill: #121212 }
- .terminal-1235291690-r23 { fill: #ffffff }
- .terminal-1235291690-r24 { fill: #565c62 }
- .terminal-1235291690-r25 { fill: #24292f }
+ .terminal-1815055849-r1 { fill: #c5c8c6 }
+ .terminal-1815055849-r2 { fill: #608ab1;font-weight: bold }
+ .terminal-1815055849-r3 { fill: #cc555a;font-weight: bold }
+ .terminal-1815055849-r4 { fill: #d0b344;font-weight: bold }
+ .terminal-1815055849-r5 { fill: #0d0d0d }
+ .terminal-1815055849-r6 { fill: #ffff00 }
+ .terminal-1815055849-r7 { fill: #e3e3e3 }
+ .terminal-1815055849-r8 { fill: #454a50 }
+ .terminal-1815055849-r9 { fill: #e2e3e3 }
+ .terminal-1815055849-r10 { fill: #ffff00;font-weight: bold }
+ .terminal-1815055849-r11 { fill: #24292f;font-weight: bold }
+ .terminal-1815055849-r12 { fill: #000000 }
+ .terminal-1815055849-r13 { fill: #dfdfdf }
+ .terminal-1815055849-r14 { fill: #e5e5e5 }
+ .terminal-1815055849-r15 { fill: #ff00ff;text-decoration: underline; }
+ .terminal-1815055849-r16 { fill: #ff00ff }
+ .terminal-1815055849-r17 { fill: #e2e3e3;font-style: italic;;text-decoration: underline; }
+ .terminal-1815055849-r18 { fill: #cc555a;font-style: italic;;text-decoration: underline; }
+ .terminal-1815055849-r19 { fill: #0080ff }
+ .terminal-1815055849-r20 { fill: #00050f }
+ .terminal-1815055849-r21 { fill: #e9e9ea }
+ .terminal-1815055849-r22 { fill: #808040 }
+ .terminal-1815055849-r23 { fill: #121212 }
+ .terminal-1815055849-r24 { fill: #ffffff }
+ .terminal-1815055849-r25 { fill: #565c62 }
+ .terminal-1815055849-r26 { fill: #24292f }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- Paint
+ Paint
-
-
-
- \|// \
- / \
- FileEditV/ ! \Hide Details
- (_______)______________
- Exception: Error Message Itself
- '::.::Test
- ____________
-
- /7H?
- ____________
-
- P,O
- ____________
-
- c==-E)=
-
- []
- []_______________
- []
- [_______]_______________
-
-
- For Help, cli
+
+
+
+ \|/ / \
+ / \
+ FileEditV/ ! \Hide Details
+ (_______)______________
+ Exception: Error Message Itself
+ '::.::Test
+ ____________
+
+ /7H?
+ ____________
+
+ P,O
+ ____________
+
+ c==-E)=
+
+ []
+ []_______________
+ []
+ [_______]_______________
+
+
+ For Help, cli
@@ -5643,217 +5644,217 @@
font-weight: 700;
}
- .terminal-3385497248-matrix {
+ .terminal-889104040-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3385497248-title {
+ .terminal-889104040-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3385497248-r1 { fill: #c5c8c6 }
- .terminal-3385497248-r2 { fill: #608ab1;font-weight: bold }
- .terminal-3385497248-r3 { fill: #cc555a;font-weight: bold }
- .terminal-3385497248-r4 { fill: #d0b344;font-weight: bold }
- .terminal-3385497248-r5 { fill: #e3e3e3 }
- .terminal-3385497248-r6 { fill: #989898 }
- .terminal-3385497248-r7 { fill: #e2e3e3 }
- .terminal-3385497248-r8 { fill: #454a50 }
- .terminal-3385497248-r9 { fill: #e5e5e5 }
- .terminal-3385497248-r10 { fill: #000000 }
- .terminal-3385497248-r11 { fill: #ff00ff;text-decoration: underline; }
- .terminal-3385497248-r12 { fill: #ff00ff }
- .terminal-3385497248-r13 { fill: #e2e3e3;font-style: italic;;text-decoration: underline; }
- .terminal-3385497248-r14 { fill: #cc555a;font-style: italic;;text-decoration: underline; }
- .terminal-3385497248-r15 { fill: #0080ff }
- .terminal-3385497248-r16 { fill: #0d0d0d }
- .terminal-3385497248-r17 { fill: #00050f }
- .terminal-3385497248-r18 { fill: #24292f }
- .terminal-3385497248-r19 { fill: #e9e9ea }
- .terminal-3385497248-r20 { fill: #808040 }
- .terminal-3385497248-r21 { fill: #ff0000;font-weight: bold }
- .terminal-3385497248-r22 { fill: #ffff00;font-weight: bold }
- .terminal-3385497248-r23 { fill: #ddedf9;font-weight: bold }
- .terminal-3385497248-r24 { fill: #e2e3e3;font-weight: bold }
- .terminal-3385497248-r25 { fill: #dfdfdf }
- .terminal-3385497248-r26 { fill: #0000ff;font-weight: bold }
- .terminal-3385497248-r27 { fill: #507bb3 }
- .terminal-3385497248-r28 { fill: #004578;font-weight: bold }
- .terminal-3385497248-r29 { fill: #001541 }
- .terminal-3385497248-r30 { fill: #121212 }
- .terminal-3385497248-r31 { fill: #ffffff }
- .terminal-3385497248-r32 { fill: #565c62 }
+ .terminal-889104040-r1 { fill: #c5c8c6 }
+ .terminal-889104040-r2 { fill: #608ab1;font-weight: bold }
+ .terminal-889104040-r3 { fill: #cc555a;font-weight: bold }
+ .terminal-889104040-r4 { fill: #d0b344;font-weight: bold }
+ .terminal-889104040-r5 { fill: #e3e3e3 }
+ .terminal-889104040-r6 { fill: #989898 }
+ .terminal-889104040-r7 { fill: #e2e3e3 }
+ .terminal-889104040-r8 { fill: #454a50 }
+ .terminal-889104040-r9 { fill: #e5e5e5 }
+ .terminal-889104040-r10 { fill: #000000 }
+ .terminal-889104040-r11 { fill: #ff00ff;text-decoration: underline; }
+ .terminal-889104040-r12 { fill: #ff00ff }
+ .terminal-889104040-r13 { fill: #e2e3e3;font-style: italic;;text-decoration: underline; }
+ .terminal-889104040-r14 { fill: #cc555a;font-style: italic;;text-decoration: underline; }
+ .terminal-889104040-r15 { fill: #0080ff }
+ .terminal-889104040-r16 { fill: #0d0d0d }
+ .terminal-889104040-r17 { fill: #00050f }
+ .terminal-889104040-r18 { fill: #24292f }
+ .terminal-889104040-r19 { fill: #e9e9ea }
+ .terminal-889104040-r20 { fill: #808040 }
+ .terminal-889104040-r21 { fill: #ff0000;font-weight: bold }
+ .terminal-889104040-r22 { fill: #ffff00;font-weight: bold }
+ .terminal-889104040-r23 { fill: #ddedf9;font-weight: bold }
+ .terminal-889104040-r24 { fill: #e2e3e3;font-weight: bold }
+ .terminal-889104040-r25 { fill: #ffffff }
+ .terminal-889104040-r26 { fill: #dfdfdf }
+ .terminal-889104040-r27 { fill: #00ffff;font-weight: bold }
+ .terminal-889104040-r28 { fill: #507bb3 }
+ .terminal-889104040-r29 { fill: #004578;font-weight: bold }
+ .terminal-889104040-r30 { fill: #001541 }
+ .terminal-889104040-r31 { fill: #121212 }
+ .terminal-889104040-r32 { fill: #565c62 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- Paint
+ Paint
-
-
-
- \|/Paint — Untitled
-
- FileEditViewImageColorsHelp
-
-
- '::.::
- ____________
-
- /7H?
- ____________
-
- P,O
- ____________
-
- c==-E)=::mm PaintX
- ____________
-
- H<)A _____ The image in the clipboard is
- ____________/ \larger than the bitmap.
- | ? |Would you like the bitmap enlarged?
- \S\__ __/
- ____________\|
- YesNoCancel
- [_]L______________________________
- ____________
-
- O{_}
- ____________
-
-
- []
- []________________________________________________________
- []
- [_______]________________________________________________________
-
-
- For Help, click Help Topics on the Help
+
+
+
+ \|/Paint — Untitled
+
+ FileEditViewImageColorsHelp
+
+
+ '::.::
+ ____________
+
+ /7H?
+ ____________
+
+ P,O
+ ____________
+
+ c==-E)=::mm PaintX
+ ____________
+
+ H<)A _____ The image in the clipboard is
+ ____________/ \larger than the bitmap.
+ | ? |Would you like the bitmap enlarged?
+ \S\__ __/
+ ____________\|
+ YesNoCancel
+ [_]L______________________________
+ ____________
+
+ O{_}
+ ____________
+
+
+ []
+ []________________________________________________________
+ []
+ [_______]________________________________________________________
+
+
+ For Help, click Help Topics on the Help
@@ -8979,159 +8980,160 @@
font-weight: 700;
}
- .terminal-3933108387-matrix {
+ .terminal-4141445308-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3933108387-title {
+ .terminal-4141445308-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3933108387-r1 { fill: #c5c8c6 }
- .terminal-3933108387-r2 { fill: #608ab1;font-weight: bold }
- .terminal-3933108387-r3 { fill: #cc555a;font-weight: bold }
- .terminal-3933108387-r4 { fill: #d0b344;font-weight: bold }
- .terminal-3933108387-r5 { fill: #e3e3e3 }
- .terminal-3933108387-r6 { fill: #989898 }
- .terminal-3933108387-r7 { fill: #e2e3e3 }
- .terminal-3933108387-r8 { fill: #454a50 }
- .terminal-3933108387-r9 { fill: #e5e5e5 }
- .terminal-3933108387-r10 { fill: #ffffff }
- .terminal-3933108387-r11 { fill: #000000 }
- .terminal-3933108387-r12 { fill: #0d0d0d }
- .terminal-3933108387-r13 { fill: #24292f }
- .terminal-3933108387-r14 { fill: #ff00ff;text-decoration: underline; }
- .terminal-3933108387-r15 { fill: #ff00ff }
- .terminal-3933108387-r16 { fill: #e2e3e3;font-style: italic;;text-decoration: underline; }
- .terminal-3933108387-r17 { fill: #ddedf9;font-weight: bold }
- .terminal-3933108387-r18 { fill: #e2e3e3;font-weight: bold }
- .terminal-3933108387-r19 { fill: #cc555a;font-style: italic;;text-decoration: underline; }
- .terminal-3933108387-r20 { fill: #0080ff }
- .terminal-3933108387-r21 { fill: #dfdfdf }
- .terminal-3933108387-r22 { fill: #00050f }
- .terminal-3933108387-r23 { fill: #507bb3 }
- .terminal-3933108387-r24 { fill: #e9e9ea }
- .terminal-3933108387-r25 { fill: #808040 }
- .terminal-3933108387-r26 { fill: #000000;font-weight: bold }
- .terminal-3933108387-r27 { fill: #004578;font-weight: bold }
- .terminal-3933108387-r28 { fill: #001541 }
- .terminal-3933108387-r29 { fill: #121212 }
- .terminal-3933108387-r30 { fill: #565c62 }
+ .terminal-4141445308-r1 { fill: #c5c8c6 }
+ .terminal-4141445308-r2 { fill: #608ab1;font-weight: bold }
+ .terminal-4141445308-r3 { fill: #cc555a;font-weight: bold }
+ .terminal-4141445308-r4 { fill: #d0b344;font-weight: bold }
+ .terminal-4141445308-r5 { fill: #e3e3e3 }
+ .terminal-4141445308-r6 { fill: #989898 }
+ .terminal-4141445308-r7 { fill: #e2e3e3 }
+ .terminal-4141445308-r8 { fill: #454a50 }
+ .terminal-4141445308-r9 { fill: #e5e5e5 }
+ .terminal-4141445308-r10 { fill: #ffffff }
+ .terminal-4141445308-r11 { fill: #000000 }
+ .terminal-4141445308-r12 { fill: #0d0d0d }
+ .terminal-4141445308-r13 { fill: #24292f }
+ .terminal-4141445308-r14 { fill: #ff00ff;text-decoration: underline; }
+ .terminal-4141445308-r15 { fill: #ff00ff }
+ .terminal-4141445308-r16 { fill: #e2e3e3;font-style: italic;;text-decoration: underline; }
+ .terminal-4141445308-r17 { fill: #ddedf9;font-weight: bold }
+ .terminal-4141445308-r18 { fill: #e2e3e3;font-weight: bold }
+ .terminal-4141445308-r19 { fill: #cc555a;font-style: italic;;text-decoration: underline; }
+ .terminal-4141445308-r20 { fill: #0080ff }
+ .terminal-4141445308-r21 { fill: #ffff00 }
+ .terminal-4141445308-r22 { fill: #dfdfdf }
+ .terminal-4141445308-r23 { fill: #00050f }
+ .terminal-4141445308-r24 { fill: #507bb3 }
+ .terminal-4141445308-r25 { fill: #e9e9ea }
+ .terminal-4141445308-r26 { fill: #808040 }
+ .terminal-4141445308-r27 { fill: #ffff00;font-weight: bold }
+ .terminal-4141445308-r28 { fill: #004578;font-weight: bold }
+ .terminal-4141445308-r29 { fill: #001541 }
+ .terminal-4141445308-r30 { fill: #121212 }
+ .terminal-4141445308-r31 { fill: #565c62 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- Paint
+ Paint
-
-
-
- \|/Paint — Untitled
-
- FileEditViewImageColorsHelp
-
-
- '::.::
- ____________
-
- /7H?PaintX
- ____________
-
- P,O_Save changes to Untitled?
- ____________/ \
- / \
- c==-E)=/ ! \YesNoCancel
- (_______)______________________________
- []
- []_______________
- []
- [_______]________________________________________________________
-
-
- For Help, click Help Topics on the Help
+
+
+
+ \|/Paint — Untitled
+
+ FileEditViewImageColorsHelp
+
+
+ '::.::
+ ____________
+
+ /7H?PaintX
+ ____________
+
+ P,O _Save changes to Untitled?
+ ____________ / \
+ / \
+ c==-E)= / ! \YesNoCancel
+ (_______)______________________________
+ []
+ []_______________
+ []
+ [_______]________________________________________________________
+
+
+ For Help, click Help Topics on the Help