Skip to content

Commit

Permalink
Merge pull request #1337 from Textualize/0.6.0
Browse files Browse the repository at this point in the history
new blog post
  • Loading branch information
willmcgugan authored Dec 11, 2022
2 parents 54a2e3e + b3fa5d1 commit 0bcd260
Show file tree
Hide file tree
Showing 21 changed files with 323 additions and 203 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).


## [0.6.0] - Unreleased
## [0.6.0] - 2022-12-11

### Added

Expand Down
106 changes: 106 additions & 0 deletions docs/blog/posts/release0-6-0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
---
draft: false
date: 2022-12-11
categories:
- Release
title: "version-060"
authors:
- willmcgugan
---

# Textual 0.6.0 adds a *tree*mendous new widget

A new release of Textual lands 3 weeks after the previous release -- and it's a big one.

<!-- more -->

!!! information

If you're new here, [Textual](https://github.com/Textualize/textual) is TUI framework for Python.

## Tree Control

The headline feature of version 0.6.0 is a new tree control built from the ground-up. The previous Tree control suffered from an overly complex API and wasn't scalable (scrolling slowed down with 1000s of nodes).

This new version has a simpler API and is highly scalable (no slowdown with larger trees). There are also a number of visual enhancements in this version.

Here's a very simple example:

=== "Output"

```{.textual path="docs/examples/widgets/tree.py"}
```

=== "tree.py"

```python
--8<-- "docs/examples/widgets/tree.py"
```

Here's the tree control being used to navigate some JSON ([json_tree.py](https://github.com/Textualize/textual/blob/main/examples/json_tree.py) in the examples directory).

<div class="video-wrapper">
<iframe width="auto" src="https://www.youtube.com/embed/Fy9fPL37P6o" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>

I'm biased of course, but I think this terminal based tree control is more usable (and even prettier) than just about anything I've seen on the web or desktop. So much of computing tends to organize itself in to a tree that I think this widget will find a lot of uses.

