peterbrittain / asciimatics

A cross platform package to do curses-like operations, plus higher level APIs and widgets to create text UIs and ASCII art animations
Apache License 2.0
3.61k stars 238 forks source link

Implementing Widget's Custom Color Palettes #373

Closed kyoto7250 closed 1 year ago

kyoto7250 commented 1 year ago

Is your feature request related to a problem? Please describe. When I try to change the color of the header of the MultiColumnListBox, but this behavior also changes the color of the title of the border.

Describe the solution you'd like I want to be able to pass a color palette to the Widget itself, and have access to a default if the desired color palette doesn't exist in the Widget.

Describe alternatives you've considered If I create a custom widget, the problem will be solved, but I think it's better to have it as a default feature.

related: https://github.com/peterbrittain/asciimatics/issues/208

Additional context My current understanding is that by doing register_frame on the Frame, the Widget is associated with the Frame, so I think it's difficult to change the custom color of only specific Widgets now.

minimal reproduction

from asciimatics.widgets import (
    Frame,
    Layout,
    MultiColumnListBox,
    Widget,
)
from asciimatics.scene import Scene
from asciimatics.screen import Screen

class DemoFrame(Frame):
    def __init__(self, screen) -> None:
        super(DemoFrame, self).__init__(
            screen,
            10,
            80,
            has_border=True,
            can_scroll=False,
            title="TITLE",
        )
        layout = Layout([50, 50], fill_frame=True)
        mc_list = MultiColumnListBox(
            Widget.FILL_FRAME,
            [10, 10],
            [(["1", "2"], 1)],
            titles=["A", "B"],
        )

        self.add_layout(layout)
        layout.add_widget(mc_list)
        # changed the color
        mc_list._frame.palette["title"] = (
            Screen.COLOUR_BLACK,
            Screen.A_NORMAL,
            Screen.COLOUR_WHITE,
        )
        self.fix()

def demo(screen) -> None:
    screen.play([Scene([DemoFrame(screen)], -1, name="title")])

Screen.wrapper(demo, catch_interrupt=False)

screenshot

スクリーンショット 2023-07-06 12 49 32

execution environment (for the future)

peterbrittain commented 1 year ago

Hi. The point of the colour palette was to ensure that there is a consistent colour scheme for each Frame to make it clear when things are selected/disabled/etc, so I'd rather not start extending that to be a per-widget concept if possible.

Overrides are available via the custom_colour property or (for some widgets like MultiColumnListBox) via a parser. They're not quite right here because:

But that does show a possible path forwards... Would it work for you if you could use the parser to set custom colours in the title?

peterbrittain commented 1 year ago

This should allow you to test it out (but is missing error handling, so is not the final patch)...

diff --git a/asciimatics/widgets/baselistbox.py b/asciimatics/widgets/baselistbox.py
index 58b932f..2c681fe 100644
--- a/asciimatics/widgets/baselistbox.py
+++ b/asciimatics/widgets/baselistbox.py
@@ -10,6 +10,7 @@ from abc import ABCMeta, abstractmethod, abstractproperty
 from future.utils import with_metaclass
 from asciimatics.event import KeyboardEvent, MouseEvent
 from asciimatics.screen import Screen
+from asciimatics.strings import ColouredText
 from asciimatics.widgets.widget import Widget
 from asciimatics.widgets.scrollbar import _ScrollBar

@@ -36,7 +37,7 @@ class _BaseListBox(with_metaclass(ABCMeta, Widget)):
         :param validator: Optional function to validate selection for this widget.
         """
         super(_BaseListBox, self).__init__(name)
-        self._titles = titles
+        self._titles = [ColouredText(x, parser) for x in titles]
         self._label = label
         self._parser = parser
         self._options = self._parse_options(options)
kyoto7250 commented 1 year ago

Sorry my late reply.

I'd rather not start extending that to be a per-widget concept if possible.

I got it.

Would it work for you if you could use the parser to set custom colours in the title?

I didn't know about a class called ColoredText. Thank you for teaching me. I will check it 👍🏽

peterbrittain commented 1 year ago

Should now work with https://github.com/peterbrittain/asciimatics/commit/48025bc9c14904b72a3c924d247bd6d04d1c57a3