feat(subject): deprecate lv_obj_bind_flag/state and add lv_obj_bind_<type> (#10541)

Co-authored-by: Gabor Kiss-Vamosi <kisvegabor@gmail.com>
This commit is contained in:
André Costa
2026-09-02 09:41:47 +02:00
committed by GitHub
co-authored by Gabor Kiss-Vamosi
parent 371b94cf9c
commit e21695f6cb
43 changed files with 1101 additions and 188 deletions
+1 -1
View File
@@ -72,7 +72,7 @@ and ask anything!
- 30+ built-in [Widgets](https://lvgl.io/docs/open/widgets): Button, Label, Slider, Chart, Keyboard, Meter, Arc, Table, and many more.
- Flexible [Style system](https://lvgl.io/docs/open/common-widget-features/styles) with 100+ style properties to customize any part of the widgets in any state.
- [Flexbox](https://lvgl.io/docs/open/common-widget-features/layouts/flex) and [Grid](https://lvgl.io/docs/open/common-widget-features/layouts/grid)-like layout engines to automatically size and position the widgets responsively.
- [Data bindings](https://lvgl.io/docs/open/main-modules/observer) to easily connect the UI with the application.
- [Data bindings](https://lvgl.io/docs/open/main-modules/data_binding/index.html) to easily connect the UI with the application.
- Supports Mouse, Touchpad, Keypad, Keyboard, External buttons, Encoder [Input devices](https://lvgl.io/docs/open/main-modules/indev).
- [Multiple display](https://lvgl.io/docs/open/main-modules/display/overview#how-many-displays-can-lvgl-use) support.
+10 -3
View File
@@ -193,6 +193,13 @@ static void create_control_panel(lv_obj_t * viewer)
create_antialiasing_panel(control_panel);
}
/*Disable a manual camera slider while a predefined camera (index > 0) is selected*/
static void disable_while_camera_selected_observer_cb(lv_observer_t * observer, lv_subject_t * subject)
{
lv_obj_t * obj = lv_observer_get_target_obj(observer);
lv_obj_set_state(obj, LV_STATE_DISABLED, lv_subject_get_int(subject) > 0);
}
static void create_camera_panel(lv_obj_t * panel, lv_obj_t * viewer)
{
@@ -243,9 +250,9 @@ static void create_camera_panel(lv_obj_t * panel, lv_obj_t * viewer)
lv_slider_set_max_value(distance_slider, 25);
style_slider(distance_slider, SLIDER_COLOR);
lv_obj_bind_state_if_gt(yaw_slider, &camera_subject, LV_STATE_DISABLED, 0);
lv_obj_bind_state_if_gt(pitch_slider, &camera_subject, LV_STATE_DISABLED, 0);
lv_obj_bind_state_if_gt(distance_slider, &camera_subject, LV_STATE_DISABLED, 0);
lv_subject_add_observer_obj(&camera_subject, disable_while_camera_selected_observer_cb, yaw_slider, NULL);
lv_subject_add_observer_obj(&camera_subject, disable_while_camera_selected_observer_cb, pitch_slider, NULL);
lv_subject_add_observer_obj(&camera_subject, disable_while_camera_selected_observer_cb, distance_slider, NULL);
}
static void create_animation_panel(lv_obj_t * panel, lv_obj_t * viewer)
+1 -1
View File
@@ -76,7 +76,7 @@
- יותר מ-30 [רכיבי ממשק](https://lvgl.io/docs/open/widgets) מובנים: Button, Label, Slider, Chart, Keyboard, Meter, Arc, Table ועוד רבים.
- [מערכת סגנונות](https://lvgl.io/docs/open/common-widget-features/styles) גמישה עם יותר מ-100 מאפייני סגנון להתאמה אישית של כל חלק ברכיבי הממשק בכל מצב.
- מנועי פריסה בסגנון [Flexbox](https://lvgl.io/docs/open/common-widget-features/layouts/flex) ו-[Grid](https://lvgl.io/docs/open/common-widget-features/layouts/grid) לקביעת גודל ומיקום אוטומטיים ורספונסיביים של רכיבי הממשק.
- [קישורי נתונים](https://lvgl.io/docs/open/main-modules/observer) לחיבור קל של ממשק המשתמש עם היישום.
- [קישורי נתונים](https://lvgl.io/docs/open/main-modules/data_binding) לחיבור קל של ממשק המשתמש עם היישום.
- תמיכה בעכבר, במשטח מגע, בלוח מקשים, במקלדת, בלחצנים חיצוניים וב-Encoder [התקני קלט](https://lvgl.io/docs/open/main-modules/indev).
- תמיכה ב[תצוגות מרובות](https://lvgl.io/docs/open/main-modules/display/overview#how-many-displays-can-lvgl-use).
+1 -1
View File
@@ -68,7 +68,7 @@
- 30種類以上の組み込み [ウィジェット](https://lvgl.io/docs/open/widgets): Button、Label、Slider、Chart、Keyboard、Meter、Arc、Tableなど多数。
- 100以上のスタイルプロパティを備えた柔軟な [スタイルシステム](https://lvgl.io/docs/open/common-widget-features/styles) で、あらゆる状態のウィジェットのあらゆる部分をカスタマイズできます。
- [Flexbox](https://lvgl.io/docs/open/common-widget-features/layouts/flex) と [Grid](https://lvgl.io/docs/open/common-widget-features/layouts/grid) 風のレイアウトエンジンで、ウィジェットのサイズと位置をレスポンシブに自動調整します。
- UIとアプリケーションを簡単に接続する [データバインディング](https://lvgl.io/docs/open/main-modules/observer)。
- UIとアプリケーションを簡単に接続する [データバインディング](https://lvgl.io/docs/open/main-modules/data_binding/index.html)。
- Mouse、Touchpad、Keypad、Keyboard、外部ボタン、Encoderの [入力デバイス](https://lvgl.io/docs/open/main-modules/indev) に対応。
- [マルチディスプレイ](https://lvgl.io/docs/open/main-modules/display/overview#how-many-displays-can-lvgl-use) 対応。
+1 -1
View File
@@ -74,7 +74,7 @@
- 30개 이상의 내장 [위젯](https://lvgl.io/docs/open/widgets): Button, Label, Slider, Chart, Keyboard, Meter, Arc, Table 등 다수.
- 어떤 상태에서든 위젯의 모든 부분을 커스터마이징할 수 있는 100개 이상의 스타일 속성을 갖춘 유연한 [스타일 시스템](https://lvgl.io/docs/open/common-widget-features/styles).
- 위젯의 크기와 위치를 반응형으로 자동 조정하는 [Flexbox](https://lvgl.io/docs/open/common-widget-features/layouts/flex) 및 [Grid](https://lvgl.io/docs/open/common-widget-features/layouts/grid) 유사 레이아웃 엔진.
- UI를 애플리케이션과 손쉽게 연결하는 [데이터 바인딩](https://lvgl.io/docs/open/main-modules/observer).
- UI를 애플리케이션과 손쉽게 연결하는 [데이터 바인딩](https://lvgl.io/docs/open/main-modules/data_binding).
- Mouse, Touchpad, Keypad, Keyboard, 외부 버튼, Encoder [입력 장치](https://lvgl.io/docs/open/main-modules/indev)를 지원합니다.
- [다중 디스플레이](https://lvgl.io/docs/open/main-modules/display/overview#how-many-displays-can-lvgl-use) 지원.
+1 -1
View File
@@ -74,7 +74,7 @@ e perguntar qualquer coisa!
- Mais de 30 [Widgets](https://lvgl.io/docs/open/widgets) integrados: Button, Label, Slider, Chart, Keyboard, Meter, Arc, Table e muitos outros.
- [Sistema de estilos](https://lvgl.io/docs/open/common-widget-features/styles) flexível com mais de 100 propriedades de estilo para personalizar qualquer parte dos widgets em qualquer estado.
- Motores de layout semelhantes a [Flexbox](https://lvgl.io/docs/open/common-widget-features/layouts/flex) e [Grid](https://lvgl.io/docs/open/common-widget-features/layouts/grid) para dimensionar e posicionar os widgets de forma responsiva automaticamente.
- [Data bindings](https://lvgl.io/docs/open/main-modules/observer) para conectar facilmente a UI à aplicação.
- [Data bindings](https://lvgl.io/docs/open/main-modules/data_binding) para conectar facilmente a UI à aplicação.
- Suporta Mouse, Touchpad, Keypad, Keyboard, botões externos e Encoder [dispositivos de entrada](https://lvgl.io/docs/open/main-modules/indev).
- Suporte a [múltiplos displays](https://lvgl.io/docs/open/main-modules/display/overview#how-many-displays-can-lvgl-use).
+1 -1
View File
@@ -68,7 +68,7 @@
- 30 多种内置[控件](https://lvgl.io/docs/open/widgets):Button、Label、Slider、Chart、Keyboard、Meter、Arc、Table 等等。
- 灵活的[样式系统](https://lvgl.io/docs/open/common-widget-features/styles),拥有 100 多个样式属性,可在任何状态下定制控件的任何部分。
- 类似 [Flexbox](https://lvgl.io/docs/open/common-widget-features/layouts/flex) 和 [Grid](https://lvgl.io/docs/open/common-widget-features/layouts/grid) 的布局引擎,可自动地响应式调整控件的尺寸和位置。
- [数据绑定](https://lvgl.io/docs/open/main-modules/observer),可轻松将 UI 与应用程序连接起来。
- [数据绑定](https://lvgl.io/docs/open/main-modules/data_binding),可轻松将 UI 与应用程序连接起来。
- 支持鼠标、触摸板、小键盘、键盘、外部按钮、编码器等[输入设备](https://lvgl.io/docs/open/main-modules/indev)。
- 支持[多显示屏](https://lvgl.io/docs/open/main-modules/display/overview#how-many-displays-can-lvgl-use)。
+27
View File
@@ -570,6 +570,33 @@ Calling the following functions with `NULL` as the display parameter is deprecat
- <ApiLink name="lv_sysmon_show_memory"/>
- <ApiLink name="lv_sysmon_hide_memory"/>
### lv_observer
- The `lv_obj_bind_flag_if_*` and `lv_obj_bind_state_if_*` families
(`_eq`/`_not_eq`/`_gt`/`_ge`/`_lt`/`_le`) are deprecated. For a boolean subject,
bind a widget flag with `lv_obj_bind_bool()` — the dedicated per-flag setters
(`lv_obj_set_hidden()`, `lv_obj_set_clickable()`, …) can be passed directly as
the callback. For a state, or for any comparison against a reference value, add
a custom observer with `lv_subject_add_observer_obj()`:
```c
/* Before: hide a widget while a 0/1 subject is non-zero */
lv_obj_bind_flag_if_not_eq(obj, &subject, LV_OBJ_FLAG_HIDDEN, 0);
/* After */
lv_obj_bind_bool(obj, &subject, lv_obj_set_hidden);
/* Before: disable a widget while a subject is greater than 80 */
lv_obj_bind_state_if_gt(obj, &subject, LV_STATE_DISABLED, 80);
/* After */
static void disabled_observer_cb(lv_observer_t * observer, lv_subject_t * subject)
{
lv_obj_t * obj = lv_observer_get_target_obj(observer);
lv_obj_set_state(obj, LV_STATE_DISABLED, lv_subject_get_int(subject) > 80);
}
lv_subject_add_observer_obj(&subject, disabled_observer_cb, obj, NULL);
```
---
## Widgets
@@ -21,7 +21,7 @@ lv_obj_set_style_bg_color(slider, lv_color_red(), LV_PART_INDICATOR | LV_STATE_F
## Binding Local Styles
By using <ApiLink name="lv_obj_bind_style_prop" />, it's possible to bind a style property
to a [Subject](/main-modules/observer/observer)'s value.
to a [Subject](/main-modules/data_binding/subjects)'s value.
It's a great way to map every slider's color or opacity to a subject and control it
externally.
@@ -146,7 +146,7 @@ notified. There are 3 options to do this:
## Binding Styles
By using <ApiLink name="lv_obj_bind_style" />, it's possible to add a style to a Widget
but enable it only if a [Subject](/main-modules/observer/observer)'s value is equal to
but enable it only if a [Subject](/main-modules/data_binding/subjects)'s value is equal to
a reference value.
It's a great way to implement a light/dark theme switch by normally adding the styles
@@ -247,4 +247,4 @@ lv_obj_set_y(slider1, 30);
lv_subject_set_int(&subject1, 30);
```
Learn more on the documentation page of [Observers](/main-modules/observer/observer).
Learn more on the documentation page of [Observers](/main-modules/data_binding/observers).
@@ -1,5 +1,5 @@
---
title: Observer
title: Data binding
---
@@ -0,0 +1,9 @@
{
"title": "Data binding",
"pages": [
"overview",
"subjects",
"observers",
"examples"
]
}
@@ -0,0 +1,147 @@
---
title: Observers
description: Learn more about Observers.
---
An **Observer** watches one [Subject](/main-modules/data_binding/subjects) and runs a
callback whenever that Subject's value changes. When you subscribe, the callback is
also called once immediately, so the Observer can pick up the current value right
away.
A callback always has the same signature:
```c
static void temperature_cb(lv_observer_t * observer, lv_subject_t * subject)
{
int32_t t = lv_subject_get_int(subject);
LV_LOG_USER("Temperature is now %d", t);
}
```
## Subscribing
The simplest way to subscribe is <ApiLink name="lv_subject_add_observer" display="lv_subject_add_observer()" />.
The third argument is free-form user data passed to the callback:
```c
lv_subject_add_observer(&temperature, temperature_cb, NULL);
```
Often the Observer needs to update a specific object. Pass a Widget with
<ApiLink name="lv_subject_add_observer_obj" display="lv_subject_add_observer_obj()" />,
then read it back in the callback with <ApiLink name="lv_observer_get_target_obj" display="lv_observer_get_target_obj()" />.
```c
lv_subject_add_observer_obj(&temperature, label_cb, my_label, NULL);
static void label_cb(lv_observer_t * observer, lv_subject_t * subject)
{
lv_obj_t * label = lv_observer_get_target_obj(observer);
lv_label_set_text_fmt(label, "%d °C", lv_subject_get_int(subject));
}
```
In a generic case any other pointer can be passed as target with
<ApiLink name="lv_subject_add_observer_with_target" display="lv_subject_add_observer_with_target()" /> and the
target can be read back with <ApiLink name="lv_observer_get_target" display="lv_observer_get_target()" />.
## Unsubscribing
An Observer bound to a Widget using <ApiLink name="lv_subject_add_observer_obj" display="lv_subject_add_observer_obj()" /> is removed automatically when that Widget is deleted, so in this case you don't need to do anything. Otherwise:
- <ApiLink name="lv_observer_delete" display="lv_observer_delete(observer)" /> removes a
single Observer (use the pointer returned when subscribing).
- <ApiLink name="lv_obj_remove_from_subject" display="lv_obj_remove_from_subject(widget, subject)" />
removes a Widget's Observers (`subject` may be `NULL` to detach from all).
- <ApiLink name="lv_subject_deinit" display="lv_subject_deinit(subject)" /> disconnects a
Subject from all of its Observers.
## Binding a Widget to a Subject
Besides subscribing by hand, a Widget's property can be bound to a Subject directly.
Each binding is just an Observer, so it too is removed automatically when the Widget
is deleted.
### Flags
To drive a flag from a `0`/non-zero Subject, use
<ApiLink name="lv_obj_bind_bool" display="lv_obj_bind_bool()" />. Because the callback
takes a `bool`, a dedicated per-flag setter can be passed straight in:
```c
lv_obj_bind_bool(widget, &subject, lv_obj_set_hidden); /* hidden while non-zero */
```
### States
There's no dedicated setter for a state, and states often depend on a comparison, so
bind them with a small Observer of your own:
```c
static void disabled_cb(lv_observer_t * observer, lv_subject_t * subject)
{
lv_obj_t * obj = lv_observer_get_target_obj(observer);
lv_obj_set_state(obj, LV_STATE_DISABLED, lv_subject_get_int(subject) > 80);
}
lv_subject_add_observer_obj(&subject, disabled_cb, widget, NULL);
```
<Callout type="warning">
The older `lv_obj_bind_flag_if_*` and `lv_obj_bind_state_if_*` functions are
**deprecated** in favor of the two approaches above.
</Callout>
### Checked state
<ApiLink name="lv_obj_bind_checked" display="lv_obj_bind_checked()" /> creates a
**two-way** binding: the Widget's checked state follows the Subject (checked while
non-zero), and clicking the Widget writes `1` or `0` back to the Subject. The Widget
should have <ApiLink name="lv_obj_set_checkable" display="lv_obj_set_checkable(obj, true)"/>
to make it checkable.
```c
lv_obj_bind_checked(checkbox, &subject);
```
<LvglExample name="lv_example_checkbox_bind_checked" path="widgets/checkbox/lv_example_checkbox_bind_checked" />
### Widget values
A Widget's main value or text is bound with the widget's own `lv_<widget>_bind_...()`
function, described under "Data binding" on each widget's page. A few examples:
#### Label text
<ApiLink name="lv_label_bind_text" display="lv_label_bind_text()" /> keeps a Label's
text in sync with a Subject, optionally through a `printf`-style format string.
<LvglExample name="lv_example_label_bind_text" path="widgets/label/lv_example_label_bind_text" />
#### Slider value
<ApiLink name="lv_slider_bind_value" display="lv_slider_bind_value()" /> ties a Slider's
position to a Subject; dragging the Slider updates the Subject and vice versa.
<LvglExample name="lv_example_slider_bind_value" path="widgets/slider/lv_example_slider_bind_value" />
#### Arc value
<ApiLink name="lv_arc_bind_value" display="lv_arc_bind_value()" /> keeps an Arc's value
in sync with a Subject.
<LvglExample name="lv_example_arc_bind_value" path="widgets/arc/lv_example_arc_bind_value" />
#### Drop-down value
<ApiLink name="lv_dropdown_bind_value" display="lv_dropdown_bind_value()" /> binds the
selected option's index to a Subject.
<LvglExample name="lv_example_dropdown_bind_value" path="widgets/dropdown/lv_example_dropdown_bind_value" />
#### Roller value
<ApiLink name="lv_roller_bind_value" display="lv_roller_bind_value()" /> binds the
selected option's index to a Subject.
<LvglExample name="lv_example_roller_bind_value" path="widgets/roller/lv_example_roller_bind_value" />
@@ -0,0 +1,92 @@
---
title: Overview
description: Get familiar with the data binding concept of LVGL.
---
## Concept
The `lv_observer` module is an implementation of the Subject-Observer pattern used to
realize data binding in LVGL.
A **Subject** is a value being "observed", usually a global variable. It can store an
integer, string, color, or other basic type.
An **Observer** is attached (subscribed) to a Subject and gets notified whenever the
Subject changes.
This pattern separates the data from the places that consume it. Imagine a Subject
that stores the temperature of a room: the sensor sets the value and doesn't need to
know who uses it. Maybe a Label Widget is subscribed to update its text, maybe a popup
is shown when the temperature is too high, maybe an I/O pin is set to `1` to start
heating when it drops too low. Each subscriber only has to know that the Subject
exists; it can subscribe to it and unsubscribe when it is deleted or no longer
interested.
## Quick Reference
This section shows the basics briefly. Read the other pages in this section to learn
all the details.
The following Subject types are supported:
- int
- float (requires `LV_USE_FLOAT`)
- string
- color
- pointer
- group
You can create a Subject by using a static/global variable:
```c
static lv_subject_t subject_2;
lv_subject_init_int(&subject_2, 10); /*10 is the initial value*/
```
Set a Subject's value like below. This also notifies all subscribed Observers when the value changes.
```c
lv_subject_set_int(subject_1, 20);
```
You can subscribe with an Observer callback:
```c
void my_observer(lv_observer_t * observer, lv_subject_t * subject)
{
printf("New value: %d\n", lv_subject_get_int(subject));
}
lv_subject_add_observer(subject_1, my_observer, NULL); /*3rd arg is optional user data*/
```
When you subscribe, the callback is called immediately to pick up the current value.
You can also subscribe together with a Widget. This way the Observer is removed
automatically when the Widget is deleted. This is convenient, because you don't need
to check whether the Widget still exists. While it exists it gets updated; once deleted
it unsubscribes itself.
```c
lv_subject_add_observer_obj(subject_1, my_observer, label_1, NULL);
```
Some Widgets also offer an API to easily create one- or two-way bindings. For example:
```c
/*Update the label when the Subject changes, using a format string*/
lv_label_bind_text(label_1, subject_1, "%d °C");
/*Keep the Subject and the selected drop-down option in sync (two-way)*/
lv_dropdown_bind_value(dropdown_1, &subject_2);
```
When the subjects are no longer needed they can be deinitialized:
```c
lv_subject_deinit(&subject_2);
```
That's it in short. Continue reading to learn more about
[Subjects](/main-modules/data_binding/subjects) and
[Observers](/main-modules/data_binding/observers).
@@ -0,0 +1,124 @@
---
title: Subjects
description: Learn more about Subjects.
---
A **Subject** is a value you can observe. When you change it, every
[Observer](/main-modules/data_binding/observers) attached to it is notified
automatically. A Subject can hold an integer, a float, a string, a pointer, a
color, or a group of other Subjects.
Keep a Subject in a static or global variable; it has to stay alive as long as
anything observes it.
## Creating a Subject
Initialize a Subject with the function for its type. The last argument is the
starting value:
```c
static lv_subject_t temperature;
lv_subject_init_int(&temperature, 20); /* integer, starts at 20 */
```
Strings need a buffer to live in (and an optional second buffer to remember the
previous value):
```c
static lv_subject_t title;
static char buf[32];
static char prev_buf[32];
lv_subject_init_string(&title, buf, prev_buf, sizeof(buf), "Hello");
```
The remaining types work the same way with <ApiLink name="lv_subject_init_float" display="lv_subject_init_float()" /> (requires <ApiLink name="LV_USE_FLOAT"/>),
<ApiLink name="lv_subject_init_pointer" display="lv_subject_init_pointer()" /> and
<ApiLink name="lv_subject_init_color" display="lv_subject_init_color()" />.
## Reading and Writing the Value
Write a value with `lv_subject_set_...()` (e.g. <ApiLink name="lv_subject_set_int"/>).
Most value types notify Observers only when the value changes, however
pointer Subjects notify on every set, group Subjects notify when their members or the group list changes, and strings
when the previous buffer is also set.
Read the current value back with `lv_subject_get_...()` (e.g. <ApiLink name="lv_subject_get_int"/>):
```c
lv_subject_set_int(&temperature, 25); /* notifies observers */
int32_t t = lv_subject_get_int(&temperature); /* -> 25 */
```
Strings are copied into the Subject's own buffer, so update them with
<ApiLink name="lv_subject_copy_string" display="lv_subject_copy_string()" />:
```c
lv_subject_copy_string(&title, "Settings");
```
Every type (except groups) also has a `lv_subject_get_previous_...()` function (e.g.
<ApiLink name="lv_subject_get_previous_int" />) that returns the value from before the
last change, which is handy when a callback needs to compare old and new.
## Limiting the Range
Integer and float Subjects can be clamped. Out-of-range values are pulled back in
before the Observers are notified:
```c
lv_subject_set_min_value_int(&temperature, 0);
lv_subject_set_max_value_int(&temperature, 100);
```
## Grouping Several Subjects
Sometimes one thing depends on several values at once. A **group** Subject ties them
together, so an Observer fires whenever *any* member changes.
```c
static lv_subject_t * list[3] = {&mode, &value, &unit};
static lv_subject_t measurement;
lv_subject_init_group(&measurement, list, 3);
```
In the callback, reach the members with
<ApiLink name="lv_subject_get_group_element" display="lv_subject_get_group_element()" />:
```c
static void measurement_cb(lv_observer_t * observer, lv_subject_t * subject)
{
lv_obj_t * label = lv_observer_get_target(observer);
int32_t mode = lv_subject_get_int(lv_subject_get_group_element(subject, 0));
int32_t value = lv_subject_get_int(lv_subject_get_group_element(subject, 1));
const char * unit = lv_subject_get_pointer(lv_subject_get_group_element(subject, 2));
lv_label_set_text_fmt(label, "%s: %d %s", mode ? "Current" : "Voltage", value, unit);
}
```
## Changing a Subject from the UI
Instead of writing an event handler, you can let a Widget update a Subject directly.
For example, toggle an integer between `0` and `1` when a button is clicked:
```c
lv_obj_add_subject_toggle_event(button, &enabled, LV_EVENT_CLICKED);
```
Other ready-made actions:
- <ApiLink name="lv_obj_add_subject_increment_event" display="lv_obj_add_subject_increment_event(obj, &subject, trigger, step)" />
adds `step` to the value (use a negative `step` to subtract). It returns a
descriptor you can adjust further with
<ApiLink name="lv_obj_set_subject_increment_event_min_value" display="lv_obj_set_subject_increment_event_min_value()" />,
<ApiLink name="lv_obj_set_subject_increment_event_max_value" display="lv_obj_set_subject_increment_event_max_value()" /> and
<ApiLink name="lv_obj_set_subject_increment_event_rollover" display="lv_obj_set_subject_increment_event_rollover()" />.
- <ApiLink name="lv_obj_add_subject_set_int_event" display="lv_obj_add_subject_set_int_event()" />,
<ApiLink name="lv_obj_add_subject_set_float_event" display="lv_obj_add_subject_set_float_event()" /> (requires <ApiLink name="LV_USE_FLOAT"/>)
and <ApiLink name="lv_obj_add_subject_set_string_event" display="lv_obj_add_subject_set_string_event()" />
write a fixed value on the trigger.
<Callout type="warning">
Do not de-initialize or delete a Subject while these events remain attached as their callbacks saved a pointer to the Subject.
</Callout>
+1 -1
View File
@@ -9,7 +9,7 @@
"timer",
"animation",
"fs",
"observer",
"data_binding",
"draw",
"translation"
]
-7
View File
@@ -1,7 +0,0 @@
{
"title": "Observer",
"pages": [
"observer",
"observer_examples"
]
}
+39 -88
View File
@@ -174,7 +174,7 @@ used. This is covered below.
Alternately, if you want to bind a Subject's value to a Widget's property, one
of the `lv_<widget_type>_bind_...()` functions can be used. See
[Widget Binding](/main-modules/observer/observer) for more details.
[Widget Binding](/main-modules/data_binding) for more details.
By using `lv_obj_add_subject_...()` it's also possible to change a subject's value
on a trigger. It's covered in [change subject on event](/widgets/scale)
@@ -220,7 +220,7 @@ Subject. A copy of the pointer to that Widget is saved in the Observer's `targe
field. This works exactly like the above method except that when the Widget is
deleted, the Observer thus created will be automatically unsubscribed from the
Subject and deleted from the LVGL heap. Note this is different from
[Widget Binding](/main-modules/observer/observer).
[Widget Binding](/main-modules/data_binding).
lv_observer_t * observer = <ApiLink name="lv_subject_add_observer_obj" display="lv_subject_add_observer_obj(&some_subject, some_observer_cb, widget, user_data)" />
@@ -228,11 +228,9 @@ Any number of Observers can be created and be associated with a Widget this way.
A copy of the pointer to the Widget can be retrieved by calling
<ApiLink name="lv_observer_get_target_obj" display="lv_observer_get_target_obj(observer)" />, e.g. inside the callback function.
Note that this function returns the stored pointer as a `lv_obj_t *` type, as
opposed to the `void *` type returned by
<ApiLink name="lv_observer_get_target_obj" display="lv_observer_get_target_obj(observer)" />.
(<ApiLink name="lv_observer_get_target" display="lv_observer_get_target(observer)" /> can still be used if you need that
pointer as a `void *` type for any reason, but in practice, this would be rare.)
This returns the stored pointer as an `lv_obj_t *`, as opposed to the `void *`
returned by <ApiLink name="lv_observer_get_target" display="lv_observer_get_target(observer)" /> (which can still be used if
you need a `void *` for any reason, but in practice this is rare).
##### Unsubscribing from a Subject
@@ -249,7 +247,7 @@ from memory using any of the methods below, depending on the effect you want to
from all associated Subjects.
- <ApiLink name="lv_subject_deinit" display="lv_subject_deinit(subject)" /> gracefully disconnects `subject` from
all associated Observers and Widget events. This includes subscriptions made
using any of the [Widget Binding](/main-modules/observer/observer) functions covered below.
using any of the [Widget Binding](/main-modules/data_binding) functions covered below.
- <ApiLink name="lv_observer_delete" display="lv_observer_delete(observer)" /> deletes that specific Observer and
gracefully disconnects it from its `subject` and any associated Widgets,
where `observer` is the return value any of the above
@@ -321,87 +319,45 @@ static void all_observer_cb(lv_observer_t * observer, lv_subject_t * subject)
### Widget Binding
The following methods of subscribing to an integer-type Subject associate the
Observer with ONE of a Widget's properties as thought that property itself were the
Observer. Any of the following Widget properties can be thus bound to an Subject's
integer value:
Besides subscribing manually, a Widget's property can be bound directly to a
Subject. Any number of bindings can be created for one Widget, and — like
<ApiLink name="lv_subject_add_observer_obj" /> — each one is removed automatically
when the Widget is deleted.
- flag (or OR-ed combination of flags) from from the `LV_OBJ_FLAG_...` enumeration values;
- state (or OR-ed combination of states) from the `LV_STATE_...` enumeration values;
- text and/or integer values for
- Label
- Span;
- Arc
- Drop-Down
- Roller
- Slider
- Scale Section Min/Max values
Flags, states, and the checked state are covered below. Values (a Label's text, an
Arc / Slider / Roller / Drop-Down value, a Scale Section's min/max) are covered in
the "Data binding" section of each widget's documentation, e.g.
[Data binding for lv_label](/widgets/label).
Any number of Observers can be created for a single Widget, each bound to ONE of
the above properties.
#### Flags and States
For all of the `lv_..._bind_...()` functions covered below, they are similar to
<ApiLink name="lv_subject_add_observer_obj" display="lv_subject_add_observer_obj(&some_subject, some_observer_cb, widget, user_data)" />
in that they create an Observer and associates the Widget with it. What is different
is that updates to the Widget's property thus bound are handled internally -- the
user *does not supply callback functions* for any of these subscribing methods -- the
callback methods are supplied by the Observer subsystem.
To drive a Widget's flag from a boolean Subject (a value that is only ever `0` or
non-zero), use <ApiLink name="lv_obj_bind_bool" />. The callback receives the value
as a `bool`, so a dedicated per-flag setter can be passed directly:
<Callout type="info">
While the examples below show saving a reference to the created Observer objects
for the various `lv_..._bind_...()` functions, it is not necessary to do so
unless you need them for some purpose, because the created Observer objects will
be automatically deleted when the Widget is deleted.
```c
lv_obj_bind_bool(widget, &subject, lv_obj_set_hidden); /* hidden while non-zero */
```
For a state, or when the Subject must be compared to a reference value, add a custom
Observer with <ApiLink name="lv_subject_add_observer_obj" /> and set the flag/state
yourself:
```c
static void disabled_observer_cb(lv_observer_t * observer, lv_subject_t * subject)
{
lv_obj_t * obj = lv_observer_get_target_obj(observer);
lv_obj_set_state(obj, LV_STATE_DISABLED, lv_subject_get_int(subject) > 80);
}
lv_subject_add_observer_obj(&subject, disabled_observer_cb, widget, NULL);
```
<Callout type="warning">
The `lv_obj_bind_flag_if_*` and `lv_obj_bind_state_if_*` functions are
**deprecated** in favor of the two approaches above.
</Callout>
##### Any Widget Type
#### Flags
The following methods of subscribing to an integer Subject affect a Widget's flag (or
OR-ed combination of flags). When the subscribing occurs, and each time the
Subject's value is changed thereafter, the Subject's value is compared with the
specified reference value, and the specified flag(s) is (are):
- SET when the Subject's integer value fulfills the indicated condition, and
- CLEARED otherwise.
Here are the functions that carry out this method of subscribing to a Subject. The
`flags` argument can contain a single, or bit-wise OR-ed combination of any of the
`LV_OBJ_FLAG_...` enumeration values.
| Field | Description |
|-------|-------------|
| `equal` | <ApiLink name="lv_obj_bind_flag_if_eq" display="lv_obj_bind_flag_if_eq(widget, &subject, flags, ref_value)" /> |
| `not equal` | <ApiLink name="lv_obj_bind_flag_if_not_eq" display="lv_obj_bind_flag_if_not_eq(widget, &subject, flags, ref_value)" /> |
| `greater than` | <ApiLink name="lv_obj_bind_flag_if_gt" display="lv_obj_bind_flag_if_gt(widget, &subject, flags, ref_value)" /> |
| `greater than or equal` | <ApiLink name="lv_obj_bind_flag_if_ge" display="lv_obj_bind_flag_if_ge(widget, &subject, flags, ref_value)" /> |
| `less than` | <ApiLink name="lv_obj_bind_flag_if_lt" display="lv_obj_bind_flag_if_lt(widget, &subject, flags, ref_value)" /> |
| `less than or equal` | <ApiLink name="lv_obj_bind_flag_if_le" display="lv_obj_bind_flag_if_le(widget, &subject, flags, ref_value)" /> |
#### States
The following methods of subscribing to an integer Subject affect a Widget's states
(or OR-ed combination of states). When the subscribing occurs, and each time the
Subject's value is changed thereafter, the Subject's value is compared with the
specified reference value, and the specified state(s) is (are):
- SET when the Subject's integer value fulfills the indicated condition, and
- CLEARED otherwise.
Here are the functions that carry out this method of subscribing to a Subject. The
`states` argument can contain a single, or bit-wise OR-ed combination of any of the
`LV_STATE_...` enumeration values.
| Field | Description |
|-------|-------------|
| `equal` | <ApiLink name="lv_obj_bind_state_if_eq" display="lv_obj_bind_state_if_eq(widget, &subject, states, ref_value)" /> |
| `not equal` | <ApiLink name="lv_obj_bind_state_if_not_eq" display="lv_obj_bind_state_if_not_eq(widget, &subject, states, ref_value)" /> |
| `greater than` | <ApiLink name="lv_obj_bind_state_if_gt" display="lv_obj_bind_state_if_gt(widget, &subject, states, ref_value)" /> |
| `greater than or equal` | <ApiLink name="lv_obj_bind_state_if_ge" display="lv_obj_bind_state_if_ge(widget, &subject, states, ref_value)" /> |
| `less than` | <ApiLink name="lv_obj_bind_state_if_lt" display="lv_obj_bind_state_if_lt(widget, &subject, states, ref_value)" /> |
| `less than or equal` | <ApiLink name="lv_obj_bind_state_if_le" display="lv_obj_bind_state_if_le(widget, &subject, states, ref_value)" /> |
#### Checked State
The following method of subscribing to an integer Subject affects a Widget's
@@ -419,11 +375,6 @@ Subject's value to be set to `1` or `0` respectively.
- <ApiLink name="lv_obj_bind_checked" display="lv_obj_bind_checked(widget, &subject)" />
##### Specific Widget Types
To learn how to bind subjects to Arcs, Labels, Sliders, etc. visit the "Data binding"
section of the given widget's documentation. For example: [Data binding for lv_label](/widgets/label).
### Change Subject on Event
It's a common requirement to update a Subject based on a user action (trigger).
+1 -1
View File
@@ -171,7 +171,7 @@ only declares its own angles and colour.
## Data binding
Data bindings connect a widget property to a piece of global data — a
[Subject](/main-modules/observer/observer). When the subject's value
[Subject](/main-modules/data_binding/subjects). When the subject's value
changes, every widget bound to it updates automatically; and an
interactive widget can write back into the subject so other subscribers
see the new value.
+1 -1
View File
@@ -78,7 +78,7 @@ both the track and the active fill independently.
## Data binding
Data bindings connect a widget property to a piece of global data — a
[Subject](/main-modules/observer/observer). When the subject's value
[Subject](/main-modules/data_binding/subjects). When the subject's value
changes, every widget bound to it updates automatically; and an
interactive widget can write back into the subject so other subscribers
see the new value.
+11 -4
View File
@@ -21,7 +21,7 @@ The Base Widget can have:
- [Layouts](/common-widget-features/layouts)
- [Styles](/common-widget-features/styles)
- [Events](/common-widget-features/events)
- [Data bindings](/main-modules/observer)
- [Data bindings](/main-modules/data_binding)
- [Flags](/common-widget-features/flags)
- [Parts and States](/common-widget-features/parts_and_states)
- And many more
@@ -42,12 +42,19 @@ Every widget inherits the Base Widget's binding helpers, which let a Subject
drive styles, flags, or states declaratively. The most common one is
`bind_style`: attach a named style to a widget only while a Subject equals a
reference value — perfect for theme switches and "mode" toggles. See the
[Observer](/main-modules/observer/observer) page for an introduction to the
[Observer](/main-modules/data_binding/observers) page for an introduction to the
underlying mechanism.
- <ApiLink name="lv_obj_bind_style" display="lv_obj_bind_style(obj, &style, selector, &subject, ref_value)" />
- <ApiLink name="lv_obj_bind_flag_if_eq" display="lv_obj_bind_flag_if_eq(obj, &subject, flag, ref_value)" /> and the matching `_not_eq`/`_gt`/`_ge`/`_lt`/`_le` variants
- <ApiLink name="lv_obj_bind_state_if_eq" display="lv_obj_bind_state_if_eq(obj, &subject, state, ref_value)" /> and the matching `_not_eq`/`_gt`/`_ge`/`_lt`/`_le` variants
- <ApiLink name="lv_obj_bind_bool" display="lv_obj_bind_bool(obj, &subject, set_flag_cb)" /> — drive a flag from a boolean Subject; a per-flag setter such as `lv_obj_set_hidden` can be passed directly
- <ApiLink name="lv_subject_add_observer_obj" display="lv_subject_add_observer_obj(&subject, observer_cb, obj, user_data)" /> — for a state, or when the Subject must be compared against a reference value
<Callout type="warning">
The `lv_obj_bind_flag_if_*` and `lv_obj_bind_state_if_*` families are
**deprecated**. Use <ApiLink name="lv_obj_bind_bool" /> for a boolean Subject, or
<ApiLink name="lv_subject_add_observer_obj" /> with a custom Observer for a
comparison. See the [Observer](/main-modules/data_binding/observers) page for details.
</Callout>
<LvglExample name="lv_example_obj_bind_style" path="widgets/obj/lv_example_obj_bind_style" />
+1 -1
View File
@@ -50,7 +50,7 @@ ticked.
## Data binding
Data bindings connect a widget property to a piece of global data — a
[Subject](/main-modules/observer/observer). When the subject's value
[Subject](/main-modules/data_binding/subjects). When the subject's value
changes, every widget bound to it updates automatically; and an
interactive widget can write back into the subject so other subscribers
see the new value.
+1 -1
View File
@@ -91,7 +91,7 @@ lv_obj_add_style(list, &my_style, LV_PART_SELECTED);
## Data binding
Data bindings connect a widget property to a piece of global data — a
[Subject](/main-modules/observer/observer). When the subject's value
[Subject](/main-modules/data_binding/subjects). When the subject's value
changes, every widget bound to it updates automatically; and an
interactive widget can write back into the subject so other subscribers
see the new value.
+1 -1
View File
@@ -145,7 +145,7 @@ When the widget is larger than its image source, `inner_align`
## Data binding
Data bindings connect a widget property to a piece of global data — a
[Subject](/main-modules/observer/observer). When the subject's value
[Subject](/main-modules/data_binding/subjects). When the subject's value
changes, every widget bound to it updates automatically; and an
interactive widget can write back into the subject so other subscribers
see the new value.
+1 -1
View File
@@ -133,7 +133,7 @@ directly on the `<lv_label>` tag for one-off variations.
## Data binding
Data bindings connect a widget property to a piece of global data — a
[Subject](/main-modules/observer/observer). When the subject's value
[Subject](/main-modules/data_binding/subjects). When the subject's value
changes, every widget bound to it updates automatically; and an
interactive widget can write back into the subject so other subscribers
see the new value.
+1 -1
View File
@@ -66,7 +66,7 @@ a chunkier highlighted row without computing a separate selected-area height.
## Data binding
Data bindings connect a widget property to a piece of global data — a
[Subject](/main-modules/observer/observer). When the subject's value
[Subject](/main-modules/data_binding/subjects). When the subject's value
changes, every widget bound to it updates automatically; and an
interactive widget can write back into the subject so other subscribers
see the new value.
+2 -2
View File
@@ -139,7 +139,7 @@ hides the section — a quick way to toggle it off
### Data binding
Data bindings connect a widget property to a piece of global data — a
[Subject](/main-modules/observer/observer). When the subject's value
[Subject](/main-modules/data_binding/subjects). When the subject's value
changes, every widget bound to it updates automatically; and an
interactive widget can write back into the subject so other subscribers
see the new value.
@@ -197,7 +197,7 @@ Needles aren't yet exposed through XML, so this example is C-only:
### Data binding
Data bindings connect a widget property to a piece of global data — a
[Subject](/main-modules/observer/observer). When the subject's value
[Subject](/main-modules/data_binding/subjects). When the subject's value
changes, every widget bound to it updates automatically; and an
interactive widget can write back into the subject so other subscribers
see the new value.
+1 -1
View File
@@ -99,7 +99,7 @@ increases the knob's click area.
## Data binding
Data bindings connect a widget property to a piece of global data — a
[Subject](/main-modules/observer/observer). When the subject's value
[Subject](/main-modules/data_binding/subjects). When the subject's value
changes, every widget bound to it updates automatically; and an
interactive widget can write back into the subject so other subscribers
see the new value.
+1 -1
View File
@@ -86,7 +86,7 @@ A few practical notes for the cursor look:
## Data binding
Data bindings connect a widget property to a piece of global data — a
[Subject](/main-modules/observer/observer). When the subject's value
[Subject](/main-modules/data_binding/subjects). When the subject's value
changes, every widget bound to it updates automatically; and an
interactive widget can write back into the subject so other subscribers
see the new value.
@@ -8,17 +8,16 @@ static void app_init(void);
static void ui_init(void);
/**
* @title PIN login via state bindings
* @brief Decouple a login UI from an engine subject using `lv_obj_bind_state_if_*`.
* @title PIN login via state observers
* @brief Decouple a login UI from an engine subject using observers on an auth subject.
*
* Two int subjects are initialised: `engine_subject` for the engine state and
* `auth_state_subject` for `LOGGED_OUT`, `LOGGED_IN`, and `AUTH_FAILED`. A
* password textarea fires `LV_EVENT_READY` to set the auth subject, a log-out
* button resets it, and an observer on `auth_state_subject` writes status text
* into an info label. `lv_obj_bind_state_if_eq` and
* `lv_obj_bind_state_if_not_eq` toggle `LV_STATE_DISABLED` on the textarea, log
* out button, and a start-engine button, which itself uses `lv_obj_bind_checked`
* to drive `engine_subject`.
* into an info label. Observers added with `lv_subject_add_observer_obj` toggle
* `LV_STATE_DISABLED` on the textarea, log out button, and a start-engine
* button, which itself uses `lv_obj_bind_checked` to drive `engine_subject`.
*/
void lv_example_observer_2(void)
{
@@ -92,6 +91,20 @@ static void info_label_observer_cb(lv_observer_t * observer, lv_subject_t * subj
}
}
/*Disable the target while logged in (e.g. the password field and keyboard)*/
static void disable_while_logged_in_observer_cb(lv_observer_t * observer, lv_subject_t * subject)
{
lv_obj_t * obj = lv_observer_get_target_obj(observer);
lv_obj_set_state(obj, LV_STATE_DISABLED, lv_subject_get_int(subject) == LOGGED_IN);
}
/*Enable the target only while logged in, disable it otherwise*/
static void enable_while_logged_in_observer_cb(lv_observer_t * observer, lv_subject_t * subject)
{
lv_obj_t * obj = lv_observer_get_target_obj(observer);
lv_obj_set_state(obj, LV_STATE_DISABLED, lv_subject_get_int(subject) != LOGGED_IN);
}
static void log_out_click_event_cb(lv_event_t * e)
{
LV_UNUSED(e);
@@ -110,7 +123,7 @@ static void ui_init(void)
lv_textarea_set_password_mode(ta, true);
lv_textarea_set_placeholder_text(ta, "The password is: hello");
lv_obj_add_event_cb(ta, textarea_event_cb, LV_EVENT_READY, NULL);
lv_obj_bind_state_if_eq(ta, &auth_state_subject, LV_STATE_DISABLED, LOGGED_IN);
lv_subject_add_observer_obj(&auth_state_subject, disable_while_logged_in_observer_cb, ta, NULL);
lv_obj_t * kb = lv_keyboard_create(lv_screen_active());
lv_keyboard_set_textarea(kb, ta);
@@ -122,7 +135,7 @@ static void ui_init(void)
btn = lv_button_create(lv_screen_active());
lv_obj_set_pos(btn, 220, 10);
lv_obj_add_event_cb(btn, log_out_click_event_cb, LV_EVENT_CLICKED, NULL);
lv_obj_bind_state_if_not_eq(btn, &auth_state_subject, LV_STATE_DISABLED, LOGGED_IN);
lv_subject_add_observer_obj(&auth_state_subject, enable_while_logged_in_observer_cb, btn, NULL);
label = lv_label_create(btn);
lv_label_set_text(label, "LOG OUT");
@@ -136,7 +149,7 @@ static void ui_init(void)
btn = lv_button_create(lv_screen_active());
lv_obj_set_pos(btn, 10, 80);
lv_obj_set_checkable(btn, true);
lv_obj_bind_state_if_not_eq(btn, &auth_state_subject, LV_STATE_DISABLED, LOGGED_IN);
lv_subject_add_observer_obj(&auth_state_subject, enable_while_logged_in_observer_cb, btn, NULL);
lv_obj_bind_checked(btn, &engine_subject);
label = lv_label_create(btn);
lv_label_set_text(label, "START ENGINE");
@@ -36,10 +36,10 @@ typedef enum {
* into `time_subject` via `lv_subject_init_group` so a single observer can
* re-render the time label whenever any element changes. A "Set" button creates
* a bottom container with two rollers and two dropdowns bound through
* `lv_roller_bind_value` and `lv_dropdown_bind_value`; the AM/PM dropdown uses
* `lv_obj_bind_state_if_eq` to disable itself in `TIME_FORMAT_24`. A second
* observer on the format subject swaps the hour roller options between the 12
* and 24 lists.
* `lv_roller_bind_value` and `lv_dropdown_bind_value`; the AM/PM dropdown adds an
* observer with `lv_subject_add_observer_obj` to disable itself in
* `TIME_FORMAT_24`. A second observer on the format subject swaps the hour roller
* options between the 12 and 24 lists.
*/
void lv_example_observer_3(void)
{
@@ -72,6 +72,13 @@ void lv_example_observer_3(void)
lv_subject_set_int(&am_pm_subject, TIME_PM);
}
/*Disable the AM/PM dropdown while the 24-hour format is selected*/
static void am_pm_disabled_observer_cb(lv_observer_t * observer, lv_subject_t * subject)
{
lv_obj_t * obj = lv_observer_get_target_obj(observer);
lv_obj_set_state(obj, LV_STATE_DISABLED, lv_subject_get_int(subject) == TIME_FORMAT_24);
}
static void set_btn_clicked_event_cb(lv_event_t * e)
{
lv_obj_t * set_btn = lv_event_get_target_obj(e);
@@ -101,7 +108,7 @@ static void set_btn_clicked_event_cb(lv_event_t * e)
lv_obj_t * am_pm_dropdown = lv_dropdown_create(cont);
lv_dropdown_set_options(am_pm_dropdown, "am\npm");
lv_dropdown_bind_value(am_pm_dropdown, &am_pm_subject);
lv_obj_bind_state_if_eq(am_pm_dropdown, &format_subject, LV_STATE_DISABLED, TIME_FORMAT_24);
lv_subject_add_observer_obj(&format_subject, am_pm_disabled_observer_cb, am_pm_dropdown, NULL);
lv_obj_set_pos(am_pm_dropdown, 128, 48);
lv_obj_set_width(am_pm_dropdown, 80);
@@ -10,9 +10,15 @@
* @brief A checkbox drives an int subject; a sibling label hides when the subject is zero.
*
* `bind_checked` connects the checkbox's checked state to `subject_flag` (0 or 1).
* The note label uses `bind_flag_if_eq` to enable the `hidden` flag while
* `subject_flag` equals 0 — so the label only shows once the box is ticked.
* The note label uses a subject to enable the `hidden` flag when `subject_flag`
* equals 0 — so the label only shows once the box is ticked.
*/
static void show_label(lv_obj_t * obj, bool value)
{
lv_obj_set_hidden(obj, !value);
}
void lv_example_checkbox_bind_checked(void)
{
static lv_subject_t subject_flag;
@@ -36,6 +42,6 @@ void lv_example_checkbox_bind_checked(void)
lv_label_set_text(label, "Extra details only visible while the box is ticked.");
lv_obj_set_align(label, LV_ALIGN_CENTER);
lv_obj_set_y(label, 30);
lv_obj_bind_flag_if_eq(label, &subject_flag, LV_OBJ_FLAG_HIDDEN, 0);
lv_obj_bind_bool(label, &subject_flag, show_label);
}
#endif
@@ -14,6 +14,10 @@
* a guard like "block submit when the value is out of range". Slide past 80 to
* watch the button grey out.
*/
static void disable_if_gt_80(lv_obj_t * obj, int32_t value)
{
lv_obj_set_disabled(obj, value > 80);
}
void lv_example_slider_bind_state(void)
{
static lv_subject_t subject_value;
@@ -46,7 +50,6 @@ void lv_example_slider_bind_state(void)
lv_obj_t * label_2 = lv_label_create(button);
lv_obj_set_align(label_2, LV_ALIGN_CENTER);
lv_label_set_text(label_2, "Submit");
lv_obj_bind_state_if_gt(button, &subject_value, LV_STATE_DISABLED, 80);
lv_obj_bind_int(button, &subject_value, disable_if_gt_80);
}
#endif
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -1540,7 +1540,7 @@ static void remove_style_core(lv_obj_t * obj, const lv_style_t * style, lv_style
static void bind_style_observer_cb(lv_observer_t * observer, lv_subject_t * subject)
{
bind_style_t * p = observer->user_data;
bind_style_t * p = lv_observer_get_user_data(observer);
int32_t v = lv_subject_get_int(subject);
bool dis = (v != p->value);
@@ -1549,7 +1549,7 @@ static void bind_style_observer_cb(lv_observer_t * observer, lv_subject_t * subj
static void bind_style_prop_observer_cb(lv_observer_t * observer, lv_subject_t * subject)
{
bind_style_prop_t * p = observer->user_data;
bind_style_prop_t * p = lv_observer_get_user_data(observer);
lv_style_value_t style_v;
if(subject->type == LV_SUBJECT_TYPE_INT) style_v.num = lv_subject_get_int(subject);
+173 -6
View File
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -33,7 +33,8 @@ struct _lv_observer_t {
lv_subject_t * subject; /**< Observed subject */
lv_observer_cb_t cb; /**< Callback that notifies when value changes */
void * target; /**< A target for the observer, e.g. a widget or any pointer */
void * user_data; /**< Additional parameter supplied when subscribing */
void * user_data; /**< Additional parameter, can be used freely by user */
void (*user_cb)(void); /**< Additional function pointer, can be used freely by user */
uint32_t auto_free_user_data : 1; /**< Automatically free user data when observer is removed */
uint32_t notified : 1; /**< Was observer already notified? */
uint32_t for_obj : 1; /**< Is `target` a pointer to a Widget (`lv_obj_t *`)? */
+1 -1
View File
@@ -1559,7 +1559,7 @@ static void label_text_observer_cb(lv_observer_t * observer, lv_subject_t * subj
LV_ASSERT(observer != NULL);
LV_ASSERT(observer->target != NULL);
LV_ASSERT(subject != NULL);
const char * fmt = observer->user_data;
const char * fmt = lv_observer_get_user_data(observer);
if(fmt == NULL) {
lv_label_set_text(observer->target, subject->value.pointer);
+8 -9
View File
@@ -2017,35 +2017,34 @@ static void needle_deleted_cb(lv_event_t * e)
static void scale_section_min_value_observer_cb(lv_observer_t * observer, lv_subject_t * subject)
{
LV_ASSERT(observer != NULL);
LV_ASSERT(observer->user_data != NULL);
LV_ASSERT(observer->target != NULL);
LV_ASSERT(subject != NULL);
LV_ASSERT(subject->type == LV_SUBJECT_TYPE_INT);
lv_scale_section_t * section = lv_observer_get_user_data(observer);
LV_ASSERT(section != NULL);
lv_scale_section_t * section = observer->user_data;
lv_scale_set_section_min_value(observer->target, section, subject->value.num);
}
static void scale_section_max_value_observer_cb(lv_observer_t * observer, lv_subject_t * subject)
{
LV_ASSERT(observer != NULL);
LV_ASSERT(observer->user_data != NULL);
LV_ASSERT(observer->target != NULL);
LV_ASSERT(subject != NULL);
LV_ASSERT(subject->type == LV_SUBJECT_TYPE_INT);
lv_scale_section_t * section = observer->user_data;
lv_scale_section_t * section = lv_observer_get_user_data(observer);
LV_ASSERT(section != NULL);
lv_scale_set_section_max_value(observer->target, section, subject->value.num);
}
static void scale_line_needle_value_observer_cb(lv_observer_t * observer, lv_subject_t * subject)
{
LV_ASSERT(observer != NULL);
LV_ASSERT(observer->user_data != NULL);
LV_ASSERT(observer->target != NULL);
LV_ASSERT(subject != NULL);
LV_ASSERT(subject->type == LV_SUBJECT_TYPE_INT);
bind_element_needle_t * bind_element = observer->user_data;
bind_element_needle_t * bind_element = lv_observer_get_user_data(observer);
LV_ASSERT(bind_element != NULL);
lv_scale_set_line_needle_value(observer->target, bind_element->needle_line, bind_element->needle_length,
subject->value.num);
}
@@ -2053,11 +2052,11 @@ static void scale_line_needle_value_observer_cb(lv_observer_t * observer, lv_sub
static void scale_image_needle_value_observer_cb(lv_observer_t * observer, lv_subject_t * subject)
{
LV_ASSERT(observer != NULL);
LV_ASSERT(observer->user_data != NULL);
LV_ASSERT(observer->target != NULL);
LV_ASSERT(subject != NULL);
LV_ASSERT(subject->type == LV_SUBJECT_TYPE_INT);
lv_obj_t * needle_img = observer->user_data;
lv_obj_t * needle_img = lv_observer_get_user_data(observer);
LV_ASSERT(needle_img != NULL);
lv_scale_set_image_needle_value(observer->target, needle_img, subject->value.num);
}
+1 -1
View File
@@ -1531,7 +1531,7 @@ static void span_text_observer_cb(lv_observer_t * observer, lv_subject_t * subje
LV_ASSERT(subject != NULL);
LV_ASSERT(subject->type == LV_SUBJECT_TYPE_INT || subject->type == LV_SUBJECT_TYPE_FLOAT ||
subject->type == LV_SUBJECT_TYPE_STRING || subject->type == LV_SUBJECT_TYPE_POINTER);
bind_element_string_t * user_data = observer->user_data;
bind_element_string_t * user_data = lv_observer_get_user_data(observer);
LV_ASSERT(user_data != NULL);
if(user_data->fmt == NULL) {
File diff suppressed because it is too large Load Diff