Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Return 'self' in some widget verb methods. #2102

Merged
merged 4 commits into from
Mar 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Breaking change: changed default behaviour of `Vertical` (see `VerticalScroll`) https://github.com/Textualize/textual/issues/1957
- The default `overflow` style for `Horizontal` was changed to `hidden hidden` https://github.com/Textualize/textual/issues/1957
- `DirectoryTree` also accepts `pathlib.Path` objects as the path to list https://github.com/Textualize/textual/issues/1438
- Some widget methods now return `self` instead of `None` https://github.com/Textualize/textual/pull/2102:
- `Widget`: `refresh`, `focus`, `reset_focus`
- `Button.press`
- `DataTable`: `clear`, `refresh_coordinate`, `refresh_row`, `refresh_column`, `sort`
- `Placehoder.cycle_variant`
- `Switch.toggle`
- `Tabs.clear`
- `TextLog`: `write`, `clear`
- `TreeNode`: `expand`, `expand_all`, `collapse`, `collapse_all`, `toggle`, `toggle_all`
- `Tree`: `clear`, `reset`

### Removed

Expand Down
6 changes: 3 additions & 3 deletions src/textual/dom.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
from .css.query import DOMQuery, QueryType
from .screen import Screen
from .widget import Widget
from typing_extensions import TypeAlias
from typing_extensions import Self, TypeAlias

from typing_extensions import Literal

Expand Down Expand Up @@ -950,5 +950,5 @@ def has_pseudo_class(self, *class_names: str) -> bool:
has_pseudo_classes = self.pseudo_classes.issuperset(class_names)
return has_pseudo_classes

