Skip to content

ScrollContainer

Usage

import toga

content = toga.Box(children=[...])

container = toga.ScrollContainer(content=content)

Reference

Bases: Widget

Source code in core/src/toga/widgets/scrollcontainer.py
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
class ScrollContainer(Widget):
    _USE_DEBUG_BACKGROUND = True

    def __init__(
        self,
        id: str | None = None,
        style: StyleT | None = None,
        horizontal: bool = True,
        vertical: bool = True,
        on_scroll: OnScrollHandler | None = None,
        content: Widget | None = None,
        **kwargs,
    ):
        """Create a new Scroll Container.

        :param id: The ID for the widget.
        :param style: A style object. If no style is provided, a default style
            will be applied to the widget.
        :param horizontal: Should horizontal scrolling be permitted?
        :param vertical: Should vertical scrolling be permitted?
        :param on_scroll: Initial [`on_scroll`][toga.ScrollContainer.on_scroll] handler.
        :param content: The content to display in the scroll window.
        :param kwargs: Initial style properties.
        """

        self._content: Widget | None = None
        self.on_scroll = None

        super().__init__(id, style, **kwargs)

        # Set all attributes
        self.vertical = vertical
        self.horizontal = horizontal
        self.content = content
        self.on_scroll = on_scroll

    def _create(self) -> Any:
        return self.factory.ScrollContainer(interface=self)

    @Widget.app.setter
    def app(self, app) -> None:
        # Invoke the superclass property setter
        Widget.app.fset(self, app)

        # Also assign the app to the content in the container
        if self._content:
            self._content.app = app

    @Widget.window.setter
    def window(self, window) -> None:
        # Invoke the superclass property setter
        Widget.window.fset(self, window)

        # Also assign the window to the content in the container
        if self._content:
            self._content.window = window

    @property
    def enabled(self) -> Literal[True]:
        """Is the widget currently enabled? i.e., can the user interact with the widget?

        ScrollContainer widgets cannot be disabled; this property will always return
        True; any attempt to modify it will be ignored.
        """
        return True

    @enabled.setter
    def enabled(self, value: object) -> None:
        pass

    def focus(self) -> None:
        """No-op; ScrollContainer cannot accept input focus."""
        pass

    @property
    def content(self) -> Widget | None:
        """The root content widget displayed inside the scroll container."""
        return self._content

    @content.setter
    def content(self, widget: Widget | None) -> None:
        if self._content:
            # Clear the window before the app so that registry entries can be cleared
            self._content.window = None
            self._content.app = None

        if widget:
            widget.app = self.app
            widget.window = self.window
            self._impl.set_content(widget._impl)
        else:
            self._impl.set_content(None)

        self._content = widget
        if widget:
            widget.refresh()

    @property
    def vertical(self) -> bool:
        """Is vertical scrolling enabled?"""
        return self._impl.get_vertical()

    @vertical.setter
    def vertical(self, value: object) -> None:
        self._impl.set_vertical(bool(value))
        if self._content:
            self._content.refresh()

    @property
    def horizontal(self) -> bool:
        """Is horizontal scrolling enabled?"""
        return self._impl.get_horizontal()

    @horizontal.setter
    def horizontal(self, value: object) -> None:
        self._impl.set_horizontal(bool(value))
        if self._content:
            self._content.refresh()

    @property
    def on_scroll(self) -> OnScrollHandler:
        """Handler to invoke when the user moves a scroll bar."""
        return self._on_scroll

    @on_scroll.setter
    def on_scroll(self, on_scroll: OnScrollHandler) -> None:
        self._on_scroll = wrapped_handler(self, on_scroll)

    @property
    def max_horizontal_position(self) -> int:
        """The maximum horizontal scroll position (read-only)."""
        if not self.horizontal:
            return 0
        else:
            return self._impl.get_max_horizontal_position()

    @property
    def horizontal_position(self) -> int:
        """The current horizontal scroll position.

        If the value provided is negative, or greater than the maximum horizontal
        position, the value will be clipped to the valid range.

        :returns: The current horizontal scroll position.
        :raises ValueError: If an attempt is made to change the horizontal position when
            horizontal scrolling is disabled.
        """
        return self._impl.get_horizontal_position()

    @horizontal_position.setter
    def horizontal_position(self, horizontal_position: SupportsInt) -> None:
        if not self.horizontal:
            raise ValueError(
                "Cannot set horizontal position when "
                "horizontal scrolling is not enabled."
            )

        self.position = (horizontal_position, self._impl.get_vertical_position())

    @property
    def max_vertical_position(self) -> int:
        """The maximum vertical scroll position (read-only)."""
        if not self.vertical:
            return 0
        else:
            return self._impl.get_max_vertical_position()

    @property
    def vertical_position(self) -> int:
        """The current vertical scroll position.

        If the value provided is negative, or greater than the maximum
        vertical position, the value will be clipped to the valid range.

        :returns: The current vertical scroll position.
        :raises ValueError: If an attempt is made to change the vertical position
            when vertical scrolling is disabled.
        """
        return self._impl.get_vertical_position()

    @vertical_position.setter
    def vertical_position(self, vertical_position: SupportsInt) -> None:
        if not self.vertical:
            raise ValueError(
                "Cannot set vertical position when vertical scrolling is not enabled."
            )

        self.position = (self._impl.get_horizontal_position(), vertical_position)

    # This combined property is necessary because on some platforms (e.g. iOS), setting
    # the horizontal and vertical position separately would cause the horizontal and
    # vertical movement to appear as two separate animations.
    @property
    def position(self) -> Position:
        """The current scroll position.

        If the value provided for either axis is negative, or greater than the maximum
        position in that axis, the value will be clipped to the valid range.

        If scrolling is disabled in either axis, the value provided for that axis will
        be ignored.
        """
        return Position(self.horizontal_position, self.vertical_position)

    @position.setter
    def position(self, position: PositionT):
        horizontal_position, vertical_position = map(int, position)
        if self.horizontal:
            if horizontal_position < 0:
                horizontal_position = 0
            else:
                max_value = self.max_horizontal_position
                if horizontal_position > max_value:
                    horizontal_position = max_value
        else:
            horizontal_position = self.horizontal_position

        if self.vertical:
            if vertical_position < 0:
                vertical_position = 0
            else:
                max_value = self.max_vertical_position
                if vertical_position > max_value:
                    vertical_position = max_value
        else:
            vertical_position = self.vertical_position

        self._impl.set_position(horizontal_position, vertical_position)

