mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
box model calculations
This commit is contained in:
@@ -8,16 +8,18 @@ from .css.styles import StylesBase
|
||||
|
||||
|
||||
class BoxModel(NamedTuple):
|
||||
content: Size
|
||||
margin: Spacing
|
||||
"""The result of `get_box_model`."""
|
||||
|
||||
size: Size # Content + padding + border
|
||||
margin: Spacing # Additional margin
|
||||
|
||||
|
||||
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],
|
||||
get_auto_width: Callable[[Size, Size, Spacing], int],
|
||||
get_auto_height: Callable[[Size, Size, Spacing], int],
|
||||
) -> BoxModel:
|
||||
"""Resolve the box model for this Styles.
|
||||
|
||||
@@ -35,19 +37,18 @@ def get_box_model(
|
||||
has_rule = styles.has_rule
|
||||
width, height = container_size
|
||||
|
||||
extra = Size(0, 0)
|
||||
gutter = Spacing(0, 0, 0, 0)
|
||||
if styles.box_sizing == "content-box":
|
||||
if has_rule("padding"):
|
||||
extra += styles.padding.totals
|
||||
extra += styles.border.spacing.totals
|
||||
gutter += styles.padding
|
||||
gutter += styles.border.spacing
|
||||
|
||||
else: # border-box
|
||||
extra -= styles.border.spacing.totals
|
||||
gutter -= styles.border.spacing
|
||||
|
||||
if has_rule("width"):
|
||||
if styles.width.is_auto:
|
||||
# extra_width = styles.padding.width + styles.border.spacing.width
|
||||
width = get_auto_width(container_size, parent_size)
|
||||
width = get_auto_width(container_size, parent_size, gutter)
|
||||
else:
|
||||
width = styles.width.resolve_dimension(container_size, parent_size)
|
||||
else:
|
||||
@@ -63,8 +64,7 @@ def get_box_model(
|
||||
|
||||
if has_rule("height"):
|
||||
if styles.height.is_auto:
|
||||
extra_height = styles.padding.height + styles.border.spacing.height
|
||||
height = get_auto_height(container_size, parent_size) + extra_height
|
||||
height = get_auto_height(container_size, parent_size, gutter)
|
||||
else:
|
||||
height = styles.height.resolve_dimension(container_size, parent_size)
|
||||
else:
|
||||
@@ -78,7 +78,7 @@ def get_box_model(
|
||||
max_height = styles.max_height.resolve_dimension(container_size, parent_size)
|
||||
height = min(width, max_height)
|
||||
|
||||
size = Size(width, height) + extra
|
||||
size = Size(width, height) + gutter.totals
|
||||
margin = styles.margin if has_rule("margin") else Spacing(0, 0, 0, 0)
|
||||
|
||||
return BoxModel(size, margin)
|
||||
|
||||
@@ -653,5 +653,14 @@ class Spacing(NamedTuple):
|
||||
)
|
||||
return NotImplemented
|
||||
|
||||
def __sub__(self, other: object) -> Spacing:
|
||||
if isinstance(other, tuple):
|
||||
top1, right1, bottom1, left1 = self
|
||||
top2, right2, bottom2, left2 = other
|
||||
return Spacing(
|
||||
top1 - top2, right1 - right2, bottom1 - bottom2, left1 - left2
|
||||
)
|
||||
return NotImplemented
|
||||
|
||||
|
||||
NULL_OFFSET = Offset(0, 0)
|
||||
|
||||
@@ -39,7 +39,7 @@ class HorizontalLayout(Layout):
|
||||
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
|
||||
content_width, content_height = box_model.size
|
||||
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)
|
||||
|
||||
@@ -40,7 +40,7 @@ class VerticalLayout(Layout):
|
||||
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
|
||||
content_width, content_height = box_model.size
|
||||
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)
|
||||
|
||||
@@ -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) -> BoxModel:
|
||||
def get_box_model(self, container_size: Size, parent_size: Size) -> BoxModel:
|
||||
box_model = get_box_model(
|
||||
self.styles,
|
||||
container_size,
|
||||
@@ -118,14 +118,18 @@ class Widget(DOMNode):
|
||||
self.log(self, self.styles.padding, self.styles.border.spacing)
|
||||
return box_model
|
||||
|
||||
def get_content_width(self, container_size: Size, parent_size: Size) -> int:
|
||||
def get_content_width(
|
||||
self, container_size: Size, parent_size: Size, gutter: Spacing
|
||||
) -> int:
|
||||
console = self.app.console
|
||||
renderable = self.render()
|
||||
measurement = Measurement.get(console, console.options, renderable)
|
||||
return measurement.maximum
|
||||
|
||||
def get_content_height(self, container_size: Size, parent_size: Size) -> int:
|
||||
return container_size.height
|
||||
def get_content_height(
|
||||
self, container_size: Size, parent_size: Size, gutter: Spacing
|
||||
) -> int:
|
||||
return container_size.height - gutter.height
|
||||
|
||||
async def watch_scroll_x(self, new_value: float) -> None:
|
||||
self.horizontal_scrollbar.position = int(new_value)
|
||||
@@ -260,7 +264,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="lineary"
|
||||
"scroll_x", self.scroll_target_x, speed=80, easing="out_cubic"
|
||||
)
|
||||
scrolled_x = True
|
||||
if y is not None:
|
||||
@@ -537,7 +541,9 @@ class Widget(DOMNode):
|
||||
"""Render all lines."""
|
||||
width, height = self.size
|
||||
renderable = self.render_styled()
|
||||
options = self.console.options.update_dimensions(width, height)
|
||||
options = self.console.options.update_dimensions(width, height).update(
|
||||
highlight=False
|
||||
)
|
||||
lines = self.console.render_lines(renderable, options)
|
||||
self._render_cache = RenderCache(self.size, lines)
|
||||
self._dirty_regions.clear()
|
||||
|
||||
Reference in New Issue
Block a user