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/)
|
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
||||||
and this project adheres to [Semantic Versioning](http://semver.org/).
|
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
|
## [0.19.1] - 2023-04-10
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
@@ -2408,6 +2408,104 @@ class Widget(DOMNode):
|
|||||||
force=force,
|
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__(
|
def __init_subclass__(
|
||||||
cls,
|
cls,
|
||||||
can_focus: bool | None = None,
|
can_focus: bool | None = None,
|
||||||
|
|||||||
@@ -409,4 +409,10 @@ def test_scroll_visible(snap_compare):
|
|||||||
|
|
||||||
|
|
||||||
def test_scroll_to_center(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"])
|
assert snap_compare(SNAPSHOT_APPS_DIR / "scroll_to_center.py", press=["s"])
|
||||||
|
|||||||
Reference in New Issue
Block a user