diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e4b928be..b9e472b24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ # Changelog ## v7.4.0 (planned on 01.09.2020) -*Available in the `dev` branch* +- arc: add set value by click feature ## v7.3.1 (planned on 18.08.2020) diff --git a/lv_conf_template.h b/lv_conf_template.h index 4032f5ab0..1af97a865 100644 --- a/lv_conf_template.h +++ b/lv_conf_template.h @@ -687,6 +687,9 @@ typedef void * lv_obj_user_data_t; # define LV_ROLLER_INF_PAGES 7 #endif +/*Rotary (dependencies: lv_arc, lv_btn)*/ +#define LV_USE_ROTARY 1 + /*Slider (dependencies: lv_bar)*/ #define LV_USE_SLIDER 1 diff --git a/src/lv_conf_internal.h b/src/lv_conf_internal.h index d43134241..8a031535f 100644 --- a/src/lv_conf_internal.h +++ b/src/lv_conf_internal.h @@ -1047,6 +1047,10 @@ e.g. "stm32f769xx.h" or "stm32f429xx.h" */ #endif #endif +/*Rotary (dependencies: lv_arc, lv_btn)*/ +#ifndef LV_USE_ROTARY +#define LV_USE_ROTARY 1 +#endif /*Slider (dependencies: lv_bar)*/ #ifndef LV_USE_SLIDER #define LV_USE_SLIDER 1 diff --git a/src/lv_core/lv_disp.h b/src/lv_core/lv_disp.h index a0757accf..888a0c093 100644 --- a/src/lv_core/lv_disp.h +++ b/src/lv_core/lv_disp.h @@ -209,7 +209,7 @@ static inline void lv_scr_load(lv_obj_t * scr) * 1 dip is 2 px on a 320 DPI screen * https://stackoverflow.com/questions/2025282/what-is-the-difference-between-px-dip-dp-and-sp */ -#define LV_DPX(n) LV_MATH_MAX((( lv_disp_get_dpi(NULL) * (n) + 80) / 160), 1) /*+80 for rounding*/ +#define LV_DPX(n) (n == 0 ? 0 :LV_MATH_MAX((( lv_disp_get_dpi(NULL) * (n) + 80) / 160), 1)) /*+80 for rounding*/ static inline lv_coord_t lv_dpx(lv_coord_t n) { diff --git a/src/lv_core/lv_obj.h b/src/lv_core/lv_obj.h index 5d10eb3bc..aa11b2e79 100644 --- a/src/lv_core/lv_obj.h +++ b/src/lv_core/lv_obj.h @@ -105,6 +105,7 @@ enum { LV_EVENT_APPLY, /**< "Ok", "Apply" or similar specific button has clicked*/ LV_EVENT_CANCEL, /**< "Close", "Cancel" or similar specific button has clicked*/ LV_EVENT_DELETE, /**< Object is being deleted */ + _LV_EVENT_LAST /** Number of events*/ }; typedef uint8_t lv_event_t; /**< Type of event being sent to the object. */ diff --git a/src/lv_misc/lv_math.c b/src/lv_misc/lv_math.c index 230d79990..b0ecfaad7 100644 --- a/src/lv_misc/lv_math.c +++ b/src/lv_misc/lv_math.c @@ -128,7 +128,6 @@ LV_ATTRIBUTE_FAST_MEM void _lv_sqrt(uint32_t x, lv_sqrt_res_t * q, uint32_t mask q->f = (uint32_t)(root & 0xf) << 4; } - /** * Calculate the atan2 of a vector. * @param x @@ -232,6 +231,32 @@ int64_t _lv_pow(int64_t base, int8_t exp) return result; } +/** + * Get the mapped of a number given an input and output range + * @param x integer which mapped value should be calculated + * @param min_in min input range + * @param max_in max input range + * @param min_out max output range + * @param max_out max output range + * @return the mapped number + */ +int16_t _lv_map(int32_t x, int32_t min_in, int32_t max_in, int32_t min_out, int32_t max_out) +{ + if(x <= min_in) return min_out; + if(x >= max_in) return max_out; + + /* The equation should be: + * ((x - min_in) / delta in) * delta_out + min_out + * To avoid rounding error reorder the operations: + * (((x - min_in) * delta_out) / delta in) + min_out + */ + + int32_t delta_in = max_in - min_in; + int32_t delta_out = max_out - min_out; + + return ((x - min_in) * delta_out) / delta_in + min_out; +} + /********************** * STATIC FUNCTIONS **********************/ diff --git a/src/lv_misc/lv_math.h b/src/lv_misc/lv_math.h index 499176244..d4d626ef9 100644 --- a/src/lv_misc/lv_math.h +++ b/src/lv_misc/lv_math.h @@ -112,6 +112,17 @@ LV_ATTRIBUTE_FAST_MEM void _lv_sqrt(uint32_t x, lv_sqrt_res_t * q, uint32_t mask */ int64_t _lv_pow(int64_t base, int8_t exp); +/** + * Get the mapped of a number given an input and output range + * @param x integer which mapped value should be calculated + * @param min_in min input range + * @param max_in max input range + * @param min_out max output range + * @param max_out max output range + * @return the mapped number + */ +int16_t _lv_map(int32_t x, int32_t min_in, int32_t max_in, int32_t min, int32_t max); + /********************** * MACROS **********************/ diff --git a/src/lv_themes/lv_theme.h b/src/lv_themes/lv_theme.h index 8297b0b53..3647f48f0 100644 --- a/src/lv_themes/lv_theme.h +++ b/src/lv_themes/lv_theme.h @@ -109,6 +109,9 @@ typedef enum { #if LV_USE_ROLLER LV_THEME_ROLLER, #endif +#if LV_USE_ROTARY + LV_THEME_ROTARY, +#endif #if LV_USE_SLIDER LV_THEME_SLIDER, #endif diff --git a/src/lv_themes/lv_theme_material.c b/src/lv_themes/lv_theme_material.c index f692b3b66..e074fb30d 100644 --- a/src/lv_themes/lv_theme_material.c +++ b/src/lv_themes/lv_theme_material.c @@ -86,6 +86,7 @@ typedef struct { #if LV_USE_ARC lv_style_t arc_indic; lv_style_t arc_bg; + lv_style_t arc_knob; #endif #if LV_USE_BAR @@ -505,6 +506,14 @@ static void arc_init(void) lv_style_set_line_color(&styles->arc_bg, LV_STATE_DEFAULT, COLOR_BG_SEC); lv_style_set_line_width(&styles->arc_bg, LV_STATE_DEFAULT, LV_DPX(25)); lv_style_set_line_rounded(&styles->arc_bg, LV_STATE_DEFAULT, true); + + style_init_reset(&styles->arc_knob); + lv_style_set_radius(&styles->arc_knob, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE); + lv_style_set_pad_top(&styles->arc_knob, LV_STATE_DEFAULT, LV_DPX(0)); + lv_style_set_pad_bottom(&styles->arc_knob, LV_STATE_DEFAULT, LV_DPX(0)); + lv_style_set_pad_left(&styles->arc_knob, LV_STATE_DEFAULT, LV_DPX(0)); + lv_style_set_pad_right(&styles->arc_knob, LV_STATE_DEFAULT, LV_DPX(0)); + #endif } @@ -1062,6 +1071,11 @@ static void theme_apply(lv_theme_t * th, lv_obj_t * obj, lv_theme_style_t name) list = lv_obj_get_style_list(obj, LV_ARC_PART_INDIC); _lv_style_list_add_style(list, &styles->arc_indic); + + list = lv_obj_get_style_list(obj, LV_ARC_PART_KNOB); + _lv_style_list_add_style(list, &styles->bg); + _lv_style_list_add_style(list, &styles->bg_click); + _lv_style_list_add_style(list, &styles->arc_knob); break; #endif @@ -1188,7 +1202,6 @@ static void theme_apply(lv_theme_t * th, lv_obj_t * obj, lv_theme_style_t name) break; #endif - #if LV_USE_OBJMASK case LV_THEME_OBJMASK: list = lv_obj_get_style_list(obj, LV_OBJMASK_PART_MAIN); diff --git a/src/lv_widgets/lv_arc.c b/src/lv_widgets/lv_arc.c index 4dd7d9f45..fad51e7a5 100644 --- a/src/lv_widgets/lv_arc.c +++ b/src/lv_widgets/lv_arc.c @@ -9,6 +9,8 @@ #include "lv_arc.h" #if LV_USE_ARC != 0 +#include "../lv_core/lv_group.h" +#include "../lv_core/lv_indev.h" #include "../lv_misc/lv_debug.h" #include "../lv_misc/lv_math.h" #include "../lv_draw/lv_draw_arc.h" @@ -29,7 +31,9 @@ static lv_design_res_t lv_arc_design(lv_obj_t * arc, const lv_area_t * clip_area, lv_design_mode_t mode); static lv_res_t lv_arc_signal(lv_obj_t * arc, lv_signal_t sign, void * param); static lv_style_list_t * lv_arc_get_style(lv_obj_t * arc, uint8_t part); -static void inv_arc_area(lv_obj_t * arc, uint16_t start_angle, uint16_t end_angle); +static void inv_arc_area(lv_obj_t * arc, uint16_t start_angle, uint16_t end_angle, lv_arc_part_t part); +static void get_center(lv_obj_t * arc, lv_point_t * center, lv_coord_t * arc_r); +static void get_knob_area(lv_obj_t * arc, const lv_point_t * center, lv_coord_t r, lv_area_t * knob_area); /********************** * STATIC VARIABLES @@ -78,7 +82,16 @@ lv_obj_t * lv_arc_create(lv_obj_t * par, const lv_obj_t * copy) ext->bg_angle_end = 45; ext->arc_angle_start = 135; ext->arc_angle_end = 270; + ext->type = LV_ARC_TYPE_NORMAL; + ext->cur_value = -1; + ext->min_value = 0; + ext->max_value = 100; + ext->dragging = false; + ext->chg_rate = 540; + ext->last_tick = lv_tick_get(); + ext->last_angle = ext->arc_angle_end; lv_style_list_init(&ext->style_arc); + lv_style_list_init(&ext->style_knob); lv_obj_set_size(arc, LV_DPI, LV_DPI); @@ -88,6 +101,10 @@ lv_obj_t * lv_arc_create(lv_obj_t * par, const lv_obj_t * copy) /*Init the new arc arc*/ if(copy == NULL) { + lv_obj_set_click(arc, true); + lv_obj_add_protect(arc, LV_PROTECT_PRESS_LOST); + lv_obj_set_ext_click_area(arc, LV_DPI / 10, LV_DPI / 10, LV_DPI / 10, LV_DPI / 10); + lv_arc_set_value(arc, ext->min_value); lv_theme_apply(arc, LV_THEME_ARC); } /*Copy an existing arc*/ @@ -97,7 +114,15 @@ lv_obj_t * lv_arc_create(lv_obj_t * par, const lv_obj_t * copy) ext->arc_angle_end = copy_ext->arc_angle_end; ext->bg_angle_start = copy_ext->bg_angle_start; ext->bg_angle_end = copy_ext->bg_angle_end; - + ext->type = copy_ext->type; + ext->cur_value = copy_ext->cur_value; + ext->min_value = copy_ext->min_value; + ext->max_value = copy_ext->max_value; + ext->dragging = copy_ext->dragging; + ext->chg_rate = copy_ext->chg_rate; + ext->last_tick = copy_ext->last_tick; + ext->last_angle = copy_ext->last_angle; + lv_style_list_copy(&ext->style_knob, ©_ext->style_knob); lv_style_list_copy(&ext->style_arc, ©_ext->style_arc); /*Refresh the style with new signal function*/ @@ -140,11 +165,11 @@ void lv_arc_set_start_angle(lv_obj_t * arc, uint16_t start) } /*Only a smaller incremental move*/ else if(ext->arc_angle_start > ext->arc_angle_end && start > ext->arc_angle_end) { - inv_arc_area(arc, LV_MATH_MIN(ext->arc_angle_start, start), LV_MATH_MAX(ext->arc_angle_start, start)); + inv_arc_area(arc, LV_MATH_MIN(ext->arc_angle_start, start), LV_MATH_MAX(ext->arc_angle_start, start), LV_ARC_PART_INDIC); } /*Only a smaller incremental move*/ else if(ext->arc_angle_start < ext->arc_angle_end && start < ext->arc_angle_end) { - inv_arc_area(arc, LV_MATH_MIN(ext->arc_angle_start, start), LV_MATH_MAX(ext->arc_angle_start, start)); + inv_arc_area(arc, LV_MATH_MIN(ext->arc_angle_start, start), LV_MATH_MAX(ext->arc_angle_start, start), LV_ARC_PART_INDIC); } /*Crossing the start angle makes the whole arc change*/ else { @@ -173,11 +198,11 @@ void lv_arc_set_end_angle(lv_obj_t * arc, uint16_t end) } /*Only a smaller incremental move*/ else if(ext->arc_angle_end > ext->arc_angle_start && end > ext->arc_angle_start) { - inv_arc_area(arc, LV_MATH_MIN(ext->arc_angle_end, end), LV_MATH_MAX(ext->arc_angle_end, end)); + inv_arc_area(arc, LV_MATH_MIN(ext->arc_angle_end, end), LV_MATH_MAX(ext->arc_angle_end, end), LV_ARC_PART_INDIC); } /*Only a smaller incremental move*/ else if(ext->arc_angle_end < ext->arc_angle_start && end < ext->arc_angle_start) { - inv_arc_area(arc, LV_MATH_MIN(ext->arc_angle_end, end), LV_MATH_MAX(ext->arc_angle_end, end)); + inv_arc_area(arc, LV_MATH_MIN(ext->arc_angle_end, end), LV_MATH_MAX(ext->arc_angle_end, end), LV_ARC_PART_INDIC); } /*Crossing the end angle makes the whole arc change*/ else { @@ -203,12 +228,12 @@ void lv_arc_set_angles(lv_obj_t * arc, uint16_t start, uint16_t end) if(start > 360) start -= 360; if(end > (start + 360)) end = start + 360; - inv_arc_area(arc, ext->arc_angle_start, ext->arc_angle_end); + inv_arc_area(arc, ext->arc_angle_start, ext->arc_angle_end, LV_ARC_PART_INDIC); ext->arc_angle_start = start; ext->arc_angle_end = end; - inv_arc_area(arc, ext->arc_angle_start, ext->arc_angle_end); + inv_arc_area(arc, ext->arc_angle_start, ext->arc_angle_end, LV_ARC_PART_INDIC); } /** @@ -230,11 +255,11 @@ void lv_arc_set_bg_start_angle(lv_obj_t * arc, uint16_t start) } /*Only a smaller incremental move*/ else if(ext->bg_angle_start > ext->bg_angle_end && start > ext->bg_angle_end) { - inv_arc_area(arc, LV_MATH_MIN(ext->bg_angle_start, start), LV_MATH_MAX(ext->bg_angle_start, start)); + inv_arc_area(arc, LV_MATH_MIN(ext->bg_angle_start, start), LV_MATH_MAX(ext->bg_angle_start, start), LV_ARC_PART_BG); } /*Only a smaller incremental move*/ else if(ext->bg_angle_start < ext->bg_angle_end && start < ext->bg_angle_end) { - inv_arc_area(arc, LV_MATH_MIN(ext->bg_angle_start, start), LV_MATH_MAX(ext->bg_angle_start, start)); + inv_arc_area(arc, LV_MATH_MIN(ext->bg_angle_start, start), LV_MATH_MAX(ext->bg_angle_start, start), LV_ARC_PART_BG); } /*Crossing the start angle makes the whole arc change*/ else { @@ -263,11 +288,11 @@ void lv_arc_set_bg_end_angle(lv_obj_t * arc, uint16_t end) } /*Only a smaller incremental move*/ else if(ext->bg_angle_end > ext->bg_angle_start && end > ext->bg_angle_start) { - inv_arc_area(arc, LV_MATH_MIN(ext->bg_angle_end, end), LV_MATH_MAX(ext->bg_angle_end, end)); + inv_arc_area(arc, LV_MATH_MIN(ext->bg_angle_end, end), LV_MATH_MAX(ext->bg_angle_end, end), LV_ARC_PART_BG); } /*Only a smaller incremental move*/ else if(ext->bg_angle_end < ext->bg_angle_start && end < ext->bg_angle_start) { - inv_arc_area(arc, LV_MATH_MIN(ext->bg_angle_end, end), LV_MATH_MAX(ext->bg_angle_end, end)); + inv_arc_area(arc, LV_MATH_MIN(ext->bg_angle_end, end), LV_MATH_MAX(ext->bg_angle_end, end), LV_ARC_PART_BG); } /*Crossing the end angle makes the whole arc change*/ else { @@ -292,12 +317,12 @@ void lv_arc_set_bg_angles(lv_obj_t * arc, uint16_t start, uint16_t end) if(start > 360) start -= 360; if(end > (start + 360)) end = start + 360; - inv_arc_area(arc, ext->bg_angle_start, ext->bg_angle_end); + inv_arc_area(arc, ext->bg_angle_start, ext->bg_angle_end, LV_ARC_PART_BG); ext->bg_angle_start = start; ext->bg_angle_end = end; - inv_arc_area(arc, ext->bg_angle_start, ext->bg_angle_end); + inv_arc_area(arc, ext->bg_angle_start, ext->bg_angle_end, LV_ARC_PART_BG); } /** @@ -316,6 +341,131 @@ void lv_arc_set_rotation(lv_obj_t * arc, uint16_t rotation_angle) lv_obj_invalidate(arc); } + +/** + * Set the type of arc. + * @param arc pointer to arc object + * @param type arc type + */ +void lv_arc_set_type(lv_obj_t * arc, lv_arc_type_t type) +{ + LV_ASSERT_OBJ(arc, LV_OBJX_NAME); + + lv_arc_ext_t *ext = (lv_arc_ext_t *)lv_obj_get_ext_attr(arc); + int16_t val = ext->cur_value; + + ext->type = type; + ext->cur_value = -1; /** Force set_value handling*/ + + int16_t bg_midpoint, bg_end = ext->bg_angle_end; + if (ext->bg_angle_end < ext->bg_angle_start) bg_end = ext->bg_angle_end + 360; + + switch(ext->type) { + case LV_ARC_TYPE_SYMMETRIC: + bg_midpoint = (ext->bg_angle_start + bg_end) / 2; + lv_arc_set_start_angle(arc, bg_midpoint); + lv_arc_set_end_angle(arc, bg_midpoint); + break; + case LV_ARC_TYPE_REVERSE: + lv_arc_set_end_angle(arc, ext->bg_angle_end); + break; + default: /** LV_ARC_TYPE_NORMAL*/ + lv_arc_set_start_angle(arc, ext->bg_angle_start); + } + + lv_arc_set_value(arc, val); +} + +/** + * Set a new value on the arc + * @param arc pointer to a arc object + * @param value new value + */ +void lv_arc_set_value(lv_obj_t * arc, int16_t value) +{ + LV_ASSERT_OBJ(arc, LV_OBJX_NAME); + + lv_arc_ext_t * ext = (lv_arc_ext_t *)lv_obj_get_ext_attr(arc); + if(ext->cur_value == value) return; + + int16_t new_value; + new_value = value > ext->max_value ? ext->max_value : value; + new_value = new_value < ext->min_value ? ext->min_value : new_value; + + if(ext->cur_value == new_value) return; + ext->cur_value = new_value; + + int16_t bg_midpoint, range_midpoint, bg_end = ext->bg_angle_end; + if (ext->bg_angle_end < ext->bg_angle_start) bg_end = ext->bg_angle_end + 360; + + int16_t angle; + switch(ext->type) { + case LV_ARC_TYPE_SYMMETRIC: + bg_midpoint = (ext->bg_angle_start + bg_end) / 2; + range_midpoint = (int32_t)(ext->min_value + ext->max_value) / 2; + + if (ext->cur_value < range_midpoint) { + angle = _lv_map(ext->cur_value, ext->min_value, range_midpoint, ext->bg_angle_start, bg_midpoint); + lv_arc_set_start_angle(arc, angle); + lv_arc_set_end_angle(arc, bg_midpoint); + } else { + angle = _lv_map(ext->cur_value, range_midpoint, ext->max_value, bg_midpoint, bg_end); + lv_arc_set_start_angle(arc, bg_midpoint); + lv_arc_set_end_angle(arc, angle); + } + break; + case LV_ARC_TYPE_REVERSE: + angle = _lv_map(ext->cur_value, ext->min_value, ext->max_value, ext->bg_angle_start, bg_end); + lv_arc_set_start_angle(arc, angle); + break; + default: /** LV_ARC_TYPE_NORMAL*/ + angle = _lv_map(ext->cur_value, ext->min_value, ext->max_value, ext->bg_angle_start, bg_end); + lv_arc_set_end_angle(arc, angle); + } + ext->last_angle = angle; /*Cache angle for slew rate limiting*/ +} + +/** + * Set minimum and the maximum values of a arc + * @param arc pointer to the arc object + * @param min minimum value + * @param max maximum value + */ +void lv_arc_set_range(lv_obj_t * arc, int16_t min, int16_t max) +{ + LV_ASSERT_OBJ(arc, LV_OBJX_NAME); + + lv_arc_ext_t * ext = (lv_arc_ext_t *)lv_obj_get_ext_attr(arc); + if(ext->min_value == min && ext->max_value == max) return; + + ext->min_value = min; + ext->max_value = max; + + if(ext->cur_value < min) { + ext->cur_value = min; + } + if(ext->cur_value > max) { + ext->cur_value = max; + } + + lv_arc_set_value(arc, ext->cur_value); +} + +/** + * Set the threshold of arc knob increments + * position. + * @param arc pointer to a arc object + * @param threshold increment threshold + */ +void lv_arc_set_chg_rate(lv_obj_t * arc, uint16_t rate) +{ + LV_ASSERT_OBJ(arc, LV_OBJX_NAME); + + lv_arc_ext_t *ext = (lv_arc_ext_t *)lv_obj_get_ext_attr(arc); + ext->chg_rate = rate; +} + + /*===================== * Getter functions *====================*/ @@ -376,6 +526,72 @@ uint16_t lv_arc_get_bg_angle_end(lv_obj_t * arc) return ext->bg_angle_end; } + +/** + * Get the value of a arc + * @param arc pointer to a arc object + * @return the value of the arc + */ +int16_t lv_arc_get_value(const lv_obj_t * arc) +{ + LV_ASSERT_OBJ(arc, LV_OBJX_NAME); + + lv_arc_ext_t * ext = lv_obj_get_ext_attr(arc); + return ext->cur_value; +} + +/** + * Get the minimum value of a arc + * @param arc pointer to a arc object + * @return the minimum value of the arc + */ +int16_t lv_arc_get_min_value(const lv_obj_t * arc) +{ + LV_ASSERT_OBJ(arc, LV_OBJX_NAME); + + lv_arc_ext_t *ext = (lv_arc_ext_t *)lv_obj_get_ext_attr(arc); + return ext->min_value; +} + +/** + * Get the maximum value of a arc + * @param arc pointer to a arc object + * @return the maximum value of the arc + */ +int16_t lv_arc_get_max_value(const lv_obj_t * arc) +{ + LV_ASSERT_OBJ(arc, LV_OBJX_NAME); + + lv_arc_ext_t *ext = (lv_arc_ext_t *)lv_obj_get_ext_attr(arc); + return ext->max_value; +} + +/** + * Give the arc is being dragged or not + * @param arc pointer to a arc object + * @return true: drag in progress false: not dragged + */ +bool lv_arc_is_dragged(const lv_obj_t * arc) +{ + LV_ASSERT_OBJ(arc, LV_OBJX_NAME); + + lv_arc_ext_t * ext = lv_obj_get_ext_attr(arc); + return ext->dragging; +} + +/** + * Get whether the arc is type or not. + * @param arc pointer to a arc object + * @return arc type + */ +lv_arc_type_t lv_arc_get_type(const lv_obj_t * arc) +{ + LV_ASSERT_OBJ(arc, LV_OBJX_NAME); + + lv_arc_ext_t * ext = (lv_arc_ext_t *)lv_obj_get_ext_attr(arc); + return ext->type; +} + /*===================== * Other functions *====================*/ @@ -414,21 +630,17 @@ static lv_design_res_t lv_arc_design(lv_obj_t * arc, const lv_area_t * clip_area lv_draw_rect(&arc->coords, clip_area, &bg_dsc); - lv_coord_t left_bg = lv_obj_get_style_pad_left(arc, LV_ARC_PART_BG); - lv_coord_t right_bg = lv_obj_get_style_pad_right(arc, LV_ARC_PART_BG); - lv_coord_t top_bg = lv_obj_get_style_pad_top(arc, LV_ARC_PART_BG); - lv_coord_t bottom_bg = lv_obj_get_style_pad_bottom(arc, LV_ARC_PART_BG); - lv_coord_t r = (LV_MATH_MIN(lv_obj_get_width(arc) - left_bg - right_bg, - lv_obj_get_height(arc) - top_bg - bottom_bg)) / 2; - lv_draw_line_dsc_t arc_dsc; - lv_coord_t x = arc->coords.x1 + r + left_bg; - lv_coord_t y = arc->coords.y1 + r + top_bg; + lv_point_t center; + lv_coord_t arc_r; + get_center(arc, ¢er, &arc_r); - if(r > 0) { + /*Draw the background arc*/ + lv_draw_line_dsc_t arc_dsc; + if(arc_r > 0) { lv_draw_line_dsc_init(&arc_dsc); lv_obj_init_draw_line_dsc(arc, LV_ARC_PART_BG, &arc_dsc); - lv_draw_arc(x, y, r, ext->bg_angle_start + ext->rotation_angle, ext->bg_angle_end + ext->rotation_angle, clip_area, + lv_draw_arc(center.x, center.y, arc_r, ext->bg_angle_start + ext->rotation_angle, ext->bg_angle_end + ext->rotation_angle, clip_area, &arc_dsc); } @@ -438,15 +650,25 @@ static lv_design_res_t lv_arc_design(lv_obj_t * arc, const lv_area_t * clip_area lv_coord_t right_indic = lv_obj_get_style_pad_right(arc, LV_ARC_PART_INDIC); lv_coord_t top_indic = lv_obj_get_style_pad_top(arc, LV_ARC_PART_INDIC); lv_coord_t bottom_indic = lv_obj_get_style_pad_bottom(arc, LV_ARC_PART_INDIC); - r -= LV_MATH_MAX4(left_indic, right_indic, top_indic, bottom_indic); + lv_coord_t indic_r = arc_r - LV_MATH_MAX4(left_indic, right_indic, top_indic, bottom_indic); - if(r > 0) { + if(indic_r > 0) { lv_draw_line_dsc_init(&arc_dsc); lv_obj_init_draw_line_dsc(arc, LV_ARC_PART_INDIC, &arc_dsc); - lv_draw_arc(x, y, r, ext->arc_angle_start + ext->rotation_angle, ext->arc_angle_end + ext->rotation_angle, clip_area, + lv_draw_arc(center.x, center.y, indic_r, ext->arc_angle_start + ext->rotation_angle, ext->arc_angle_end + ext->rotation_angle, clip_area, &arc_dsc); } + + lv_area_t knob_area; + get_knob_area(arc, ¢er, arc_r, &knob_area); + + lv_draw_rect_dsc_t knob_rect_dsc; + lv_draw_rect_dsc_init(&knob_rect_dsc); + lv_obj_init_draw_rect_dsc(arc, LV_ARC_PART_KNOB, &knob_rect_dsc); + + lv_draw_rect(&knob_area, clip_area, &knob_rect_dsc); + } /*Post draw when the children are drawn*/ else if(mode == LV_DESIGN_DRAW_POST) { @@ -478,7 +700,119 @@ static lv_res_t lv_arc_signal(lv_obj_t * arc, lv_signal_t sign, void * param) if(sign == LV_SIGNAL_GET_TYPE) return lv_obj_handle_get_type_signal(param, LV_OBJX_NAME); - if(sign == LV_SIGNAL_CLEANUP) { + lv_arc_ext_t * ext = lv_obj_get_ext_attr(arc); + + if(sign == LV_SIGNAL_PRESSING) { + lv_indev_t * indev = lv_indev_get_act(); + if(indev == NULL) return res; + + /*Handle only pointers here*/ + lv_indev_type_t indev_type = lv_indev_get_type(indev); + if(indev_type != LV_INDEV_TYPE_POINTER) return res; + + lv_point_t p; + lv_indev_get_point(indev, &p); + + /*Make point relative to the arc's center*/ + lv_point_t center; + lv_coord_t r; + get_center(arc, ¢er, &r); + + p.x -= center.x; + p.y -= center.y; + + /*Enter dragging mode if pressed out of the knob*/ + if(ext->dragging == false) { + lv_coord_t indic_width = lv_obj_get_style_line_width(arc, LV_ARC_PART_INDIC); + r -= indic_width; + r -= r / 2; /*Add some more sensitive area*/ + if(p.x * p.x + p.y * p.y > r * r) { + ext->dragging = true; + ext->last_tick = lv_tick_get(); /*Capture timestamp at dragging start*/ + } + } + + /*It must be in "dragging" mode to turn the arc*/ + if(ext->dragging == false) return res; + + /*Calculate the angle of the pressed point*/ + int16_t angle; + int16_t bg_end = ext->bg_angle_end; + if (ext->bg_angle_end < ext->bg_angle_start) { + bg_end = ext->bg_angle_end + 360; + } + + angle = 360 - _lv_atan2(p.x, p.y) + 90; /*Some transformation is required*/ + if(angle < ext->bg_angle_start) angle = ext->bg_angle_start; + if(angle > bg_end) angle = bg_end; + + /*Calculate the slew rate limited angle based on change rate (degrees/sec)*/ + int16_t delta_angle = angle - ext->last_angle; + uint32_t delta_tick = lv_tick_elaps(ext->last_tick); + int16_t delta_angle_max = (ext->chg_rate * delta_tick) / 1000; + + if (delta_angle > delta_angle_max) { + delta_angle = delta_angle_max; + } else if (delta_angle < -delta_angle_max) { + delta_angle = -delta_angle_max; + } + + angle = ext->last_angle + delta_angle; /*Apply the limited angle change*/ + + /*Rounding for symmetry*/ + int32_t round = ((bg_end - ext->bg_angle_start) * 8) / (ext->max_value - ext->min_value); + round = (round + 4) >> 4; + angle += round; + + /*Set the new value*/ + int16_t old_value = ext->cur_value; + int16_t new_value = _lv_map(angle, ext->bg_angle_start, bg_end, ext->min_value, ext->max_value); + if(new_value != lv_arc_get_value(arc)) { + ext->last_tick = lv_tick_get(); /*Cache timestamp for the next iteration*/ + lv_arc_set_value(arc, new_value); /*set_value caches the last_angle for the next iteration*/ + if(new_value != old_value) { + res = lv_event_send(arc, LV_EVENT_VALUE_CHANGED, NULL); + if(res != LV_RES_OK) return res; + } + } + + /*Don1't let the elapsed time to big while sitting on an end point*/ + if(new_value == ext->min_value || new_value == ext->max_value) { + ext->last_tick = lv_tick_get(); /*Cache timestamp for the next iteration*/ + } + } + else if(sign == LV_SIGNAL_RELEASED || sign == LV_SIGNAL_PRESS_LOST) { + ext->dragging = false; + +#if LV_USE_GROUP + /*Leave edit mode if released. (No need to wait for LONG_PRESS) */ + lv_group_t * g = lv_obj_get_group(arc); + bool editing = lv_group_get_editing(g); + lv_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act()); + if(indev_type == LV_INDEV_TYPE_ENCODER) { + if(editing) lv_group_set_editing(g, false); + } +#endif + + } + else if(sign == LV_SIGNAL_CONTROL) { + char c = *((char *)param); + + int16_t old_value = ext->cur_value; + if(c == LV_KEY_RIGHT || c == LV_KEY_UP) { + lv_arc_set_value(arc, lv_arc_get_value(arc) + 1); + } + else if(c == LV_KEY_LEFT || c == LV_KEY_DOWN) { + lv_arc_set_value(arc, lv_arc_get_value(arc) - 1); + } + + if (old_value != ext->cur_value) { + res = lv_event_send(arc, LV_EVENT_VALUE_CHANGED, NULL); + if(res != LV_RES_OK) return res; + } + } + else if(sign == LV_SIGNAL_CLEANUP) { + lv_obj_clean_style_list(arc, LV_ARC_PART_KNOB); lv_obj_clean_style_list(arc, LV_ARC_PART_INDIC); } @@ -506,6 +840,9 @@ static lv_style_list_t * lv_arc_get_style(lv_obj_t * arc, uint8_t part) case LV_ARC_PART_INDIC: style_dsc_p = &ext->style_arc; break; + case LV_ARC_PART_KNOB: + style_dsc_p = &ext->style_knob; + break; default: style_dsc_p = NULL; } @@ -513,7 +850,7 @@ static lv_style_list_t * lv_arc_get_style(lv_obj_t * arc, uint8_t part) return style_dsc_p; } -static void inv_arc_area(lv_obj_t * arc, uint16_t start_angle, uint16_t end_angle) +static void inv_arc_area(lv_obj_t * arc, uint16_t start_angle, uint16_t end_angle, lv_arc_part_t part) { lv_arc_ext_t * ext = lv_obj_get_ext_attr(arc); @@ -533,10 +870,26 @@ static void inv_arc_area(lv_obj_t * arc, uint16_t start_angle, uint16_t end_angl lv_coord_t rout = (LV_MATH_MIN(lv_obj_get_width(arc) - left - right, lv_obj_get_height(arc) - top - bottom)) / 2; lv_coord_t x = arc->coords.x1 + rout + left; lv_coord_t y = arc->coords.y1 + rout + top; - lv_style_int_t w = lv_obj_get_style_line_width(arc, LV_ARC_PART_INDIC); - lv_style_int_t rounded = lv_obj_get_style_line_rounded(arc, LV_ARC_PART_INDIC); + lv_style_int_t w = lv_obj_get_style_line_width(arc, part); + lv_style_int_t rounded = lv_obj_get_style_line_rounded(arc, part); lv_coord_t rin = rout - w; - lv_coord_t extra_area = rounded ? w / 2 + 2 : 0; + lv_coord_t extra_area = 0; + + extra_area = rounded ? w / 2 + 2 : 0; + + if(part == LV_ARC_PART_INDIC && lv_style_list_get_style(&ext->style_knob, 0) != NULL) { + lv_coord_t knob_extra_size = lv_obj_get_draw_rect_ext_pad_size(arc, LV_ARC_PART_KNOB); + + lv_coord_t knob_left = lv_obj_get_style_pad_left(arc, LV_ARC_PART_KNOB); + lv_coord_t knob_right = lv_obj_get_style_pad_right(arc, LV_ARC_PART_KNOB); + lv_coord_t knob_top = lv_obj_get_style_pad_top(arc, LV_ARC_PART_KNOB); + lv_coord_t knob_bottom = lv_obj_get_style_pad_bottom(arc, LV_ARC_PART_KNOB); + + knob_extra_size += LV_MATH_MAX4(knob_left, knob_right, knob_top, knob_bottom); + + extra_area = LV_MATH_MAX(extra_area, w / 2 + 2 + knob_extra_size); + + } lv_area_t inv_area; @@ -617,4 +970,56 @@ static void inv_arc_area(lv_obj_t * arc, uint16_t start_angle, uint16_t end_angl lv_obj_invalidate(arc); } } + +static void get_center(lv_obj_t * arc, lv_point_t * center, lv_coord_t * arc_r) +{ + lv_coord_t left_bg = lv_obj_get_style_pad_left(arc, LV_ARC_PART_BG); + lv_coord_t right_bg = lv_obj_get_style_pad_right(arc, LV_ARC_PART_BG); + lv_coord_t top_bg = lv_obj_get_style_pad_top(arc, LV_ARC_PART_BG); + lv_coord_t bottom_bg = lv_obj_get_style_pad_bottom(arc, LV_ARC_PART_BG); + + lv_coord_t r = (LV_MATH_MIN(lv_obj_get_width(arc) - left_bg - right_bg, + lv_obj_get_height(arc) - top_bg - bottom_bg)) / 2; + + *arc_r = r; + center->x = arc->coords.x1 + r + left_bg; + center->y = arc->coords.y1 + r + top_bg; + + + lv_coord_t indic_width = lv_obj_get_style_line_width(arc, LV_ARC_PART_INDIC); + r -= indic_width; +} + +static void get_knob_area(lv_obj_t * arc, const lv_point_t * center, lv_coord_t r, lv_area_t * knob_area) +{ + lv_arc_ext_t * ext = lv_obj_get_ext_attr(arc); + + lv_coord_t indic_width = lv_obj_get_style_line_width(arc, LV_ARC_PART_INDIC); + lv_coord_t indic_width_half = indic_width / 2; + r -= indic_width_half; + + uint16_t angle = ext->rotation_angle; + if(ext->type == LV_ARC_TYPE_NORMAL) { + angle += ext->arc_angle_end; + } else if(ext->type == LV_ARC_TYPE_REVERSE) { + angle += ext->arc_angle_start; + } else if(ext->type == LV_ARC_TYPE_SYMMETRIC) { + int32_t range_midpoint = (int32_t)(ext->min_value + ext->max_value) / 2; + if(ext->cur_value < range_midpoint) angle += ext->arc_angle_start; + else angle += ext->arc_angle_end; + } + lv_coord_t knob_x = (r * _lv_trigo_sin(angle + 90)) >> LV_TRIGO_SHIFT; + lv_coord_t knob_y = (r * _lv_trigo_sin(angle)) >> LV_TRIGO_SHIFT; + + lv_coord_t left_knob = lv_obj_get_style_pad_left(arc, LV_ARC_PART_KNOB); + lv_coord_t right_knob = lv_obj_get_style_pad_right(arc, LV_ARC_PART_KNOB); + lv_coord_t top_knob = lv_obj_get_style_pad_top(arc, LV_ARC_PART_KNOB); + lv_coord_t bottom_knob = lv_obj_get_style_pad_bottom(arc, LV_ARC_PART_KNOB); + + knob_area->x1 = center->x + knob_x - left_knob - indic_width_half; + knob_area->x2 = center->x + knob_x + right_knob + indic_width_half; + knob_area->y1 = center->y + knob_y - top_knob - indic_width_half; + knob_area->y2 = center->y + knob_y + bottom_knob + indic_width_half; +} + #endif diff --git a/src/lv_widgets/lv_arc.h b/src/lv_widgets/lv_arc.h index 10481f405..94f711661 100644 --- a/src/lv_widgets/lv_arc.h +++ b/src/lv_widgets/lv_arc.h @@ -26,6 +26,14 @@ extern "C" { /********************** * TYPEDEFS **********************/ + +enum { + LV_ARC_TYPE_NORMAL, + LV_ARC_TYPE_SYMMETRIC, + LV_ARC_TYPE_REVERSE +}; +typedef uint8_t lv_arc_type_t; + /*Data of arc*/ typedef struct { /*New data for this type */ @@ -35,14 +43,24 @@ typedef struct { uint16_t bg_angle_start; uint16_t bg_angle_end; lv_style_list_t style_arc; + lv_style_list_t style_knob; /* Style of the knob */ + + int16_t cur_value; /*Current value of the arc*/ + int16_t min_value; /*Minimum value of the arc*/ + int16_t max_value; /*Maximum value of the arc*/ + uint16_t dragging :1; + uint16_t type :2; + uint16_t chg_rate; /*Drag angle rate of change of the arc (degrees/sec)*/ + uint32_t last_tick; /*Last dragging event timestamp of the arc*/ + int16_t last_angle; /*Last dragging angle of the arc*/ } lv_arc_ext_t; /*Parts of the arc*/ enum { LV_ARC_PART_BG = LV_OBJ_PART_MAIN, LV_ARC_PART_INDIC, + LV_ARC_PART_KNOB, _LV_ARC_PART_VIRTUAL_LAST, - _LV_ARC_PART_REAL_LAST = _LV_OBJ_PART_REAL_LAST, }; typedef uint8_t lv_arc_part_t; @@ -118,6 +136,45 @@ void lv_arc_set_bg_angles(lv_obj_t * arc, uint16_t start, uint16_t end); */ void lv_arc_set_rotation(lv_obj_t * arc, uint16_t rotation_angle); + +/** + * Set the type of arc. + * @param arc pointer to arc object + * @param type arc type + */ +void lv_arc_set_type(lv_obj_t * arc, lv_arc_type_t type); + +/** + * Set a new value on the arc + * @param arc pointer to a arc object + * @param value new value + */ +void lv_arc_set_value(lv_obj_t * arc, int16_t value); + +/** + * Set minimum and the maximum values of a arc + * @param arc pointer to the arc object + * @param min minimum value + * @param max maximum value + */ +void lv_arc_set_range(lv_obj_t * arc, int16_t min, int16_t max); + +/** + * Reverse arc behavior. The indicator will grow from arc end instead of arc start. + * position. + * @param arc pointer to a arc object + * @param reverse true: enable disable reverse behavior; false: disable + */ +void lv_arc_set_reverse(lv_obj_t * arc, bool reverse); + +/** + * Set the threshold of arc knob increments + * position. + * @param arc pointer to a arc object + * @param threshold increment threshold + */ +void lv_arc_set_chg_rate(lv_obj_t * arc, uint16_t threshold); + /*===================== * Getter functions *====================*/ @@ -150,6 +207,41 @@ uint16_t lv_arc_get_bg_angle_start(lv_obj_t * arc); */ uint16_t lv_arc_get_bg_angle_end(lv_obj_t * arc); +/** + * Get whether the arc is type or not. + * @param arc pointer to a arc object + * @return arc type + */ +lv_arc_type_t lv_arc_get_type(const lv_obj_t * arc); + +/** + * Get the value of the of a arc + * @param arc pointer to a arc object + * @return the value of the of the arc + */ +int16_t lv_arc_get_value(const lv_obj_t * arc); + +/** + * Get the minimum value of a arc + * @param arc pointer to a arc object + * @return the minimum value of the arc + */ +int16_t lv_arc_get_min_value(const lv_obj_t * arc); + +/** + * Get the maximum value of a arc + * @param arc pointer to a arc object + * @return the maximum value of the arc + */ +int16_t lv_arc_get_max_value(const lv_obj_t * arc); + +/** + * Give the arc is being dragged or not + * @param arc pointer to a arc object + * @return true: drag in progress false: not dragged + */ +bool lv_arc_is_dragged(const lv_obj_t * arc); + /*===================== * Other functions *====================*/ diff --git a/tests/build.py b/tests/build.py index 73a06148f..f014027c7 100755 --- a/tests/build.py +++ b/tests/build.py @@ -120,6 +120,7 @@ minimal_monochrome = { "LV_USE_PAGE":0, "LV_USE_SPINNER":0, "LV_USE_ROLLER":0, + "LV_USE_ROTARY":0, "LV_USE_SLIDER":0, "LV_USE_SPINBOX":0, "LV_USE_SWITCH":0, @@ -196,6 +197,7 @@ all_obj_minimal_features = { "LV_USE_PAGE":1, "LV_USE_SPINNER":0, #Disabled beacsue needs anim "LV_USE_ROLLER":1, + "LV_USE_ROTARY":1, "LV_USE_SLIDER":1, "LV_USE_SPINBOX":1, "LV_USE_SWITCH":1, @@ -274,6 +276,7 @@ all_obj_all_features = { "LV_USE_PAGE":1, "LV_USE_SPINNER":1, "LV_USE_ROLLER":1, + "LV_USE_ROTARY":1, "LV_USE_SLIDER":1, "LV_USE_SPINBOX":1, "LV_USE_SWITCH":1, @@ -365,6 +368,7 @@ advanced_features = { "LV_USE_PAGE":1, "LV_USE_SPINNER":1, "LV_USE_ROLLER":1, + "LV_USE_ROTARY":1, "LV_USE_SLIDER":1, "LV_USE_SPINBOX":1, "LV_USE_SWITCH":1,