The Tree control forms the foundation of the [DirectoryTree](../../widgets/directory_tree.md) widget, which has also been updated. Here it is used in the [code_browser.py](https://github.com/Textualize/textual/blob/main/examples/code_browser.py) example:

<div class="video-wrapper">
<iframe width="auto" src="https://www.youtube.com/embed/ZrYWyZXuYRY" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>

## List View

We have a new [ListView](../../widgets/list_view.md) control to navigate and select items in a list. Items can be widgets themselves, which makes this a great platform for building more sophisticated controls.

=== "Output"

```{.textual path="docs/examples/widgets/list_view.py"}
```

=== "list_view.py"

```python
--8<-- "docs/examples/widgets/list_view.py"
```

=== "list_view.css"

```sass
--8<-- "docs/examples/widgets/list_view.css"
```

## Placeholder

The [Placeholder](../../widgets/placeholder.md) widget was broken since the big CSS update. We've brought it back and given it a bit of a polish.

Use this widget in place of custom widgets you have yet to build when designing your UI. The colors are automatically cycled to differentiate one placeholder rom the next. You can click a placeholder to cycle between its ID, size, and lorem ipsum text.

=== "Output"

```{.textual path="docs/examples/widgets/placeholder.py" columns="100" lines="45"}
```

=== "placeholder.py"

```python
--8<-- "docs/examples/widgets/placeholder.py"
```

=== "placeholder.css"

```sass
--8<-- "docs/examples/widgets/placeholder.css"
```


## Fixes

As always, there are a number of fixes in this release. Mostly related to layout. See [CHANGELOG.md](https://github.com/Textualize/textual/blob/main/CHANGELOG.md) for the details.

## What's next?

The next release will focus on *pain points* we discovered while in a dog-fooding phase (see the [DevLog](https://textual.textualize.io/blog/category/devlog/) for details on what Textual devs have been building).

5 changes: 4 additions & 1 deletion docs/examples/widgets/list_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@


class ListViewExample(App):

CSS_PATH = "list_view.css"

def compose(self) -> ComposeResult:
yield ListView(
ListItem(Label("One")),
Expand All @@ -12,6 +15,6 @@ def compose(self) -> ComposeResult:
yield Footer()


app = ListViewExample(css_path="list_view.css")
if __name__ == "__main__":
app = ListViewExample()
app.run()
2 changes: 1 addition & 1 deletion docs/examples/widgets/placeholder.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def compose(self) -> ComposeResult:
Placeholder(variant="size", id="col3"),
id="c1",
),
id="bot"
id="bot",
),
Container(
Placeholder(variant="text", id="left"),
Expand Down
4 changes: 2 additions & 2 deletions docs/examples/widgets/tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@

class TreeApp(App):
def compose(self) -> ComposeResult:
tree: Tree = Tree("Dune")
tree: Tree[dict] = Tree("Dune")
tree.root.expand()
characters = tree.root.add("Characters", expand=True)
characters.add_leaf("Paul")
characters.add_leaf("Jessica")
characters.add_leaf("Channi")
characters.add_leaf("Chani")
yield tree


Expand Down
2 changes: 1 addition & 1 deletion docs/widgets/directory_tree.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,5 @@ The `DirectoryTree.FileSelected` message is sent when the user selects a file in

## See Also

* [Tree][textual.widgets.DirectoryTree] code reference
* [DirectoryTree][textual.widgets.DirectoryTree] code reference
* [Tree][textual.widgets.Tree] code reference
14 changes: 10 additions & 4 deletions docs/widgets/list_view.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,16 @@ The example below shows an app with a simple `ListView`.
--8<-- "docs/examples/widgets/list_view.py"
```

=== "list_view.css"

```sass
--8<-- "docs/examples/widgets/list_view.css"
```

## Reactive Attributes

| Name | Type | Default | Description |
|---------|-------|---------|---------------------------------|
| ------- | ----- | ------- | ------------------------------- |
| `index` | `int` | `0` | The currently highlighted index |

## Messages
Expand All @@ -40,7 +46,7 @@ click on a list item.
#### Attributes

| attribute | type | purpose |
|-----------|------------|--------------------------------|
| --------- | ---------- | ------------------------------ |
| `item` | `ListItem` | The item that was highlighted. |

### Selected
Expand All @@ -54,7 +60,7 @@ or by clicking on it.
#### Attributes

| attribute | type | purpose |
|-----------|------------|-----------------------------|
| --------- | ---------- | --------------------------- |
| `item` | `ListItem` | The item that was selected. |


Expand All @@ -68,7 +74,7 @@ are changed (e.g. a child is added, or the list is cleared).
#### Attributes

| attribute | type | purpose |
|------------|------------------|---------------------------|
| ---------- | ---------------- | ------------------------- |
| `children` | `list[ListItem]` | The new ListView children |


Expand Down
11 changes: 11 additions & 0 deletions examples/json_tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ def add_json(cls, node: TreeNode, json_data: object) -> None:
highlighter = ReprHighlighter()

def add_node(name: str, node: TreeNode, data: object) -> None:
"""Adds a node to the tree.
Args:
name (str): Name of the node.
node (TreeNode): Parent node.
data (object): Data associated with the node.
"""
if isinstance(data, dict):
node._label = Text(f"{{}} {name}")
for key, value in data.items():
Expand All @@ -56,20 +63,24 @@ def add_node(name: str, node: TreeNode, data: object) -> None:
add_node("JSON", node, json_data)

def on_mount(self) -> None:
"""Load some JSON when the app starts."""
with open("food.json") as data_file:
self.json_data = json.load(data_file)

def action_add(self) -> None:
"""Add a node to the tree."""
tree = self.query_one(Tree)
json_node = tree.root.add("JSON")
self.add_json(json_node, self.json_data)
tree.root.expand()

def action_clear(self) -> None:
"""Clear the tree (remove all nodes)."""
tree = self.query_one(Tree)
tree.clear()

def action_toggle_root(self) -> None:
"""Toggle the root node."""
tree = self.query_one(Tree)
tree.show_root = not tree.show_root

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "textual"
version = "0.5.0"
version = "0.6.0"
homepage = "https://github.com/Textualize/textual"
description = "Modern Text User Interface framework"
authors = ["Will McGugan <[email protected]>"]
Expand Down
4 changes: 2 additions & 2 deletions src/textual/_compositor.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@
from ._cells import cell_len
from ._loop import loop_last
from ._types import Lines
from .geometry import Offset, Region, Size
from ._typing import TypeAlias
from .geometry import NULL_OFFSET, Offset, Region, Size

if TYPE_CHECKING:
from .widget import Widget
Expand Down Expand Up @@ -334,7 +334,7 @@ def _arrange_root(
tuple[CompositorMap, set[Widget]]: Compositor map and set of widgets.
"""

ORIGIN = Offset(0, 0)
ORIGIN = NULL_OFFSET

map: CompositorMap = {}
widgets: set[Widget] = set()
Expand Down
Loading

0 comments on commit 0bcd260

Please sign in to comment.