1 /**
2 * @file lv_page.c
3 *
4 */
5
6 /*********************
7 * INCLUDES
8 *********************/
9 #include "../lv_widgets/lv_page.h"
10 #if LV_USE_PAGE != 0
11
12 #include "../lv_misc/lv_debug.h"
13 #include "../lv_core/lv_group.h"
14 #include "../lv_draw/lv_draw.h"
15 #include "../lv_themes/lv_theme.h"
16 #include "../lv_core/lv_refr.h"
17 #include "../lv_misc/lv_anim.h"
18 #include "../lv_misc/lv_math.h"
19
20 /*********************
21 * DEFINES
22 *********************/
23 #define LV_OBJX_NAME "lv_page"
24
25 #define LV_PAGE_SB_MIN_SIZE (LV_DPI / 8)
26
27 /*[ms] Scroll anim time on `lv_page_scroll_up/down/left/rigth`*/
28 #define LV_PAGE_SCROLL_ANIM_TIME 200
29
30 #define LV_PAGE_END_FLASH_SIZE (LV_DPI / 4)
31 #define LV_PAGE_END_ANIM_TIME 300
32 #define LV_PAGE_END_ANIM_WAIT_TIME 300
33
34 #if LV_USE_ANIMATION == 0
35 #undef LV_PAGE_DEF_ANIM_TIME
36 #define LV_PAGE_DEF_ANIM_TIME 0 /*No animation*/
37 #endif
38
39 /**********************
40 * TYPEDEFS
41 **********************/
42
43 /**********************
44 * STATIC PROTOTYPES
45 **********************/
46 static void scrlbar_refresh(lv_obj_t * page);
47 static void scrl_reposition(lv_obj_t * page);
48 static lv_design_res_t lv_page_design(lv_obj_t * page, const lv_area_t * clip_area, lv_design_mode_t mode);
49 static lv_res_t lv_page_signal(lv_obj_t * page, lv_signal_t sign, void * param);
50 static lv_style_list_t * lv_page_get_style(lv_obj_t * page, uint8_t part);
51 static lv_res_t lv_page_scrollable_signal(lv_obj_t * scrl, lv_signal_t sign, void * param);
52 static void scrl_def_event_cb(lv_obj_t * scrl, lv_event_t event);
53 static void refr_ext_draw_pad(lv_obj_t * page);
54 #if LV_USE_ANIMATION
55 static void edge_flash_anim(void * page, lv_anim_value_t v);
56 static void edge_flash_anim_end(lv_anim_t * a);
57 static void get_edge_flash_area(lv_obj_t * page, lv_area_t * area, lv_coord_t state);
58 #endif
59
60 /**********************
61 * STATIC VARIABLES
62 **********************/
63 static lv_design_cb_t ancestor_design;
64 static lv_signal_cb_t ancestor_signal;
65
66 /**********************
67 * MACROS
68 **********************/
69
70 /**********************
71 * GLOBAL FUNCTIONS
72 **********************/
73
74 /**
75 * Create a page objects
76 * @param par pointer to an object, it will be the parent of the new page
77 * @param copy pointer to a page object, if not NULL then the new object will be copied from it
78 * @return pointer to the created page
79 */
lv_page_create(lv_obj_t * par,const lv_obj_t * copy)80 lv_obj_t * lv_page_create(lv_obj_t * par, const lv_obj_t * copy)
81 {
82 LV_LOG_TRACE("page create started");
83
84 /*Create the ancestor object*/
85 lv_obj_t * page = lv_cont_create(par, copy);
86 LV_ASSERT_MEM(page);
87 if(page == NULL) return NULL;
88
89 if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_cb(page);
90 if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_cb(page);
91
92 /*Allocate the object type specific extended data*/
93 lv_page_ext_t * ext = lv_obj_allocate_ext_attr(page, sizeof(lv_page_ext_t));
94 LV_ASSERT_MEM(ext);
95 if(ext == NULL) {
96 lv_obj_del(page);
97 return NULL;
98 }
99
100 ext->scrl = NULL;
101 lv_style_list_init(&ext->scrlbar.style);
102 ext->scrlbar.hor_draw = 0;
103 ext->scrlbar.ver_draw = 0;
104 ext->scrlbar.mode = LV_SCROLLBAR_MODE_AUTO;
105 #if LV_USE_ANIMATION
106 lv_style_list_init(&ext->edge_flash.style);
107 ext->edge_flash.enabled = 0;
108 ext->edge_flash.bottom_ip = 0;
109 ext->edge_flash.top_ip = 0;
110 ext->edge_flash.left_ip = 0;
111 ext->edge_flash.right_ip = 0;
112 ext->edge_flash.state = 0;
113 ext->anim_time = LV_PAGE_DEF_ANIM_TIME;
114 #endif
115 ext->scroll_prop = 0;
116 ext->scroll_prop_obj = NULL;
117
118 /*Init the new page object*/
119 if(copy == NULL) {
120 ext->scrl = lv_cont_create(page, NULL);
121 lv_obj_set_focus_parent(ext->scrl, true);
122 lv_obj_set_drag(ext->scrl, true);
123 lv_obj_set_drag_throw(ext->scrl, true);
124 lv_obj_add_protect(ext->scrl, LV_PROTECT_PARENT | LV_PROTECT_PRESS_LOST);
125 lv_cont_set_fit(ext->scrl, LV_FIT_MAX);
126 lv_obj_set_event_cb(ext->scrl, scrl_def_event_cb); /*Propagate some event to the background
127 object by default for convenience */
128 lv_obj_set_signal_cb(ext->scrl, lv_page_scrollable_signal);
129
130 /* Add the signal function only if 'scrolling' is created
131 + * because everything has to be ready before any signal is received*/
132 lv_obj_set_signal_cb(page, lv_page_signal);
133 lv_obj_set_design_cb(page, lv_page_design);
134
135 lv_page_set_scrollbar_mode(page, ext->scrlbar.mode);
136
137 lv_theme_apply(page, LV_THEME_PAGE);
138
139 }
140 else {
141 lv_page_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
142 ext->scrl = lv_cont_create(page, copy_ext->scrl);
143 lv_obj_set_signal_cb(ext->scrl, lv_page_scrollable_signal);
144
145 lv_style_list_copy(&ext->scrlbar.style, ©_ext->scrlbar.style);
146 #if LV_USE_ANIMATION
147 lv_style_list_copy(&ext->edge_flash.style, ©_ext->edge_flash.style);
148 #endif
149
150 /* Add the signal function only if 'scrolling' is created
151 * because everything has to be ready before any signal is received*/
152 lv_obj_set_signal_cb(page, lv_page_signal);
153 lv_obj_set_design_cb(page, lv_page_design);
154
155 lv_page_set_scrollbar_mode(page, copy_ext->scrlbar.mode);
156 }
157
158
159 scrlbar_refresh(page);
160
161 LV_LOG_INFO("page created");
162
163 return page;
164 }
165
166 /**
167 * Delete all children of the scrl object, without deleting scrl child.
168 * @param page pointer to an object
169 */
lv_page_clean(lv_obj_t * page)170 void lv_page_clean(lv_obj_t * page)
171 {
172 LV_ASSERT_OBJ(page, LV_OBJX_NAME);
173
174 lv_obj_t * scrl = lv_page_get_scrollable(page);
175 lv_obj_clean(scrl);
176 }
177
178 /*=====================
179 * Setter functions
180 *====================*/
181
182 /**
183 * Set the scroll bar mode on a page
184 * @param page pointer to a page object
185 * @param sb_mode the new mode from 'lv_page_sb.mode_t' enum
186 */
lv_page_set_scrollbar_mode(lv_obj_t * page,lv_scrollbar_mode_t sb_mode)187 void lv_page_set_scrollbar_mode(lv_obj_t * page, lv_scrollbar_mode_t sb_mode)
188 {
189 LV_ASSERT_OBJ(page, LV_OBJX_NAME);
190
191 lv_page_ext_t * ext = lv_obj_get_ext_attr(page);
192 if(ext->scrlbar.mode == sb_mode) return;
193
194 if(sb_mode == LV_SCROLLBAR_MODE_HIDE)
195 ext->scrlbar.mode |= LV_SCROLLBAR_MODE_HIDE; /*Set the hidden flag*/
196 else if(sb_mode == LV_SCROLLBAR_MODE_UNHIDE)
197 ext->scrlbar.mode &= (~LV_SCROLLBAR_MODE_HIDE); /*Clear the hidden flag*/
198 else {
199 if(ext->scrlbar.mode & LV_SCROLLBAR_MODE_HIDE) sb_mode |= LV_SCROLLBAR_MODE_HIDE;
200 ext->scrlbar.mode = sb_mode;
201 }
202
203 ext->scrlbar.hor_draw = 0;
204 ext->scrlbar.ver_draw = 0;
205
206 scrlbar_refresh(page);
207 lv_obj_invalidate(page);
208 }
209
210 /**
211 * Set the animation time for the page
212 * @param page pointer to a page object
213 * @param anim_time animation time in milliseconds
214 */
lv_page_set_anim_time(lv_obj_t * page,uint16_t anim_time)215 void lv_page_set_anim_time(lv_obj_t * page, uint16_t anim_time)
216 {
217 LV_ASSERT_OBJ(page, LV_OBJX_NAME);
218
219 #if LV_USE_ANIMATION
220 lv_page_ext_t * ext = lv_obj_get_ext_attr(page);
221 ext->anim_time = anim_time;
222 #else
223 (void)page; /*Unused*/
224 (void)anim_time; /*Unused*/
225 #endif
226 }
227
228 /**
229 * Enable the scroll propagation feature. If enabled then the page will move its parent if there is
230 * no more space to scroll.
231 * The page needs to have a page-like parent (e.g. `lv_page`, `lv_tabview` tab, `lv_win` content area etc)
232 * If enabled drag direction will be changed `LV_DRAG_DIR_ONE` automatically to allow scrolling only in one direction at one time.
233 * @param page pointer to a Page
234 * @param en true or false to enable/disable scroll propagation
235 */
lv_page_set_scroll_propagation(lv_obj_t * page,bool en)236 void lv_page_set_scroll_propagation(lv_obj_t * page, bool en)
237 {
238 LV_ASSERT_OBJ(page, LV_OBJX_NAME);
239
240 lv_page_ext_t * ext = lv_obj_get_ext_attr(page);
241 if(en) lv_obj_set_drag_dir(ext->scrl, LV_DRAG_DIR_ONE);
242 else lv_obj_set_drag_dir(ext->scrl, LV_DRAG_DIR_BOTH);
243
244 ext->scroll_prop = en ? 1 : 0;
245 }
246
247 /**
248 * Enable the edge flash effect. (Show an arc when the an edge is reached)
249 * @param page pointer to a Page
250 * @param en true or false to enable/disable end flash
251 */
lv_page_set_edge_flash(lv_obj_t * page,bool en)252 void lv_page_set_edge_flash(lv_obj_t * page, bool en)
253 {
254 LV_ASSERT_OBJ(page, LV_OBJX_NAME);
255
256 #if LV_USE_ANIMATION
257 lv_page_ext_t * ext = lv_obj_get_ext_attr(page);
258 ext->edge_flash.enabled = en ? 1 : 0;
259 #else
260 (void)page;
261 (void)en;
262 #endif
263 }
264
265 /*=====================
266 * Getter functions
267 *====================*/
268
269 /**
270 * Get the scrollable object of a page
271 * @param page pointer to a page object
272 * @return pointer to a container which is the scrollable part of the page
273 */
lv_page_get_scrollable(const lv_obj_t * page)274 lv_obj_t * lv_page_get_scrollable(const lv_obj_t * page)
275 {
276 LV_ASSERT_OBJ(page, LV_OBJX_NAME);
277
278 lv_page_ext_t * ext = lv_obj_get_ext_attr(page);
279
280 return ext->scrl;
281 }
282
283 /**
284 * Get the animation time
285 * @param page pointer to a page object
286 * @return the animation time in milliseconds
287 */
lv_page_get_anim_time(const lv_obj_t * page)288 uint16_t lv_page_get_anim_time(const lv_obj_t * page)
289 {
290 LV_ASSERT_OBJ(page, LV_OBJX_NAME);
291
292 #if LV_USE_ANIMATION
293 lv_page_ext_t * ext = lv_obj_get_ext_attr(page);
294 return ext->anim_time;
295 #else
296 (void)page; /*Unused*/
297 return 0;
298 #endif
299 }
300
301 /**
302 * Set the scroll bar mode on a page
303 * @param page pointer to a page object
304 * @return the mode from 'lv_page_sb.mode_t' enum
305 */
lv_page_get_scrollbar_mode(const lv_obj_t * page)306 lv_scrollbar_mode_t lv_page_get_scrollbar_mode(const lv_obj_t * page)
307 {
308 LV_ASSERT_OBJ(page, LV_OBJX_NAME);
309
310 lv_page_ext_t * ext = lv_obj_get_ext_attr(page);
311 return ext->scrlbar.mode;
312 }
313
314 /**
315 * Get the scroll propagation property
316 * @param page pointer to a Page
317 * @return true or false
318 */
lv_page_get_scroll_propagation(lv_obj_t * page)319 bool lv_page_get_scroll_propagation(lv_obj_t * page)
320 {
321 LV_ASSERT_OBJ(page, LV_OBJX_NAME);
322
323 lv_page_ext_t * ext = lv_obj_get_ext_attr(page);
324 return ext->scroll_prop ? true : false;
325 }
326
327 /**
328 * Get the edge flash effect property.
329 * @param page pointer to a Page
330 * return true or false
331 */
lv_page_get_edge_flash(lv_obj_t * page)332 bool lv_page_get_edge_flash(lv_obj_t * page)
333 {
334 LV_ASSERT_OBJ(page, LV_OBJX_NAME);
335
336 #if LV_USE_ANIMATION
337 lv_page_ext_t * ext = lv_obj_get_ext_attr(page);
338 return ext->edge_flash.enabled == 0 ? false : true;
339 #else
340 (void)page;
341 return false;
342 #endif
343 }
344
345 /**
346 * Get that width which can be set to the children to still not cause overflow (show scrollbars)
347 * @param page pointer to a page object
348 * @return the width which still fits into the page
349 */
lv_page_get_width_fit(lv_obj_t * page)350 lv_coord_t lv_page_get_width_fit(lv_obj_t * page)
351 {
352 LV_ASSERT_OBJ(page, LV_OBJX_NAME);
353
354 lv_page_ext_t * ext = lv_obj_get_ext_attr(page);
355 lv_style_int_t bg_left = lv_obj_get_style_pad_left(page, LV_PAGE_PART_BG);
356 lv_style_int_t bg_right = lv_obj_get_style_pad_right(page, LV_PAGE_PART_BG);
357 lv_style_int_t scrl_left = lv_obj_get_style_pad_left(ext->scrl, LV_CONT_PART_MAIN);
358 lv_style_int_t scrl_right = lv_obj_get_style_pad_right(ext->scrl, LV_CONT_PART_MAIN);
359
360 return lv_obj_get_width(page) - bg_left - bg_right - scrl_left - scrl_right;
361 }
362
363 /**
364 * Get that height which can be set to the children to still not cause overflow (show scrollbars)
365 * @param page pointer to a page object
366 * @return the height which still fits into the page
367 */
lv_page_get_height_fit(lv_obj_t * page)368 lv_coord_t lv_page_get_height_fit(lv_obj_t * page)
369 {
370 LV_ASSERT_OBJ(page, LV_OBJX_NAME);
371
372 lv_page_ext_t * ext = lv_obj_get_ext_attr(page);
373
374 lv_style_int_t bg_top = lv_obj_get_style_pad_top(page, LV_PAGE_PART_BG);
375 lv_style_int_t bg_bottom = lv_obj_get_style_pad_bottom(page, LV_PAGE_PART_BG);
376 lv_style_int_t scrl_top = lv_obj_get_style_pad_top(ext->scrl, LV_CONT_PART_MAIN);
377 lv_style_int_t scrl_bottom = lv_obj_get_style_pad_bottom(ext->scrl, LV_CONT_PART_MAIN);
378
379 return lv_obj_get_height(page) - bg_top - bg_bottom - scrl_top - scrl_bottom;
380 }
381
382 /**
383 * Divide the width of the object and get the width of a given number of columns.
384 * Take into account the paddings of the background and scrollable too.
385 * @param page pointer to an object
386 * @param div indicates how many columns are assumed.
387 * If 1 the width will be set the the parent's width
388 * If 2 only half parent width - inner padding of the parent
389 * If 3 only third parent width - 2 * inner padding of the parent
390 * @param span how many columns are combined
391 * @return the width according to the given parameters
392 */
lv_page_get_width_grid(lv_obj_t * page,uint8_t div,uint8_t span)393 lv_coord_t lv_page_get_width_grid(lv_obj_t * page, uint8_t div, uint8_t span)
394 {
395
396 lv_coord_t obj_w = lv_page_get_width_fit(page);
397 lv_style_int_t pinner = lv_obj_get_style_pad_inner(page, LV_PAGE_PART_SCROLLABLE);
398
399 lv_coord_t r = (obj_w - (div - 1) * pinner) / div;
400
401 r = r * span + (span - 1) * pinner;
402 return r;
403 }
404
405 /**
406 * Divide the height of the object and get the height of a given number of rows.
407 * Take into account the paddings of the background and scrollable too.
408 * @param obj pointer to an object
409 * @param div indicates how many rows are assumed.
410 * If 1 the height will be set the the parent's height
411 * If 2 only half parent height - inner padding of the parent
412 * If 3 only third parent height - 2 * inner padding of the parent
413 * @param span how many rows are combined
414 * @return the height according to the given parameters
415 */
lv_page_get_height_grid(lv_obj_t * page,uint8_t div,uint8_t span)416 lv_coord_t lv_page_get_height_grid(lv_obj_t * page, uint8_t div, uint8_t span)
417 {
418 lv_coord_t obj_h = lv_page_get_height_fit(page);
419 lv_style_int_t pinner = lv_obj_get_style_pad_inner(page, LV_PAGE_PART_SCROLLABLE);
420
421 lv_coord_t r = (obj_h - (div - 1) * pinner) / div;
422
423 r = r * span + (span - 1) * pinner;
424 return r;
425 }
426
427 /*=====================
428 * Other functions
429 *====================*/
430
431 /**
432 * Find whether the page has been scrolled to a certain edge.
433 * @param page Page object
434 * @param edge Edge to check
435 * @return true if the page is on the specified edge
436 */
lv_page_on_edge(lv_obj_t * page,lv_page_edge_t edge)437 bool lv_page_on_edge(lv_obj_t * page, lv_page_edge_t edge)
438 {
439 lv_obj_t * scrl = lv_page_get_scrollable(page);
440 lv_area_t page_coords;
441 lv_area_t scrl_coords;
442
443 lv_obj_get_coords(scrl, &scrl_coords);
444 lv_obj_get_coords(page, &page_coords);
445
446 lv_style_int_t left = lv_obj_get_style_pad_left(page, LV_PAGE_PART_BG);
447 lv_style_int_t right = lv_obj_get_style_pad_right(page, LV_PAGE_PART_BG);
448 lv_style_int_t top = lv_obj_get_style_pad_top(page, LV_PAGE_PART_BG);
449 lv_style_int_t bottom = lv_obj_get_style_pad_bottom(page, LV_PAGE_PART_BG);
450
451 if((edge & LV_PAGE_EDGE_TOP) && scrl_coords.y1 == page_coords.y1 + top) return true;
452 if((edge & LV_PAGE_EDGE_BOTTOM) && scrl_coords.y2 == page_coords.y2 - bottom) return true;
453 if((edge & LV_PAGE_EDGE_LEFT) && scrl_coords.x1 == page_coords.x1 + left) return true;
454 if((edge & LV_PAGE_EDGE_RIGHT) && scrl_coords.x2 == page_coords.x2 - right) return true;
455
456 return false;
457 }
458
459 /**
460 * Glue the object to the page. After it the page can be moved (dragged) with this object too.
461 * @param obj pointer to an object on a page
462 * @param glue true: enable glue, false: disable glue
463 */
lv_page_glue_obj(lv_obj_t * obj,bool glue)464 void lv_page_glue_obj(lv_obj_t * obj, bool glue)
465 {
466 lv_obj_set_drag_parent(obj, glue);
467 lv_obj_set_drag(obj, glue);
468 }
469
470 /**
471 * Focus on an object. It ensures that the object will be visible on the page.
472 * @param page pointer to a page object
473 * @param obj pointer to an object to focus (must be on the page)
474 * @param anim_en LV_ANIM_ON to focus with animation; LV_ANIM_OFF to focus without animation
475 */
lv_page_focus(lv_obj_t * page,const lv_obj_t * obj,lv_anim_enable_t anim_en)476 void lv_page_focus(lv_obj_t * page, const lv_obj_t * obj, lv_anim_enable_t anim_en)
477 {
478 lv_page_ext_t * ext = lv_obj_get_ext_attr(page);
479
480 #if LV_USE_ANIMATION
481 /* Be sure there is no position changing animation in progress
482 * because it can override the current changes*/
483 lv_anim_del(page, (lv_anim_exec_xcb_t)lv_obj_set_x);
484 lv_anim_del(page, (lv_anim_exec_xcb_t)lv_obj_set_y);
485 lv_anim_del(ext->scrl, (lv_anim_exec_xcb_t)lv_obj_set_x);
486 lv_anim_del(ext->scrl, (lv_anim_exec_xcb_t)lv_obj_set_y);
487 #endif
488
489 /*if using focus mode, change target to parent*/
490 obj = lv_obj_get_focused_obj(obj);
491
492
493 /*If obj is higher then the page focus where the "error" is smaller*/
494 lv_coord_t obj_y = obj->coords.y1 - ext->scrl->coords.y1;
495 lv_coord_t obj_h = lv_obj_get_height(obj);
496 lv_coord_t scrlable_y = lv_obj_get_y(ext->scrl);
497 lv_coord_t page_h = lv_obj_get_height(page);
498
499 lv_style_int_t bg_top = lv_obj_get_style_pad_top(page, LV_PAGE_PART_BG);
500 lv_style_int_t bg_bottom = lv_obj_get_style_pad_bottom(page, LV_PAGE_PART_BG);
501 lv_style_int_t scrl_top = lv_obj_get_style_pad_top(ext->scrl, LV_CONT_PART_MAIN);
502 lv_style_int_t scrl_bottom = lv_obj_get_style_pad_bottom(ext->scrl, LV_CONT_PART_MAIN);
503
504 lv_coord_t top_err = -((scrlable_y + obj_y) - bg_top);
505 lv_coord_t bot_err = scrlable_y + obj_y + obj_h - (page_h - bg_bottom);
506
507 /*Out of the page on the top*/
508 if((obj_h <= page_h && top_err > 0) || (obj_h > page_h && top_err < bot_err)) {
509 /*Calculate a new position and let some space above*/
510 scrlable_y = -(obj_y - scrl_top - bg_top);
511 scrlable_y += scrl_top;
512 }
513 /*Out of the page on the bottom*/
514 else if((obj_h <= page_h && bot_err > 0) || (obj_h > page_h && top_err >= bot_err)) {
515 /*Calculate a new position and let some space below*/
516 scrlable_y = -(obj_y + scrl_bottom + bg_bottom);
517 scrlable_y -= scrl_bottom;
518 scrlable_y += page_h - obj_h;
519 }
520
521
522 /*If obj is wider then the page focus where the "error" is smaller*/
523 lv_coord_t obj_x = obj->coords.x1 - ext->scrl->coords.x1;
524 lv_coord_t obj_w = lv_obj_get_width(obj);
525 lv_coord_t scrlable_x = lv_obj_get_x(ext->scrl);
526 lv_coord_t page_w = lv_obj_get_width(page);
527
528 lv_style_int_t bg_left = lv_obj_get_style_pad_left(page, LV_PAGE_PART_BG);
529 lv_style_int_t bg_right = lv_obj_get_style_pad_right(page, LV_PAGE_PART_BG);
530 lv_style_int_t scrl_left = lv_obj_get_style_pad_top(ext->scrl, LV_CONT_PART_MAIN);
531 lv_style_int_t scrl_right = lv_obj_get_style_pad_bottom(ext->scrl, LV_CONT_PART_MAIN);
532
533 lv_coord_t left_err = -((scrlable_x + obj_x) - bg_left);
534 lv_coord_t right_err = scrlable_x + obj_x + obj_w - (page_w - bg_right);
535
536 /*Out of the page on the left*/
537 if((obj_w <= page_w && left_err > 0) || (obj_w > page_w && left_err < right_err)) {
538 /*Calculate a new position and let some space on the side*/
539 scrlable_x = -(obj_x - scrl_left - bg_left);
540 scrlable_x += scrl_left;
541 }
542 /*Out of the page on the rigth*/
543 else if((obj_w <= page_w && right_err > 0) || (obj_w > page_w && left_err >= right_err)) {
544 /*Calculate a new position and let some space on the side*/
545 scrlable_x = -(obj_x + scrl_right + bg_right);
546 scrlable_x -= scrl_right;
547 scrlable_x += page_w - obj_w;
548 }
549
550 if(anim_en == LV_ANIM_OFF || lv_page_get_anim_time(page) == 0) {
551 lv_obj_set_y(ext->scrl, scrlable_y);
552 lv_obj_set_x(ext->scrl, scrlable_x);
553 }
554 else {
555 #if LV_USE_ANIMATION
556 lv_anim_t a;
557 lv_anim_init(&a);
558 lv_anim_set_var(&a, ext->scrl);
559 lv_anim_set_exec_cb(&a, (lv_anim_exec_xcb_t)lv_obj_set_y);
560 lv_anim_set_values(&a, lv_obj_get_y(ext->scrl), scrlable_y);
561 lv_anim_set_time(&a, lv_page_get_anim_time(page));
562 lv_anim_start(&a);
563
564 lv_anim_set_values(&a, lv_obj_get_x(ext->scrl), scrlable_x);
565 lv_anim_set_exec_cb(&a, (lv_anim_exec_xcb_t)lv_obj_set_x);
566 lv_anim_start(&a);
567 #endif
568 }
569 }
570
571 /**
572 * Scroll the page horizontally
573 * @param page pointer to a page object
574 * @param dist the distance to scroll (< 0: scroll right; > 0 scroll left)
575 */
lv_page_scroll_hor(lv_obj_t * page,lv_coord_t dist)576 void lv_page_scroll_hor(lv_obj_t * page, lv_coord_t dist)
577 {
578 lv_obj_t * scrl = lv_page_get_scrollable(page);
579
580 #if LV_USE_ANIMATION
581 lv_anim_t a;
582 lv_anim_init(&a);
583 lv_anim_set_var(&a, scrl);
584 lv_anim_set_exec_cb(&a, (lv_anim_exec_xcb_t)lv_obj_set_x);
585 lv_anim_set_values(&a, lv_obj_get_x(scrl), lv_obj_get_x(scrl) + dist);
586 lv_anim_set_time(&a, lv_page_get_anim_time(page));
587 lv_anim_start(&a);
588 #else
589 lv_obj_set_x(scrl, lv_obj_get_x(scrl) + dist);
590 #endif
591 }
592
593 /**
594 * Scroll the page vertically
595 * @param page pointer to a page object
596 * @param dist the distance to scroll (< 0: scroll down; > 0 scroll up)
597 */
lv_page_scroll_ver(lv_obj_t * page,lv_coord_t dist)598 void lv_page_scroll_ver(lv_obj_t * page, lv_coord_t dist)
599 {
600 lv_obj_t * scrl = lv_page_get_scrollable(page);
601
602 #if LV_USE_ANIMATION
603 lv_anim_t a;
604 lv_anim_init(&a);
605 lv_anim_set_var(&a, scrl);
606 lv_anim_set_exec_cb(&a, (lv_anim_exec_xcb_t)lv_obj_set_y);
607 lv_anim_set_values(&a, lv_obj_get_y(scrl), lv_obj_get_y(scrl) + dist);
608 lv_anim_set_time(&a, lv_page_get_anim_time(page));
609 lv_anim_start(&a);
610 #else
611 lv_obj_set_y(scrl, lv_obj_get_y(scrl) + dist);
612 #endif
613 }
614
615 /**
616 * Not intended to use directly by the user but by other object types internally.
617 * Start an edge flash animation.
618 * @param page
619 * @param edge the edge to flash. Can be `LV_PAGE_EDGE_LEFT/RIGHT/TOP/BOTTOM`
620 */
lv_page_start_edge_flash(lv_obj_t * page,lv_page_edge_t edge)621 void lv_page_start_edge_flash(lv_obj_t * page, lv_page_edge_t edge)
622 {
623 #if LV_USE_ANIMATION
624 lv_page_ext_t * ext = lv_obj_get_ext_attr(page);
625 if(ext->edge_flash.enabled == 0) return;
626
627 if(ext->edge_flash.left_ip ||
628 ext->edge_flash.right_ip ||
629 ext->edge_flash.top_ip ||
630 ext->edge_flash.bottom_ip) {
631 return;
632 }
633
634 lv_anim_t a;
635 lv_anim_init(&a);
636 lv_anim_set_var(&a, page);
637 lv_anim_set_exec_cb(&a, (lv_anim_exec_xcb_t)edge_flash_anim);
638 lv_anim_set_values(&a, 0, LV_PAGE_END_FLASH_SIZE);
639 lv_anim_set_time(&a, lv_page_get_anim_time(page));
640 lv_anim_set_playback_time(&a, lv_page_get_anim_time(page));
641 lv_anim_set_playback_delay(&a, LV_PAGE_END_ANIM_WAIT_TIME);
642 lv_anim_set_ready_cb(&a, edge_flash_anim_end);
643 lv_anim_start(&a);
644
645 switch(edge) {
646 case LV_PAGE_EDGE_BOTTOM:
647 ext->edge_flash.bottom_ip = 1;
648 break;
649 case LV_PAGE_EDGE_TOP:
650 ext->edge_flash.top_ip = 1;
651 break;
652 case LV_PAGE_EDGE_LEFT:
653 ext->edge_flash.left_ip = 1;
654 break;
655 case LV_PAGE_EDGE_RIGHT:
656 ext->edge_flash.right_ip = 1;
657 break;
658 }
659
660 #else
661 LV_UNUSED(page);
662 LV_UNUSED(edge);
663 #endif
664 }
665
666 /**********************
667 * STATIC FUNCTIONS
668 **********************/
669
670 /**
671 * Handle the drawing related tasks of the pages
672 * @param page pointer to an object
673 * @param clip_area the object will be drawn only in this area
674 * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
675 * (return 'true' if yes)
676 * LV_DESIGN_DRAW: draw the object (always return 'true')
677 * LV_DESIGN_DRAW_POST: drawing after every children are drawn
678 * @param return an element of `lv_design_res_t`
679 */
lv_page_design(lv_obj_t * page,const lv_area_t * clip_area,lv_design_mode_t mode)680 static lv_design_res_t lv_page_design(lv_obj_t * page, const lv_area_t * clip_area, lv_design_mode_t mode)
681 {
682 if(mode == LV_DESIGN_COVER_CHK) {
683 return ancestor_design(page, clip_area, mode);
684 }
685
686 if(mode == LV_DESIGN_DRAW_MAIN) {
687 return ancestor_design(page, clip_area, mode);
688 }
689 else if(mode == LV_DESIGN_DRAW_POST) {
690 ancestor_design(page, clip_area, mode);
691 lv_page_ext_t * ext = lv_obj_get_ext_attr(page);
692
693 lv_area_t sb_hor_area;
694 lv_area_t sb_ver_area;
695 /*Convert the relative coordinates to absolute*/
696 lv_area_copy(&sb_hor_area, &ext->scrlbar.hor_area);
697 sb_hor_area.x1 += page->coords.x1;
698 sb_hor_area.y1 += page->coords.y1;
699 sb_hor_area.x2 += page->coords.x1;
700 sb_hor_area.y2 += page->coords.y1;
701
702 /*Convert the relative coordinates to absolute*/
703 lv_area_copy(&sb_ver_area, &ext->scrlbar.ver_area);
704 sb_ver_area.x1 += page->coords.x1;
705 sb_ver_area.y1 += page->coords.y1;
706 sb_ver_area.x2 += page->coords.x1;
707 sb_ver_area.y2 += page->coords.y1;
708
709 if((ext->scrlbar.hor_draw && _lv_area_is_on(&sb_hor_area, clip_area)) ||
710 (ext->scrlbar.ver_draw && _lv_area_is_on(&sb_ver_area, clip_area))) {
711 /*Draw the scrollbars*/
712 lv_draw_rect_dsc_t rect_dsc;
713 lv_draw_rect_dsc_init(&rect_dsc);
714 lv_obj_init_draw_rect_dsc(page, LV_PAGE_PART_SCROLLBAR, &rect_dsc);
715 if(ext->scrlbar.hor_draw && (ext->scrlbar.mode & LV_SCROLLBAR_MODE_HIDE) == 0) {
716 lv_draw_rect(&sb_hor_area, clip_area, &rect_dsc);
717 }
718
719 if(ext->scrlbar.ver_draw && (ext->scrlbar.mode & LV_SCROLLBAR_MODE_HIDE) == 0) {
720 lv_draw_rect(&sb_ver_area, clip_area, &rect_dsc);
721 }
722 }
723
724 #if LV_USE_ANIMATION
725 {
726
727
728 if(ext->edge_flash.left_ip || ext->edge_flash.right_ip || ext->edge_flash.top_ip ||
729 ext->edge_flash.bottom_ip) {
730 lv_area_t flash_area;
731 get_edge_flash_area(page, &flash_area, ext->edge_flash.state);
732
733 lv_draw_rect_dsc_t edge_draw_dsc;
734 lv_draw_rect_dsc_init(&edge_draw_dsc);
735 lv_obj_init_draw_rect_dsc(page, LV_PAGE_PART_EDGE_FLASH, &edge_draw_dsc);
736 edge_draw_dsc.radius = LV_RADIUS_CIRCLE;
737 uint32_t opa = (edge_draw_dsc.bg_opa * ext->edge_flash.state) / LV_PAGE_END_FLASH_SIZE;
738 edge_draw_dsc.bg_opa = opa;
739 lv_draw_rect(&flash_area, clip_area, &edge_draw_dsc);
740 }
741 }
742 #endif
743 }
744
745 return LV_DESIGN_RES_OK;
746 }
747
748 /**
749 * Signal function of the page
750 * @param page pointer to a page object
751 * @param sign a signal type from lv_signal_t enum
752 * @param param pointer to a signal specific variable
753 * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
754 */
lv_page_signal(lv_obj_t * page,lv_signal_t sign,void * param)755 static lv_res_t lv_page_signal(lv_obj_t * page, lv_signal_t sign, void * param)
756 {
757 lv_res_t res;
758 if(sign == LV_SIGNAL_GET_STYLE) {
759 lv_get_style_info_t * info = param;
760 info->result = lv_page_get_style(page, info->part);
761 if(info->result != NULL) return LV_RES_OK;
762 else return ancestor_signal(page, sign, param);
763 }
764 else if(sign == LV_SIGNAL_GET_STATE_DSC) {
765 lv_get_state_info_t * info = param;
766 if(info->part == LV_PAGE_PART_SCROLLABLE) info->result = lv_obj_get_state(lv_page_get_scrollable(page),
767 LV_CONT_PART_MAIN);
768 else info->result = lv_obj_get_state(page, info->part);
769 return LV_RES_OK;
770 }
771
772 /* Include the ancient signal function */
773 res = ancestor_signal(page, sign, param);
774 if(res != LV_RES_OK) return res;
775 if(sign == LV_SIGNAL_GET_TYPE) return lv_obj_handle_get_type_signal(param, LV_OBJX_NAME);
776
777 lv_page_ext_t * ext = lv_obj_get_ext_attr(page);
778 if(sign == LV_SIGNAL_CLEANUP) {
779 /*Check whether the object being deleted is propagating scroll to the parent */
780 if(ext->scroll_prop) {
781 lv_obj_t * parent_page = lv_obj_get_parent(lv_obj_get_parent(page));
782 lv_page_ext_t * parent_ext = lv_obj_get_ext_attr(parent_page);
783 if(parent_ext->scroll_prop_obj == page) {
784 parent_ext->scroll_prop_obj = NULL;
785 }
786 }
787
788 lv_obj_clean_style_list(page, LV_PAGE_PART_SCROLLBAR);
789 #if LV_USE_ANIMATION
790 lv_obj_clean_style_list(page, LV_PAGE_PART_EDGE_FLASH);
791 #endif
792 }
793 /*Automatically move children to the scrollable object*/
794 else if(sign == LV_SIGNAL_CHILD_CHG) {
795 lv_obj_t * child;
796 if(ext->scrl == NULL) return LV_RES_OK;
797
798 lv_fit_t fit_left = lv_page_get_scrl_fit_left(page);
799 lv_fit_t fit_right = lv_page_get_scrl_fit_right(page);
800 lv_fit_t fit_top = lv_page_get_scrl_fit_top(page);
801 lv_bidi_dir_t base_dir = lv_obj_get_base_dir(page);
802
803 lv_style_int_t scrl_left = lv_obj_get_style_pad_left(ext->scrl, LV_CONT_PART_MAIN);
804 lv_style_int_t scrl_right = lv_obj_get_style_pad_right(ext->scrl, LV_CONT_PART_MAIN);
805 lv_style_int_t scrl_top = lv_obj_get_style_pad_top(ext->scrl, LV_CONT_PART_MAIN);
806
807 child = lv_obj_get_child(page, NULL);
808 while(child != NULL) {
809 if(lv_obj_is_protected(child, LV_PROTECT_PARENT) == false) {
810 lv_obj_t * tmp = child;
811 child = lv_obj_get_child(page, child); /*Get the next child before move this*/
812
813 /* Reposition the child to take padding into account
814 * It's required to keep new the object on the same coordinate if FIT is enabled.*/
815 if((tmp->coords.x1 == page->coords.x1) &&
816 (fit_left == LV_FIT_TIGHT || fit_left == LV_FIT_MAX) &&
817 base_dir != LV_BIDI_DIR_RTL) {
818 tmp->coords.x1 += scrl_left;
819 tmp->coords.x2 += scrl_left;
820 }
821 else if((tmp->coords.x2 == page->coords.x2) &&
822 (fit_right == LV_FIT_TIGHT || fit_right == LV_FIT_MAX)
823 && base_dir == LV_BIDI_DIR_RTL) {
824 tmp->coords.x1 -= scrl_right;
825 tmp->coords.x2 -= scrl_right;
826 }
827 if((tmp->coords.y1 == page->coords.y1) && (fit_top == LV_FIT_TIGHT || fit_top == LV_FIT_MAX)) {
828 tmp->coords.y1 += scrl_top;
829 tmp->coords.y2 += scrl_top;
830 }
831 lv_obj_set_parent(tmp, ext->scrl);
832 }
833 else {
834 child = lv_obj_get_child(page, child);
835 }
836 }
837 }
838 else if(sign == LV_SIGNAL_STYLE_CHG) {
839 lv_style_int_t sb_width = lv_obj_get_style_size(page, LV_PAGE_PART_SCROLLBAR);
840 lv_area_set_height(&ext->scrlbar.hor_area, sb_width);
841 lv_area_set_width(&ext->scrlbar.ver_area, sb_width);
842
843 /*The scrollbars are important only if they are visible now*/
844 if(ext->scrlbar.hor_draw || ext->scrlbar.ver_draw) scrlbar_refresh(page);
845
846 /*Refresh the ext. size because the scrollbars might be positioned out of the page*/
847 refr_ext_draw_pad(page);
848 }
849 else if(sign == LV_SIGNAL_COORD_CHG) {
850 /*Refresh the scrollbar and notify the scrl if the size is changed*/
851 if(ext->scrl != NULL && (lv_obj_get_width(page) != lv_area_get_width(param) ||
852 lv_obj_get_height(page) != lv_area_get_height(param))) {
853 ext->scrl->signal_cb(ext->scrl, LV_SIGNAL_COORD_CHG, &ext->scrl->coords);
854
855 /*The scrollbars are important only if they are visible now*/
856 if(ext->scrlbar.hor_draw || ext->scrlbar.ver_draw) scrlbar_refresh(page);
857 }
858 }
859 else if(sign == LV_SIGNAL_REFR_EXT_DRAW_PAD) {
860 refr_ext_draw_pad(page);
861 }
862 else if(sign == LV_SIGNAL_CONTROL) {
863 #if LV_USE_GROUP
864 uint32_t c = *((uint32_t *)param);
865
866 if(c == LV_KEY_DOWN) {
867 lv_page_scroll_ver(page, -lv_obj_get_height(page) / 4);
868 }
869 else if(c == LV_KEY_UP) {
870 lv_page_scroll_ver(page, lv_obj_get_height(page) / 4);
871 }
872 else if(c == LV_KEY_RIGHT) {
873 /*If the page can't be scrolled horizontally because it's not wide enough then scroll it
874 * vertically*/
875 if(lv_page_get_scrl_width(page) <= lv_obj_get_width(page))
876 lv_page_scroll_ver(page, -lv_obj_get_height(page) / 4);
877 else
878 lv_page_scroll_hor(page, -lv_obj_get_width(page) / 4);
879 }
880 else if(c == LV_KEY_LEFT) {
881 /*If the page can't be scrolled horizontally because it's not wide enough then scroll it
882 * vertically*/
883 if(lv_page_get_scrl_width(page) <= lv_obj_get_width(page))
884 lv_page_scroll_ver(page, lv_obj_get_height(page) / 4);
885 else
886 lv_page_scroll_hor(page, lv_obj_get_width(page) / 4);
887 }
888 #endif
889 }
890 else if(sign == LV_SIGNAL_GET_EDITABLE) {
891 #if LV_USE_GROUP
892 bool * editable = (bool *)param;
893 *editable = true;
894 #endif
895 }
896
897 return res;
898 }
899
900 /**
901 * Signal function of the scrollable part of a page
902 * @param scrl pointer to the scrollable object
903 * @param sign a signal type from lv_signal_t enum
904 * @param param pointer to a signal specific variable
905 * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
906 */
lv_page_scrollable_signal(lv_obj_t * scrl,lv_signal_t sign,void * param)907 static lv_res_t lv_page_scrollable_signal(lv_obj_t * scrl, lv_signal_t sign, void * param)
908 {
909
910 if(sign == LV_SIGNAL_GET_STYLE) {
911 return ancestor_signal(scrl, sign, param);
912 }
913
914 lv_res_t res;
915
916 /* Include the ancient signal function */
917 res = ancestor_signal(scrl, sign, param);
918
919 if(res != LV_RES_OK) return res;
920 if(sign == LV_SIGNAL_GET_TYPE) return lv_obj_handle_get_type_signal(param, "");
921
922 lv_obj_t * page = lv_obj_get_parent(scrl);
923 lv_page_ext_t * page_ext = lv_obj_get_ext_attr(page);
924
925 if(sign == LV_SIGNAL_COORD_CHG) {
926 lv_obj_t * page_parent = lv_obj_get_parent(page);
927
928 /*Handle scroll propagation*/
929 lv_indev_t * indev = lv_indev_get_act();
930 if(page_ext->scroll_prop && indev) {
931 lv_point_t * drag_sum = &indev->proc.types.pointer.drag_sum;
932 lv_page_ext_t * parent_ext = lv_obj_get_ext_attr(lv_obj_get_parent(page_parent));
933 if(parent_ext->scroll_prop_obj == NULL) {
934 /*If the dragging just started or scroll is already propagated to this object
935 * enable the scroll propagation if the conditions are met*/
936 if((lv_indev_is_dragging(indev) == false || page_ext->scroll_prop_obj) && (drag_sum->y || drag_sum->x)) {
937 /*Propagate vertically?*/
938 if((drag_sum->y > 0 && lv_page_on_edge(page, LV_PAGE_EDGE_TOP)) ||
939 (drag_sum->y < 0 && lv_page_on_edge(page, LV_PAGE_EDGE_BOTTOM))) {
940 lv_obj_set_drag_parent(page, true);
941 lv_obj_set_drag_parent(scrl, true);
942 parent_ext->scroll_prop_obj = page;
943 }
944 /*Propagate horizontally?*/
945 if((drag_sum->x > 0 && lv_page_on_edge(page, LV_PAGE_EDGE_LEFT)) ||
946 (drag_sum->x < 0 && lv_page_on_edge(page, LV_PAGE_EDGE_RIGHT))) {
947 lv_obj_set_drag_parent(page, true);
948 lv_obj_set_drag_parent(scrl, true);
949 parent_ext->scroll_prop_obj = page;
950 }
951 }
952 }
953 }
954
955 scrl_reposition(page);
956
957 lv_page_ext_t * ext = lv_obj_get_ext_attr(page);
958
959 /*The scrollbars are important only if they are visible now or the scrollable's size has changed*/
960 if((ext->scrlbar.hor_draw || ext->scrlbar.ver_draw) ||
961 (lv_obj_get_width(scrl) != lv_area_get_width(param) || lv_obj_get_height(scrl) != lv_area_get_height(param))) {
962 scrlbar_refresh(page);
963 }
964 }
965 else if(sign == LV_SIGNAL_STYLE_CHG) {
966 scrl_reposition(page);
967 scrlbar_refresh(page);
968 }
969 else if(sign == LV_SIGNAL_DRAG_BEGIN) {
970 if(page_ext->scrlbar.mode == LV_SCROLLBAR_MODE_DRAG) {
971 scrlbar_refresh(page);
972 }
973 }
974 else if(sign == LV_SIGNAL_DRAG_END) {
975 /*Scroll propagation is finished on drag end*/
976 if(page_ext->scroll_prop_obj) {
977 lv_obj_t * scroller_page = page_ext->scroll_prop_obj;
978 lv_page_ext_t * scroller_page_ext = lv_obj_get_ext_attr(scroller_page);
979 page_ext->scroll_prop_obj = NULL;
980 lv_obj_set_drag_parent(scroller_page, false);
981 lv_obj_set_drag_parent(lv_page_get_scrollable(scroller_page), false);
982
983 /*Hide scrollbars if required*/
984 if(scroller_page_ext->scrlbar.mode == LV_SCROLLBAR_MODE_DRAG) {
985 lv_area_t sb_area_tmp;
986 if(scroller_page_ext->scrlbar.hor_draw) {
987 lv_area_copy(&sb_area_tmp, &scroller_page_ext->scrlbar.hor_area);
988 sb_area_tmp.x1 += scroller_page->coords.x1;
989 sb_area_tmp.y1 += scroller_page->coords.y1;
990 sb_area_tmp.x2 += scroller_page->coords.x1;
991 sb_area_tmp.y2 += scroller_page->coords.y1;
992 lv_obj_invalidate_area(scroller_page, &sb_area_tmp);
993 scroller_page_ext->scrlbar.hor_draw = 0;
994 }
995 if(scroller_page_ext->scrlbar.ver_draw) {
996 lv_area_copy(&sb_area_tmp, &scroller_page_ext->scrlbar.ver_area);
997 sb_area_tmp.x1 += scroller_page->coords.x1;
998 sb_area_tmp.y1 += scroller_page->coords.y1;
999 sb_area_tmp.x2 += scroller_page->coords.x1;
1000 sb_area_tmp.y2 += scroller_page->coords.y1;
1001 lv_obj_invalidate_area(scroller_page, &sb_area_tmp);
1002 scroller_page_ext->scrlbar.ver_draw = 0;
1003 }
1004 }
1005
1006 /*The scrolling can be chained so stop all of them*/
1007 lv_page_ext_t * scroller_ext = lv_obj_get_ext_attr(scroller_page);
1008 while(scroller_ext->scroll_prop_obj) {
1009 scroller_page = scroller_ext->scroll_prop_obj;
1010 scroller_ext->scroll_prop_obj = NULL;
1011 lv_obj_set_drag_parent(scroller_page, false);
1012 lv_obj_set_drag_parent(lv_page_get_scrollable(scroller_page), false);
1013
1014 /*Hide scrollbars if required*/
1015 if(scroller_page_ext->scrlbar.mode == LV_SCROLLBAR_MODE_DRAG) {
1016 scroller_page_ext->scrlbar.hor_draw = 0;
1017 scroller_page_ext->scrlbar.ver_draw = 0;
1018 lv_obj_invalidate(scroller_page);
1019 }
1020 scroller_ext = lv_obj_get_ext_attr(scroller_page);
1021 }
1022 }
1023
1024 /*Hide scrollbars if required*/
1025 if(page_ext->scrlbar.mode == LV_SCROLLBAR_MODE_DRAG) {
1026 lv_area_t sb_area_tmp;
1027 if(page_ext->scrlbar.hor_draw) {
1028 lv_area_copy(&sb_area_tmp, &page_ext->scrlbar.hor_area);
1029 sb_area_tmp.x1 += page->coords.x1;
1030 sb_area_tmp.y1 += page->coords.y1;
1031 sb_area_tmp.x2 += page->coords.x1;
1032 sb_area_tmp.y2 += page->coords.y1;
1033 lv_obj_invalidate_area(page, &sb_area_tmp);
1034 page_ext->scrlbar.hor_draw = 0;
1035 }
1036 if(page_ext->scrlbar.ver_draw) {
1037 lv_area_copy(&sb_area_tmp, &page_ext->scrlbar.ver_area);
1038 sb_area_tmp.x1 += page->coords.x1;
1039 sb_area_tmp.y1 += page->coords.y1;
1040 sb_area_tmp.x2 += page->coords.x1;
1041 sb_area_tmp.y2 += page->coords.y1;
1042 lv_obj_invalidate_area(page, &sb_area_tmp);
1043 page_ext->scrlbar.ver_draw = 0;
1044 }
1045 }
1046 }
1047 else if(sign == LV_SIGNAL_CLEANUP) {
1048 page_ext->scrl = NULL;
1049
1050 }
1051 return res;
1052 }
1053
1054 /**
1055 * Propagate the input device related event of the scrollable to the parent page background
1056 * It is used by default if the scrollable's event is not specified
1057 * @param scrl pointer to the page's scrollable object
1058 * @param event type of the event
1059 * @param data data of the event
1060 */
scrl_def_event_cb(lv_obj_t * scrl,lv_event_t event)1061 static void scrl_def_event_cb(lv_obj_t * scrl, lv_event_t event)
1062 {
1063 lv_obj_t * page = lv_obj_get_parent(scrl);
1064
1065 /*clang-format off*/
1066 if(event == LV_EVENT_PRESSED || event == LV_EVENT_PRESSING || event == LV_EVENT_PRESS_LOST ||
1067 event == LV_EVENT_RELEASED || event == LV_EVENT_SHORT_CLICKED || event == LV_EVENT_CLICKED ||
1068 event == LV_EVENT_LONG_PRESSED || event == LV_EVENT_LONG_PRESSED_REPEAT ||
1069 event == LV_EVENT_DRAG_BEGIN || event == LV_EVENT_DRAG_END || event == LV_EVENT_DRAG_THROW_BEGIN) {
1070 lv_event_send(page, event, lv_event_get_data());
1071 }
1072 /*clang-format on*/
1073 }
1074
1075 /**
1076 * Get the style descriptor of a part of the object
1077 * @param page pointer the object
1078 * @param part the part of the page. (LV_PAGE_PART_...)
1079 * @return pointer to the style descriptor of the specified part
1080 */
lv_page_get_style(lv_obj_t * page,uint8_t part)1081 static lv_style_list_t * lv_page_get_style(lv_obj_t * page, uint8_t part)
1082 {
1083 LV_ASSERT_OBJ(page, LV_OBJX_NAME);
1084
1085 lv_page_ext_t * ext = lv_obj_get_ext_attr(page);
1086 lv_style_list_t * style_dsc_p;
1087
1088 switch(part) {
1089 case LV_PAGE_PART_BG:
1090 style_dsc_p = &page->style_list;
1091 break;
1092 case LV_PAGE_PART_SCROLLABLE:
1093 style_dsc_p = lv_obj_get_style_list(ext->scrl, LV_CONT_PART_MAIN);
1094 break;
1095 case LV_PAGE_PART_SCROLLBAR:
1096 style_dsc_p = &ext->scrlbar.style;
1097 break;
1098 #if LV_USE_ANIMATION
1099 case LV_PAGE_PART_EDGE_FLASH:
1100 style_dsc_p = &ext->edge_flash.style;
1101 break;
1102 #endif
1103 default:
1104 style_dsc_p = NULL;
1105 }
1106
1107 return style_dsc_p;
1108 }
1109
scrl_reposition(lv_obj_t * page)1110 static void scrl_reposition(lv_obj_t * page)
1111 {
1112 /*Limit the position of the scrollable object to be always visible
1113 * (Do not let its edge inner then its parent respective edge)*/
1114 lv_obj_t * scrl = lv_page_get_scrollable(page);
1115 lv_coord_t new_x = lv_obj_get_x(scrl);
1116 lv_coord_t new_y = lv_obj_get_y(scrl);
1117 bool refr_x = false;
1118 bool refr_y = false;
1119 lv_area_t page_coords;
1120 lv_area_t scrl_coords;
1121 lv_obj_get_coords(scrl, &scrl_coords);
1122 lv_obj_get_coords(page, &page_coords);
1123
1124 lv_style_int_t left = lv_obj_get_style_pad_left(page, LV_PAGE_PART_BG);
1125 lv_style_int_t right = lv_obj_get_style_pad_right(page, LV_PAGE_PART_BG);
1126 lv_style_int_t top = lv_obj_get_style_pad_top(page, LV_PAGE_PART_BG);
1127 lv_style_int_t bottom = lv_obj_get_style_pad_bottom(page, LV_PAGE_PART_BG);
1128
1129 /*scrollable width smaller then page width? -> align to left*/
1130 if(lv_area_get_width(&scrl_coords) + left + right <= lv_area_get_width(&page_coords)) {
1131 if(scrl_coords.x1 != page_coords.x1 + left) {
1132 new_x = left;
1133 refr_x = true;
1134 }
1135 }
1136 else {
1137 /*The edges of the scrollable can not be in the page (minus hpad) */
1138 if(scrl_coords.x2 < page_coords.x2 - right) {
1139 new_x = lv_area_get_width(&page_coords) - lv_area_get_width(&scrl_coords) - right; /* Right align */
1140 refr_x = true;
1141 lv_page_start_edge_flash(page, LV_PAGE_EDGE_RIGHT);
1142 }
1143 else if(scrl_coords.x1 > page_coords.x1 + left) {
1144 new_x = left; /*Left align*/
1145 refr_x = true;
1146 lv_page_start_edge_flash(page, LV_PAGE_EDGE_LEFT);
1147 }
1148 }
1149
1150 /*scrollable height smaller then page height? -> align to top*/
1151 if(lv_area_get_height(&scrl_coords) + top + bottom <= lv_area_get_height(&page_coords)) {
1152 if(scrl_coords.y1 != page_coords.y1 + top) {
1153 new_y = top;
1154 refr_y = true;
1155 }
1156 }
1157 else {
1158 /*The edges of the scrollable can not be in the page (minus vpad) */
1159 if(scrl_coords.y2 < page_coords.y2 - bottom) {
1160 new_y = lv_area_get_height(&page_coords) - lv_area_get_height(&scrl_coords) - bottom; /* Bottom align */
1161 refr_y = true;
1162 lv_page_start_edge_flash(page, LV_PAGE_EDGE_BOTTOM);
1163 }
1164 else if(scrl_coords.y1 > page_coords.y1 + top) {
1165 new_y = top; /*Top align*/
1166 refr_y = true;
1167 lv_page_start_edge_flash(page, LV_PAGE_EDGE_TOP);
1168 }
1169 }
1170
1171 if(refr_x || refr_y) {
1172 lv_obj_set_pos(scrl, new_x, new_y);
1173 }
1174 }
1175
1176 /**
1177 * Refresh the position and size of the scroll bars.
1178 * @param page pointer to a page object
1179 */
scrlbar_refresh(lv_obj_t * page)1180 static void scrlbar_refresh(lv_obj_t * page)
1181 {
1182 lv_page_ext_t * ext = lv_obj_get_ext_attr(page);
1183 lv_obj_t * scrl = ext->scrl;
1184 lv_coord_t size_tmp;
1185 lv_coord_t scrl_w = lv_obj_get_width(scrl);
1186 lv_coord_t scrl_h = lv_obj_get_height(scrl);
1187 lv_coord_t obj_w = lv_obj_get_width(page);
1188 lv_coord_t obj_h = lv_obj_get_height(page);
1189
1190 lv_style_int_t sb_width = lv_obj_get_style_size(page, LV_PAGE_PART_SCROLLBAR);
1191 lv_style_int_t sb_right = lv_obj_get_style_pad_right(page, LV_PAGE_PART_SCROLLBAR);
1192 lv_style_int_t sb_bottom = lv_obj_get_style_pad_bottom(page, LV_PAGE_PART_SCROLLBAR);
1193
1194 lv_style_int_t bg_left = lv_obj_get_style_pad_left(page, LV_PAGE_PART_BG);
1195 lv_style_int_t bg_right = lv_obj_get_style_pad_right(page, LV_PAGE_PART_BG);
1196 lv_style_int_t bg_top = lv_obj_get_style_pad_top(page, LV_PAGE_PART_BG);
1197 lv_style_int_t bg_bottom = lv_obj_get_style_pad_bottom(page, LV_PAGE_PART_BG);
1198
1199 /*Always let 'scrollbar width' padding above, under, left and right to the scrollbars
1200 * else:
1201 * - horizontal and vertical scrollbars can overlap on the corners
1202 * - if the page has radius the scrollbar can be out of the radius */
1203 lv_coord_t sb_hor_pad = LV_MATH_MAX(sb_width, sb_right);
1204 lv_coord_t sb_ver_pad = LV_MATH_MAX(sb_width, sb_bottom);
1205
1206 if(ext->scrlbar.mode == LV_SCROLLBAR_MODE_OFF) return;
1207
1208 if(ext->scrlbar.mode == LV_SCROLLBAR_MODE_ON) {
1209 ext->scrlbar.hor_draw = 1;
1210 ext->scrlbar.ver_draw = 1;
1211 }
1212
1213 /*Invalidate the current (old) scrollbar areas*/
1214 lv_area_t sb_area_tmp;
1215 if(ext->scrlbar.hor_draw != 0) {
1216 lv_area_copy(&sb_area_tmp, &ext->scrlbar.hor_area);
1217 sb_area_tmp.x1 += page->coords.x1;
1218 sb_area_tmp.y1 += page->coords.y1;
1219 sb_area_tmp.x2 += page->coords.x1;
1220 sb_area_tmp.y2 += page->coords.y1;
1221 lv_obj_invalidate_area(page, &sb_area_tmp);
1222 }
1223 if(ext->scrlbar.ver_draw != 0) {
1224 lv_area_copy(&sb_area_tmp, &ext->scrlbar.ver_area);
1225 sb_area_tmp.x1 += page->coords.x1;
1226 sb_area_tmp.y1 += page->coords.y1;
1227 sb_area_tmp.x2 += page->coords.x1;
1228 sb_area_tmp.y2 += page->coords.y1;
1229 lv_obj_invalidate_area(page, &sb_area_tmp);
1230 }
1231
1232 /*Full sized horizontal scrollbar*/
1233 if(scrl_w <= obj_w - bg_left - bg_right) {
1234 lv_area_set_width(&ext->scrlbar.hor_area, obj_w - 2 * sb_hor_pad);
1235 _lv_area_set_pos(&ext->scrlbar.hor_area, sb_hor_pad,
1236 obj_h - sb_width - sb_bottom);
1237 if(ext->scrlbar.mode == LV_SCROLLBAR_MODE_AUTO ||
1238 ext->scrlbar.mode == LV_SCROLLBAR_MODE_DRAG) ext->scrlbar.hor_draw = 0;
1239 }
1240 /*Smaller horizontal scrollbar*/
1241 else {
1242 size_tmp =
1243 (obj_w * (obj_w - (2 * sb_hor_pad))) / (scrl_w + bg_left + bg_right);
1244 if(size_tmp < LV_PAGE_SB_MIN_SIZE) size_tmp = LV_PAGE_SB_MIN_SIZE;
1245 lv_area_set_width(&ext->scrlbar.hor_area, size_tmp);
1246
1247 _lv_area_set_pos(&ext->scrlbar.hor_area,
1248 sb_hor_pad +
1249 (-(lv_obj_get_x(scrl) - bg_left) * (obj_w - size_tmp - 2 * sb_hor_pad)) /
1250 (scrl_w + bg_left + bg_right - obj_w),
1251 obj_h - sb_width - sb_bottom);
1252
1253 if(ext->scrlbar.mode == LV_SCROLLBAR_MODE_AUTO ||
1254 ext->scrlbar.mode == LV_SCROLLBAR_MODE_DRAG) ext->scrlbar.hor_draw = 1;
1255 }
1256
1257 /*Full sized vertical scroll bar*/
1258 if(scrl_h <= obj_h - bg_top - bg_bottom) {
1259 lv_area_set_height(&ext->scrlbar.ver_area, obj_h - 2 * sb_ver_pad);
1260 _lv_area_set_pos(&ext->scrlbar.ver_area,
1261 obj_w - sb_width - sb_right, sb_ver_pad);
1262 if(ext->scrlbar.mode == LV_SCROLLBAR_MODE_AUTO ||
1263 ext->scrlbar.mode == LV_SCROLLBAR_MODE_DRAG) ext->scrlbar.ver_draw = 0;
1264 }
1265 /*Smaller vertical scroll bar*/
1266 else {
1267 size_tmp =
1268 (obj_h * (obj_h - (2 * sb_ver_pad))) / (scrl_h + bg_top + bg_bottom);
1269 if(size_tmp < LV_PAGE_SB_MIN_SIZE) size_tmp = LV_PAGE_SB_MIN_SIZE;
1270 lv_area_set_height(&ext->scrlbar.ver_area, size_tmp);
1271
1272 _lv_area_set_pos(&ext->scrlbar.ver_area,
1273 obj_w - sb_width - sb_right,
1274 sb_ver_pad + (-(lv_obj_get_y(scrl) - bg_left) *
1275 (obj_h - size_tmp - 2 * sb_ver_pad)) /
1276 (scrl_h + bg_top + bg_bottom - obj_h));
1277
1278 if(ext->scrlbar.mode == LV_SCROLLBAR_MODE_AUTO ||
1279 ext->scrlbar.mode == LV_SCROLLBAR_MODE_DRAG) ext->scrlbar.ver_draw = 1;
1280 }
1281
1282 /*Invalidate the new scrollbar areas*/
1283 if(ext->scrlbar.hor_draw != 0) {
1284 lv_area_copy(&sb_area_tmp, &ext->scrlbar.hor_area);
1285 sb_area_tmp.x1 += page->coords.x1;
1286 sb_area_tmp.y1 += page->coords.y1;
1287 sb_area_tmp.x2 += page->coords.x1;
1288 sb_area_tmp.y2 += page->coords.y1;
1289 lv_obj_invalidate_area(page, &sb_area_tmp);
1290 }
1291 if(ext->scrlbar.ver_draw != 0) {
1292 lv_area_copy(&sb_area_tmp, &ext->scrlbar.ver_area);
1293 sb_area_tmp.x1 += page->coords.x1;
1294 sb_area_tmp.y1 += page->coords.y1;
1295 sb_area_tmp.x2 += page->coords.x1;
1296 sb_area_tmp.y2 += page->coords.y1;
1297 lv_obj_invalidate_area(page, &sb_area_tmp);
1298 }
1299 }
1300
refr_ext_draw_pad(lv_obj_t * page)1301 static void refr_ext_draw_pad(lv_obj_t * page)
1302 {
1303 lv_style_int_t sb_bottom = lv_obj_get_style_pad_bottom(page, LV_PAGE_PART_SCROLLBAR);
1304 lv_style_int_t sb_right = lv_obj_get_style_pad_right(page, LV_PAGE_PART_SCROLLBAR);
1305
1306 /*Ensure ext. size for the scrollbars if they are out of the page*/
1307 if(page->ext_draw_pad < (-sb_right)) page->ext_draw_pad = -sb_right;
1308 if(page->ext_draw_pad < (-sb_bottom)) page->ext_draw_pad = -sb_bottom;
1309 }
1310
1311 #if LV_USE_ANIMATION
edge_flash_anim(void * page,lv_anim_value_t v)1312 static void edge_flash_anim(void * page, lv_anim_value_t v)
1313 {
1314 lv_page_ext_t * ext = lv_obj_get_ext_attr(page);
1315 ext->edge_flash.state = v;
1316
1317 lv_area_t flash_area;
1318 get_edge_flash_area(page, &flash_area, LV_PAGE_END_FLASH_SIZE);
1319 lv_obj_invalidate_area(page, &flash_area);
1320 }
1321
edge_flash_anim_end(lv_anim_t * a)1322 static void edge_flash_anim_end(lv_anim_t * a)
1323 {
1324 lv_area_t flash_area;
1325 get_edge_flash_area(a->var, &flash_area, LV_PAGE_END_FLASH_SIZE);
1326 lv_obj_invalidate_area(a->var, &flash_area);
1327
1328 lv_page_ext_t * ext = lv_obj_get_ext_attr(a->var);
1329 ext->edge_flash.top_ip = 0;
1330 ext->edge_flash.bottom_ip = 0;
1331 ext->edge_flash.left_ip = 0;
1332 ext->edge_flash.right_ip = 0;
1333
1334 }
1335
get_edge_flash_area(lv_obj_t * page,lv_area_t * flash_area,lv_coord_t state)1336 static void get_edge_flash_area(lv_obj_t * page, lv_area_t * flash_area, lv_coord_t state)
1337 {
1338 lv_coord_t page_w = lv_obj_get_width(page);
1339 lv_coord_t page_h = lv_obj_get_height(page);
1340 lv_page_ext_t * ext = lv_obj_get_ext_attr(page);
1341
1342 if(ext->edge_flash.top_ip) {
1343 flash_area->x1 = page->coords.x1 - page_w;
1344 flash_area->x2 = page->coords.x2 + page_w;
1345 flash_area->y1 = page->coords.y1 - 3 * page_w + state;
1346 flash_area->y2 = page->coords.y1 + state;
1347 }
1348 else if(ext->edge_flash.bottom_ip) {
1349 flash_area->x1 = page->coords.x1 - page_w;
1350 flash_area->x2 = page->coords.x2 + page_w;
1351 flash_area->y1 = page->coords.y2 - state;
1352 flash_area->y2 = page->coords.y2 + 3 * page_w - state;
1353 }
1354 else if(ext->edge_flash.right_ip) {
1355 flash_area->x1 = page->coords.x2 - state;
1356 flash_area->x2 = page->coords.x2 + 3 * page_h - state;
1357 flash_area->y1 = page->coords.y1 - page_h;
1358 flash_area->y2 = page->coords.y2 + page_h;
1359 }
1360 else if(ext->edge_flash.left_ip) {
1361 flash_area->x1 = page->coords.x1 - 3 * page_h + state;
1362 flash_area->x2 = page->coords.x1 + state;
1363 flash_area->y1 = page->coords.y1 - page_h;
1364 flash_area->y2 = page->coords.y2 + page_h;
1365 }
1366 else {
1367 lv_area_set(flash_area, 0, 0, -1, -1);
1368 }
1369 }
1370
1371 #endif
1372
1373 #endif
1374