Skip to content

Commit 32e852b

Browse files
committed
keys and playground
1 parent 4e03fea commit 32e852b

File tree

3 files changed

+41
-10
lines changed

3 files changed

+41
-10
lines changed

Diff for: docs/guide/content.md

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Content
22

3-
Custom widgets will typically implement a [`render()`][textual.widget.Widget.render] method which returns the *content* of the widget.
4-
In other words, the output that will be displayed within the widget's borders.
3+
The *content* of widget (displayed within the widget's borders) is typically specified in a call to [`Static.update`][textual.widgets.static.Static.update] or returned from [`render()`][textual.widget.Widget.render] in the case of [custom widgets](./widgets.md#custom-widgets).
4+
55
There are a few ways for you to specify this content.
66

77
- Text — either plain text, or [markup](#markup).
@@ -13,6 +13,7 @@ In this chapter, we will cover all these methods.
1313
## Markup
1414

1515
When building a custom widget you can embed color and style information in the string returned from the Widget's [`render()`][textual.widget.Widget.render] method.
16+
Markup is specified as a string which contains
1617
Text enclosed in square brackets (`[]`) won't appear in the output, but will modify the style of the text that follows.
1718
This is known as *Textual markup*.
1819

@@ -380,7 +381,7 @@ class WelcomeWidget(Widget):
380381

381382
While this is straightforward and intuitive, it can potentially break in subtle ways.
382383
If the 'name' variable contains square brackets, these may be interpreted as markup.
383-
For instance if the user entered their name at some point as "[magenta italic underline]Will is Cool!" then your app will display those styles where you didn't intend them to be.
384+
For instance if the user entered their name at some point as "[magenta italic]Will" then your app will display those styles where you didn't intend them to be.
384385

385386
We can avoid this problem by relying on the [Content.from_markup][textual.content.Content.from_markup] method to insert the variables for us.
386387
If you supply variables as keyword arguments, these will be substituted in the markup using the same syntax as [string.Template](https://docs.python.org/3/library/string.html#template-strings).
@@ -396,7 +397,7 @@ You can experiment with this feature by entering a dictionary of variables in th
396397

397398
Here's what that looks like:
398399

399-
```{.textual path="docs/examples/guide/content/playground.py" lines=16 type="hello [bold]$name[/bold]!\t{'name': '[magenta italic underline]Will is Cool!'}"]}
400+
```{.textual path="docs/examples/guide/content/playground.py" lines=20 columns=100 type='hello [bold]$name[/bold]!\t{"name": "[magenta italic]Will"}\t']}
400401
```
401402

402403
## Rich renderables

Diff for: src/textual/_markup_playground.py

+27-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import json
22

3-
from textual import containers, on
3+
from textual import containers, events, on
44
from textual.app import App, ComposeResult
55
from textual.content import Content
66
from textual.reactive import reactive
@@ -14,22 +14,29 @@ class MarkupPlayground(App):
1414
Screen {
1515
& > * {
1616
margin: 0 1;
17+
height: 1fr;
1718
}
1819
layout: vertical;
1920
#editor {
2021
width: 2fr;
2122
height: 1fr;
22-
border: tab $primary;
23+
border: tab $foreground 50%;
2324
padding: 1;
2425
margin: 1 1 0 0;
26+
&:focus {
27+
border: tab $primary;
28+
}
2529
2630
}
2731
#variables {
2832
width: 1fr;
2933
height: 1fr;
30-
border: tab $primary;
34+
border: tab $foreground 50%;
3135
padding: 1;
3236
margin: 1 0 0 1;
37+
&:focus {
38+
border: tab $primary;
39+
}
3340
}
3441
#variables.-bad-json {
3542
border: tab $error;
@@ -52,13 +59,15 @@ class MarkupPlayground(App):
5259
variables: reactive[dict[str, object]] = reactive({})
5360

5461
def compose(self) -> ComposeResult:
55-
with containers.HorizontalScroll():
62+
with containers.HorizontalGroup():
5663
yield (editor := TextArea(id="editor"))
5764
yield (variables := TextArea("", id="variables", language="json"))
5865
editor.border_title = "Markup"
5966
variables.border_title = "Variables (JSON)"
6067

61-
with containers.VerticalScroll(id="results-container") as container:
68+
with containers.VerticalScroll(
69+
id="results-container", can_focus=False
70+
) as container:
6271
yield Static(id="results")
6372
container.border_title = "Output"
6473

@@ -88,6 +97,18 @@ def watch_variables(self, variables: dict[str, object]) -> None:
8897

8998
@on(TextArea.Changed, "#variables")
9099
def on_variables_change(self, event: TextArea.Changed) -> None:
100+
variables_text_area = self.query_one("#variables", TextArea)
101+
try:
102+
variables = json.loads(variables_text_area.text)
103+
except Exception as error:
104+
variables_text_area.add_class("-bad-json")
105+
self.variables = {}
106+
else:
107+
variables_text_area.remove_class("-bad-json")
108+
self.variables = variables
109+
110+
@on(events.DescendantBlur, "#variables")
111+
def on_variables_blur(self) -> None:
91112
variables_text_area = self.query_one("#variables", TextArea)
92113
try:
93114
variables = json.loads(variables_text_area.text)
@@ -97,4 +118,5 @@ def on_variables_change(self, event: TextArea.Changed) -> None:
97118
variables_text_area.add_class("-bad-json")
98119
else:
99120
variables_text_area.remove_class("-bad-json")
121+
variables_text_area.text = json.dumps(variables, indent=4)
100122
self.variables = variables

Diff for: src/textual/keys.py

+9-1
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,9 @@ def value(self) -> str:
270270
}
271271

272272

273+
ASCII_KEY_NAMES = {"\t": "tab"}
274+
275+
273276
def _get_unicode_name_from_key(key: str) -> str:
274277
"""Get the best guess for the Unicode name of the char corresponding to the key.
275278
@@ -341,7 +344,12 @@ def _character_to_key(character: str) -> str:
341344
This transformation can be undone by the function `_get_unicode_name_from_key`.
342345
"""
343346
if not character.isalnum():
344-
key = unicodedata.name(character).lower().replace("-", "_").replace(" ", "_")
347+
try:
348+
key = (
349+
unicodedata.name(character).lower().replace("-", "_").replace(" ", "_")
350+
)
351+
except ValueError:
352+
key = ASCII_KEY_NAMES.get(character, character)
345353
else:
346354
key = character
347355
key = KEY_NAME_REPLACEMENTS.get(key, key)

0 commit comments

Comments
 (0)