LVGL 库提交
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,452 @@
|
||||
/**
|
||||
* @file lv_indev.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_INDEV_H
|
||||
#define LV_INDEV_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "../core/lv_group.h"
|
||||
#include "../misc/lv_area.h"
|
||||
#include "../misc/lv_timer.h"
|
||||
#include "../misc/lv_event.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/** Possible input device types*/
|
||||
typedef enum {
|
||||
LV_INDEV_TYPE_NONE, /**< Uninitialized state*/
|
||||
LV_INDEV_TYPE_POINTER, /**< Touch pad, mouse, external button*/
|
||||
LV_INDEV_TYPE_KEYPAD, /**< Keypad or keyboard*/
|
||||
LV_INDEV_TYPE_BUTTON, /**< External (hardware button) which is assigned to a specific point of the screen*/
|
||||
LV_INDEV_TYPE_ENCODER, /**< Encoder with only Left, Right turn and a Button*/
|
||||
} lv_indev_type_t;
|
||||
|
||||
/** States for input devices*/
|
||||
typedef enum {
|
||||
LV_INDEV_STATE_RELEASED = 0,
|
||||
LV_INDEV_STATE_PRESSED
|
||||
} lv_indev_state_t;
|
||||
|
||||
typedef enum {
|
||||
LV_INDEV_MODE_NONE = 0,
|
||||
LV_INDEV_MODE_TIMER,
|
||||
LV_INDEV_MODE_EVENT,
|
||||
} lv_indev_mode_t;
|
||||
|
||||
|
||||
/* Supported types of gestures */
|
||||
typedef enum {
|
||||
LV_INDEV_GESTURE_NONE = 0,
|
||||
LV_INDEV_GESTURE_PINCH,
|
||||
LV_INDEV_GESTURE_SWIPE,
|
||||
LV_INDEV_GESTURE_ROTATE,
|
||||
LV_INDEV_GESTURE_TWO_FINGERS_SWIPE,
|
||||
LV_INDEV_GESTURE_SCROLL, /* Used with scrollwheels */
|
||||
LV_INDEV_GESTURE_CNT, /* Total number of gestures types */
|
||||
} lv_indev_gesture_type_t;
|
||||
|
||||
/** Data structure passed to an input driver to fill*/
|
||||
typedef struct {
|
||||
lv_point_t point; /**< For LV_INDEV_TYPE_POINTER the currently pressed point*/
|
||||
uint32_t key; /**< For LV_INDEV_TYPE_KEYPAD the currently pressed key*/
|
||||
uint32_t btn_id; /**< For LV_INDEV_TYPE_BUTTON the currently pressed button*/
|
||||
int16_t enc_diff; /**< For LV_INDEV_TYPE_ENCODER number of steps since the previous read*/
|
||||
|
||||
lv_indev_state_t state; /**< LV_INDEV_STATE_RELEASED or LV_INDEV_STATE_PRESSED*/
|
||||
bool continue_reading; /**< If set to true, the read callback is invoked again, unless the device is in event-driven mode*/
|
||||
|
||||
lv_indev_gesture_type_t gesture_type[LV_INDEV_GESTURE_CNT]; /* Current gesture types, per gesture */
|
||||
void * gesture_data[LV_INDEV_GESTURE_CNT]; /* Used to store data per gesture */
|
||||
|
||||
} lv_indev_data_t;
|
||||
|
||||
typedef void (*lv_indev_read_cb_t)(lv_indev_t * indev, lv_indev_data_t * data);
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* Create an indev
|
||||
* @return Pointer to the created indev or NULL when allocation failed
|
||||
*/
|
||||
lv_indev_t * lv_indev_create(void);
|
||||
|
||||
/**
|
||||
* Remove the provided input device. Make sure not to use the provided input device afterwards anymore.
|
||||
* @param indev pointer to delete
|
||||
*/
|
||||
void lv_indev_delete(lv_indev_t * indev);
|
||||
|
||||
/**
|
||||
* Get the next input device.
|
||||
* @param indev pointer to the current input device. NULL to initialize.
|
||||
* @return the next input device or NULL if there are no more. Provide the first input device when
|
||||
* the parameter is NULL
|
||||
*/
|
||||
lv_indev_t * lv_indev_get_next(lv_indev_t * indev);
|
||||
|
||||
/**
|
||||
* Read data from an input device.
|
||||
* @param indev pointer to an input device
|
||||
*/
|
||||
void lv_indev_read(lv_indev_t * indev);
|
||||
|
||||
/**
|
||||
* Called periodically to read the input devices
|
||||
* @param timer pointer to a timer to read
|
||||
*/
|
||||
void lv_indev_read_timer_cb(lv_timer_t * timer);
|
||||
|
||||
/**
|
||||
* Enable or disable one or all input devices (default enabled)
|
||||
* @param indev pointer to an input device or NULL to enable/disable all of them
|
||||
* @param enable true to enable, false to disable
|
||||
*/
|
||||
void lv_indev_enable(lv_indev_t * indev, bool enable);
|
||||
|
||||
/**
|
||||
* Get the currently processed input device. Can be used in action functions too.
|
||||
* @return pointer to the currently processed input device or NULL if no input device processing
|
||||
* right now
|
||||
*/
|
||||
lv_indev_t * lv_indev_active(void);
|
||||
|
||||
/**
|
||||
* Set the type of an input device
|
||||
* @param indev pointer to an input device
|
||||
* @param indev_type the type of the input device from `lv_indev_type_t` (`LV_INDEV_TYPE_...`)
|
||||
*/
|
||||
void lv_indev_set_type(lv_indev_t * indev, lv_indev_type_t indev_type);
|
||||
|
||||
/**
|
||||
* Set a callback function to read input device data to the indev
|
||||
* @param indev pointer to an input device
|
||||
* @param read_cb pointer to callback function to read input device data
|
||||
*/
|
||||
void lv_indev_set_read_cb(lv_indev_t * indev, lv_indev_read_cb_t read_cb);
|
||||
|
||||
/**
|
||||
* Set user data to the indev
|
||||
* @param indev pointer to an input device
|
||||
* @param user_data pointer to user data
|
||||
*/
|
||||
void lv_indev_set_user_data(lv_indev_t * indev, void * user_data);
|
||||
|
||||
/**
|
||||
* Set driver data to the indev
|
||||
* @param indev pointer to an input device
|
||||
* @param driver_data pointer to driver data
|
||||
*/
|
||||
void lv_indev_set_driver_data(lv_indev_t * indev, void * driver_data);
|
||||
|
||||
/**
|
||||
* Assign a display to the indev
|
||||
* @param indev pointer to an input device
|
||||
* @param disp pointer to an display
|
||||
*/
|
||||
void lv_indev_set_display(lv_indev_t * indev, struct _lv_display_t * disp);
|
||||
|
||||
/**
|
||||
* Set long press time to indev
|
||||
* @param indev pointer to input device
|
||||
* @param long_press_time time long press time in ms
|
||||
*/
|
||||
void lv_indev_set_long_press_time(lv_indev_t * indev, uint16_t long_press_time);
|
||||
|
||||
/**
|
||||
* Set long press repeat time to indev
|
||||
* @param indev pointer to input device
|
||||
* @param long_press_repeat_time long press repeat time in ms
|
||||
*/
|
||||
void lv_indev_set_long_press_repeat_time(lv_indev_t * indev, uint16_t long_press_repeat_time);
|
||||
|
||||
/**
|
||||
* Set scroll limit to the input device
|
||||
* @param indev pointer to an input device
|
||||
* @param scroll_limit the number of pixels to slide before actually drag the object
|
||||
*/
|
||||
void lv_indev_set_scroll_limit(lv_indev_t * indev, uint8_t scroll_limit);
|
||||
|
||||
/**
|
||||
* Set scroll throw slow-down to the indev. Greater value means faster slow-down
|
||||
* @param indev pointer to an input device
|
||||
* @param scroll_throw the slow-down in [%]
|
||||
*/
|
||||
void lv_indev_set_scroll_throw(lv_indev_t * indev, uint8_t scroll_throw);
|
||||
|
||||
/**
|
||||
* Get the type of an input device
|
||||
* @param indev pointer to an input device
|
||||
* @return the type of the input device from `lv_hal_indev_type_t` (`LV_INDEV_TYPE_...`)
|
||||
*/
|
||||
lv_indev_type_t lv_indev_get_type(const lv_indev_t * indev);
|
||||
|
||||
/**
|
||||
* Get the callback function to read input device data to the indev
|
||||
* @param indev pointer to an input device
|
||||
* @return Pointer to callback function to read input device data or NULL if indev is NULL
|
||||
*/
|
||||
lv_indev_read_cb_t lv_indev_get_read_cb(lv_indev_t * indev);
|
||||
|
||||
/**
|
||||
* Get the indev state
|
||||
* @param indev pointer to an input device
|
||||
* @return Indev state or LV_INDEV_STATE_RELEASED if indev is NULL
|
||||
*/
|
||||
lv_indev_state_t lv_indev_get_state(const lv_indev_t * indev);
|
||||
|
||||
/**
|
||||
* Get the indev assigned group
|
||||
* @param indev pointer to an input device
|
||||
* @return Pointer to indev assigned group or NULL if indev is NULL
|
||||
*/
|
||||
lv_group_t * lv_indev_get_group(const lv_indev_t * indev);
|
||||
|
||||
/**
|
||||
* Get a pointer to the assigned display of the indev
|
||||
* @param indev pointer to an input device
|
||||
* @return pointer to the assigned display or NULL if indev is NULL
|
||||
*/
|
||||
lv_display_t * lv_indev_get_display(const lv_indev_t * indev);
|
||||
|
||||
/**
|
||||
* Get a pointer to the user data of the indev
|
||||
* @param indev pointer to an input device
|
||||
* @return pointer to the user data or NULL if indev is NULL
|
||||
*/
|
||||
void * lv_indev_get_user_data(const lv_indev_t * indev);
|
||||
|
||||
/**
|
||||
* Get a pointer to the driver data of the indev
|
||||
* @param indev pointer to an input device
|
||||
* @return pointer to the driver data or NULL if indev is NULL
|
||||
*/
|
||||
void * lv_indev_get_driver_data(const lv_indev_t * indev);
|
||||
|
||||
/**
|
||||
* Get whether indev is moved while pressed
|
||||
* @param indev pointer to an input device
|
||||
* @return true: indev is moved while pressed; false: indev is not moved while pressed
|
||||
*/
|
||||
bool lv_indev_get_press_moved(const lv_indev_t * indev);
|
||||
|
||||
/**
|
||||
* Reset one or all input devices
|
||||
* @param indev pointer to an input device to reset or NULL to reset all of them
|
||||
* @param obj pointer to an object which triggers the reset.
|
||||
*/
|
||||
void lv_indev_reset(lv_indev_t * indev, lv_obj_t * obj);
|
||||
|
||||
/**
|
||||
* Touch and key related events are sent to the input device first and to the widget after that.
|
||||
* If this functions called in an indev event, the event won't be sent to the widget.
|
||||
* @param indev pointer to an input device
|
||||
*/
|
||||
void lv_indev_stop_processing(lv_indev_t * indev);
|
||||
|
||||
/**
|
||||
* Reset the long press state of an input device
|
||||
* @param indev pointer to an input device
|
||||
*/
|
||||
void lv_indev_reset_long_press(lv_indev_t * indev);
|
||||
|
||||
/**
|
||||
* Set a cursor for a pointer input device (for LV_INPUT_TYPE_POINTER and LV_INPUT_TYPE_BUTTON)
|
||||
* @param indev pointer to an input device
|
||||
* @param cur_obj pointer to an object to be used as cursor
|
||||
*/
|
||||
void lv_indev_set_cursor(lv_indev_t * indev, lv_obj_t * cur_obj);
|
||||
|
||||
/**
|
||||
* Set a destination group for a keypad input device (for LV_INDEV_TYPE_KEYPAD)
|
||||
* @param indev pointer to an input device
|
||||
* @param group pointer to a group
|
||||
*/
|
||||
void lv_indev_set_group(lv_indev_t * indev, lv_group_t * group);
|
||||
|
||||
/**
|
||||
* Set the an array of points for LV_INDEV_TYPE_BUTTON.
|
||||
* These points will be assigned to the buttons to press a specific point on the screen
|
||||
* @param indev pointer to an input device
|
||||
* @param points array of points
|
||||
*/
|
||||
void lv_indev_set_button_points(lv_indev_t * indev, const lv_point_t points[]);
|
||||
|
||||
/**
|
||||
* Get the last point of an input device (for LV_INDEV_TYPE_POINTER and LV_INDEV_TYPE_BUTTON)
|
||||
* @param indev pointer to an input device
|
||||
* @param point pointer to a point to store the result
|
||||
*/
|
||||
void lv_indev_get_point(const lv_indev_t * indev, lv_point_t * point);
|
||||
|
||||
/**
|
||||
* Get the current gesture direct
|
||||
* @param indev pointer to an input device
|
||||
* @return current gesture direct
|
||||
*/
|
||||
lv_dir_t lv_indev_get_gesture_dir(const lv_indev_t * indev);
|
||||
|
||||
/**
|
||||
* Get the last pressed key of an input device (for LV_INDEV_TYPE_KEYPAD)
|
||||
* @param indev pointer to an input device
|
||||
* @return the last pressed key (0 on error)
|
||||
*/
|
||||
uint32_t lv_indev_get_key(const lv_indev_t * indev);
|
||||
|
||||
|
||||
/**
|
||||
* Get the counter for consecutive clicks within a short distance and time.
|
||||
* The counter is updated before LV_EVENT_SHORT_CLICKED is fired.
|
||||
* @param indev pointer to an input device
|
||||
* @return short click streak counter
|
||||
*/
|
||||
uint8_t lv_indev_get_short_click_streak(const lv_indev_t * indev);
|
||||
|
||||
/**
|
||||
* Check the current scroll direction of an input device (for LV_INDEV_TYPE_POINTER and
|
||||
* LV_INDEV_TYPE_BUTTON)
|
||||
* @param indev pointer to an input device
|
||||
* @return LV_DIR_NONE: no scrolling now
|
||||
* LV_DIR_HOR/VER
|
||||
*/
|
||||
lv_dir_t lv_indev_get_scroll_dir(const lv_indev_t * indev);
|
||||
|
||||
/**
|
||||
* Get the currently scrolled object (for LV_INDEV_TYPE_POINTER and
|
||||
* LV_INDEV_TYPE_BUTTON)
|
||||
* @param indev pointer to an input device
|
||||
* @return pointer to the currently scrolled object or NULL if no scrolling by this indev
|
||||
*/
|
||||
lv_obj_t * lv_indev_get_scroll_obj(const lv_indev_t * indev);
|
||||
|
||||
/**
|
||||
* Get the movement vector of an input device (for LV_INDEV_TYPE_POINTER and
|
||||
* LV_INDEV_TYPE_BUTTON)
|
||||
* @param indev pointer to an input device
|
||||
* @param point pointer to a point to store the types.pointer.vector
|
||||
*/
|
||||
void lv_indev_get_vect(const lv_indev_t * indev, lv_point_t * point);
|
||||
|
||||
/**
|
||||
* Get the cursor object of an input device (for LV_INDEV_TYPE_POINTER only)
|
||||
* @param indev pointer to an input device
|
||||
* @return pointer to the cursor object
|
||||
*/
|
||||
lv_obj_t * lv_indev_get_cursor(lv_indev_t * indev);
|
||||
|
||||
/**
|
||||
* Do nothing until the next release
|
||||
* @param indev pointer to an input device
|
||||
*/
|
||||
void lv_indev_wait_release(lv_indev_t * indev);
|
||||
|
||||
/**
|
||||
* Gets a pointer to the currently active object in the currently processed input device.
|
||||
* @return pointer to currently active object or NULL if no active object
|
||||
*/
|
||||
lv_obj_t * lv_indev_get_active_obj(void);
|
||||
|
||||
/**
|
||||
* Get a pointer to the indev read timer to
|
||||
* modify its parameters with `lv_timer_...` functions.
|
||||
* @param indev pointer to an input device
|
||||
* @return pointer to the indev read refresher timer. (NULL on error)
|
||||
*/
|
||||
lv_timer_t * lv_indev_get_read_timer(lv_indev_t * indev);
|
||||
|
||||
/**
|
||||
* Set the input device's event model: event-driven mode or timer mode.
|
||||
* @param indev pointer to an input device
|
||||
* @param mode the mode of input device
|
||||
*/
|
||||
void lv_indev_set_mode(lv_indev_t * indev, lv_indev_mode_t mode);
|
||||
|
||||
/**
|
||||
* Get the input device's running mode.
|
||||
* @param indev pointer to an input device
|
||||
* @return the running mode for the specified input device.
|
||||
*/
|
||||
lv_indev_mode_t lv_indev_get_mode(lv_indev_t * indev);
|
||||
|
||||
/**
|
||||
* Search the most top, clickable object by a point
|
||||
* @param obj pointer to a start object, typically the screen
|
||||
* @param point pointer to a point for searching the most top child
|
||||
* @return pointer to the found object or NULL if there was no suitable object
|
||||
*/
|
||||
lv_obj_t * lv_indev_search_obj(lv_obj_t * obj, lv_point_t * point);
|
||||
|
||||
/**
|
||||
* Add an event handler to the indev
|
||||
* @param indev pointer to an indev
|
||||
* @param event_cb an event callback
|
||||
* @param filter event code to react or `LV_EVENT_ALL`
|
||||
* @param user_data optional user_data
|
||||
*/
|
||||
void lv_indev_add_event_cb(lv_indev_t * indev, lv_event_cb_t event_cb, lv_event_code_t filter, void * user_data);
|
||||
|
||||
/**
|
||||
* Get the number of event attached to an indev
|
||||
* @param indev pointer to an indev
|
||||
* @return number of events
|
||||
*/
|
||||
uint32_t lv_indev_get_event_count(lv_indev_t * indev);
|
||||
|
||||
/**
|
||||
* Get an event descriptor for an event
|
||||
* @param indev pointer to an indev
|
||||
* @param index the index of the event
|
||||
* @return the event descriptor
|
||||
*/
|
||||
lv_event_dsc_t * lv_indev_get_event_dsc(lv_indev_t * indev, uint32_t index);
|
||||
|
||||
/**
|
||||
* Remove an event
|
||||
* @param indev pointer to an indev
|
||||
* @param index the index of the event to remove
|
||||
* @return true: and event was removed; false: no event was removed
|
||||
*/
|
||||
bool lv_indev_remove_event(lv_indev_t * indev, uint32_t index);
|
||||
|
||||
/**
|
||||
* Remove an event_cb with user_data
|
||||
* @param indev pointer to a indev
|
||||
* @param event_cb the event_cb of the event to remove
|
||||
* @param user_data user_data
|
||||
* @return the count of the event removed
|
||||
*/
|
||||
uint32_t lv_indev_remove_event_cb_with_user_data(lv_indev_t * indev, lv_event_cb_t event_cb, void * user_data);
|
||||
|
||||
/**
|
||||
* Send an event to an indev
|
||||
* @param indev pointer to an indev
|
||||
* @param code an event code. LV_EVENT_...
|
||||
* @param param optional param
|
||||
* @return LV_RESULT_OK: indev wasn't deleted in the event.
|
||||
*/
|
||||
lv_result_t lv_indev_send_event(lv_indev_t * indev, lv_event_code_t code, void * param);
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_INDEV_H*/
|
||||
@@ -0,0 +1,964 @@
|
||||
/******************************************************************
|
||||
* @file lv_indev_gesture.c
|
||||
*
|
||||
* Recognize gestures that consist of multiple touch events
|
||||
*
|
||||
* Copyright (c) 2024 EDGEMTech Ltd
|
||||
*
|
||||
* Author EDGEMTech Ltd. (erik.tagirov@edgemtech.ch)
|
||||
*
|
||||
******************************************************************/
|
||||
|
||||
/********************
|
||||
* INCLUDES
|
||||
********************/
|
||||
|
||||
#include "lv_indev_private.h"
|
||||
#include "../misc/lv_event_private.h"
|
||||
|
||||
#if LV_USE_GESTURE_RECOGNITION
|
||||
|
||||
#include <math.h>
|
||||
#include "lv_indev_gesture.h"
|
||||
#include "lv_indev_gesture_private.h"
|
||||
|
||||
/********************
|
||||
* DEFINES
|
||||
********************/
|
||||
|
||||
#define LV_GESTURE_PINCH_DOWN_THRESHOLD 0.75f /* Default value - start sending events when reached */
|
||||
#define LV_GESTURE_PINCH_UP_THRESHOLD 1.5f /* Default value - start sending events when reached */
|
||||
#define LV_GESTURE_PINCH_MAX_INITIAL_SCALE 2.5f /* Default value */
|
||||
#define LV_GESTURE_ROTATION_ANGLE_RAD_THRESHOLD 0.2f /* Default value - start sending events when reached */
|
||||
|
||||
|
||||
/********************
|
||||
* TYPEDEFS
|
||||
********************/
|
||||
|
||||
/********************
|
||||
* STATIC PROTOTYPES
|
||||
********************/
|
||||
|
||||
static lv_indev_gesture_t * init_gesture_info(void);
|
||||
static lv_indev_gesture_motion_t * get_motion(uint8_t id, lv_indev_gesture_t * info);
|
||||
static int8_t get_motion_idx(uint8_t id, lv_indev_gesture_t * info);
|
||||
static void process_touch_event(lv_indev_touch_data_t * touch, lv_indev_gesture_t * info);
|
||||
static void gesture_update_center_point(lv_indev_gesture_t * gesture, int touch_points_nb);
|
||||
static void gesture_calculate_factors(lv_indev_gesture_t * gesture, int touch_points_nb);
|
||||
static void reset_recognizer(lv_indev_gesture_recognizer_t * recognizer);
|
||||
static lv_indev_gesture_recognizer_t * lv_indev_get_gesture_recognizer(lv_event_t * gesture_event,
|
||||
lv_indev_gesture_type_t type);
|
||||
static lv_dir_t calculate_swipe_dir(lv_indev_gesture_recognizer_t * recognizer);
|
||||
static lv_indev_gesture_type_t get_first_recognized_or_ended_gesture(lv_indev_t * indev);
|
||||
|
||||
/********************
|
||||
* STATIC VARIABLES
|
||||
********************/
|
||||
|
||||
/********************
|
||||
* MACROS
|
||||
********************/
|
||||
#define SQUARE(x) ((x) * (x))
|
||||
#define SQUARE_SUM(x, y) (SQUARE(x) + SQUARE(y))
|
||||
|
||||
/********************
|
||||
* GLOBAL FUNCTIONS
|
||||
********************/
|
||||
|
||||
void lv_indev_set_pinch_up_threshold(lv_indev_t * indev, float threshold)
|
||||
{
|
||||
/* A up threshold MUST always be bigger than 1 */
|
||||
LV_ASSERT(threshold > 1.0f);
|
||||
|
||||
lv_indev_gesture_recognizer_t * recognizer = &indev->recognizers[LV_INDEV_GESTURE_PINCH];
|
||||
|
||||
if(recognizer->config == NULL) {
|
||||
recognizer->config = lv_malloc_zeroed(sizeof(lv_indev_gesture_configuration_t));
|
||||
LV_ASSERT_MALLOC(recognizer->config);
|
||||
recognizer->config->pinch_down_threshold = LV_GESTURE_PINCH_DOWN_THRESHOLD;
|
||||
}
|
||||
|
||||
recognizer->config->pinch_up_threshold = threshold;
|
||||
}
|
||||
|
||||
void lv_indev_set_pinch_down_threshold(lv_indev_t * indev, float threshold)
|
||||
{
|
||||
/* A down threshold MUST always be smaller than 1 */
|
||||
LV_ASSERT(threshold < 1.0f);
|
||||
|
||||
lv_indev_gesture_recognizer_t * recognizer = &indev->recognizers[LV_INDEV_GESTURE_PINCH];
|
||||
|
||||
if(recognizer->config == NULL) {
|
||||
recognizer->config = lv_malloc_zeroed(sizeof(lv_indev_gesture_configuration_t));
|
||||
LV_ASSERT_MALLOC(recognizer->config);
|
||||
recognizer->config->pinch_up_threshold = LV_GESTURE_PINCH_UP_THRESHOLD;
|
||||
}
|
||||
|
||||
recognizer->config->pinch_down_threshold = threshold;
|
||||
}
|
||||
|
||||
void lv_indev_set_rotation_rad_threshold(lv_indev_t * indev, float threshold)
|
||||
{
|
||||
/* A rotation threshold MUST always be a positive number */
|
||||
LV_ASSERT(threshold > 0.0f);
|
||||
|
||||
lv_indev_gesture_recognizer_t * recognizer = &indev->recognizers[LV_INDEV_GESTURE_ROTATE];
|
||||
|
||||
if(recognizer->config == NULL) {
|
||||
|
||||
recognizer->config = lv_malloc_zeroed(sizeof(lv_indev_gesture_configuration_t));
|
||||
LV_ASSERT(recognizer->config != NULL);
|
||||
recognizer->config->rotation_angle_rad_threshold = LV_GESTURE_ROTATION_ANGLE_RAD_THRESHOLD;
|
||||
}
|
||||
|
||||
recognizer->config->rotation_angle_rad_threshold = threshold;
|
||||
}
|
||||
|
||||
void lv_indev_get_gesture_primary_point(lv_indev_gesture_recognizer_t * recognizer, lv_point_t * point)
|
||||
{
|
||||
if(recognizer->info->motions[0].finger != -1) {
|
||||
point->x = recognizer->info->motions[0].point.x;
|
||||
point->y = recognizer->info->motions[0].point.y;
|
||||
return;
|
||||
}
|
||||
|
||||
/* There are currently no active contact points */
|
||||
point->x = 0;
|
||||
point->y = 0;
|
||||
}
|
||||
|
||||
bool lv_indev_recognizer_is_active(lv_indev_gesture_recognizer_t * recognizer)
|
||||
{
|
||||
if(recognizer->state == LV_INDEV_GESTURE_STATE_ENDED ||
|
||||
recognizer->info->finger_cnt == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
float lv_event_get_pinch_scale(lv_event_t * gesture_event)
|
||||
{
|
||||
lv_indev_gesture_recognizer_t * recognizer;
|
||||
|
||||
if((recognizer = lv_indev_get_gesture_recognizer(gesture_event, LV_INDEV_GESTURE_PINCH)) == NULL) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return recognizer->scale;
|
||||
}
|
||||
|
||||
float lv_event_get_rotation(lv_event_t * gesture_event)
|
||||
{
|
||||
lv_indev_gesture_recognizer_t * recognizer;
|
||||
|
||||
if((recognizer = lv_indev_get_gesture_recognizer(gesture_event, LV_INDEV_GESTURE_ROTATE)) == NULL) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return recognizer->rotation;
|
||||
}
|
||||
|
||||
float lv_event_get_two_fingers_swipe_distance(lv_event_t * gesture_event)
|
||||
{
|
||||
lv_indev_gesture_recognizer_t * recognizer;
|
||||
|
||||
if((recognizer = lv_indev_get_gesture_recognizer(gesture_event, LV_INDEV_GESTURE_TWO_FINGERS_SWIPE)) == NULL) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return recognizer->distance;
|
||||
}
|
||||
|
||||
lv_dir_t lv_event_get_two_fingers_swipe_dir(lv_event_t * gesture_event)
|
||||
{
|
||||
|
||||
lv_indev_gesture_recognizer_t * recognizer;
|
||||
|
||||
if((recognizer = lv_indev_get_gesture_recognizer(gesture_event, LV_INDEV_GESTURE_TWO_FINGERS_SWIPE)) == NULL) {
|
||||
return LV_DIR_NONE;
|
||||
}
|
||||
|
||||
return recognizer->two_fingers_swipe_dir;
|
||||
}
|
||||
|
||||
void lv_indev_get_gesture_center_point(lv_indev_gesture_recognizer_t * recognizer, lv_point_t * point)
|
||||
{
|
||||
if(lv_indev_recognizer_is_active(recognizer) == false) {
|
||||
point->x = 0;
|
||||
point->y = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
point->x = recognizer->info->center.x;
|
||||
point->y = recognizer->info->center.y;
|
||||
}
|
||||
|
||||
lv_indev_gesture_state_t lv_event_get_gesture_state(lv_event_t * gesture_event, lv_indev_gesture_type_t type)
|
||||
{
|
||||
lv_indev_gesture_recognizer_t * recognizer;
|
||||
|
||||
if((recognizer = lv_indev_get_gesture_recognizer(gesture_event, type)) == NULL) {
|
||||
return LV_INDEV_GESTURE_STATE_NONE;
|
||||
}
|
||||
|
||||
return recognizer->state;
|
||||
}
|
||||
|
||||
lv_indev_gesture_type_t lv_event_get_gesture_type(lv_event_t * gesture_event)
|
||||
{
|
||||
lv_indev_t * indev = (lv_indev_t *) gesture_event->param;
|
||||
|
||||
if(indev == NULL) {
|
||||
return LV_INDEV_GESTURE_NONE;
|
||||
}
|
||||
|
||||
return indev->cur_gesture;
|
||||
}
|
||||
|
||||
void lv_indev_set_gesture_data(lv_indev_data_t * data, lv_indev_gesture_recognizer_t * recognizer,
|
||||
lv_indev_gesture_type_t type)
|
||||
{
|
||||
bool is_active;
|
||||
lv_point_t cur_pnt;
|
||||
|
||||
if(recognizer == NULL) return;
|
||||
|
||||
/* If there is a single contact point use its coords,
|
||||
* when there are no contact points it's set to 0,0
|
||||
*
|
||||
* Note: If a gesture was detected, the primary point is overwritten below
|
||||
*/
|
||||
|
||||
lv_indev_get_gesture_primary_point(recognizer, &cur_pnt);
|
||||
data->point.x = cur_pnt.x;
|
||||
data->point.y = cur_pnt.y;
|
||||
|
||||
data->gesture_type[type] = LV_INDEV_GESTURE_NONE;
|
||||
data->gesture_data[type] = NULL;
|
||||
|
||||
/* The call below returns false if there are no active contact points */
|
||||
/* - OR when the gesture has ended, false is considered as a RELEASED state */
|
||||
is_active = lv_indev_recognizer_is_active(recognizer);
|
||||
|
||||
if(is_active == false) {
|
||||
data->state = LV_INDEV_STATE_RELEASED;
|
||||
}
|
||||
else {
|
||||
data->state = LV_INDEV_STATE_PRESSED;
|
||||
}
|
||||
|
||||
switch(recognizer->state) {
|
||||
case LV_INDEV_GESTURE_STATE_RECOGNIZED:
|
||||
lv_indev_get_gesture_center_point(recognizer, &cur_pnt);
|
||||
data->point.x = cur_pnt.x;
|
||||
data->point.y = cur_pnt.y;
|
||||
data->gesture_type[type] = type;
|
||||
data->gesture_data[type] = (void *) recognizer;
|
||||
break;
|
||||
|
||||
case LV_INDEV_GESTURE_STATE_ENDED:
|
||||
data->gesture_type[type] = type;
|
||||
data->gesture_data[type] = (void *) recognizer;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void lv_indev_gesture_detect_pinch(lv_indev_gesture_recognizer_t * recognizer, lv_indev_touch_data_t * touches,
|
||||
uint16_t touch_cnt)
|
||||
{
|
||||
lv_indev_touch_data_t * touch;
|
||||
lv_indev_gesture_recognizer_t * r = recognizer;
|
||||
uint8_t i;
|
||||
|
||||
if(r->info == NULL) {
|
||||
LV_LOG_TRACE("init gesture info");
|
||||
r->info = init_gesture_info();
|
||||
}
|
||||
|
||||
if(r->config == NULL) {
|
||||
LV_LOG_TRACE("init gesture configuration - set defaults");
|
||||
r->config = lv_malloc_zeroed(sizeof(lv_indev_gesture_configuration_t));
|
||||
LV_ASSERT_MALLOC(r->config);
|
||||
|
||||
r->config->pinch_up_threshold = LV_GESTURE_PINCH_UP_THRESHOLD;
|
||||
r->config->pinch_down_threshold = LV_GESTURE_PINCH_DOWN_THRESHOLD;
|
||||
}
|
||||
|
||||
/* Process collected touch events */
|
||||
for(i = 0; i < touch_cnt; i++) {
|
||||
touch = touches;
|
||||
process_touch_event(touch, r->info);
|
||||
touches++;
|
||||
|
||||
LV_LOG_TRACE("processed touch ev: %d finger id: %d state: %d x: %" LV_PRId32 " y: %" LV_PRId32 " finger_cnt: %d",
|
||||
i, touch->id, touch->state, touch->point.x, touch->point.y, r->info->finger_cnt);
|
||||
}
|
||||
|
||||
LV_LOG_TRACE("Current finger count: %d state: %d", r->info->finger_cnt, r->state);
|
||||
|
||||
if(r->info->finger_cnt == 2) {
|
||||
switch(r->state) {
|
||||
case LV_INDEV_GESTURE_STATE_NONE:
|
||||
|
||||
/* 2 fingers down - potential pinch or swipe */
|
||||
reset_recognizer(recognizer);
|
||||
gesture_update_center_point(r->info, 2);
|
||||
r->state = LV_INDEV_GESTURE_STATE_ONGOING;
|
||||
break;
|
||||
case LV_INDEV_GESTURE_STATE_ONGOING:
|
||||
gesture_calculate_factors(r->info, 2);
|
||||
if(r->info->scale > LV_GESTURE_PINCH_MAX_INITIAL_SCALE) {
|
||||
r->state = LV_INDEV_GESTURE_STATE_CANCELED;
|
||||
break;
|
||||
}
|
||||
if(r->info->scale > r->config->pinch_up_threshold ||
|
||||
r->info->scale < r->config->pinch_down_threshold) {
|
||||
r->state = LV_INDEV_GESTURE_STATE_RECOGNIZED;
|
||||
}
|
||||
break;
|
||||
case LV_INDEV_GESTURE_STATE_RECOGNIZED:
|
||||
/* It's an ongoing pinch gesture - update the factors */
|
||||
gesture_calculate_factors(r->info, 2);
|
||||
LV_ASSERT(r->info != NULL);
|
||||
r->scale = r->info->scale;
|
||||
r->type = LV_INDEV_GESTURE_PINCH;
|
||||
break;
|
||||
case LV_INDEV_GESTURE_STATE_ENDED:
|
||||
case LV_INDEV_GESTURE_STATE_CANCELED:
|
||||
reset_recognizer(r);
|
||||
break;
|
||||
default:
|
||||
LV_ASSERT_MSG(true, "invalid gesture recognizer state");
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch(r->state) {
|
||||
case LV_INDEV_GESTURE_STATE_RECOGNIZED:
|
||||
/* Gesture has ended */
|
||||
r->state = LV_INDEV_GESTURE_STATE_ENDED;
|
||||
break;
|
||||
case LV_INDEV_GESTURE_STATE_ONGOING:
|
||||
/* User lifted a finger before reaching threshold */
|
||||
r->state = LV_INDEV_GESTURE_STATE_CANCELED;
|
||||
break;
|
||||
case LV_INDEV_GESTURE_STATE_ENDED:
|
||||
case LV_INDEV_GESTURE_STATE_CANCELED:
|
||||
reset_recognizer(r);
|
||||
break;
|
||||
|
||||
default:
|
||||
LV_ASSERT_MSG(true, "invalid gesture recognizer state");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void lv_indev_gesture_detect_rotation(lv_indev_gesture_recognizer_t * recognizer, lv_indev_touch_data_t * touches,
|
||||
uint16_t touch_cnt)
|
||||
{
|
||||
lv_indev_touch_data_t * touch;
|
||||
lv_indev_gesture_recognizer_t * r = recognizer;
|
||||
uint8_t i;
|
||||
|
||||
if(r->info == NULL) {
|
||||
LV_LOG_TRACE("init gesture info");
|
||||
r->info = init_gesture_info();
|
||||
}
|
||||
|
||||
if(r->config == NULL) {
|
||||
LV_LOG_TRACE("init gesture configuration - set defaults");
|
||||
r->config = lv_malloc_zeroed(sizeof(lv_indev_gesture_configuration_t));
|
||||
|
||||
LV_ASSERT(r->config != NULL);
|
||||
}
|
||||
|
||||
/* Process collected touch events */
|
||||
for(i = 0; i < touch_cnt; i++) {
|
||||
|
||||
touch = touches;
|
||||
process_touch_event(touch, r->info);
|
||||
touches++;
|
||||
|
||||
LV_LOG_TRACE("processed touch ev: %d finger id: %d state: %d x: %" LV_PRId32 " y: %" LV_PRId32 " finger_cnt: %d",
|
||||
i, touch->id, touch->state, touch->point.x, touch->point.y, r->info->finger_cnt);
|
||||
}
|
||||
|
||||
LV_LOG_TRACE("Current finger count: %d state: %d", r->info->finger_cnt, r->state);
|
||||
|
||||
if(r->info->finger_cnt == 2) {
|
||||
switch(r->state) {
|
||||
case LV_INDEV_GESTURE_STATE_NONE:
|
||||
/* 2 fingers down - potential rotation or swipe */
|
||||
reset_recognizer(recognizer);
|
||||
gesture_update_center_point(r->info, 2);
|
||||
r->state = LV_INDEV_GESTURE_STATE_ONGOING;
|
||||
break;
|
||||
case LV_INDEV_GESTURE_STATE_ONGOING:
|
||||
/* Update the rotation from the inputs */
|
||||
gesture_calculate_factors(r->info, 2);
|
||||
if(fabs(r->info->rotation - r->info->p_rotation) > r->config->rotation_angle_rad_threshold) {
|
||||
|
||||
gesture_update_center_point(r->info, 2);
|
||||
r->state = LV_INDEV_GESTURE_STATE_RECOGNIZED;
|
||||
}
|
||||
break;
|
||||
case LV_INDEV_GESTURE_STATE_RECOGNIZED:
|
||||
/* It's a recognized rotation gesture - update the factors */
|
||||
gesture_calculate_factors(r->info, 2);
|
||||
r->type = LV_INDEV_GESTURE_ROTATE;
|
||||
r->rotation = r->info->rotation;
|
||||
break;
|
||||
case LV_INDEV_GESTURE_STATE_ENDED:
|
||||
case LV_INDEV_GESTURE_STATE_CANCELED:
|
||||
reset_recognizer(r);
|
||||
r->type = LV_INDEV_GESTURE_NONE;
|
||||
r->state = LV_INDEV_GESTURE_STATE_CANCELED;
|
||||
break;
|
||||
default:
|
||||
LV_ASSERT_MSG(true, "invalid gesture recognizer state");
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch(r->state) {
|
||||
case LV_INDEV_GESTURE_STATE_RECOGNIZED:
|
||||
/* Gesture has ended */
|
||||
r->type = LV_INDEV_GESTURE_ROTATE;
|
||||
r->state = LV_INDEV_GESTURE_STATE_ENDED;
|
||||
break;
|
||||
case LV_INDEV_GESTURE_STATE_ONGOING:
|
||||
/* User lifted a finger before reaching threshold */
|
||||
reset_recognizer(r);
|
||||
break;
|
||||
case LV_INDEV_GESTURE_STATE_CANCELED:
|
||||
case LV_INDEV_GESTURE_STATE_ENDED:
|
||||
reset_recognizer(r);
|
||||
break;
|
||||
default:
|
||||
LV_ASSERT_MSG(true, "invalid gesture recognizer state");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void lv_indev_gesture_detect_two_fingers_swipe(lv_indev_gesture_recognizer_t * recognizer,
|
||||
lv_indev_touch_data_t * touches,
|
||||
uint16_t touch_cnt)
|
||||
{
|
||||
lv_indev_touch_data_t * touch;
|
||||
lv_indev_gesture_recognizer_t * r = recognizer;
|
||||
uint8_t i;
|
||||
float dist;
|
||||
|
||||
if(r->info == NULL) {
|
||||
LV_LOG_TRACE("init gesture info");
|
||||
r->info = init_gesture_info();
|
||||
}
|
||||
|
||||
if(r->config == NULL) {
|
||||
LV_LOG_TRACE("init gesture configuration - set defaults");
|
||||
r->config = lv_malloc_zeroed(sizeof(lv_indev_gesture_configuration_t));
|
||||
|
||||
LV_ASSERT(r->config != NULL);
|
||||
}
|
||||
|
||||
/* Process collected touch events */
|
||||
for(i = 0; i < touch_cnt; i++) {
|
||||
|
||||
touch = touches;
|
||||
process_touch_event(touch, r->info);
|
||||
touches++;
|
||||
|
||||
LV_LOG_TRACE("processed touch ev: %d finger id: %d state: %d x: %" LV_PRId32 " y: %" LV_PRId32 " finger_cnt: %d",
|
||||
i, touch->id, touch->state, touch->point.x, touch->point.y, r->info->finger_cnt);
|
||||
}
|
||||
|
||||
LV_LOG_TRACE("Current finger count: %d state: %d", r->info->finger_cnt, r->state);
|
||||
|
||||
if(r->info->finger_cnt == 2) {
|
||||
|
||||
switch(r->state) {
|
||||
case LV_INDEV_GESTURE_STATE_NONE:
|
||||
/* 2 fingers down - potential rotation or swipe */
|
||||
reset_recognizer(recognizer);
|
||||
gesture_update_center_point(r->info, 2);
|
||||
r->state = LV_INDEV_GESTURE_STATE_ONGOING;
|
||||
break;
|
||||
case LV_INDEV_GESTURE_STATE_ONGOING:
|
||||
/* The gesture is ongoing, now wait for the distance from the center
|
||||
to be higher than the threshold to pass it as recognized */
|
||||
gesture_calculate_factors(r->info, 2);
|
||||
dist = SQUARE_SUM(r->info->delta_x, r->info->delta_y);
|
||||
if(dist > SQUARE(lv_indev_active()->gesture_limit)) {
|
||||
r->state = LV_INDEV_GESTURE_STATE_RECOGNIZED;
|
||||
}
|
||||
break;
|
||||
case LV_INDEV_GESTURE_STATE_RECOGNIZED:
|
||||
/* The gesture is now recognized, and will stay recognized
|
||||
until a finger is lifted */
|
||||
gesture_calculate_factors(r->info, 2);
|
||||
r->distance = (float) sqrt(SQUARE_SUM(r->info->delta_x, r->info->delta_y));
|
||||
r->two_fingers_swipe_dir = calculate_swipe_dir(r);
|
||||
r->type = LV_INDEV_GESTURE_TWO_FINGERS_SWIPE;
|
||||
break;
|
||||
case LV_INDEV_GESTURE_STATE_ENDED:
|
||||
case LV_INDEV_GESTURE_STATE_CANCELED:
|
||||
reset_recognizer(r);
|
||||
break;
|
||||
default:
|
||||
LV_ASSERT_MSG(true, "invalid gesture recognizer state");
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
switch(r->state) {
|
||||
case LV_INDEV_GESTURE_STATE_RECOGNIZED:
|
||||
/* Gesture has ended */
|
||||
r->state = LV_INDEV_GESTURE_STATE_ENDED;
|
||||
r->type = LV_INDEV_GESTURE_TWO_FINGERS_SWIPE;
|
||||
break;
|
||||
case LV_INDEV_GESTURE_STATE_ONGOING:
|
||||
/* User lifted a finger before reaching threshold */
|
||||
reset_recognizer(r);
|
||||
r->state = LV_INDEV_GESTURE_STATE_ENDED;
|
||||
break;
|
||||
case LV_INDEV_GESTURE_STATE_CANCELED:
|
||||
case LV_INDEV_GESTURE_STATE_ENDED:
|
||||
reset_recognizer(r);
|
||||
r->state = LV_INDEV_GESTURE_STATE_NONE;
|
||||
break;
|
||||
default:
|
||||
LV_ASSERT_MSG(true, "invalid gesture recognizer state");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void lv_indev_gesture_recognizers_update(lv_indev_t * indev, lv_indev_touch_data_t * touches, uint16_t touch_cnt)
|
||||
{
|
||||
lv_indev_gesture_type_t type;
|
||||
|
||||
/* First check if a recognizer state is RECOGNIZED or ENDED. *
|
||||
* In that case, call its recongizer function and reset the other*/
|
||||
type = get_first_recognized_or_ended_gesture(indev);
|
||||
if(type != LV_INDEV_GESTURE_NONE) {
|
||||
|
||||
for(int i = 0; i < LV_INDEV_GESTURE_CNT; i++) {
|
||||
|
||||
if(indev->recognizers[i].recog_fn != NULL) {
|
||||
|
||||
/* Update all recognizers to let them process input */
|
||||
indev->recognizers[i].recog_fn(&indev->recognizers[i], &touches[0], touch_cnt);
|
||||
|
||||
/* Then reset the recognizers which did not repport RECONIZED or ENDED */
|
||||
if(((lv_indev_gesture_type_t)i) != type) {
|
||||
reset_recognizer(&indev->recognizers[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
/* Otherwise call all recognizer functions, and stop as soon as one recognizer *
|
||||
* reports the state RECOGNIZED or ENDED */
|
||||
for(int i = 0; i < LV_INDEV_GESTURE_CNT; i++) {
|
||||
if(indev->recognizers[i].recog_fn != NULL) {
|
||||
indev->recognizers[i].recog_fn(&indev->recognizers[i], &touches[0], touch_cnt);
|
||||
|
||||
/* If the new state is RECOGNIZED or ENDED */
|
||||
if(indev->recognizers[i].state == LV_INDEV_GESTURE_STATE_RECOGNIZED ||
|
||||
indev->recognizers[i].state == LV_INDEV_GESTURE_STATE_ENDED) {
|
||||
|
||||
/* Reset the others registered recognizers */
|
||||
for(int j = 0; j < LV_INDEV_GESTURE_CNT; j++) {
|
||||
if(j != i && indev->recognizers[j].recog_fn != NULL) {
|
||||
reset_recognizer(&indev->recognizers[j]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void lv_indev_gesture_recognizers_set_data(lv_indev_t * indev, lv_indev_data_t * data)
|
||||
{
|
||||
lv_indev_gesture_type_t type;
|
||||
type = get_first_recognized_or_ended_gesture(indev);
|
||||
|
||||
/* If a gesture is RECOGNIZED or ENDED, set only its data */
|
||||
if(type != LV_INDEV_GESTURE_NONE) {
|
||||
lv_indev_set_gesture_data(data, &indev->recognizers[type], type);
|
||||
}
|
||||
else {
|
||||
/* Otherwise, set data from all initialized recognizer */
|
||||
for(int i = 0; i < LV_INDEV_GESTURE_CNT; i++) {
|
||||
if(indev->recognizers[i].recog_fn != NULL) {
|
||||
lv_indev_set_gesture_data(data, &indev->recognizers[i], i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/********************
|
||||
* STATIC FUNCTIONS
|
||||
********************/
|
||||
|
||||
/**
|
||||
* Caluclate the direction from the starting center of a two fingers swipe gesture
|
||||
* @param recognizer pointer to the recognizer handling the two fingers
|
||||
* swipe gesture
|
||||
* @return the direction of the swipe, from the starting center
|
||||
*/
|
||||
static lv_dir_t calculate_swipe_dir(lv_indev_gesture_recognizer_t * recognizer)
|
||||
{
|
||||
float abs_x = LV_ABS(recognizer->info->delta_x);
|
||||
float abs_y = LV_ABS(recognizer->info->delta_y);
|
||||
|
||||
if(abs_x > abs_y) {
|
||||
return recognizer->info->delta_x > 0 ? LV_DIR_RIGHT : LV_DIR_LEFT;
|
||||
}
|
||||
else {
|
||||
return recognizer->info->delta_y > 0 ? LV_DIR_BOTTOM : LV_DIR_TOP;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the gesture recognizer associated to the event
|
||||
* @param gesture_event an LV_GESTURE_EVENT event
|
||||
* @param type the type of the recognizer we want to get
|
||||
* @return a pointer to the gesture recognizer that emitted the event
|
||||
*/
|
||||
lv_indev_gesture_recognizer_t * lv_indev_get_gesture_recognizer(lv_event_t * gesture_event,
|
||||
lv_indev_gesture_type_t type)
|
||||
{
|
||||
lv_indev_t * indev;
|
||||
|
||||
if(gesture_event == NULL || gesture_event->param == NULL) return NULL;
|
||||
|
||||
indev = (lv_indev_t *) gesture_event->param;
|
||||
|
||||
if(indev == NULL || indev->gesture_data[type] == NULL) return NULL;
|
||||
|
||||
return (lv_indev_gesture_recognizer_t *) indev->gesture_data[type];
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets a gesture recognizer, motion descriptors are preserved
|
||||
* @param recognizer a pointer to the recognizer to reset
|
||||
*/
|
||||
static void reset_recognizer(lv_indev_gesture_recognizer_t * recognizer)
|
||||
{
|
||||
uint8_t finger_cnt;
|
||||
lv_indev_gesture_t * info;
|
||||
lv_indev_gesture_configuration_t * conf;
|
||||
lv_recognizer_func_t recog_fn;
|
||||
|
||||
if(recognizer == NULL) return;
|
||||
|
||||
finger_cnt = recognizer->info->finger_cnt;
|
||||
info = recognizer->info;
|
||||
conf = recognizer->config;
|
||||
recog_fn = recognizer->recog_fn;
|
||||
|
||||
/* Set everything to zero but preserve the motion descriptors,
|
||||
* which are located at the start of the lv_indev_gesture_t struct */
|
||||
lv_memzero((uint8_t *)info + sizeof(info->motions), sizeof(lv_indev_gesture_t) - sizeof(info->motions));
|
||||
lv_memzero(recognizer, sizeof(lv_indev_gesture_recognizer_t));
|
||||
|
||||
recognizer->info = info;
|
||||
recognizer->config = conf;
|
||||
recognizer->recog_fn = recog_fn;
|
||||
|
||||
recognizer->scale = recognizer->info->scale = 1.0;
|
||||
recognizer->info->finger_cnt = finger_cnt;
|
||||
|
||||
recognizer->state = LV_INDEV_GESTURE_STATE_NONE;
|
||||
recognizer->type = LV_INDEV_GESTURE_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a motion descriptors used with the recognizer(s)
|
||||
* @return a pointer to gesture descriptor
|
||||
*/
|
||||
static lv_indev_gesture_t * init_gesture_info(void)
|
||||
{
|
||||
lv_indev_gesture_t * info;
|
||||
uint8_t i;
|
||||
|
||||
info = lv_malloc_zeroed(sizeof(lv_indev_gesture_t));
|
||||
LV_ASSERT_MALLOC(info);
|
||||
|
||||
info->scale = 1;
|
||||
info->rotation = 0.0;
|
||||
|
||||
for(i = 0; i < LV_GESTURE_MAX_POINTS; i++) {
|
||||
info->motions[i].finger = -1;
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the contact point motion descriptor with id
|
||||
* @param id the id of the contact point
|
||||
* @param info a pointer to the gesture descriptor that stores the motion of each contact point
|
||||
* @return a pointer to the motion descriptor or NULL if not found
|
||||
*/
|
||||
static lv_indev_gesture_motion_t * get_motion(uint8_t id, lv_indev_gesture_t * info)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
for(i = 0; i < LV_GESTURE_MAX_POINTS; i++) {
|
||||
if(info->motions[i].finger == id) {
|
||||
return &info->motions[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the index of the contact point motion descriptor
|
||||
* @param id the id of the contact point
|
||||
* @param info a pointer to the gesture descriptor that stores the motion of each contact point
|
||||
* @return the index of the motion descriptor or -1 if not found
|
||||
*/
|
||||
static int8_t get_motion_idx(uint8_t id, lv_indev_gesture_t * info)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
for(i = 0; i < LV_GESTURE_MAX_POINTS; i++) {
|
||||
if(info->motions[i].finger == id) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the motion descriptors of a gesture
|
||||
* @param touch a pointer to a touch data structure
|
||||
* @param info a pointer to a gesture descriptor
|
||||
*/
|
||||
static void process_touch_event(lv_indev_touch_data_t * touch, lv_indev_gesture_t * info)
|
||||
{
|
||||
lv_indev_gesture_t * g = info;
|
||||
lv_indev_gesture_motion_t * motion;
|
||||
int8_t motion_idx;
|
||||
int8_t len;
|
||||
|
||||
motion_idx = get_motion_idx(touch->id, g);
|
||||
|
||||
if(motion_idx == -1 && touch->state == LV_INDEV_STATE_PRESSED) {
|
||||
if(g->finger_cnt >= LV_GESTURE_MAX_POINTS) {
|
||||
/* Skip touch */
|
||||
return;
|
||||
}
|
||||
|
||||
/* New touch point id */
|
||||
motion = &g->motions[g->finger_cnt];
|
||||
motion->start_point.x = touch->point.x;
|
||||
motion->start_point.y = touch->point.y;
|
||||
motion->point.x = touch->point.x;
|
||||
motion->point.y = touch->point.y;
|
||||
motion->finger = touch->id;
|
||||
motion->state = touch->state;
|
||||
|
||||
g->finger_cnt++;
|
||||
}
|
||||
else if(motion_idx >= 0 && touch->state == LV_INDEV_STATE_RELEASED) {
|
||||
if(motion_idx == g->finger_cnt - 1) {
|
||||
|
||||
/* Mark last item as un-used */
|
||||
motion = get_motion(touch->id, g);
|
||||
|
||||
if(motion == NULL) {
|
||||
LV_LOG_ERROR("Released touch not found (id=%d)", touch->id);
|
||||
return;
|
||||
}
|
||||
|
||||
motion->finger = -1;
|
||||
motion->state = touch->state;
|
||||
}
|
||||
else {
|
||||
|
||||
/* Move back by one */
|
||||
len = (g->finger_cnt - 1) - motion_idx;
|
||||
|
||||
if(len > 0) {
|
||||
lv_memmove(g->motions + motion_idx,
|
||||
g->motions + motion_idx + 1,
|
||||
sizeof(lv_indev_gesture_motion_t) * len);
|
||||
}
|
||||
|
||||
if(g->finger_cnt > 0) {
|
||||
g->motions[g->finger_cnt - 1].finger = -1;
|
||||
}
|
||||
|
||||
LV_ASSERT(g->motions[motion_idx + 1].finger == -1);
|
||||
}
|
||||
|
||||
if(g->finger_cnt > 0) {
|
||||
g->finger_cnt--;
|
||||
}
|
||||
else {
|
||||
g->finger_cnt = 0;
|
||||
}
|
||||
}
|
||||
else if(motion_idx >= 0) {
|
||||
motion = get_motion(touch->id, g);
|
||||
|
||||
if(motion == NULL) {
|
||||
LV_LOG_ERROR("Active touch missing (id=%d state=%d)", touch->id, touch->state);
|
||||
return;
|
||||
}
|
||||
|
||||
motion->point.x = touch->point.x;
|
||||
motion->point.y = touch->point.y;
|
||||
motion->state = touch->state;
|
||||
}
|
||||
else {
|
||||
LV_LOG_TRACE("Ignore extra touch id: %d", touch->id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the center point of a gesture, called when there
|
||||
* is a probability for the gesture to occur
|
||||
* @param touch a pointer to a touch data structure
|
||||
* @param touch_points_nb The number of contact point to take into account
|
||||
*/
|
||||
static void gesture_update_center_point(lv_indev_gesture_t * gesture, int touch_points_nb)
|
||||
{
|
||||
lv_indev_gesture_motion_t * motion;
|
||||
lv_indev_gesture_t * g = gesture;
|
||||
int32_t x = 0;
|
||||
int32_t y = 0;
|
||||
uint8_t i;
|
||||
float scale_factor = 0.0f;
|
||||
float delta_x[LV_GESTURE_MAX_POINTS] = {0.0f};
|
||||
float delta_y[LV_GESTURE_MAX_POINTS] = {0.0f};
|
||||
uint8_t touch_cnt = 0;
|
||||
x = y = 0;
|
||||
|
||||
g->p_scale = g->scale;
|
||||
g->p_delta_x = g->delta_x;
|
||||
g->p_delta_y = g->delta_y;
|
||||
g->p_rotation = g->rotation;
|
||||
|
||||
for(i = 0; i < touch_points_nb; i++) {
|
||||
motion = &g->motions[i];
|
||||
|
||||
if(motion->finger >= 0) {
|
||||
x += motion->point.x;
|
||||
y += motion->point.y;
|
||||
touch_cnt++;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
g->center.x = x / touch_cnt;
|
||||
g->center.y = y / touch_cnt;
|
||||
|
||||
for(i = 0; i < touch_points_nb; i++) {
|
||||
motion = &g->motions[i];
|
||||
if(motion->finger >= 0) {
|
||||
delta_x[i] = motion->point.x - g->center.x;
|
||||
delta_y[i] = motion->point.y - g->center.y;
|
||||
scale_factor += (delta_x[i] * delta_x[i]) + (delta_y[i] * delta_y[i]);
|
||||
}
|
||||
}
|
||||
for(i = 0; i < touch_points_nb; i++) {
|
||||
motion = &g->motions[i];
|
||||
if(motion->finger >= 0) {
|
||||
g->scale_factors_x[i] = delta_x[i] / scale_factor;
|
||||
g->scale_factors_y[i] = delta_y[i] / scale_factor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the scale, translation and rotation of a gesture, called when
|
||||
* the gesture has been recognized
|
||||
* @param gesture a pointer to the gesture descriptor
|
||||
* @param touch_points_nb the number of contact points to take into account
|
||||
*/
|
||||
static void gesture_calculate_factors(lv_indev_gesture_t * gesture, int touch_points_nb)
|
||||
{
|
||||
lv_indev_gesture_motion_t * motion;
|
||||
lv_indev_gesture_t * g = gesture;
|
||||
float center_x = 0;
|
||||
float center_y = 0;
|
||||
float a = 0;
|
||||
float b = 0;
|
||||
float d_x;
|
||||
float d_y;
|
||||
int8_t i;
|
||||
int8_t touch_cnt = 0;
|
||||
|
||||
for(i = 0; i < touch_points_nb; i++) {
|
||||
motion = &g->motions[i];
|
||||
|
||||
if(motion->finger >= 0) {
|
||||
center_x += motion->point.x;
|
||||
center_y += motion->point.y;
|
||||
touch_cnt++;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
center_x = center_x / touch_cnt;
|
||||
center_y = center_y / touch_cnt;
|
||||
|
||||
/* translation */
|
||||
g->delta_x = g->p_delta_x + (center_x - g->center.x);
|
||||
g->delta_y = g->p_delta_y + (center_y - g->center.y);
|
||||
|
||||
/* rotation & scaling */
|
||||
for(i = 0; i < touch_points_nb; i++) {
|
||||
motion = &g->motions[i];
|
||||
|
||||
if(motion->finger >= 0) {
|
||||
d_x = (motion->point.x - center_x);
|
||||
d_y = (motion->point.y - center_y);
|
||||
a += g->scale_factors_x[i] * d_x + g->scale_factors_y[i] * d_y;
|
||||
b += g->scale_factors_x[i] * d_y - g->scale_factors_y[i] * d_x;
|
||||
}
|
||||
}
|
||||
|
||||
g->rotation = g->p_rotation + atan2f(b, a);
|
||||
g->scale = g->p_scale * sqrtf((a * a) + (b * b));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type of the first gesture which reports either a LV_INDEV_GESTURE_STATE_RECOGNIZED
|
||||
* or LV_INDEV_GESTURE_STATE_ENDED state
|
||||
* @param indev pointer to the indev device from which we want to check the gestures states
|
||||
* @return the type of the gesture having the state LV_INDEV_GESTURE_STATE_RECOGNIZED or
|
||||
* LV_INDEV_GESTURE_STATE_ENDED, if found
|
||||
* LV_INDEV_GESTURE_NONE otherwise
|
||||
*/
|
||||
static lv_indev_gesture_type_t get_first_recognized_or_ended_gesture(lv_indev_t * indev)
|
||||
{
|
||||
for(int i = 0; i < LV_INDEV_GESTURE_CNT; i++) {
|
||||
if(indev->recognizers[i].state == LV_INDEV_GESTURE_STATE_RECOGNIZED ||
|
||||
indev->recognizers[i].state == LV_INDEV_GESTURE_STATE_ENDED)
|
||||
return (lv_indev_gesture_type_t) i;
|
||||
}
|
||||
|
||||
return LV_INDEV_GESTURE_NONE;
|
||||
}
|
||||
|
||||
|
||||
#endif /* LV_USE_GESTURE_RECOGNITION */
|
||||
@@ -0,0 +1,244 @@
|
||||
/*******************************************************************
|
||||
*
|
||||
* @file lv_indev_gesture.h
|
||||
*
|
||||
* Copyright (c) 2024 EDGEMTech Ltd.
|
||||
*
|
||||
* Author EDGEMTech Ltd, (erik.tagirov@edgemtech.ch)
|
||||
*
|
||||
******************************************************************/
|
||||
|
||||
#ifndef LV_INDEV_GESTURE_H
|
||||
#define LV_INDEV_GESTURE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "../core/lv_obj.h"
|
||||
|
||||
#if LV_USE_GESTURE_RECOGNITION
|
||||
|
||||
#if LV_USE_FLOAT == 0
|
||||
#error "LV_USE_FLOAT is required for gesture detection."
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/* Opaque types defined in the private header */
|
||||
struct lv_indev_gesture;
|
||||
struct lv_indev_gesture_configuration;
|
||||
|
||||
typedef struct lv_indev_gesture_recognizer lv_indev_gesture_recognizer_t;
|
||||
typedef struct lv_indev_touch_data lv_indev_touch_data_t;
|
||||
|
||||
typedef struct lv_indev_gesture lv_indev_gesture_t;
|
||||
typedef struct lv_indev_gesture_configuration lv_indev_gesture_configuration_t;
|
||||
|
||||
typedef void (*lv_recognizer_func_t)(lv_indev_gesture_recognizer_t *, lv_indev_touch_data_t *, uint16_t);
|
||||
|
||||
/* The states of a gesture recognizer */
|
||||
typedef enum {
|
||||
LV_INDEV_GESTURE_STATE_NONE = 0, /* Beginning & end */
|
||||
LV_INDEV_GESTURE_STATE_ONGOING, /* Set when there is a probability */
|
||||
LV_INDEV_GESTURE_STATE_RECOGNIZED, /* Recognized, the event will contain touch info */
|
||||
LV_INDEV_GESTURE_STATE_ENDED, /* A recognized gesture has ended */
|
||||
LV_INDEV_GESTURE_STATE_CANCELED, /* Canceled - usually a finger is lifted */
|
||||
} lv_indev_gesture_state_t;
|
||||
|
||||
/* Data structures for touch events - used to repsensent a libinput event */
|
||||
/* Emitted by devices capable of tracking identifiable contacts (type B) */
|
||||
struct lv_indev_touch_data {
|
||||
lv_point_t point; /* Coordinates of the touch */
|
||||
lv_indev_state_t state; /* The state i.e PRESSED or RELEASED */
|
||||
uint8_t id; /* Identification/slot of the contact point */
|
||||
uint32_t timestamp; /* Timestamp in milliseconds */
|
||||
};
|
||||
|
||||
/* Gesture recognizer */
|
||||
struct lv_indev_gesture_recognizer {
|
||||
lv_indev_gesture_type_t type; /* The detected gesture type */
|
||||
lv_indev_gesture_state_t state; /* The gesture state ongoing, recognized */
|
||||
lv_indev_gesture_t * info; /* Information on the motion of each touch point */
|
||||
float scale; /* Relevant for the pinch gesture */
|
||||
float rotation; /* Relevant for rotation */
|
||||
float distance; /* Relevant for swipes */
|
||||
float speed;
|
||||
lv_dir_t two_fingers_swipe_dir; /* Relevant for swipes */
|
||||
|
||||
lv_indev_gesture_configuration_t * config; /* The recognizer config, containing the gestures
|
||||
thresholds */
|
||||
lv_recognizer_func_t recog_fn; /* The recognizer function that this recongnizer must execute */
|
||||
};
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
|
||||
/* PINCH Gesture */
|
||||
|
||||
/**
|
||||
* Pinch gesture recognizer function
|
||||
* Will update the recognizer data
|
||||
* @param recognizer pointer to a gesture recognizer
|
||||
* @param touches pointer to the first element of the collected touch events
|
||||
* @param touch_cnt length of passed touch event array.
|
||||
*/
|
||||
void lv_indev_gesture_detect_pinch(lv_indev_gesture_recognizer_t * recognizer, lv_indev_touch_data_t * touches,
|
||||
uint16_t touch_cnt);
|
||||
|
||||
/**
|
||||
* Rotation gesture recognizer function
|
||||
* Will update the recognizer data
|
||||
* @param recognizer pointer to a gesture recognizer
|
||||
* @param touches pointer to the first element of the collected touch events
|
||||
* @param touch_cnt length of passed touch event array.
|
||||
*/
|
||||
void lv_indev_gesture_detect_rotation(lv_indev_gesture_recognizer_t * recognizer, lv_indev_touch_data_t * touches,
|
||||
uint16_t touch_cnt);
|
||||
|
||||
/**
|
||||
* Two finger swipe gesture recognizer function
|
||||
* Will update the recognizer data
|
||||
* @param recognizer pointer to a gesture recognizer
|
||||
* @param touches pointer to the first element of the collected touch events
|
||||
* @param touch_cnt length of passed touch event array.
|
||||
*/
|
||||
void lv_indev_gesture_detect_two_fingers_swipe(lv_indev_gesture_recognizer_t * recognizer,
|
||||
lv_indev_touch_data_t * touches,
|
||||
uint16_t touch_cnt);
|
||||
|
||||
/**
|
||||
* Set the threshold for the pinch gesture scale up, when the scale factor of gesture
|
||||
* reaches the threshold events get sent
|
||||
* @param indev pointer to the indev device containing the pinch recognizer
|
||||
* @param threshold threshold for a pinch up gesture to be recognized
|
||||
*/
|
||||
void lv_indev_set_pinch_up_threshold(lv_indev_t * indev, float threshold);
|
||||
|
||||
/**
|
||||
* Set the threshold for the pinch gesture scale down, when the scale factor of gesture
|
||||
* reaches the threshold events get sent
|
||||
* @param indev pointer to the indev device containing the pinch recognizer
|
||||
* @param threshold threshold for a pinch down gesture to be recognized
|
||||
*/
|
||||
void lv_indev_set_pinch_down_threshold(lv_indev_t * indev, float threshold);
|
||||
|
||||
/**
|
||||
* Set the rotation threshold in radian for the rotation gesture
|
||||
* @param indev pointer to the indev device containing the rotation recognizer
|
||||
* @param threshold threshold in radian for a rotation gesture to be recognized
|
||||
*/
|
||||
void lv_indev_set_rotation_rad_threshold(lv_indev_t * indev, float threshold);
|
||||
|
||||
/**
|
||||
* Obtains the current scale of a pinch gesture
|
||||
* @param gesture_event pointer to a gesture event
|
||||
* @return the scale of the current gesture
|
||||
*/
|
||||
float lv_event_get_pinch_scale(lv_event_t * gesture_event);
|
||||
|
||||
/**
|
||||
* Obtains the current angle in radian of a rotation gesture
|
||||
* @param gesture_event pointer to a gesture event
|
||||
* @return the rotation angle in radian of the current gesture
|
||||
*/
|
||||
float lv_event_get_rotation(lv_event_t * gesture_event);
|
||||
|
||||
/**
|
||||
* Obtains the current distance in pixels of a two fingers swipe gesture, from the starting center
|
||||
* @param gesture_event pointer to a gesture event
|
||||
* @return the distance from the center, in pixels, of the current gesture
|
||||
*/
|
||||
float lv_event_get_two_fingers_swipe_distance(lv_event_t * gesture_event);
|
||||
|
||||
/**
|
||||
* Obtains the current direction from the center of a two finger swipe
|
||||
* @param gesture_event pointer to a gesture event
|
||||
* @return the rotation angle in radian of the current gesture
|
||||
*/
|
||||
lv_dir_t lv_event_get_two_fingers_swipe_dir(lv_event_t * gesture_event);
|
||||
|
||||
/**
|
||||
* Sets the state of the recognizer to a indev data structure,
|
||||
* it is usually called from the indev read callback
|
||||
* @param data the indev data
|
||||
* @param recognizer pointer to a gesture recognizer
|
||||
*/
|
||||
void lv_indev_set_gesture_data(lv_indev_data_t * data, lv_indev_gesture_recognizer_t * recognizer,
|
||||
lv_indev_gesture_type_t type);
|
||||
|
||||
/**
|
||||
* Obtains the center point of a gesture
|
||||
* @param gesture_event pointer to a gesture recognizer event
|
||||
* @param point pointer to a point
|
||||
*/
|
||||
void lv_indev_get_gesture_center_point(lv_indev_gesture_recognizer_t * recognizer, lv_point_t * point);
|
||||
|
||||
/**
|
||||
* Obtains the current state of the gesture recognizer attached to an event
|
||||
* @param gesture_event pointer to a gesture recognizer event
|
||||
* @return current state of the gesture recognizer
|
||||
*/
|
||||
lv_indev_gesture_state_t lv_event_get_gesture_state(lv_event_t * gesture_event, lv_indev_gesture_type_t type);
|
||||
|
||||
/**
|
||||
* Obtains the current event type of the gesture recognizer attached to an event
|
||||
* @param gesture_event pointer to a gesture recognizer event
|
||||
* @return current event type of the gesture recognizer
|
||||
*/
|
||||
lv_indev_gesture_type_t lv_event_get_gesture_type(lv_event_t * gesture_event);
|
||||
|
||||
/**
|
||||
* Obtains the coordinates of the current primary point
|
||||
* @param recognizer pointer to a gesture recognizer
|
||||
* @param point pointer to a point
|
||||
*/
|
||||
void lv_indev_get_gesture_primary_point(lv_indev_gesture_recognizer_t * recognizer, lv_point_t * point);
|
||||
|
||||
/**
|
||||
* Allows to determine if there is an are ongoing gesture
|
||||
* @param recognizer pointer to a gesture recognizer
|
||||
* @return false if there are no contact points, or the gesture has ended - true otherwise
|
||||
*/
|
||||
bool lv_indev_recognizer_is_active(lv_indev_gesture_recognizer_t * recognizer);
|
||||
|
||||
/**
|
||||
* Update the recognizers. It execute the recognizers functions and checks for
|
||||
* LV_GESTURE_STATE_RECOGNIZED or LV_GESTURE_STATE_ENDED gestures.
|
||||
* To be called in the indev read_cb.
|
||||
* @param indev pointer to the indev containing from which the reconizer need an update
|
||||
* @param touches indev touch data array, containing the last touch data from indev
|
||||
* since the last recognizers update
|
||||
* @param touch_cnt number of indev touch data in touches
|
||||
*/
|
||||
void lv_indev_gesture_recognizers_update(lv_indev_t * indev, lv_indev_touch_data_t * touches, uint16_t touch_cnt);
|
||||
|
||||
/**
|
||||
* Set the lv_indev_data_t struct from the recognizer data.
|
||||
* To be called in the indev read_cb.
|
||||
*/
|
||||
void lv_indev_gesture_recognizers_set_data(lv_indev_t * indev, lv_indev_data_t * data);
|
||||
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#endif /* END LV_USE_RECOGNITION */
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /* END LV_INDEV_GESTURE_H */
|
||||
@@ -0,0 +1,97 @@
|
||||
/*******************************************************************
|
||||
*
|
||||
* @file lv_indev_gesture_private.h
|
||||
*
|
||||
* Contains declarations and definition that are internal
|
||||
* to the gesture detection logic
|
||||
*
|
||||
* Copyright (c) 2024 EDGEMTech Ltd.
|
||||
*
|
||||
* Author EDGEMTech Ltd, (erik.tagirov@edgemtech.ch)
|
||||
*
|
||||
******************************************************************/
|
||||
|
||||
#ifndef LV_INDEV_GESTURE_PRIVATE_H
|
||||
#define LV_INDEV_GESTURE_PRIVATE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "../core/lv_obj.h"
|
||||
|
||||
#if LV_USE_GESTURE_RECOGNITION
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
#define LV_GESTURE_MAX_POINTS 2
|
||||
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/* Represent the motion of a finger */
|
||||
struct lv_indev_gesture_motion {
|
||||
int8_t finger; /* The ID of the tracked finger */
|
||||
lv_point_t start_point; /* The coordinates where the DOWN event occurred */
|
||||
lv_point_t point; /* The current coordinates */
|
||||
lv_indev_state_t state; /* DEBUG: The state i.e PRESSED or RELEASED */
|
||||
};
|
||||
|
||||
typedef struct lv_indev_gesture_motion lv_indev_gesture_motion_t;
|
||||
|
||||
/* General descriptor for a gesture, used by recognizer state machines to track
|
||||
* the scale, rotation, and translation NOTE: (this will likely become private) */
|
||||
struct lv_indev_gesture {
|
||||
|
||||
/* Motion descriptor, stores the coordinates and velocity of a contact point */
|
||||
lv_indev_gesture_motion_t motions[LV_GESTURE_MAX_POINTS];
|
||||
|
||||
lv_point_t center; /* Center point */
|
||||
float scale; /* Scale factor & previous scale factor */
|
||||
float p_scale;
|
||||
float scale_factors_x[LV_GESTURE_MAX_POINTS]; /* Scale factor relative to center for each point */
|
||||
float scale_factors_y[LV_GESTURE_MAX_POINTS];
|
||||
|
||||
float delta_x; /* Translation & previous translation */
|
||||
float delta_y;
|
||||
float p_delta_x;
|
||||
float p_delta_y;
|
||||
float rotation; /* Rotation & previous rotation*/
|
||||
float p_rotation;
|
||||
uint8_t finger_cnt; /* Current number of contact points */
|
||||
|
||||
};
|
||||
|
||||
/* Recognizer configuration. It stores the thresholds needed to detect the gestures and
|
||||
* consider them as recognized. Once recognized, indev start sending LV_GESTURE event
|
||||
*/
|
||||
struct lv_indev_gesture_configuration {
|
||||
|
||||
float pinch_up_threshold; /* Threshold for the pinch up gesture to be recognized - in pixels */
|
||||
float pinch_down_threshold; /* Threshold for the pinch down gesture to be recognized - in pixels */
|
||||
float rotation_angle_rad_threshold; /* Threshold for the rotation gesture to be recognized - in radians */
|
||||
|
||||
};
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#endif /* END LV_USE_RECOGNITION */
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /* END LV_INDEV_GESTURE_PRIVATE_H */
|
||||
@@ -0,0 +1,150 @@
|
||||
/**
|
||||
* @file lv_indev_private.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_INDEV_PRIVATE_H
|
||||
#define LV_INDEV_PRIVATE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "lv_indev.h"
|
||||
#include "../misc/lv_anim.h"
|
||||
#include "lv_indev_scroll.h"
|
||||
#include "lv_indev_gesture.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
struct _lv_indev_t {
|
||||
/** Input device type*/
|
||||
lv_indev_type_t type;
|
||||
|
||||
/** Function pointer to read input device data.*/
|
||||
lv_indev_read_cb_t read_cb;
|
||||
|
||||
lv_indev_state_t state; /**< Current state of the input device.*/
|
||||
lv_indev_state_t prev_state; /**< Previous state of the input device.*/
|
||||
lv_indev_mode_t mode;
|
||||
|
||||
/*Flags*/
|
||||
uint8_t long_pr_sent : 1;
|
||||
uint8_t reset_query : 1;
|
||||
uint8_t enabled : 1;
|
||||
uint8_t wait_until_release : 1;
|
||||
uint8_t stop_processing_query : 1;
|
||||
|
||||
uint32_t pr_timestamp; /**< Pressed time stamp*/
|
||||
uint32_t longpr_rep_timestamp; /**< Long press repeat time stamp*/
|
||||
|
||||
void * driver_data;
|
||||
void * user_data;
|
||||
|
||||
/**< Pointer to the assigned display*/
|
||||
lv_display_t * disp;
|
||||
|
||||
/**< Timer to periodically read the input device*/
|
||||
lv_timer_t * read_timer;
|
||||
|
||||
/**< Number of pixels to slide before actually drag the object*/
|
||||
uint8_t scroll_limit;
|
||||
|
||||
/**< Drag throw slow-down in [%]. Greater value means faster slow-down*/
|
||||
uint8_t scroll_throw;
|
||||
|
||||
/**< At least this difference should be between two points to evaluate as gesture*/
|
||||
uint8_t gesture_min_velocity;
|
||||
|
||||
/**< At least this difference should be to send a gesture*/
|
||||
uint8_t gesture_limit;
|
||||
|
||||
/**< Long press time in milliseconds*/
|
||||
uint16_t long_press_time;
|
||||
|
||||
/**< Repeated trigger period in long press [ms]*/
|
||||
uint16_t long_press_repeat_time;
|
||||
|
||||
/**< Rotary diff count will be multiplied by this value and divided by 256*/
|
||||
int32_t rotary_sensitivity;
|
||||
|
||||
struct {
|
||||
/*Pointer and button data*/
|
||||
lv_point_t act_point; /**< Current point of input device.*/
|
||||
lv_point_t last_point; /**< Last point of input device.*/
|
||||
lv_point_t last_raw_point; /**< Last point read from read_cb. */
|
||||
lv_point_t vect; /**< Difference between `act_point` and `last_point`.*/
|
||||
lv_point_t scroll_sum; /*Count the dragged pixels to check LV_INDEV_DEF_SCROLL_LIMIT*/
|
||||
lv_point_t scroll_throw_vect;
|
||||
lv_point_t scroll_throw_vect_ori;
|
||||
lv_obj_t * act_obj; /*The object being pressed*/
|
||||
lv_obj_t * last_obj; /*The last object which was pressed*/
|
||||
lv_obj_t * scroll_obj; /*The object being scrolled*/
|
||||
lv_obj_t * last_pressed; /*The lastly pressed object*/
|
||||
lv_obj_t * last_hovered; /*The lastly hovered object*/
|
||||
lv_area_t scroll_area;
|
||||
lv_point_t gesture_sum; /*Count the gesture pixels to check LV_INDEV_DEF_GESTURE_LIMIT*/
|
||||
int32_t diff;
|
||||
/*Short click streaks*/
|
||||
uint8_t short_click_streak;
|
||||
lv_point_t last_short_click_point;
|
||||
uint32_t last_short_click_timestamp;
|
||||
/*Flags*/
|
||||
uint8_t scroll_dir : 4;
|
||||
uint8_t gesture_dir : 4;
|
||||
uint8_t gesture_sent : 1;
|
||||
uint8_t press_moved : 1;
|
||||
uint8_t pressed : 1;
|
||||
} pointer;
|
||||
struct {
|
||||
/*Keypad data*/
|
||||
lv_indev_state_t last_state;
|
||||
uint32_t last_key;
|
||||
} keypad;
|
||||
|
||||
lv_obj_t * cursor; /**< Cursor for LV_INPUT_TYPE_POINTER*/
|
||||
lv_group_t * group; /**< Keypad destination group*/
|
||||
const lv_point_t * btn_points; /**< Array points assigned to the button ()screen will be pressed
|
||||
here by the buttons*/
|
||||
lv_event_list_t event_list;
|
||||
lv_anim_t * scroll_throw_anim;
|
||||
|
||||
#if LV_USE_GESTURE_RECOGNITION
|
||||
lv_indev_gesture_recognizer_t recognizers[LV_INDEV_GESTURE_CNT];
|
||||
lv_indev_gesture_type_t cur_gesture;
|
||||
void * gesture_data[LV_INDEV_GESTURE_CNT];
|
||||
lv_indev_gesture_type_t gesture_type[LV_INDEV_GESTURE_CNT];
|
||||
#endif
|
||||
};
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* Find a scrollable object based on the current scroll vector in the indev.
|
||||
* In handles scroll propagation to the parent if needed, and scroll directions too.
|
||||
* @param indev pointer to an indev
|
||||
* @return the found scrollable object or NULL if not found.
|
||||
*/
|
||||
lv_obj_t * lv_indev_find_scroll_obj(lv_indev_t * indev);
|
||||
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_INDEV_PRIVATE_H*/
|
||||
@@ -0,0 +1,768 @@
|
||||
/**
|
||||
* @file lv_indev_scroll.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "../core/lv_obj_scroll_private.h"
|
||||
#include "../core/lv_obj_private.h"
|
||||
#include "lv_indev.h"
|
||||
#include "lv_indev_private.h"
|
||||
#include "lv_indev_scroll.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
#define ELASTIC_SLOWNESS_FACTOR 4 /*Scrolling on elastic parts are slower by this factor*/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
static void init_scroll_limits(lv_indev_t * indev);
|
||||
static int32_t find_snap_point_x(const lv_obj_t * obj, int32_t min, int32_t max, int32_t ofs);
|
||||
static int32_t find_snap_point_y(const lv_obj_t * obj, int32_t min, int32_t max, int32_t ofs);
|
||||
static void scroll_limit_diff(lv_indev_t * indev, int32_t * diff_x, int32_t * diff_y);
|
||||
static int32_t elastic_diff(lv_obj_t * scroll_obj, int32_t diff, int32_t scroll_start, int32_t scroll_end,
|
||||
lv_dir_t dir);
|
||||
static void has_more_snap_points(lv_obj_t * scroll_obj, lv_dir_t dir, bool * has_start_snap, bool * has_end_snap);
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
void lv_indev_scroll_handler(lv_indev_t * indev)
|
||||
{
|
||||
if(indev->pointer.vect.x == 0 && indev->pointer.vect.y == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
lv_obj_t * scroll_obj = indev->pointer.scroll_obj;
|
||||
/*If there is no scroll object yet try to find one*/
|
||||
if(scroll_obj == NULL) {
|
||||
scroll_obj = lv_indev_find_scroll_obj(indev);
|
||||
if(scroll_obj == NULL) return;
|
||||
|
||||
init_scroll_limits(indev);
|
||||
|
||||
lv_obj_remove_state(indev->pointer.act_obj, LV_STATE_PRESSED);
|
||||
lv_obj_send_event(scroll_obj, LV_EVENT_SCROLL_BEGIN, NULL);
|
||||
if(indev->reset_query) return;
|
||||
}
|
||||
|
||||
/*Set new position or scroll if the vector is not zero*/
|
||||
int16_t angle = 0;
|
||||
int16_t scale_x = 256;
|
||||
int16_t scale_y = 256;
|
||||
lv_obj_t * parent = scroll_obj;
|
||||
while(parent) {
|
||||
angle += lv_obj_get_style_transform_rotation(parent, 0);
|
||||
int32_t zoom_act_x = lv_obj_get_style_transform_scale_x_safe(parent, 0);
|
||||
int32_t zoom_act_y = lv_obj_get_style_transform_scale_y_safe(parent, 0);
|
||||
scale_x = (scale_x * zoom_act_x) >> 8;
|
||||
scale_y = (scale_y * zoom_act_y) >> 8;
|
||||
parent = lv_obj_get_parent(parent);
|
||||
}
|
||||
|
||||
if(scale_x == 0) {
|
||||
scale_x = 1;
|
||||
}
|
||||
|
||||
if(scale_y == 0) {
|
||||
scale_y = 1;
|
||||
}
|
||||
|
||||
if(angle != 0 || scale_x != LV_SCALE_NONE || scale_y != LV_SCALE_NONE) {
|
||||
angle = -angle;
|
||||
scale_x = (256 * 256) / scale_x;
|
||||
scale_y = (256 * 256) / scale_y;
|
||||
lv_point_t pivot = { 0, 0 };
|
||||
lv_point_transform(&indev->pointer.vect, angle, scale_x, scale_y, &pivot, false);
|
||||
}
|
||||
|
||||
int32_t diff_x = 0;
|
||||
int32_t diff_y = 0;
|
||||
if(indev->pointer.scroll_dir == LV_DIR_HOR) {
|
||||
int32_t sr = lv_obj_get_scroll_right(scroll_obj);
|
||||
int32_t sl = lv_obj_get_scroll_left(scroll_obj);
|
||||
diff_x = elastic_diff(scroll_obj, indev->pointer.vect.x, sl, sr, LV_DIR_HOR);
|
||||
}
|
||||
else {
|
||||
int32_t st = lv_obj_get_scroll_top(scroll_obj);
|
||||
int32_t sb = lv_obj_get_scroll_bottom(scroll_obj);
|
||||
diff_y = elastic_diff(scroll_obj, indev->pointer.vect.y, st, sb, LV_DIR_VER);
|
||||
}
|
||||
|
||||
lv_dir_t scroll_dir = lv_obj_get_scroll_dir(scroll_obj);
|
||||
if((scroll_dir & LV_DIR_LEFT) == 0 && diff_x > 0) diff_x = 0;
|
||||
if((scroll_dir & LV_DIR_RIGHT) == 0 && diff_x < 0) diff_x = 0;
|
||||
if((scroll_dir & LV_DIR_TOP) == 0 && diff_y > 0) diff_y = 0;
|
||||
if((scroll_dir & LV_DIR_BOTTOM) == 0 && diff_y < 0) diff_y = 0;
|
||||
|
||||
/*Respect the scroll limit area*/
|
||||
scroll_limit_diff(indev, &diff_x, &diff_y);
|
||||
|
||||
lv_obj_scroll_by_raw(scroll_obj, diff_x, diff_y);
|
||||
if(indev->reset_query) return;
|
||||
indev->pointer.scroll_sum.x += diff_x;
|
||||
indev->pointer.scroll_sum.y += diff_y;
|
||||
}
|
||||
|
||||
void lv_indev_scroll_throw_handler(lv_indev_t * indev)
|
||||
{
|
||||
lv_obj_t * scroll_obj = indev->pointer.scroll_obj;
|
||||
if(scroll_obj == NULL) return;
|
||||
if(indev->pointer.scroll_dir == LV_DIR_NONE) return;
|
||||
|
||||
int32_t scroll_throw = indev->scroll_throw;
|
||||
|
||||
if(lv_obj_has_flag(scroll_obj, LV_OBJ_FLAG_SCROLL_MOMENTUM) == false) {
|
||||
indev->pointer.scroll_throw_vect.y = 0;
|
||||
indev->pointer.scroll_throw_vect.x = 0;
|
||||
}
|
||||
|
||||
lv_scroll_snap_t align_x = lv_obj_get_scroll_snap_x(scroll_obj);
|
||||
lv_scroll_snap_t align_y = lv_obj_get_scroll_snap_y(scroll_obj);
|
||||
|
||||
if(indev->pointer.scroll_dir == LV_DIR_VER) {
|
||||
indev->pointer.scroll_throw_vect.x = 0;
|
||||
/*If no snapping "throw"*/
|
||||
if(align_y == LV_SCROLL_SNAP_NONE) {
|
||||
indev->pointer.scroll_throw_vect.y =
|
||||
indev->pointer.scroll_throw_vect.y * (100 - scroll_throw) / 100;
|
||||
|
||||
int32_t sb = lv_obj_get_scroll_bottom(scroll_obj);
|
||||
int32_t st = lv_obj_get_scroll_top(scroll_obj);
|
||||
|
||||
indev->pointer.scroll_throw_vect.y = elastic_diff(scroll_obj, indev->pointer.scroll_throw_vect.y, st, sb,
|
||||
LV_DIR_VER);
|
||||
|
||||
lv_obj_scroll_by_raw(scroll_obj, 0, indev->pointer.scroll_throw_vect.y);
|
||||
if(indev->reset_query) return;
|
||||
}
|
||||
/*With snapping find the nearest snap point and scroll there*/
|
||||
else {
|
||||
int32_t diff_y = lv_indev_scroll_throw_predict(indev, LV_DIR_VER);
|
||||
indev->pointer.scroll_throw_vect.y = 0;
|
||||
scroll_limit_diff(indev, NULL, &diff_y);
|
||||
int32_t y = find_snap_point_y(scroll_obj, LV_COORD_MIN, LV_COORD_MAX, diff_y);
|
||||
lv_obj_scroll_by(scroll_obj, 0, diff_y + y, LV_ANIM_ON);
|
||||
if(indev->reset_query) return;
|
||||
}
|
||||
}
|
||||
else if(indev->pointer.scroll_dir == LV_DIR_HOR) {
|
||||
indev->pointer.scroll_throw_vect.y = 0;
|
||||
/*If no snapping "throw"*/
|
||||
if(align_x == LV_SCROLL_SNAP_NONE) {
|
||||
indev->pointer.scroll_throw_vect.x =
|
||||
indev->pointer.scroll_throw_vect.x * (100 - scroll_throw) / 100;
|
||||
|
||||
int32_t sl = lv_obj_get_scroll_left(scroll_obj);
|
||||
int32_t sr = lv_obj_get_scroll_right(scroll_obj);
|
||||
|
||||
indev->pointer.scroll_throw_vect.x = elastic_diff(scroll_obj, indev->pointer.scroll_throw_vect.x, sl, sr,
|
||||
LV_DIR_HOR);
|
||||
|
||||
lv_obj_scroll_by_raw(scroll_obj, indev->pointer.scroll_throw_vect.x, 0);
|
||||
if(indev->reset_query) return;
|
||||
}
|
||||
/*With snapping find the nearest snap point and scroll there*/
|
||||
else {
|
||||
int32_t diff_x = lv_indev_scroll_throw_predict(indev, LV_DIR_HOR);
|
||||
indev->pointer.scroll_throw_vect.x = 0;
|
||||
scroll_limit_diff(indev, &diff_x, NULL);
|
||||
int32_t x = find_snap_point_x(scroll_obj, LV_COORD_MIN, LV_COORD_MAX, diff_x);
|
||||
lv_obj_scroll_by(scroll_obj, x + diff_x, 0, LV_ANIM_ON);
|
||||
if(indev->reset_query) return;
|
||||
}
|
||||
}
|
||||
|
||||
/*Check if the scroll has finished*/
|
||||
if(indev->pointer.scroll_throw_vect.x == 0 && indev->pointer.scroll_throw_vect.y == 0) {
|
||||
/*Revert if scrolled in*/
|
||||
/*If vertically scrollable and not controlled by snap*/
|
||||
if(align_y == LV_SCROLL_SNAP_NONE) {
|
||||
int32_t st = lv_obj_get_scroll_top(scroll_obj);
|
||||
int32_t sb = lv_obj_get_scroll_bottom(scroll_obj);
|
||||
if(st > 0 || sb > 0) {
|
||||
if(st < 0) {
|
||||
lv_obj_scroll_by(scroll_obj, 0, st, LV_ANIM_ON);
|
||||
if(indev->reset_query) return;
|
||||
}
|
||||
else if(sb < 0) {
|
||||
lv_obj_scroll_by(scroll_obj, 0, -sb, LV_ANIM_ON);
|
||||
if(indev->reset_query) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*If horizontally scrollable and not controlled by snap*/
|
||||
if(align_x == LV_SCROLL_SNAP_NONE) {
|
||||
int32_t sl = lv_obj_get_scroll_left(scroll_obj);
|
||||
int32_t sr = lv_obj_get_scroll_right(scroll_obj);
|
||||
if(sl > 0 || sr > 0) {
|
||||
if(sl < 0) {
|
||||
lv_obj_scroll_by(scroll_obj, sl, 0, LV_ANIM_ON);
|
||||
if(indev->reset_query) return;
|
||||
}
|
||||
else if(sr < 0) {
|
||||
lv_obj_scroll_by(scroll_obj, -sr, 0, LV_ANIM_ON);
|
||||
if(indev->reset_query) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lv_obj_send_event(scroll_obj, LV_EVENT_SCROLL_END, indev);
|
||||
if(indev->reset_query) return;
|
||||
|
||||
indev->pointer.scroll_dir = LV_DIR_NONE;
|
||||
indev->pointer.scroll_obj = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t lv_indev_scroll_throw_predict(lv_indev_t * indev, lv_dir_t dir)
|
||||
{
|
||||
if(indev == NULL) return 0;
|
||||
int32_t v;
|
||||
switch(dir) {
|
||||
case LV_DIR_VER:
|
||||
v = indev->pointer.scroll_throw_vect_ori.y;
|
||||
break;
|
||||
case LV_DIR_HOR:
|
||||
v = indev->pointer.scroll_throw_vect_ori.x;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t scroll_throw = indev->scroll_throw;
|
||||
int32_t sum = 0;
|
||||
while(v) {
|
||||
sum += v;
|
||||
v = v * (100 - scroll_throw) / 100;
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
void lv_indev_scroll_get_snap_dist(lv_obj_t * obj, lv_point_t * p)
|
||||
{
|
||||
p->x = find_snap_point_x(obj, obj->coords.x1, obj->coords.x2, 0);
|
||||
p->y = find_snap_point_y(obj, obj->coords.y1, obj->coords.y2, 0);
|
||||
}
|
||||
|
||||
lv_obj_t * lv_indev_find_scroll_obj(lv_indev_t * indev)
|
||||
{
|
||||
lv_obj_t * obj_candidate = NULL;
|
||||
lv_dir_t dir_candidate = LV_DIR_NONE;
|
||||
int32_t scroll_limit = indev->scroll_limit;
|
||||
|
||||
/*Go until find a scrollable object in the current direction
|
||||
*More precisely:
|
||||
* 1. Check the pressed object and all of its ancestors and try to find an object which is scrollable
|
||||
* 2. Scrollable means it has some content out of its area
|
||||
* 3. If an object can be scrolled into the current direction then use it ("real match"")
|
||||
* 4. If can be scrolled on the current axis (hor/ver) save it as candidate (at least show an elastic scroll effect)
|
||||
* 5. Use the last candidate. Always the "deepest" parent or the object from point 3*/
|
||||
lv_obj_t * obj_act = indev->pointer.act_obj;
|
||||
|
||||
/*Decide if it's a horizontal or vertical scroll*/
|
||||
bool hor_en = false;
|
||||
bool ver_en = false;
|
||||
indev->pointer.scroll_sum.x += indev->pointer.vect.x;
|
||||
indev->pointer.scroll_sum.y += indev->pointer.vect.y;
|
||||
|
||||
while(obj_act) {
|
||||
/*Get the transformed scroll_sum with this object*/
|
||||
int16_t angle = 0;
|
||||
int32_t scale_x = 256;
|
||||
int32_t scale_y = 256;
|
||||
lv_point_t pivot = { 0, 0 };
|
||||
lv_obj_t * parent = obj_act;
|
||||
while(parent) {
|
||||
angle += lv_obj_get_style_transform_rotation(parent, 0);
|
||||
int32_t zoom_act_x = lv_obj_get_style_transform_scale_x_safe(parent, 0);
|
||||
int32_t zoom_act_y = lv_obj_get_style_transform_scale_y_safe(parent, 0);
|
||||
scale_x = (scale_x * zoom_act_x) >> 8;
|
||||
scale_y = (scale_y * zoom_act_y) >> 8;
|
||||
parent = lv_obj_get_parent(parent);
|
||||
}
|
||||
|
||||
if(scale_x == 0) {
|
||||
scale_x = 1;
|
||||
}
|
||||
|
||||
if(scale_y == 0) {
|
||||
scale_y = 1;
|
||||
}
|
||||
|
||||
lv_point_t obj_scroll_sum = indev->pointer.scroll_sum;
|
||||
if(angle != 0 || scale_x != LV_SCALE_NONE || scale_y != LV_SCALE_NONE) {
|
||||
angle = -angle;
|
||||
scale_x = (256 * 256) / scale_x;
|
||||
scale_y = (256 * 256) / scale_y;
|
||||
lv_point_transform(&obj_scroll_sum, angle, scale_x, scale_y, &pivot, false);
|
||||
}
|
||||
|
||||
if(LV_ABS(obj_scroll_sum.x) > LV_ABS(obj_scroll_sum.y)) {
|
||||
hor_en = true;
|
||||
}
|
||||
else {
|
||||
ver_en = true;
|
||||
}
|
||||
|
||||
if(lv_obj_has_flag(obj_act, LV_OBJ_FLAG_SCROLLABLE) == false) {
|
||||
/*If this object don't want to chain the scroll to the parent stop searching*/
|
||||
if(lv_obj_has_flag(obj_act, LV_OBJ_FLAG_SCROLL_CHAIN_HOR) == false && hor_en) break;
|
||||
if(lv_obj_has_flag(obj_act, LV_OBJ_FLAG_SCROLL_CHAIN_VER) == false && ver_en) break;
|
||||
|
||||
obj_act = lv_obj_get_parent(obj_act);
|
||||
continue;
|
||||
}
|
||||
|
||||
/*Consider both up-down or left/right scrollable according to the current direction*/
|
||||
bool up_en = ver_en;
|
||||
bool down_en = ver_en;
|
||||
bool left_en = hor_en;
|
||||
bool right_en = hor_en;
|
||||
|
||||
/*The object might have disabled some directions.*/
|
||||
lv_dir_t scroll_dir = lv_obj_get_scroll_dir(obj_act);
|
||||
if((scroll_dir & LV_DIR_LEFT) == 0) left_en = false;
|
||||
if((scroll_dir & LV_DIR_RIGHT) == 0) right_en = false;
|
||||
if((scroll_dir & LV_DIR_TOP) == 0) up_en = false;
|
||||
if((scroll_dir & LV_DIR_BOTTOM) == 0) down_en = false;
|
||||
|
||||
/*Horizontal scroll*/
|
||||
int32_t sl = 0;
|
||||
int32_t sr = 0;
|
||||
lv_scroll_snap_t snap_x = lv_obj_get_scroll_snap_x(obj_act);
|
||||
if(snap_x == LV_SCROLL_SNAP_NONE) {
|
||||
sl = lv_obj_get_scroll_left(obj_act);
|
||||
sr = lv_obj_get_scroll_right(obj_act);
|
||||
}
|
||||
else {
|
||||
bool has_start_snap;
|
||||
bool has_end_snap;
|
||||
has_more_snap_points(obj_act, LV_DIR_HOR, &has_start_snap, &has_end_snap);
|
||||
|
||||
/*Assume scrolling is there are more snap point
|
||||
*Assumed scroll in if there are NO more nap points*/
|
||||
sl = has_start_snap ? 1 : -1;
|
||||
sr = has_end_snap ? 1 : -1;
|
||||
}
|
||||
|
||||
/*Vertical scroll*/
|
||||
int32_t st = 0;
|
||||
int32_t sb = 0;
|
||||
lv_scroll_snap_t snap_y = lv_obj_get_scroll_snap_y(obj_act);
|
||||
if(snap_y == LV_SCROLL_SNAP_NONE) {
|
||||
st = lv_obj_get_scroll_top(obj_act);
|
||||
sb = lv_obj_get_scroll_bottom(obj_act);
|
||||
}
|
||||
else {
|
||||
bool has_start_snap;
|
||||
bool has_end_snap;
|
||||
has_more_snap_points(obj_act, LV_DIR_VER, &has_start_snap, &has_end_snap);
|
||||
|
||||
/*Assume scrolling is there are more snap point
|
||||
*Assumed scroll in if there are NO more nap points*/
|
||||
st = has_start_snap ? 1 : -1;
|
||||
sb = has_end_snap ? 1 : -1;
|
||||
}
|
||||
|
||||
/*If this object is scrollable into the current scroll direction then save it as a candidate.
|
||||
*It's important only to be scrollable on the current axis (hor/ver) because if the scroll
|
||||
*is propagated to this object it can show at least elastic scroll effect.
|
||||
*But if not hor/ver scrollable do not scroll it at all (so it's not a good candidate)*/
|
||||
if((st > 0 || sb > 0) &&
|
||||
((up_en && obj_scroll_sum.y >= scroll_limit) ||
|
||||
(down_en && obj_scroll_sum.y <= - scroll_limit))) {
|
||||
obj_candidate = obj_act;
|
||||
dir_candidate = LV_DIR_VER;
|
||||
}
|
||||
|
||||
if((sl > 0 || sr > 0) &&
|
||||
((left_en && obj_scroll_sum.x >= scroll_limit) ||
|
||||
(right_en && obj_scroll_sum.x <= - scroll_limit))) {
|
||||
obj_candidate = obj_act;
|
||||
dir_candidate = LV_DIR_HOR;
|
||||
}
|
||||
|
||||
if(st <= 0) up_en = false;
|
||||
if(sb <= 0) down_en = false;
|
||||
if(sl <= 0) left_en = false;
|
||||
if(sr <= 0) right_en = false;
|
||||
|
||||
/*If the object really can be scrolled into the current direction then use it.*/
|
||||
if((left_en && obj_scroll_sum.x >= scroll_limit) ||
|
||||
(right_en && obj_scroll_sum.x <= - scroll_limit) ||
|
||||
(up_en && obj_scroll_sum.y >= scroll_limit) ||
|
||||
(down_en && obj_scroll_sum.y <= - scroll_limit)) {
|
||||
indev->pointer.scroll_dir = hor_en ? LV_DIR_HOR : LV_DIR_VER;
|
||||
break;
|
||||
}
|
||||
|
||||
/*If this object don't want to chain the scroll to the parent stop searching*/
|
||||
if(lv_obj_has_flag(obj_act, LV_OBJ_FLAG_SCROLL_CHAIN_HOR) == false && hor_en) break;
|
||||
if(lv_obj_has_flag(obj_act, LV_OBJ_FLAG_SCROLL_CHAIN_VER) == false && ver_en) break;
|
||||
|
||||
/*Try the parent*/
|
||||
obj_act = lv_obj_get_parent(obj_act);
|
||||
}
|
||||
|
||||
/*Use the last candidate*/
|
||||
if(obj_candidate) {
|
||||
indev->pointer.scroll_dir = dir_candidate;
|
||||
indev->pointer.scroll_obj = obj_candidate;
|
||||
indev->pointer.scroll_sum.x = 0;
|
||||
indev->pointer.scroll_sum.y = 0;
|
||||
}
|
||||
|
||||
return obj_candidate;
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
static void init_scroll_limits(lv_indev_t * indev)
|
||||
{
|
||||
lv_obj_t * obj = indev->pointer.scroll_obj;
|
||||
/*If there no STOP allow scrolling anywhere*/
|
||||
if(lv_obj_has_flag(obj, LV_OBJ_FLAG_SCROLL_ONE) == false) {
|
||||
lv_area_set(&indev->pointer.scroll_area, LV_COORD_MIN, LV_COORD_MIN, LV_COORD_MAX, LV_COORD_MAX);
|
||||
}
|
||||
/*With STOP limit the scrolling to the perv and next snap point*/
|
||||
else {
|
||||
switch(lv_obj_get_scroll_snap_y(obj)) {
|
||||
case LV_SCROLL_SNAP_START:
|
||||
indev->pointer.scroll_area.y1 = find_snap_point_y(obj, obj->coords.y1 + 1, LV_COORD_MAX, 0);
|
||||
indev->pointer.scroll_area.y2 = find_snap_point_y(obj, LV_COORD_MIN, obj->coords.y1 - 1, 0);
|
||||
break;
|
||||
case LV_SCROLL_SNAP_END:
|
||||
indev->pointer.scroll_area.y1 = find_snap_point_y(obj, obj->coords.y2, LV_COORD_MAX, 0);
|
||||
indev->pointer.scroll_area.y2 = find_snap_point_y(obj, LV_COORD_MIN, obj->coords.y2, 0);
|
||||
break;
|
||||
case LV_SCROLL_SNAP_CENTER: {
|
||||
int32_t y_mid = obj->coords.y1 + lv_area_get_height(&obj->coords) / 2;
|
||||
indev->pointer.scroll_area.y1 = find_snap_point_y(obj, y_mid + 1, LV_COORD_MAX, 0);
|
||||
indev->pointer.scroll_area.y2 = find_snap_point_y(obj, LV_COORD_MIN, y_mid - 1, 0);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
indev->pointer.scroll_area.y1 = LV_COORD_MIN;
|
||||
indev->pointer.scroll_area.y2 = LV_COORD_MAX;
|
||||
break;
|
||||
}
|
||||
|
||||
switch(lv_obj_get_scroll_snap_x(obj)) {
|
||||
case LV_SCROLL_SNAP_START:
|
||||
indev->pointer.scroll_area.x1 = find_snap_point_x(obj, obj->coords.x1, LV_COORD_MAX, 0);
|
||||
indev->pointer.scroll_area.x2 = find_snap_point_x(obj, LV_COORD_MIN, obj->coords.x1, 0);
|
||||
break;
|
||||
case LV_SCROLL_SNAP_END:
|
||||
indev->pointer.scroll_area.x1 = find_snap_point_x(obj, obj->coords.x2, LV_COORD_MAX, 0);
|
||||
indev->pointer.scroll_area.x2 = find_snap_point_x(obj, LV_COORD_MIN, obj->coords.x2, 0);
|
||||
break;
|
||||
case LV_SCROLL_SNAP_CENTER: {
|
||||
int32_t x_mid = obj->coords.x1 + lv_area_get_width(&obj->coords) / 2;
|
||||
indev->pointer.scroll_area.x1 = find_snap_point_x(obj, x_mid + 1, LV_COORD_MAX, 0);
|
||||
indev->pointer.scroll_area.x2 = find_snap_point_x(obj, LV_COORD_MIN, x_mid - 1, 0);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
indev->pointer.scroll_area.x1 = LV_COORD_MIN;
|
||||
indev->pointer.scroll_area.x2 = LV_COORD_MAX;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*`find_snap_point_x/y()` return LV_COORD_MAX is not snap point was found,
|
||||
*but x1/y1 should be small. */
|
||||
if(indev->pointer.scroll_area.x1 == LV_COORD_MAX) indev->pointer.scroll_area.x1 = LV_COORD_MIN;
|
||||
if(indev->pointer.scroll_area.y1 == LV_COORD_MAX) indev->pointer.scroll_area.y1 = LV_COORD_MIN;
|
||||
|
||||
/*Allow scrolling on the edges. It will be reverted to the edge due to snapping anyway*/
|
||||
if(indev->pointer.scroll_area.x1 == 0) indev->pointer.scroll_area.x1 = LV_COORD_MIN;
|
||||
if(indev->pointer.scroll_area.x2 == 0) indev->pointer.scroll_area.x2 = LV_COORD_MAX;
|
||||
if(indev->pointer.scroll_area.y1 == 0) indev->pointer.scroll_area.y1 = LV_COORD_MIN;
|
||||
if(indev->pointer.scroll_area.y2 == 0) indev->pointer.scroll_area.y2 = LV_COORD_MAX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for snap point in the min..max range.
|
||||
* @param obj the object on which snap point should be found
|
||||
* @param min ignore snap points smaller than this. (Absolute coordinate)
|
||||
* @param max ignore snap points greater than this. (Absolute coordinate)
|
||||
* @param ofs offset to snap points. Useful the get a snap point in an imagined case
|
||||
* what if children are already moved by this value
|
||||
* @return the absolute x coordinate of the nearest snap point
|
||||
* or `LV_COORD_MAX` if there is no snap point in the min..max range
|
||||
*/
|
||||
static int32_t find_snap_point_x(const lv_obj_t * obj, int32_t min, int32_t max, int32_t ofs)
|
||||
{
|
||||
lv_scroll_snap_t align = lv_obj_get_scroll_snap_x(obj);
|
||||
if(align == LV_SCROLL_SNAP_NONE) return LV_COORD_MAX;
|
||||
|
||||
int32_t dist = LV_COORD_MAX;
|
||||
|
||||
int32_t pad_left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
|
||||
int32_t pad_right = lv_obj_get_style_pad_right(obj, LV_PART_MAIN);
|
||||
|
||||
uint32_t i;
|
||||
uint32_t child_cnt = lv_obj_get_child_count(obj);
|
||||
for(i = 0; i < child_cnt; i++) {
|
||||
lv_obj_t * child = obj->spec_attr->children[i];
|
||||
if(lv_obj_has_flag_any(child, LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
|
||||
if(lv_obj_has_flag(child, LV_OBJ_FLAG_SNAPPABLE)) {
|
||||
int32_t x_child = 0;
|
||||
int32_t x_parent = 0;
|
||||
switch(align) {
|
||||
case LV_SCROLL_SNAP_START:
|
||||
x_child = child->coords.x1;
|
||||
x_parent = obj->coords.x1 + pad_left;
|
||||
break;
|
||||
case LV_SCROLL_SNAP_END:
|
||||
x_child = child->coords.x2;
|
||||
x_parent = obj->coords.x2 - pad_right;
|
||||
break;
|
||||
case LV_SCROLL_SNAP_CENTER:
|
||||
x_child = child->coords.x1 + lv_area_get_width(&child->coords) / 2;
|
||||
x_parent = obj->coords.x1 + pad_left + (lv_area_get_width(&obj->coords) - pad_left - pad_right) / 2;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
x_child += ofs;
|
||||
if(x_child >= min && x_child <= max) {
|
||||
int32_t x = x_child - x_parent;
|
||||
if(LV_ABS(x) < LV_ABS(dist)) dist = x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dist == LV_COORD_MAX ? LV_COORD_MAX : -dist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for snap point in the min..max range.
|
||||
* @param obj the object on which snap point should be found
|
||||
* @param min ignore snap points smaller than this. (Absolute coordinate)
|
||||
* @param max ignore snap points greater than this. (Absolute coordinate)
|
||||
* @param ofs offset to snap points. Useful to get a snap point in an imagined case
|
||||
* what if children are already moved by this value
|
||||
* @return the absolute y coordinate of the nearest snap point
|
||||
* or `LV_COORD_MAX` if there is no snap point in the min..max range
|
||||
*/
|
||||
static int32_t find_snap_point_y(const lv_obj_t * obj, int32_t min, int32_t max, int32_t ofs)
|
||||
{
|
||||
lv_scroll_snap_t align = lv_obj_get_scroll_snap_y(obj);
|
||||
if(align == LV_SCROLL_SNAP_NONE) return LV_COORD_MAX;
|
||||
|
||||
int32_t dist = LV_COORD_MAX;
|
||||
|
||||
int32_t pad_top = lv_obj_get_style_pad_top(obj, LV_PART_MAIN);
|
||||
int32_t pad_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN);
|
||||
|
||||
uint32_t i;
|
||||
uint32_t child_cnt = lv_obj_get_child_count(obj);
|
||||
for(i = 0; i < child_cnt; i++) {
|
||||
lv_obj_t * child = obj->spec_attr->children[i];
|
||||
if(lv_obj_has_flag_any(child, LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
|
||||
if(lv_obj_has_flag(child, LV_OBJ_FLAG_SNAPPABLE)) {
|
||||
int32_t y_child = 0;
|
||||
int32_t y_parent = 0;
|
||||
switch(align) {
|
||||
case LV_SCROLL_SNAP_START:
|
||||
y_child = child->coords.y1;
|
||||
y_parent = obj->coords.y1 + pad_top;
|
||||
break;
|
||||
case LV_SCROLL_SNAP_END:
|
||||
y_child = child->coords.y2;
|
||||
y_parent = obj->coords.y2 - pad_bottom;
|
||||
break;
|
||||
case LV_SCROLL_SNAP_CENTER:
|
||||
y_child = child->coords.y1 + lv_area_get_height(&child->coords) / 2;
|
||||
y_parent = obj->coords.y1 + pad_top + (lv_area_get_height(&obj->coords) - pad_top - pad_bottom) / 2;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
y_child += ofs;
|
||||
if(y_child >= min && y_child <= max) {
|
||||
int32_t y = y_child - y_parent;
|
||||
if(LV_ABS(y) < LV_ABS(dist)) dist = y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dist == LV_COORD_MAX ? LV_COORD_MAX : -dist;
|
||||
}
|
||||
|
||||
static void scroll_limit_diff(lv_indev_t * indev, int32_t * diff_x, int32_t * diff_y)
|
||||
{
|
||||
if(diff_y) {
|
||||
if(indev->pointer.scroll_sum.y + *diff_y < indev->pointer.scroll_area.y1) {
|
||||
*diff_y = indev->pointer.scroll_area.y1 - indev->pointer.scroll_sum.y;
|
||||
}
|
||||
|
||||
if(indev->pointer.scroll_sum.y + *diff_y > indev->pointer.scroll_area.y2) {
|
||||
*diff_y = indev->pointer.scroll_area.y2 - indev->pointer.scroll_sum.y;
|
||||
}
|
||||
}
|
||||
|
||||
if(diff_x) {
|
||||
if(indev->pointer.scroll_sum.x + *diff_x < indev->pointer.scroll_area.x1) {
|
||||
*diff_x = indev->pointer.scroll_area.x1 - indev->pointer.scroll_sum.x;
|
||||
}
|
||||
|
||||
if(indev->pointer.scroll_sum.x + *diff_x > indev->pointer.scroll_area.x2) {
|
||||
*diff_x = indev->pointer.scroll_area.x2 - indev->pointer.scroll_sum.x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t elastic_diff(lv_obj_t * scroll_obj, int32_t diff, int32_t scroll_start, int32_t scroll_end,
|
||||
lv_dir_t dir)
|
||||
{
|
||||
if(diff == 0) return 0;
|
||||
|
||||
/*Scroll back to the edge if required*/
|
||||
if(!lv_obj_has_flag(scroll_obj, LV_OBJ_FLAG_SCROLL_ELASTIC)) {
|
||||
/*
|
||||
* If the scrolling object does not set the `LV_OBJ_FLAG_SCROLL_ELASTIC` flag,
|
||||
* make sure that `diff` will not cause the scroll to exceed the `start` or `end` boundary of the content.
|
||||
* If the content has exceeded the boundary due to external factors like `LV_SCROLL_SNAP_CENTER`,
|
||||
* then respect the current position instead of going straight back to 0.
|
||||
*/
|
||||
const int32_t scroll_ended = diff > 0 ? scroll_start : scroll_end;
|
||||
if(scroll_ended < 0) diff = 0;
|
||||
else if(scroll_ended - diff < 0) diff = scroll_ended;
|
||||
}
|
||||
/*Handle elastic scrolling*/
|
||||
else {
|
||||
|
||||
lv_scroll_snap_t snap;
|
||||
snap = dir == LV_DIR_HOR ? lv_obj_get_scroll_snap_x(scroll_obj) : lv_obj_get_scroll_snap_y(scroll_obj);
|
||||
|
||||
/*Without snapping just scale down the diff when scrolled out*/
|
||||
if(snap == LV_SCROLL_SNAP_NONE) {
|
||||
if(scroll_end < 0 || scroll_start < 0) {
|
||||
/*Rounding*/
|
||||
if(diff < 0) diff -= ELASTIC_SLOWNESS_FACTOR / 2;
|
||||
if(diff > 0) diff += ELASTIC_SLOWNESS_FACTOR / 2;
|
||||
return diff / ELASTIC_SLOWNESS_FACTOR;
|
||||
}
|
||||
else {
|
||||
return diff;
|
||||
}
|
||||
}
|
||||
|
||||
/*With snapping the widget is scrolled out if there are no more snap points
|
||||
*at least in one direction (start or end)*/
|
||||
bool has_start_snap;
|
||||
bool has_end_snap;
|
||||
has_more_snap_points(scroll_obj, dir, &has_start_snap, &has_end_snap);
|
||||
|
||||
if(!has_start_snap || !has_end_snap) {
|
||||
/*Rounding*/
|
||||
if(diff < 0) diff -= ELASTIC_SLOWNESS_FACTOR / 2;
|
||||
if(diff > 0) diff += ELASTIC_SLOWNESS_FACTOR / 2;
|
||||
return diff / ELASTIC_SLOWNESS_FACTOR;
|
||||
}
|
||||
else {
|
||||
return diff;
|
||||
}
|
||||
}
|
||||
|
||||
return diff;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell is there are more snap point in a given direction considering snap position.
|
||||
* There is a snap point if there is a snapanble object in the given direction
|
||||
* @param scroll_obj the object on which snap points should be found
|
||||
* @param dir LV_DIR_HOR or LV_DIR_VER
|
||||
* @param has_start_snap true: there is snap point in the start direction (top or left depending on dir)
|
||||
* @param has_end_snap true: there is snap point in the end direction (bottom or right depending on dir)
|
||||
* @note snap points will be searched relative to the
|
||||
* center point in case of LV_SCROLL_SNAP_CENTER
|
||||
* start point (top or left) in case of LV_SCROLL_SNAP_START
|
||||
* end point (bottom or right) in case of LV_SCROLL_SNAP_END
|
||||
*/
|
||||
static void has_more_snap_points(lv_obj_t * scroll_obj, lv_dir_t dir, bool * has_start_snap, bool * has_end_snap)
|
||||
{
|
||||
*has_start_snap = true;
|
||||
*has_end_snap = true;
|
||||
lv_scroll_snap_t snap;
|
||||
snap = dir == LV_DIR_HOR ? lv_obj_get_scroll_snap_x(scroll_obj) : lv_obj_get_scroll_snap_y(scroll_obj);
|
||||
|
||||
if(dir == LV_DIR_HOR) {
|
||||
int32_t x = 0;
|
||||
switch(snap) {
|
||||
case LV_SCROLL_SNAP_CENTER: {
|
||||
int32_t pad_left = lv_obj_get_style_pad_left(scroll_obj, 0);
|
||||
int32_t pad_right = lv_obj_get_style_pad_right(scroll_obj, 0);
|
||||
x = scroll_obj->coords.x1;
|
||||
x += (lv_area_get_width(&scroll_obj->coords) - pad_left - pad_right) / 2;
|
||||
x += pad_left;
|
||||
}
|
||||
break;
|
||||
case LV_SCROLL_SNAP_START:
|
||||
x = scroll_obj->coords.x1 + lv_obj_get_style_pad_left(scroll_obj, 0);
|
||||
break;
|
||||
case LV_SCROLL_SNAP_END:
|
||||
x = scroll_obj->coords.x2 - lv_obj_get_style_pad_right(scroll_obj, 0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
int32_t d;
|
||||
d = find_snap_point_x(scroll_obj, x + 1, LV_COORD_MAX, 0);
|
||||
if(d == LV_COORD_MAX) *has_end_snap = false;
|
||||
d = find_snap_point_x(scroll_obj, LV_COORD_MIN, x - 1, 0);
|
||||
if(d == LV_COORD_MAX) *has_start_snap = false;
|
||||
}
|
||||
else {
|
||||
int32_t y = 0;
|
||||
switch(snap) {
|
||||
case LV_SCROLL_SNAP_CENTER: {
|
||||
int32_t pad_top = lv_obj_get_style_pad_top(scroll_obj, 0);
|
||||
int32_t pad_bottom = lv_obj_get_style_pad_bottom(scroll_obj, 0);
|
||||
y = scroll_obj->coords.y1;
|
||||
y += (lv_area_get_height(&scroll_obj->coords) - pad_top - pad_bottom) / 2;
|
||||
y += pad_top;
|
||||
}
|
||||
break;
|
||||
case LV_SCROLL_SNAP_START:
|
||||
y = scroll_obj->coords.y1 + lv_obj_get_style_pad_top(scroll_obj, 0);
|
||||
break;
|
||||
case LV_SCROLL_SNAP_END:
|
||||
y = scroll_obj->coords.y2 - lv_obj_get_style_pad_bottom(scroll_obj, 0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
int32_t d;
|
||||
d = find_snap_point_y(scroll_obj, y + 1, LV_COORD_MAX, 0);
|
||||
if(d == LV_COORD_MAX) *has_end_snap = false;
|
||||
d = find_snap_point_y(scroll_obj, LV_COORD_MIN, y - 1, 0);
|
||||
if(d == LV_COORD_MAX) *has_start_snap = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* @file lv_indev_scroll.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_INDEV_SCROLL_H
|
||||
#define LV_INDEV_SCROLL_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "../core/lv_obj.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* Handle scrolling. Called by LVGL during input device processing
|
||||
* @param indev pointer to an input device
|
||||
*/
|
||||
void lv_indev_scroll_handler(lv_indev_t * indev);
|
||||
|
||||
/**
|
||||
* Handle throwing after scrolling. Called by LVGL during input device processing
|
||||
* @param indev pointer to an input device
|
||||
*/
|
||||
void lv_indev_scroll_throw_handler(lv_indev_t * indev);
|
||||
|
||||
/**
|
||||
* Predict where would a scroll throw end
|
||||
* @param indev pointer to an input device
|
||||
* @param dir ` LV_DIR_VER` or `LV_DIR_HOR`
|
||||
* @return the difference compared to the current position when the throw would be finished
|
||||
*/
|
||||
int32_t lv_indev_scroll_throw_predict(lv_indev_t * indev, lv_dir_t dir);
|
||||
|
||||
/**
|
||||
* Get the distance of the nearest snap point
|
||||
* @param obj the object on which snap points should be found
|
||||
* @param p save the distance of the found snap point there
|
||||
*/
|
||||
void lv_indev_scroll_get_snap_dist(lv_obj_t * obj, lv_point_t * p);
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_INDEV_SCROLL_H*/
|
||||
Reference in New Issue
Block a user