/* * Copyright (c) 2019 Actions Semi Co., Inc. * * SPDX-License-Identifier: Apache-2.0 */ /** * @file * @brief ui service interface */ #define LOG_MODULE_CUSTOMER #include #include #include #include #include LOG_MODULE_REGISTER(view_scrl, LOG_LEVEL_INF); #ifdef CONFIG_VIEW_SCROLL_TRACKING_FINGER #define GESTURE_DROP_START_THRESHOLD 100 #else #define GESTURE_DROP_START_THRESHOLD 400 #endif #define GESTURE_DROP_THRESHOLD 100 #define GESTURE_MOVE_THRESHOLD 10 #define GESTURE_ANIM_SPEED 32 /* pixels per 16 ms */ /* gesture moving snap to edge anim enable flag */ #define USE_GESTURE_MOVE_SNAP_ANIM 0 /* gesture long view snap to edge anim enable flag */ #define USE_GESTURE_LONGVIEW_SNAP_ANIM 0 /* gesture animation config */ #define USE_GESTURE_ANIM_PATH_COS 1 #define USE_GESTURE_ANIM_PATH_BEZIER 0 #if USE_GESTURE_ANIM_PATH_COS static int32_t _animation_path_cos(int32_t elaps); #elif USE_GESTURE_ANIM_PATH_BEZIER static int32_t _animation_update_bezier(int32_t elaps); #endif static const uint8_t opposite_gestures[] = { 0, GESTURE_DROP_UP, GESTURE_DROP_DOWN, GESTURE_DROP_RIGHT, GESTURE_DROP_LEFT, }; #if USE_GESTURE_MOVE_SNAP_ANIM == 0 || USE_GESTURE_LONGVIEW_SNAP_ANIM == 0 static void _reposition_region_in_display(ui_region_t *region) { ui_region_t cont = { .x1 = 0, .y1 = 0, .x2 = view_manager_get_disp_xres() - 1, .y2 = view_manager_get_disp_yres() - 1, }; ui_region_fit_in(region, &cont); } #endif static int gesture_scroll_begin(input_dev_t * pointer_dev) { input_dev_runtime_t *runtime = &pointer_dev->proc; point_t *act_point = &runtime->types.pointer.act_point; int16_t x_res = view_manager_get_disp_xres(); int16_t y_res = view_manager_get_disp_yres(); uint16_t focus_id; uint8_t focus_attr; bool towards_screen = false; runtime->view_id = view_manager_get_draggable_view(runtime->scroll_dir, &towards_screen); #if USE_GESTURE_MOVE_SNAP_ANIM == 0 if (runtime->view_id == VIEW_INVALID_ID) { return -1; } #endif focus_id = view_manager_get_focused_view(); if (focus_id == VIEW_INVALID_ID) { return -1; } focus_attr = view_get_drag_attribute(focus_id); if (focus_id != runtime->view_id && focus_attr > 0) { return -1; } runtime->types.pointer.scroll_to_screen = towards_screen; runtime->types.pointer.scroll_start = *act_point; runtime->types.pointer.last_scroll_off = 0; #if USE_GESTURE_MOVE_SNAP_ANIM == 0 if (view_has_move_attribute(runtime->view_id)) #else if (runtime->view_id == VIEW_INVALID_ID || view_has_move_attribute(runtime->view_id)) #endif { /* move */ runtime->last_view_id = focus_id; runtime->pre_view_id = view_manager_get_draggable_view(opposite_gestures[runtime->scroll_dir], NULL); #if USE_GESTURE_MOVE_SNAP_ANIM if (runtime->view_id == VIEW_INVALID_ID && runtime->pre_view_id == VIEW_INVALID_ID) { return -2; } #endif } else { /* drop */ int16_t offset = 0; switch (runtime->scroll_dir) { case GESTURE_DROP_DOWN: offset = act_point->y; #ifdef CONFIG_VIEW_SCROLL_TRACKING_FINGER runtime->types.pointer.scroll_start.y = 0; #endif break; case GESTURE_DROP_UP: offset = y_res - 1 - act_point->y; #ifdef CONFIG_VIEW_SCROLL_TRACKING_FINGER runtime->types.pointer.scroll_start.y = y_res - 1; #endif break; case GESTURE_DROP_RIGHT: offset = act_point->x; #ifdef CONFIG_VIEW_SCROLL_TRACKING_FINGER runtime->types.pointer.scroll_start.x = 0; #endif break; case GESTURE_DROP_LEFT: offset = x_res - 1 - act_point->x; #ifdef CONFIG_VIEW_SCROLL_TRACKING_FINGER runtime->types.pointer.scroll_start.x = x_res - 1; #endif break; default: break; } if (offset >= GESTURE_DROP_START_THRESHOLD) return -1; runtime->last_view_id = VIEW_INVALID_ID; runtime->pre_view_id = VIEW_INVALID_ID; } runtime->current_view_id = runtime->view_id; runtime->related_view_id = runtime->last_view_id; SYS_LOG_INF("gesture begin %d, start (%d %d), view %u, last_view %u, pre_view %u\n", runtime->scroll_dir, act_point->x, act_point->y, runtime->view_id, runtime->last_view_id, runtime->pre_view_id); return 0; } static void gesture_scroll(input_dev_t * pointer_dev) { input_dev_runtime_t *runtime = &pointer_dev->proc; point_t *act_point = &runtime->types.pointer.act_point; int16_t x_res = view_manager_get_disp_xres(); int16_t y_res = view_manager_get_disp_yres(); bool is_vscroll = (runtime->scroll_dir <= GESTURE_DROP_UP); ui_point_t drag_pos = { 0, 0 }; ui_region_t rel_region = { 0, 0, x_res - 1, y_res - 1 }; #if USE_GESTURE_MOVE_SNAP_ANIM == 0 if (view_has_move_attribute(runtime->current_view_id)) #else if (runtime->current_view_id == VIEW_INVALID_ID || view_has_move_attribute(runtime->current_view_id)) #endif { if (runtime->view_id == runtime->related_view_id) { /* long view */ view_get_region(runtime->view_id, &rel_region); if (is_vscroll) { ui_region_set_y(&rel_region, rel_region.y1 + runtime->types.pointer.vect.y); } else { ui_region_set_x(&rel_region, rel_region.x1 + runtime->types.pointer.vect.x); } #if USE_GESTURE_LONGVIEW_SNAP_ANIM == 0 _reposition_region_in_display(&rel_region); #endif view_set_drag_pos(runtime->view_id, rel_region.x1, rel_region.y1); } else { int16_t drop_sign; int16_t scroll_off; if (is_vscroll) { scroll_off = act_point->y - runtime->types.pointer.scroll_start.y; drag_pos.y = (runtime->scroll_dir == GESTURE_DROP_DOWN) ? -y_res : y_res; drop_sign = (runtime->scroll_dir == GESTURE_DROP_DOWN) ? 1 : -1; } else { scroll_off = act_point->x - runtime->types.pointer.scroll_start.x; drag_pos.x = (runtime->scroll_dir == GESTURE_DROP_RIGHT) ? -x_res : x_res; drop_sign = (runtime->scroll_dir == GESTURE_DROP_RIGHT) ? 1 : -1; } /* check move direction compared to the origin */ if (scroll_off * drop_sign > 0) { if (runtime->view_id != runtime->current_view_id) { view_set_drag_pos(runtime->view_id, -drag_pos.x, -drag_pos.y); runtime->view_id = runtime->current_view_id; } } else if (scroll_off * drop_sign < 0) { drag_pos.x = -drag_pos.x; drag_pos.y = -drag_pos.y; if (runtime->view_id == runtime->current_view_id) { view_set_drag_pos(runtime->view_id, drag_pos.x, drag_pos.y); runtime->view_id = runtime->pre_view_id; } } runtime->types.pointer.last_scroll_off = scroll_off; if (runtime->view_id != VIEW_INVALID_ID) { if (is_vscroll) { drag_pos.y += runtime->types.pointer.scroll_sum.y; } else { drag_pos.x += runtime->types.pointer.scroll_sum.x; } view_set_drag_pos(runtime->view_id, drag_pos.x, drag_pos.y); } if (runtime->related_view_id != VIEW_INVALID_ID) { if (is_vscroll) { ui_region_set_y(&rel_region, runtime->types.pointer.scroll_sum.y); } else { ui_region_set_x(&rel_region, runtime->types.pointer.scroll_sum.x); } #if USE_GESTURE_MOVE_SNAP_ANIM == 0 if (runtime->view_id == VIEW_INVALID_ID) { _reposition_region_in_display(&rel_region); } #endif view_set_drag_pos(runtime->related_view_id, rel_region.x1, rel_region.y1); } SYS_LOG_DBG("scroll_off %d, view %u pos (%d,%d), related_view %u (%d,%d)\n", scroll_off, runtime->view_id, drag_pos.x, drag_pos.y, runtime->related_view_id, rel_region.x1, rel_region.y1); } } else { int16_t scroll_off; if (is_vscroll) { scroll_off = act_point->y - runtime->types.pointer.scroll_start.y; } else { scroll_off = act_point->x - runtime->types.pointer.scroll_start.x; } switch (runtime->scroll_dir) { case GESTURE_DROP_DOWN: scroll_off = UI_MAX(scroll_off, 0); drag_pos.y = runtime->types.pointer.scroll_to_screen ? (scroll_off - y_res) : scroll_off; break; case GESTURE_DROP_UP: scroll_off = UI_MIN(scroll_off, 0); drag_pos.y = runtime->types.pointer.scroll_to_screen ? (scroll_off + y_res) : scroll_off; break; case GESTURE_DROP_RIGHT: scroll_off = UI_MAX(scroll_off, 0); drag_pos.x = runtime->types.pointer.scroll_to_screen ? (scroll_off - x_res) : scroll_off; break; case GESTURE_DROP_LEFT: default: scroll_off = UI_MIN(scroll_off, 0); drag_pos.x = runtime->types.pointer.scroll_to_screen ? (scroll_off + x_res) : scroll_off; break; } runtime->types.pointer.last_scroll_off = scroll_off; view_set_drag_pos(runtime->view_id, drag_pos.x, drag_pos.y); } } static void gesture_fixed(input_dev_runtime_t *runtime) { uint16_t temp_view_id; /* must both are vertical or horizontal direction */ if (runtime->last_scroll_dir == 0 || runtime->last_scroll_dir == runtime->scroll_dir || (runtime->last_scroll_dir < GESTURE_DROP_LEFT) != (runtime->scroll_dir < GESTURE_DROP_LEFT)) { return; } runtime->scroll_dir = runtime->last_scroll_dir; temp_view_id = runtime->related_view_id; runtime->related_view_id = runtime->view_id; runtime->view_id = temp_view_id; } static void gesture_scroll_end(input_dev_t * pointer_dev) { static const uint8_t slidein_anim[] = { 0, UI_ANIM_SLIDE_IN_DOWN, UI_ANIM_SLIDE_IN_UP, UI_ANIM_SLIDE_IN_LEFT, UI_ANIM_SLIDE_IN_RIGHT, }; static const uint8_t slideout_anim[] = { 0, UI_ANIM_SLIDE_OUT_UP, UI_ANIM_SLIDE_OUT_DOWN, UI_ANIM_SLIDE_OUT_RIGHT, UI_ANIM_SLIDE_OUT_LEFT, }; input_dev_runtime_t *runtime = &pointer_dev->proc; int16_t animation_type = -1; int16_t scroll_off = UI_ABS(runtime->types.pointer.last_scroll_off); bool is_longview = false; #if USE_GESTURE_MOVE_SNAP_ANIM == 0 if (view_has_move_attribute(runtime->current_view_id)) #else if (runtime->current_view_id == VIEW_INVALID_ID || view_has_move_attribute(runtime->current_view_id)) #endif { if (runtime->view_id == runtime->related_view_id) { /* long view */ is_longview = true; } else if (runtime->view_id == runtime->current_view_id && #if USE_GESTURE_MOVE_SNAP_ANIM == 0 1 #else runtime->current_view_id != VIEW_INVALID_ID #endif ) { if (scroll_off >= GESTURE_MOVE_THRESHOLD) { gesture_fixed(runtime); animation_type = slidein_anim[runtime->scroll_dir]; } else { animation_type = slideout_anim[runtime->scroll_dir]; } } else { if (runtime->view_id != VIEW_INVALID_ID) { if (scroll_off >= GESTURE_MOVE_THRESHOLD) { gesture_fixed(runtime); animation_type = slidein_anim[opposite_gestures[runtime->scroll_dir]]; } else { animation_type = slideout_anim[opposite_gestures[runtime->scroll_dir]]; } } else { runtime->view_id = runtime->related_view_id; runtime->related_view_id = VIEW_INVALID_ID; animation_type = slidein_anim[opposite_gestures[runtime->scroll_dir]]; } } } else { if (runtime->types.pointer.scroll_to_screen) { if (scroll_off >= GESTURE_DROP_THRESHOLD) { animation_type = slidein_anim[runtime->scroll_dir]; } else { animation_type = slideout_anim[runtime->scroll_dir]; } } else { if (scroll_off >= GESTURE_DROP_THRESHOLD) { animation_type = slideout_anim[opposite_gestures[runtime->scroll_dir]]; } else { animation_type = slidein_anim[opposite_gestures[runtime->scroll_dir]]; } } } SYS_LOG_INF("gesture end %d, view %u, related_view %u, scroll_off %d, point (%d %d), anim %d\n", runtime->scroll_dir, runtime->view_id, runtime->related_view_id, runtime->types.pointer.last_scroll_off, runtime->types.pointer.last_point.x, runtime->types.pointer.last_point.y, animation_type); if (runtime->view_id != VIEW_INVALID_ID) { ui_view_anim_cfg_t cfg; memset(&cfg, 0, sizeof(cfg)); if (is_longview) { view_manager_get_drag_animation_config(runtime->view_id, &cfg, runtime); } else { view_manager_get_slide_animation_config(runtime->view_id, &cfg, animation_type); } if (cfg.duration == 0) { uint16_t step_x = UI_ABS(cfg.stop.x - cfg.start.x); uint16_t step_y = UI_ABS(cfg.stop.y - cfg.start.y); cfg.duration = (UI_MAX(step_x, step_y) * 16 + GESTURE_ANIM_SPEED - 1) / GESTURE_ANIM_SPEED; } if (cfg.path_cb == NULL) { /* default is linear */ #if USE_GESTURE_ANIM_PATH_COS cfg.path_cb = _animation_path_cos; #elif USE_GESTURE_ANIM_PATH_BEZIER cfg.path_cb = _animation_path_bezier; #endif } if (is_longview) { view_manager_drag_animation_start(runtime->view_id, &cfg); } else { view_manager_slide_animation_start(runtime->view_id, runtime->related_view_id, animation_type, &cfg); } } } #if USE_GESTURE_ANIM_PATH_COS static int32_t _animation_path_cos(int32_t elaps) { uint32_t angle = ui_map(elaps, 0, UI_VIEW_ANIM_RANGE, 900, 1800); int32_t cos_value = -sw_cos30(angle) / UI_VIEW_ANIM_RANGE; return ui_map(cos_value, 0, -sw_cos30(1800) / UI_VIEW_ANIM_RANGE, 0, UI_VIEW_ANIM_RANGE); } #elif USE_GESTURE_ANIM_PATH_BEZIER static int32_t _animation_path_bezier(int32_t elaps) { return ui_bezier3(elaps, 0, 900, 950, UI_VIEW_ANIM_RANGE); } #endif /* USE_GESTURE_ANIM_PATH_COS */ static const ui_gesture_callback_t gesture_callback = { .scroll_begin = gesture_scroll_begin, .scroll = gesture_scroll, .scroll_end = gesture_scroll_end, }; int ui_service_register_gesture_default_callback(void) { return ui_service_register_gesture_callback(&gesture_callback); }