mirror of
https://github.com/lvgl/lvgl.git
synced 2026-06-02 17:47:28 +08:00
fix(arc): ignore hits that are outside drawn background arc (#6753)
This commit is contained in:
+12
-6
@@ -105,13 +105,19 @@ the object non-clickable:
|
|||||||
lv_obj_remove_style(arc, NULL, LV_PART_KNOB);
|
lv_obj_remove_style(arc, NULL, LV_PART_KNOB);
|
||||||
lv_obj_remove_flag(arc, LV_OBJ_FLAG_CLICKABLE);
|
lv_obj_remove_flag(arc, LV_OBJ_FLAG_CLICKABLE);
|
||||||
|
|
||||||
Advanced hit test
|
Interactive area
|
||||||
-----------------
|
----------------
|
||||||
|
|
||||||
If the :cpp:enumerator:`LV_OBJ_FLAG_ADV_HITTEST` flag is enabled the arc can be
|
By default :cpp:enumerator:`LV_OBJ_FLAG_ADV_HITTEST` is disabled which
|
||||||
clicked through in the middle. Clicks are recognized only on the ring of
|
means the arc's whole area is interactive.
|
||||||
the background arc. :cpp:func:`lv_obj_set_ext_click_size` makes the sensitive
|
As usual :cpp:func:`lv_obj_set_ext_click_size` can be used to increase
|
||||||
area larger inside and outside with the given number of pixels.
|
the sensitive area outside the arc by a specified number of pixels.
|
||||||
|
|
||||||
|
If :cpp:enumerator:`LV_OBJ_FLAG_ADV_HITTEST` is enabled the arc will be sensitive only
|
||||||
|
in the range of start and end background angles and on the arc itself (not inside the arc).
|
||||||
|
In this case ``ext_click_size`` makes the sensitive area ticker both inward and outward.
|
||||||
|
Additionally, a tolerance of :cpp:expr:`lv_dpx(50)` pixels is applied to each angle, extending the
|
||||||
|
hit-test range along the arc's length.
|
||||||
|
|
||||||
Place another object to the knob
|
Place another object to the knob
|
||||||
--------------------------------
|
--------------------------------
|
||||||
|
|||||||
@@ -199,6 +199,9 @@ void lv_arc_set_rotation(lv_obj_t * obj, int32_t rotation)
|
|||||||
LV_ASSERT_OBJ(obj, MY_CLASS);
|
LV_ASSERT_OBJ(obj, MY_CLASS);
|
||||||
lv_arc_t * arc = (lv_arc_t *)obj;
|
lv_arc_t * arc = (lv_arc_t *)obj;
|
||||||
|
|
||||||
|
/* ensure the angle is in the range [0, 360) */
|
||||||
|
while(rotation < 0) rotation += 360;
|
||||||
|
while(rotation >= 360) rotation -= 360;
|
||||||
arc->rotation = rotation;
|
arc->rotation = rotation;
|
||||||
|
|
||||||
lv_obj_invalidate(obj);
|
lv_obj_invalidate(obj);
|
||||||
@@ -506,8 +509,9 @@ static void lv_arc_event(const lv_obj_class_t * class_p, lv_event_t * e)
|
|||||||
angle -= arc->rotation;
|
angle -= arc->rotation;
|
||||||
angle -= arc->bg_angle_start; /*Make the angle relative to the start angle*/
|
angle -= arc->bg_angle_start; /*Make the angle relative to the start angle*/
|
||||||
|
|
||||||
/* If we click near the bg_angle_start the angle will be close to 360° instead of a small angle */
|
/* ensure the angle is in the range [0, 360) */
|
||||||
if(angle < 0) angle += 360;
|
while(angle < 0) angle += 360;
|
||||||
|
while(angle >= 360) angle -= 360;
|
||||||
|
|
||||||
const uint32_t circumference = (uint32_t)((2U * r * 314U) / 100U); /* Equivalent to: 2r * 3.14, avoiding floats */
|
const uint32_t circumference = (uint32_t)((2U * r * 314U) / 100U); /* Equivalent to: 2r * 3.14, avoiding floats */
|
||||||
const lv_value_precise_t tolerance_deg = (360 * lv_dpx(50U)) / circumference;
|
const lv_value_precise_t tolerance_deg = (360 * lv_dpx(50U)) / circumference;
|
||||||
@@ -655,6 +659,25 @@ static void lv_arc_event(const lv_obj_class_t * class_p, lv_event_t * e)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*Calculate the angle of the pressed point*/
|
||||||
|
lv_value_precise_t angle = lv_atan2(info->point->y - p.y, info->point->x - p.x);
|
||||||
|
angle -= arc->rotation;
|
||||||
|
angle -= arc->bg_angle_start; /*Make the angle relative to the start angle*/
|
||||||
|
|
||||||
|
/* ensure the angle is in the range [0, 360) */
|
||||||
|
while(angle < 0) angle += 360;
|
||||||
|
while(angle >= 360) angle -= 360;
|
||||||
|
|
||||||
|
const uint32_t circumference = (uint32_t)((2U * r * 314U) / 100U); /* Equivalent to: 2r * 3.14, avoiding floats */
|
||||||
|
const lv_value_precise_t tolerance_deg = (360 * lv_dpx(50U)) / circumference;
|
||||||
|
|
||||||
|
/* Check if the angle is outside the drawn background arc */
|
||||||
|
const bool is_angle_within_bg_bounds = lv_arc_angle_within_bg_bounds(obj, angle, tolerance_deg);
|
||||||
|
if(!is_angle_within_bg_bounds) {
|
||||||
|
info->res = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/*Valid if no clicked outside*/
|
/*Valid if no clicked outside*/
|
||||||
lv_area_increase(&a, w + ext_click_area * 2, w + ext_click_area * 2);
|
lv_area_increase(&a, w + ext_click_area * 2, w + ext_click_area * 2);
|
||||||
info->res = lv_area_is_point_on(&a, info->point, LV_RADIUS_CIRCLE);
|
info->res = lv_area_is_point_on(&a, info->point, LV_RADIUS_CIRCLE);
|
||||||
@@ -941,7 +964,10 @@ static bool lv_arc_angle_within_bg_bounds(lv_obj_t * obj, const lv_value_precise
|
|||||||
lv_arc_t * arc = (lv_arc_t *)obj;
|
lv_arc_t * arc = (lv_arc_t *)obj;
|
||||||
|
|
||||||
lv_value_precise_t bounds_angle = arc->bg_angle_end - arc->bg_angle_start;
|
lv_value_precise_t bounds_angle = arc->bg_angle_end - arc->bg_angle_start;
|
||||||
if(bounds_angle < 0) bounds_angle += 360;
|
|
||||||
|
/* ensure the angle is in the range [0, 360) */
|
||||||
|
while(bounds_angle < 0) bounds_angle += 360;
|
||||||
|
while(bounds_angle >= 360) bounds_angle -= 360;
|
||||||
|
|
||||||
/* Angle is in the bounds */
|
/* Angle is in the bounds */
|
||||||
if(angle <= bounds_angle) {
|
if(angle <= bounds_angle) {
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 5.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 5.9 KiB |
@@ -18,9 +18,12 @@ void test_arc_angles_when_reversed(void);
|
|||||||
|
|
||||||
static lv_obj_t * active_screen = NULL;
|
static lv_obj_t * active_screen = NULL;
|
||||||
static lv_obj_t * arc = NULL;
|
static lv_obj_t * arc = NULL;
|
||||||
|
static lv_obj_t * arc2 = NULL;
|
||||||
static uint32_t event_cnt;
|
static uint32_t event_cnt;
|
||||||
|
static uint32_t event_cnt2;
|
||||||
|
|
||||||
static void dummy_event_cb(lv_event_t * e);
|
static void dummy_event_cb(lv_event_t * e);
|
||||||
|
static void dummy_event_cb2(lv_event_t * e);
|
||||||
|
|
||||||
void setUp(void)
|
void setUp(void)
|
||||||
{
|
{
|
||||||
@@ -232,10 +235,71 @@ void test_arc_click_sustained_from_start_to_end_does_not_set_value_to_max(void)
|
|||||||
TEST_ASSERT_EQUAL_SCREENSHOT("widgets/arc_3.png");
|
TEST_ASSERT_EQUAL_SCREENSHOT("widgets/arc_3.png");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_two_overlapping_arcs_can_be_interacted_independently(void)
|
||||||
|
{
|
||||||
|
arc = lv_arc_create(lv_screen_active());
|
||||||
|
arc2 = lv_arc_create(lv_screen_active());
|
||||||
|
|
||||||
|
lv_arc_set_value(arc, 0);
|
||||||
|
lv_arc_set_value(arc2, 0);
|
||||||
|
lv_obj_set_size(arc, 100, 100);
|
||||||
|
lv_obj_set_size(arc2, 100, 100);
|
||||||
|
lv_arc_set_bg_angles(arc, 20, 160);
|
||||||
|
lv_arc_set_bg_angles(arc2, 200, 340);
|
||||||
|
lv_obj_add_flag(arc, LV_OBJ_FLAG_ADV_HITTEST);
|
||||||
|
lv_obj_add_flag(arc2, LV_OBJ_FLAG_ADV_HITTEST);
|
||||||
|
lv_arc_set_value(arc, 10);
|
||||||
|
lv_arc_set_value(arc2, 10);
|
||||||
|
lv_arc_set_rotation(arc, 355);
|
||||||
|
lv_arc_set_rotation(arc2, 355);
|
||||||
|
lv_obj_center(arc);
|
||||||
|
lv_obj_center(arc2);
|
||||||
|
|
||||||
|
// Add event callback to both arcs
|
||||||
|
lv_obj_add_event_cb(arc, dummy_event_cb, LV_EVENT_PRESSED, NULL);
|
||||||
|
lv_obj_add_event_cb(arc2, dummy_event_cb2, LV_EVENT_PRESSED, NULL);
|
||||||
|
|
||||||
|
// Reset event counters
|
||||||
|
event_cnt = 0;
|
||||||
|
event_cnt2 = 0;
|
||||||
|
|
||||||
|
// Click on the position of the first arc (center)
|
||||||
|
lv_test_mouse_move_to(400, 195);
|
||||||
|
lv_test_mouse_press();
|
||||||
|
lv_test_indev_wait(500);
|
||||||
|
lv_test_mouse_release();
|
||||||
|
lv_test_indev_wait(50);
|
||||||
|
|
||||||
|
// Verify that the event callback was called for the first arc
|
||||||
|
TEST_ASSERT_EQUAL_UINT32(0, event_cnt);
|
||||||
|
TEST_ASSERT_EQUAL_UINT32(1, event_cnt2);
|
||||||
|
|
||||||
|
// click on the position of the second arc (center)
|
||||||
|
lv_test_mouse_move_to(400, 285);
|
||||||
|
lv_test_mouse_press();
|
||||||
|
lv_test_indev_wait(500);
|
||||||
|
lv_test_mouse_release();
|
||||||
|
lv_test_indev_wait(50);
|
||||||
|
|
||||||
|
// Verify that the event callback was called for the second arc
|
||||||
|
TEST_ASSERT_EQUAL_UINT32(1, event_cnt);
|
||||||
|
TEST_ASSERT_EQUAL_UINT32(1, event_cnt2);
|
||||||
|
|
||||||
|
// Verify that the screen remains as expected after the interactions
|
||||||
|
TEST_ASSERT_EQUAL_SCREENSHOT("widgets/overlapping_arcs_test.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void dummy_event_cb(lv_event_t * e)
|
static void dummy_event_cb(lv_event_t * e)
|
||||||
{
|
{
|
||||||
LV_UNUSED(e);
|
LV_UNUSED(e);
|
||||||
event_cnt++;
|
event_cnt++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void dummy_event_cb2(lv_event_t * e)
|
||||||
|
{
|
||||||
|
LV_UNUSED(e);
|
||||||
|
event_cnt2++;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user