Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a formal mapping of Pack->CSS #1778

Merged
merged 16 commits into from
Feb 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changes/1778.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The mapping between Pack layout and HTML/CSS has been formalized.
6 changes: 5 additions & 1 deletion core/src/toga/style/applicator.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@ class TogaApplicator:
def __init__(self, widget):
self.widget = widget

def refresh(self):
# print("RE-EVALUATE LAYOUT", self.widget)
self.widget.refresh()

def set_bounds(self):
# print("LAYOUT", self.widget, self.widget.layout)
# print("APPLY LAYOUT", self.widget, self.widget.layout)
self.widget._impl.set_bounds(
self.widget.layout.absolute_content_left,
self.widget.layout.absolute_content_top,
Expand Down
111 changes: 91 additions & 20 deletions core/src/toga/style/pack.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,18 +93,15 @@ def apply(self, prop, value):
else:
value = LEFT
self._applicator.set_text_alignment(value)
if prop == "text_direction":
elif prop == "text_direction":
if self.text_align is None:
self._applicator.set_text_alignment(RIGHT if value == RTL else LEFT)
elif prop == "color":
self._applicator.set_color(value)
elif prop == "background_color":
self._applicator.set_background_color(value)
elif prop == "visibility":
hidden = False
if value == HIDDEN:
hidden = True
self._applicator.set_hidden(hidden)
self._applicator.set_hidden(value == HIDDEN)
elif prop in (
"font_family",
"font_size",
Expand All @@ -121,20 +118,24 @@ def apply(self, prop, value):
weight=self.font_weight,
)
)
else:
# Any other style change will cause a change in layout geometry,
# so perform a refresh.
self._applicator.refresh()

def layout(self, node, viewport):
# Precompute `scale_factor` by providing it as a default param.
def scale(value, scale_factor=viewport.dpi / viewport.baseline_dpi):
return int(value * scale_factor)

self._layout_node(node, viewport.width, viewport.height, scale)
node.layout.content_top = node.style.padding_top
node.layout.content_bottom = node.style.padding_bottom
self._layout_node(node, viewport.width, viewport.height, scale, root=True)
node.layout.content_top = scale(node.style.padding_top)
node.layout.content_bottom = scale(node.style.padding_bottom)

node.layout.content_left = node.style.padding_left
node.layout.content_right = node.style.padding_right
node.layout.content_left = scale(node.style.padding_left)
node.layout.content_right = scale(node.style.padding_right)

def _layout_node(self, node, alloc_width, alloc_height, scale):
def _layout_node(self, node, alloc_width, alloc_height, scale, root=False):
self.__class__._depth += 1
# self._debug("COMPUTE LAYOUT for", node, "available", alloc_width, alloc_height)

Expand Down Expand Up @@ -197,6 +198,11 @@ def _layout_node(self, node, alloc_width, alloc_height, scale):
node, available_width, available_height, scale
)

if root:
# self._debug("ROOT NODE")
width = max(width, available_width)
height = max(height, available_height)

else:
# self._debug("NO CHILDREN", available_width)
width = available_width
Expand Down Expand Up @@ -526,6 +532,52 @@ def _layout_column_children(self, node, available_width, available_height, scale

def __css__(self):
css = []
# display
if self.display == NONE:
css.append("display: none;")
else:
# if self.display != NONE, it must be pack; it will inherit
# the pack definition from the Toga stylesheet.
pass

# visibility
if self.visibility != VISIBLE:
css.append(f"visibility: {self.visibility}")

# direction
css.append(f"flex-direction: {self.direction.lower()};")

# alignment
if self.direction == ROW:
if self.alignment:
if self.alignment == LEFT:
css.append("align-items: start;")
elif self.alignment == RIGHT:
css.append("align-items: end;")
elif self.alignment == CENTER:
css.append("align-items: center;")
else:
if self.alignment:
if self.alignment == TOP:
css.append("align-items: start;")
elif self.alignment == BOTTOM:
css.append("align-items: end;")
elif self.alignment == CENTER:
css.append("align-items: center;")

# width/flex
if self.width:
css.append(f"width: {self.width}px;")
elif self.direction == ROW:
css.append(f"flex: {self.flex} 0 0;")

# height/flex
if self.height:
css.append(f"width: {self.width}px;")
elif self.direction == COLUMN:
css.append(f"flex: {self.flex} 0 0;")

# padding_*
if self.padding_top:
css.append(f"margin-top: {self.padding_top}px;")
if self.padding_bottom:
Expand All @@ -534,15 +586,34 @@ def __css__(self):
css.append(f"margin-left: {self.padding_left}px;")
if self.padding_right:
css.append(f"margin-right: {self.padding_right}px;")
if self.width:
css.append(f"width: {self.width}px;")
else:
if self.flex:
css.append(f"flex: {self.flex} 0 0%;")
else:
css.append("flex: 0 0 0%;")
if self.direction:
css.append(f"flex-direction: {self.direction.lower()};")

# color
if self.color:
css.append(f"color: {self.color};")

# background_color
if self.background_color:
css.append(f"background-color: {self.background_color};")

# text_align
if self.text_align:
css.append(f"text-align: {self.text_align}")

# text_direction
if self.text_direction != LTR:
css.append(f"text-direction: {self.text_direction}")

# font-*
if self.font_family != SYSTEM:
css.append(f"font-family: {self.font_family};")
if self.font_size != SYSTEM_DEFAULT_FONT_SIZE:
css.append(f"font-size: {self.font_size};")
if self.font_weight != NORMAL:
css.append(f"font-weight: {self.font_weight};")
if self.font_style != NORMAL:
css.append(f"font-style: {self.font_style};")
if self.font_variant != NORMAL:
css.append(f"font-variant: {self.font_variant};")

return " ".join(css)

Expand Down
4 changes: 3 additions & 1 deletion core/src/toga/widgets/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,9 @@ def refresh(self):
self._root.refresh()
else:
self.refresh_sublayouts()
super().refresh(self._impl.viewport)
# We can't compute a layout until we have a viewport
if self._impl.viewport:
super().refresh(self._impl.viewport)

def refresh_sublayouts(self):
for child in self.children:
Expand Down
6 changes: 3 additions & 3 deletions core/src/toga/widgets/optioncontainer.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,11 +303,11 @@ def __init__(
# End backwards compatibility.
######################################################################

self._content = OptionList(self)
self._on_select = None
self._impl = self.factory.OptionContainer(interface=self)

self.on_select = on_select
self._content = OptionList(self)
if content:
for text, widget in content:
self.add(text, widget)
Expand All @@ -318,11 +318,11 @@ def __init__(

@property
def content(self):
"""The sub layouts of the `SplitContainer`.
"""The sub layouts of the `OptionContainer`.

Returns:
A OptionList ``list`` of :class:`~toga.OptionItem`. Each element of the list
is a sub layout of the `SplitContainer`
is a sub layout of the `OptionContainer`

Raises:
ValueError: If the list is less than two elements long.
Expand Down
Loading