mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Implement scroll_to_center method.
This commit is contained in:
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
|
||||
## Unreleased
|
||||
|
||||
### Added
|
||||
|
||||
- `Widget.scroll_to_center` now scrolls the widget to the center of the screen https://github.com/Textualize/textual/pull/2255
|
||||
|
||||
## [0.19.1] - 2023-04-10
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -2408,6 +2408,104 @@ class Widget(DOMNode):
|
||||
force=force,
|
||||
)
|
||||
|
||||
async def _scroll_to_center_of(
|
||||
self,
|
||||
widget: Widget,
|
||||
animate: bool = True,
|
||||
*,
|
||||
speed: float | None = None,
|
||||
duration: float | None = None,
|
||||
easing: EasingFunction | str | None = None,
|
||||
force: bool = False,
|
||||
) -> None:
|
||||
"""Scroll a widget to the center of this container.
|
||||
|
||||
Args:
|
||||
widget: The widget to center.
|
||||
animate: Whether to animate the scroll.
|
||||
speed: Speed of scroll if animate is `True`; or `None` to use `duration`.
|
||||
duration: Duration of animation, if `animate` is `True` and `speed` is `None`.
|
||||
easing: An easing method for the scrolling animation.
|
||||
force: Force scrolling even when prohibited by overflow styling.
|
||||
"""
|
||||
|
||||
central_point = Offset(
|
||||
widget.virtual_region.x + (1 + widget.virtual_region.width) // 2,
|
||||
widget.virtual_region.y + (1 + widget.virtual_region.height) // 2,
|
||||
)
|
||||
|
||||
container = widget.parent
|
||||
while isinstance(container, Widget) and widget is not self:
|
||||
container_virtual_region = container.virtual_region
|
||||
# The region we want to scroll to must be centered around the central point.
|
||||
# We make it as big as possible because `scroll_to_region` scrolls as little
|
||||
# as possible.
|
||||
target_region = Region(
|
||||
central_point.x - container_virtual_region.width // 2,
|
||||
central_point.y - container_virtual_region.height // 2,
|
||||
container_virtual_region.width,
|
||||
container_virtual_region.height,
|
||||
)
|
||||
scroll = container.scroll_to_region(
|
||||
target_region,
|
||||
animate=animate,
|
||||
speed=speed,
|
||||
duration=duration,
|
||||
easing=easing,
|
||||
force=force,
|
||||
)
|
||||
|
||||
# We scroll `widget` within `container` with the central point written in
|
||||
# the frame of reference of `container`. However, we need to update it so
|
||||
# that we are ready to scroll `container` within _its_ container.
|
||||
# To do this, notice that
|
||||
# (central_point.y - container.scroll_offset.y - scroll.y) is the number
|
||||
# of rows of `widget` that are visible within `container`.
|
||||
# We add that to `container_virtual_region.y` to find the total vertical
|
||||
# offset of the central point with respect to the container of `container`.
|
||||
# A similar calculation is made for the horizontal update.
|
||||
central_point = Offset(
|
||||
container_virtual_region.x
|
||||
+ central_point.x
|
||||
- container.scroll_offset.x
|
||||
- scroll.x,
|
||||
container_virtual_region.y
|
||||
+ central_point.y
|
||||
- container.scroll_offset.y
|
||||
- scroll.y,
|
||||
)
|
||||
widget = container
|
||||
container = widget.parent
|
||||
|
||||
def scroll_to_center(
|
||||
self,
|
||||
animate: bool = True,
|
||||
*,
|
||||
speed: float | None = None,
|
||||
duration: float | None = None,
|
||||
easing: EasingFunction | str | None = None,
|
||||
force: bool = False,
|
||||
) -> None:
|
||||
"""Scroll this widget to the center of the screen.
|
||||
|
||||
Args:
|
||||
animate: Whether to animate the scroll.
|
||||
speed: Speed of scroll if animate is `True`; or `None` to use `duration`.
|
||||
duration: Duration of animation, if `animate` is `True` and `speed` is `None`.
|
||||
easing: An easing method for the scrolling animation.
|
||||
force: Force scrolling even when prohibited by overflow styling.
|
||||
"""
|
||||
|
||||
self.call_after_refresh(
|
||||
self.screen._scroll_to_center_of,
|
||||
widget=self,
|
||||
animate=animate,
|
||||
speed=speed,
|
||||
duration=duration,
|
||||
easing=easing,
|
||||
force=force,
|
||||
)
|
||||
|
||||
def __init_subclass__(
|
||||
cls,
|
||||
can_focus: bool | None = None,
|
||||
|
||||
@@ -409,4 +409,10 @@ def test_scroll_visible(snap_compare):
|
||||
|
||||
|
||||
def test_scroll_to_center(snap_compare):
|
||||
# READ THIS IF THIS TEST FAILS:
|
||||
# While https://github.com/Textualize/textual/issues/2254 is open, the snapshot
|
||||
# this is being compared against is INCORRECT.
|
||||
# The correct output for this snapshot test would show a couple of containers
|
||||
# scrolled so that the red string >>bullseye<< is centered on the screen.
|
||||
# When this snapshot "breaks" because #2254 is fixed, this snapshot can be updated.
|
||||
assert snap_compare(SNAPSHOT_APPS_DIR / "scroll_to_center.py", press=["s"])
|
||||
|
||||
Reference in New Issue
Block a user