1.. _scrolling:
2
3=========
4Scrolling
5=========
6
7
8Overview
9********
10
11In LVGL scrolling works very intuitively: if a Widget is outside its
12parent content area (the size without padding), the parent becomes
13scrollable and scrollbar(s) will appear. That's it.
14
15Any Widget can be scrollable including :ref:`base_widget`, ``lv_image``,
16``lv_button``, ``lv_meter``, etc
17
18The Widget can either be scrolled horizontally or vertically in one
19stroke; diagonal scrolling is not possible.
20
21
22Scrollbar
23---------
24
25Mode
26~~~~
27
28Scrollbars are displayed according to the configured ``scrollbar-mode``.  The
29following modes are available:
30
31- :cpp:enumerator:`LV_SCROLLBAR_MODE_OFF`: Never show the scrollbars
32- :cpp:enumerator:`LV_SCROLLBAR_MODE_ON`: Always show the scrollbars
33- :cpp:enumerator:`LV_SCROLLBAR_MODE_ACTIVE`: Show scroll bars while a Widget is being scrolled
34- :cpp:enumerator:`LV_SCROLLBAR_MODE_AUTO`: Show scroll bars when the content is large enough to be scrolled
35
36:cpp:expr:`lv_obj_set_scrollbar_mode(widget, LV_SCROLLBAR_MODE_...)` sets the scrollbar mode on a Widget.
37
38Styling
39~~~~~~~
40
41A Scrollbar is a dedicated part of a Widget, called
42:cpp:enumerator:`LV_PART_SCROLLBAR`.  For example, a scrollbar can turn to red like
43this:
44
45.. code-block:: c
46
47   static lv_style_t style_red;
48   lv_style_init(&style_red);
49   lv_style_set_bg_color(&style_red, lv_color_red());
50
51   ...
52
53   lv_obj_add_style(widget, &style_red, LV_PART_SCROLLBAR);
54
55A Widget goes to the :cpp:enumerator:`LV_STATE_SCROLLED` state while it's being
56scrolled.  This allows adding different styles to the Widget that will be effective
57while it is being scrolled.  For example, this code makes the scrollbar blue while
58the Widget is being scrolled:
59
60.. code-block:: c
61
62   static lv_style_t style_blue;
63   lv_style_init(&style_blue);
64   lv_style_set_bg_color(&style_blue, lv_color_blue());
65
66   ...
67
68   lv_obj_add_style(widget, &style_blue, LV_STATE_SCROLLED | LV_PART_SCROLLBAR);
69
70If the base direction of the :cpp:enumerator:`LV_PART_SCROLLBAR` is RTL
71(:c:macro:`LV_BASE_DIR_RTL`) the vertical scrollbar will be placed on the left.
72Note that, the ``base_dir`` style property is inherited. Therefore, it
73can be set directly on the :cpp:enumerator:`LV_PART_SCROLLBAR` part of a Widget, or
74on the Widget's LV_PART_MAIN part, or that of any of its parents, to make a scrollbar
75inherit the base direction.
76
77``pad_left/right/top/bottom`` sets the spacing around the scrollbars,
78``width`` sets the scrollbar's width and ``length`` sets the scrollbar's length:
79If `length` is not set or left at `0` the scrollbar's length will be set automatically according to the length of the content.
80.. code-block:: c
81
82   static lv_style_t style_scrollbar;
83   lv_style_init(&style_scrollbar);
84   lv_style_set_pad_left(&style_scrollbar, 2);
85   lv_style_set_pad_right(&style_scrollbar, 2);
86   lv_style_set_pad_top(&style_scrollbar, 2);
87   lv_style_set_pad_bottom(&style_scrollbar, 2);
88   lv_style_set_width(&style_scrollbar, 10);
89   lv_style_set_length(&style_scrollbar, 50);
90
91   ...
92
93   lv_obj_add_style(widget, &style_scrollbar, LV_PART_SCROLLBAR);
94
95The minimum length of the scrollbar is fixed to 10, while its maximum length is limited by the
96Widget's height or width, depending on whether the scrollbar is vertical or horizontal. Any length value
97set outside of these limits will automatically result in a length fixed to either limit.
98
99.. _scroll_events:
100
101Scrolling Events
102----------------
103
104The following events are emitted as part of scrolling:
105
106- :cpp:enumerator:`LV_EVENT_SCROLL_BEGIN`: Signals that scrolling has begun. The
107  event parameter is ``NULL`` or an ``lv_anim_t *`` with a scroll animation
108  descriptor that can be modified if required.
109- :cpp:enumerator:`LV_EVENT_SCROLL_END`: Signals that scrolling has ended.
110- :cpp:enumerator:`LV_EVENT_SCROLL`: Signals that the scrolling position changed;
111  triggered on every position change.
112
113
114
115Features of Scrolling
116*********************
117
118Besides, managing "normal" scrolling there are many interesting and
119useful additional features.
120
121Scrollable
122----------
123
124It is possible to make a Widget non-scrollable with
125:cpp:expr:`lv_obj_remove_flag(widget, LV_OBJ_FLAG_SCROLLABLE)`.
126
127Non-scrollable Widgets can still propagate the scrolling (chain) to
128their parents.
129
130The direction in which scrolling happens can be controlled by
131:cpp:expr:`lv_obj_set_scroll_dir(widget, LV_DIR_...)`.
132
133The following values can be used for the direction:
134
135- :cpp:enumerator:`LV_DIR_TOP`: only scroll up
136- :cpp:enumerator:`LV_DIR_LEFT`: only scroll left
137- :cpp:enumerator:`LV_DIR_BOTTOM`: only scroll down
138- :cpp:enumerator:`LV_DIR_RIGHT`: only scroll right
139- :cpp:enumerator:`LV_DIR_HOR`: only scroll horizontally
140- :cpp:enumerator:`LV_DIR_VER`: only scroll vertically
141- :cpp:enumerator:`LV_DIR_ALL`: scroll any directions
142
143OR-ed values are also possible. E.g. :cpp:expr:`LV_DIR_TOP | LV_DIR_LEFT`.
144
145Scroll chaining
146---------------
147
148If a Widget can't be scrolled further (e.g. its content has reached the
149bottom-most position), additional scrolling is propagated to its parent.
150If the parent can be scrolled in that direction than it will be scrolled
151instead. It continues propagating up the Widget's parent hierarchy up to
152the :ref:`Screen <screens>`.
153
154The propagation on scrolling is called "scroll chaining" and it can be
155enabled/disabled with ``LV_OBJ_FLAG_SCROLL_CHAIN_HOR/VER`` flag. If
156chaining is disabled the propagation stops on the Widget and the
157parent(s) won't be scrolled.
158
159Scroll momentum
160---------------
161
162When the user scrolls a Widget and releases it, LVGL can emulate
163inertial momentum for the scrolling. It's like the Widget was "thrown"
164and scrolling slows down smoothly.
165
166Scroll momentum can be enabled/disabled with the
167:cpp:enumerator:`LV_OBJ_FLAG_SCROLL_MOMENTUM` flag.
168
169Elastic scroll
170--------------
171
172Normally a Widget can't be scrolled past the extremities of its
173content.  That is, the top side of the content can't be below the top side
174of the Widget, and vice versa for the bottom side.
175
176However, with :cpp:enumerator:`LV_OBJ_FLAG_SCROLL_ELASTIC` a fancy effect is added
177when the user "over-scrolls" the content. The scrolling slows down, and
178the content can be scrolled inside the Widget. When the Widget is
179released the content scrolled in it is animated back to the closest valid
180position.
181
182Snapping
183--------
184
185The children of a Widget can be snapped according to specific rules
186when scrolling ends. Children can be made snappable individually with
187the :cpp:enumerator:`LV_OBJ_FLAG_SNAPPABLE` flag.
188
189A Widget can align snapped children in four ways:
190
191- :cpp:enumerator:`LV_SCROLL_SNAP_NONE`: Snapping is disabled. (default)
192- :cpp:enumerator:`LV_SCROLL_SNAP_START`: Align the children to the left/top side of a scrolled Widget
193- :cpp:enumerator:`LV_SCROLL_SNAP_END`: Align the children to the right/bottom side of a scrolled Widget
194- :cpp:enumerator:`LV_SCROLL_SNAP_CENTER`: Align the children to the center of a scrolled Widget
195
196Snap alignment is set with
197:cpp:expr:`lv_obj_set_scroll_snap_x(widget, LV_SCROLL_SNAP_...)` and
198:cpp:expr:`lv_obj_set_scroll_snap_y(widget, LV_SCROLL_SNAP_...)`.
199
200This is what happens under the hood:
201
2021. user scrolls and releases a Widget;
2032. LVGL calculates where the scroll would end considering scroll momentum;
2043. LVGL finds the nearest scroll point;
2054. LVGL scrolls to the snap point with an animation.
206
207Scroll one
208----------
209
210The "scroll one" feature tells LVGL to allow scrolling only one
211snappable child at a time. This requires making the children snappable
212and setting scroll snap alignment to something other than
213:cpp:enumerator:`LV_SCROLL_SNAP_NONE`.
214
215This feature can be enabled by the :cpp:enumerator:`LV_OBJ_FLAG_SCROLL_ONE` flag.
216
217Scroll on focus
218---------------
219
220Imagine that there are a lot of Widgets in a group that are on a scrollable
221Widget. Pressing the "Tab" button moves focus to the next Widget but it might
222be outside the visible area of the scrollable Widget. If the "scroll on
223focus" feature is enabled LVGL will automatically scroll Widgets to
224bring the child Widget with focus into view.  The scrolling happens recursively
225therefore even nested scrollable Widgets are handled properly. The
226Widget will be scrolled into view even if it is on a different page of a
227tabview.
228
229
230
231Scrolling Programmatically
232**************************
233
234The following API functions allow programmatic scrolling of Widgets:
235
236- ``lv_obj_scroll_by(widget, x, y, LV_ANIM_ON/OFF)`` scroll by ``x`` and ``y`` values
237- ``lv_obj_scroll_to(widget, x, y, LV_ANIM_ON/OFF)`` scroll to bring the given coordinate to the top left corner
238- ``lv_obj_scroll_to_x(widget, x, LV_ANIM_ON/OFF)`` scroll to bring the given coordinate to the left side
239- ``lv_obj_scroll_to_y(widget, y, LV_ANIM_ON/OFF)`` scroll to bring the given coordinate to the top side
240
241From time to time you may need to retrieve the *scroll position* of a
242scrollable Widget, either to restore it later, or to dynamically display some
243elements according to its current scroll position. Here is an example to illustrate
244how to combine scroll event and store the scroll-top position.
245
246.. code-block:: c
247
248   static int scroll_value = 0;
249
250   static void store_scroll_top_value_event_cb(lv_event_t* e) {
251     lv_obj_t * scr = lv_event_get_target(e);
252     scroll_value = lv_obj_get_scroll_top(scr);
253     printf("%d pixels are scrolled above top edge of display.\n", scroll_value);
254   }
255
256   lv_obj_t * scr = lv_obj_create(NULL);
257   lv_obj_add_event_cb(scr, store_scroll_top_value_event_cb, LV_EVENT_SCROLL, NULL);
258
259Scroll coordinates can be retrieved from different axes with these functions:
260
261- :cpp:expr:`lv_obj_get_scroll_x(widget)`      Pixels scrolled past left edge of Widget's view window.
262- :cpp:expr:`lv_obj_get_scroll_y(widget)`      Pixels scrolled past top of Widget's view window.
263- :cpp:expr:`lv_obj_get_scroll_top(widget)`    Identical to :cpp:expr:`lv_obj_get_scroll_y(widget)`
264- :cpp:expr:`lv_obj_get_scroll_bottom(widget)` Pixels scrolled past bottom of Widget's view window.
265- :cpp:expr:`lv_obj_get_scroll_left(widget)`   Identical to :cpp:expr:`lv_obj_get_scroll_x(widget)`.
266- :cpp:expr:`lv_obj_get_scroll_right(widget)`  Pixels scrolled past right edge of Widget's view window.
267
268Setting scroll position can be done with these functions:
269
270- :cpp:expr:`lv_obj_scroll_by(widget, dx, dy, anim_enable)`               Scroll by given amount of pixels.
271- :cpp:expr:`lv_obj_scroll_by_bounded(widget, dx, dy, animation_enable)`  Scroll by given amount of pixels.
272- :cpp:expr:`lv_obj_scroll_to(widget, x, y, animation_enable)`            Scroll to given coordinate on Widget.
273- :cpp:expr:`lv_obj_scroll_to_x(widget, x, animation_enable)`             Scroll to X coordinate on Widget.
274- :cpp:expr:`lv_obj_scroll_to_y(widget, y, animation_enable)`             Scroll to Y coordinate on Widget.
275- :cpp:expr:`lv_obj_scroll_to_view(widget, animation_enable)`             Scroll ``obj``'s parent Widget until ``obj`` becomes visible.
276- :cpp:expr:`lv_obj_scroll_to_view_recursive(widget, animation_enable)`   Scroll ``obj``'s parent Widgets recursively until ``obj`` becomes visible.
277
278
279
280Self Size
281*********
282
283Self size is a property of a Widget. Normally, the user shouldn't use
284this parameter but if a custom widget is created it might be useful.
285
286In short, self size establishes the size of a Widget's content. To
287understand it better take the example of a table. Let's say it has 10
288rows each with 50 px height. So the total height of the content is 500
289px. In other words the "self height" is 500 px. If the user sets only
290200 px height for the table LVGL will see that the self size is larger
291and make the table scrollable.
292
293This means not only the children can make a Widget scrollable but a
294larger self size will as well.
295
296LVGL uses the :cpp:enumerator:`LV_EVENT_GET_SELF_SIZE` event to get the self size of
297a Widget. Here is an example to see how to handle the event:
298
299.. code-block:: c
300
301    if(event_code == LV_EVENT_GET_SELF_SIZE) {
302        lv_point_t * p = lv_event_get_param(e);
303
304        /* If x or y < 0 then it doesn't need to be calculated now. */
305        if(p->x >= 0) {
306            p->x = 200; /* Set or calculate self width. */
307        }
308
309        if(p->y >= 0) {
310            p->y = 50;  /* Set or calculate self height. */
311        }
312    }
313
314
315
316.. _scroll_example:
317
318Examples
319********
320
321.. include:: ../../examples/scroll/index.rst
322
323.. _scroll_api:
324
325
326
327API
328***
329