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