From e4e1b26d1d29cd59f7f10acd2c57b48e81edbd68 Mon Sep 17 00:00:00 2001 From: Luca Vanesche Date: Tue, 3 Sep 2024 11:55:55 +0200 Subject: [PATCH] fix(arc): ignore hits that are outside drawn background arc (#6753) --- docs/widgets/arc.rst | 18 +++-- src/widgets/arc/lv_arc.c | 32 ++++++++- .../widgets/overlapping_arcs_test.png | Bin 0 -> 5566 bytes .../widgets/overlapping_arcs_test.png | Bin 0 -> 6028 bytes tests/src/test_cases/widgets/test_arc.c | 64 ++++++++++++++++++ 5 files changed, 105 insertions(+), 9 deletions(-) create mode 100644 tests/ref_imgs/widgets/overlapping_arcs_test.png create mode 100644 tests/ref_imgs_vg_lite/widgets/overlapping_arcs_test.png diff --git a/docs/widgets/arc.rst b/docs/widgets/arc.rst index a9c923bb5..ffadf0106 100644 --- a/docs/widgets/arc.rst +++ b/docs/widgets/arc.rst @@ -105,13 +105,19 @@ the object non-clickable: lv_obj_remove_style(arc, NULL, LV_PART_KNOB); 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 -clicked through in the middle. Clicks are recognized only on the ring of -the background arc. :cpp:func:`lv_obj_set_ext_click_size` makes the sensitive -area larger inside and outside with the given number of pixels. +By default :cpp:enumerator:`LV_OBJ_FLAG_ADV_HITTEST` is disabled which +means the arc's whole area is interactive. +As usual :cpp:func:`lv_obj_set_ext_click_size` can be used to increase +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 -------------------------------- diff --git a/src/widgets/arc/lv_arc.c b/src/widgets/arc/lv_arc.c index 7e4315c29..6aa14840d 100644 --- a/src/widgets/arc/lv_arc.c +++ b/src/widgets/arc/lv_arc.c @@ -199,6 +199,9 @@ void lv_arc_set_rotation(lv_obj_t * obj, int32_t rotation) LV_ASSERT_OBJ(obj, MY_CLASS); 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; 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->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 */ - if(angle < 0) angle += 360; + /* 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; @@ -655,6 +659,25 @@ static void lv_arc_event(const lv_obj_class_t * class_p, lv_event_t * e) 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*/ 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); @@ -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_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 */ if(angle <= bounds_angle) { diff --git a/tests/ref_imgs/widgets/overlapping_arcs_test.png b/tests/ref_imgs/widgets/overlapping_arcs_test.png new file mode 100644 index 0000000000000000000000000000000000000000..878585780d3c59e06efcd591875f63cd07d20940 GIT binary patch literal 5566 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