from typing import Optional
from PIL import Image
from pydantic import Field
from todays_paper_web.widgets import registerView, View
from todays_paper_web.widgets.view import ViewParams
[docs]
class BoxParams(ViewParams):
width: Optional[int] = Field(
None, description="Fixes the box's rendered width; auto-computed from children if unset."
)
height: Optional[int] = Field(
None, description="Fixes the box's rendered height; auto-computed from children if unset."
)
@registerView("box")
class BoxView(View):
Params = BoxParams
def render(self):
width, height = 0, 0
imgs = []
# Loop all children to get total width and height of the box
for child in self.children:
childImg = child.render()
if not childImg:
continue
x = child.params.x or 0
y = child.params.y or 0
align = child.params.align or "left"
valign = child.params.valign or "top"
if child.params.x is not None:
# Only align x here if we have x attribute
if align == "right":
x -= childImg.width
elif align == "center":
x -= int(childImg.width / 2.0)
if child.params.y is not None:
# Only align y here if we have y attribute
if valign == "bottom":
y -= childImg.height
elif valign in ["middle", "center"]:
y -= int(childImg.height / 2.0)
width = max(width, childImg.width + x)
height = max(height, childImg.height + y)
imgs.append((childImg, x, y, align, valign))
if self.params.width is not None:
width = self.params.width
if self.params.height is not None:
height = self.params.height
img = Image.new("RGBA", (width, height), (0, 0, 0, 0))
# Now we have the box width and height, loop all and render them
for childImg, x, y, align, valign in imgs:
if x == 0:
# Do alignment here if no x attribute are set
if align == "right":
x = width - childImg.width
elif align == "center":
x = int((width - childImg.width) / 2.0)
if y == 0:
# Do alignment here if no y attribute are set
if valign == "bottom":
y = height - childImg.height
elif valign in ["middle", "center"]:
y = int((height - childImg.height) / 2.0)
self.paste(img, childImg, (x, y))
return img
[docs]
class ColumnParams(ViewParams):
"""No attributes of its own - `<column>` only reads a child's `x` (see `ViewParams`),
inherited rather than redeclared."""
@registerView("column")
class ColumnView(View):
Params = ColumnParams
def render(self):
width, height = 0, 0
imgs = []
for child in self.children:
childImg = child.render()
x = child.params.x or 0
width = max(width, childImg.width + x)
height += childImg.height
imgs.append((childImg, x))
y = 0
img = Image.new("RGBA", (width, height), (0, 0, 0, 0))
for childImg, x in imgs:
ColumnView.paste(img, childImg, (x, y))
y += childImg.height
return img
[docs]
class RowParams(ViewParams):
valign: str = Field(
"top",
description="Default vertical alignment for a child that doesn't set its own `y`: `top`, "
"`middle`/`center`, or `bottom`.",
)
@registerView("row")
class RowView(View):
Params = RowParams
def render(self):
width, height = 0, 0
imgs = []
for child in self.children:
childImg = child.render()
y = child.params.y or 0
height = max(height, childImg.height + y)
width += childImg.width
imgs.append((childImg, y))
x = 0
img = Image.new("RGBA", (width, height), (0, 0, 0, 0))
for childImg, y in imgs:
if not y:
if self.params.valign == "bottom":
y = height - childImg.height
elif self.params.valign in ["middle", "center"]:
y = int((height - int(childImg.height)) / 2.0)
self.paste(img, childImg, (x, y))
x += childImg.width
return img