acid-drop- Hacking the planet from a LilyGo T-Deck using custom firmware |
git clone git://git.acid.vegas/acid-drop.git |
Log | Files | Refs | Archive | README | LICENSE |
lv_slider.c (17305B)
1 /** 2 * @file lv_slider.c 3 * 4 */ 5 6 /********************* 7 * INCLUDES 8 *********************/ 9 #include "lv_slider.h" 10 #if LV_USE_SLIDER != 0 11 12 #include "../misc/lv_assert.h" 13 #include "../core/lv_group.h" 14 #include "../core/lv_indev.h" 15 #include "../draw/lv_draw.h" 16 #include "../misc/lv_math.h" 17 #include "../core/lv_disp.h" 18 #include "lv_img.h" 19 20 /********************* 21 * DEFINES 22 *********************/ 23 #define MY_CLASS &lv_slider_class 24 25 #define LV_SLIDER_KNOB_COORD(is_rtl, area) (is_rtl ? area.x1 : area.x2) 26 27 /********************** 28 * TYPEDEFS 29 **********************/ 30 31 /********************** 32 * STATIC PROTOTYPES 33 **********************/ 34 static void lv_slider_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj); 35 static void lv_slider_event(const lv_obj_class_t * class_p, lv_event_t * e); 36 static void position_knob(lv_obj_t * obj, lv_area_t * knob_area, const lv_coord_t knob_size, const bool hor); 37 static void draw_knob(lv_event_t * e); 38 static bool is_slider_horizontal(lv_obj_t * obj); 39 40 /********************** 41 * STATIC VARIABLES 42 **********************/ 43 const lv_obj_class_t lv_slider_class = { 44 .constructor_cb = lv_slider_constructor, 45 .event_cb = lv_slider_event, 46 .editable = LV_OBJ_CLASS_EDITABLE_TRUE, 47 .group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE, 48 .instance_size = sizeof(lv_slider_t), 49 .base_class = &lv_bar_class 50 }; 51 52 /********************** 53 * MACROS 54 **********************/ 55 56 /********************** 57 * GLOBAL FUNCTIONS 58 **********************/ 59 60 lv_obj_t * lv_slider_create(lv_obj_t * parent) 61 { 62 LV_LOG_INFO("begin"); 63 lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent); 64 lv_obj_class_init_obj(obj); 65 return obj; 66 } 67 68 bool lv_slider_is_dragged(const lv_obj_t * obj) 69 { 70 LV_ASSERT_OBJ(obj, MY_CLASS); 71 lv_slider_t * slider = (lv_slider_t *)obj; 72 73 return slider->dragging ? true : false; 74 } 75 76 /********************** 77 * STATIC FUNCTIONS 78 **********************/ 79 80 static void lv_slider_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj) 81 { 82 LV_UNUSED(class_p); 83 lv_slider_t * slider = (lv_slider_t *)obj; 84 85 /*Initialize the allocated 'slider'*/ 86 slider->value_to_set = NULL; 87 slider->dragging = 0U; 88 slider->left_knob_focus = 0U; 89 90 lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLL_CHAIN); 91 lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE); 92 lv_obj_set_ext_click_area(obj, LV_DPX(8)); 93 } 94 95 static void lv_slider_event(const lv_obj_class_t * class_p, lv_event_t * e) 96 { 97 LV_UNUSED(class_p); 98 99 lv_res_t res; 100 101 /*Call the ancestor's event handler*/ 102 res = lv_obj_event_base(MY_CLASS, e); 103 if(res != LV_RES_OK) return; 104 105 lv_event_code_t code = lv_event_get_code(e); 106 lv_obj_t * obj = lv_event_get_target(e); 107 lv_slider_t * slider = (lv_slider_t *)obj; 108 lv_slider_mode_t type = lv_slider_get_mode(obj); 109 110 /*Advanced hit testing: react only on dragging the knob(s)*/ 111 if(code == LV_EVENT_HIT_TEST) { 112 lv_hit_test_info_t * info = lv_event_get_param(e); 113 lv_coord_t ext_click_area = obj->spec_attr ? obj->spec_attr->ext_click_pad : 0; 114 115 /*Ordinary slider: was the knob area hit?*/ 116 lv_area_t a; 117 lv_area_copy(&a, &slider->right_knob_area); 118 lv_area_increase(&a, ext_click_area, ext_click_area); 119 info->res = _lv_area_is_point_on(&a, info->point, 0); 120 121 /*There's still a chance that there is a hit if there is another knob*/ 122 if((info->res == false) && (type == LV_SLIDER_MODE_RANGE)) { 123 lv_area_copy(&a, &slider->left_knob_area); 124 lv_area_increase(&a, ext_click_area, ext_click_area); 125 info->res = _lv_area_is_point_on(&a, info->point, 0); 126 } 127 } 128 else if(code == LV_EVENT_PRESSED) { 129 lv_obj_invalidate(obj); 130 131 lv_point_t p; 132 slider->dragging = true; 133 if(type == LV_SLIDER_MODE_NORMAL || type == LV_SLIDER_MODE_SYMMETRICAL) { 134 slider->value_to_set = &slider->bar.cur_value; 135 } 136 else if(type == LV_SLIDER_MODE_RANGE) { 137 lv_indev_get_point(lv_indev_get_act(), &p); 138 bool hor = lv_obj_get_width(obj) >= lv_obj_get_height(obj); 139 lv_base_dir_t base_dir = lv_obj_get_style_base_dir(obj, LV_PART_MAIN); 140 141 lv_coord_t dist_left, dist_right; 142 if(hor) { 143 if((base_dir != LV_BASE_DIR_RTL && p.x > slider->right_knob_area.x2) || (base_dir == LV_BASE_DIR_RTL && 144 p.x < slider->right_knob_area.x1)) { 145 slider->value_to_set = &slider->bar.cur_value; 146 } 147 else if((base_dir != LV_BASE_DIR_RTL && p.x < slider->left_knob_area.x1) || (base_dir == LV_BASE_DIR_RTL && 148 p.x > slider->left_knob_area.x2)) { 149 slider->value_to_set = &slider->bar.start_value; 150 } 151 else { 152 /*Calculate the distance from each knob*/ 153 dist_left = LV_ABS((slider->left_knob_area.x1 + (slider->left_knob_area.x2 - slider->left_knob_area.x1) / 2) - p.x); 154 dist_right = LV_ABS((slider->right_knob_area.x1 + (slider->right_knob_area.x2 - slider->right_knob_area.x1) / 2) - p.x); 155 156 /*Use whichever one is closer*/ 157 if(dist_right < dist_left) { 158 slider->value_to_set = &slider->bar.cur_value; 159 slider->left_knob_focus = 0; 160 } 161 else { 162 slider->value_to_set = &slider->bar.start_value; 163 slider->left_knob_focus = 1; 164 } 165 } 166 } 167 else { 168 if(p.y < slider->right_knob_area.y1) { 169 slider->value_to_set = &slider->bar.cur_value; 170 } 171 else if(p.y > slider->left_knob_area.y2) { 172 slider->value_to_set = &slider->bar.start_value; 173 } 174 else { 175 /*Calculate the distance from each knob*/ 176 dist_left = LV_ABS((slider->left_knob_area.y1 + (slider->left_knob_area.y2 - slider->left_knob_area.y1) / 2) - p.y); 177 dist_right = LV_ABS((slider->right_knob_area.y1 + (slider->right_knob_area.y2 - slider->right_knob_area.y1) / 2) - p.y); 178 179 /*Use whichever one is closer*/ 180 if(dist_right < dist_left) { 181 slider->value_to_set = &slider->bar.cur_value; 182 slider->left_knob_focus = 0; 183 } 184 else { 185 slider->value_to_set = &slider->bar.start_value; 186 slider->left_knob_focus = 1; 187 } 188 } 189 } 190 } 191 } 192 else if(code == LV_EVENT_PRESSING && slider->value_to_set != NULL) { 193 lv_indev_t * indev = lv_indev_get_act(); 194 if(lv_indev_get_type(indev) != LV_INDEV_TYPE_POINTER) return; 195 196 lv_point_t p; 197 lv_indev_get_point(indev, &p); 198 int32_t new_value = 0; 199 200 const int32_t range = slider->bar.max_value - slider->bar.min_value; 201 if(is_slider_horizontal(obj)) { 202 const lv_coord_t bg_left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN); 203 const lv_coord_t bg_right = lv_obj_get_style_pad_right(obj, LV_PART_MAIN); 204 const lv_coord_t w = lv_obj_get_width(obj); 205 const lv_coord_t indic_w = w - bg_left - bg_right; 206 207 if(lv_obj_get_style_base_dir(obj, LV_PART_MAIN) == LV_BASE_DIR_RTL) { 208 /*Make the point relative to the indicator*/ 209 new_value = (obj->coords.x2 - bg_right) - p.x; 210 } 211 else { 212 /*Make the point relative to the indicator*/ 213 new_value = p.x - (obj->coords.x1 + bg_left); 214 } 215 new_value = (new_value * range) / indic_w; 216 new_value += slider->bar.min_value; 217 } 218 else { 219 const lv_coord_t bg_top = lv_obj_get_style_pad_top(obj, LV_PART_MAIN); 220 const lv_coord_t bg_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN); 221 const lv_coord_t h = lv_obj_get_height(obj); 222 const lv_coord_t indic_h = h - bg_bottom - bg_top; 223 224 /*Make the point relative to the indicator*/ 225 new_value = p.y - (obj->coords.y2 + bg_bottom); 226 new_value = (-new_value * range) / indic_h; 227 new_value += slider->bar.min_value; 228 } 229 230 int32_t real_max_value = slider->bar.max_value; 231 int32_t real_min_value = slider->bar.min_value; 232 /*Figure out the min. and max. for this mode*/ 233 if(slider->value_to_set == &slider->bar.start_value) { 234 real_max_value = slider->bar.cur_value; 235 } 236 else { 237 real_min_value = slider->bar.start_value; 238 } 239 240 new_value = LV_CLAMP(real_min_value, new_value, real_max_value); 241 242 if(*slider->value_to_set != new_value) { 243 *slider->value_to_set = new_value; 244 lv_obj_invalidate(obj); 245 res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL); 246 if(res != LV_RES_OK) return; 247 } 248 249 } 250 else if(code == LV_EVENT_RELEASED || code == LV_EVENT_PRESS_LOST) { 251 slider->dragging = false; 252 slider->value_to_set = NULL; 253 254 lv_obj_invalidate(obj); 255 256 /*Leave edit mode if released. (No need to wait for LONG_PRESS)*/ 257 lv_group_t * g = lv_obj_get_group(obj); 258 bool editing = lv_group_get_editing(g); 259 lv_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act()); 260 if(indev_type == LV_INDEV_TYPE_ENCODER) { 261 if(editing) { 262 if(lv_slider_get_mode(obj) == LV_SLIDER_MODE_RANGE) { 263 if(slider->left_knob_focus == 0) slider->left_knob_focus = 1; 264 else { 265 slider->left_knob_focus = 0; 266 lv_group_set_editing(g, false); 267 } 268 } 269 else { 270 lv_group_set_editing(g, false); 271 } 272 } 273 } 274 275 } 276 else if(code == LV_EVENT_FOCUSED) { 277 lv_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act()); 278 if(indev_type == LV_INDEV_TYPE_ENCODER || indev_type == LV_INDEV_TYPE_KEYPAD) { 279 slider->left_knob_focus = 0; 280 } 281 } 282 else if(code == LV_EVENT_SIZE_CHANGED) { 283 lv_obj_refresh_ext_draw_size(obj); 284 } 285 else if(code == LV_EVENT_REFR_EXT_DRAW_SIZE) { 286 lv_coord_t knob_left = lv_obj_get_style_pad_left(obj, LV_PART_KNOB); 287 lv_coord_t knob_right = lv_obj_get_style_pad_right(obj, LV_PART_KNOB); 288 lv_coord_t knob_top = lv_obj_get_style_pad_top(obj, LV_PART_KNOB); 289 lv_coord_t knob_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_KNOB); 290 291 /*The smaller size is the knob diameter*/ 292 lv_coord_t zoom = lv_obj_get_style_transform_zoom(obj, LV_PART_KNOB); 293 lv_coord_t trans_w = lv_obj_get_style_transform_width(obj, LV_PART_KNOB); 294 lv_coord_t trans_h = lv_obj_get_style_transform_height(obj, LV_PART_KNOB); 295 lv_coord_t knob_size = LV_MIN(lv_obj_get_width(obj) + 2 * trans_w, lv_obj_get_height(obj) + 2 * trans_h) >> 1; 296 knob_size = (knob_size * zoom) >> 8; 297 knob_size += LV_MAX(LV_MAX(knob_left, knob_right), LV_MAX(knob_bottom, knob_top)); 298 knob_size += 2; /*For rounding error*/ 299 knob_size += lv_obj_calculate_ext_draw_size(obj, LV_PART_KNOB); 300 301 /*Indic. size is handled by bar*/ 302 lv_coord_t * s = lv_event_get_param(e); 303 *s = LV_MAX(*s, knob_size); 304 305 } 306 else if(code == LV_EVENT_KEY) { 307 char c = *((char *)lv_event_get_param(e)); 308 309 if(c == LV_KEY_RIGHT || c == LV_KEY_UP) { 310 if(!slider->left_knob_focus) lv_slider_set_value(obj, lv_slider_get_value(obj) + 1, LV_ANIM_ON); 311 else lv_slider_set_left_value(obj, lv_slider_get_left_value(obj) + 1, LV_ANIM_ON); 312 } 313 else if(c == LV_KEY_LEFT || c == LV_KEY_DOWN) { 314 if(!slider->left_knob_focus) lv_slider_set_value(obj, lv_slider_get_value(obj) - 1, LV_ANIM_ON); 315 else lv_slider_set_left_value(obj, lv_slider_get_left_value(obj) - 1, LV_ANIM_ON); 316 } 317 else { 318 return; 319 } 320 321 res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL); 322 if(res != LV_RES_OK) return; 323 } 324 else if(code == LV_EVENT_DRAW_MAIN) { 325 draw_knob(e); 326 } 327 } 328 329 static void draw_knob(lv_event_t * e) 330 { 331 lv_obj_t * obj = lv_event_get_target(e); 332 lv_slider_t * slider = (lv_slider_t *)obj; 333 lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e); 334 335 const bool is_rtl = LV_BASE_DIR_RTL == lv_obj_get_style_base_dir(obj, LV_PART_MAIN); 336 const bool is_horizontal = is_slider_horizontal(obj); 337 338 lv_area_t knob_area; 339 lv_coord_t knob_size; 340 if(is_horizontal) { 341 knob_size = lv_obj_get_height(obj); 342 knob_area.x1 = LV_SLIDER_KNOB_COORD(is_rtl, slider->bar.indic_area); 343 } 344 else { 345 bool is_symmetrical = false; 346 if(slider->bar.mode == LV_BAR_MODE_SYMMETRICAL && slider->bar.min_value < 0 && 347 slider->bar.max_value > 0) is_symmetrical = true; 348 349 knob_size = lv_obj_get_width(obj); 350 if(is_symmetrical && slider->bar.cur_value < 0) knob_area.y1 = slider->bar.indic_area.y2; 351 else knob_area.y1 = slider->bar.indic_area.y1; 352 } 353 354 lv_draw_rect_dsc_t knob_rect_dsc; 355 lv_draw_rect_dsc_init(&knob_rect_dsc); 356 lv_obj_init_draw_rect_dsc(obj, LV_PART_KNOB, &knob_rect_dsc); 357 /* Update knob area with knob style */ 358 position_knob(obj, &knob_area, knob_size, is_horizontal); 359 /* Update right knob area with calculated knob area */ 360 lv_area_copy(&slider->right_knob_area, &knob_area); 361 362 lv_obj_draw_part_dsc_t part_draw_dsc; 363 lv_obj_draw_dsc_init(&part_draw_dsc, draw_ctx); 364 part_draw_dsc.part = LV_PART_KNOB; 365 part_draw_dsc.class_p = MY_CLASS; 366 part_draw_dsc.type = LV_SLIDER_DRAW_PART_KNOB; 367 part_draw_dsc.id = 0; 368 part_draw_dsc.draw_area = &slider->right_knob_area; 369 part_draw_dsc.rect_dsc = &knob_rect_dsc; 370 371 if(lv_slider_get_mode(obj) != LV_SLIDER_MODE_RANGE) { 372 lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_draw_dsc); 373 lv_draw_rect(draw_ctx, &knob_rect_dsc, &slider->right_knob_area); 374 lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_draw_dsc); 375 } 376 else { 377 /*Save the draw part_draw_dsc. because it can be modified in the event*/ 378 lv_draw_rect_dsc_t knob_rect_dsc_tmp; 379 lv_memcpy(&knob_rect_dsc_tmp, &knob_rect_dsc, sizeof(lv_draw_rect_dsc_t)); 380 /* Draw the right knob */ 381 lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_draw_dsc); 382 lv_draw_rect(draw_ctx, &knob_rect_dsc, &slider->right_knob_area); 383 lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_draw_dsc); 384 385 /*Calculate the second knob area*/ 386 if(is_horizontal) { 387 /*use !is_rtl to get the other knob*/ 388 knob_area.x1 = LV_SLIDER_KNOB_COORD(!is_rtl, slider->bar.indic_area); 389 } 390 else { 391 knob_area.y1 = slider->bar.indic_area.y2; 392 } 393 position_knob(obj, &knob_area, knob_size, is_horizontal); 394 lv_area_copy(&slider->left_knob_area, &knob_area); 395 396 lv_memcpy(&knob_rect_dsc, &knob_rect_dsc_tmp, sizeof(lv_draw_rect_dsc_t)); 397 part_draw_dsc.type = LV_SLIDER_DRAW_PART_KNOB_LEFT; 398 part_draw_dsc.draw_area = &slider->left_knob_area; 399 part_draw_dsc.rect_dsc = &knob_rect_dsc; 400 part_draw_dsc.id = 1; 401 402 lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_draw_dsc); 403 lv_draw_rect(draw_ctx, &knob_rect_dsc, &slider->left_knob_area); 404 lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_draw_dsc); 405 } 406 } 407 408 static void position_knob(lv_obj_t * obj, lv_area_t * knob_area, const lv_coord_t knob_size, const bool hor) 409 { 410 if(hor) { 411 knob_area->x1 -= (knob_size >> 1); 412 knob_area->x2 = knob_area->x1 + knob_size - 1; 413 knob_area->y1 = obj->coords.y1; 414 knob_area->y2 = obj->coords.y2; 415 } 416 else { 417 knob_area->y1 -= (knob_size >> 1); 418 knob_area->y2 = knob_area->y1 + knob_size - 1; 419 knob_area->x1 = obj->coords.x1; 420 knob_area->x2 = obj->coords.x2; 421 } 422 423 lv_coord_t knob_left = lv_obj_get_style_pad_left(obj, LV_PART_KNOB); 424 lv_coord_t knob_right = lv_obj_get_style_pad_right(obj, LV_PART_KNOB); 425 lv_coord_t knob_top = lv_obj_get_style_pad_top(obj, LV_PART_KNOB); 426 lv_coord_t knob_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_KNOB); 427 428 lv_coord_t transf_w = lv_obj_get_style_transform_width(obj, LV_PART_KNOB); 429 lv_coord_t transf_h = lv_obj_get_style_transform_height(obj, LV_PART_KNOB); 430 431 /*Apply the paddings on the knob area*/ 432 knob_area->x1 -= knob_left + transf_w; 433 knob_area->x2 += knob_right + transf_w; 434 knob_area->y1 -= knob_top + transf_h; 435 knob_area->y2 += knob_bottom + transf_h; 436 } 437 438 static bool is_slider_horizontal(lv_obj_t * obj) 439 { 440 return lv_obj_get_width(obj) >= lv_obj_get_height(obj); 441 } 442 443 #endif