textual-inputs icon indicating copy to clipboard operation
textual-inputs copied to clipboard

Add configurable dimensions and styles

Open chelnak opened this issue 4 years ago • 14 comments

This PR allows the consumer of an input widget to override the following properties when initialising a new instance.

  • width
  • height
  • style
  • border_style

Closes #3

chelnak avatar Nov 01 '21 10:11 chelnak

@chelnak I like where you're thinking 🧠 . Styling was the first thing I thought of after pushing the first version. I'll take a look at what you've got here. I'll take a look at what I already have and we'll collaborate on something.

sirfuzzalot avatar Dec 18 '21 02:12 sirfuzzalot

Hey sounds good 👍. The PR probably needs a bit of a refresh now but I'll wait for further instruction!

chelnak avatar Dec 18 '21 07:12 chelnak

I want to extend the scope of this PR. When I looked at styling this component I looked at css and the different states a component can be in, focus for example. I want developers to be able to change the styling based on changes in component state. I believe Will is working on a CSS engine for Textual, but the release timeline of that is unclear yet. Here's the two states I want us to tackle for this PR.

States

  • default
  • focus

Styling

  • cursor/caret
  • text
  • border
  • width
  • height

The solution should be designed in a way that more states can be added later with the developer simply adding configuration for a new state (once that state is supported by the component).

I had started with something like this below. It won't cover most of the styling points mentioned above, but gives you an idea of what the interface could look like, a class with options to set configuration for different states. Resolution for states that the developer opted not to customize. Then that single configuration object is passed in to the component.

class Element(Enum):
    CURSOR = "cursor"
    TEXT = "text"
    BORDER = "border"


class InputState(Enum):
    DEFAULT = "default"
    FOCUS = "focus"


class InputStyles:
    """
    Styles mapped to states
    """

    def __init__(
        self,
        default: Style,
        *,
        focus: Optional[Style] = None,
        hover: Optional[Style] = None,
        active: Optional[Style] = None,
        error: Optional[Style] = None,
    ) -> None:
        self.default = default
        self.focus = focus
        self.hover = hover
        self.active = active
        self.error = error

    def get_element_style(self, element: Element, state: InputState) -> Style:
        """Produces the style for the element in the given state"""
        ...

One note on the current PR. I do want to keep the title animation. However, I am open to a parameter that enable title_animation to be disabled.

Let me know if this is more then you'd want to take on or if something is unclear. Otherwise, have a go at the next version and we'll take a look when you're ready. Thanks for helping make this a better library! 😺

sirfuzzalot avatar Dec 19 '21 01:12 sirfuzzalot

This sounds like a nice solution. I'm more than happy to take it on.

I'll digest your design comments today and hopefully make a start this evening (GMT).

chelnak avatar Dec 19 '21 11:12 chelnak

Hey @sirfuzzalot.

I've had a play around this evening and pushed up a rough idea of where this pr could go.. let me know your thoughts. 👍

P.s.

What are your thoughts on code styling/linting for this project?

I've been using this config for a while now but appreciate everyone has different views when it comes to this stuff.

chelnak avatar Dec 19 '21 22:12 chelnak

P.s.

What are your thoughts on code styling/linting for this project?

I've been using this config for a while now but appreciate everyone has different views when it comes to this stuff.

Looks pretty similar to what's in the repo already. I like defaults on black, isort, and flake8. I had not heard of darglint, but have been missing some qa on my docstrings, so good find 👍 . I will check it out. I'm also open to using the pre-commit framework. I've heard a bunch about it, just haven't taken the leap yet to try it. For this PR though, we'll use existing .flake8, and pyproject.toml configs for linting/formatting.

sirfuzzalot avatar Dec 21 '21 01:12 sirfuzzalot

In the new CSS engine it might look something like this.

caret could also be cursor. CSS has a property called caret-color which is where I'm drawing that from. At the moment, caret is not supported on the outline branch css, nor does TextInput properly load on outline.

#username {
  text: white on #20639b;
  border: round white;
  height: 3;
}

#username.-focus {
  border: rounded bold white;
  caret: blink white on #20639b;
}
from textual.app import App
from textual.widget import Widget

from textual_inputs import TextInput


class BasicApp(App):
    """A basic app demonstrating CSS"""

    def on_load(self):
        """Bind keys here."""
        self.bind("tab", "toggle_class('#sidebar', '-active')")

    def on_mount(self):
        """Build layout here."""
        self.mount(
            header=Widget(),
            username=TextInput(title="Username", placeholder="enter your username..."),
            footer=Widget(),
            sidebar=Widget(),
        )


BasicApp.run(css_file="basic.css", watch_css=True, log="textual.log")

sirfuzzalot avatar Dec 31 '21 20:12 sirfuzzalot

Happy new year! Should have some more stuff for review soon!

chelnak avatar Jan 01 '22 09:01 chelnak

@sirfuzzalot Just pushed a few changes that enable some sort of configurable dimensions based on state.

I feel like there is a lot of code needed by the dev to produce a styled text box at the moment. What do you think?

chelnak avatar Jan 01 '22 20:01 chelnak

Thanks for the comments. I'll see what I can come up with.

Re the typing comments. I was using:

from __future__ import annotations

which should enable support for the newer style type hint evaluation from 3.7 but happy to revert to the older way for now.

chelnak avatar Jan 05 '22 16:01 chelnak

You may already be using this, but for others -> Setting a single style and border_style.

email = TextInput(title="Email")
email.style = Style(color="orange", bgcolor="blue")
email.border_style = Style(color="white", bgcolor="blue")

sirfuzzalot avatar Jan 10 '22 01:01 sirfuzzalot

Thanks for the comments. I'll see what I can come up with.

Re the typing comments. I was using:

from __future__ import annotations

which should enable support for the newer style type hint evaluation from 3.7 but happy to revert to the older way for now.

I think the issue is that FieldDimension = tuple[int, int] isn't treated as a type annotation, but rather an assignment. PEP585 demonstrates usage within a function signature.

line 56, in <module>
    FieldDimension = tuple[int, int]
TypeError: 'type' object is not subscriptable

sirfuzzalot avatar Jan 10 '22 02:01 sirfuzzalot

@chelnak the more Will works on the CSS engine the more I'm starting to think we're going to be better off waiting for the release of that major feature set. We may want to put this PR on hold. There will still likely be opportunity for styling that isn't covered by the engine, but we'll be better positioned to work on that when its released. What do you think? I appreciate all the time you've spent on this already 🙏.

sirfuzzalot avatar Feb 05 '22 18:02 sirfuzzalot

Hey! I completely agree. I was thinking the same too. Have been meaning to get back to this PR for a while now but have not had time.

chelnak avatar Feb 05 '22 20:02 chelnak

As you have undoubtedly seen, Textual now supports inputs natively, along with CSS. It was fun working on this with you however. Best wishes on your projects!

Inputs -> https://textual.textualize.io/widgets/input/ CSS -> https://textual.textualize.io/css_types/

sirfuzzalot avatar Mar 18 '23 19:03 sirfuzzalot