diff --git a/examples/others/observer/lv_example_observer.h b/examples/others/observer/lv_example_observer.h index a43fca7b3a..2e03d051b2 100644 --- a/examples/others/observer/lv_example_observer.h +++ b/examples/others/observer/lv_example_observer.h @@ -31,6 +31,7 @@ void lv_example_observer_3(void); void lv_example_observer_4(void); void lv_example_observer_5(void); void lv_example_observer_6(void); +void lv_example_observer_7(void); /********************** * MACROS diff --git a/examples/others/observer/lv_example_observer_7.c b/examples/others/observer/lv_example_observer_7.c new file mode 100644 index 0000000000..1c278fcb4d --- /dev/null +++ b/examples/others/observer/lv_example_observer_7.c @@ -0,0 +1,109 @@ +#include "../../lv_examples.h" +#if LV_USE_OBSERVER && LV_USE_SLIDER && LV_USE_LABEL && LV_BUILD_EXAMPLES + +/*Default style for the light theme*/ +static lv_style_t style_screen; + +static lv_style_t style_slider_main; +static lv_style_t style_slider_indicator; +static lv_style_t style_slider_knob; + +/*Style to make the default theme dark*/ +static lv_style_t style_screen_dark; +static lv_style_t style_yellow; +static lv_style_t style_bg_dark; + +/*Subjects for a temperature and the selected theme*/ +static lv_subject_t subject_room_temperature; +static lv_subject_t subject_theme; + +/** + * Very simple and elegant way to create light and dark themes with subjects + */ +void lv_example_observer_7(void) +{ + /*------------------- + * Initialize subjects + *-------------------*/ + + lv_subject_init_int(&subject_theme, 0); /*Light theme by default*/ + lv_subject_init_int(&subject_room_temperature, 25); + + /*------------------- + * Initialize styles + *-------------------*/ + + /*Initialize the default light styles*/ + lv_style_init(&style_screen); + lv_style_set_bg_color(&style_screen, lv_color_hex3(0xccc)); + + lv_style_init(&style_slider_main); + lv_style_set_radius(&style_slider_main, 2); + lv_style_set_bg_color(&style_slider_main, lv_palette_main(LV_PALETTE_RED)); + + lv_style_init(&style_slider_indicator); + lv_style_set_bg_color(&style_slider_indicator, lv_palette_main(LV_PALETTE_RED)); + + lv_style_init(&style_slider_knob); + lv_style_set_bg_color(&style_slider_knob, lv_palette_main(LV_PALETTE_RED)); + lv_style_set_outline_color(&style_slider_knob, lv_color_hex3(0xfff)); + lv_style_set_outline_width(&style_slider_knob, 4); + + /*Initialize the styles that will overwrite color for the dark theme*/ + lv_style_init(&style_screen_dark); + lv_style_set_bg_color(&style_screen_dark, lv_color_hex3(0x444)); + lv_style_set_text_color(&style_screen_dark, lv_color_hex3(0xeee)); + + lv_style_init(&style_bg_dark); + lv_style_set_bg_color(&style_bg_dark, lv_color_hex3(0x222)); + lv_style_set_text_color(&style_bg_dark, lv_color_hex3(0xeee)); + lv_style_set_border_opa(&style_bg_dark, LV_OPA_30); + + lv_style_init(&style_yellow); + lv_style_set_bg_color(&style_yellow, lv_palette_main(LV_PALETTE_YELLOW)); + lv_style_set_outline_color(&style_yellow, lv_color_hex3(0x222)); + + /*---------------- + * Create widgets + *----------------*/ + + /*Add the light them to the screen and bind the dark style only if the + *dark theme is selected*/ + lv_obj_add_style(lv_screen_active(), &style_screen, 0); + lv_obj_bind_style(lv_screen_active(), &style_screen_dark, 0, &subject_theme, 1); + + /*Create a container and add the dark style if the dark theme is selected*/ + lv_obj_t * cont = lv_obj_create(lv_screen_active()); + lv_obj_bind_style(cont, &style_bg_dark, 0, &subject_theme, 1); + lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_COLUMN); + lv_obj_set_flex_align(cont, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); + lv_obj_set_size(cont, LV_SIZE_CONTENT, LV_SIZE_CONTENT); + lv_obj_align(cont, LV_ALIGN_TOP_MID, 0, 20); + + lv_obj_t * label = lv_label_create(cont); + lv_label_bind_text(label, &subject_room_temperature, "%d °C"); + + /*Bind the slider to the temperature subject and some of its styles to + *theme subject*/ + lv_obj_t * slider = lv_slider_create(cont); + lv_slider_bind_value(slider, &subject_room_temperature); + lv_obj_add_style(slider, &style_slider_main, 0); + lv_obj_add_style(slider, &style_slider_indicator, LV_PART_INDICATOR); + lv_obj_add_style(slider, &style_slider_knob, LV_PART_KNOB); + lv_obj_bind_style(slider, &style_yellow, 0, &subject_theme, 1); + lv_obj_bind_style(slider, &style_yellow, LV_PART_INDICATOR, &subject_theme, 1); + lv_obj_bind_style(slider, &style_yellow, LV_PART_KNOB, &subject_theme, 1); + lv_slider_set_range(slider, 20, 40); + + /*Create a dropdown to select a theme. + *Also bind make dark if the dark theme is selected*/ + lv_obj_t * dropdown = lv_dropdown_create(lv_screen_active()); + lv_obj_align(dropdown, LV_ALIGN_TOP_MID, 0, 120); + lv_dropdown_set_options(dropdown, "Light\nDark"); + lv_dropdown_bind_value(dropdown, &subject_theme); + lv_obj_bind_style(dropdown, &style_bg_dark, 0, &subject_theme, 1); + lv_obj_bind_style(lv_dropdown_get_list(dropdown), &style_bg_dark, 0, &subject_theme, 1); +} + + +#endif diff --git a/src/core/lv_obj_private.h b/src/core/lv_obj_private.h index 5b1100fb85..747e756eb3 100644 --- a/src/core/lv_obj_private.h +++ b/src/core/lv_obj_private.h @@ -78,7 +78,6 @@ struct _lv_obj_t { uint16_t is_deleting : 1; }; - /********************** * GLOBAL PROTOTYPES **********************/ diff --git a/src/core/lv_obj_style.c b/src/core/lv_obj_style.c index afffffa151..dc947c986f 100644 --- a/src/core/lv_obj_style.c +++ b/src/core/lv_obj_style.c @@ -322,6 +322,37 @@ void lv_obj_refresh_style(lv_obj_t * obj, lv_style_selector_t selector, lv_style LV_PROFILER_STYLE_END; } +void lv_obj_style_set_disabled(lv_obj_t * obj, const lv_style_t * style, lv_style_selector_t selector, bool dis) +{ + uint32_t i; + for(i = 0; i < obj->style_cnt; i++) { + if(obj->styles[i].style == style && obj->styles[i].selector == selector) { + if(dis == obj->styles[i].is_disabled) { + return; /*Already in the right state*/ + } + obj->styles[i].is_disabled = dis; + full_cache_refresh(obj, lv_obj_style_get_selector_part(selector)); + lv_obj_refresh_style(obj, selector, LV_STYLE_PROP_ANY); + return; + } + } + LV_LOG_WARN("%p style was not found on %p widget with %6x selector", (void *)style, (void *)obj, selector); +} + +bool lv_obj_style_get_disabled(lv_obj_t * obj, const lv_style_t * style, lv_style_selector_t selector) +{ + uint32_t i; + for(i = 0; i < obj->style_cnt; i++) { + if(obj->styles[i].style == style && obj->styles[i].selector == selector) { + return obj->styles[i].is_disabled; + } + } + + LV_LOG_WARN("%p style was not found on %p widget with %6x selector", (void *)style, (void *)obj, selector); + return false; +} + + void lv_obj_enable_style_refresh(bool en) { style_refr = en; @@ -773,6 +804,7 @@ static lv_style_res_t get_prop_core(const lv_obj_t * obj, lv_style_selector_t se lv_obj_style_t * obj_style = &obj->styles[i]; if(obj_style->is_trans == false) break; if(skip_trans) continue; + if(obj_style->is_disabled) continue; lv_part_t part_act = lv_obj_style_get_selector_part(obj->styles[i].selector); @@ -786,6 +818,7 @@ static lv_style_res_t get_prop_core(const lv_obj_t * obj, lv_style_selector_t se for(; i < obj->style_cnt; i++) { if((obj->styles[i].style->has_group & group) == 0) continue; + if(obj->styles[i].is_disabled) continue; lv_obj_style_t * obj_style = &obj->styles[i]; lv_part_t part_act = lv_obj_style_get_selector_part(obj->styles[i].selector); if(part_act != part) continue; @@ -1045,6 +1078,7 @@ static void full_cache_refresh(lv_obj_t * obj, lv_part_t part) obj->style_main_prop_is_set = 0; for(i = 0; i < obj->style_cnt; i++) { if(lv_obj_style_get_selector_part(obj->styles[i].selector) != LV_PART_MAIN) continue; + if(obj->styles[i].is_disabled) continue; lv_style_t * style = (lv_style_t *)obj->styles[i].style; uint32_t j; if(lv_style_is_const(style)) { @@ -1065,6 +1099,8 @@ static void full_cache_refresh(lv_obj_t * obj, lv_part_t part) obj->style_other_prop_is_set = 0; for(i = 0; i < obj->style_cnt; i++) { if(lv_obj_style_get_selector_part(obj->styles[i].selector) == LV_PART_MAIN) continue; + if(obj->styles[i].is_disabled) continue; + lv_style_t * style = (lv_style_t *)obj->styles[i].style; uint32_t j; if(lv_style_is_const(style)) { diff --git a/src/core/lv_obj_style.h b/src/core/lv_obj_style.h index 6db661e367..95e0458540 100644 --- a/src/core/lv_obj_style.h +++ b/src/core/lv_obj_style.h @@ -117,6 +117,24 @@ void lv_obj_report_style_change(lv_style_t * style); */ void lv_obj_refresh_style(lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop); +/** + * Temporary disable a style for a selector. It will look like is the style wasn't added + * @param obj pointer to an object + * @param style pointer to a style + * @param selector the selector of a style (e.g. LV_STATE_PRESSED | LV_PART_KNOB) + * @param dis true: disable the style, false: enable the style + */ +void lv_obj_style_set_disabled(lv_obj_t * obj, const lv_style_t * style, lv_style_selector_t selector, bool dis); + +/** + * Get if a given style is disabled on an object. + * @param obj pointer to an object + * @param style pointer to a style + * @param selector the selector of a style (e.g. LV_STATE_PRESSED | LV_PART_KNOB) + * @return true: disable the style, false: enable the style + */ +bool lv_obj_style_get_disabled(lv_obj_t * obj, const lv_style_t * style, lv_style_selector_t selector); + /** * Enable or disable automatic style refreshing when a new style is added/removed to/from an object * or any other style change happens. diff --git a/src/core/lv_obj_style_private.h b/src/core/lv_obj_style_private.h index e6be1279e5..97bb607f71 100644 --- a/src/core/lv_obj_style_private.h +++ b/src/core/lv_obj_style_private.h @@ -29,6 +29,7 @@ struct _lv_obj_style_t { uint32_t selector : 24; uint32_t is_local : 1; uint32_t is_trans : 1; + uint32_t is_disabled : 1; }; struct _lv_obj_style_transition_dsc_t { diff --git a/src/others/observer/lv_observer.c b/src/others/observer/lv_observer.c index 15a60a5a08..c61d0ff55b 100644 --- a/src/others/observer/lv_observer.c +++ b/src/others/observer/lv_observer.c @@ -34,6 +34,12 @@ typedef struct { flag_cond_t cond : 3; } flag_and_cond_t; +typedef struct { + const lv_style_t * style; + lv_style_selector_t selector; + int32_t value; +} bind_style_t; + typedef struct { lv_subject_t * subject; int32_t value; @@ -64,6 +70,8 @@ static void unsubscribe_on_delete_cb(lv_event_t * e); static void group_notify_cb(lv_observer_t * observer, lv_subject_t * subject); static lv_observer_t * bind_to_bitfield(lv_subject_t * subject, lv_obj_t * obj, lv_observer_cb_t cb, uint32_t flag, int32_t ref_value, bool inv, flag_cond_t cond); + +static void bind_style_observer_cb(lv_observer_t * observer, lv_subject_t * subject); static void obj_flag_observer_cb(lv_observer_t * observer, lv_subject_t * subject); static void obj_state_observer_cb(lv_observer_t * observer, lv_subject_t * subject); static void obj_value_changed_event_cb(lv_event_t * e); @@ -538,6 +546,34 @@ void lv_obj_add_subject_set_string_event(lv_obj_t * obj, lv_subject_t * subject, lv_obj_add_event_cb(obj, subject_set_string_free_user_data_event_cb, LV_EVENT_DELETE, user_data); } +lv_observer_t * lv_obj_bind_style(lv_obj_t * obj, const lv_style_t * style, lv_style_selector_t selector, + lv_subject_t * subject, int32_t ref_value) +{ + LV_ASSERT_NULL(subject); + LV_ASSERT_NULL(obj); + + if(subject->type != LV_SUBJECT_TYPE_INT) { + LV_LOG_WARN("Incompatible subject type: %d", subject->type); + return NULL; + } + + lv_obj_add_style(obj, style, selector); + + bind_style_t * p = lv_malloc(sizeof(bind_style_t)); + if(p == NULL) { + LV_LOG_WARN("Out of memory"); + return NULL; + } + + p->style = style; + p->selector = selector; + p->value = ref_value; + + lv_observer_t * observable = lv_subject_add_observer_obj(subject, bind_style_observer_cb, obj, p); + observable->auto_free_user_data = 1; + return observable; +} + lv_observer_t * lv_obj_bind_flag_if_eq(lv_obj_t * obj, lv_subject_t * subject, lv_obj_flag_t flag, int32_t ref_value) { lv_observer_t * observable = bind_to_bitfield(subject, obj, obj_flag_observer_cb, flag, ref_value, false, FLAG_COND_EQ); @@ -816,6 +852,15 @@ static lv_observer_t * bind_to_bitfield(lv_subject_t * subject, lv_obj_t * obj, return observable; } +static void bind_style_observer_cb(lv_observer_t * observer, lv_subject_t * subject) +{ + bind_style_t * p = observer->user_data; + + int32_t current_v = lv_subject_get_int(subject); + bool dis = current_v != p->value; + lv_obj_style_set_disabled(observer->target, p->style, p->selector, dis); +} + static void obj_flag_observer_cb(lv_observer_t * observer, lv_subject_t * subject) { flag_and_cond_t * p = observer->user_data; diff --git a/src/others/observer/lv_observer.h b/src/others/observer/lv_observer.h index f3ec9514fb..9545fb5c65 100644 --- a/src/others/observer/lv_observer.h +++ b/src/others/observer/lv_observer.h @@ -337,6 +337,18 @@ void lv_obj_add_subject_set_int_event(lv_obj_t * obj, lv_subject_t * subject, lv void lv_obj_add_subject_set_string_event(lv_obj_t * obj, lv_subject_t * subject, lv_event_code_t trigger, const char * value); +/** + * Disable a style if a subject's value is not equal to a reference value + * @param obj pointer to Widget + * @param style pointer to a style + * @param selector pointer to a selector + * @param subject pointer to Subject + * @param ref_value reference value to compare Subject's value with + * @return pointer to newly-created Observer + */ +lv_observer_t * lv_obj_bind_style(lv_obj_t * obj, const lv_style_t * style, lv_style_selector_t selector, + lv_subject_t * subject, int32_t ref_value); + /** * Set Widget's flag(s) if an integer Subject's value is equal to a reference value, clear flag otherwise. * @param obj pointer to Widget diff --git a/src/others/xml/lv_xml.c b/src/others/xml/lv_xml.c index 789f41f20b..2dfa748340 100644 --- a/src/others/xml/lv_xml.c +++ b/src/others/xml/lv_xml.c @@ -124,6 +124,7 @@ void lv_xml_init(void) lv_xml_widget_register("lv_obj-screen_create_event", lv_obj_xml_screen_create_event_create, lv_obj_xml_screen_create_event_apply); + lv_xml_widget_register("lv_obj-bind_style", lv_obj_xml_bind_style_create, lv_obj_xml_bind_style_apply); lv_xml_widget_register("lv_obj-bind_flag_if_eq", lv_obj_xml_bind_flag_create, lv_obj_xml_bind_flag_apply); lv_xml_widget_register("lv_obj-bind_flag_if_not_eq", lv_obj_xml_bind_flag_create, lv_obj_xml_bind_flag_apply); lv_xml_widget_register("lv_obj-bind_flag_if_gt", lv_obj_xml_bind_flag_create, lv_obj_xml_bind_flag_apply); diff --git a/src/others/xml/parsers/lv_xml_obj_parser.c b/src/others/xml/parsers/lv_xml_obj_parser.c index 81191ef9e4..92c468163f 100644 --- a/src/others/xml/parsers/lv_xml_obj_parser.c +++ b/src/others/xml/parsers/lv_xml_obj_parser.c @@ -164,8 +164,8 @@ void lv_obj_xml_style_apply(lv_xml_parser_state_t * state, const char ** attrs) { const char * name = lv_xml_get_value_of(attrs, "name"); if(name == NULL) { - /*Silently ignore this issue, as it might valid the name is not resolved during - *parameter replacement if there is no default value.*/ + /*Silently ignore this issue. + *The name set to NULL if there there was no default value when resolving params*/ return; } lv_xml_style_t * xml_style = lv_xml_get_style_by_name(&state->scope, name); @@ -370,6 +370,54 @@ void lv_obj_xml_subject_increment_apply(lv_xml_parser_state_t * state, const cha lv_obj_add_subject_increment_event(item, subject, trigger, step, min_v, max_v); } +void * lv_obj_xml_bind_style_create(lv_xml_parser_state_t * state, const char ** attrs) +{ + LV_UNUSED(attrs); + void * item = lv_xml_state_get_parent(state); + return item; +} + +void lv_obj_xml_bind_style_apply(lv_xml_parser_state_t * state, const char ** attrs) +{ + const char * name = lv_xml_get_value_of(attrs, "name"); + if(name == NULL) { + /*Silently ignore this issue. + *The name set to NULL if there there was no default value when resolving params*/ + return; + } + lv_xml_style_t * xml_style = lv_xml_get_style_by_name(&state->scope, name); + if(xml_style == NULL) { + LV_LOG_WARN("`%s` style is not found", name); + return; + } + const char * subject_str = lv_xml_get_value_of(attrs, "subject"); + + if(subject_str == NULL) { + LV_LOG_WARN("`subject` is missing in lv_obj bind_style"); + return; + } + + lv_subject_t * subject = lv_xml_get_subject(&state->scope, subject_str); + if(subject == NULL) { + LV_LOG_WARN("Subject `%s` doesn't exist in lv_obj bind_style", subject_str); + return; + } + + const char * ref_value_str = lv_xml_get_value_of(attrs, "ref_value"); + if(ref_value_str == NULL) { + LV_LOG_WARN("`ref_value` is missing in lv_obj bind_style"); + return; + } + + int32_t ref_value = lv_xml_atoi(ref_value_str); + + const char * selector_str = lv_xml_get_value_of(attrs, "selector"); + lv_style_selector_t selector = get_selector(selector_str); + + void * item = lv_xml_state_get_parent(state); + lv_obj_bind_style(item, &xml_style->style, selector, subject, ref_value); +} + void * lv_obj_xml_bind_flag_create(lv_xml_parser_state_t * state, const char ** attrs) { LV_UNUSED(attrs); diff --git a/src/others/xml/parsers/lv_xml_obj_parser.h b/src/others/xml/parsers/lv_xml_obj_parser.h index 28b462c2ed..8dd0eea570 100644 --- a/src/others/xml/parsers/lv_xml_obj_parser.h +++ b/src/others/xml/parsers/lv_xml_obj_parser.h @@ -45,6 +45,9 @@ void lv_obj_xml_subject_set_apply(lv_xml_parser_state_t * state, const char ** a void * lv_obj_xml_subject_increment_create(lv_xml_parser_state_t * state, const char ** attrs); void lv_obj_xml_subject_increment_apply(lv_xml_parser_state_t * state, const char ** attrs); +void * lv_obj_xml_bind_style_create(lv_xml_parser_state_t * state, const char ** attrs); +void lv_obj_xml_bind_style_apply(lv_xml_parser_state_t * state, const char ** attrs); + void * lv_obj_xml_bind_flag_create(lv_xml_parser_state_t * state, const char ** attrs); void lv_obj_xml_bind_flag_apply(lv_xml_parser_state_t * state, const char ** attrs); diff --git a/tests/src/test_cases/xml/test_xml_style.c b/tests/src/test_cases/xml/test_xml_style.c index 87ce12cd25..3a300d472c 100644 --- a/tests/src/test_cases/xml/test_xml_style.c +++ b/tests/src/test_cases/xml/test_xml_style.c @@ -182,4 +182,30 @@ void test_xml_style_remove(void) lv_xml_component_unregister("comp1"); } +void test_xml_style_binding(void) +{ + const char * comp1_xml = { + "" + " " + " " + " " + " " + "