1 /**
2  * @file lv_win.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "lv_win.h"
10 #if LV_USE_WIN != 0
11 
12 #include "../lv_misc/lv_debug.h"
13 #include "../lv_themes/lv_theme.h"
14 #include "../lv_core/lv_disp.h"
15 
16 /*********************
17  *      DEFINES
18  *********************/
19 #define LV_OBJX_NAME "lv_win"
20 #define DEF_TITLE "Window"
21 
22 /**********************
23  *      TYPEDEFS
24  **********************/
25 
26 /** Extended data of win_btn*/
27 typedef struct {
28     /** Ext. of ancestor*/
29     lv_btn_ext_t btn;
30 
31     /** Which side of the header should the button be aligned to.
32      * 0: Align to right (default), 1: Align to left */
33     uint8_t alignment_in_header : 1;
34 } lv_win_btn_ext_t;
35 
36 enum {
37     LV_WIN_BTN_ALIGN_RIGHT = 0,     /**< Align button to right of the header */
38     LV_WIN_BTN_ALIGN_LEFT   /**< Align button to left of the header */
39 };
40 typedef uint8_t lv_win_btn_align_t;
41 
42 /**********************
43  *  STATIC PROTOTYPES
44  **********************/
45 static lv_res_t lv_win_signal(lv_obj_t * win, lv_signal_t sign, void * param);
46 static lv_design_res_t lv_win_header_design(lv_obj_t * header, const lv_area_t * clip_area, lv_design_mode_t mode);
47 static lv_style_list_t * lv_win_get_style(lv_obj_t * win, uint8_t part);
48 static void lv_win_realign(lv_obj_t * win);
49 static lv_obj_t * lv_win_btn_create(lv_obj_t * par, const void * img_src);
50 static void lv_win_btn_set_alignment(lv_obj_t * par, const lv_win_btn_align_t alignment);
51 static lv_win_btn_align_t lv_win_btn_get_alignment(const lv_obj_t * par);
52 
53 /**********************
54  *  STATIC VARIABLES
55  **********************/
56 static lv_design_cb_t ancestor_header_design;
57 static lv_signal_cb_t ancestor_signal;
58 
59 /**********************
60  *      MACROS
61  **********************/
62 
63 /**********************
64  *   GLOBAL FUNCTIONS
65  **********************/
66 
67 /**
68  * Create a window objects
69  * @param par pointer to an object, it will be the parent of the new window
70  * @param copy pointer to a window object, if not NULL then the new object will be copied from it
71  * @return pointer to the created window
72  */
lv_win_create(lv_obj_t * par,const lv_obj_t * copy)73 lv_obj_t * lv_win_create(lv_obj_t * par, const lv_obj_t * copy)
74 {
75     LV_LOG_TRACE("window create started");
76 
77     /*Create the ancestor object*/
78     lv_obj_t * new_win = lv_obj_create(par, copy);
79     LV_ASSERT_MEM(new_win);
80     if(new_win == NULL) return NULL;
81 
82     if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_cb(new_win);
83 
84     /*Allocate the object type specific extended data*/
85     lv_win_ext_t * ext = lv_obj_allocate_ext_attr(new_win, sizeof(lv_win_ext_t));
86     LV_ASSERT_MEM(ext);
87     if(ext == NULL) {
88         lv_obj_del(new_win);
89         return NULL;
90     }
91 
92     ext->page          = NULL;
93     ext->header        = NULL;
94     ext->title_txt    = lv_mem_alloc(strlen(DEF_TITLE) + 1);
95     strcpy(ext->title_txt, DEF_TITLE);
96 
97     /*Init the new window object*/
98     if(copy == NULL) {
99         /* Set a size which fits into the parent.
100          * Don't use `par` directly because if the window is created on a page it is moved to the
101          * scrollable so the parent has changed */
102         lv_coord_t w;
103         lv_coord_t h;
104         if(par) {
105             w = lv_obj_get_width_fit(lv_obj_get_parent(new_win));
106             h = lv_obj_get_height_fit(lv_obj_get_parent(new_win));
107         }
108         else {
109             w = lv_disp_get_hor_res(NULL);
110             h = lv_disp_get_ver_res(NULL);
111         }
112 
113         lv_obj_set_size(new_win, w, h);
114 
115         ext->btn_w = LV_DPX(65);
116 
117         ext->page = lv_page_create(new_win, NULL);
118         lv_obj_add_protect(ext->page, LV_PROTECT_PARENT);
119         lv_page_set_scrollbar_mode(ext->page, LV_SCROLLBAR_MODE_AUTO);
120         lv_obj_clean_style_list(ext->page, LV_PAGE_PART_BG);
121 
122         /*Create a holder for the header*/
123         ext->header = lv_obj_create(new_win, NULL);
124         /*Move back to window background because it's automatically moved to the content page*/
125         lv_obj_add_protect(ext->header, LV_PROTECT_PARENT);
126         lv_obj_set_parent(ext->header, new_win);
127         if(ancestor_header_design == NULL) ancestor_header_design = lv_obj_get_design_cb(ext->header);
128         lv_obj_set_height(ext->header, LV_DPX(65));
129 
130         lv_obj_set_design_cb(ext->header, lv_win_header_design);
131         lv_obj_set_signal_cb(new_win, lv_win_signal);
132 
133         lv_theme_apply(new_win, LV_THEME_WIN);
134     }
135     /*Copy an existing object*/
136     else {
137         lv_win_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
138         /*Create the objects*/
139         ext->header   = lv_obj_create(new_win, copy_ext->header);
140         ext->title_txt    = lv_mem_alloc(strlen(copy_ext->title_txt) + 1);
141         strcpy(ext->title_txt, copy_ext->title_txt);
142         ext->page     = lv_page_create(new_win, copy_ext->page);
143         ext->btn_w     = copy_ext->btn_w;
144 
145         /*Copy the buttons*/
146         lv_obj_t * child;
147         child = lv_obj_get_child_back(copy_ext->header, NULL);
148         child = lv_obj_get_child_back(copy_ext->header, child); /*Sip the title*/
149         while(child != NULL) {
150             lv_obj_t * btn = lv_btn_create(ext->header, child);
151             lv_img_create(btn, lv_obj_get_child(child, NULL));
152             child = lv_obj_get_child_back(copy_ext->header, child);
153         }
154 
155         lv_obj_set_signal_cb(new_win, lv_win_signal);
156     }
157 
158     /*Refresh the style with new signal function*/
159     lv_obj_refresh_style(new_win, LV_OBJ_PART_ALL, LV_STYLE_PROP_ALL);
160 
161     lv_win_realign(new_win);
162 
163     LV_LOG_INFO("window created");
164 
165     return new_win;
166 }
167 
168 /**
169  * Delete all children of the scrl object, without deleting scrl child.
170  * @param win pointer to an object
171  */
lv_win_clean(lv_obj_t * win)172 void lv_win_clean(lv_obj_t * win)
173 {
174     LV_ASSERT_OBJ(win, LV_OBJX_NAME);
175 
176     lv_obj_t * scrl = lv_page_get_scrollable(win);
177     lv_obj_clean(scrl);
178 }
179 
180 /*======================
181  * Add/remove functions
182  *=====================*/
183 
184 /**
185  * Add control button to the header of the window
186  * @param win pointer to a window object
187  * @param img_src an image source ('lv_img_t' variable, path to file or a symbol)
188  * @param alignment button alignment on the header
189  * @return pointer to the created button object
190  */
lv_win_add_btn_right(lv_obj_t * win,const void * img_src)191 lv_obj_t * lv_win_add_btn_right(lv_obj_t * win, const void * img_src)
192 {
193     LV_ASSERT_OBJ(win, LV_OBJX_NAME);
194     LV_ASSERT_NULL(img_src);
195 
196     lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
197 
198     lv_obj_t * btn = lv_win_btn_create(ext->header, img_src);
199     lv_win_btn_set_alignment(btn, LV_WIN_BTN_ALIGN_RIGHT);
200 
201     lv_win_realign(win);
202 
203     return btn;
204 }
205 
206 /**
207  * Add control button on the left side of the window header
208  * @param win pointer to a window object
209  * @param img_src an image source ('lv_img_t' variable, path to file or a symbol)
210  * @return pointer to the created button object
211  */
lv_win_add_btn_left(lv_obj_t * win,const void * img_src)212 lv_obj_t * lv_win_add_btn_left(lv_obj_t * win, const void * img_src)
213 {
214     LV_ASSERT_OBJ(win, LV_OBJX_NAME);
215     LV_ASSERT_NULL(img_src);
216 
217     lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
218 
219     lv_obj_t * btn = lv_win_btn_create(ext->header, img_src);
220     lv_win_btn_set_alignment(btn, LV_WIN_BTN_ALIGN_LEFT);
221 
222     lv_win_realign(win);
223 
224     return btn;
225 }
226 
227 /*=====================
228  * Setter functions
229  *====================*/
230 
231 /**
232  * Can be assigned to a window control button to close the window
233  * @param btn pointer to the control button on the widows header
234  * @param evet the event type
235  */
lv_win_close_event_cb(lv_obj_t * btn,lv_event_t event)236 void lv_win_close_event_cb(lv_obj_t * btn, lv_event_t event)
237 {
238     LV_ASSERT_OBJ(btn, "lv_btn");
239 
240     if(event == LV_EVENT_RELEASED) {
241         lv_obj_t * win = lv_win_get_from_btn(btn);
242 
243         lv_obj_del(win);
244     }
245 }
246 
247 /**
248  * Set the title of a window
249  * @param win pointer to a window object
250  * @param title string of the new title
251  */
lv_win_set_title(lv_obj_t * win,const char * title)252 void lv_win_set_title(lv_obj_t * win, const char * title)
253 {
254     LV_ASSERT_OBJ(win, LV_OBJX_NAME);
255     LV_ASSERT_STR(title);
256 
257     lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
258 
259     ext->title_txt    = lv_mem_realloc(ext->title_txt, strlen(title) + 1);
260     LV_ASSERT_MEM(ext->title_txt);
261     if(ext->title_txt == NULL) return;
262 
263     strcpy(ext->title_txt, title);
264     lv_obj_invalidate(ext->header);
265 }
266 
267 /**
268  * Set the height of the header
269  * @param win pointer to a window object
270  * @param height height of the header
271  */
lv_win_set_header_height(lv_obj_t * win,lv_coord_t height)272 void lv_win_set_header_height(lv_obj_t * win, lv_coord_t height)
273 {
274     LV_ASSERT_OBJ(win, LV_OBJX_NAME);
275 
276     lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
277 
278     lv_obj_set_height(ext->header, height);
279     lv_win_realign(win);
280 }
281 
282 /**
283  * Set the width of the control buttons on the header
284  * @param win pointer to a window object
285  * @param width width of the control button. 0: to make them square automatically.
286  */
lv_win_set_btn_width(lv_obj_t * win,lv_coord_t width)287 void lv_win_set_btn_width(lv_obj_t * win, lv_coord_t width)
288 {
289     LV_ASSERT_OBJ(win, LV_OBJX_NAME);
290 
291     lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
292     ext->btn_w = width;
293     lv_win_realign(win);
294 }
295 
296 /**
297  * Set the size of the content area.
298  * It's the effective area where object can be placed.
299  * @param win pointer to a window object
300  * @param w width
301  * @param h height (the window will be higher with the height of the header)
302  */
lv_win_set_content_size(lv_obj_t * win,lv_coord_t w,lv_coord_t h)303 void lv_win_set_content_size(lv_obj_t * win, lv_coord_t w, lv_coord_t h)
304 {
305     LV_ASSERT_OBJ(win, LV_OBJX_NAME);
306 
307     lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
308     h += lv_obj_get_height(ext->header);
309 
310     lv_obj_set_size(win, w, h);
311 }
312 
313 /**
314  * Set the layout of the window
315  * @param win pointer to a window object
316  * @param layout the layout from 'lv_layout_t'
317  */
lv_win_set_layout(lv_obj_t * win,lv_layout_t layout)318 void lv_win_set_layout(lv_obj_t * win, lv_layout_t layout)
319 {
320     LV_ASSERT_OBJ(win, LV_OBJX_NAME);
321 
322     lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
323     lv_page_set_scrl_layout(ext->page, layout);
324 }
325 
326 /**
327  * Set the scroll bar mode of a window
328  * @param win pointer to a window object
329  * @param sb_mode the new scroll bar mode from  'lv_sb_mode_t'
330  */
lv_win_set_scrollbar_mode(lv_obj_t * win,lv_scrollbar_mode_t sb_mode)331 void lv_win_set_scrollbar_mode(lv_obj_t * win, lv_scrollbar_mode_t sb_mode)
332 {
333     LV_ASSERT_OBJ(win, LV_OBJX_NAME);
334 
335     lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
336     lv_page_set_scrollbar_mode(ext->page, sb_mode);
337 }
338 /**
339  * Set focus animation duration on `lv_win_focus()`
340  * @param win pointer to a window object
341  * @param anim_time duration of animation [ms]
342  */
lv_win_set_anim_time(lv_obj_t * win,uint16_t anim_time)343 void lv_win_set_anim_time(lv_obj_t * win, uint16_t anim_time)
344 {
345     LV_ASSERT_OBJ(win, LV_OBJX_NAME);
346 
347     lv_page_set_anim_time(lv_win_get_content(win), anim_time);
348 }
349 
350 /**
351  * Set drag status of a window. If set to 'true' window can be dragged like on a PC.
352  * @param win pointer to a window object
353  * @param en whether dragging is enabled
354  */
lv_win_set_drag(lv_obj_t * win,bool en)355 void lv_win_set_drag(lv_obj_t * win, bool en)
356 {
357     LV_ASSERT_OBJ(win, LV_OBJX_NAME);
358 
359     lv_win_ext_t * ext    = lv_obj_get_ext_attr(win);
360     lv_obj_t * win_header = ext->header;
361     lv_obj_set_drag_parent(win_header, en);
362     lv_obj_set_drag(win, en);
363 }
364 
365 /*=====================
366  * Getter functions
367  *====================*/
368 
369 /**
370  * Get the title of a window
371  * @param win pointer to a window object
372  * @return title string of the window
373  */
lv_win_get_title(const lv_obj_t * win)374 const char * lv_win_get_title(const lv_obj_t * win)
375 {
376     LV_ASSERT_OBJ(win, LV_OBJX_NAME);
377 
378     lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
379     return ext->title_txt;
380 }
381 
382 /**
383  * Get the content holder object of window (`lv_page`) to allow additional customization
384  * @param win pointer to a window object
385  * @return the Page object where the window's content is
386  */
lv_win_get_content(const lv_obj_t * win)387 lv_obj_t * lv_win_get_content(const lv_obj_t * win)
388 {
389     LV_ASSERT_OBJ(win, LV_OBJX_NAME);
390 
391     lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
392     return ext->page;
393 }
394 
395 /**
396  * Get the header height
397  * @param win pointer to a window object
398  * @return header height
399  */
lv_win_get_header_height(const lv_obj_t * win)400 lv_coord_t lv_win_get_header_height(const lv_obj_t * win)
401 {
402     LV_ASSERT_OBJ(win, LV_OBJX_NAME);
403 
404     lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
405     return lv_obj_get_height(ext->header);
406 }
407 
408 /**
409  * Get the width of the control buttons on the header
410  * @param win pointer to a window object
411  * @return width of the control button. 0: square.
412  */
lv_win_get_btn_width(lv_obj_t * win)413 lv_coord_t lv_win_get_btn_width(lv_obj_t * win)
414 {
415     LV_ASSERT_OBJ(win, LV_OBJX_NAME);
416 
417     lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
418     return ext->btn_w;
419 
420 }
421 
422 /**
423  * Get the pointer of a widow from one of  its control button.
424  * It is useful in the action of the control buttons where only button is known.
425  * @param ctrl_btn pointer to a control button of a window
426  * @return pointer to the window of 'ctrl_btn'
427  */
lv_win_get_from_btn(const lv_obj_t * ctrl_btn)428 lv_obj_t * lv_win_get_from_btn(const lv_obj_t * ctrl_btn)
429 {
430     LV_ASSERT_OBJ(ctrl_btn, "lv_btn");
431 
432     lv_obj_t * header = lv_obj_get_parent(ctrl_btn);
433     lv_obj_t * win    = lv_obj_get_parent(header);
434 
435     return win;
436 }
437 
438 /**
439  * Get the layout of a window
440  * @param win pointer to a window object
441  * @return the layout of the window (from 'lv_layout_t')
442  */
lv_win_get_layout(lv_obj_t * win)443 lv_layout_t lv_win_get_layout(lv_obj_t * win)
444 {
445     LV_ASSERT_OBJ(win, LV_OBJX_NAME);
446 
447     lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
448     return lv_page_get_scrl_layout(ext->page);
449 }
450 
451 /**
452  * Get the scroll bar mode of a window
453  * @param win pointer to a window object
454  * @return the scroll bar mode of the window (from 'lv_sb_mode_t')
455  */
lv_win_get_sb_mode(lv_obj_t * win)456 lv_scrollbar_mode_t lv_win_get_sb_mode(lv_obj_t * win)
457 {
458     LV_ASSERT_OBJ(win, LV_OBJX_NAME);
459 
460     lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
461     return lv_page_get_scrollbar_mode(ext->page);
462 }
463 
464 /**
465  * Get focus animation duration
466  * @param win pointer to a window object
467  * @return duration of animation [ms]
468  */
lv_win_get_anim_time(const lv_obj_t * win)469 uint16_t lv_win_get_anim_time(const lv_obj_t * win)
470 {
471     LV_ASSERT_OBJ(win, LV_OBJX_NAME);
472 
473     return lv_page_get_anim_time(lv_win_get_content(win));
474 }
475 
476 /**
477  * Get width of the content area (page scrollable) of the window
478  * @param win pointer to a window object
479  * @return the width of the content_bg area
480  */
lv_win_get_width(lv_obj_t * win)481 lv_coord_t lv_win_get_width(lv_obj_t * win)
482 {
483     LV_ASSERT_OBJ(win, LV_OBJX_NAME);
484 
485     lv_win_ext_t * ext            = lv_obj_get_ext_attr(win);
486     lv_obj_t * scrl               = lv_page_get_scrollable(ext->page);
487     lv_coord_t left = lv_obj_get_style_pad_left(win, LV_WIN_PART_BG);
488     lv_coord_t right = lv_obj_get_style_pad_left(win, LV_WIN_PART_BG);
489 
490     return lv_obj_get_width_fit(scrl) - left - right;
491 }
492 
493 /*=====================
494  * Other functions
495  *====================*/
496 
497 /**
498  * Focus on an object. It ensures that the object will be visible in the window.
499  * @param win pointer to a window object
500  * @param obj pointer to an object to focus (must be in the window)
501  * @param anim_en LV_ANIM_ON focus with an animation; LV_ANIM_OFF focus without animation
502  */
lv_win_focus(lv_obj_t * win,lv_obj_t * obj,lv_anim_enable_t anim_en)503 void lv_win_focus(lv_obj_t * win, lv_obj_t * obj, lv_anim_enable_t anim_en)
504 {
505     LV_ASSERT_OBJ(win, LV_OBJX_NAME);
506     LV_ASSERT_OBJ(obj, "");
507 
508 
509     lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
510     lv_page_focus(ext->page, obj, anim_en);
511 }
512 
513 /**********************
514  *   STATIC FUNCTIONS
515  **********************/
516 /**
517  * Handle the drawing related tasks of the window header
518  * @param header pointer to an object
519  * @param clip_area the object will be drawn only in this area
520  * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
521  *                                  (return 'true' if yes)
522  *             LV_DESIGN_DRAW: draw the object (always return 'true')
523  *             LV_DESIGN_DRAW_POST: drawing after every children are drawn
524  * @param return an element of `lv_design_res_t`
525  */
lv_win_header_design(lv_obj_t * header,const lv_area_t * clip_area,lv_design_mode_t mode)526 static lv_design_res_t lv_win_header_design(lv_obj_t * header, const lv_area_t * clip_area, lv_design_mode_t mode)
527 {
528     /*Return false if the object is not covers the mask_p area*/
529     if(mode == LV_DESIGN_COVER_CHK) {
530         return ancestor_header_design(header, clip_area, mode);
531     }
532     /*Draw the object*/
533     else if(mode == LV_DESIGN_DRAW_MAIN) {
534         ancestor_header_design(header, clip_area, mode);
535 
536         lv_obj_t * win = lv_obj_get_parent(header);
537         lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
538 
539         lv_style_int_t header_left = lv_obj_get_style_pad_left(win, LV_WIN_PART_HEADER);
540         lv_style_int_t header_inner = lv_obj_get_style_pad_inner(win, LV_WIN_PART_HEADER);
541 
542         lv_draw_label_dsc_t label_dsc;
543         lv_draw_label_dsc_init(&label_dsc);
544         lv_obj_init_draw_label_dsc(header, LV_OBJ_PART_MAIN, &label_dsc);
545 
546         lv_area_t txt_area;
547         lv_point_t txt_size;
548 
549         _lv_txt_get_size(&txt_size, ext->title_txt, label_dsc.font, label_dsc.letter_space, label_dsc.line_space, LV_COORD_MAX,
550                          label_dsc.flag);
551 
552         lv_obj_t * btn = NULL;
553 
554         lv_coord_t btn_h = lv_obj_get_height_fit(header);
555         lv_coord_t btn_w = ext->btn_w != 0 ? ext->btn_w : btn_h;
556 
557         /*Get x position of the title (should be on the right of the buttons on the left)*/
558 
559         lv_coord_t left_btn_offset = 0;
560         btn = lv_obj_get_child_back(ext->header, NULL);
561         while(btn != NULL) {
562             if(LV_WIN_BTN_ALIGN_LEFT == lv_win_btn_get_alignment(btn)) {
563                 left_btn_offset += btn_w + header_inner;
564             }
565 
566             btn = lv_obj_get_child_back(header, btn);
567         }
568 
569         txt_area.x1 = header->coords.x1 + header_left + left_btn_offset;
570         txt_area.y1 = header->coords.y1 + (lv_obj_get_height(header) - txt_size.y) / 2;
571         txt_area.x2 = txt_area.x1 + txt_size.x  + left_btn_offset;
572         txt_area.y2 = txt_area.y1 + txt_size.y;
573 
574         lv_draw_label(&txt_area, clip_area, &label_dsc, ext->title_txt, NULL);
575     }
576     else if(mode == LV_DESIGN_DRAW_POST) {
577         ancestor_header_design(header, clip_area, mode);
578     }
579 
580     return LV_DESIGN_RES_OK;
581 }
582 /**
583  * Signal function of the window
584  * @param win pointer to a window object
585  * @param sign a signal type from lv_signal_t enum
586  * @param param pointer to a signal specific variable
587  * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
588  */
lv_win_signal(lv_obj_t * win,lv_signal_t sign,void * param)589 static lv_res_t lv_win_signal(lv_obj_t * win, lv_signal_t sign, void * param)
590 {
591     lv_res_t res;
592 
593     if(sign == LV_SIGNAL_GET_STYLE) {
594         lv_get_style_info_t * info = param;
595         info->result = lv_win_get_style(win, info->part);
596         if(info->result != NULL) return LV_RES_OK;
597         else return ancestor_signal(win, sign, param);
598     }
599     else if(sign == LV_SIGNAL_GET_STATE_DSC) {
600         lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
601         lv_get_state_info_t * info = param;
602         if(info->part == LV_WIN_PART_CONTENT_SCROLLABLE) info->result = lv_obj_get_state(lv_page_get_scrollable(ext->page),
603                                                                                              LV_CONT_PART_MAIN);
604         else if(info->part == LV_WIN_PART_SCROLLBAR) info->result = lv_obj_get_state(ext->page, LV_PAGE_PART_SCROLLBAR);
605         else if(info->part == LV_WIN_PART_HEADER) info->result = lv_obj_get_state(ext->header, LV_OBJ_PART_MAIN);
606         return LV_RES_OK;
607     }
608 
609     /* Include the ancient signal function */
610     res = ancestor_signal(win, sign, param);
611     if(res != LV_RES_OK) return res;
612     if(sign == LV_SIGNAL_GET_TYPE) return lv_obj_handle_get_type_signal(param, LV_OBJX_NAME);
613 
614     lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
615     if(sign == LV_SIGNAL_CHILD_CHG) { /*Move children to the page*/
616         lv_obj_t * page = ext->page;
617         if(page != NULL) {
618             lv_obj_t * child;
619             child = lv_obj_get_child(win, NULL);
620             while(child != NULL) {
621                 if(lv_obj_is_protected(child, LV_PROTECT_PARENT) == false) {
622                     lv_obj_t * tmp = child;
623                     child          = lv_obj_get_child(win, child); /*Get the next child before move this*/
624                     lv_obj_set_parent(tmp, page);
625                 }
626                 else {
627                     child = lv_obj_get_child(win, child);
628                 }
629             }
630         }
631     }
632     else if(sign == LV_SIGNAL_STYLE_CHG) {
633         lv_win_realign(win);
634     }
635     else if(sign == LV_SIGNAL_COORD_CHG) {
636         /*If the size is changed refresh the window*/
637         if(lv_area_get_width(param) != lv_obj_get_width(win) || lv_area_get_height(param) != lv_obj_get_height(win)) {
638             lv_win_realign(win);
639         }
640     }
641     else if(sign == LV_SIGNAL_CLEANUP) {
642         ext->header = NULL; /*These objects were children so they are already invalid*/
643         ext->page   = NULL;
644         lv_mem_free(ext->title_txt);
645         ext->title_txt  = NULL;
646     }
647     else if(sign == LV_SIGNAL_CONTROL) {
648 #if LV_USE_GROUP
649         /*Forward all the control signals to the page*/
650         ext->page->signal_cb(ext->page, sign, param);
651 #endif
652     }
653 
654     return res;
655 }
656 /**
657  * Get the style descriptor of a part of the object
658  * @param win pointer the object
659  * @param part the part of the win. (LV_PAGE_WIN_...)
660  * @return pointer to the style descriptor of the specified part
661  */
lv_win_get_style(lv_obj_t * win,uint8_t part)662 static lv_style_list_t * lv_win_get_style(lv_obj_t * win, uint8_t part)
663 {
664     LV_ASSERT_OBJ(win, LV_OBJX_NAME);
665 
666     lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
667     lv_style_list_t * style_dsc_p;
668 
669     switch(part) {
670         case LV_WIN_PART_BG:
671             style_dsc_p = &win->style_list;
672             break;
673         case LV_WIN_PART_HEADER:
674             style_dsc_p = lv_obj_get_style_list(ext->header, LV_OBJ_PART_MAIN);
675             break;
676         case LV_WIN_PART_SCROLLBAR:
677             style_dsc_p = lv_obj_get_style_list(ext->page, LV_PAGE_PART_SCROLLBAR);
678             break;
679         case LV_WIN_PART_CONTENT_SCROLLABLE:
680             style_dsc_p = lv_obj_get_style_list(ext->page, LV_PAGE_PART_SCROLLABLE);
681             break;
682         default:
683             style_dsc_p = NULL;
684     }
685 
686     return style_dsc_p;
687 }
688 /**
689  * Realign the building elements of a window
690  * @param win pointer to a window object
691  */
lv_win_realign(lv_obj_t * win)692 static void lv_win_realign(lv_obj_t * win)
693 {
694     lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
695 
696     if(ext->page == NULL || ext->header == NULL) return;
697 
698     lv_obj_set_width(ext->header, lv_obj_get_width(win));
699 
700     lv_obj_t * btn;
701     lv_obj_t * btn_prev_at_left = NULL;
702     lv_obj_t * btn_prev_at_right = NULL;
703 
704     bool is_header_right_side_empty = true;
705     bool is_header_left_side_empty = true;
706 
707     lv_coord_t btn_h = lv_obj_get_height_fit(ext->header);
708     lv_coord_t btn_w = ext->btn_w != 0 ? ext->btn_w : btn_h;
709     lv_style_int_t header_inner = lv_obj_get_style_pad_inner(win, LV_WIN_PART_HEADER);
710     lv_style_int_t header_right = lv_obj_get_style_pad_right(win, LV_WIN_PART_HEADER);
711     lv_style_int_t header_left = lv_obj_get_style_pad_left(win, LV_WIN_PART_HEADER);
712 
713     /*Refresh the size of all control buttons*/
714     btn = lv_obj_get_child_back(ext->header, NULL);
715     while(btn != NULL) {
716         lv_obj_set_size(btn, btn_w, btn_h);
717         uint8_t btn_alignment = lv_win_btn_get_alignment(btn);
718 
719         if(LV_WIN_BTN_ALIGN_RIGHT == btn_alignment) {
720             if(is_header_right_side_empty) {
721                 /* Align the button to the right of the header */
722                 lv_obj_align(btn, ext->header, LV_ALIGN_IN_RIGHT_MID, -header_right, 0);
723 
724                 is_header_right_side_empty = false;
725             }
726             else {
727                 /* Align the button to the left of the previous button */
728                 lv_obj_align(btn, btn_prev_at_right, LV_ALIGN_OUT_LEFT_MID, -header_inner, 0);
729             }
730 
731             btn_prev_at_right = btn;
732         }
733         else if(LV_WIN_BTN_ALIGN_LEFT == btn_alignment) {
734             if(is_header_left_side_empty) {
735                 /* Align the button to the right of the header */
736                 lv_obj_align(btn, ext->header, LV_ALIGN_IN_LEFT_MID, header_left, 0);
737 
738                 is_header_left_side_empty = false;
739             }
740             else {
741                 /* Align the button to the right of the previous button */
742                 lv_obj_align(btn, btn_prev_at_left, LV_ALIGN_OUT_RIGHT_MID, header_inner, 0);
743             }
744 
745             btn_prev_at_left = btn;
746         }
747 
748         btn = lv_obj_get_child_back(ext->header, btn);
749     }
750 
751     lv_obj_set_pos(ext->header, 0, 0);
752 
753     lv_obj_set_size(ext->page, lv_obj_get_width(win), lv_obj_get_height(win) - lv_obj_get_height(ext->header));
754     lv_obj_align(ext->page, ext->header, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 0);
755 }
756 
lv_win_btn_create(lv_obj_t * par,const void * img_src)757 static lv_obj_t * lv_win_btn_create(lv_obj_t * par, const void * img_src)
758 {
759     LV_LOG_TRACE("win btn create started");
760 
761     lv_obj_t * win_btn;
762 
763     win_btn = lv_btn_create(par, NULL);
764     LV_ASSERT_MEM(win_btn);
765     if(win_btn == NULL) return NULL;
766 
767     /*Allocate the extended data*/
768     lv_win_btn_ext_t * ext = lv_obj_allocate_ext_attr(win_btn, sizeof(lv_win_btn_ext_t));
769     LV_ASSERT_MEM(ext);
770     if(ext == NULL) {
771         lv_obj_del(win_btn);
772         return NULL;
773     }
774 
775     ext->alignment_in_header = LV_WIN_BTN_ALIGN_RIGHT;
776 
777     lv_obj_set_click(win_btn, true);
778     lv_win_btn_set_alignment(win_btn, LV_WIN_BTN_ALIGN_RIGHT);
779 
780     lv_theme_apply(win_btn, LV_THEME_WIN_BTN);
781     lv_coord_t btn_size = lv_obj_get_height_fit(par);
782     lv_obj_set_size(win_btn, btn_size, btn_size);
783 
784     lv_obj_t * img = lv_img_create(win_btn, NULL);
785     lv_obj_set_click(img, false);
786     lv_img_set_src(img, img_src);
787 
788     LV_LOG_INFO("win btn created");
789 
790     return win_btn;
791 }
792 
lv_win_btn_set_alignment(lv_obj_t * win_btn,const uint8_t alignment)793 static void lv_win_btn_set_alignment(lv_obj_t * win_btn, const uint8_t alignment)
794 {
795     lv_win_btn_ext_t * ext = lv_obj_get_ext_attr(win_btn);
796 
797     ext->alignment_in_header = alignment;
798 }
799 
lv_win_btn_get_alignment(const lv_obj_t * win_btn)800 static uint8_t lv_win_btn_get_alignment(const lv_obj_t * win_btn)
801 {
802     lv_win_btn_ext_t * ext = lv_obj_get_ext_attr(win_btn);
803 
804     return ext->alignment_in_header;
805 }
806 
807 #endif
808