vertical layout, fix scroll issue for screen

This commit is contained in:
Will McGugan
2022-04-20 15:47:06 +01:00
parent 7f85fc6795
commit 3869eb7463
6 changed files with 73 additions and 46 deletions

View File

@@ -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;
}

View File

@@ -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)

View File

@@ -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))

View File

@@ -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))

View File

@@ -217,6 +217,9 @@ class Screen(Widget):
return
scroll_widget = widget
if scroll_widget is not None:
await scroll_widget.forward_event(event)
if scroll_widget is self:
await self.post_message(event)
else:
await scroll_widget.forward_event(event)
else:
await self.post_message(event)

View File

@@ -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: