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_themes/lv_theme.h"
13 #include "../lv_core/lv_disp.h"
14 
15 /*********************
16  *      DEFINES
17  *********************/
18 
19 /**********************
20  *      TYPEDEFS
21  **********************/
22 
23 /**********************
24  *  STATIC PROTOTYPES
25  **********************/
26 static lv_res_t lv_win_signal(lv_obj_t * win, lv_signal_t sign, void * param);
27 static void lv_win_realign(lv_obj_t * win);
28 
29 /**********************
30  *  STATIC VARIABLES
31  **********************/
32 static lv_signal_cb_t ancestor_signal;
33 
34 /**********************
35  *      MACROS
36  **********************/
37 
38 /**********************
39  *   GLOBAL FUNCTIONS
40  **********************/
41 
42 /**
43  * Create a window objects
44  * @param par pointer to an object, it will be the parent of the new window
45  * @param copy pointer to a window object, if not NULL then the new object will be copied from it
46  * @return pointer to the created window
47  */
lv_win_create(lv_obj_t * par,const lv_obj_t * copy)48 lv_obj_t * lv_win_create(lv_obj_t * par, const lv_obj_t * copy)
49 {
50     LV_LOG_TRACE("window create started");
51 
52     /*Create the ancestor object*/
53     lv_obj_t * new_win = lv_obj_create(par, copy);
54     lv_mem_assert(new_win);
55     if(new_win == NULL) return NULL;
56 
57     if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_cb(new_win);
58 
59     /*Allocate the object type specific extended data*/
60     lv_win_ext_t * ext = lv_obj_allocate_ext_attr(new_win, sizeof(lv_win_ext_t));
61     lv_mem_assert(ext);
62     if(ext == NULL) return NULL;
63 
64     ext->page          = NULL;
65     ext->header        = NULL;
66     ext->title         = NULL;
67     ext->style_btn_rel = &lv_style_btn_rel;
68     ext->style_btn_pr  = &lv_style_btn_pr;
69     ext->btn_size      = (LV_DPI) / 2;
70 
71     /*Init the new window object*/
72     if(copy == NULL) {
73         /* Set a size which fits into the parent.
74          * Don't use `par` directly because if the window is created on a page it is moved to the
75          * scrollable so the parent has changed */
76         lv_obj_set_size(new_win, lv_obj_get_width_fit(lv_obj_get_parent(new_win)),
77                         lv_obj_get_height_fit(lv_obj_get_parent(new_win)));
78 
79         lv_obj_set_pos(new_win, 0, 0);
80         lv_obj_set_style(new_win, &lv_style_pretty);
81 
82         ext->page = lv_page_create(new_win, NULL);
83         lv_obj_set_protect(ext->page, LV_PROTECT_PARENT);
84         lv_page_set_sb_mode(ext->page, LV_SB_MODE_AUTO);
85         lv_page_set_style(ext->page, LV_PAGE_STYLE_BG, &lv_style_transp_fit);
86 
87         /*Create a holder for the header*/
88         ext->header = lv_obj_create(new_win, NULL);
89         /*Move back the header because it is automatically moved to the scrollable */
90         lv_obj_set_protect(ext->header, LV_PROTECT_PARENT);
91         lv_obj_set_parent(ext->header, new_win);
92 
93         /*Create a title on the header*/
94         ext->title = lv_label_create(ext->header, NULL);
95         lv_label_set_text(ext->title, "My title");
96 
97         /*Set the default styles*/
98         lv_theme_t * th = lv_theme_get_current();
99         if(th) {
100             lv_win_set_style(new_win, LV_WIN_STYLE_BG, th->style.win.bg);
101             lv_win_set_style(new_win, LV_WIN_STYLE_SB, th->style.win.sb);
102             lv_win_set_style(new_win, LV_WIN_STYLE_HEADER, th->style.win.header);
103             lv_win_set_style(new_win, LV_WIN_STYLE_CONTENT, th->style.win.content);
104             lv_win_set_style(new_win, LV_WIN_STYLE_BTN_REL, th->style.win.btn.rel);
105             lv_win_set_style(new_win, LV_WIN_STYLE_BTN_PR, th->style.win.btn.pr);
106         } else {
107             lv_win_set_style(new_win, LV_WIN_STYLE_BG, &lv_style_plain);
108             lv_win_set_style(new_win, LV_WIN_STYLE_CONTENT, &lv_style_transp);
109             lv_win_set_style(new_win, LV_WIN_STYLE_HEADER, &lv_style_plain_color);
110         }
111 
112         lv_obj_set_signal_cb(new_win, lv_win_signal);
113     }
114     /*Copy an existing object*/
115     else {
116         lv_win_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
117         /*Create the objects*/
118         ext->header   = lv_obj_create(new_win, copy_ext->header);
119         ext->title    = lv_label_create(ext->header, copy_ext->title);
120         ext->page     = lv_page_create(new_win, copy_ext->page);
121         ext->btn_size = copy_ext->btn_size;
122 
123         /*Copy the control buttons*/
124         lv_obj_t * child;
125         lv_obj_t * cbtn;
126         child = lv_obj_get_child_back(copy_ext->header, NULL);
127         child = lv_obj_get_child_back(copy_ext->header, child); /*Sip the title*/
128         while(child != NULL) {
129             cbtn = lv_btn_create(ext->header, child);
130             lv_img_create(cbtn, lv_obj_get_child(child, NULL));
131             child = lv_obj_get_child_back(copy_ext->header, child);
132         }
133 
134         lv_obj_set_signal_cb(new_win, lv_win_signal);
135     }
136 
137     /*Refresh the style with new signal function*/
138     lv_obj_refresh_style(new_win);
139 
140     lv_win_realign(new_win);
141 
142     LV_LOG_INFO("window created");
143 
144     return new_win;
145 }
146 
147 /**
148  * Delete all children of the scrl object, without deleting scrl child.
149  * @param obj pointer to an object
150  */
lv_win_clean(lv_obj_t * obj)151 void lv_win_clean(lv_obj_t * obj)
152 {
153     lv_obj_t * scrl = lv_page_get_scrl(obj);
154     lv_obj_clean(scrl);
155 }
156 
157 /*======================
158  * Add/remove functions
159  *=====================*/
160 
161 /**
162  * Add control button to the header of the window
163  * @param win pointer to a window object
164  * @param img_src an image source ('lv_img_t' variable, path to file or a symbol)
165  * @return pointer to the created button object
166  */
lv_win_add_btn(lv_obj_t * win,const void * img_src)167 lv_obj_t * lv_win_add_btn(lv_obj_t * win, const void * img_src)
168 {
169     lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
170 
171     lv_obj_t * btn = lv_btn_create(ext->header, NULL);
172     lv_btn_set_style(btn, LV_BTN_STYLE_REL, ext->style_btn_rel);
173     lv_btn_set_style(btn, LV_BTN_STYLE_PR, ext->style_btn_pr);
174     lv_obj_set_size(btn, ext->btn_size, ext->btn_size);
175 
176     lv_obj_t * img = lv_img_create(btn, NULL);
177     lv_obj_set_click(img, false);
178     lv_img_set_src(img, img_src);
179 
180     lv_win_realign(win);
181 
182     return btn;
183 }
184 
185 /*=====================
186  * Setter functions
187  *====================*/
188 
189 /**
190  * Can be assigned to a window control button to close the window
191  * @param btn pointer to the control button on teh widows header
192  * @param evet the event type
193  */
lv_win_close_event_cb(lv_obj_t * btn,lv_event_t event)194 void lv_win_close_event_cb(lv_obj_t * btn, lv_event_t event)
195 {
196     if(event == LV_EVENT_RELEASED) {
197         lv_obj_t * win = lv_win_get_from_btn(btn);
198 
199         lv_obj_del(win);
200     }
201 }
202 
203 /**
204  * Set the title of a window
205  * @param win pointer to a window object
206  * @param title string of the new title
207  */
lv_win_set_title(lv_obj_t * win,const char * title)208 void lv_win_set_title(lv_obj_t * win, const char * title)
209 {
210     lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
211 
212     lv_label_set_text(ext->title, title);
213     lv_win_realign(win);
214 }
215 
216 /**
217  * Set the control button size of a window
218  * @param win pointer to a window object
219  * @param size control button size
220  */
lv_win_set_btn_size(lv_obj_t * win,lv_coord_t size)221 void lv_win_set_btn_size(lv_obj_t * win, lv_coord_t size)
222 {
223     lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
224     if(ext->btn_size == size) return;
225 
226     ext->btn_size = size;
227 
228     lv_win_realign(win);
229 }
230 
231 /**
232  * Set the layout of the window
233  * @param win pointer to a window object
234  * @param layout the layout from 'lv_layout_t'
235  */
lv_win_set_layout(lv_obj_t * win,lv_layout_t layout)236 void lv_win_set_layout(lv_obj_t * win, lv_layout_t layout)
237 {
238     lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
239     lv_page_set_scrl_layout(ext->page, layout);
240 }
241 
242 /**
243  * Set the scroll bar mode of a window
244  * @param win pointer to a window object
245  * @param sb_mode the new scroll bar mode from  'lv_sb_mode_t'
246  */
lv_win_set_sb_mode(lv_obj_t * win,lv_sb_mode_t sb_mode)247 void lv_win_set_sb_mode(lv_obj_t * win, lv_sb_mode_t sb_mode)
248 {
249     lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
250     lv_page_set_sb_mode(ext->page, sb_mode);
251 }
252 /**
253  * Set focus animation duration on `lv_win_focus()`
254  * @param win pointer to a window object
255  * @param anim_time duration of animation [ms]
256  */
lv_win_set_anim_time(lv_obj_t * win,uint16_t anim_time)257 void lv_win_set_anim_time(lv_obj_t * win, uint16_t anim_time)
258 {
259     lv_page_set_anim_time(lv_win_get_content(win), anim_time);
260 }
261 
262 /**
263  * Set a style of a window
264  * @param win pointer to a window object
265  * @param type which style should be set
266  * @param style pointer to a style
267  */
lv_win_set_style(lv_obj_t * win,lv_win_style_t type,const lv_style_t * style)268 void lv_win_set_style(lv_obj_t * win, lv_win_style_t type, const lv_style_t * style)
269 {
270     lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
271 
272     switch(type) {
273         case LV_WIN_STYLE_BG:
274             lv_obj_set_style(win, style);
275             lv_win_realign(win);
276             break;
277         case LV_WIN_STYLE_CONTENT: lv_page_set_style(ext->page, LV_PAGE_STYLE_SCRL, style); break;
278         case LV_WIN_STYLE_SB: lv_page_set_style(ext->page, LV_PAGE_STYLE_SB, style); break;
279         case LV_WIN_STYLE_HEADER:
280             lv_obj_set_style(ext->header, style);
281             lv_win_realign(win);
282             break;
283         case LV_WIN_STYLE_BTN_REL: ext->style_btn_rel = style; break;
284         case LV_WIN_STYLE_BTN_PR: ext->style_btn_pr = style; break;
285     }
286 
287     /*Refresh the existing buttons*/
288     if(type == LV_WIN_STYLE_BTN_REL || type == LV_WIN_STYLE_BTN_PR) {
289         lv_obj_t * btn;
290         btn = lv_obj_get_child_back(ext->header, NULL);
291         btn = lv_obj_get_child_back(ext->header, btn); /*Skip the title*/
292         while(btn != NULL) {
293             if(type == LV_WIN_STYLE_BTN_REL)
294                 lv_btn_set_style(btn, LV_BTN_STYLE_REL, style);
295             else
296                 lv_btn_set_style(btn, LV_BTN_STYLE_PR, style);
297             btn = lv_obj_get_child_back(ext->header, btn);
298         }
299     }
300 }
301 
302 /**
303  * Set drag status of a window. If set to 'true' window can be dragged like on a PC.
304  * @param win pointer to a window object
305  * @param en whether dragging is enabled
306  */
lv_win_set_drag(lv_obj_t * win,bool en)307 void lv_win_set_drag(lv_obj_t * win, bool en)
308 {
309     lv_win_ext_t * ext    = lv_obj_get_ext_attr(win);
310     lv_obj_t * win_header = ext->header;
311     lv_obj_set_drag_parent(win_header, en);
312     lv_obj_set_drag(win, en);
313 }
314 
315 /*=====================
316  * Getter functions
317  *====================*/
318 
319 /**
320  * Get the title of a window
321  * @param win pointer to a window object
322  * @return title string of the window
323  */
lv_win_get_title(const lv_obj_t * win)324 const char * lv_win_get_title(const lv_obj_t * win)
325 {
326     lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
327     return lv_label_get_text(ext->title);
328 }
329 
330 /**
331  * Get the content holder object of window (`lv_page`) to allow additional customization
332  * @param win pointer to a window object
333  * @return the Page object where the window's content is
334  */
lv_win_get_content(const lv_obj_t * win)335 lv_obj_t * lv_win_get_content(const lv_obj_t * win)
336 {
337     lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
338     return ext->page;
339 }
340 
341 /**
342  * Get the control button size of a window
343  * @param win pointer to a window object
344  * @return control button size
345  */
lv_win_get_btn_size(const lv_obj_t * win)346 lv_coord_t lv_win_get_btn_size(const lv_obj_t * win)
347 {
348     lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
349     return ext->btn_size;
350 }
351 
352 /**
353  * Get the pointer of a widow from one of  its control button.
354  * It is useful in the action of the control buttons where only button is known.
355  * @param ctrl_btn pointer to a control button of a window
356  * @return pointer to the window of 'ctrl_btn'
357  */
lv_win_get_from_btn(const lv_obj_t * ctrl_btn)358 lv_obj_t * lv_win_get_from_btn(const lv_obj_t * ctrl_btn)
359 {
360     lv_obj_t * header = lv_obj_get_parent(ctrl_btn);
361     lv_obj_t * win    = lv_obj_get_parent(header);
362 
363     return win;
364 }
365 
366 /**
367  * Get the layout of a window
368  * @param win pointer to a window object
369  * @return the layout of the window (from 'lv_layout_t')
370  */
lv_win_get_layout(lv_obj_t * win)371 lv_layout_t lv_win_get_layout(lv_obj_t * win)
372 {
373     lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
374     return lv_page_get_scrl_layout(ext->page);
375 }
376 
377 /**
378  * Get the scroll bar mode of a window
379  * @param win pointer to a window object
380  * @return the scroll bar mode of the window (from 'lv_sb_mode_t')
381  */
lv_win_get_sb_mode(lv_obj_t * win)382 lv_sb_mode_t lv_win_get_sb_mode(lv_obj_t * win)
383 {
384     lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
385     return lv_page_get_sb_mode(ext->page);
386 }
387 
388 /**
389  * Get focus animation duration
390  * @param win pointer to a window object
391  * @return duration of animation [ms]
392  */
lv_win_get_anim_time(const lv_obj_t * win)393 uint16_t lv_win_get_anim_time(const lv_obj_t * win)
394 {
395     return lv_page_get_anim_time(lv_win_get_content(win));
396 }
397 
398 /**
399  * Get width of the content area (page scrollable) of the window
400  * @param win pointer to a window object
401  * @return the width of the content_bg area
402  */
lv_win_get_width(lv_obj_t * win)403 lv_coord_t lv_win_get_width(lv_obj_t * win)
404 {
405     lv_win_ext_t * ext            = lv_obj_get_ext_attr(win);
406     lv_obj_t * scrl               = lv_page_get_scrl(ext->page);
407     const lv_style_t * style_scrl = lv_obj_get_style(scrl);
408 
409     return lv_obj_get_width(scrl) - style_scrl->body.padding.left - style_scrl->body.padding.right;
410 }
411 
412 /**
413  * Get a style of a window
414  * @param win pointer to a button object
415  * @param type which style window be get
416  * @return style pointer to a style
417  */
lv_win_get_style(const lv_obj_t * win,lv_win_style_t type)418 const lv_style_t * lv_win_get_style(const lv_obj_t * win, lv_win_style_t type)
419 {
420     const lv_style_t * style = NULL;
421     lv_win_ext_t * ext       = lv_obj_get_ext_attr(win);
422 
423     switch(type) {
424         case LV_WIN_STYLE_BG: style = lv_obj_get_style(win); break;
425         case LV_WIN_STYLE_CONTENT: style = lv_page_get_style(ext->page, LV_PAGE_STYLE_SCRL); break;
426         case LV_WIN_STYLE_SB: style = lv_page_get_style(ext->page, LV_PAGE_STYLE_SB); break;
427         case LV_WIN_STYLE_HEADER: style = lv_obj_get_style(ext->header); break;
428         case LV_WIN_STYLE_BTN_REL: style = ext->style_btn_rel; break;
429         case LV_WIN_STYLE_BTN_PR: style = ext->style_btn_pr; break;
430         default: style = NULL; break;
431     }
432 
433     return style;
434 }
435 
436 /*=====================
437  * Other functions
438  *====================*/
439 
440 /**
441  * Focus on an object. It ensures that the object will be visible in the window.
442  * @param win pointer to a window object
443  * @param obj pointer to an object to focus (must be in the window)
444  * @param anim_en LV_ANIM_ON focus with an animation; LV_ANIM_OFF focus without animation
445  */
lv_win_focus(lv_obj_t * win,lv_obj_t * obj,lv_anim_enable_t anim_en)446 void lv_win_focus(lv_obj_t * win, lv_obj_t * obj, lv_anim_enable_t anim_en)
447 {
448     lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
449     lv_page_focus(ext->page, obj, anim_en);
450 }
451 
452 /**********************
453  *   STATIC FUNCTIONS
454  **********************/
455 
456 /**
457  * Signal function of the window
458  * @param win pointer to a window object
459  * @param sign a signal type from lv_signal_t enum
460  * @param param pointer to a signal specific variable
461  * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
462  */
lv_win_signal(lv_obj_t * win,lv_signal_t sign,void * param)463 static lv_res_t lv_win_signal(lv_obj_t * win, lv_signal_t sign, void * param)
464 {
465     lv_res_t res;
466 
467     /* Include the ancient signal function */
468     res = ancestor_signal(win, sign, param);
469     if(res != LV_RES_OK) return res;
470 
471     lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
472     if(sign == LV_SIGNAL_CHILD_CHG) { /*Move children to the page*/
473         lv_obj_t * page = ext->page;
474         if(page != NULL) {
475             lv_obj_t * child;
476             child = lv_obj_get_child(win, NULL);
477             while(child != NULL) {
478                 if(lv_obj_is_protected(child, LV_PROTECT_PARENT) == false) {
479                     lv_obj_t * tmp = child;
480                     child          = lv_obj_get_child(win, child); /*Get the next child before move this*/
481                     lv_obj_set_parent(tmp, page);
482                 } else {
483                     child = lv_obj_get_child(win, child);
484                 }
485             }
486         }
487     } else if(sign == LV_SIGNAL_STYLE_CHG) {
488         lv_win_realign(win);
489     } else if(sign == LV_SIGNAL_CORD_CHG) {
490         /*If the size is changed refresh the window*/
491         if(lv_area_get_width(param) != lv_obj_get_width(win) || lv_area_get_height(param) != lv_obj_get_height(win)) {
492             lv_win_realign(win);
493         }
494     } else if(sign == LV_SIGNAL_CLEANUP) {
495         ext->header = NULL; /*These objects were children so they are already invalid*/
496         ext->page   = NULL;
497         ext->title  = NULL;
498     } else if(sign == LV_SIGNAL_CONTROL) {
499         /*Forward all the control signals to the page*/
500         ext->page->signal_cb(ext->page, sign, param);
501     } else if(sign == LV_SIGNAL_GET_TYPE) {
502         lv_obj_type_t * buf = param;
503         uint8_t i;
504         for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
505             if(buf->type[i] == NULL) break;
506         }
507         buf->type[i] = "lv_win";
508     }
509 
510     return res;
511 }
512 
513 /**
514  * Realign the building elements of a window
515  * @param win pointer to window objectker
516  */
lv_win_realign(lv_obj_t * win)517 static void lv_win_realign(lv_obj_t * win)
518 {
519     lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
520 
521     if(ext->page == NULL || ext->header == NULL || ext->title == NULL) return;
522 
523     const lv_style_t * header_style = lv_win_get_style(win, LV_WIN_STYLE_HEADER);
524     lv_obj_set_size(ext->header, lv_obj_get_width(win),
525                     ext->btn_size + header_style->body.padding.top + header_style->body.padding.bottom);
526 
527     bool first_btn = true;
528     lv_obj_t * btn;
529     lv_obj_t * btn_prev = NULL;
530     /*Refresh the size of all control buttons*/
531     btn = lv_obj_get_child_back(ext->header, NULL);
532     btn = lv_obj_get_child_back(ext->header, btn); /*Skip the title*/
533     while(btn != NULL) {
534         lv_obj_set_size(btn, ext->btn_size, ext->btn_size);
535         if(first_btn) {
536             lv_obj_align(btn, ext->header, LV_ALIGN_IN_RIGHT_MID, -header_style->body.padding.right, 0);
537             first_btn = false;
538         } else {
539             lv_obj_align(btn, btn_prev, LV_ALIGN_OUT_LEFT_MID, -header_style->body.padding.inner, 0);
540         }
541         btn_prev = btn;
542         btn      = lv_obj_get_child_back(ext->header, btn);
543     }
544 
545     const lv_style_t * style_header = lv_win_get_style(win, LV_WIN_STYLE_HEADER);
546     lv_obj_align(ext->title, NULL, LV_ALIGN_IN_LEFT_MID, style_header->body.padding.left, 0);
547 
548     lv_obj_set_pos(ext->header, 0, 0);
549 
550     lv_obj_set_size(ext->page, lv_obj_get_width(win), lv_obj_get_height(win) - lv_obj_get_height(ext->header));
551     lv_obj_align(ext->page, ext->header, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 0);
552 }
553 
554 #endif
555