1 /**
2 * @file lv_tabview.c
3 *
4 */
5
6 /*********************
7 * INCLUDES
8 *********************/
9 #include "lv_tabview.h"
10 #if LV_USE_TABVIEW
11
12 #include "../../../misc/lv_assert.h"
13
14 /*********************
15 * DEFINES
16 *********************/
17 #define MY_CLASS &lv_tabview_class
18
19 /**********************
20 * TYPEDEFS
21 **********************/
22
23 /**********************
24 * STATIC PROTOTYPES
25 **********************/
26 static void lv_tabview_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
27 static void lv_tabview_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
28 static void lv_tabview_event(const lv_obj_class_t * class_p, lv_event_t * e);
29 static void btns_value_changed_event_cb(lv_event_t * e);
30 static void cont_scroll_end_event_cb(lv_event_t * e);
31
32 /**********************
33 * STATIC VARIABLES
34 **********************/
35 const lv_obj_class_t lv_tabview_class = {
36 .constructor_cb = lv_tabview_constructor,
37 .destructor_cb = lv_tabview_destructor,
38 .event_cb = lv_tabview_event,
39 .width_def = LV_PCT(100),
40 .height_def = LV_PCT(100),
41 .base_class = &lv_obj_class,
42 .instance_size = sizeof(lv_tabview_t)
43 };
44
45 static lv_dir_t tabpos_create;
46 static lv_coord_t tabsize_create;
47
48 /**********************
49 * MACROS
50 **********************/
51
52 /**********************
53 * GLOBAL FUNCTIONS
54 **********************/
55
lv_tabview_create(lv_obj_t * parent,lv_dir_t tab_pos,lv_coord_t tab_size)56 lv_obj_t * lv_tabview_create(lv_obj_t * parent, lv_dir_t tab_pos, lv_coord_t tab_size)
57 {
58 LV_LOG_INFO("begin");
59 tabpos_create = tab_pos;
60 tabsize_create = tab_size;
61
62 lv_obj_t * obj = lv_obj_class_create_obj(&lv_tabview_class, parent);
63 lv_obj_class_init_obj(obj);
64 return obj;
65 }
66
lv_tabview_add_tab(lv_obj_t * obj,const char * name)67 lv_obj_t * lv_tabview_add_tab(lv_obj_t * obj, const char * name)
68 {
69 LV_ASSERT_OBJ(obj, MY_CLASS);
70 lv_tabview_t * tabview = (lv_tabview_t *)obj;
71 lv_obj_t * cont = lv_tabview_get_content(obj);
72
73 lv_obj_t * page = lv_obj_create(cont);
74 lv_obj_set_size(page, LV_PCT(100), LV_PCT(100));
75 lv_obj_clear_flag(page, LV_OBJ_FLAG_CLICK_FOCUSABLE);
76 uint32_t tab_id = lv_obj_get_child_cnt(cont);
77
78 lv_obj_t * btns = lv_tabview_get_tab_btns(obj);
79
80 char ** old_map = tabview->map;
81 char ** new_map;
82
83 /*top or bottom dir*/
84 if(tabview->tab_pos & LV_DIR_VER) {
85 new_map = lv_mem_alloc((tab_id + 1) * sizeof(const char *));
86 lv_memcpy_small(new_map, old_map, sizeof(const char *) * (tab_id - 1));
87 new_map[tab_id - 1] = lv_mem_alloc(strlen(name) + 1);
88 strcpy((char *)new_map[tab_id - 1], name);
89 new_map[tab_id] = "";
90 }
91 /*left or right dir*/
92 else {
93 new_map = lv_mem_alloc((tab_id * 2) * sizeof(const char *));
94 lv_memcpy_small(new_map, old_map, sizeof(const char *) * (tab_id - 1) * 2);
95 if(tabview->tab_cnt == 0) {
96 new_map[0] = lv_mem_alloc(strlen(name) + 1);
97 strcpy((char *)new_map[0], name);
98 new_map[1] = "";
99 }
100 else {
101 new_map[tab_id * 2 - 3] = "\n";
102 new_map[tab_id * 2 - 2] = lv_mem_alloc(strlen(name) + 1);
103 new_map[tab_id * 2 - 1] = "";
104 strcpy((char *)new_map[(tab_id * 2) - 2], name);
105 }
106 }
107 tabview->map = new_map;
108 lv_btnmatrix_set_map(btns, (const char **)new_map);
109 lv_mem_free(old_map);
110
111 lv_btnmatrix_set_btn_ctrl_all(btns, LV_BTNMATRIX_CTRL_CHECKABLE | LV_BTNMATRIX_CTRL_CLICK_TRIG |
112 LV_BTNMATRIX_CTRL_NO_REPEAT);
113
114 tabview->tab_cnt++;
115 if(tabview->tab_cnt == 1) {
116 lv_tabview_set_act(obj, 0, LV_ANIM_OFF);
117 }
118
119 lv_btnmatrix_set_btn_ctrl(btns, tabview->tab_cur, LV_BTNMATRIX_CTRL_CHECKED);
120
121 return page;
122 }
123
lv_tabview_rename_tab(lv_obj_t * obj,uint32_t id,const char * new_name)124 void lv_tabview_rename_tab(lv_obj_t * obj, uint32_t id, const char * new_name)
125 {
126 LV_ASSERT_OBJ(obj, MY_CLASS);
127 lv_tabview_t * tabview = (lv_tabview_t *)obj;
128
129 if(id >= tabview->tab_cnt) return;
130 if(tabview->tab_pos & LV_DIR_HOR) id *= 2;
131
132 lv_mem_free(tabview->map[id]);
133 tabview->map[id] = lv_mem_alloc(strlen(new_name) + 1);
134 strcpy(tabview->map[id], new_name);
135 lv_obj_invalidate(obj);
136 }
137
lv_tabview_set_act(lv_obj_t * obj,uint32_t id,lv_anim_enable_t anim_en)138 void lv_tabview_set_act(lv_obj_t * obj, uint32_t id, lv_anim_enable_t anim_en)
139 {
140 LV_ASSERT_OBJ(obj, MY_CLASS);
141 lv_tabview_t * tabview = (lv_tabview_t *)obj;
142
143 if(id >= tabview->tab_cnt) {
144 id = tabview->tab_cnt - 1;
145 }
146
147 /*To be sure lv_obj_get_content_width will return valid value*/
148 lv_obj_update_layout(obj);
149
150 lv_obj_t * cont = lv_tabview_get_content(obj);
151 if(cont == NULL) return;
152
153 if((tabview->tab_pos & LV_DIR_VER) != 0) {
154 lv_coord_t gap = lv_obj_get_style_pad_column(cont, LV_PART_MAIN);
155 lv_coord_t w = lv_obj_get_content_width(cont);
156 if(lv_obj_get_style_base_dir(obj, LV_PART_MAIN) != LV_BASE_DIR_RTL) {
157 lv_obj_scroll_to_x(cont, id * (gap + w), anim_en);
158 }
159 else {
160 int32_t id_rtl = -(int32_t)id;
161 lv_obj_scroll_to_x(cont, (gap + w) * id_rtl, anim_en);
162 }
163 }
164 else {
165 lv_coord_t gap = lv_obj_get_style_pad_row(cont, LV_PART_MAIN);
166 lv_coord_t h = lv_obj_get_content_height(cont);
167 lv_obj_scroll_to_y(cont, id * (gap + h), anim_en);
168 }
169
170 lv_obj_t * btns = lv_tabview_get_tab_btns(obj);
171 lv_btnmatrix_set_btn_ctrl(btns, id, LV_BTNMATRIX_CTRL_CHECKED);
172 tabview->tab_cur = id;
173 }
174
lv_tabview_get_tab_act(lv_obj_t * obj)175 uint16_t lv_tabview_get_tab_act(lv_obj_t * obj)
176 {
177 LV_ASSERT_OBJ(obj, MY_CLASS);
178 lv_tabview_t * tabview = (lv_tabview_t *)obj;
179 return tabview->tab_cur;
180 }
181
lv_tabview_get_content(lv_obj_t * tv)182 lv_obj_t * lv_tabview_get_content(lv_obj_t * tv)
183 {
184 return lv_obj_get_child(tv, 1);
185 }
186
lv_tabview_get_tab_btns(lv_obj_t * tv)187 lv_obj_t * lv_tabview_get_tab_btns(lv_obj_t * tv)
188 {
189 return lv_obj_get_child(tv, 0);
190 }
191
192 /**********************
193 * STATIC FUNCTIONS
194 **********************/
195
lv_tabview_constructor(const lv_obj_class_t * class_p,lv_obj_t * obj)196 static void lv_tabview_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
197 {
198 LV_UNUSED(class_p);
199 lv_tabview_t * tabview = (lv_tabview_t *)obj;
200
201 tabview->tab_pos = tabpos_create;
202
203 switch(tabview->tab_pos) {
204 case LV_DIR_TOP:
205 lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_COLUMN);
206 break;
207 case LV_DIR_BOTTOM:
208 lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_COLUMN_REVERSE);
209 break;
210 case LV_DIR_LEFT:
211 lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_ROW);
212 break;
213 case LV_DIR_RIGHT:
214 lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_ROW_REVERSE);
215 break;
216 }
217
218 lv_obj_set_size(obj, LV_PCT(100), LV_PCT(100));
219
220 lv_obj_t * btnm;
221 lv_obj_t * cont;
222
223 btnm = lv_btnmatrix_create(obj);
224 cont = lv_obj_create(obj);
225
226 lv_btnmatrix_set_one_checked(btnm, true);
227 tabview->map = lv_mem_alloc(sizeof(const char *));
228 tabview->map[0] = "";
229 lv_btnmatrix_set_map(btnm, (const char **)tabview->map);
230 lv_obj_add_event_cb(btnm, btns_value_changed_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
231 lv_obj_add_flag(btnm, LV_OBJ_FLAG_EVENT_BUBBLE);
232
233 lv_obj_add_event_cb(cont, cont_scroll_end_event_cb, LV_EVENT_ALL, NULL);
234 lv_obj_set_scrollbar_mode(cont, LV_SCROLLBAR_MODE_OFF);
235
236 switch(tabview->tab_pos) {
237 case LV_DIR_TOP:
238 case LV_DIR_BOTTOM:
239 lv_obj_set_size(btnm, LV_PCT(100), tabsize_create);
240 lv_obj_set_width(cont, LV_PCT(100));
241 lv_obj_set_flex_grow(cont, 1);
242 break;
243 case LV_DIR_LEFT:
244 case LV_DIR_RIGHT:
245 lv_obj_set_size(btnm, tabsize_create, LV_PCT(100));
246 lv_obj_set_height(cont, LV_PCT(100));
247 lv_obj_set_flex_grow(cont, 1);
248 break;
249 }
250
251 lv_group_t * g = lv_group_get_default();
252 if(g) lv_group_add_obj(g, btnm);
253
254 if((tabview->tab_pos & LV_DIR_VER) != 0) {
255 lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_ROW);
256 lv_obj_set_scroll_snap_x(cont, LV_SCROLL_SNAP_CENTER);
257 }
258 else {
259 lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_COLUMN);
260 lv_obj_set_scroll_snap_y(cont, LV_SCROLL_SNAP_CENTER);
261 }
262 lv_obj_add_flag(cont, LV_OBJ_FLAG_SCROLL_ONE);
263 lv_obj_clear_flag(cont, LV_OBJ_FLAG_SCROLL_ON_FOCUS);
264 }
265
lv_tabview_destructor(const lv_obj_class_t * class_p,lv_obj_t * obj)266 static void lv_tabview_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
267 {
268 LV_UNUSED(class_p);
269 lv_tabview_t * tabview = (lv_tabview_t *)obj;
270
271 uint32_t i;
272 if(tabview->tab_pos & LV_DIR_VER) {
273 for(i = 0; i < tabview->tab_cnt; i++) {
274 lv_mem_free(tabview->map[i]);
275 tabview->map[i] = NULL;
276 }
277 }
278 if(tabview->tab_pos & LV_DIR_HOR) {
279 for(i = 0; i < tabview->tab_cnt; i++) {
280 lv_mem_free(tabview->map[i * 2]);
281 tabview->map[i * 2] = NULL;
282 }
283 }
284
285
286 lv_mem_free(tabview->map);
287 tabview->map = NULL;
288 }
289
lv_tabview_event(const lv_obj_class_t * class_p,lv_event_t * e)290 static void lv_tabview_event(const lv_obj_class_t * class_p, lv_event_t * e)
291 {
292 LV_UNUSED(class_p);
293 lv_res_t res = lv_obj_event_base(&lv_tabview_class, e);
294 if(res != LV_RES_OK) return;
295
296 lv_event_code_t code = lv_event_get_code(e);
297 lv_obj_t * target = lv_event_get_target(e);
298
299 if(code == LV_EVENT_SIZE_CHANGED) {
300 lv_tabview_set_act(target, lv_tabview_get_tab_act(target), LV_ANIM_OFF);
301 }
302 }
303
304
btns_value_changed_event_cb(lv_event_t * e)305 static void btns_value_changed_event_cb(lv_event_t * e)
306 {
307 lv_obj_t * btns = lv_event_get_target(e);
308
309 lv_obj_t * tv = lv_obj_get_parent(btns);
310 uint32_t id = lv_btnmatrix_get_selected_btn(btns);
311 lv_tabview_set_act(tv, id, LV_ANIM_ON);
312 }
313
cont_scroll_end_event_cb(lv_event_t * e)314 static void cont_scroll_end_event_cb(lv_event_t * e)
315 {
316 lv_obj_t * cont = lv_event_get_target(e);
317 lv_event_code_t code = lv_event_get_code(e);
318
319 lv_obj_t * tv = lv_obj_get_parent(cont);
320 lv_tabview_t * tv_obj = (lv_tabview_t *)tv;
321 if(code == LV_EVENT_LAYOUT_CHANGED) {
322 lv_tabview_set_act(tv, lv_tabview_get_tab_act(tv), LV_ANIM_OFF);
323 }
324 else if(code == LV_EVENT_SCROLL_END) {
325 lv_indev_t * indev = lv_indev_get_act();
326 if(indev && indev->proc.state == LV_INDEV_STATE_PRESSED) {
327 return;
328 }
329
330 lv_point_t p;
331 lv_obj_get_scroll_end(cont, &p);
332
333 lv_coord_t t;
334 if((tv_obj->tab_pos & LV_DIR_VER) != 0) {
335 lv_coord_t w = lv_obj_get_content_width(cont);
336 if(lv_obj_get_style_base_dir(tv, LV_PART_MAIN) == LV_BASE_DIR_RTL) t = -(p.x - w / 2) / w;
337 else t = (p.x + w / 2) / w;
338 }
339 else {
340 lv_coord_t h = lv_obj_get_content_height(cont);
341 t = (p.y + h / 2) / h;
342 }
343
344 if(t < 0) t = 0;
345 bool new_tab = false;
346 if(t != lv_tabview_get_tab_act(tv)) new_tab = true;
347 lv_tabview_set_act(tv, t, LV_ANIM_ON);
348
349 if(new_tab) lv_event_send(tv, LV_EVENT_VALUE_CHANGED, NULL);
350 }
351 }
352 #endif /*LV_USE_TABVIEW*/
353