1.. _widget_id:
2
3=========
4Widget ID
5=========
6
7Widgets can optionally have identifiers added to their functionality if needed for
8the application.  Exactly how that happens is designed to be flexible, and can morph
9with the needs of the application.  It can even be a timestamp or other data current
10at the time the Widget was created.
11
12
13
14.. _widget_id_usage:
15
16Usage
17*****
18
19Enable Widget ID functionality by setting :c:macro:`LV_USE_OBJ_ID` to ``1`` in ``lv_conf.h``.
20
21Once enabled, several things change:
22
23- each Widget will now have a ``void *`` field called ``id``;
24- these two API functions become available:
25
26  - :cpp:expr:`lv_obj_get_id(widget)`,
27  - :cpp:expr:`lv_obj_get_child_by_id(widget, id)`;
28
29- several more Widget-ID-related API functions become available if
30  :c:macro:`LV_USE_OBJ_ID_BUILTIN` is non-zero (more on this below);
31- two additional configuration macros both :c:macro:`LV_OBJ_ID_AUTO_ASSIGN` and
32  :c:macro:`LV_USE_OBJ_ID_BUILTIN` now have meaning.
33
34
35:c:macro:`LV_OBJ_ID_AUTO_ASSIGN`
36--------------------------------
37
38This macro  in ``lv_conf.h`` defaults to whatever value :c:macro:`LV_USE_OBJ_ID`
39equates to.  You can change this if you wish.  Either way, if it equates to a
40non-zero value, it causes two things to happen:
41
42- :cpp:expr:`lv_obj_assign_id(class_p, widget)` will be called at the end of each
43  Widget's creation, and
44- :cpp:expr:`lv_obj_free_id(widget)` will be called at the end of the sequence when
45  each Widget is deleted.
46
47Because of this timing, custom versions of these functions can be used according to
48the below, and they can even be used like "event hooks" to implement a trace
49operation that occurs when each Widget is created and deleted.
50
51:cpp:expr:`lv_obj_assign_id(class_p, widget)`
52~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
53
54This function (whether provided by LVGL or by you --- more on this below) is
55responsible for assigning a value to the Widget's ``id`` field, and possibly do
56other things, depending on the implementation.
57
58:cpp:expr:`lv_obj_free_id(widget)`
59~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
60
61This function (whether provided by LVGL or by you --- more on this below) is
62responsible for doing the clean-up of any resources allocated by
63:cpp:func:`lv_obj_assign_id()`
64
65
66:c:macro:`LV_USE_OBJ_ID_BUILTIN`
67--------------------------------
68
69This macro  in ``lv_conf.h`` equates to ``1`` by default.  You can change this if you
70wish.  When it equates to a non-zero value the following function implementations are
71provided by LVGL:
72
73- :cpp:expr:`lv_obj_assign_id(class_p, widget)`
74- :cpp:expr:`lv_obj_free_id(widget)`
75- :cpp:expr:`lv_obj_set_id(widget, id)`
76- :cpp:expr:`lv_obj_stringify_id(widget, buf, len)`
77- :cpp:expr:`lv_obj_id_compare(id1, id2)`
78
79These supply the default implementation for Widget IDs, namely that for each Widget
80created, :cpp:expr:`lv_obj_stringify_id(widget, buf, len)` will produce a unique
81string for it.  Example:  if the following 6 Widgets are created in this sequence:
82
83- Screen
84- Label
85- Button
86- Label
87- Label
88- Image
89
90the strings produced by :cpp:expr:`lv_obj_stringify_id(widget, buf, len)` would be
91
92- obj1
93- label1
94- btn1
95- label2
96- label3
97- image1
98
99respectively.
100
101
102.. _widget_id_custom_generator:
103
104Using a custom ID generator
105---------------------------
106
107If you wish, you can provide custom implementations for several Widget-ID related
108functions.  You do this by first setting :c:macro:`LV_USE_OBJ_ID_BUILTIN` to `0` in
109``lv_conf.h``.
110
111You will then need to provide implementations for the following functions (and link
112them with LVGL):
113
114.. code-block:: c
115
116    const char * lv_obj_stringify_id(lv_obj_t * widget, char * buf, uint32_t len);
117    int          lv_obj_id_compare(const void * id1, const void * id2);
118
119If :c:macro:`LV_OBJ_ID_AUTO_ASSIGN` equates to a non-zero value (or if you otherwise
120simply need to use them), you will also need to provide implementations for:
121
122.. code-block:: c
123
124    void         lv_obj_assign_id(const lv_obj_class_t * class_p, lv_obj_t * widget);
125    void         lv_obj_free_id(lv_obj_t * widget);
126
127If :c:macro:`LV_BUILD_TEST` equates to a non-zero value and you are including LVGL
128test code in your compile (or if you otherwise simply need to use them), you
129will also need to provide an implementation for:
130
131.. code-block:: c
132
133    void         lv_obj_set_id(lv_obj_t * widget, void * id);
134
135
136Examples of implementations of these functions exist in ``lv_obj_id_builtin.c``, but
137you are free to use a different design if needed.
138
139:cpp:func:`lv_obj_stringify_id` converts the passed ``widget`` to a string
140representation (typically incorporating the ``id`` field) and writes it into the
141buffer provided in its ``buf`` argument.
142
143:cpp:func:`lv_obj_id_compare` compares 2 ``void * id`` values and returns ``0`` when
144they are considered equal, and non-zero otherwise.
145
146If :c:macro:`LV_OBJ_ID_AUTO_ASSIGN` equates to a non-zero value,
147:cpp:func:`lv_obj_assign_id` is called when a Widget is created.  It is responsible
148for assigning a value to the Widget's ``id`` field.  A pointer to the Widget's final
149class is passed in its ``class_p`` argument in case it is needed for determining the
150value for the ``id`` field, or for other possible needs related to your design for
151Widget IDs.  Note that this pointer may be different than :cpp:expr:`widget->class_p`
152which is the class of the Widget currently being created.
153
154If :c:macro:`LV_OBJ_ID_AUTO_ASSIGN` equates to a non-zero value,
155:cpp:func:`lv_obj_free_id` is called when a Widget is deleted.  It needs to perform
156the clean-up for any resources allocated by :cpp:func:`lv_obj_assign_id`.
157
158
159Dumping a Widget Tree
160---------------------
161
162Regardless of the state of any of the above macros, the function
163:cpp:expr:`lv_obj_dump_tree(widget)` provides a "dump" of the Widget Tree for the
164specified Widget (that Widget plus all its children recursively) using the
165currently-configured method used by the :c:macro:`LV_LOG_USER` macro.  If NULL is
166passed instead of a pointer to a "root" Widget, the dump will include the Widget Tree
167for all :ref:`Screens`, for all :ref:`Displays <display>` in the system.
168
169For :c:macro:`LV_LOG_USER` to produce output, the following needs to be true in
170``lv_conf.h``:
171
172- :c:macro:`LV_USE_LOG` must equate to a non-zero value
173- :c:macro:`LV_LOG_LEVEL` <= :c:macro:`LV_LOG_LEVEL_USER`
174
175It will recursively walk through all that Widget's children (starting with the Widget
176itself) and print the Widget's parent's address, the Widget's address, and if
177:c:macro:`LV_USE_OBJ_ID` equates to a non-zero value, will also print the output of
178:cpp:func:`lv_obj_stringify_id` for that Widget.
179
180This can be useful in the event of a UI crash.  From that log you can examine the
181state of the Widget Tree when :cpp:expr:`lv_obj_dump_tree(widget)` was called.
182
183For example, if a pointer to a deleted Widget is stored in a Timer's
184:cpp:expr:`timer->user_data` field when the timer event callback is called, attempted
185use of that pointer will likly cause a crash because the pointer is not valid any
186more.  However, a timely dump of the Widget Tree right before that point will show
187that the Widget no longer exists.
188
189
190Find child by ID
191----------------
192
193:cpp:expr:`lv_obj_get_child_by_id(widget, id)` will perform a recursive walk through
194``widget``\ 's children and return the first child encountered having the given ID.
195
196
197