1
0
mirror of https://github.com/lvgl/lvgl.git synced 2025-01-28 07:03:00 +08:00

feat(bar): reverse drawing direction of lv_bar if min > max (#4654)

Signed-off-by: wangxuedong <wangxuedong@xiaomi.com>
This commit is contained in:
xaowang96 2023-10-21 05:22:56 +08:00 committed by GitHub
parent c0356c9380
commit 8f57f12a44
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 217 additions and 65 deletions

View File

@ -33,7 +33,9 @@ A new value can be set by
``lv_bar_set_value(bar, new_value, LV_ANIM_ON/OFF)``. The value is
interpreted in a range (minimum and maximum values) which can be
modified with :cpp:expr:`lv_bar_set_range(bar, min, max)`. The default range is
0..100.
0..100, and the default drawing direction is from left to right in horizontal mode and
bottom to top in vertical mode. If the minimum value is greater than the maximum value, like
100..0, the drawing direction changes to the opposite direction.
The new value in :cpp:func:`lv_bar_set_value` can be set with or without an
animation depending on the last parameter (``LV_ANIM_ON/OFF``).

View File

@ -35,6 +35,9 @@ To set an initial value use :cpp:expr:`lv_slider_set_value(slider, new_value, LV
animation time is set by the styles' ``anim_time`` property.
To specify the range (min, max values), :cpp:expr:`lv_slider_set_range(slider, min , max)` can be used.
The default range is 0..100, and the default drawing direction is from left to right in horizontal mode and
bottom to top in vertical mode. If the minimum value is greater than the maximum value, like
100..0, the drawing direction changes to the opposite direction.
Modes
-----

View File

@ -34,3 +34,9 @@ Custom drawer to show the current value
.. lv_example:: widgets/bar/lv_example_bar_6
:language: c
Bar with opposite direction
---------------------------
.. lv_example:: widgets/bar/lv_example_bar_7
:language: c

View File

@ -0,0 +1,23 @@
#include "../../lv_examples.h"
#if LV_USE_BAR && LV_BUILD_EXAMPLES
/**
* Bar with opposite direction
*/
void lv_example_bar_7(void)
{
lv_obj_t * label;
lv_obj_t * bar_tob = lv_bar_create(lv_scr_act());
lv_obj_set_size(bar_tob, 20, 200);
lv_bar_set_range(bar_tob, 100, 0);
lv_bar_set_value(bar_tob, 70, LV_ANIM_OFF);
lv_obj_align(bar_tob, LV_ALIGN_CENTER, 0, -30);
label = lv_label_create(lv_scr_act());
lv_label_set_text(label, "From top to bottom");
lv_obj_align_to(label, bar_tob, LV_ALIGN_OUT_TOP_MID, 0, -5);
}
#endif

View File

@ -0,0 +1 @@
pass

View File

@ -36,6 +36,7 @@ void lv_example_bar_3(void);
void lv_example_bar_4(void);
void lv_example_bar_5(void);
void lv_example_bar_6(void);
void lv_example_bar_7(void);
void lv_example_button_1(void);
void lv_example_button_2(void);
@ -118,6 +119,7 @@ void lv_example_scale_5(void);
void lv_example_slider_1(void);
void lv_example_slider_2(void);
void lv_example_slider_3(void);
void lv_example_slider_4(void);
void lv_example_span_1(void);

View File

@ -17,4 +17,8 @@ Slider with extended drawer
.. lv_example:: widgets/slider/lv_example_slider_3
:language: c
Slider with opposite direction
------------------------------
.. lv_example:: widgets/slider/lv_example_slider_4
:language: c

View File

@ -0,0 +1,34 @@
#include "../../lv_examples.h"
#if LV_USE_SLIDER && LV_BUILD_EXAMPLES
static void slider_event_cb(lv_event_t * e);
static lv_obj_t * slider_label;
/**
* Slider with opposite direction
*/
void lv_example_slider_4(void)
{
/*Create a slider in the center of the display*/
lv_obj_t * slider = lv_slider_create(lv_scr_act());
lv_obj_center(slider);
lv_obj_add_event(slider, slider_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
/*Reverse the direction of the slider*/
lv_slider_set_range(slider, 100, 0);
/*Create a label below the slider*/
slider_label = lv_label_create(lv_scr_act());
lv_label_set_text(slider_label, "0%");
lv_obj_align_to(slider_label, slider, LV_ALIGN_OUT_BOTTOM_MID, 0, 10);
}
static void slider_event_cb(lv_event_t * e)
{
lv_obj_t * slider = lv_event_get_target(e);
char buf[8];
lv_snprintf(buf, sizeof(buf), "%d%%", (int)lv_slider_get_value(slider));
lv_label_set_text(slider_label, buf);
lv_obj_align_to(slider_label, slider, LV_ALIGN_OUT_BOTTOM_MID, 0, 10);
}
#endif

View File

@ -0,0 +1,21 @@
#
# Slider with opposite direction
#
def slider_event_cb(e):
slider = e.get_target_obj()
slider_label.set_text("{:d}%".format(slider.get_value()))
slider_label.align_to(slider, lv.ALIGN.OUT_BOTTOM_MID, 0, 10)
# Create a slider in the center of the display
slider = lv.slider(lv.screen_active())
slider.center()
slider.add_event(slider_event_cb, lv.EVENT.VALUE_CHANGED, None)
slider.set_range(100, 0)
# Create a label below the slider
slider_label = lv.label(lv.screen_active())
slider_label.set_text("0%")
slider_label.align_to(slider, lv.ALIGN.OUT_BOTTOM_MID, 0, 10)

View File

@ -99,6 +99,7 @@ void lv_bar_set_value(lv_obj_t * obj, int32_t value, lv_anim_enable_t anim)
value = value < bar->start_value ? bar->start_value : value; /*Can't be smaller than the left value*/
if(bar->cur_value == value) return;
lv_bar_set_value_with_anim(obj, value, &bar->cur_value, &bar->cur_value_anim, anim);
}
@ -116,6 +117,7 @@ void lv_bar_set_start_value(lv_obj_t * obj, int32_t value, lv_anim_enable_t anim
value = value > bar->cur_value ? bar->cur_value : value; /*Can't be greater than the right value*/
if(bar->start_value == value) return;
lv_bar_set_value_with_anim(obj, value, &bar->start_value, &bar->start_value_anim, anim);
}
@ -125,27 +127,27 @@ void lv_bar_set_range(lv_obj_t * obj, int32_t min, int32_t max)
lv_bar_t * bar = (lv_bar_t *)obj;
if(max < min) {
LV_LOG_WARN("error range: min = %" LV_PRId32 ", max = %" LV_PRId32, min, max);
return;
}
bar->val_reversed = min > max;
if(bar->min_value == min && bar->max_value == max) return;
int32_t real_min = bar->val_reversed ? max : min;
int32_t real_max = bar->val_reversed ? min : max;
if(bar->min_value == real_min && bar->max_value == real_max) return;
bar->max_value = max;
bar->min_value = min;
bar->max_value = real_max;
bar->min_value = real_min;
if(lv_bar_get_mode(obj) != LV_BAR_MODE_RANGE)
bar->start_value = min;
bar->start_value = real_min;
if(bar->cur_value > max) {
bar->cur_value = max;
if(bar->cur_value > real_max) {
bar->cur_value = real_max;
lv_bar_set_value(obj, bar->cur_value, LV_ANIM_OFF);
}
if(bar->cur_value < min) {
bar->cur_value = min;
if(bar->cur_value < real_min) {
bar->cur_value = real_min;
lv_bar_set_value(obj, bar->cur_value, LV_ANIM_OFF);
}
lv_obj_invalidate(obj);
}
@ -188,7 +190,7 @@ int32_t lv_bar_get_min_value(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_bar_t * bar = (lv_bar_t *)obj;
return bar->min_value;
return bar->val_reversed ? bar->max_value : bar->min_value;
}
int32_t lv_bar_get_max_value(const lv_obj_t * obj)
@ -196,7 +198,7 @@ int32_t lv_bar_get_max_value(const lv_obj_t * obj)
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_bar_t * bar = (lv_bar_t *)obj;
return bar->max_value;
return bar->val_reversed ? bar->min_value : bar->max_value;
}
lv_bar_mode_t lv_bar_get_mode(lv_obj_t * obj)
@ -207,6 +209,15 @@ lv_bar_mode_t lv_bar_get_mode(lv_obj_t * obj)
return bar->mode;
}
bool lv_bar_is_symmetrical(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_bar_t * bar = (lv_bar_t *)obj;
return bar->mode == LV_BAR_MODE_SYMMETRICAL && bar->min_value < 0 && bar->max_value > 0 &&
bar->start_value == bar->min_value;
}
/**********************
* STATIC FUNCTIONS
**********************/
@ -226,6 +237,7 @@ static void lv_bar_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
bar->indic_area.y1 = 0;
bar->indic_area.y2 = 0;
bar->mode = LV_BAR_MODE_NORMAL;
bar->val_reversed = false;
lv_bar_init_anim(obj, &bar->cur_value_anim);
lv_bar_init_anim(obj, &bar->start_value_anim);
@ -269,9 +281,7 @@ static void draw_indic(lv_event_t * e)
}
bool hor = barw >= barh;
bool sym = false;
if(bar->mode == LV_BAR_MODE_SYMMETRICAL && bar->min_value < 0 && bar->max_value > 0 &&
bar->start_value == bar->min_value) sym = true;
bool sym = lv_bar_is_symmetrical(obj);
/*Calculate the indicator area*/
lv_coord_t bg_left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
@ -345,8 +355,15 @@ static void draw_indic(lv_event_t * e)
anim_cur_value_x = (int32_t)((int32_t)anim_length * (bar->cur_value - bar->min_value)) / range;
}
/**
* The drawing drection of the bar can be reversed only when one of the two conditions(value inversion
* or horizontal direction base dir is LV_BASE_DIR_RTL) is met.
*/
lv_base_dir_t base_dir = lv_obj_get_style_base_dir(obj, LV_PART_MAIN);
if(base_dir == LV_BASE_DIR_RTL) {
bool hor_need_reversed = hor && base_dir == LV_BASE_DIR_RTL;
bool reversed = bar->val_reversed ^ hor_need_reversed;
if(reversed) {
/*Swap axes*/
lv_coord_t * tmp;
tmp = axis1;
@ -365,31 +382,43 @@ static void draw_indic(lv_event_t * e)
*axis1 = *axis2 - anim_cur_value_x + 1;
*axis2 -= anim_start_value_x;
}
if(sym) {
lv_coord_t zero, shift;
shift = (-bar->min_value * anim_length) / range;
if(hor) {
zero = *axis1 + shift;
if(*axis2 > zero)
*axis1 = zero;
lv_coord_t * left = reversed ? axis2 : axis1;
lv_coord_t * right = reversed ? axis1 : axis2;
if(reversed)
zero = *axis1 - shift + 1;
else
zero = *axis1 + shift;
if(*axis2 > zero) {
*right = *axis2;
*left = zero;
}
else {
*axis1 = *axis2;
*axis2 = zero;
*left = *axis2;
*right = zero;
}
}
else {
zero = *axis2 - shift + 1;
if(*axis1 > zero)
*axis2 = zero;
else {
*axis2 = *axis1;
*axis1 = zero;
lv_coord_t * top = reversed ? axis2 : axis1;
lv_coord_t * bottom = reversed ? axis1 : axis2;
if(reversed)
zero = *axis2 + shift;
else
zero = *axis2 - shift + 1;
if(*axis1 > zero) {
*bottom = *axis1;
*top = zero;
}
if(*axis2 < *axis1) {
/*swap*/
zero = *axis1;
*axis1 = *axis2;
*axis2 = zero;
else {
*top = *axis1;
*bottom = zero;
}
}
}

View File

@ -55,6 +55,7 @@ typedef struct {
int32_t max_value; /**< Maximum value of the bar*/
int32_t start_value; /**< Start value of the bar*/
lv_area_t indic_area; /**< Save the indicator area. Might be used by derived types*/
bool val_reversed; /**< Whether value been reversed */
_lv_bar_anim_t cur_value_anim;
_lv_bar_anim_t start_value_anim;
lv_bar_mode_t mode : 2; /**< Type of bar*/
@ -99,6 +100,7 @@ void lv_bar_set_start_value(lv_obj_t * obj, int32_t start_value, lv_anim_enable_
* @param obj pointer to the bar object
* @param min minimum value
* @param max maximum value
* @note If min is greater than max, the drawing direction becomes to the oppsite direction.
*/
void lv_bar_set_range(lv_obj_t * obj, int32_t min, int32_t max);
@ -148,6 +150,13 @@ int32_t lv_bar_get_max_value(const lv_obj_t * obj);
*/
lv_bar_mode_t lv_bar_get_mode(lv_obj_t * obj);
/**
* Give the bar is in symmetrical mode or not
* @param obj pointer to bar object
* @return true: in symmetrical mode false : not in
*/
bool lv_bar_is_symmetrical(lv_obj_t * obj);
/**********************
* MACROS
**********************/

View File

@ -24,7 +24,8 @@
*********************/
#define MY_CLASS &lv_slider_class
#define LV_SLIDER_KNOB_COORD(is_rtl, area) (is_rtl ? area.x1 : area.x2)
#define LV_SLIDER_KNOB_COORD(is_reversed, area) (is_reversed ? area.x1 : area.x2)
#define LV_SLIDER_KNOB_COORD_VERTICAL(is_reversed, area) (is_reversed ? area.y2 : area.y1)
/**********************
* TYPEDEFS
@ -238,24 +239,24 @@ static void draw_knob(lv_event_t * e)
const bool is_rtl = LV_BASE_DIR_RTL == lv_obj_get_style_base_dir(obj, LV_PART_MAIN);
const bool is_horizontal = is_slider_horizontal(obj);
const bool is_reversed = slider->bar.val_reversed ^ (is_rtl && is_horizontal);
lv_area_t knob_area;
lv_coord_t knob_size;
bool is_symmetrical = false;
if(slider->bar.mode == LV_BAR_MODE_SYMMETRICAL && slider->bar.min_value < 0 &&
slider->bar.max_value > 0) is_symmetrical = true;
bool is_symmetrical = lv_slider_is_symmetrical(obj);
if(is_horizontal) {
knob_size = lv_obj_get_height(obj);
if(is_symmetrical && slider->bar.cur_value < 0) knob_area.x1 = slider->bar.indic_area.x1;
else knob_area.x1 = LV_SLIDER_KNOB_COORD(is_rtl, slider->bar.indic_area);
if(is_symmetrical &&
slider->bar.cur_value < 0) knob_area.x1 = LV_SLIDER_KNOB_COORD(!is_reversed, slider->bar.indic_area);
else knob_area.x1 = LV_SLIDER_KNOB_COORD(is_reversed, slider->bar.indic_area);
}
else {
knob_size = lv_obj_get_width(obj);
if(is_symmetrical && slider->bar.cur_value < 0) knob_area.y1 = slider->bar.indic_area.y2;
else knob_area.y1 = slider->bar.indic_area.y1;
if(is_symmetrical &&
slider->bar.cur_value < 0) knob_area.y1 = LV_SLIDER_KNOB_COORD_VERTICAL(!is_reversed, slider->bar.indic_area);
else knob_area.y1 = LV_SLIDER_KNOB_COORD_VERTICAL(is_reversed, slider->bar.indic_area);
}
lv_draw_rect_dsc_t knob_rect_dsc;
lv_draw_rect_dsc_init(&knob_rect_dsc);
lv_obj_init_draw_rect_dsc(obj, LV_PART_KNOB, &knob_rect_dsc);
@ -276,11 +277,11 @@ static void draw_knob(lv_event_t * e)
/*Calculate the second knob area*/
if(is_horizontal) {
/*use !is_rtl to get the other knob*/
knob_area.x1 = LV_SLIDER_KNOB_COORD(!is_rtl, slider->bar.indic_area);
/*use !is_reversed to get the other knob*/
knob_area.x1 = LV_SLIDER_KNOB_COORD(!is_reversed, slider->bar.indic_area);
}
else {
knob_area.y1 = slider->bar.indic_area.y2;
knob_area.y1 = LV_SLIDER_KNOB_COORD_VERTICAL(!is_reversed, slider->bar.indic_area);
}
position_knob(obj, &knob_area, knob_size, is_horizontal);
lv_area_copy(&slider->left_knob_area, &knob_area);
@ -338,24 +339,21 @@ static void drag_start(lv_obj_t * obj)
else if(mode == LV_SLIDER_MODE_RANGE) {
lv_indev_get_point(lv_indev_get_act(), &p);
lv_obj_transform_point(obj, &p, true, true);
bool hor = is_slider_horizontal(obj);
lv_base_dir_t base_dir = lv_obj_get_style_base_dir(obj, LV_PART_MAIN);
const bool is_rtl = LV_BASE_DIR_RTL == lv_obj_get_style_base_dir(obj, LV_PART_MAIN);
const bool is_horizontal = is_slider_horizontal(obj);
const bool is_reversed = slider->bar.val_reversed ^ (is_rtl && is_horizontal);
lv_coord_t dist_left, dist_right;
if(hor) {
if((base_dir != LV_BASE_DIR_RTL && p.x > slider->right_knob_area.x2) || (base_dir == LV_BASE_DIR_RTL &&
p.x < slider->right_knob_area.x1)) {
if(is_horizontal) {
if((!is_reversed && p.x > slider->right_knob_area.x2) || (is_reversed && p.x < slider->right_knob_area.x1)) {
slider->value_to_set = &slider->bar.cur_value;
}
else if((base_dir != LV_BASE_DIR_RTL && p.x < slider->left_knob_area.x1) || (base_dir == LV_BASE_DIR_RTL &&
p.x > slider->left_knob_area.x2)) {
else if((!is_reversed && p.x < slider->left_knob_area.x1) || (is_reversed && p.x > slider->left_knob_area.x2)) {
slider->value_to_set = &slider->bar.start_value;
}
else {
/*Calculate the distance from each knob*/
dist_left = LV_ABS((slider->left_knob_area.x1 + (slider->left_knob_area.x2 - slider->left_knob_area.x1) / 2) - p.x);
dist_right = LV_ABS((slider->right_knob_area.x1 + (slider->right_knob_area.x2 - slider->right_knob_area.x1) / 2) - p.x);
/*Use whichever one is closer*/
if(dist_right < dist_left) {
slider->value_to_set = &slider->bar.cur_value;
@ -368,10 +366,10 @@ static void drag_start(lv_obj_t * obj)
}
}
else {
if(p.y < slider->right_knob_area.y1) {
if((!is_reversed && p.y < slider->right_knob_area.y1) || (is_reversed && p.y > slider->right_knob_area.y2)) {
slider->value_to_set = &slider->bar.cur_value;
}
else if(p.y > slider->left_knob_area.y2) {
else if((!is_reversed && p.y > slider->left_knob_area.y2) || (is_reversed && p.y < slider->left_knob_area.y1)) {
slider->value_to_set = &slider->bar.start_value;
}
else {
@ -424,13 +422,17 @@ static void update_knob_pos(lv_obj_t * obj, bool check_drag)
int32_t new_value = 0;
const int32_t range = slider->bar.max_value - slider->bar.min_value;
const bool is_rtl = LV_BASE_DIR_RTL == lv_obj_get_style_base_dir(obj, LV_PART_MAIN);
const bool is_horizontal = is_slider_horizontal(obj);
const bool is_reversed = slider->bar.val_reversed ^ (is_rtl && is_horizontal);
if(is_hor) {
const lv_coord_t bg_left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
const lv_coord_t bg_right = lv_obj_get_style_pad_right(obj, LV_PART_MAIN);
const lv_coord_t w = lv_obj_get_width(obj);
const lv_coord_t indic_w = w - bg_left - bg_right;
if(lv_obj_get_style_base_dir(obj, LV_PART_MAIN) == LV_BASE_DIR_RTL) {
if(is_reversed) {
/*Make the point relative to the indicator*/
new_value = (obj->coords.x2 - bg_right) - p.x;
}
@ -449,9 +451,16 @@ static void update_knob_pos(lv_obj_t * obj, bool check_drag)
const lv_coord_t h = lv_obj_get_height(obj);
const lv_coord_t indic_h = h - bg_bottom - bg_top;
/*Make the point relative to the indicator*/
new_value = p.y - (obj->coords.y2 + bg_bottom);
new_value = (-new_value * range + indic_h / 2) / indic_h;
if(is_reversed) {
/*Make the point relative to the indicator*/
new_value = p.y - (obj->coords.y1 + bg_top);
}
else {
/*Make the point relative to the indicator*/
new_value = p.y - (obj->coords.y2 + bg_bottom);
new_value = -new_value;
}
new_value = (new_value * range + indic_h / 2) / indic_h;
new_value += slider->bar.min_value;
}

View File

@ -166,7 +166,7 @@ bool lv_slider_is_dragged(const lv_obj_t * obj);
/**
* Get the mode of the slider.
* @param slider pointer to a bar object
* @param slider pointer to a slider object
* @return see ::lv_slider_mode_t
*/
static inline lv_slider_mode_t lv_slider_get_mode(lv_obj_t * slider)
@ -177,6 +177,16 @@ static inline lv_slider_mode_t lv_slider_get_mode(lv_obj_t * slider)
else return LV_SLIDER_MODE_NORMAL;
}
/**
* Give the slider is in symmetrical mode or not
* @param obj pointer to slider object
* @return true: in symmetrical mode false : not in
*/
static inline bool lv_slider_is_symmetrical(lv_obj_t * obj)
{
return lv_bar_is_symmetrical(obj);
}
/**********************
* MACROS
**********************/

View File

@ -199,10 +199,9 @@ void test_bar_normal(void)
lv_obj_set_style_bg_opa(test_bar, LV_OPA_30, LV_PART_MAIN);
lv_obj_set_style_bg_color(test_bar, bg_color, LV_PART_MAIN);
lv_obj_set_style_bg_color(test_bar, indic_color, LV_PART_INDICATOR);
lv_bar_set_range(test_bar, 0, 100);
lv_bar_set_range(test_bar, 100, 0);
lv_bar_set_value(test_bar, 30, LV_ANIM_OFF);
lv_obj_set_size(test_bar, h, w);
lv_obj_set_style_base_dir(test_bar, LV_BASE_DIR_RTL, 0);
lv_obj_align(test_bar, LV_ALIGN_TOP_LEFT, x, y);
TEST_ASSERT_EQUAL_SCREENSHOT("bar_1.png");
}