def refresh(self, *, repaint: bool = True, layout: bool = False) -> None:
pass
def refresh(self, *, repaint: bool = True, layout: bool = False) -> Self:
return self
28 changes: 20 additions & 8 deletions src/textual/widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -2542,7 +2542,7 @@ def refresh(
*regions: Region,
repaint: bool = True,
layout: bool = False,
) -> None:
) -> Self:
"""Initiate a refresh of the widget.

This method sets an internal flag to perform a refresh, which will be done on the
Expand All @@ -2558,8 +2558,11 @@ def refresh(

Args:
*regions: Additional screen regions to mark as dirty.
repaint: Repaint the widget (will call render() again). Defaults to True.
layout: Also layout widgets in the view. Defaults to False.
repaint: Repaint the widget (will call render() again).
layout: Also layout widgets in the view.

Returns:
The `Widget` instance.
"""
if layout and not self._layout_required:
self._layout_required = True
Expand All @@ -2577,6 +2580,7 @@ def refresh(
self._repaint_required = True

self.check_idle()
return self

def remove(self) -> AwaitRemove:
"""Remove the Widget from the DOM (effectively deleting it).
Expand Down Expand Up @@ -2658,12 +2662,14 @@ def _check_refresh(self) -> None:
self._layout_required = False
screen.post_message(messages.Layout())

def focus(self, scroll_visible: bool = True) -> None:
def focus(self, scroll_visible: bool = True) -> Self:
"""Give focus to this widget.

Args:
scroll_visible: Scroll parent to make this widget
visible. Defaults to True.
scroll_visible: Scroll parent to make this widget visible.

Returns:
The `Widget` instance.
"""

def set_focus(widget: Widget):
Expand All @@ -2674,13 +2680,19 @@ def set_focus(widget: Widget):
pass

self.app.call_later(set_focus, self)
return self

def reset_focus(self) -> Self:
"""Reset the focus (move it to the next available widget).

def reset_focus(self) -> None:
"""Reset the focus (move it to the next available widget)."""
Returns:
The `Widget` instance.
"""
try:
self.screen._reset_focus(self)
except NoScreen:
pass
return self

def capture_mouse(self, capture: bool = True) -> None:
"""Capture (or release) the mouse.
Expand Down
13 changes: 8 additions & 5 deletions src/textual/widgets/_button.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
from __future__ import annotations

from functools import partial
from typing import cast

import rich.repr
from rich.text import Text, TextType
from typing_extensions import Literal
from typing_extensions import Literal, Self

from .. import events
from ..css._error_tools import friendly_list
Expand Down Expand Up @@ -233,14 +232,18 @@ async def _on_click(self, event: events.Click) -> None:
event.stop()
self.press()

def press(self) -> None:
"""Respond to a button press."""
def press(self) -> Self:
"""Respond to a button press.

Returns:
The button instance."""
if self.disabled or not self.display:
return
return self
# Manage the "active" effect:
self._start_active_affect()
# ...and let other components know that we've just been clicked:
self.post_message(Button.Pressed(self))
return self

def _start_active_affect(self) -> None:
"""Start a small animation to show the button was clicked."""
Expand Down
57 changes: 41 additions & 16 deletions src/textual/widgets/_data_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from rich.segment import Segment
from rich.style import Style
from rich.text import Text, TextType
from typing_extensions import Literal, TypeAlias
from typing_extensions import Literal, Self, TypeAlias

from .. import events
from .._cache import LRUCache
Expand Down Expand Up @@ -1156,11 +1156,14 @@ def _get_column_region(self, column_index: int) -> Region:
full_column_region = Region(x, 0, width, height)
return full_column_region

def clear(self, columns: bool = False) -> None:
def clear(self, columns: bool = False) -> Self:
"""Clear the table.

Args:
columns: Also clear the columns. Defaults to False.
columns: Also clear the columns.

Returns:
The `DataTable` instance.
"""
self._clear_caches()
self._y_offsets.clear()
Expand All @@ -1176,6 +1179,7 @@ def clear(self, columns: bool = False) -> None:
self._label_column = Column(self._label_column_key, Text(), auto_width=True)
self._labelled_row_exists = False
self.refresh()
return self

def add_column(
self, label: TextType, *, width: int | None = None, key: str | None = None
Expand Down Expand Up @@ -1333,49 +1337,66 @@ def on_idle(self) -> None:
self._updated_cells.clear()
self._update_column_widths(updated_columns)

def refresh_coordinate(self, coordinate: Coordinate) -> None:
def refresh_coordinate(self, coordinate: Coordinate) -> Self:
"""Refresh the cell at a coordinate.

Args:
coordinate: The coordinate to refresh.

Returns:
The `DataTable` instance.
"""
if not self.is_valid_coordinate(coordinate):
return
return self
region = self._get_cell_region(coordinate)
self._refresh_region(region)
return self

def refresh_row(self, row_index: int) -> None:
def refresh_row(self, row_index: int) -> Self:
"""Refresh the row at the given index.

Args:
row_index: The index of the row to refresh.

Returns:
The `DataTable` instance.
"""
if not self.is_valid_row_index(row_index):
return
return self

region = self._get_row_region(row_index)
self._refresh_region(region)
return self

def refresh_column(self, column_index: int) -> None:
def refresh_column(self, column_index: int) -> Self:
"""Refresh the column at the given index.

Args:
column_index: The index of the column to refresh.

Returns:
The `DataTable` instance.
"""
if not self.is_valid_column_index(column_index):
return
return self

region = self._get_column_region(column_index)
self._refresh_region(region)
return self

def _refresh_region(self, region: Region) -> Self:
"""Refresh a region of the DataTable, if it's visible within the window.

This method will translate the region to account for scrolling.

def _refresh_region(self, region: Region) -> None:
"""Refresh a region of the DataTable, if it's visible within
the window. This method will translate the region to account
for scrolling."""
Returns:
The `DataTable` instance.
"""
if not self.window_region.overlaps(region):
return
return self
region = region.translate(-self.scroll_offset)
self.refresh(region)
return self

def is_valid_row_index(self, row_index: int) -> bool:
"""Return a boolean indicating whether the row_index is within table bounds.
Expand Down Expand Up @@ -1839,12 +1860,15 @@ def sort(
self,
*columns: ColumnKey | str,
reverse: bool = False,
) -> None:
"""Sort the rows in the DataTable by one or more column keys.
) -> Self:
"""Sort the rows in the `DataTable` by one or more column keys.

Args:
columns: One or more columns to sort by the values in.
reverse: If True, the sort order will be reversed.

Returns:
The `DataTable` instance.
"""

def sort_by_column_keys(
Expand All @@ -1862,6 +1886,7 @@ def sort_by_column_keys(
)
self._update_count += 1
self.refresh()
return self

def _scroll_cursor_into_view(self, animate: bool = False) -> None:
"""When the cursor is at a boundary of the DataTable and moves out
Expand Down
11 changes: 8 additions & 3 deletions src/textual/widgets/_placeholder.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from itertools import cycle

from rich.console import RenderableType
from typing_extensions import Literal
from typing_extensions import Literal, Self

from .. import events
from ..css._error_tools import friendly_list
Expand Down Expand Up @@ -132,9 +132,14 @@ def render(self) -> RenderableType:
"""
return self._renderables[self.variant]

def cycle_variant(self) -> None:
"""Get the next variant in the cycle."""
def cycle_variant(self) -> Self:
"""Get the next variant in the cycle.

Returns:
The `Placeholder` instance.
"""
self.variant = next(self._variants_cycle)
return self

def watch_variant(
self, old_variant: PlaceholderVariant, variant: PlaceholderVariant
Expand Down
11 changes: 9 additions & 2 deletions src/textual/widgets/_switch.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from __future__ import annotations

from typing import ClassVar
from typing import TYPE_CHECKING, ClassVar

from rich.console import RenderableType

Expand All @@ -11,6 +11,9 @@
from ..scrollbar import ScrollBarRender
from ..widget import Widget

if TYPE_CHECKING:
from typing_extensions import Self


class Switch(Widget, can_focus=True):
"""A switch widget that represents a boolean value.
Expand Down Expand Up @@ -158,10 +161,14 @@ def action_toggle(self) -> None:
"""Toggle the state of the switch."""
self.toggle()

def toggle(self) -> None:
def toggle(self) -> Self:
"""Toggle the switch value.

As a result of the value changing, a `Switch.Changed` message will
be posted.

Returns:
The `Switch` instance.
"""
self.value = not self.value
return self
14 changes: 11 additions & 3 deletions src/textual/widgets/_tabs.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from __future__ import annotations

from typing import ClassVar
from typing import TYPE_CHECKING, ClassVar

import rich.repr
from rich.style import Style
Expand All @@ -18,6 +18,9 @@
from ..widget import Widget
from ..widgets import Static

if TYPE_CHECKING:
from typing_extensions import Self


class Underline(Widget):
"""The animated underline beneath tabs."""
Expand Down Expand Up @@ -316,13 +319,18 @@ async def refresh_active() -> None:

self.call_after_refresh(refresh_active)

def clear(self) -> None:
"""Clear all the tabs."""
def clear(self) -> Self:
"""Clear all the tabs.

Returns:
The `Tabs` instance.
"""
underline = self.query_one(Underline)
underline.highlight_start = 0
underline.highlight_end = 0
self.query("#tabs-list > Tab").remove()
self.post_message(self.Cleared(self))
return self

def remove_tab(self, tab_or_id: Tab | str | None) -> None:
"""Remove a tab.
Expand Down
Loading