Skip to content

Commit

Permalink
Add toga.Row and toga.Column (beeware#3116)
Browse files Browse the repository at this point in the history
Added toga.Row and toga.Column shortcuts for building layouts.
  • Loading branch information
mhsmith authored Jan 23, 2025
1 parent 1bc7002 commit 59d427a
Show file tree
Hide file tree
Showing 8 changed files with 69 additions and 15 deletions.
1 change: 1 addition & 0 deletions changes/3010.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
``toga.Row`` and ``toga.Column`` can now be used as shorthands for ``toga.Box(style=Pack(direction=...))``.
2 changes: 2 additions & 0 deletions core/src/toga/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
"Box": "toga.widgets.box",
"Button": "toga.widgets.button",
"Canvas": "toga.widgets.canvas",
"Column": "toga.widgets.box",
"DateInput": "toga.widgets.dateinput",
"DatePicker": "toga.widgets.dateinput",
"DetailedList": "toga.widgets.detailedlist",
Expand All @@ -70,6 +71,7 @@
"OptionItem": "toga.widgets.optioncontainer",
"PasswordInput": "toga.widgets.passwordinput",
"ProgressBar": "toga.widgets.progressbar",
"Row": "toga.widgets.box",
"ScrollContainer": "toga.widgets.scrollcontainer",
"Selection": "toga.widgets.selection",
"Slider": "toga.widgets.slider",
Expand Down
10 changes: 10 additions & 0 deletions core/src/toga/widgets/box.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,13 @@ def enabled(self, value: bool) -> None:
def focus(self) -> None:
"""No-op; Box cannot accept input focus."""
pass


def Row(*args, **kwargs):
"""Shorthand for :any:`Box` with its :ref:`pack-direction` set to "row"."""
return Box(*args, direction="row", **kwargs)


def Column(*args, **kwargs):
"""Shorthand for :any:`Box` with its :ref:`pack-direction` set to "column"."""
return Box(*args, direction="column", **kwargs)
43 changes: 43 additions & 0 deletions core/tests/widgets/test_box.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import pytest

import toga
from toga.style import Pack
from toga_dummy.utils import (
assert_action_not_performed,
assert_action_performed,
Expand Down Expand Up @@ -62,3 +65,43 @@ def test_focus_noop():

box.focus()
assert_action_not_performed(box, "focus")


@pytest.mark.parametrize("direction", ["row", "column"])
def test_row_column(direction):
"""Row and Column shorthands can be used."""
func = getattr(toga, direction.capitalize())
box = func(
id="my-id", # Non-style property
width=100, # Style property
style=Pack(height=200), # Style object
)
assert type(box) is toga.Box
assert box.id == "my-id"
assert box.style.direction == direction
assert box.style.width == 100
assert box.style.height == 200


@pytest.mark.parametrize("func_direction", ["column", "row"])
@pytest.mark.parametrize("style_direction", ["column", "row"])
def test_row_column_conflict_style(func_direction, style_direction):
"""If a shorthand function is passed a style object specifying a direction,
the shorthand takes priority, and the style object is not modified."""
func = getattr(toga, func_direction.capitalize())
style = Pack(direction=style_direction)
box = func(style=style)
assert box.style.direction == func_direction
assert style.direction == style_direction


@pytest.mark.parametrize("func_direction", ["column", "row"])
@pytest.mark.parametrize("mixin_direction", ["column", "row"])
def test_row_column_conflict_mixin(func_direction, mixin_direction):
"""If a shorthand function is passed a mixin argument specifying a direction,
an exception is raised."""
func = getattr(toga, func_direction.capitalize())
with pytest.raises(
TypeError, match="got multiple values for keyword argument 'direction'"
):
func(direction=mixin_direction)
2 changes: 2 additions & 0 deletions docs/reference/api/containers/box.rst
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,5 @@ Reference
---------

.. autoclass:: toga.Box
.. autofunction:: toga.Row
.. autofunction:: toga.Column
2 changes: 2 additions & 0 deletions docs/reference/style/pack.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ If a previously hidden widget is made visible, any children of the widget with
a visibility of ``hidden`` will remain hidden. Any descendants of the hidden
child will also remain hidden, regardless of their visibility.

.. _pack-direction:

``direction``
-------------

Expand Down
10 changes: 4 additions & 6 deletions examples/layout/layout/app.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import toga
from toga.constants import CENTER, COLUMN, HIDDEN, ROW, VISIBLE
from toga.constants import CENTER, HIDDEN, VISIBLE


class ExampleLayoutApp(toga.App):
Expand All @@ -19,13 +19,13 @@ def startup(self):
text="Add new label", on_press=self.add_label
)

self.content_box = toga.Box(children=[], direction=COLUMN, gap=4)
self.content_box = toga.Column(children=[], gap=4)

image = toga.Image("resources/tiberius.png")
self.image_view = toga.ImageView(image, width=60, height=60)

# this tests adding children during init, before we have an implementation
self.button_box = toga.Box(
self.button_box = toga.Column(
children=[
self.button_hide,
self.button_add,
Expand All @@ -34,14 +34,12 @@ def startup(self):
self.button_remove,
self.button_add_to_scroll,
],
direction=COLUMN,
width=120,
gap=20,
)

self.box = toga.Box(
self.box = toga.Row(
children=[],
direction=ROW,
margin=20,
gap=20,
align_items=CENTER,
Expand Down
14 changes: 5 additions & 9 deletions examples/resize/resize/app.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import toga
from toga.style.pack import COLUMN, ROW
from toga.style.pack import COLUMN


class SizeButton(toga.Button):
Expand Down Expand Up @@ -27,8 +27,7 @@ def __init__(self, title, *, on_change):
align_items="center",
children=[
toga.Label(title.upper(), font_weight="bold"),
toga.Box(
direction=ROW,
toga.Row(
children=[self.width_button, self.height_button, self.flex_button],
),
],
Expand All @@ -49,19 +48,16 @@ def startup(self):
self.text_label, self.style_label = (
toga.Label("", background_color="cyan") for i in range(2)
)
main_box = toga.Box(
direction=COLUMN,
main_box = toga.Column(
children=[
toga.Box(
direction=ROW,
toga.Row(
children=[
SizePanel("Text", on_change=self.on_change_text),
toga.Box(flex=1),
SizePanel("Style", on_change=self.on_change_style),
],
),
toga.Box(
direction=ROW,
toga.Row(
children=[
self.text_label,
toga.Label(
Expand Down

0 comments on commit 59d427a

Please sign in to comment.