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