mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
vertical layout, fix scroll issue for screen
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
|
||||
|
||||
Screen {
|
||||
layout: horizontal;
|
||||
|
||||
layout: vertical;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
Widget {
|
||||
@@ -11,15 +11,15 @@ Widget {
|
||||
|
||||
#thing {
|
||||
width: auto;
|
||||
height: 10;
|
||||
height: auto;
|
||||
|
||||
background:magenta;
|
||||
margin: 1;
|
||||
margin: 3;
|
||||
padding: 1;
|
||||
border: solid white;
|
||||
box-sizing: content-box;
|
||||
/* border: solid white; */
|
||||
align-vertical: middle;
|
||||
align-horizontal: center;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ Widget {
|
||||
box-sizing: content-box;
|
||||
|
||||
background:green;
|
||||
align-vertical: middle;
|
||||
align-horizontal: center;
|
||||
color:white;
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ Widget {
|
||||
#thing3 {
|
||||
width: 20;
|
||||
height: 10;
|
||||
|
||||
margin: 1;
|
||||
background:blue;
|
||||
align-vertical: bottom;
|
||||
align-horizontal: center;
|
||||
}
|
||||
|
||||
@@ -1,27 +1,35 @@
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
from typing import Callable, TYPE_CHECKING
|
||||
from typing import Callable, NamedTuple, TYPE_CHECKING
|
||||
|
||||
from .geometry import Size, Spacing
|
||||
from .css.styles import StylesBase
|
||||
|
||||
|
||||
class BoxModel(NamedTuple):
|
||||
content: Size
|
||||
margin: Spacing
|
||||
|
||||
|
||||
def get_box_model(
|
||||
styles: StylesBase,
|
||||
container_size: Size,
|
||||
parent_size: Size,
|
||||
get_auto_width: Callable[[Size, Size], int],
|
||||
get_auto_height: Callable[[Size, Size], int],
|
||||
) -> tuple[Size, Spacing]:
|
||||
) -> BoxModel:
|
||||
"""Resolve the box model for this Styles.
|
||||
|
||||
Args:
|
||||
styles (StylesBase): Styles object.
|
||||
container_size (Size): The size of the widget container.
|
||||
parent_size (Size): The size widget's parent.
|
||||
get_auto_width (Callable): A callable which accepts container size and parent size and returns a width.
|
||||
get_auto_height (Callable): A callable which accepts container size and parent size and returns a height.
|
||||
|
||||
Returns:
|
||||
tuple[Size, Spacing]: A tuple with the size of the content area and margin.
|
||||
BoxModel: A tuple with the size of the content area and margin.
|
||||
"""
|
||||
|
||||
has_rule = styles.has_rule
|
||||
@@ -73,4 +81,4 @@ def get_box_model(
|
||||
size = Size(width, height) + extra
|
||||
margin = styles.margin if has_rule("margin") else Spacing(0, 0, 0, 0)
|
||||
|
||||
return size, margin
|
||||
return BoxModel(size, margin)
|
||||
@@ -1,5 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import cast
|
||||
|
||||
from textual.geometry import Size, Offset, Region
|
||||
from textual.layout import Layout, WidgetPlacement
|
||||
@@ -24,21 +25,29 @@ class HorizontalLayout(Layout):
|
||||
x = max_width = max_height = 0
|
||||
parent_size = parent.size
|
||||
|
||||
for widget in parent.children:
|
||||
styles = widget.styles
|
||||
(content_width, content_height), margin = widget.get_box_model(
|
||||
size, parent_size
|
||||
)
|
||||
offset_y = styles.align_height(
|
||||
content_height + margin.height, parent_size.height
|
||||
)
|
||||
region = Region(
|
||||
margin.left + x, margin.top + offset_y, content_width, content_height
|
||||
)
|
||||
max_height = max(max_height, content_height + margin.height)
|
||||
box_models = [
|
||||
widget.get_box_model(size, parent_size)
|
||||
for widget in cast("list[Widget]", parent.children)
|
||||
]
|
||||
|
||||
margins = [
|
||||
max((box1.margin.right, box2.margin.left))
|
||||
for box1, box2 in zip(box_models, box_models[1:])
|
||||
]
|
||||
margins.append(box_models[-1].margin.right)
|
||||
|
||||
x = box_models[0].margin.left
|
||||
|
||||
for widget, box_model, margin in zip(parent.children, box_models, margins):
|
||||
content_width, content_height = box_model.content
|
||||
offset_y = widget.styles.align_height(content_height, parent_size.height)
|
||||
region = Region(x, offset_y, content_width, content_height)
|
||||
max_height = max(max_height, content_height)
|
||||
add_placement(WidgetPlacement(region, widget, 0))
|
||||
x += region.width + margin.left
|
||||
max_width = x + margin.right
|
||||
x += region.width + margin
|
||||
max_width = x
|
||||
|
||||
max_width += margins[-1]
|
||||
|
||||
total_region = Region(0, 0, max_width, max_height)
|
||||
add_placement(WidgetPlacement(total_region, None, 0))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import cast, TYPE_CHECKING
|
||||
|
||||
from .. import log
|
||||
|
||||
@@ -12,7 +12,7 @@ if TYPE_CHECKING:
|
||||
|
||||
|
||||
class VerticalLayout(Layout):
|
||||
"""Simple vertical layout."""
|
||||
"""Used to layout Widgets vertically on screen, from top to bottom."""
|
||||
|
||||
name = "vertical"
|
||||
|
||||
@@ -26,22 +26,29 @@ class VerticalLayout(Layout):
|
||||
y = max_width = max_height = 0
|
||||
parent_size = parent.size
|
||||
|
||||
for widget in parent.children:
|
||||
styles = widget.styles
|
||||
(content_width, content_height), margin = widget.get_box_model(
|
||||
size, parent_size
|
||||
)
|
||||
offset_x = styles.align_width(
|
||||
content_width + margin.width, parent_size.width
|
||||
)
|
||||
box_models = [
|
||||
widget.get_box_model(size, parent_size)
|
||||
for widget in cast("list[Widget]", parent.children)
|
||||
]
|
||||
|
||||
region = Region(
|
||||
margin.left + offset_x, y + margin.top, content_width, content_height
|
||||
)
|
||||
max_width = max(max_width, content_width + margin.width)
|
||||
margins = [
|
||||
max((box1.margin.bottom, box2.margin.top))
|
||||
for box1, box2 in zip(box_models, box_models[1:])
|
||||
]
|
||||
margins.append(box_models[-1].margin.bottom)
|
||||
|
||||
y = box_models[0].margin.top
|
||||
|
||||
for widget, box_model, margin in zip(parent.children, box_models, margins):
|
||||
content_width, content_height = box_model.content
|
||||
offset_x = widget.styles.align_width(content_width, parent_size.width)
|
||||
region = Region(offset_x, y, content_width, content_height)
|
||||
max_height = max(max_height, content_height)
|
||||
add_placement(WidgetPlacement(region, widget, 0))
|
||||
y += region.height + margin.top
|
||||
max_height = y + margin.bottom
|
||||
y += region.height + margin
|
||||
max_height = y
|
||||
|
||||
max_height += margins[-1]
|
||||
|
||||
total_region = Region(0, 0, max_width, max_height)
|
||||
add_placement(WidgetPlacement(total_region, None, 0))
|
||||
|
||||
@@ -217,6 +217,9 @@ class Screen(Widget):
|
||||
return
|
||||
scroll_widget = widget
|
||||
if scroll_widget is not None:
|
||||
if scroll_widget is self:
|
||||
await self.post_message(event)
|
||||
else:
|
||||
await scroll_widget.forward_event(event)
|
||||
else:
|
||||
await self.post_message(event)
|
||||
|
||||
@@ -24,7 +24,7 @@ from . import errors, log
|
||||
from . import events
|
||||
from ._animator import BoundAnimator
|
||||
from ._border import Border
|
||||
from ._box_model import get_box_model
|
||||
from .box_model import BoxModel, get_box_model
|
||||
from ._callback import invoke
|
||||
from .color import Color
|
||||
from ._context import active_app
|
||||
@@ -107,7 +107,7 @@ class Widget(DOMNode):
|
||||
show_vertical_scrollbar = Reactive(False, layout=True)
|
||||
show_horizontal_scrollbar = Reactive(False, layout=True)
|
||||
|
||||
def get_box_model(self, container_size, parent_size) -> tuple[Size, Spacing]:
|
||||
def get_box_model(self, container_size, parent_size) -> BoxModel:
|
||||
box_model = get_box_model(
|
||||
self.styles,
|
||||
container_size,
|
||||
@@ -260,7 +260,7 @@ class Widget(DOMNode):
|
||||
self.scroll_target_x = x
|
||||
if x != self.scroll_x:
|
||||
self.animate(
|
||||
"scroll_x", self.scroll_target_x, speed=80, easing="out_cubic"
|
||||
"scroll_x", self.scroll_target_x, speed=80, easing="lineary"
|
||||
)
|
||||
scrolled_x = True
|
||||
if y is not None:
|
||||
|
||||
Reference in New Issue
Block a user