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