content property writable

The root content widget displayed inside the scroll container.

enabled property writable

Is the widget currently enabled? i.e., can the user interact with the widget?

ScrollContainer widgets cannot be disabled; this property will always return True; any attempt to modify it will be ignored.

horizontal property writable

Is horizontal scrolling enabled?

horizontal_position property writable

The current horizontal scroll position.

If the value provided is negative, or greater than the maximum horizontal position, the value will be clipped to the valid range.

:returns: The current horizontal scroll position. :raises ValueError: If an attempt is made to change the horizontal position when horizontal scrolling is disabled.

max_horizontal_position property

The maximum horizontal scroll position (read-only).

max_vertical_position property

The maximum vertical scroll position (read-only).

on_scroll property writable

Handler to invoke when the user moves a scroll bar.

position property writable

The current scroll position.

If the value provided for either axis is negative, or greater than the maximum position in that axis, the value will be clipped to the valid range.

If scrolling is disabled in either axis, the value provided for that axis will be ignored.

vertical property writable

Is vertical scrolling enabled?

vertical_position property writable

The current vertical scroll position.

If the value provided is negative, or greater than the maximum vertical position, the value will be clipped to the valid range.

:returns: The current vertical scroll position. :raises ValueError: If an attempt is made to change the vertical position when vertical scrolling is disabled.

__init__(id=None, style=None, horizontal=True, vertical=True, on_scroll=None, content=None, **kwargs)

Create a new Scroll Container.

:param id: The ID for the widget. :param style: A style object. If no style is provided, a default style will be applied to the widget. :param horizontal: Should horizontal scrolling be permitted? :param vertical: Should vertical scrolling be permitted? :param on_scroll: Initial on_scroll handler. :param content: The content to display in the scroll window. :param kwargs: Initial style properties.

Source code in core/src/toga/widgets/scrollcontainer.py
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
def __init__(
    self,
    id: str | None = None,
    style: StyleT | None = None,
    horizontal: bool = True,
    vertical: bool = True,
    on_scroll: OnScrollHandler | None = None,
    content: Widget | None = None,
    **kwargs,
):
    """Create a new Scroll Container.

    :param id: The ID for the widget.
    :param style: A style object. If no style is provided, a default style
        will be applied to the widget.
    :param horizontal: Should horizontal scrolling be permitted?
    :param vertical: Should vertical scrolling be permitted?
    :param on_scroll: Initial [`on_scroll`][toga.ScrollContainer.on_scroll] handler.
    :param content: The content to display in the scroll window.
    :param kwargs: Initial style properties.
    """

    self._content: Widget | None = None
    self.on_scroll = None

    super().__init__(id, style, **kwargs)

    # Set all attributes
    self.vertical = vertical
    self.horizontal = horizontal
    self.content = content
    self.on_scroll = on_scroll

focus()

No-op; ScrollContainer cannot accept input focus.

Source code in core/src/toga/widgets/scrollcontainer.py
93
94
95
def focus(self) -> None:
    """No-op; ScrollContainer cannot accept input focus."""
    pass

Bases: Protocol

Source code in core/src/toga/widgets/scrollcontainer.py
14
15
16
17
18
19
20
class OnScrollHandler(Protocol):
    def __call__(self, widget: ScrollContainer, **kwargs: Any) -> None:
        """A handler to invoke when the container is scrolled.

        :param widget: The ScrollContainer that was scrolled.
        :param kwargs: Ensures compatibility with arguments added in future versions.
        """

__call__(widget, **kwargs)

A handler to invoke when the container is scrolled.

:param widget: The ScrollContainer that was scrolled. :param kwargs: Ensures compatibility with arguments added in future versions.

Source code in core/src/toga/widgets/scrollcontainer.py
15
16
17
18
19
20
def __call__(self, widget: ScrollContainer, **kwargs: Any) -> None:
    """A handler to invoke when the container is scrolled.

    :param widget: The ScrollContainer that was scrolled.
    :param kwargs: Ensures compatibility with arguments added in future versions.
    """