mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Merge branch 'main' into add-containers
This commit is contained in:
12
CHANGELOG.md
12
CHANGELOG.md
@@ -20,6 +20,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
- Added `Middle` https://github.com/Textualize/textual/issues/1957
|
||||
- Added `VerticalScroll` (mimicking the old behaviour of `Vertical`) https://github.com/Textualize/textual/issues/1957
|
||||
|
||||
|
||||
## [0.15.1] - 2023-03-14
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed how the namespace for messages is calculated to facilitate inheriting messages https://github.com/Textualize/textual/issues/1814
|
||||
- `Tab` is now correctly made available from `textual.widgets`. https://github.com/Textualize/textual/issues/2044
|
||||
|
||||
## [0.15.0] - 2023-03-13
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed container not resizing when a widget is removed https://github.com/Textualize/textual/issues/2007
|
||||
@@ -603,6 +613,8 @@ https://textual.textualize.io/blog/2022/11/08/version-040/#version-040
|
||||
- New handler system for messages that doesn't require inheritance
|
||||
- Improved traceback handling
|
||||
|
||||
[0.15.1]: https://github.com/Textualize/textual/compare/v0.15.0...v0.15.1
|
||||
[0.15.0]: https://github.com/Textualize/textual/compare/v0.14.0...v0.15.0
|
||||
[0.14.0]: https://github.com/Textualize/textual/compare/v0.13.0...v0.14.0
|
||||
[0.13.0]: https://github.com/Textualize/textual/compare/v0.12.1...v0.13.0
|
||||
[0.12.1]: https://github.com/Textualize/textual/compare/v0.12.0...v0.12.1
|
||||
|
||||
@@ -35,7 +35,7 @@ Additionally, we've simplified constructing messages classes. Previously all mes
|
||||
So prior to 0.14.0 you might have posted messages like the following:
|
||||
|
||||
```python
|
||||
async self.post_message(self.Changed(self, item=self.item))
|
||||
await self.post_message(self.Changed(self, item=self.item))
|
||||
```
|
||||
|
||||
You can now replace it with this simpler function call:
|
||||
|
||||
@@ -68,10 +68,10 @@ dock: top; /* Docks on the top edge of the parent container. */
|
||||
## Python
|
||||
|
||||
```python
|
||||
widget.styles.dock = bottom; # Dock bottom.
|
||||
widget.styles.dock = left; # Dock left.
|
||||
widget.styles.dock = right; # Dock right.
|
||||
widget.styles.dock = top; # Dock top.
|
||||
widget.styles.dock = "bottom" # Dock bottom.
|
||||
widget.styles.dock = "left" # Dock left.
|
||||
widget.styles.dock = "right" # Dock right.
|
||||
widget.styles.dock = "top" # Dock top.
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "textual"
|
||||
version = "0.15.0"
|
||||
version = "0.15.1"
|
||||
homepage = "https://github.com/Textualize/textual"
|
||||
description = "Modern Text User Interface framework"
|
||||
authors = ["Will McGugan <will@textualize.io>"]
|
||||
|
||||
@@ -62,7 +62,7 @@ class MessagePumpMeta(type):
|
||||
isclass = inspect.isclass
|
||||
for value in class_dict.values():
|
||||
if isclass(value) and issubclass(value, Message):
|
||||
if not value.namespace:
|
||||
if "namespace" not in value.__dict__:
|
||||
value.namespace = namespace
|
||||
class_obj = super().__new__(cls, name, bases, class_dict, **kwargs)
|
||||
return class_obj
|
||||
|
||||
@@ -14,9 +14,6 @@ class Checkbox(ToggleButton):
|
||||
This message can be handled using an `on_checkbox_changed` method.
|
||||
"""
|
||||
|
||||
# https://github.com/Textualize/textual/issues/1814
|
||||
namespace = "checkbox"
|
||||
|
||||
@property
|
||||
def checkbox(self) -> Checkbox:
|
||||
"""The checkbox that was changed."""
|
||||
|
||||
@@ -21,9 +21,6 @@ class RadioButton(ToggleButton):
|
||||
This message can be handled using an `on_radio_button_changed` method.
|
||||
"""
|
||||
|
||||
# https://github.com/Textualize/textual/issues/1814
|
||||
namespace = "radio_button"
|
||||
|
||||
@property
|
||||
def radio_button(self) -> RadioButton:
|
||||
"""The radio button that was changed."""
|
||||
|
||||
3
src/textual/widgets/_tab.py
Normal file
3
src/textual/widgets/_tab.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from ._tabs import Tab
|
||||
|
||||
__all__ = ["Tab"]
|
||||
File diff suppressed because one or more lines are too long
@@ -1,19 +1,6 @@
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.containers import VerticalScroll, Horizontal
|
||||
from textual.widgets import (
|
||||
Header,
|
||||
Footer,
|
||||
Button,
|
||||
DataTable,
|
||||
Input,
|
||||
ListView,
|
||||
ListItem,
|
||||
Label,
|
||||
Markdown,
|
||||
MarkdownViewer,
|
||||
Tree,
|
||||
TextLog,
|
||||
)
|
||||
from textual.containers import Horizontal
|
||||
from textual.widgets import Button
|
||||
|
||||
|
||||
class WidgetDisableTestApp(App[None]):
|
||||
@@ -21,62 +8,26 @@ class WidgetDisableTestApp(App[None]):
|
||||
Horizontal {
|
||||
height: auto;
|
||||
}
|
||||
DataTable, ListView, Tree, TextLog {
|
||||
height: 2;
|
||||
}
|
||||
|
||||
Markdown, MarkdownViewer {
|
||||
height: 1fr;
|
||||
Button {
|
||||
width: 1fr;
|
||||
}
|
||||
"""
|
||||
|
||||
@property
|
||||
def data_table(self) -> DataTable:
|
||||
data_table = DataTable[str]()
|
||||
data_table.add_columns("Column 1", "Column 2", "Column 3", "Column 4")
|
||||
data_table.add_rows(
|
||||
[(str(n), str(n * 10), str(n * 100), str(n * 1000)) for n in range(100)]
|
||||
)
|
||||
return data_table
|
||||
|
||||
@property
|
||||
def list_view(self) -> ListView:
|
||||
return ListView(*[ListItem(Label(f"This is list item {n}")) for n in range(20)])
|
||||
|
||||
@property
|
||||
def test_tree(self) -> Tree:
|
||||
tree = Tree[None](label="This is a test tree")
|
||||
for n in range(10):
|
||||
tree.root.add_leaf(f"Leaf {n}")
|
||||
tree.root.expand()
|
||||
return tree
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Header()
|
||||
yield VerticalScroll(
|
||||
Horizontal(
|
||||
Button(),
|
||||
Button(variant="primary"),
|
||||
Button(variant="success"),
|
||||
Button(variant="warning"),
|
||||
Button(variant="error"),
|
||||
),
|
||||
self.data_table,
|
||||
self.list_view,
|
||||
self.test_tree,
|
||||
TextLog(),
|
||||
Input(),
|
||||
Input(placeholder="This is an empty input with a placeholder"),
|
||||
Input("This is some text in an input"),
|
||||
Markdown("# Hello, World!"),
|
||||
MarkdownViewer("# Hello, World!"),
|
||||
id="test-container",
|
||||
)
|
||||
yield Footer()
|
||||
|
||||
def on_mount(self) -> None:
|
||||
self.query_one(TextLog).write("Hello, World!")
|
||||
self.query_one("#test-container", VerticalScroll).disabled = True
|
||||
for _ in range(4):
|
||||
with Horizontal():
|
||||
yield Button()
|
||||
yield Button(variant="primary")
|
||||
yield Button(variant="success")
|
||||
yield Button(variant="warning")
|
||||
yield Button(variant="error")
|
||||
with Horizontal(disabled=True):
|
||||
yield Button()
|
||||
yield Button(variant="primary")
|
||||
yield Button(variant="success")
|
||||
yield Button(variant="warning")
|
||||
yield Button(variant="error")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
45
tests/test_message_handling.py
Normal file
45
tests/test_message_handling.py
Normal file
@@ -0,0 +1,45 @@
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.message import Message
|
||||
from textual.widget import Widget
|
||||
|
||||
|
||||
async def test_message_inheritance_namespace():
|
||||
"""Inherited messages get their correct namespaces.
|
||||
|
||||
Regression test for https://github.com/Textualize/textual/issues/1814.
|
||||
"""
|
||||
|
||||
class BaseWidget(Widget):
|
||||
class Fired(Message):
|
||||
pass
|
||||
|
||||
def trigger(self) -> None:
|
||||
self.post_message(self.Fired())
|
||||
|
||||
class Left(BaseWidget):
|
||||
class Fired(BaseWidget.Fired):
|
||||
pass
|
||||
|
||||
class Right(BaseWidget):
|
||||
class Fired(BaseWidget.Fired):
|
||||
pass
|
||||
|
||||
handlers_called = []
|
||||
|
||||
class MessageInheritanceApp(App[None]):
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Left()
|
||||
yield Right()
|
||||
|
||||
def on_left_fired(self):
|
||||
handlers_called.append("left")
|
||||
|
||||
def on_right_fired(self):
|
||||
handlers_called.append("right")
|
||||
|
||||
app = MessageInheritanceApp()
|
||||
async with app.run_test():
|
||||
app.query_one(Left).trigger()
|
||||
app.query_one(Right).trigger()
|
||||
|
||||
assert handlers_called == ["left", "right"]
|
||||
Reference in New Issue
Block a user