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