1.. _observer: 2 3======== 4Observer 5======== 6 7.. _observer_overview: 8 9Overview 10******** 11 12The ``lv_observer`` module implements a standard `Observer pattern <https://en.wikipedia.org/wiki/Observer_pattern>`__. 13 14It consists of: 15 16- **subjects**: each containing a value 17- **observers**: attached to subjects to be notified on value change 18 19 20A typical use case looks like this: 21 22.. code-block:: c 23 24 //It's a global variable 25 lv_subject_t my_subject; 26 27 /*------- 28 * main.c 29 *-------*/ 30 31 extern lv_subject_t my_subject; 32 33 void main(void) 34 { 35 //Initialize the subject as integer with the default value of 10 36 lv_subject_init_int(&my_subject, 10); 37 38 some_module_init(); 39 } 40 41 /*-------------- 42 * some_module.c 43 *--------------*/ 44 45 extern lv_subject_t some_subject; 46 47 //Will be called when the related subject's value changes 48 static void some_observer_cb(lv_observer_t * observer, lv_subject_t * subject) 49 { 50 int32_t v = lv_subject_get_int(subject); 51 do_something(v); 52 } 53 54 void some_module_init(void) 55 { 56 //Subscribe to a subject 57 lv_subject_add_observer(&some_subject, some_observer_cb, NULL); 58 } 59 60 /*-------------- 61 * some_system.c 62 *--------------*/ 63 64 extern lv_subject_t some_subject; 65 66 void some_event(void) 67 { 68 //Set the subject's value to 30. It will notify `some_observer_cb` 69 lv_subject_set_int(&some_subject, 30); 70 } 71 72 73.. _observer_subject: 74 75Subject 76******* 77 78Subject initialization 79---------------------- 80 81Subjects have to be static or global :cpp:type:`lv_subject_t` type variables. 82 83To initialize a subject use ``lv_subject_init_<type>(&subject, params, init_value)``. 84The following initializations exist for types: 85 86- **Integer** ``void lv_subject_init_int(lv_subject_t * subject, int32_t value)`` 87- **String** ``void lv_subject_init_string(lv_subject_t * subject, char * buf, char * prev_buf, size_t size, const char * value)`` 88- **Pointer** ``void lv_subject_init_pointer(lv_subject_t * subject, void * value)`` 89- **Color** ``void lv_subject_init_color(lv_subject_t * subject, lv_color_t color)`` 90- **Group** ``void lv_subject_init_group(lv_subject_t * subject, lv_subject_t * list[], uint32_t list_len)`` 91 92 93Set subject value 94----------------- 95 96The following functions can be used to set a subject's value: 97 98- **Integer** ``void lv_subject_set_int(lv_subject_t * subject, int32_t value)`` 99- **String** ``void lv_subject_copy_string(lv_subject_t * subject, char * buf)`` 100- **Pointer** ``void lv_subject_set_pointer(lv_subject_t * subject, void * ptr)`` 101- **Color** ``void lv_subject_set_color(lv_subject_t * subject, lv_color_t color)`` 102 103Get subject's value 104------------------- 105 106The following functions can be used to get a subject's value: 107 108 109- **Integer** ``int32_t lv_subject_get_int(lv_subject_t * subject)`` 110- **String** ``const char * lv_subject_get_string(lv_subject_t * subject)`` 111- **Pointer** ``const void * lv_subject_get_pointer(lv_subject_t * subject)`` 112- **Color** ``lv_color_t lv_subject_get_color(lv_subject_t * subject)`` 113 114 115Get subject's previous value 116---------------------------- 117 118The following functions can be used to get a subject's previous value: 119 120 121- **Integer** ``int32_t lv_subject_get_previous_int(lv_subject_t * subject)`` 122- **String** ``const char * lv_subject_get_previous_string(lv_subject_t * subject)`` 123- **Pointer** ``const void * lv_subject_get_previous_pointer(lv_subject_t * subject)`` 124- **Color** ``lv_color_t lv_subject_get_previous_color(lv_subject_t * subject)`` 125 126.. _observer_observer: 127 128Observer 129******** 130 131Subscribe to a subject 132---------------------- 133 134To subscribe to a subject the following function can be used: 135 136.. code-block:: c 137 138 lv_observer_t * observer = lv_subject_add_observer(&some_subject, some_observer_cb, user_data); 139 140 141Where the observer callback should look like this: 142 143.. code-block:: c 144 145 static void some_observer_cb(lv_observer_t * observer, lv_subject_t * subject) 146 { 147 ... 148 } 149 150 151It's also possible to save a target widget when subscribing to a subject. 152In this case when widget is deleted, it will automatically unsubscribe from the subject. 153 154In the observer callback :cpp:expr:`lv_observer_get_target(observer)` can be used to get the saved widget. 155 156.. code-block:: c 157 158 lv_observer_t * observer = lv_subject_add_observer_obj(&some_subject, some_observer_cb, widget, user_data); 159 160 161In more generic case any pointer can be saved a target: 162 163.. code-block:: c 164 165 lv_observer_t * observer = lv_subject_add_observer_with_target(&some_subject, some_observer_cb, some_pointer, user_data); 166 167 168 169Unsubscribe from a subject 170-------------------------- 171 172.. code-block:: c 173 174 /* `observer` is the return value of `lv_subject_add_observer*` */ 175 lv_observer_remove(observer); 176 177To unsubscribe a widget from a given or all subject use: 178 179.. code-block:: c 180 181 lv_obj_remove_from_subject(widget, subject); /* `subject` can be NULL to unsubscribe from all */ 182 183.. _observer_subject_groups: 184 185Subject groups 186************** 187 188There are cases when a subject changes and the value of some other subjects are also required by the observer. 189As a practical example imagine an instrument which measures either voltage or current. 190To display the measured value on a label 3 things are required: 191 1921. What do we measure (current or voltage)? 1932. What is the measured value? 1943. What is the range or unit (mV, V, mA, A)? 195 196When any of these 3 parameters changes the label needs to be updated, 197and it needs to know all 3 parameters to compose its text. 198 199To handle this you can create an array from some existing subjects and pass 200this array as a parameter when you initialize a subject with group type. 201 202.. code-block:: c 203 204 static lv_subject_t * subject_list[3] = {&subject_1, &subject_2, &subject_3}; 205 lv_subject_init_group(&subject_all, subject_list, 3); /*The last parameter is the number of elements */ 206 207You can add observers to subject groups in the regular way. 208The trick is that when any element of the group is notified the subject group will be notified as well. 209 210The above Voltage/Current measurement example looks like this in the practice: 211 212.. code-block:: c 213 214 lv_obj_t * label = lv_label_create(lv_screen_active()); 215 216 lv_subject_t subject_mode; //Voltage or Current 217 lv_subject_t subject_value; //Measured value 218 lv_subject_t subject_unit; //The unit 219 lv_subject_t subject_all; //It will be the subject group 220 lv_subject_t * subject_list[3] = {&subject_mode, &subject_value, &subject_unit}; //The elements of the group 221 222 lv_subject_init_int(&subject_mode, 0); //Let's say 0 is Voltage, 1 is Current 223 lv_subject_init_int(&subject_value, 0); 224 lv_subject_init_pointer(&subject_unit, "V"); 225 lv_subject_init_group(&subject_all, subject_list, 3); 226 227 lv_subject_add_observer_obj(&subject_all, all_observer_cb, label, NULL); 228 229 ... 230 231 static void all_observer_cb(lv_observer_t * observer, lv_subject_t * subject) 232 { 233 lv_obj_t * label = lv_observer_get_target(observer); 234 lv_subject_t * subject_mode = lv_subject_get_group_element(subject, 0); 235 lv_subject_t * subject_value = lv_subject_get_group_element(subject, 1); 236 lv_subject_t * subject_unit = lv_subject_get_group_element(subject, 2); 237 238 int32_t mode = lv_subject_get_int(subject_mode); 239 int32_t value = lv_subject_get_int(subject_value); 240 const char * unit = lv_subject_get_pointer(subject_unit); 241 242 lv_label_set_text_fmt(label, "%s: %d %s", mode ? "Current" : "Voltage", value, unit); 243 } 244 245 246.. _observer_widget_binding: 247 248Widget binding 249************** 250 251Base Widget 252----------- 253 254Set a Widget flag if an integer subject's value is equal to a reference value, clear the flag otherwise 255 256.. code-block:: c 257 258 observer = lv_obj_bind_flag_if_eq(widget, &subject, LV_OBJ_FLAG_*, ref_value); 259 260Set a Widget flag if an integer subject's value is not equal to a reference value, clear the flag otherwise 261 262.. code-block:: c 263 264 observer = lv_obj_bind_flag_if_not_eq(widget, &subject, LV_OBJ_FLAG_*, ref_value); 265 266Set a Widget state if an integer subject's value is equal to a reference value, clear the flag otherwise 267 268.. code-block:: c 269 270 observer = lv_obj_bind_state_if_eq(widget, &subject, LV_STATE_*, ref_value); 271 272Set a Widget state if an integer subject's value is not equal to a reference value, clear the flag otherwise 273 274.. code-block:: c 275 276 observer = lv_obj_bind_state_if_not_eq(widget, &subject, LV_STATE_*, ref_value); 277 278Set an integer subject to 1 when a Widget is checked and set it 0 when unchecked. 279 280.. code-block:: c 281 282 observer = lv_obj_bind_checked(widget, &subject); 283 284Label 285----- 286 287Bind an integer, string, or pointer (pointing to a string) subject to a label. 288An optional format string can be added with 1 format specifier (e.g. ``"%d °C"``) 289If the format string is ``NULL`` the value will be used directly. In this case on string and pointer type subjects can be used. 290 291.. code-block:: c 292 293 observer = lv_label_bind_text(widget, &subject, format_string); 294 295 296Arc 297--- 298 299Bind an integer subject to an arc's value. 300 301.. code-block:: c 302 303 observer = lv_arc_bind_value(widget, &subject); 304 305Slider 306------ 307 308Bind an integer subject to a slider's value 309 310.. code-block:: c 311 312 observer = lv_slider_bind_value(widget, &subject); 313 314Roller 315------ 316 317Bind an integer subject to a roller's value 318 319.. code-block:: c 320 321 observer = lv_roller_bind_value(widget, &subject); 322 323 324Drop-down 325--------- 326Bind an integer subject to a drop-down's value 327 328.. code-block:: c 329 330 observer = lv_dropdown_bind_value(widget, &subject); 331 332.. _observer_example: 333 334Example 335******* 336 337.. include:: ../../examples/others/observer/index.rst 338 339.. _observer_api: 340 341API 342*** 343