textual-inputs
textual-inputs copied to clipboard
Add configurable dimensions and styles
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 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.
Hey sounds good 👍. The PR probably needs a bit of a refresh now but I'll wait for further instruction!
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! 😺
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).
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.
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.
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")
Happy new year! Should have some more stuff for review soon!
@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?
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.
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")
Thanks for the comments. I'll see what I can come up with.
Re the typing comments. I was using:
from __future__ import annotationswhich 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
@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 🙏.
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.
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/