mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
added char attribute to Text event
This commit is contained in:
@@ -62,7 +62,7 @@ class FileSearchApp(App):
|
|||||||
self.file_table.filter = event.value
|
self.file_table.filter = event.value
|
||||||
|
|
||||||
|
|
||||||
app = FileSearchApp(log_path="textual.log", css_path="file_search.scss", watch_css=True)
|
app = FileSearchApp(css_path="file_search.scss", watch_css=True)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
result = app.run()
|
result = app.run()
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
Screen {
|
Screen {
|
||||||
layout: dock;
|
|
||||||
docks: top=top bottom=bottom;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#file_table_wrapper {
|
#file_table_wrapper {
|
||||||
|
|||||||
17
sandbox/will/input.py
Normal file
17
sandbox/will/input.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
from textual.app import App
|
||||||
|
from textual.widgets import TextInput
|
||||||
|
|
||||||
|
|
||||||
|
class InputApp(App):
|
||||||
|
|
||||||
|
CSS = """
|
||||||
|
TextInput {
|
||||||
|
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
def compose(self):
|
||||||
|
yield TextInput(initial="foo")
|
||||||
|
|
||||||
|
|
||||||
|
app = InputApp()
|
||||||
@@ -101,7 +101,7 @@ class XTermParser(Parser[events.Event]):
|
|||||||
key_events = sequence_to_key_events(character)
|
key_events = sequence_to_key_events(character)
|
||||||
for event in key_events:
|
for event in key_events:
|
||||||
if event.key == "escape":
|
if event.key == "escape":
|
||||||
event = events.Key(event.sender, key="^")
|
event = events.Key(event.sender, "^", None)
|
||||||
on_token(event)
|
on_token(event)
|
||||||
|
|
||||||
while not self.is_eof:
|
while not self.is_eof:
|
||||||
@@ -229,7 +229,21 @@ class XTermParser(Parser[events.Event]):
|
|||||||
on_token(event)
|
on_token(event)
|
||||||
|
|
||||||
def _sequence_to_key_events(self, sequence: str) -> Iterable[events.Key]:
|
def _sequence_to_key_events(self, sequence: str) -> Iterable[events.Key]:
|
||||||
default = (sequence,) if len(sequence) == 1 else ()
|
"""Map a sequence of code points on to a sequence of keys.
|
||||||
keys = ANSI_SEQUENCES_KEYS.get(sequence, default)
|
|
||||||
for key in keys:
|
Args:
|
||||||
yield events.Key(self.sender, key)
|
sequence (str): Sequence of code points.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Iterable[events.Key]: keys
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
keys = ANSI_SEQUENCES_KEYS.get(sequence)
|
||||||
|
if keys is not None:
|
||||||
|
for key in keys:
|
||||||
|
yield events.Key(
|
||||||
|
self.sender, key.value, sequence if len(sequence) == 1 else None
|
||||||
|
)
|
||||||
|
elif len(sequence) == 1:
|
||||||
|
yield events.Key(self.sender, sequence, sequence)
|
||||||
|
|||||||
@@ -656,7 +656,9 @@ class App(Generic[ReturnType], DOMNode):
|
|||||||
await asyncio.sleep(0.05)
|
await asyncio.sleep(0.05)
|
||||||
else:
|
else:
|
||||||
print(f"press {key!r}")
|
print(f"press {key!r}")
|
||||||
driver.send_event(events.Key(self, key))
|
driver.send_event(
|
||||||
|
events.Key(self, key, key if len(key) == 1 else None)
|
||||||
|
)
|
||||||
await asyncio.sleep(0.01)
|
await asyncio.sleep(0.01)
|
||||||
if screenshot:
|
if screenshot:
|
||||||
self._screenshot = self.export_screenshot(
|
self._screenshot = self.export_screenshot(
|
||||||
|
|||||||
@@ -188,18 +188,26 @@ class Key(InputEvent):
|
|||||||
"""Sent when the user hits a key on the keyboard.
|
"""Sent when the user hits a key on the keyboard.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
sender (MessageTarget): The sender of the event (the App)
|
sender (MessageTarget): The sender of the event (the App).
|
||||||
key (str): The pressed key if a single character (or a longer string for special characters)
|
key (str): A key name (textual.keys.Keys).
|
||||||
|
char (str | None, optional): A printable character or None if it is not printable.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = ["key"]
|
__slots__ = ["key", "char"]
|
||||||
|
|
||||||
def __init__(self, sender: MessageTarget, key: str) -> None:
|
def __init__(self, sender: MessageTarget, key: str, char: str | None) -> None:
|
||||||
super().__init__(sender)
|
super().__init__(sender)
|
||||||
self.key = key.value if isinstance(key, Keys) else key
|
self.key = key
|
||||||
|
self.char = (key if len(key) == 1 else None) if char is None else char
|
||||||
|
|
||||||
def __rich_repr__(self) -> rich.repr.Result:
|
def __rich_repr__(self) -> rich.repr.Result:
|
||||||
yield "key", self.key
|
yield "key", self.key
|
||||||
|
yield "char", self.char, None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def key_name(self) -> str | None:
|
||||||
|
"""Name of a key suitable for use as a Python identifier."""
|
||||||
|
return self.key.replace("+", "_")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_printable(self) -> bool:
|
def is_printable(self) -> bool:
|
||||||
@@ -209,7 +217,7 @@ class Key(InputEvent):
|
|||||||
Returns:
|
Returns:
|
||||||
bool: True if the key is printable.
|
bool: True if the key is printable.
|
||||||
"""
|
"""
|
||||||
return self.key == Keys.Space or self.key not in KEY_VALUES
|
return False if self.char is None else self.char.isprintable()
|
||||||
|
|
||||||
|
|
||||||
@rich.repr.auto
|
@rich.repr.auto
|
||||||
|
|||||||
@@ -517,7 +517,7 @@ class MessagePump(metaclass=MessagePumpMeta):
|
|||||||
Args:
|
Args:
|
||||||
event (events.Key): A key event.
|
event (events.Key): A key event.
|
||||||
"""
|
"""
|
||||||
key_method = getattr(self, f"key_{event.key}", None)
|
key_method = getattr(self, f"key_{event.key_name}", None)
|
||||||
if key_method is not None:
|
if key_method is not None:
|
||||||
if await invoke(key_method, event):
|
if await invoke(key_method, event):
|
||||||
event.prevent_default()
|
event.prevent_default()
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ __all__ = [
|
|||||||
"Placeholder",
|
"Placeholder",
|
||||||
"Pretty",
|
"Pretty",
|
||||||
"Static",
|
"Static",
|
||||||
|
"TextInput",
|
||||||
"TreeControl",
|
"TreeControl",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
# This stub file must re-export every classes exposed in the __init__.py's `__all__` list:
|
# This stub file must re-export every classes exposed in the __init__.py's `__all__` list:
|
||||||
from ._button import Button as Button
|
from ._button import Button as Button
|
||||||
from ._data_table import DataTable
|
from ._data_table import DataTable as DataTable
|
||||||
from ._directory_tree import DirectoryTree as DirectoryTree
|
from ._directory_tree import DirectoryTree as DirectoryTree
|
||||||
from ._footer import Footer as Footer
|
from ._footer import Footer as Footer
|
||||||
from ._header import Header as Header
|
from ._header import Header as Header
|
||||||
from ._placeholder import Placeholder as Placeholder
|
from ._placeholder import Placeholder as Placeholder
|
||||||
from ._pretty import Pretty as Pretty
|
from ._pretty import Pretty as Pretty
|
||||||
from ._static import Static as Static
|
from ._static import Static as Static
|
||||||
|
from ._text_input import TextInput as TextInput
|
||||||
from ._tree_control import TreeControl as TreeControl
|
from ._tree_control import TreeControl as TreeControl
|
||||||
|
|||||||
@@ -40,12 +40,9 @@ class TextWidgetBase(Widget):
|
|||||||
key = event.key
|
key = event.key
|
||||||
if key == "escape":
|
if key == "escape":
|
||||||
return
|
return
|
||||||
elif key == "space":
|
|
||||||
key = " "
|
|
||||||
|
|
||||||
changed = False
|
changed = False
|
||||||
if event.is_printable:
|
if event.char is not None and event.is_printable:
|
||||||
changed = self._editor.insert(key)
|
changed = self._editor.insert(event.char)
|
||||||
elif key == "ctrl+h":
|
elif key == "ctrl+h":
|
||||||
changed = self._editor.delete_back()
|
changed = self._editor.delete_back()
|
||||||
elif key == "ctrl+d":
|
elif key == "ctrl+d":
|
||||||
@@ -59,11 +56,11 @@ class TextWidgetBase(Widget):
|
|||||||
elif key == "end" or key == "ctrl+e":
|
elif key == "end" or key == "ctrl+e":
|
||||||
self._editor.cursor_text_end()
|
self._editor.cursor_text_end()
|
||||||
|
|
||||||
|
self.refresh(layout=True)
|
||||||
|
|
||||||
if changed:
|
if changed:
|
||||||
self.post_message_no_wait(self.Changed(self, value=self._editor.content))
|
self.post_message_no_wait(self.Changed(self, value=self._editor.content))
|
||||||
|
|
||||||
self.refresh(layout=True)
|
|
||||||
|
|
||||||
def _apply_cursor_to_text(self, display_text: Text, index: int) -> Text:
|
def _apply_cursor_to_text(self, display_text: Text, index: int) -> Text:
|
||||||
if index < 0:
|
if index < 0:
|
||||||
return display_text
|
return display_text
|
||||||
@@ -115,10 +112,9 @@ class TextInput(TextWidgetBase, can_focus=True):
|
|||||||
DEFAULT_CSS = """
|
DEFAULT_CSS = """
|
||||||
TextInput {
|
TextInput {
|
||||||
width: auto;
|
width: auto;
|
||||||
background: $surface;
|
|
||||||
height: 3;
|
height: 3;
|
||||||
padding: 0 1;
|
padding: 1;
|
||||||
content-align: left middle;
|
background: $surface;
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -165,11 +161,15 @@ class TextInput(TextWidgetBase, can_focus=True):
|
|||||||
self._editor.cursor_text_end()
|
self._editor.cursor_text_end()
|
||||||
self.refresh()
|
self.refresh()
|
||||||
|
|
||||||
def on_resize(self, event: events.Resize) -> None:
|
def get_content_width(self, container: Size, viewport: Size) -> int:
|
||||||
|
# TODO: Why does this need +2 ?
|
||||||
|
return min(cell_len(self._editor.content) + 2, container.width)
|
||||||
|
|
||||||
|
def _on_resize(self, event: events.Resize) -> None:
|
||||||
# Ensure the cursor remains visible when the widget is resized
|
# Ensure the cursor remains visible when the widget is resized
|
||||||
self._reset_visible_range()
|
self._reset_visible_range()
|
||||||
|
|
||||||
def on_click(self, event: events.Click) -> None:
|
def _on_click(self, event: events.Click) -> None:
|
||||||
"""When the user clicks on the text input, the cursor moves to the
|
"""When the user clicks on the text input, the cursor moves to the
|
||||||
character that was clicked on. Double-width characters makes this more
|
character that was clicked on. Double-width characters makes this more
|
||||||
difficult."""
|
difficult."""
|
||||||
Reference in New Issue
Block a user