diff --git a/src/lv_api_map.h b/src/lv_api_map.h index 1318555f4..941f84e72 100644 --- a/src/lv_api_map.h +++ b/src/lv_api_map.h @@ -66,6 +66,61 @@ static inline bool lv_ddlist_get_draw_arrow(lv_obj_t * ddlist) #endif +#if LV_USE_SLIDER + +/** + * Make the slider symmetric to zero. The indicator will grow from zero instead of the minimum + * position. + * @param slider pointer to a bar object + * @param en true: enable disable symmetric behavior; false: disable + * @deprecated As of v7.0, you should use `lv_slider_set_type` instead. + */ +static inline void lv_slider_set_sym(lv_obj_t * slider, bool en) +{ + lv_bar_set_sym(slider, en); +} + +/** + * Get whether the slider is symmetric or not. + * @param slider pointer to a slider object + * @return true: symmetric is enabled; false: disable + * @deprecated As of v7.0, you should use `lv_slider_get_type` instead. + */ +static inline bool lv_slider_get_sym(lv_obj_t * slider) { + return lv_bar_get_sym(slider); +} + +#endif + +#if LV_USE_BAR + +/** + * Make the bar symmetric to zero. The indicator will grow from zero instead of the minimum + * position. + * @param bar pointer to a bar object + * @param en true: enable disable symmetric behavior; false: disable + * @deprecated As of v7.0, you should use `lv_bar_set_type` instead. + */ +static inline void lv_bar_set_sym(lv_obj_t * bar, bool en) +{ + if(en) + lv_bar_set_type(bar, LV_BAR_TYPE_SYM); + else + lv_bar_set_type(bar, LV_BAR_TYPE_NORMAL); +} + +/** + * Get whether the bar is symmetric or not. + * @param bar pointer to a bar object + * @return true: symmetric is enabled; false: disable + * @deprecated As of v7.0, you should use `lv_bar_get_type` instead. + */ +static inline bool lv_bar_get_sym(lv_obj_t * bar) { + return lv_bar_get_type(bar) == LV_BAR_TYPE_SYM; +} + +#endif + /********************** * MACROS **********************/ diff --git a/src/lv_objx/lv_bar.c b/src/lv_objx/lv_bar.c index d5d29624b..0a8de0d3f 100644 --- a/src/lv_objx/lv_bar.c +++ b/src/lv_objx/lv_bar.c @@ -34,12 +34,14 @@ **********************/ static lv_design_res_t lv_bar_design(lv_obj_t * bar, const lv_area_t * clip_area, lv_design_mode_t mode); static lv_res_t lv_bar_signal(lv_obj_t * bar, lv_signal_t sign, void * param); +static void lv_bar_set_value_with_anim(lv_obj_t * bar, int16_t new_value, int16_t *value_ptr, lv_bar_anim_t *anim_info, lv_anim_enable_t en); +static void lv_bar_init_anim(lv_obj_t * bar, lv_bar_anim_t * bar_anim); static void draw_bg(lv_obj_t * bar, const lv_area_t * clip_area, lv_design_mode_t mode, lv_opa_t opa); static void draw_indic(lv_obj_t * bar, const lv_area_t * clip_area, lv_design_mode_t mode, lv_opa_t opa); #if LV_USE_ANIMATION -static void lv_bar_anim(void * bar, lv_anim_value_t value); +static void lv_bar_anim(lv_bar_anim_t * bar, lv_anim_value_t value); static void lv_bar_anim_ready(lv_anim_t * a); #endif @@ -81,15 +83,15 @@ lv_obj_t * lv_bar_create(lv_obj_t * par, const lv_obj_t * copy) if(ext == NULL) return NULL; ext->min_value = 0; + ext->start_value = 0; ext->max_value = 100; ext->cur_value = 0; #if LV_USE_ANIMATION ext->anim_time = 200; - ext->anim_start = 0; - ext->anim_end = 0; - ext->anim_state = LV_BAR_ANIM_STATE_INV; + lv_bar_init_anim(new_bar, &ext->cur_value_anim); + lv_bar_init_anim(new_bar, &ext->start_value_anim); #endif - ext->sym = 0; + ext->type = LV_BAR_TYPE_NORMAL; ext->style_indic = &lv_style_pretty_color; lv_obj_set_signal_cb(new_bar, lv_bar_signal); @@ -111,10 +113,11 @@ lv_obj_t * lv_bar_create(lv_obj_t * par, const lv_obj_t * copy) } else { lv_bar_ext_t * ext_copy = lv_obj_get_ext_attr(copy); ext->min_value = ext_copy->min_value; + ext->start_value = ext_copy->start_value; ext->max_value = ext_copy->max_value; ext->cur_value = ext_copy->cur_value; ext->style_indic = ext_copy->style_indic; - ext->sym = ext_copy->sym; + ext->type = ext_copy->type; /*Refresh the style with new signal function*/ lv_obj_refresh_style(new_bar); @@ -152,39 +155,33 @@ void lv_bar_set_value(lv_obj_t * bar, int16_t value, lv_anim_enable_t anim) if(ext->cur_value == new_value) return; - if(anim == LV_ANIM_OFF) { - ext->cur_value = new_value; - lv_obj_invalidate(bar); - } else { -#if LV_USE_ANIMATION - /*No animation in progress -> simply set the values*/ - if(ext->anim_state == LV_BAR_ANIM_STATE_INV) { - ext->anim_start = ext->cur_value; - ext->anim_end = new_value; - } - /*Animation in progress. Start from the animation end value*/ - else { - ext->anim_start = ext->anim_end; - ext->anim_end = new_value; - } + lv_bar_set_value_with_anim(bar, new_value, &ext->cur_value, &ext->cur_value_anim, anim); - lv_anim_t a; - a.var = bar; - a.start = LV_BAR_ANIM_STATE_START; - a.end = LV_BAR_ANIM_STATE_END; - a.exec_cb = (lv_anim_exec_xcb_t)lv_bar_anim; - a.path_cb = lv_anim_path_linear; - a.ready_cb = lv_bar_anim_ready; - a.act_time = 0; - a.time = ext->anim_time; - a.playback = 0; - a.playback_pause = 0; - a.repeat = 0; - a.repeat_pause = 0; +} - lv_anim_create(&a); +/** + * Set a new start value on the bar + * @param bar pointer to a bar object + * @param value new start value + * @param anim LV_ANIM_ON: set the value with an animation; LV_ANIM_OFF: change the value immediatelly + */ +void lv_bar_set_start_value(lv_obj_t * bar, int16_t start_value, lv_anim_enable_t anim) +{ + LV_ASSERT_OBJ(bar, LV_OBJX_NAME); + +#if LV_USE_ANIMATION == 0 + anim = false; #endif - } + lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar); + if(ext->start_value == start_value) return; + + int16_t new_value; + new_value = start_value > ext->max_value ? ext->max_value : start_value; + new_value = new_value < ext->min_value ? ext->min_value : start_value; + + if(ext->start_value == new_value) return; + + lv_bar_set_value_with_anim(bar, start_value, &ext->start_value, &ext->start_value_anim, anim); } /** @@ -202,6 +199,10 @@ void lv_bar_set_range(lv_obj_t * bar, int16_t min, int16_t max) ext->max_value = max; ext->min_value = min; + + if(lv_bar_get_type(bar) != LV_BAR_TYPE_CUSTOM) + ext->start_value = min; + if(ext->cur_value > max) { ext->cur_value = max; lv_bar_set_value(bar, ext->cur_value, false); @@ -214,17 +215,20 @@ void lv_bar_set_range(lv_obj_t * bar, int16_t min, int16_t max) } /** - * Make the bar symmetric to zero. The indicator will grow from zero instead of the minimum - * position. - * @param bar pointer to a bar object - * @param en true: enable disable symmetric behavior; false: disable + * Set the type of bar. + * @param bar pointer to bar object + * @param type bar type */ -void lv_bar_set_sym(lv_obj_t * bar, bool en) +void lv_bar_set_type(lv_obj_t * bar, lv_bar_type_t type) { - LV_ASSERT_OBJ(bar, LV_OBJX_NAME); + LV_ASSERT_OBJ(bar, LV_OBJX_NAME); lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar); - ext->sym = en ? 1 : 0; + ext->type = type; + if(ext->type != LV_BAR_TYPE_CUSTOM) + ext->start_value = ext->min_value; + + lv_obj_invalidate(bar); } /** @@ -282,12 +286,33 @@ int16_t lv_bar_get_value(const lv_obj_t * bar) lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar); /*If animated tell that it's already at the end value*/ #if LV_USE_ANIMATION - if(ext->anim_state != LV_BAR_ANIM_STATE_INV) return ext->anim_end; + if(ext->cur_value_anim.is_animating) return ext->cur_value_anim.anim_end; #endif /*No animation, simple return the current value*/ return ext->cur_value; } +/** + * Get the start value of a bar + * @param bar pointer to a bar object + * @return the start value of the bar + */ +int16_t lv_bar_get_start_value(const lv_obj_t * bar) +{ + LV_ASSERT_OBJ(bar, LV_OBJX_NAME); + + lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar); + + if(ext->type != LV_BAR_TYPE_CUSTOM) return ext->min_value; + + /*If animated tell that it's already at the end value*/ +#if LV_USE_ANIMATION + if(ext->start_value_anim.is_animating) return ext->start_value_anim.anim_end; +#endif + /*No animation, simple return the current value*/ + return ext->start_value; +} + /** * Get the minimum value of a bar * @param bar pointer to a bar object @@ -315,16 +340,15 @@ int16_t lv_bar_get_max_value(const lv_obj_t * bar) } /** - * Get whether the bar is symmetric or not. - * @param bar pointer to a bar object - * @return true: symmetric is enabled; false: disable + * Get the type of bar. + * @param bar pointer to bar object + * @return bar type */ -bool lv_bar_get_sym(lv_obj_t * bar) -{ - LV_ASSERT_OBJ(bar, LV_OBJX_NAME); +lv_bar_type_t lv_bar_get_type(lv_obj_t * bar) { + LV_ASSERT_OBJ(bar, LV_OBJX_NAME); lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar); - return ext->sym ? true : false; + return ext->type; } /** @@ -443,11 +467,15 @@ static void draw_indic(lv_obj_t * bar, const lv_area_t * clip_area, lv_design_mo int32_t range = ext->max_value - ext->min_value; bool hor = objw >= objh ? true : false; bool sym = false; - if(ext->sym && ext->min_value < 0 && ext->max_value > 0) sym = true; + if(ext->type == LV_BAR_TYPE_SYM && ext->min_value < 0 && ext->max_value > 0) sym = true; - bool anim = false; + bool cur_value_anim = false; #if LV_USE_ANIMATION - if(ext->anim_state != LV_BAR_ANIM_STATE_INV) anim = true; + if(ext->cur_value_anim.is_animating) cur_value_anim = true; +#endif + bool start_value_anim = false; +#if LV_USE_ANIMATION + if(ext->start_value_anim.is_animating) start_value_anim = true; #endif /*Calculate the indicator area*/ @@ -474,24 +502,26 @@ static void draw_indic(lv_obj_t * bar, const lv_area_t * clip_area, lv_design_mo /*Calculate the indicator length*/ lv_coord_t indic_length; - if(anim) { -#if LV_USE_ANIMATION - /*Calculate the coordinates of anim. start and end*/ - lv_coord_t anim_start_v = (int32_t)((int32_t)(hor ? indicw : indich) * - (ext->anim_start - ext->min_value)) / range; - lv_coord_t anim_end_v = (int32_t)((int32_t)(hor ? indicw : indich) * - (ext->anim_end - ext->min_value)) / range; - /*Calculate the real position based on `anim_state` (between `anim_start` and - * `anim_end`)*/ - indic_length = anim_start_v + (((anim_end_v - anim_start_v) * ext->anim_state) >> LV_BAR_ANIM_STATE_NORM); + lv_coord_t anim_cur_value, anim_start_value; + if(cur_value_anim) { +#if LV_USE_ANIMATION + anim_cur_value = ext->cur_value_anim.anim_val; #endif - } - else - { - indic_length = (int32_t)((int32_t)(hor ? indicw : indich) * (ext->cur_value - ext->min_value)) / + } else { + anim_cur_value = ext->cur_value; + } + + if(start_value_anim) { +#if LV_USE_ANIMATION + anim_start_value = ext->start_value_anim.anim_val; +#endif + } else { + anim_start_value = ext->start_value; + } + + indic_length = (int32_t)((int32_t)(hor ? indicw : indich) * ((anim_cur_value - ext->min_value) - (anim_start_value - ext->min_value))) / (ext->max_value - ext->min_value); - } /*Horizontal bar*/ if(hor) { @@ -505,7 +535,11 @@ static void draw_indic(lv_obj_t * bar, const lv_area_t * clip_area, lv_design_mo ext->indic_area.x1 = ext->indic_area.x2; ext->indic_area.x2 = zero; } - } + } else { + lv_coord_t increment = (anim_start_value * indicw) / range; + ext->indic_area.x1 += increment; + ext->indic_area.x2 += increment; + } } /*Vertical bar*/ else { @@ -519,7 +553,11 @@ static void draw_indic(lv_obj_t * bar, const lv_area_t * clip_area, lv_design_mo ext->indic_area.y2 = ext->indic_area.y1; ext->indic_area.y1 = zero; } - } + } else { + lv_coord_t increment = (anim_start_value * objh) / range; + ext->indic_area.y1 += increment; + ext->indic_area.y2 += increment; + } } /*Draw the indicator*/ @@ -605,23 +643,81 @@ static lv_res_t lv_bar_signal(lv_obj_t * bar, lv_signal_t sign, void * param) if(style_indic->body.shadow.width > bar->ext_draw_pad) bar->ext_draw_pad = style_indic->body.shadow.width; } + if(sign == LV_SIGNAL_CLEANUP) { + lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar); + lv_anim_del(&ext->cur_value_anim, NULL); + lv_anim_del(&ext->start_value_anim, NULL); + } + return res; } #if LV_USE_ANIMATION -static void lv_bar_anim(void * bar, lv_anim_value_t value) +static void lv_bar_anim(lv_bar_anim_t * var, lv_anim_value_t value) { - lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar); - ext->anim_state = value; - lv_obj_invalidate(bar); + var->anim_val = value; + lv_obj_invalidate(var->bar); } static void lv_bar_anim_ready(lv_anim_t * a) { - lv_bar_ext_t * ext = lv_obj_get_ext_attr(a->var); - ext->anim_state = LV_BAR_ANIM_STATE_INV; - lv_bar_set_value(a->var, ext->anim_end, false); + lv_bar_anim_t * var = a->var; + lv_bar_ext_t * ext = lv_obj_get_ext_attr(var->bar); + var->is_animating = false; + if(var == &ext->cur_value_anim) + ext->cur_value = var->anim_end; + else if(var == &ext->start_value_anim) + ext->start_value = var->anim_end; + lv_obj_invalidate(var->bar); } #endif +static void lv_bar_set_value_with_anim(lv_obj_t * bar, int16_t new_value, int16_t *value_ptr, lv_bar_anim_t *anim_info, lv_anim_enable_t en) { + if(en == LV_ANIM_OFF) { + *value_ptr = new_value; + lv_obj_invalidate(bar); + } else { +#if LV_USE_ANIMATION + lv_bar_ext_t *ext = lv_obj_get_ext_attr(bar); + /*No animation in progress -> simply set the values*/ + if(!anim_info->is_animating) { + anim_info->anim_start = *value_ptr; + anim_info->anim_end = new_value; + } + /*Animation in progress. Start from the animation end value*/ + else { + anim_info->anim_start = anim_info->anim_end; + anim_info->anim_end = new_value; + } + /* Stop the previous animation if it exists */ + lv_anim_del(anim_info, NULL); + + lv_anim_t a; + a.var = anim_info; + a.start = anim_info->anim_start; + a.end = anim_info->anim_end; + a.exec_cb = (lv_anim_exec_xcb_t)lv_bar_anim; + a.path_cb = lv_anim_path_linear; + a.ready_cb = lv_bar_anim_ready; + a.act_time = 0; + a.time = ext->anim_time; + a.playback = 0; + a.playback_pause = 0; + a.repeat = 0; + a.repeat_pause = 0; + + lv_anim_create(&a); + anim_info->is_animating = true; +#endif + } +} + +static void lv_bar_init_anim(lv_obj_t * bar, lv_bar_anim_t * bar_anim) +{ + bar_anim->bar = bar; + bar_anim->anim_start = 0; + bar_anim->anim_end = 0; + bar_anim->is_animating = false; +} + #endif diff --git a/src/lv_objx/lv_bar.h b/src/lv_objx/lv_bar.h index 0336964f1..86747c43c 100644 --- a/src/lv_objx/lv_bar.h +++ b/src/lv_objx/lv_bar.h @@ -31,27 +31,27 @@ extern "C" { * DEFINES *********************/ -/** Bar animation start value. (Not the real value of the Bar just indicates process animation)*/ -#define LV_BAR_ANIM_STATE_START 0 - -/** Bar animation end value. (Not the real value of the Bar just indicates process animation)*/ -#define LV_BAR_ANIM_STATE_END 256 - -/** Mark no animation is in progress */ -#define LV_BAR_ANIM_STATE_INV -1 - -/** log2(LV_BAR_ANIM_STATE_END) used to normalize data*/ -#define LV_BAR_ANIM_STATE_NORM 8 - -LV_EXPORT_CONST_INT(LV_BAR_ANIM_STATE_START); -LV_EXPORT_CONST_INT(LV_BAR_ANIM_STATE_END); -LV_EXPORT_CONST_INT(LV_BAR_ANIM_STATE_INV); -LV_EXPORT_CONST_INT(LV_BAR_ANIM_STATE_NORM); - /********************** * TYPEDEFS **********************/ +enum { + LV_BAR_TYPE_NORMAL, + LV_BAR_TYPE_SYM, + LV_BAR_TYPE_CUSTOM +}; +typedef uint8_t lv_bar_type_t; + +#if LV_USE_ANIMATION +typedef struct { + lv_obj_t * bar; + lv_anim_value_t anim_start; + lv_anim_value_t anim_end; + lv_anim_value_t anim_val; + uint8_t is_animating : 1; +} lv_bar_anim_t; +#endif + /** Data of bar*/ typedef struct { @@ -61,14 +61,14 @@ typedef struct int16_t cur_value; /*Current value of the bar*/ int16_t min_value; /*Minimum value of the bar*/ int16_t max_value; /*Maximum value of the bar*/ + int16_t start_value; /*Start value of the bar*/ lv_area_t indic_area; /*Save the indicator area. MIght be used by derived types*/ #if LV_USE_ANIMATION - lv_anim_value_t anim_start; - lv_anim_value_t anim_end; - lv_anim_value_t anim_state; lv_anim_value_t anim_time; + lv_bar_anim_t cur_value_anim; + lv_bar_anim_t start_value_anim; #endif - uint8_t sym : 1; /*Symmetric: means the center is around zero value*/ + uint8_t type : 2; /*Type of bar*/ const lv_style_t * style_indic; /*Style of the indicator*/ } lv_bar_ext_t; @@ -103,6 +103,14 @@ lv_obj_t * lv_bar_create(lv_obj_t * par, const lv_obj_t * copy); */ void lv_bar_set_value(lv_obj_t * bar, int16_t value, lv_anim_enable_t anim); +/** + * Set a new start value on the bar + * @param bar pointer to a bar object + * @param value new start value + * @param anim LV_ANIM_ON: set the value with an animation; LV_ANIM_OFF: change the value immediatelly + */ +void lv_bar_set_start_value(lv_obj_t * bar, int16_t start_value, lv_anim_enable_t anim); + /** * Set minimum and the maximum values of a bar * @param bar pointer to the bar object @@ -112,12 +120,11 @@ void lv_bar_set_value(lv_obj_t * bar, int16_t value, lv_anim_enable_t anim); void lv_bar_set_range(lv_obj_t * bar, int16_t min, int16_t max); /** - * Make the bar symmetric to zero. The indicator will grow from zero instead of the minimum - * position. - * @param bar pointer to a bar object - * @param en true: enable disable symmetric behavior; false: disable + * Set the type of bar. + * @param bar pointer to bar object + * @param type bar type */ -void lv_bar_set_sym(lv_obj_t * bar, bool en); +void lv_bar_set_type(lv_obj_t * bar, lv_bar_type_t type); /** * Set the animation time of the bar @@ -145,6 +152,13 @@ void lv_bar_set_style(lv_obj_t * bar, lv_bar_style_t type, const lv_style_t * st */ int16_t lv_bar_get_value(const lv_obj_t * bar); +/** + * Get the start value of a bar + * @param bar pointer to a bar object + * @return the start value of the bar + */ +int16_t lv_bar_get_start_value(const lv_obj_t * bar); + /** * Get the minimum value of a bar * @param bar pointer to a bar object @@ -160,11 +174,11 @@ int16_t lv_bar_get_min_value(const lv_obj_t * bar); int16_t lv_bar_get_max_value(const lv_obj_t * bar); /** - * Get whether the bar is symmetric or not. - * @param bar pointer to a bar object - * @return true: symmetric is enabled; false: disable + * Get the type of bar. + * @param bar pointer to bar object + * @return bar type */ -bool lv_bar_get_sym(lv_obj_t * bar); +lv_bar_type_t lv_bar_get_type(lv_obj_t * bar); /** * Get the animation time of the bar diff --git a/src/lv_objx/lv_slider.c b/src/lv_objx/lv_slider.c index c9a123a7b..cb6c24e9f 100644 --- a/src/lv_objx/lv_slider.c +++ b/src/lv_objx/lv_slider.c @@ -32,6 +32,8 @@ **********************/ static lv_design_res_t lv_slider_design(lv_obj_t * slider, const lv_area_t * clip_area, lv_design_mode_t mode); static lv_res_t lv_slider_signal(lv_obj_t * slider, lv_signal_t sign, void * param); +static void lv_slider_position_knob(lv_obj_t * slider, lv_area_t * knob_area, lv_coord_t knob_size, bool hor); +static void lv_slider_draw_knob(lv_obj_t * slider, lv_area_t * knob_area, lv_area_t * clip_area); /********************** * STATIC VARIABLES @@ -72,6 +74,8 @@ lv_obj_t * lv_slider_create(lv_obj_t * par, const lv_obj_t * copy) /*Initialize the allocated 'ext' */ ext->style_knob = &lv_style_pretty; + ext->value_to_set = NULL; + ext->dragging = false; ext->img_knob = NULL; /*The signal and design functions are not copied so set them here*/ @@ -243,15 +247,13 @@ static lv_design_res_t lv_slider_design(lv_obj_t * slider, const lv_area_t * cli ancestor_design_f(slider, clip_area, mode); lv_slider_ext_t * ext = lv_obj_get_ext_attr(slider); - const lv_style_t * style_knob = lv_slider_get_style(slider, LV_SLIDER_STYLE_KNOB); - lv_opa_t opa_scale = lv_obj_get_opa_scale(slider); lv_coord_t objw = lv_obj_get_width(slider); lv_coord_t objh = lv_obj_get_height(slider); bool hor = objw >= objh ? true : false; lv_coord_t knob_size = hor ? objh : objw; bool sym = false; - if(ext->bar.sym && ext->bar.min_value < 0 && ext->bar.max_value > 0) sym = true; + if(ext->bar.type == LV_BAR_TYPE_SYM && ext->bar.min_value < 0 && ext->bar.max_value > 0) sym = true; lv_area_t knob_area; @@ -266,14 +268,10 @@ static lv_design_res_t lv_slider_design(lv_obj_t * slider, const lv_area_t * cli knob_area.x1 = ext->bar.indic_area.x1; } } - knob_area.x1 -= (knob_size >> 1); - knob_area.x2 = knob_area.x1 + knob_size; - knob_area.y1 = slider->coords.y1; - knob_area.y2 = slider->coords.y2; } /*Vertical*/ else { - if(!sym) { + if(!sym) { knob_area.y1 = ext->bar.indic_area.y1; } else { if(ext->bar.cur_value >= 0) { @@ -282,37 +280,23 @@ static lv_design_res_t lv_slider_design(lv_obj_t * slider, const lv_area_t * cli knob_area.y1 = ext->bar.indic_area.y2; } } - knob_area.y1 -= (knob_size >> 1); - knob_area.y2 = knob_area.y1 + knob_size; - knob_area.x1 = slider->coords.x1; - knob_area.x2 = slider->coords.x2; } + lv_slider_position_knob(slider, &knob_area, knob_size, hor); - /*Apply the paddings on the knob area*/ - knob_area.x1 -= style_knob->body.padding.left; - knob_area.x2 += style_knob->body.padding.right; - knob_area.y1 -= style_knob->body.padding.top; - knob_area.y2 += style_knob->body.padding.bottom; + lv_area_copy(&ext->left_knob_area, &knob_area); + lv_slider_draw_knob(slider, &knob_area, clip_area); - lv_draw_rect(&knob_area, clip_area, style_knob, opa_scale); + if(lv_slider_get_type(slider) == LV_SLIDER_TYPE_RANGE) { + /* Draw a second knob for the start_value side */ + if(hor) { + knob_area.x1 = ext->bar.indic_area.x1; + } else { + knob_area.y1 = ext->bar.indic_area.y1; + } + lv_slider_position_knob(slider, &knob_area, knob_size, hor); - if(ext->img_knob) { - lv_res_t res; - lv_img_header_t info; - res = lv_img_decoder_get_info(ext->img_knob, &info); - if(res == LV_RES_OK) { - lv_coord_t x_ofs = knob_area.x1 + (lv_area_get_width(&knob_area) - info.w) / 2; - lv_coord_t y_ofs = knob_area.y1 + (lv_area_get_height(&knob_area) - info.h) / 2; - lv_area_t a; - a.x1 = x_ofs; - a.y1 = y_ofs; - a.x2 = info.w - 1 + x_ofs; - a.y2 = info.h - 1 + y_ofs; - - lv_draw_img(&a, clip_area, ext->img_knob, style_knob, 0, NULL, LV_IMG_ZOOM_NONE, false, opa_scale); - } else { - LV_LOG_WARN("lv_slider_design: can't get knob image info") - } + lv_area_copy(&ext->right_knob_area, &knob_area); + lv_slider_draw_knob(slider, &knob_area, clip_area); } } /*Post draw when the children are drawn*/ @@ -344,38 +328,68 @@ static lv_res_t lv_slider_signal(lv_obj_t * slider, lv_signal_t sign, void * par if(sign == LV_SIGNAL_PRESSED) { ext->dragging = true; - } else if(sign == LV_SIGNAL_PRESSING) { + if(lv_slider_get_type(slider) == LV_SLIDER_TYPE_RANGE) { + lv_indev_get_point(param, &p); + bool hor = lv_obj_get_width(slider) >= lv_obj_get_height(slider); + + /* Calculate the distance from each knob */ + lv_coord_t dist_left, dist_right; + if(hor) { + dist_left = LV_MATH_ABS((ext->left_knob_area.x1+(ext->left_knob_area.x2 - ext->left_knob_area.x1)/2) - p.x); + dist_right = LV_MATH_ABS((ext->right_knob_area.x1+(ext->right_knob_area.x2 - ext->right_knob_area.x1)/2) - p.x); + } else { + dist_left = LV_MATH_ABS((ext->left_knob_area.y1+(ext->left_knob_area.y2 - ext->left_knob_area.y1)/2) - p.y); + dist_right = LV_MATH_ABS((ext->right_knob_area.y1+(ext->right_knob_area.y2 - ext->right_knob_area.y1)/2) - p.y); + } + + /* Use whichever one is closer */ + if(dist_right > dist_left) + ext->value_to_set = &ext->bar.cur_value; + else + ext->value_to_set = &ext->bar.start_value; + } else + ext->value_to_set = &ext->bar.cur_value; + } else if(sign == LV_SIGNAL_PRESSING && ext->value_to_set != NULL) { lv_indev_get_point(param, &p); lv_coord_t w = lv_obj_get_width(slider); lv_coord_t h = lv_obj_get_height(slider); const lv_style_t * indic_style = lv_slider_get_style(slider, LV_SLIDER_STYLE_INDIC); int32_t range = ext->bar.max_value - ext->bar.min_value; - int16_t new_value = 0; - if(w >= h) { - lv_coord_t indic_w = w - indic_style->body.padding.left - indic_style->body.padding.right; - new_value = p.x - (slider->coords.x1 + indic_style->body.padding.left); /*Make the point relative to the indicator*/ - new_value = (new_value * range) / indic_w; - new_value += ext->bar.min_value; - } else { - lv_coord_t indic_h = h - indic_style->body.padding.bottom - indic_style->body.padding.top; - new_value = p.y - (slider->coords.y2 + indic_style->body.padding.bottom); /*Make the point relative to the indicator*/ - new_value = (-new_value * range) / indic_h; - new_value += ext->bar.min_value; + int16_t new_value = 0, real_max_value = ext->bar.max_value, real_min_value = ext->bar.min_value; - } + if(w >= h) { + lv_coord_t indic_w = w - indic_style->body.padding.left - indic_style->body.padding.right; + new_value = p.x - (slider->coords.x1 + indic_style->body.padding.left); /*Make the point relative to the indicator*/ + new_value = (new_value * range) / indic_w; + new_value += ext->bar.min_value; + } else { + lv_coord_t indic_h = h - indic_style->body.padding.bottom - indic_style->body.padding.top; + new_value = p.y - (slider->coords.y2 + indic_style->body.padding.bottom); /*Make the point relative to the indicator*/ + new_value = (-new_value * range) / indic_h; + new_value += ext->bar.min_value; - if(new_value < ext->bar.min_value) new_value = ext->bar.min_value; - else if(new_value > ext->bar.max_value) new_value = ext->bar.max_value; + } - if(new_value != ext->bar.cur_value) { - ext->bar.cur_value = new_value; - lv_obj_invalidate(slider); - res = lv_event_send(slider, LV_EVENT_VALUE_CHANGED, NULL); - if(res != LV_RES_OK) return res; - } + /* Figure out the min. and max. for this mode */ + if(ext->value_to_set == &ext->bar.start_value) { + real_max_value = ext->bar.cur_value; + } else + real_min_value = ext->bar.start_value; + + if(new_value < real_min_value) new_value = real_min_value; + else if(new_value > real_max_value) new_value = real_max_value; + + if(new_value != ext->bar.cur_value) { + *ext->value_to_set = new_value; + lv_obj_invalidate(slider); + res = lv_event_send(slider, LV_EVENT_VALUE_CHANGED, NULL); + if(res != LV_RES_OK) return res; + } + } else if(sign == LV_SIGNAL_RELEASED || sign == LV_SIGNAL_PRESS_LOST) { ext->dragging = false; + ext->value_to_set = NULL; #if LV_USE_GROUP /*Leave edit mode if released. (No need to wait for LONG_PRESS) */ @@ -450,4 +464,54 @@ static lv_res_t lv_slider_signal(lv_obj_t * slider, lv_signal_t sign, void * par return res; } + +static void lv_slider_position_knob(lv_obj_t * slider, lv_area_t * knob_area, lv_coord_t knob_size, bool hor) +{ + const lv_style_t * style_knob = lv_slider_get_style(slider, LV_SLIDER_STYLE_KNOB); + + if(hor) { + knob_area->x1 -= (knob_size >> 1); + knob_area->x2 = knob_area->x1 + knob_size; + knob_area->y1 = slider->coords.y1; + knob_area->y2 = slider->coords.y2; + } else { + knob_area->y1 -= (knob_size >> 1); + knob_area->y2 = knob_area->y1 + knob_size; + knob_area->x1 = slider->coords.x1; + knob_area->x2 = slider->coords.x2; + } + + /*Apply the paddings on the knob area*/ + knob_area->x1 -= style_knob->body.padding.left; + knob_area->x2 += style_knob->body.padding.right; + knob_area->y1 -= style_knob->body.padding.top; + knob_area->y2 += style_knob->body.padding.bottom; +} + +static void lv_slider_draw_knob(lv_obj_t * slider, lv_area_t * knob_area, lv_area_t * clip_area) { + lv_slider_ext_t * ext = lv_obj_get_ext_attr(slider); + const lv_style_t * style_knob = lv_slider_get_style(slider, LV_SLIDER_STYLE_KNOB); + lv_opa_t opa_scale = lv_obj_get_opa_scale(slider); + + lv_draw_rect(knob_area, clip_area, style_knob, opa_scale); + + if(ext->img_knob) { + lv_res_t res; + lv_img_header_t info; + res = lv_img_decoder_get_info(ext->img_knob, &info); + if(res == LV_RES_OK) { + lv_coord_t x_ofs = knob_area->x1 + (lv_area_get_width(knob_area) - info.w) / 2; + lv_coord_t y_ofs = knob_area->y1 + (lv_area_get_height(knob_area) - info.h) / 2; + lv_area_t a; + a.x1 = x_ofs; + a.y1 = y_ofs; + a.x2 = info.w - 1 + x_ofs; + a.y2 = info.h - 1 + y_ofs; + + lv_draw_img(&a, clip_area, ext->img_knob, style_knob, 0, NULL, LV_IMG_ZOOM_NONE, false, opa_scale); + } else { + LV_LOG_WARN("lv_slider_design: can't get knob image info") + } + } +} #endif diff --git a/src/lv_objx/lv_slider.h b/src/lv_objx/lv_slider.h index e629d1851..adbd1e04e 100644 --- a/src/lv_objx/lv_slider.h +++ b/src/lv_objx/lv_slider.h @@ -36,12 +36,22 @@ extern "C" { /********************** * TYPEDEFS **********************/ + +enum { + LV_SLIDER_TYPE_NORMAL, + LV_SLIDER_TYPE_SYM, + LV_SLIDER_TYPE_RANGE +}; +typedef uint8_t lv_slider_type_t; /*Data of slider*/ typedef struct { lv_bar_ext_t bar; /*Ext. of ancestor*/ /*New data for this type */ const lv_style_t * style_knob; /*Style of the knob*/ + lv_area_t left_knob_area; + lv_area_t right_knob_area; + int16_t *value_to_set; /* Which bar value to set */ const void * img_knob; uint8_t dragging :1; /*1: the slider is being dragged*/ } lv_slider_ext_t; @@ -81,6 +91,17 @@ static inline void lv_slider_set_value(lv_obj_t * slider, int16_t value, lv_anim lv_bar_set_value(slider, value, anim); } +/** + * Set a new value for the left knob of a slider + * @param slider pointer to a slider object + * @param left_value new value + * @param anim LV_ANIM_ON: set the value with an animation; LV_ANIM_OFF: change the value immediately + */ +static inline void lv_slider_set_left_value(const lv_obj_t * slider, int16_t left_value, lv_anim_enable_t anim) +{ + lv_bar_set_start_value(slider, left_value, anim); +} + /** * Set minimum and the maximum values of a bar * @param slider pointer to the slider object @@ -116,9 +137,14 @@ void lv_slider_set_knob_img(lv_obj_t * slider, const void * img_src); * @param slider pointer to a bar object * @param anim_time the animation time in milliseconds. */ -static inline void lv_slider_set_sym(lv_obj_t * slider, bool en) +static inline void lv_slider_set_type(lv_obj_t * slider, lv_slider_type_t type) { - lv_bar_set_sym(slider, en); + if(type == LV_SLIDER_TYPE_NORMAL) + lv_bar_set_type(slider, LV_BAR_TYPE_NORMAL); + else if(type == LV_SLIDER_TYPE_SYM) + lv_bar_set_type(slider, LV_BAR_TYPE_SYM); + else if(type == LV_SLIDER_TYPE_RANGE) + lv_bar_set_type(slider, LV_BAR_TYPE_CUSTOM); } /** @@ -134,12 +160,22 @@ void lv_slider_set_style(lv_obj_t * slider, lv_slider_style_t type, const lv_sty *====================*/ /** - * Get the value of a slider + * Get the value of the main knob of a slider * @param slider pointer to a slider object - * @return the value of the slider + * @return the value of the main knob of the slider */ int16_t lv_slider_get_value(const lv_obj_t * slider); +/** + * Get the value of the left knob of a slider + * @param slider pointer to a slider object + * @return the value of the left knob of the slider + */ +static inline int16_t lv_slider_get_left_value(const lv_obj_t * slider) +{ + return lv_bar_get_start_value(slider); +} + /** * Get the minimum value of a slider * @param slider pointer to a slider object @@ -189,9 +225,15 @@ static inline uint16_t lv_slider_get_anim_time(lv_obj_t * slider) * @param slider pointer to a bar object * @return true: symmetric is enabled; false: disable */ -static inline bool lv_slider_get_sym(lv_obj_t * slider) +static inline lv_slider_type_t lv_slider_get_type(lv_obj_t * slider) { - return lv_bar_get_sym(slider); + lv_bar_type_t type = lv_bar_get_type(slider); + if(type == LV_BAR_TYPE_SYM) + return LV_SLIDER_TYPE_SYM; + else if(type == LV_BAR_TYPE_CUSTOM) + return LV_SLIDER_TYPE_RANGE; + else + return LV_SLIDER_TYPE_NORMAL; } /**