diff --git a/codegen/generate_utilities.py b/codegen/generate_utilities.py index 06cb16d8..a6f01998 100755 --- a/codegen/generate_utilities.py +++ b/codegen/generate_utilities.py @@ -343,6 +343,11 @@ def literal_vals(obj: object, field: str) -> tuple[str, ...]: generated_lines.append("") +for a in literal_vals(Flex, "align_self"): + generated_lines.append(f'align_self_{a.replace("-", "_")} = Style(layout=Flex(align_self="{a}"))') + +generated_lines.append("") + generated_lines.append("weight_none = Style(layout=Flex(weight=None))") for n in N: if n <= 0: diff --git a/reprisal/layout.py b/reprisal/layout.py index bd8f8166..51964584 100644 --- a/reprisal/layout.py +++ b/reprisal/layout.py @@ -134,6 +134,7 @@ def height(self) -> int: class LayoutBox(ForbidExtras): element: AnyElement dims: BoxDimensions = Field(default_factory=BoxDimensions) + parent: LayoutBox | None children: list[LayoutBox] = Field(default_factory=list) def walk_from_bottom(self) -> Iterator[AnyElement]: @@ -245,15 +246,34 @@ def first_pass(self) -> None: self.dims.content.height = max(self.dims.content.height, child_box.dims.height()) def second_pass(self) -> None: + style = self.element.style + layout = style.layout + parent = self.parent + # TODO: positions - # TODO: align self + # handle align self + if layout.position == "relative" and parent: + if parent.element.style.layout.direction == "row": + match layout.align_self: + case "center": + self.dims.content.y += (parent.dims.content.height - self.dims.height()) // 2 + case "end": + self.dims.content.y += parent.dims.content.height - self.dims.height() + case "stretch": + self.dims.content.height = parent.dims.content.height - self.dims.vertical_edge_width() + elif parent.element.style.layout.direction == "column": + match layout.align_self: + case "center": + self.dims.content.x += (parent.dims.content.width - self.dims.width()) // 2 + case "end": + self.dims.content.x += parent.dims.content.width - self.dims.width() + case "stretch": + self.dims.content.width = parent.dims.content.width - self.dims.horizontal_edge_width() # calculate available width for children, minus how much they use, # then divide that between them based on the content justification # We are in the parent, justifying the children! - style = self.element.style - layout = style.layout available_width = self.dims.content.width available_height = self.dims.content.height @@ -415,6 +435,10 @@ def second_pass(self) -> None: # alignment (cross-axis placement) # content width/height of self, but full width/height of children for child in relative_children: + # Skip children that will align themselves + if child.element.style.layout.align_self != "none": + continue + if layout.direction == "row": if layout.align_children == "center": # TODO: these floordivs aren't great @@ -433,16 +457,16 @@ def second_pass(self) -> None: child.dims.content.width = self.dims.content.width - child.dims.horizontal_edge_width() -def build_layout_tree(element: AnyElement) -> LayoutBox: +def build_layout_tree(element: AnyElement, parent: LayoutBox | None = None) -> LayoutBox: if element.style.hidden: raise Exception("Root element cannot have layout='hidden'") - children = [] + box = LayoutBox(element=element, parent=parent) for child in element.children: if isinstance(child, Component): raise Exception("Layout tree must be built from concrete Elements, not Components") if not child.style.hidden == "hidden": - children.append(build_layout_tree(child)) + box.children.append(build_layout_tree(element=child, parent=box)) - return LayoutBox(element=element, children=children) + return box diff --git a/reprisal/styles/styles.py b/reprisal/styles/styles.py index f7c1e3f1..a709b089 100644 --- a/reprisal/styles/styles.py +++ b/reprisal/styles/styles.py @@ -314,6 +314,7 @@ class Flex(StyleFragment): "end", "stretch", ] = "start" + align_self: Literal["none", "start", "center", "end", "stretch"] = "none" class Style(StyleFragment): diff --git a/reprisal/styles/utilities.py b/reprisal/styles/utilities.py index 1986d5fd..fad2bd57 100644 --- a/reprisal/styles/utilities.py +++ b/reprisal/styles/utilities.py @@ -1492,6 +1492,12 @@ align_children_end = Style(layout=Flex(align_children="end")) align_children_stretch = Style(layout=Flex(align_children="stretch")) +align_self_none = Style(layout=Flex(align_self="none")) +align_self_start = Style(layout=Flex(align_self="start")) +align_self_center = Style(layout=Flex(align_self="center")) +align_self_end = Style(layout=Flex(align_self="end")) +align_self_stretch = Style(layout=Flex(align_self="stretch")) + weight_none = Style(layout=Flex(weight=None)) weight_1 = Style(layout=Flex(weight=1)) weight_2 = Style(layout=Flex(weight=2))