feat(observer): implemented an observer pattern (#4541)
Co-authored-by: Zoltan Janosy <zjanosy@fishman.com> Co-authored-by: Gabor Kiss-Vamosi <kisvegabor@gmail.com>
This commit is contained in:
318
docs/others/observer.rst
Normal file
318
docs/others/observer.rst
Normal file
@@ -0,0 +1,318 @@
|
||||
========
|
||||
Observer
|
||||
========
|
||||
|
||||
Overview
|
||||
********
|
||||
|
||||
The ``lv_observer`` module implements a standard `Observer pattern <https://en.wikipedia.org/wiki/Observer_pattern>`__.
|
||||
It consists of
|
||||
|
||||
- subjects: each containing a value
|
||||
- observers: attached to subjects to be notified on value change
|
||||
|
||||
|
||||
A typical use case looks like this:
|
||||
|
||||
.. code:: c
|
||||
//It's a global variable
|
||||
lv_subject_t my_subject;
|
||||
|
||||
/*-------
|
||||
* main.c
|
||||
*-------*/
|
||||
|
||||
extern lv_subject_t my_subject;
|
||||
|
||||
void main(void)
|
||||
{
|
||||
//Initialize the subject as integer with the default value of 10
|
||||
lv_subject_init_int(&my_subject, 10);
|
||||
|
||||
some_module_init();
|
||||
}
|
||||
|
||||
/*--------------
|
||||
* some_module.c
|
||||
*--------------*/
|
||||
|
||||
extern lv_subject_t some_subject;
|
||||
|
||||
//Will be called when the related subject's value changes
|
||||
static void some_observer_cb(lv_subject_t * subject, lv_observer_t * observer)
|
||||
{
|
||||
int32_t v = lv_subject_get_int(subject);
|
||||
do_something(v);
|
||||
}
|
||||
|
||||
void some_module_init(void)
|
||||
{
|
||||
//Subscribe to a subject
|
||||
lv_subject_add_observer(&some_subject, some_observer_cb, NULL);
|
||||
}
|
||||
|
||||
/*--------------
|
||||
* some_system.c
|
||||
*--------------*/
|
||||
|
||||
extern lv_subject_t some_subject;
|
||||
|
||||
void some_event(void)
|
||||
{
|
||||
//Set the subject's value to 30. It will notify `some_observer_cb`
|
||||
lv_subject_set_int(&some_subject, 30);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Subject
|
||||
*******
|
||||
|
||||
Subject initialization
|
||||
----------------------
|
||||
|
||||
Subjects have to be static or global :c:expr:`lv_subject_t` type variables.
|
||||
|
||||
To initialize a subject use :c:expr:`lv_subject_init_<type>(&subject, <params>, init_value)`.
|
||||
The following initializations exist for types:
|
||||
|
||||
- **Integer** :c:expr:`void lv_subject_init_int(lv_subject_t * subject, int32_t value)`
|
||||
- **String** :c:expr:`void lv_subject_init_string(lv_subject_t * subject, char * buf, char * prev_buf, size_t size, const char * value)`
|
||||
- **Pointer** :c:expr:`void lv_subject_init_pointer(lv_subject_t * subject, void * value)`
|
||||
- **Color** :c:expr:`void lv_subject_init_color(lv_subject_t * subject, lv_color_t color)`
|
||||
- **Group** :c:expr:`void lv_subject_init_group(lv_subject_t * subject, lv_subject_t * list[], uint32_t list_len)`
|
||||
|
||||
|
||||
Set subject value
|
||||
-----------------
|
||||
|
||||
The following functions can be used to set a subject's value:
|
||||
|
||||
- **Integer** :c:expr:`void lv_subject_set_int(lv_subject_t * subject, int32_t value)`
|
||||
- **String** :c:expr:`void lv_subject_copy_string(lv_subject_t * subject, char * buf)`
|
||||
- **Pointer** :c:expr:`void lv_subject_set_pointer(lv_subject_t * subject, void * ptr)`
|
||||
- **Color** :c:expr:`void lv_subject_set_color(lv_subject_t * subject, lv_color_t color)`
|
||||
|
||||
Get subject's value
|
||||
-------------------
|
||||
|
||||
The following functions can be used to get a subject's value:
|
||||
|
||||
|
||||
- **Integer** :c:expr:`int32_t lv_subject_get_int(lv_subject_t * subject)`
|
||||
- **String** :c:expr:`const char * lv_subject_get_string(lv_subject_t * subject)`
|
||||
- **Pointer** :c:expr:`const void * lv_subject_get_pointer(lv_subject_t * subject)`
|
||||
- **Color** :c:expr:`lv_color_t lv_subject_get_color(lv_subject_t * subject)`
|
||||
|
||||
|
||||
Get subject's previous value
|
||||
----------------------------
|
||||
|
||||
The following functions can be used to get a subject's previous value:
|
||||
|
||||
|
||||
- **Integer** :c:expr:`int32_t lv_subject_get_previous_int(lv_subject_t * subject)`
|
||||
- **String** :c:expr:`const char * lv_subject_get_previous_string(lv_subject_t * subject)`
|
||||
- **Pointer** :c:expr:`const void * lv_subject_get_previous_pointer(lv_subject_t * subject)`
|
||||
- **Color** :c:expr:`lv_color_t lv_subject_get_previous_color(lv_subject_t * subject)`
|
||||
|
||||
|
||||
Observer
|
||||
********
|
||||
|
||||
Subscribe to a subject
|
||||
----------------------
|
||||
|
||||
to subscribe to a subject the following function can be used:
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_observer_t * observer = lv_subject_add_observer(&some_subject, some_observer_cb, user_data);
|
||||
|
||||
|
||||
Where the observer callback should look like this:
|
||||
.. code:: c
|
||||
|
||||
static void some_observer_cb(lv_subject_t * subject, lv_observer_t * observer)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
|
||||
It's also possible to save a target widget when subscribing to a subject.
|
||||
In this case when widget is deleted, it will automatically unsubscribe from the subject.
|
||||
|
||||
In the observer callback ``lv_observer_get_target(observer)`` can be used to get the saved widget.
|
||||
.. code:: c
|
||||
|
||||
lv_observer_t * observer = lv_subject_add_observer_obj(&some_subject, some_observer_cb, obj, user_data);
|
||||
|
||||
|
||||
In more generic case any pointer can be saved a target:
|
||||
.. code:: c
|
||||
|
||||
lv_observer_t * observer = lv_subject_add_observer_with_target(&some_subject, some_observer_cb, some_pointer, user_data);
|
||||
|
||||
|
||||
|
||||
Unsubscribe from a subject
|
||||
--------------------------
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_observer_remove(observer)
|
||||
|
||||
To unsubscribe from a subject with all widgets you can use:
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_subject_remove_obj(subject, obj)
|
||||
|
||||
|
||||
Subject groups
|
||||
**************
|
||||
|
||||
There are cases when a subject changes, the value of some other subjects are also required.
|
||||
As practical example imagine an instrument which measures either voltage or current.
|
||||
To display the measured value on a label 3 things are required:
|
||||
|
||||
1. What we measure (current or voltage)?
|
||||
2. What is the measured value?
|
||||
3. What is the unit (mV, V, mA, A)?
|
||||
|
||||
What any of these 3 parameters changes the label needs to be updated,
|
||||
and it needs to know all 3 parameters to compose its text.
|
||||
|
||||
For these kind of scenarios subject groups can be created.
|
||||
For the above example it looks like this:
|
||||
|
||||
.. code:: c
|
||||
lv_subject_t subject_mode; //Voltage or Current
|
||||
lv_subject_t subject_value; //Measured value
|
||||
lv_subject_t subject_unit; //The unit
|
||||
lv_subject_t subject_all; //It will be the subject group
|
||||
lv_subject_t subject_list[3] = {&subject_mode, &subject_value, &subject_unit}; //The elements of the group
|
||||
|
||||
lv_subject_init_int(&subject_mode, 0); //Let's say 0 is Voltage, 1 is Current
|
||||
lv_subject_init_int(&subject_value, 0);
|
||||
lv_subject_init_pointer(&subject_unit, "V");
|
||||
lv_subject_init_group(&subject_all, subject_list, 3);
|
||||
|
||||
lv_subject_add_observer(&subject_all, all_observer_cb, some_label, NULL);
|
||||
|
||||
...
|
||||
|
||||
static void some_observer_cb(lv_subject_t * subject, lv_observer_t * observer)
|
||||
{
|
||||
lv_obj_t * label = lv_observer_get_target(observer);
|
||||
lv_subject_t * subject_mode = lv_subject_get_group_element(subject, 0)
|
||||
lv_subject_t * subject_value = lv_subject_get_group_element(subject, 1)
|
||||
lv_subject_t * subject_unit = lv_subject_get_group_element(subject, 2)
|
||||
|
||||
int32_t mode = lv_subject_get_int(subject_mode);
|
||||
int32_t value = lv_subject_get_int(subject_value);
|
||||
const char * unit = lv_subject_get_pointer(subject_unit);
|
||||
|
||||
|
||||
lv_label_set_text_fmt(label, "%s: %d %s, mode ? "Current" : "Voltage", value, unit);
|
||||
}
|
||||
|
||||
|
||||
Widget binding
|
||||
**************
|
||||
|
||||
Base object
|
||||
-----------
|
||||
|
||||
Set an object flag if an integer subject's value is equal to a reference value, clear the flag otherwise
|
||||
|
||||
.. code:: c
|
||||
|
||||
observer = lv_obj_bind_flag_if_eq(obj, &subject, LV_OBJ_FLAG_*, ref_value);
|
||||
|
||||
Set an object flag if an integer subject's value is not equal to a reference value, clear the flag otherwise
|
||||
|
||||
.. code:: c
|
||||
|
||||
observer = lv_obj_bind_flag_if_not_eq(obj, &subject, LV_OBJ_FLAG_*, ref_value);
|
||||
|
||||
Set an object state if an integer subject's value is equal to a reference value, clear the flag otherwise
|
||||
|
||||
.. code:: c
|
||||
|
||||
observer = lv_obj_bind_state_if_eq(obj, &subject, LV_STATE_*, ref_value);
|
||||
|
||||
Set an object state if an integer subject's value is not equal to a reference value, clear the flag otherwise
|
||||
|
||||
.. code:: c
|
||||
|
||||
observer = lv_obj_bind_state_if_not_eq(obj, &subject, LV_STATE_*, ref_value);
|
||||
|
||||
Button
|
||||
------
|
||||
|
||||
Set an integer subject to 1 when a button is checked and set it 0 when unchecked.
|
||||
|
||||
.. code:: c
|
||||
|
||||
observer = lv_button_bind_checked(obj, &subject);
|
||||
|
||||
Label
|
||||
-----
|
||||
|
||||
Bind an integer, string, or pointer (pointing to a string) subject to a label.
|
||||
An optional format string can be added with 1 format specifier (e.g. "%d °C")
|
||||
If the format string is NULL the value will be used directly. In this case on string ans pointer type subjects can be used.
|
||||
|
||||
.. code:: c
|
||||
|
||||
observer = lv_label_bind_text(obj, &subject, format_string);
|
||||
|
||||
|
||||
Arc
|
||||
---
|
||||
|
||||
Bind an integer subject to an arc's value.
|
||||
|
||||
.. code:: c
|
||||
|
||||
observer = lv_arc_bind_value(obj, &subject);
|
||||
|
||||
Slider
|
||||
------
|
||||
|
||||
Bind an integer subject to a slider's value
|
||||
|
||||
.. code:: c
|
||||
|
||||
observer = lv_slider_bind_value(obj, &subject);
|
||||
|
||||
Roller
|
||||
------
|
||||
|
||||
Bind an integer subject to a roller's value
|
||||
|
||||
.. code:: c
|
||||
|
||||
observer = lv_roller_bind_value(obj, &subject);
|
||||
|
||||
|
||||
Drop-down
|
||||
---------
|
||||
Bind an integer subject to a drop-down's value
|
||||
|
||||
.. code:: c
|
||||
|
||||
observer = lv_dropdown_bind_value(obj, &subject);
|
||||
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
.. include:: ../examples/others/observer/index.rst
|
||||
|
||||
API
|
||||
---
|
||||
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ extern "C" {
|
||||
#include "imgfont/lv_example_imgfont.h"
|
||||
#include "monkey/lv_example_monkey.h"
|
||||
#include "msg/lv_example_msg.h"
|
||||
#include "observer/lv_example_observer.h"
|
||||
#include "snapshot/lv_example_snapshot.h"
|
||||
|
||||
/*********************
|
||||
|
||||
38
examples/others/observer/index.rst
Normal file
38
examples/others/observer/index.rst
Normal file
@@ -0,0 +1,38 @@
|
||||
|
||||
Bind a slider's value to a label
|
||||
--------------------------------
|
||||
|
||||
.. lv_example:: others/observer/lv_example_observer_1
|
||||
:language: c
|
||||
|
||||
Handling login and its states
|
||||
-----------------------------
|
||||
|
||||
.. lv_example:: others/observer/lv_example_observer_2
|
||||
:language: c
|
||||
|
||||
Set time with 12/24 mode and AM/PM
|
||||
----------------------------------
|
||||
|
||||
.. lv_example:: others/observer/lv_example_observer_3
|
||||
:language: c
|
||||
|
||||
Custom tab view with state management
|
||||
-------------------------------------
|
||||
|
||||
.. lv_example:: others/observer/lv_example_observer_4
|
||||
:language: c
|
||||
|
||||
Firmware update process
|
||||
-----------------------
|
||||
|
||||
.. lv_example:: others/observer/lv_example_observer_5
|
||||
:language: c
|
||||
|
||||
Modular style update on theme change
|
||||
------------------------------------
|
||||
|
||||
.. lv_example:: others/observer/lv_example_observer_6
|
||||
:language: c
|
||||
|
||||
|
||||
43
examples/others/observer/lv_example_observer.h
Normal file
43
examples/others/observer/lv_example_observer.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* @file lv_example_observer.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_EXAMPLE_OBSERVER_H
|
||||
#define LV_EXAMPLE_OBSERVER_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
void lv_example_observer_1(void);
|
||||
void lv_example_observer_2(void);
|
||||
void lv_example_observer_3(void);
|
||||
void lv_example_observer_4(void);
|
||||
void lv_example_observer_5(void);
|
||||
void lv_example_observer_6(void);
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_EXAMPLE_OBSERVER_H*/
|
||||
25
examples/others/observer/lv_example_observer_1.c
Normal file
25
examples/others/observer/lv_example_observer_1.c
Normal file
@@ -0,0 +1,25 @@
|
||||
#include "../../lv_examples.h"
|
||||
#if LV_USE_OBSERVER && LV_USE_SLIDER && LV_USE_LABEL && LV_BUILD_EXAMPLES
|
||||
|
||||
static lv_subject_t temperature_subject;
|
||||
|
||||
/**
|
||||
* A slider sends a message on value change and a label display's that value
|
||||
*/
|
||||
void lv_example_observer_1(void)
|
||||
{
|
||||
lv_subject_init_int(&temperature_subject, 28);
|
||||
|
||||
/*Create a slider in the center of the display*/
|
||||
lv_obj_t * slider = lv_slider_create(lv_scr_act());
|
||||
lv_obj_center(slider);
|
||||
lv_slider_bind_value(slider, &temperature_subject);
|
||||
|
||||
/*Create a label below the slider*/
|
||||
lv_obj_t * label = lv_label_create(lv_scr_act());
|
||||
lv_obj_align(label, LV_ALIGN_CENTER, 0, 30);
|
||||
lv_label_bind_text(label, &temperature_subject, "%d °C");
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
136
examples/others/observer/lv_example_observer_2.c
Normal file
136
examples/others/observer/lv_example_observer_2.c
Normal file
@@ -0,0 +1,136 @@
|
||||
#include "../../lv_examples.h"
|
||||
#if LV_USE_OBSERVER && LV_USE_SLIDER && LV_USE_LABEL && LV_BUILD_EXAMPLES
|
||||
|
||||
/*This the only interface between the UI and the application*/
|
||||
static lv_subject_t engine_subject;
|
||||
|
||||
static void app_init(void);
|
||||
static void ui_init(void);
|
||||
|
||||
/**
|
||||
* Simple PIN login screen to start an engine.
|
||||
* The only interface between the UI and the application is a single "subject".
|
||||
*/
|
||||
void lv_example_observer_2(void)
|
||||
{
|
||||
lv_subject_init_int(&engine_subject, 0);
|
||||
app_init();
|
||||
ui_init();
|
||||
}
|
||||
|
||||
|
||||
/*--------------------------------------------------
|
||||
* APPLICATION
|
||||
*
|
||||
* This part contains a demo application logic.
|
||||
* It doesn't know anything about the internals of the UI
|
||||
* and uses any the `engine_subject` as an interface.
|
||||
* -------------------------------------------------*/
|
||||
static void engine_state_observer_cb(lv_subject_t * subject, lv_observer_t * observer)
|
||||
{
|
||||
LV_UNUSED(observer);
|
||||
|
||||
int32_t v = lv_subject_get_int(subject);
|
||||
/*In a real application set/clear a pin here*/
|
||||
LV_LOG_USER("Engine state: %d", v);
|
||||
}
|
||||
|
||||
static void app_init(void)
|
||||
{
|
||||
lv_subject_add_observer(&engine_subject, engine_state_observer_cb, NULL);
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
* USER INTERFACE
|
||||
*
|
||||
* This part contains only UI related code and data.
|
||||
* In a project it would a separate file and the
|
||||
* application couldn't see its internals
|
||||
* -------------------------------------------------*/
|
||||
|
||||
typedef enum {
|
||||
LOGGED_OUT,
|
||||
LOGGED_IN,
|
||||
AUTH_FAILED,
|
||||
} auth_state_t;
|
||||
|
||||
static lv_subject_t auth_state_subject;
|
||||
|
||||
static void textarea_event_cb(lv_event_t * e)
|
||||
{
|
||||
lv_obj_t * ta = lv_event_get_target(e);
|
||||
if(strcmp(lv_textarea_get_text(ta), "hello") == 0) {
|
||||
lv_subject_set_int(&auth_state_subject, LOGGED_IN);
|
||||
}
|
||||
else {
|
||||
lv_subject_set_int(&auth_state_subject, AUTH_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
static void info_label_observer_cb(lv_subject_t * subject, lv_observer_t * observer)
|
||||
{
|
||||
lv_obj_t * label = lv_observer_get_target(observer);
|
||||
switch(lv_subject_get_int(subject)) {
|
||||
case LOGGED_IN:
|
||||
lv_label_set_text(label, "Login successful");
|
||||
break;
|
||||
case LOGGED_OUT:
|
||||
lv_label_set_text(label, "Logged out");
|
||||
break;
|
||||
case AUTH_FAILED:
|
||||
lv_label_set_text(label, "Login failed");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void log_out_click_event_cb(lv_event_t * e)
|
||||
{
|
||||
LV_UNUSED(e);
|
||||
lv_subject_set_int(&auth_state_subject, LOGGED_OUT);
|
||||
}
|
||||
|
||||
static void ui_init(void)
|
||||
{
|
||||
lv_subject_init_int(&auth_state_subject, LOGGED_OUT);
|
||||
|
||||
/*Create a slider in the center of the display*/
|
||||
lv_obj_t * ta = lv_textarea_create(lv_scr_act());
|
||||
lv_obj_set_pos(ta, 10, 10);
|
||||
lv_obj_set_width(ta, 200);
|
||||
lv_textarea_set_one_line(ta, true);
|
||||
lv_textarea_set_password_mode(ta, true);
|
||||
lv_textarea_set_placeholder_text(ta, "The password is: hello");
|
||||
lv_obj_add_event(ta, textarea_event_cb, LV_EVENT_READY, NULL);
|
||||
lv_obj_bind_state_if_eq(ta, &auth_state_subject, LV_STATE_DISABLED, LOGGED_IN);
|
||||
|
||||
lv_obj_t * kb = lv_keyboard_create(lv_scr_act());
|
||||
lv_keyboard_set_textarea(kb, ta);
|
||||
|
||||
lv_obj_t * btn;
|
||||
lv_obj_t * label;
|
||||
|
||||
/*Create a log out button which will be active only when logged in*/
|
||||
btn = lv_button_create(lv_scr_act());
|
||||
lv_obj_set_pos(btn, 220, 10);
|
||||
lv_obj_add_event(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);
|
||||
|
||||
label = lv_label_create(btn);
|
||||
lv_label_set_text(label, "LOG OUT");
|
||||
|
||||
/*Create a label to show info*/
|
||||
label = lv_label_create(lv_scr_act());
|
||||
lv_obj_set_pos(label, 10, 60);
|
||||
lv_subject_add_observer_obj(&auth_state_subject, info_label_observer_cb, label, NULL);
|
||||
|
||||
/*Create button which will be active only when logged in*/
|
||||
btn = lv_button_create(lv_scr_act());
|
||||
lv_obj_set_pos(btn, 10, 80);
|
||||
lv_obj_add_flag(btn, LV_OBJ_FLAG_CHECKABLE);
|
||||
lv_obj_bind_state_if_not_eq(btn, &auth_state_subject, LV_STATE_DISABLED, LOGGED_IN);
|
||||
lv_button_bind_checked(btn, &engine_subject);
|
||||
label = lv_label_create(btn);
|
||||
lv_label_set_text(label, "START ENGINE");
|
||||
}
|
||||
|
||||
#endif
|
||||
160
examples/others/observer/lv_example_observer_3.c
Normal file
160
examples/others/observer/lv_example_observer_3.c
Normal file
@@ -0,0 +1,160 @@
|
||||
#include "../../lv_examples.h"
|
||||
#if LV_USE_OBSERVER && LV_USE_SLIDER && LV_USE_LABEL && LV_USE_ROLLER && LV_USE_DROPDOWN && LV_FONT_MONTSERRAT_30 && LV_BUILD_EXAMPLES
|
||||
|
||||
static lv_subject_t hour_subject;
|
||||
static lv_subject_t minute_subject;
|
||||
static lv_subject_t format_subject;
|
||||
static lv_subject_t am_pm_subject;
|
||||
static lv_subject_t time_subject;
|
||||
static lv_subject_t * time_group_array_subject[] = {&hour_subject, &minute_subject, &format_subject, &am_pm_subject};
|
||||
const char * hour12_options = "01\n02\n03\n04\n05\n06\n07\n08\n09\n10\n11\n12";
|
||||
const char * hour24_options =
|
||||
"00\n01\n02\n03\n04\n05\n06\n07\n08\n09\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23";
|
||||
const char * minute_options =
|
||||
"00\n01\n02\n03\n04\n05\n06\n07\n08\n09\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n25\n26\n27\n28\n29\n30\n31\n32\n33\n34\n35\n36\n37\n38\n39\n40\n41\n42\n43\n44\n45\n46\n47\n48\n49\n50\n51\n52\n53\n54\n55\n56\n57\n58\n59";
|
||||
|
||||
static void set_btn_clicked_event_cb(lv_event_t * e);
|
||||
static void close_clicked_event_cb(lv_event_t * e);
|
||||
static void hour_roller_options_update(lv_subject_t * subject, lv_observer_t * observer);
|
||||
static void time_observer_cb(lv_subject_t * subject, lv_observer_t * observer);
|
||||
|
||||
typedef enum {
|
||||
TIME_FORMAT_12,
|
||||
TIME_FORMAT_24,
|
||||
} time_format_t;
|
||||
|
||||
typedef enum {
|
||||
TIME_AM,
|
||||
TIME_PM,
|
||||
} time_am_pm_t;
|
||||
|
||||
/**
|
||||
* Show how to handle a complex time setting with hour, minute, 12/24 hour mode, and AM/PM switch
|
||||
* In a real application the time can be displayed on multiple screens and it's not trivial
|
||||
* how and where to store the current values and how to get them.
|
||||
* In this example the widgets to set the time are create/deleted dynamically,
|
||||
* yet they always know what the current values are by using subjects.
|
||||
*/
|
||||
void lv_example_observer_3(void)
|
||||
{
|
||||
/*Initialize the subjects.
|
||||
*The UI will update these and read the current values from here,
|
||||
*however the application can update these values at any time and
|
||||
*the widgets will be updated automatically. */
|
||||
lv_subject_init_int(&hour_subject, 7);
|
||||
lv_subject_init_int(&minute_subject, 45);
|
||||
lv_subject_init_int(&format_subject, TIME_FORMAT_12);
|
||||
lv_subject_init_int(&am_pm_subject, TIME_AM);
|
||||
lv_subject_init_group(&time_subject, time_group_array_subject, 4);
|
||||
|
||||
/*Create the UI*/
|
||||
lv_obj_t * time_label = lv_label_create(lv_scr_act());
|
||||
lv_obj_set_style_text_font(time_label, &lv_font_montserrat_30, 0);
|
||||
lv_subject_add_observer_obj(&time_subject, time_observer_cb, time_label, NULL);
|
||||
lv_obj_set_pos(time_label, 24, 24);
|
||||
|
||||
lv_obj_t * set_btn = lv_button_create(lv_scr_act());
|
||||
lv_obj_set_pos(set_btn, 180, 24);
|
||||
lv_obj_add_event(set_btn, set_btn_clicked_event_cb, LV_EVENT_CLICKED, NULL);
|
||||
|
||||
lv_obj_t * set_label = lv_label_create(set_btn);
|
||||
lv_label_set_text(set_label, "Set");
|
||||
|
||||
|
||||
/*Update some subjects to see if the UI is updated as well*/
|
||||
lv_subject_set_int(&hour_subject, 9);
|
||||
lv_subject_set_int(&minute_subject, 30);
|
||||
lv_subject_set_int(&am_pm_subject, TIME_PM);
|
||||
}
|
||||
|
||||
|
||||
static void set_btn_clicked_event_cb(lv_event_t * e)
|
||||
{
|
||||
lv_obj_t * set_btn = lv_event_get_target(e);
|
||||
lv_obj_add_state(set_btn, LV_STATE_DISABLED);
|
||||
|
||||
lv_obj_t * cont = lv_obj_create(lv_scr_act());
|
||||
lv_obj_set_size(cont, lv_pct(100), LV_SIZE_CONTENT);
|
||||
lv_obj_align(cont, LV_ALIGN_BOTTOM_MID, 0, 0);
|
||||
|
||||
lv_obj_t * hour_roller = lv_roller_create(cont);
|
||||
lv_obj_add_flag(hour_roller, LV_OBJ_FLAG_FLEX_IN_NEW_TRACK);
|
||||
lv_subject_add_observer_obj(&format_subject, hour_roller_options_update, hour_roller, NULL);
|
||||
lv_roller_bind_value(hour_roller, &hour_subject);
|
||||
lv_obj_set_pos(hour_roller, 0, 0);
|
||||
|
||||
lv_obj_t * min_roller = lv_roller_create(cont);
|
||||
lv_roller_set_options(min_roller, minute_options, LV_ROLLER_MODE_NORMAL);
|
||||
lv_roller_bind_value(min_roller, &minute_subject);
|
||||
lv_obj_set_pos(min_roller, 64, 0);
|
||||
|
||||
lv_obj_t * format_dropdown = lv_dropdown_create(cont);
|
||||
lv_dropdown_set_options(format_dropdown, "12\n24");
|
||||
lv_dropdown_bind_value(format_dropdown, &format_subject);
|
||||
lv_obj_set_pos(format_dropdown, 128, 0);
|
||||
lv_obj_set_width(format_dropdown, 80);
|
||||
|
||||
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_obj_set_pos(am_pm_dropdown, 128, 48);
|
||||
lv_obj_set_width(am_pm_dropdown, 80);
|
||||
|
||||
lv_obj_t * close_btn = lv_button_create(cont);
|
||||
lv_obj_align(close_btn, LV_ALIGN_TOP_RIGHT, 0, 0);
|
||||
/*Pass the set_btn as user_data to make it non-disabled on close*/
|
||||
lv_obj_add_event(close_btn, close_clicked_event_cb, LV_EVENT_CLICKED, set_btn);
|
||||
|
||||
lv_obj_t * close_label = lv_label_create(close_btn);
|
||||
lv_label_set_text(close_label, LV_SYMBOL_CLOSE);
|
||||
}
|
||||
|
||||
static void close_clicked_event_cb(lv_event_t * e)
|
||||
{
|
||||
lv_obj_t * set_btn = lv_event_get_user_data(e);
|
||||
lv_obj_t * close_btn = lv_event_get_target(e);
|
||||
lv_obj_t * cont = lv_obj_get_parent(close_btn);
|
||||
lv_obj_clear_state(set_btn, LV_STATE_DISABLED);
|
||||
lv_obj_del(cont);
|
||||
}
|
||||
|
||||
/*Watch all related subject to display the current time correctly*/
|
||||
static void time_observer_cb(lv_subject_t * subject, lv_observer_t * observer)
|
||||
{
|
||||
int32_t hour = lv_subject_get_int(lv_subject_get_group_element(subject, 0));
|
||||
int32_t minute = lv_subject_get_int(lv_subject_get_group_element(subject, 1));
|
||||
int32_t format = lv_subject_get_int(lv_subject_get_group_element(subject, 2));
|
||||
int32_t am_pm = lv_subject_get_int(lv_subject_get_group_element(subject, 3));
|
||||
|
||||
lv_obj_t * label = lv_observer_get_target(observer);
|
||||
|
||||
if(format == TIME_FORMAT_24) {
|
||||
lv_label_set_text_fmt(label, "%d:%02d", hour, minute);
|
||||
}
|
||||
else {
|
||||
lv_label_set_text_fmt(label, "%d:%02d %s", hour + 1, minute, am_pm == TIME_AM ? "am" : "pm");
|
||||
}
|
||||
}
|
||||
|
||||
/*Change the hour options on format change*/
|
||||
static void hour_roller_options_update(lv_subject_t * subject, lv_observer_t * observer)
|
||||
{
|
||||
lv_obj_t * roller = lv_observer_get_target(observer);
|
||||
int32_t prev_selected = lv_roller_get_selected(roller);
|
||||
int32_t v = lv_subject_get_int(subject);
|
||||
if(v == TIME_FORMAT_12) {
|
||||
prev_selected--;
|
||||
if(prev_selected > 12) prev_selected -= 12;
|
||||
lv_roller_set_options(roller, hour12_options, LV_ROLLER_MODE_NORMAL);
|
||||
}
|
||||
else {
|
||||
prev_selected++;
|
||||
lv_roller_set_options(roller, hour24_options, LV_ROLLER_MODE_NORMAL);
|
||||
}
|
||||
|
||||
lv_roller_set_selected(roller, prev_selected, LV_ANIM_OFF);
|
||||
lv_obj_send_event(roller, LV_EVENT_VALUE_CHANGED, NULL);
|
||||
}
|
||||
|
||||
#endif
|
||||
206
examples/others/observer/lv_example_observer_4.c
Normal file
206
examples/others/observer/lv_example_observer_4.c
Normal file
@@ -0,0 +1,206 @@
|
||||
#include "../../lv_examples.h"
|
||||
#if LV_USE_OBSERVER && LV_USE_SLIDER && LV_USE_LABEL && LV_USE_ROLLER && LV_USE_DROPDOWN && LV_FONT_MONTSERRAT_30 && LV_BUILD_EXAMPLES
|
||||
|
||||
static void cont_observer_cb(lv_subject_t * subject, lv_observer_t * observer);
|
||||
static void btn_create(lv_obj_t * parent, const char * text);
|
||||
static void btn_click_event_cb(lv_event_t * e);
|
||||
static void btn_observer_cb(lv_subject_t * subject, lv_observer_t * observer);
|
||||
static void indicator_observer_cb(lv_subject_t * subject, lv_observer_t * observer);
|
||||
|
||||
static lv_subject_t current_tab_subject;
|
||||
static lv_subject_t slider_subject[4];
|
||||
static lv_subject_t dropdown_subject[3];
|
||||
static lv_subject_t roller_subject[2];
|
||||
|
||||
void lv_example_observer_4(void)
|
||||
{
|
||||
lv_subject_init_int(¤t_tab_subject, 0);
|
||||
lv_subject_init_int(&slider_subject[0], 0);
|
||||
lv_subject_init_int(&slider_subject[1], 0);
|
||||
lv_subject_init_int(&slider_subject[2], 0);
|
||||
lv_subject_init_int(&slider_subject[3], 0);
|
||||
lv_subject_init_int(&dropdown_subject[0], 0);
|
||||
lv_subject_init_int(&dropdown_subject[1], 0);
|
||||
lv_subject_init_int(&dropdown_subject[2], 0);
|
||||
lv_subject_init_int(&roller_subject[0], 0);
|
||||
lv_subject_init_int(&roller_subject[1], 0);
|
||||
|
||||
lv_obj_t * main_cont = lv_obj_create(lv_scr_act());
|
||||
lv_obj_remove_style_all(main_cont);
|
||||
lv_obj_set_size(main_cont, lv_pct(100), lv_pct(100));
|
||||
lv_obj_set_style_pad_all(main_cont, 0, 0);
|
||||
lv_obj_set_flex_flow(main_cont, LV_FLEX_FLOW_COLUMN);
|
||||
|
||||
lv_obj_t * cont = lv_obj_create(main_cont);
|
||||
lv_obj_remove_style_all(cont);
|
||||
lv_obj_set_flex_grow(cont, 1);
|
||||
lv_obj_set_style_pad_all(cont, 8, 0);
|
||||
lv_obj_set_width(cont, lv_pct(100));
|
||||
lv_subject_add_observer_obj(¤t_tab_subject, cont_observer_cb, cont, NULL);
|
||||
lv_obj_set_scroll_dir(cont, LV_DIR_VER);
|
||||
|
||||
lv_obj_t * footer = lv_obj_create(main_cont);
|
||||
lv_obj_remove_style_all(footer);
|
||||
lv_obj_set_style_pad_column(footer, 8, 0);
|
||||
lv_obj_set_style_pad_all(footer, 8, 0);
|
||||
lv_obj_set_flex_flow(footer, LV_FLEX_FLOW_ROW);
|
||||
lv_obj_set_size(footer, lv_pct(100), 60);
|
||||
lv_obj_align(footer, LV_ALIGN_BOTTOM_MID, 0, 0);
|
||||
|
||||
btn_create(footer, "First");
|
||||
btn_create(footer, "Second");
|
||||
btn_create(footer, "Third");
|
||||
|
||||
lv_obj_t * indicator = lv_obj_create(footer);
|
||||
lv_obj_remove_style(indicator, NULL, 0);
|
||||
lv_obj_set_style_bg_opa(indicator, LV_OPA_40, 0);
|
||||
lv_subject_add_observer_obj(¤t_tab_subject, indicator_observer_cb, indicator, NULL);
|
||||
lv_obj_set_height(indicator, 10);
|
||||
lv_obj_align(indicator, LV_ALIGN_BOTTOM_LEFT, 0, 0);
|
||||
lv_obj_add_flag(indicator, LV_OBJ_FLAG_IGNORE_LAYOUT);
|
||||
|
||||
/*Be sure the indicator has the correct size*/
|
||||
lv_obj_update_layout(indicator);
|
||||
lv_subject_notify(¤t_tab_subject);
|
||||
}
|
||||
|
||||
static int32_t anim_get_x_cb(lv_anim_t * a)
|
||||
{
|
||||
return lv_obj_get_x_aligned(a->var);
|
||||
}
|
||||
|
||||
static void anim_set_x_cb(void * obj, int32_t v)
|
||||
{
|
||||
lv_obj_set_x(obj, v);
|
||||
}
|
||||
|
||||
static void cont_observer_cb(lv_subject_t * subject, lv_observer_t * observer)
|
||||
{
|
||||
int32_t prev_v = lv_subject_get_previous_int(subject);
|
||||
int32_t cur_v = lv_subject_get_int(subject);
|
||||
lv_obj_t * cont = lv_observer_get_target(observer);
|
||||
|
||||
/*Animate out the previous content*/
|
||||
lv_anim_t a;
|
||||
lv_anim_init(&a);
|
||||
lv_anim_set_time(&a, 300);
|
||||
lv_anim_set_path_cb(&a, lv_anim_path_ease_in_out);
|
||||
lv_anim_set_exec_cb(&a, anim_set_x_cb);
|
||||
lv_anim_set_get_value_cb(&a, anim_get_x_cb);
|
||||
lv_anim_set_ready_cb(&a, lv_obj_del_anim_ready_cb);
|
||||
|
||||
uint32_t i;
|
||||
uint32_t delay = 0;
|
||||
uint32_t child_cnt_prev = lv_obj_get_child_cnt(cont);
|
||||
for(i = 0; i < child_cnt_prev; i++) {
|
||||
lv_obj_t * child = lv_obj_get_child(cont, i);
|
||||
lv_anim_set_var(&a, child);
|
||||
if(prev_v < cur_v) {
|
||||
lv_anim_set_values(&a, 0, -20);
|
||||
}
|
||||
else {
|
||||
lv_anim_set_values(&a, 0, 20);
|
||||
}
|
||||
lv_anim_set_delay(&a, delay);
|
||||
lv_anim_start(&a);
|
||||
lv_obj_fade_out(child, 200, delay);
|
||||
delay += 50;
|
||||
}
|
||||
|
||||
/*Create the widgets according to the current value*/
|
||||
if(cur_v == 0) {
|
||||
for(i = 0; i < 4; i++) {
|
||||
lv_obj_t * slider = lv_slider_create(cont);
|
||||
lv_slider_bind_value(slider, &slider_subject[i]);
|
||||
lv_obj_align(slider, LV_ALIGN_TOP_MID, 0, 10 + i * 30);
|
||||
}
|
||||
}
|
||||
if(cur_v == 1) {
|
||||
for(i = 0; i < 3; i++) {
|
||||
lv_obj_t * dropdown = lv_dropdown_create(cont);
|
||||
lv_dropdown_bind_value(dropdown, &dropdown_subject[i]);
|
||||
lv_obj_align(dropdown, LV_ALIGN_TOP_MID, 0, i * 50);
|
||||
}
|
||||
}
|
||||
if(cur_v == 2) {
|
||||
for(i = 0; i < 2; i++) {
|
||||
lv_obj_t * roller = lv_roller_create(cont);
|
||||
lv_roller_bind_value(roller, &roller_subject[i]);
|
||||
lv_obj_align(roller, LV_ALIGN_CENTER, - 80 + i * 160, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*Animate in the new widgets*/
|
||||
lv_anim_set_ready_cb(&a, NULL);
|
||||
for(i = child_cnt_prev; i < lv_obj_get_child_cnt(cont); i++) {
|
||||
lv_obj_t * child = lv_obj_get_child(cont, i);
|
||||
lv_anim_set_var(&a, child);
|
||||
if(prev_v < cur_v) {
|
||||
lv_anim_set_values(&a, 20, 0);
|
||||
}
|
||||
else {
|
||||
lv_anim_set_values(&a, -20, -0);
|
||||
}
|
||||
lv_anim_set_delay(&a, delay);
|
||||
lv_anim_start(&a);
|
||||
lv_obj_fade_in(child, 200, delay);
|
||||
delay += 50;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void btn_create(lv_obj_t * parent, const char * text)
|
||||
{
|
||||
lv_obj_t * btn = lv_button_create(parent);
|
||||
lv_obj_set_flex_grow(btn, 1);
|
||||
lv_obj_set_height(btn, lv_pct(100));
|
||||
lv_obj_set_style_radius(btn, 0, 0);
|
||||
lv_subject_add_observer_obj(¤t_tab_subject, btn_observer_cb, btn, NULL);
|
||||
lv_obj_add_event(btn, btn_click_event_cb, LV_EVENT_CLICKED, NULL);
|
||||
|
||||
lv_obj_t * label = lv_label_create(btn);
|
||||
lv_label_set_text(label, text);
|
||||
lv_obj_center(label);
|
||||
}
|
||||
|
||||
static void btn_click_event_cb(lv_event_t * e)
|
||||
{
|
||||
lv_obj_t * btn = lv_event_get_target(e);
|
||||
uint32_t idx = lv_obj_get_index(btn);
|
||||
lv_subject_set_int(¤t_tab_subject, idx);
|
||||
}
|
||||
|
||||
static void btn_observer_cb(lv_subject_t * subject, lv_observer_t * observer)
|
||||
{
|
||||
int32_t prev_v = lv_subject_get_previous_int(subject);
|
||||
int32_t cur_v = lv_subject_get_int(subject);
|
||||
|
||||
lv_obj_t * btn = lv_observer_get_target(observer);
|
||||
int32_t idx = (int32_t)lv_obj_get_index(btn);
|
||||
|
||||
if(idx == prev_v) lv_obj_clear_state(btn, LV_STATE_CHECKED);
|
||||
if(idx == cur_v) lv_obj_add_state(btn, LV_STATE_CHECKED);
|
||||
}
|
||||
|
||||
static void indicator_observer_cb(lv_subject_t * subject, lv_observer_t * observer)
|
||||
{
|
||||
int32_t cur_v = lv_subject_get_int(subject);
|
||||
lv_obj_t * indicator = lv_observer_get_target(observer);
|
||||
|
||||
lv_obj_t * footer = lv_obj_get_parent(indicator);
|
||||
lv_obj_t * btn_act = lv_obj_get_child(footer, cur_v);
|
||||
lv_obj_set_width(indicator, lv_obj_get_width(btn_act));
|
||||
|
||||
lv_anim_t a;
|
||||
lv_anim_init(&a);
|
||||
lv_anim_set_exec_cb(&a, anim_set_x_cb);
|
||||
lv_anim_set_time(&a, 300);
|
||||
lv_anim_set_path_cb(&a, lv_anim_path_ease_in_out);
|
||||
lv_anim_set_var(&a, indicator);
|
||||
lv_anim_set_values(&a, lv_obj_get_x(indicator), lv_obj_get_x(btn_act));
|
||||
lv_anim_start(&a);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
167
examples/others/observer/lv_example_observer_5.c
Normal file
167
examples/others/observer/lv_example_observer_5.c
Normal file
@@ -0,0 +1,167 @@
|
||||
#include "../../lv_examples.h"
|
||||
#if LV_USE_OBSERVER && LV_USE_ARC && LV_USE_LABEL && LV_USE_BTN && LV_USE_SPINNER && LV_BUILD_EXAMPLES
|
||||
|
||||
typedef enum {
|
||||
FW_UPDATE_STATE_IDLE,
|
||||
FW_UPDATE_STATE_CONNECTING,
|
||||
FW_UPDATE_STATE_CONNECTED,
|
||||
FW_UPDATE_STATE_DOWNLOADING,
|
||||
FW_UPDATE_STATE_CANCEL,
|
||||
FW_UPDATE_STATE_READY,
|
||||
} fw_update_state_t;
|
||||
|
||||
static void fw_upload_manager_observer_cb(lv_subject_t * subject, lv_observer_t * observer);
|
||||
static void fw_update_btn_clicked_event_cb(lv_event_t * e);
|
||||
static void fw_update_close_event_cb(lv_event_t * e);
|
||||
static void fw_update_win_observer_cb(lv_subject_t * subject, lv_observer_t * observer);
|
||||
|
||||
static lv_subject_t fw_download_percent_subject;
|
||||
static lv_subject_t fw_update_status_subject;
|
||||
|
||||
/**
|
||||
* Show how to handle a complete firmware update process with observers.
|
||||
* Normally it's hard to implement a firmware update process because in some cases
|
||||
* - the App needs to was for the UI (wait for a button press)
|
||||
* - the UI needs to wait for the App (connecting or downloading)
|
||||
* With observers these complex mechanisms can be implemented a simple and clean way.
|
||||
*/
|
||||
void lv_example_observer_5(void)
|
||||
{
|
||||
lv_subject_init_int(&fw_download_percent_subject, 0);
|
||||
lv_subject_init_int(&fw_update_status_subject, FW_UPDATE_STATE_IDLE);
|
||||
|
||||
lv_subject_add_observer(&fw_update_status_subject, fw_upload_manager_observer_cb, NULL);
|
||||
|
||||
/*Create start FW update button*/
|
||||
lv_obj_t * btn = lv_btn_create(lv_scr_act());
|
||||
lv_obj_add_event(btn, fw_update_btn_clicked_event_cb, LV_EVENT_CLICKED, NULL);
|
||||
lv_obj_center(btn);
|
||||
lv_obj_t * label = lv_label_create(btn);
|
||||
lv_label_set_text(label, "Firmware update");
|
||||
}
|
||||
|
||||
static void fw_update_btn_clicked_event_cb(lv_event_t * e)
|
||||
{
|
||||
LV_UNUSED(e);
|
||||
lv_obj_t * win = lv_win_create(lv_scr_act());
|
||||
lv_obj_set_size(win, lv_pct(90), lv_pct(90));
|
||||
lv_obj_set_height(lv_win_get_header(win), 40);
|
||||
lv_obj_set_style_radius(win, 8, 0);
|
||||
lv_obj_set_style_shadow_width(win, 24, 0);
|
||||
lv_obj_set_style_shadow_ofs_x(win, 2, 0);
|
||||
lv_obj_set_style_shadow_ofs_y(win, 3, 0);
|
||||
lv_obj_set_style_shadow_color(win, lv_color_hex3(0x888), 0);
|
||||
lv_win_add_title(win, "Firmware update");
|
||||
lv_obj_t * btn = lv_win_add_button(win, LV_SYMBOL_CLOSE, 40);
|
||||
lv_obj_add_event(btn, fw_update_close_event_cb, LV_EVENT_CLICKED, NULL);
|
||||
lv_obj_center(win);
|
||||
|
||||
lv_subject_set_int(&fw_update_status_subject, FW_UPDATE_STATE_IDLE);
|
||||
lv_subject_add_observer_obj(&fw_update_status_subject, fw_update_win_observer_cb, win, NULL);
|
||||
}
|
||||
|
||||
static void fw_update_close_event_cb(lv_event_t * e)
|
||||
{
|
||||
LV_UNUSED(e);
|
||||
lv_subject_set_int(&fw_update_status_subject, FW_UPDATE_STATE_CANCEL);
|
||||
}
|
||||
|
||||
static void restart_btn_click_event_cb(lv_event_t * e)
|
||||
{
|
||||
lv_obj_t * win = lv_event_get_user_data(e);
|
||||
lv_obj_del(win);
|
||||
lv_subject_set_int(&fw_update_status_subject, FW_UPDATE_STATE_IDLE);
|
||||
}
|
||||
|
||||
static void fw_update_win_observer_cb(lv_subject_t * subject, lv_observer_t * observer)
|
||||
{
|
||||
lv_obj_t * win = lv_observer_get_target(observer);
|
||||
lv_obj_t * cont = lv_win_get_content(win);
|
||||
fw_update_state_t status = lv_subject_get_int(&fw_update_status_subject);
|
||||
if(status == FW_UPDATE_STATE_IDLE) {
|
||||
lv_obj_clean(cont);
|
||||
lv_obj_t * spinner = lv_spinner_create(cont);
|
||||
lv_obj_center(spinner);
|
||||
lv_obj_set_size(spinner, 130, 130);
|
||||
|
||||
lv_obj_t * label = lv_label_create(cont);
|
||||
lv_label_set_text(label, "Connecting");
|
||||
lv_obj_center(label);
|
||||
|
||||
lv_subject_set_int(subject, FW_UPDATE_STATE_CONNECTING);
|
||||
}
|
||||
else if(status == FW_UPDATE_STATE_DOWNLOADING) {
|
||||
lv_obj_clean(cont);
|
||||
lv_obj_t * arc = lv_arc_create(cont);
|
||||
lv_arc_bind_value(arc, &fw_download_percent_subject);
|
||||
lv_obj_center(arc);
|
||||
lv_obj_set_size(arc, 130, 130);
|
||||
lv_obj_clear_flag(arc, LV_OBJ_FLAG_CLICKABLE);
|
||||
|
||||
lv_obj_t * label = lv_label_create(cont);
|
||||
lv_label_bind_text(label, &fw_download_percent_subject, "%d %%");
|
||||
lv_obj_center(label);
|
||||
}
|
||||
else if(status == FW_UPDATE_STATE_READY) {
|
||||
lv_obj_clean(cont);
|
||||
lv_obj_t * label = lv_label_create(cont);
|
||||
lv_label_set_text(label, "Firmware update is ready");
|
||||
lv_obj_align(label, LV_ALIGN_CENTER, 0, -20);
|
||||
|
||||
lv_obj_t * btn = lv_button_create(cont);
|
||||
lv_obj_align(btn, LV_ALIGN_CENTER, 0, 20);
|
||||
lv_obj_add_event(btn, restart_btn_click_event_cb, LV_EVENT_CLICKED, win);
|
||||
|
||||
label = lv_label_create(btn);
|
||||
lv_label_set_text(label, "Restart");
|
||||
}
|
||||
else if(status == FW_UPDATE_STATE_CANCEL) {
|
||||
lv_obj_del(win);
|
||||
}
|
||||
}
|
||||
|
||||
static void connect_timer_cb(lv_timer_t * t)
|
||||
{
|
||||
if(lv_subject_get_int(&fw_update_status_subject) != FW_UPDATE_STATE_CANCEL) {
|
||||
lv_subject_set_int(&fw_update_status_subject, FW_UPDATE_STATE_CONNECTED);
|
||||
}
|
||||
lv_timer_del(t);
|
||||
}
|
||||
|
||||
static void download_timer_cb(lv_timer_t * t)
|
||||
{
|
||||
if(lv_subject_get_int(&fw_update_status_subject) == FW_UPDATE_STATE_CANCEL) {
|
||||
lv_timer_del(t);
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t v = lv_subject_get_int(&fw_download_percent_subject);
|
||||
if(v < 100) {
|
||||
lv_subject_set_int(&fw_download_percent_subject, v + 1);
|
||||
}
|
||||
else {
|
||||
lv_subject_set_int(&fw_update_status_subject, FW_UPDATE_STATE_READY);
|
||||
lv_timer_del(t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emulate connection and FW dowlading by timers
|
||||
*/
|
||||
static void fw_upload_manager_observer_cb(lv_subject_t * subject, lv_observer_t * observer)
|
||||
{
|
||||
LV_UNUSED(subject);
|
||||
LV_UNUSED(observer);
|
||||
|
||||
fw_update_state_t state = lv_subject_get_int(&fw_update_status_subject);
|
||||
if(state == FW_UPDATE_STATE_CONNECTING) {
|
||||
lv_timer_create(connect_timer_cb, 2000, NULL);
|
||||
}
|
||||
else if(state == FW_UPDATE_STATE_CONNECTED) {
|
||||
lv_subject_set_int(&fw_download_percent_subject, 0);
|
||||
lv_subject_set_int(&fw_update_status_subject, FW_UPDATE_STATE_DOWNLOADING);
|
||||
lv_timer_create(download_timer_cb, 50, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
192
examples/others/observer/lv_example_observer_6.c
Normal file
192
examples/others/observer/lv_example_observer_6.c
Normal file
@@ -0,0 +1,192 @@
|
||||
#include "../../lv_examples.h"
|
||||
#if LV_USE_OBSERVER && LV_USE_SLIDER && LV_USE_LABEL && LV_BUILD_EXAMPLES
|
||||
|
||||
typedef enum {
|
||||
THEME_MODE_LIGHT,
|
||||
THEME_MODE_DARK,
|
||||
} theme_mode_t;
|
||||
|
||||
|
||||
static lv_obj_t * my_panel_create(lv_obj_t * parent);
|
||||
static lv_obj_t * my_button_create(lv_obj_t * parent, const char * text, lv_event_cb_t event_cb);
|
||||
static void switch_theme_event_cb(lv_event_t * e);
|
||||
|
||||
static lv_subject_t theme_subject;
|
||||
|
||||
/**
|
||||
* Change between light and dark mode
|
||||
*/
|
||||
void lv_example_observer_6(void)
|
||||
{
|
||||
lv_subject_init_int(&theme_subject, THEME_MODE_DARK);
|
||||
|
||||
lv_obj_t * panel1 = my_panel_create(lv_scr_act());
|
||||
lv_obj_set_flex_flow(panel1, LV_FLEX_FLOW_COLUMN);
|
||||
lv_obj_set_flex_align(panel1, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
|
||||
lv_obj_set_size(panel1, lv_pct(90), lv_pct(90));
|
||||
lv_obj_center(panel1);
|
||||
|
||||
my_button_create(panel1, "Button 1", switch_theme_event_cb);
|
||||
my_button_create(panel1, "Button 2", switch_theme_event_cb);
|
||||
my_button_create(panel1, "Button 3", switch_theme_event_cb);
|
||||
my_button_create(panel1, "Button 4", switch_theme_event_cb);
|
||||
my_button_create(panel1, "Button 5", switch_theme_event_cb);
|
||||
my_button_create(panel1, "Button 6", switch_theme_event_cb);
|
||||
my_button_create(panel1, "Button 7", switch_theme_event_cb);
|
||||
my_button_create(panel1, "Button 8", switch_theme_event_cb);
|
||||
my_button_create(panel1, "Button 9", switch_theme_event_cb);
|
||||
my_button_create(panel1, "Button 10", switch_theme_event_cb);
|
||||
}
|
||||
|
||||
static void switch_theme_event_cb(lv_event_t * e)
|
||||
{
|
||||
LV_UNUSED(e);
|
||||
if(lv_subject_get_int(&theme_subject) == THEME_MODE_LIGHT) lv_subject_set_int(&theme_subject, THEME_MODE_DARK);
|
||||
else lv_subject_set_int(&theme_subject, THEME_MODE_LIGHT);
|
||||
}
|
||||
|
||||
|
||||
/*-----------------------------------------
|
||||
* my_panel.c
|
||||
*
|
||||
* It would be a separate file with its own
|
||||
* local types, data, and functions
|
||||
*------------------------------------------*/
|
||||
|
||||
typedef struct {
|
||||
lv_style_t style_main;
|
||||
lv_style_t style_scrollbar;
|
||||
} my_panel_styles_t;
|
||||
|
||||
static void my_panel_style_observer_cb(lv_subject_t * subject, lv_observer_t * observer)
|
||||
{
|
||||
LV_UNUSED(subject);
|
||||
LV_UNUSED(observer);
|
||||
|
||||
theme_mode_t m = lv_subject_get_int(&theme_subject);
|
||||
my_panel_styles_t * styles = lv_observer_get_target(observer);
|
||||
if(m == THEME_MODE_LIGHT) {
|
||||
lv_style_set_bg_color(&styles->style_main, lv_color_hex3(0xfff));
|
||||
lv_style_set_shadow_color(&styles->style_main, lv_color_hex3(0x888));
|
||||
lv_style_set_text_color(&styles->style_main, lv_color_hex3(0x222));
|
||||
lv_style_set_bg_color(&styles->style_scrollbar, lv_color_hex3(0x888));
|
||||
}
|
||||
if(m == THEME_MODE_DARK) {
|
||||
lv_style_set_bg_color(&styles->style_main, lv_color_hex(0x040038));
|
||||
lv_style_set_shadow_color(&styles->style_main, lv_color_hex3(0xaaa));
|
||||
lv_style_set_text_color(&styles->style_main, lv_color_hex3(0xeee));
|
||||
lv_style_set_bg_color(&styles->style_scrollbar, lv_color_hex3(0xaaa));
|
||||
}
|
||||
|
||||
lv_obj_report_style_change(&styles->style_main);
|
||||
lv_obj_report_style_change(&styles->style_scrollbar);
|
||||
}
|
||||
|
||||
static lv_obj_t * my_panel_create(lv_obj_t * parent)
|
||||
{
|
||||
static bool inited = false;
|
||||
static my_panel_styles_t styles;
|
||||
if(!inited) {
|
||||
inited = true;
|
||||
|
||||
lv_style_init(&styles.style_main);
|
||||
lv_style_set_radius(&styles.style_main, 12);
|
||||
lv_style_set_bg_opa(&styles.style_main, LV_OPA_COVER);
|
||||
lv_style_set_shadow_width(&styles.style_main, 24);
|
||||
lv_style_set_shadow_ofs_x(&styles.style_main, 4);
|
||||
lv_style_set_shadow_ofs_y(&styles.style_main, 6);
|
||||
lv_style_set_pad_all(&styles.style_main, 12);
|
||||
lv_style_set_pad_gap(&styles.style_main, 16);
|
||||
|
||||
lv_style_init(&styles.style_scrollbar);
|
||||
lv_style_set_width(&styles.style_scrollbar, 4);
|
||||
lv_style_set_radius(&styles.style_scrollbar, 2);
|
||||
lv_style_set_pad_right(&styles.style_scrollbar, 8);
|
||||
lv_style_set_pad_ver(&styles.style_scrollbar, 8);
|
||||
lv_style_set_bg_opa(&styles.style_scrollbar, LV_OPA_50);
|
||||
|
||||
lv_subject_add_observer_with_target(&theme_subject, my_panel_style_observer_cb, &styles, NULL);
|
||||
}
|
||||
|
||||
lv_obj_t * panel = lv_obj_create(parent);
|
||||
lv_obj_remove_style_all(panel);
|
||||
lv_obj_add_style(panel, &styles.style_main, 0);
|
||||
lv_obj_add_style(panel, &styles.style_scrollbar, LV_PART_SCROLLBAR);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
/*-----------------------------------------
|
||||
* my_button.c
|
||||
*
|
||||
* It would be a separate file with its own
|
||||
* local types, data, and functions
|
||||
*------------------------------------------*/
|
||||
|
||||
typedef struct {
|
||||
lv_style_t style_main;
|
||||
lv_style_t style_pressed;
|
||||
} my_button_styles_t;
|
||||
|
||||
static void my_button_style_observer_cb(lv_subject_t * subject, lv_observer_t * observer)
|
||||
{
|
||||
LV_UNUSED(subject);
|
||||
LV_UNUSED(observer);
|
||||
|
||||
theme_mode_t m = lv_subject_get_int(&theme_subject);
|
||||
my_button_styles_t * styles = lv_observer_get_target(observer);
|
||||
if(m == THEME_MODE_LIGHT) {
|
||||
lv_style_set_bg_color(&styles->style_main, lv_color_hex(0x3379de));
|
||||
lv_style_set_bg_grad_color(&styles->style_main, lv_color_hex(0xd249a5));
|
||||
lv_style_set_shadow_color(&styles->style_main, lv_color_hex(0x3379de));
|
||||
lv_style_set_text_color(&styles->style_main, lv_color_hex3(0xfff));
|
||||
|
||||
lv_style_set_color_filter_opa(&styles->style_pressed, LV_OPA_70);
|
||||
}
|
||||
if(m == THEME_MODE_DARK) {
|
||||
lv_style_set_bg_color(&styles->style_main, lv_color_hex(0xde1382));
|
||||
lv_style_set_bg_grad_color(&styles->style_main, lv_color_hex(0x4b0c72));
|
||||
lv_style_set_shadow_color(&styles->style_main, lv_color_hex(0x4b0c72));
|
||||
lv_style_set_text_color(&styles->style_main, lv_color_hex3(0xfff));
|
||||
|
||||
lv_style_set_color_filter_opa(&styles->style_pressed, LV_OPA_30);
|
||||
}
|
||||
|
||||
lv_obj_report_style_change(&styles->style_main);
|
||||
lv_obj_report_style_change(&styles->style_pressed);
|
||||
}
|
||||
|
||||
static lv_obj_t * my_button_create(lv_obj_t * parent, const char * text, lv_event_cb_t event_cb)
|
||||
{
|
||||
static bool inited = false;
|
||||
static my_button_styles_t styles;
|
||||
if(!inited) {
|
||||
inited = true;
|
||||
|
||||
lv_style_init(&styles.style_main);
|
||||
lv_style_set_radius(&styles.style_main, LV_RADIUS_CIRCLE);
|
||||
lv_style_set_bg_opa(&styles.style_main, LV_OPA_COVER);
|
||||
lv_style_set_bg_grad_dir(&styles.style_main, LV_GRAD_DIR_HOR);
|
||||
lv_style_set_shadow_width(&styles.style_main, 24);
|
||||
lv_style_set_shadow_ofs_y(&styles.style_main, 6);
|
||||
lv_style_set_pad_hor(&styles.style_main, 32);
|
||||
lv_style_set_pad_ver(&styles.style_main, 12);
|
||||
|
||||
lv_style_init(&styles.style_pressed);
|
||||
lv_style_set_color_filter_dsc(&styles.style_pressed, &lv_color_filter_shade);
|
||||
lv_subject_add_observer_with_target(&theme_subject, my_button_style_observer_cb, &styles, NULL);
|
||||
}
|
||||
|
||||
lv_obj_t * btn = lv_btn_create(parent);
|
||||
lv_obj_remove_style_all(btn);
|
||||
lv_obj_add_style(btn, &styles.style_main, 0);
|
||||
lv_obj_add_style(btn, &styles.style_pressed, LV_STATE_PRESSED);
|
||||
lv_obj_add_event(btn, event_cb, LV_EVENT_CLICKED, NULL);
|
||||
|
||||
lv_obj_t * label = lv_label_create(btn);
|
||||
lv_label_set_text(label, text);
|
||||
|
||||
return btn;
|
||||
}
|
||||
|
||||
#endif
|
||||
1
examples/others/observer/lv_example_observer_7.c
Normal file
1
examples/others/observer/lv_example_observer_7.c
Normal file
@@ -0,0 +1 @@
|
||||
typedef int dummy_int_t;
|
||||
@@ -699,6 +699,9 @@
|
||||
/*1: Enable a published subscriber based messaging system */
|
||||
#define LV_USE_MSG 0
|
||||
|
||||
/*1: Enable an observer pattern implementation*/
|
||||
#define LV_USE_OBSERVER 0
|
||||
|
||||
/*1: Enable Pinyin input method*/
|
||||
/*Requires: lv_keyboard*/
|
||||
#define LV_USE_IME_PINYIN 0
|
||||
|
||||
1
lvgl.h
1
lvgl.h
@@ -86,6 +86,7 @@ extern "C" {
|
||||
#include "src/others/fragment/lv_fragment.h"
|
||||
#include "src/others/imgfont/lv_imgfont.h"
|
||||
#include "src/others/msg/lv_msg.h"
|
||||
#include "src/others/observer/lv_observer.h"
|
||||
#include "src/others/ime/lv_ime_pinyin.h"
|
||||
#include "src/others/file_explorer/lv_file_explorer.h"
|
||||
|
||||
|
||||
@@ -124,6 +124,23 @@ bool lv_obj_remove_event(lv_obj_t * obj, uint32_t index)
|
||||
return lv_event_remove(&obj->spec_attr->event_list, index);
|
||||
}
|
||||
|
||||
bool lv_obj_remove_event_cb(lv_obj_t * obj, lv_event_cb_t event_cb)
|
||||
{
|
||||
LV_ASSERT_NULL(obj);
|
||||
|
||||
uint32_t event_cnt = lv_obj_get_event_count(obj);
|
||||
uint32_t i;
|
||||
for(i = 0; i < event_cnt; i++) {
|
||||
lv_event_dsc_t * dsc = lv_obj_get_event_dsc(obj, i);
|
||||
if(dsc->cb == event_cb) {
|
||||
lv_obj_remove_event(obj, i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
lv_obj_t * lv_event_get_current_target_obj(lv_event_t * e)
|
||||
{
|
||||
return lv_event_get_current_target(e);
|
||||
|
||||
@@ -112,6 +112,8 @@ lv_event_dsc_t * lv_obj_get_event_dsc(struct _lv_obj_t * obj, uint32_t index);
|
||||
|
||||
bool lv_obj_remove_event(struct _lv_obj_t * obj, uint32_t index);
|
||||
|
||||
bool lv_obj_remove_event_cb(struct _lv_obj_t * obj, lv_event_cb_t event_cb);
|
||||
|
||||
/**
|
||||
* Get the input device passed as parameter to indev related events.
|
||||
* @param e pointer to an event
|
||||
|
||||
@@ -2283,6 +2283,15 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*1: Enable an observer pattern implementation*/
|
||||
#ifndef LV_USE_OBSERVER
|
||||
#ifdef CONFIG_LV_USE_OBSERVER
|
||||
#define LV_USE_OBSERVER CONFIG_LV_USE_OBSERVER
|
||||
#else
|
||||
#define LV_USE_OBSERVER 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*1: Enable Pinyin input method*/
|
||||
/*Requires: lv_keyboard*/
|
||||
#ifndef LV_USE_IME_PINYIN
|
||||
|
||||
@@ -21,6 +21,13 @@
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
static lv_color_t lv_color_filter_shade_cb(const lv_color_filter_dsc_t * dsc, lv_color_t c, lv_opa_t opa);
|
||||
|
||||
/**********************
|
||||
* GLOBAL VARIABLES
|
||||
**********************/
|
||||
|
||||
const lv_color_filter_dsc_t lv_color_filter_shade = {.filter_cb = lv_color_filter_shade_cb};
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
@@ -261,3 +268,29 @@ lv_color_hsv_t lv_color_to_hsv(lv_color_t c)
|
||||
{
|
||||
return lv_color_rgb_to_hsv(c.red, c.green, c.blue);
|
||||
}
|
||||
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* Helper function to easily create color filters
|
||||
* @param dsc pointer to a color filter descriptor
|
||||
* @param c the color to modify
|
||||
* @param opa the intensity of the modification
|
||||
* - LV_OPA_50: do nothing
|
||||
* - < LV_OPA_50: darken
|
||||
* - LV_OPA_0: fully black
|
||||
* - > LV_OPA_50: lighten
|
||||
* - LV_OPA_100: fully white
|
||||
* @return the modified color
|
||||
*/
|
||||
static lv_color_t lv_color_filter_shade_cb(const lv_color_filter_dsc_t * dsc, lv_color_t c, lv_opa_t opa)
|
||||
{
|
||||
LV_UNUSED(dsc);
|
||||
if(opa == LV_OPA_50) return c;
|
||||
if(opa < LV_OPA_50) return lv_color_lighten(c, (LV_OPA_50 - opa) * 2);
|
||||
else return lv_color_darken(c, (opa - LV_OPA_50 * LV_OPA_50) * 2);
|
||||
}
|
||||
|
||||
|
||||
@@ -295,6 +295,7 @@ static inline lv_color_t lv_color_black(void)
|
||||
return lv_color_make(0x00, 0x00, 0x00);
|
||||
}
|
||||
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
@@ -302,6 +303,8 @@ static inline lv_color_t lv_color_black(void)
|
||||
#include "lv_palette.h"
|
||||
#include "lv_color_op.h"
|
||||
|
||||
extern const lv_color_filter_dsc_t lv_color_filter_shade;
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
643
src/others/observer/lv_observer.c
Normal file
643
src/others/observer/lv_observer.c
Normal file
@@ -0,0 +1,643 @@
|
||||
/**
|
||||
* @file lv_observer.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "lv_observer.h"
|
||||
#if LV_USE_OBSERVER
|
||||
|
||||
#include "../../lvgl.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
typedef struct {
|
||||
uint32_t flag;
|
||||
lv_subject_value_t value;
|
||||
uint32_t inv : 1;
|
||||
} flag_and_cond_t;
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
static void unsubscribe_on_delete_cb(lv_event_t * e);
|
||||
static void group_notify_cb(lv_subject_t * subject, lv_observer_t * observer);
|
||||
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);
|
||||
static void obj_flag_observer_cb(lv_subject_t * subject, lv_observer_t * observer);
|
||||
static void obj_state_observer_cb(lv_subject_t * subject, lv_observer_t * observer);
|
||||
static void btn_value_changed_event_cb(lv_event_t * e);
|
||||
|
||||
static void label_text_observer_cb(lv_subject_t * subject, lv_observer_t * observer);
|
||||
|
||||
static void arc_value_changed_event_cb(lv_event_t * e);
|
||||
static void arc_value_observer_cb(lv_subject_t * subject, lv_observer_t * observer);
|
||||
|
||||
static void slider_value_changed_event_cb(lv_event_t * e);
|
||||
static void slider_value_observer_cb(lv_subject_t * subject, lv_observer_t * observer);
|
||||
|
||||
static void roller_value_changed_event_cb(lv_event_t * e);
|
||||
static void roller_value_observer_cb(lv_subject_t * subject, lv_observer_t * observer);
|
||||
|
||||
static void dropdown_value_changed_event_cb(lv_event_t * e);
|
||||
static void dropdown_value_observer_cb(lv_subject_t * subject, lv_observer_t * observer);
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
void lv_subject_init_int(lv_subject_t * subject, int32_t value)
|
||||
{
|
||||
lv_memzero(subject, sizeof(lv_subject_t));
|
||||
subject->type = LV_SUBJECT_TYPE_INT;
|
||||
subject->value.num = value;
|
||||
subject->prev_value.num = value;
|
||||
_lv_ll_init(&(subject->subs_ll), sizeof(lv_observer_t));
|
||||
}
|
||||
|
||||
|
||||
void lv_subject_set_int(lv_subject_t * subject, int32_t value)
|
||||
{
|
||||
if(subject->type != LV_SUBJECT_TYPE_INT) {
|
||||
LV_LOG_WARN("Subject type is not LV_SUBJECT_TYPE_INT");
|
||||
return;
|
||||
}
|
||||
|
||||
subject->prev_value.num = subject->value.num;
|
||||
subject->value.num = value;
|
||||
lv_subject_notify(subject);
|
||||
}
|
||||
|
||||
int32_t lv_subject_get_int(lv_subject_t * subject)
|
||||
{
|
||||
if(subject->type != LV_SUBJECT_TYPE_INT) {
|
||||
LV_LOG_WARN("Subject type is not LV_SUBJECT_TYPE_INT");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return subject->value.num;
|
||||
}
|
||||
|
||||
int32_t lv_subject_get_previous_int(lv_subject_t * subject)
|
||||
{
|
||||
if(subject->type != LV_SUBJECT_TYPE_INT) {
|
||||
LV_LOG_WARN("Subject type is not LV_SUBJECT_TYPE_INT");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return subject->prev_value.num;
|
||||
}
|
||||
|
||||
void lv_subject_init_string(lv_subject_t * subject, char * buf, char * prev_buf, size_t size, const char * value)
|
||||
{
|
||||
lv_memzero(subject, sizeof(lv_subject_t));
|
||||
lv_strncpy(buf, value, size);
|
||||
if(prev_buf) lv_strncpy(prev_buf, value, size);
|
||||
|
||||
subject->type = LV_SUBJECT_TYPE_STRING;
|
||||
subject->size = size;
|
||||
subject->value.pointer = buf;
|
||||
subject->prev_value.pointer = prev_buf;
|
||||
|
||||
_lv_ll_init(&(subject->subs_ll), sizeof(lv_observer_t));
|
||||
}
|
||||
|
||||
void lv_subject_copy_string(lv_subject_t * subject, const char * buf)
|
||||
{
|
||||
if(subject->type != LV_SUBJECT_TYPE_STRING) {
|
||||
LV_LOG_WARN("Subject type is not LV_SUBJECT_TYPE_INT");
|
||||
return;
|
||||
}
|
||||
|
||||
if(subject->size < 1) return;
|
||||
if(subject->prev_value.pointer) {
|
||||
lv_strncpy((char *)subject->prev_value.pointer, subject->value.pointer, subject->size - 1);
|
||||
}
|
||||
|
||||
lv_strncpy((char *)subject->value.pointer, buf, subject->size - 1);
|
||||
|
||||
lv_subject_notify(subject);
|
||||
|
||||
}
|
||||
|
||||
const char * lv_subject_get_string(lv_subject_t * subject)
|
||||
{
|
||||
if(subject->type != LV_SUBJECT_TYPE_STRING) {
|
||||
LV_LOG_WARN("Subject type is not LV_SUBJECT_TYPE_STRING");
|
||||
return "";
|
||||
}
|
||||
|
||||
return subject->value.pointer;
|
||||
}
|
||||
|
||||
const char * lv_subject_get_previous_string(lv_subject_t * subject)
|
||||
{
|
||||
if(subject->type != LV_SUBJECT_TYPE_STRING) {
|
||||
LV_LOG_WARN("Subject type is not LV_SUBJECT_TYPE_STRING");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return subject->prev_value.pointer;
|
||||
}
|
||||
|
||||
void lv_subject_init_pointer(lv_subject_t * subject, void * value)
|
||||
{
|
||||
lv_memzero(subject, sizeof(lv_subject_t));
|
||||
subject->type = LV_SUBJECT_TYPE_POINTER;
|
||||
subject->value.pointer = value;
|
||||
subject->prev_value.pointer = value;
|
||||
_lv_ll_init(&(subject->subs_ll), sizeof(lv_observer_t));
|
||||
}
|
||||
|
||||
void lv_subject_set_pointer(lv_subject_t * subject, void * ptr)
|
||||
{
|
||||
if(subject->type != LV_SUBJECT_TYPE_POINTER) {
|
||||
LV_LOG_WARN("Subject type is not LV_SUBJECT_TYPE_POINTER");
|
||||
return;
|
||||
}
|
||||
|
||||
subject->prev_value.pointer = subject->value.pointer;
|
||||
subject->value.pointer = ptr;
|
||||
lv_subject_notify(subject);
|
||||
}
|
||||
|
||||
const void * lv_subject_get_pointer(lv_subject_t * subject)
|
||||
{
|
||||
if(subject->type != LV_SUBJECT_TYPE_POINTER) {
|
||||
LV_LOG_WARN("Subject type is not LV_SUBJECT_TYPE_POINTER");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return subject->value.pointer;
|
||||
}
|
||||
|
||||
const void * lv_subject_get_previous_pointer(lv_subject_t * subject)
|
||||
{
|
||||
if(subject->type != LV_SUBJECT_TYPE_POINTER) {
|
||||
LV_LOG_WARN("Subject type is not LV_SUBJECT_TYPE_POINTER");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return subject->prev_value.pointer;
|
||||
}
|
||||
|
||||
void lv_subject_init_color(lv_subject_t * subject, lv_color_t color)
|
||||
{
|
||||
lv_memzero(subject, sizeof(lv_subject_t));
|
||||
subject->type = LV_SUBJECT_TYPE_COLOR;
|
||||
subject->value.color = color;
|
||||
subject->prev_value.color = color;
|
||||
_lv_ll_init(&(subject->subs_ll), sizeof(lv_observer_t));
|
||||
}
|
||||
|
||||
|
||||
void lv_subject_set_color(lv_subject_t * subject, lv_color_t color)
|
||||
{
|
||||
if(subject->type != LV_SUBJECT_TYPE_COLOR) {
|
||||
LV_LOG_WARN("Subject type is not LV_SUBJECT_TYPE_COLOR");
|
||||
return;
|
||||
}
|
||||
|
||||
subject->prev_value.color = subject->value.color;
|
||||
subject->value.color = color;
|
||||
lv_subject_notify(subject);
|
||||
}
|
||||
|
||||
lv_color_t lv_subject_get_color(lv_subject_t * subject)
|
||||
{
|
||||
if(subject->type != LV_SUBJECT_TYPE_COLOR) {
|
||||
LV_LOG_WARN("Subject type is not LV_SUBJECT_TYPE_COLOR");
|
||||
return lv_color_black();
|
||||
}
|
||||
|
||||
return subject->value.color;
|
||||
}
|
||||
|
||||
lv_color_t lv_subject_get_previous_color(lv_subject_t * subject)
|
||||
{
|
||||
if(subject->type != LV_SUBJECT_TYPE_COLOR) {
|
||||
LV_LOG_WARN("Subject type is not LV_SUBJECT_TYPE_COLOR");
|
||||
return lv_color_black();
|
||||
}
|
||||
|
||||
return subject->prev_value.color;
|
||||
}
|
||||
|
||||
void lv_subject_init_group(lv_subject_t * subject, lv_subject_t * list[], uint32_t list_len)
|
||||
{
|
||||
subject->type = LV_SUBJECT_TYPE_GROUP;
|
||||
subject->size = list_len;
|
||||
_lv_ll_init(&(subject->subs_ll), sizeof(lv_observer_t));
|
||||
subject->value.pointer = list;
|
||||
|
||||
/* bind all subjects to this subject */
|
||||
uint32_t i;
|
||||
for(i = 0; i < list_len; i++) {
|
||||
/*If a subject in the group changes notify the group itself*/
|
||||
lv_subject_add_observer(list[i], group_notify_cb, subject);
|
||||
}
|
||||
}
|
||||
|
||||
lv_subject_t * lv_subject_get_group_element(lv_subject_t * subject, int32_t index)
|
||||
{
|
||||
if(subject->type != LV_SUBJECT_TYPE_GROUP) {
|
||||
LV_LOG_WARN("Subject type is not LV_SUBJECT_TYPE_GROUP");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(index >= subject->size) return NULL;
|
||||
|
||||
return ((lv_subject_t **)(subject->value.pointer))[index];
|
||||
}
|
||||
|
||||
|
||||
lv_observer_t * lv_subject_add_observer(lv_subject_t * subject, lv_observer_cb_t cb, void * user_data)
|
||||
{
|
||||
lv_observer_t * observer = lv_subject_add_observer_obj(subject, cb, NULL, user_data);
|
||||
return observer;
|
||||
}
|
||||
|
||||
lv_observer_t * lv_subject_add_observer_obj(lv_subject_t * subject, lv_observer_cb_t cb, lv_obj_t * obj,
|
||||
void * user_data)
|
||||
{
|
||||
lv_observer_t * observer = _lv_ll_ins_tail(&(subject->subs_ll));
|
||||
LV_ASSERT_MALLOC(observer);
|
||||
if(observer == NULL) return NULL;
|
||||
|
||||
lv_memzero(observer, sizeof(*observer));
|
||||
|
||||
observer->subject = subject;
|
||||
observer->cb = cb;
|
||||
observer->user_data = user_data;
|
||||
observer->target = obj;
|
||||
/* subscribe to delete event of the object */
|
||||
if(obj != NULL) {
|
||||
lv_obj_add_event(obj, unsubscribe_on_delete_cb, LV_EVENT_DELETE, observer);
|
||||
}
|
||||
|
||||
/* update object immediately */
|
||||
if(observer->cb) observer->cb(subject, observer);
|
||||
|
||||
return observer;
|
||||
}
|
||||
|
||||
|
||||
lv_observer_t * lv_subject_add_observer_with_target(lv_subject_t * subject, lv_observer_cb_t cb, void * target,
|
||||
void * user_data)
|
||||
{
|
||||
lv_observer_t * observer = _lv_ll_ins_tail(&(subject->subs_ll));
|
||||
LV_ASSERT_MALLOC(observer);
|
||||
if(observer == NULL) return NULL;
|
||||
|
||||
lv_memzero(observer, sizeof(*observer));
|
||||
|
||||
observer->subject = subject;
|
||||
observer->cb = cb;
|
||||
observer->user_data = user_data;
|
||||
observer->target = target;
|
||||
|
||||
/* update object immediately */
|
||||
if(observer->cb) observer->cb(subject, observer);
|
||||
|
||||
return observer;
|
||||
}
|
||||
|
||||
void lv_observer_remove(lv_observer_t * observer)
|
||||
{
|
||||
LV_ASSERT_NULL(observer);
|
||||
|
||||
observer->subject->notify_restart_query = 1;
|
||||
|
||||
_lv_ll_remove(&(observer->subject->subs_ll), observer);
|
||||
|
||||
if(observer->auto_free_user_data) {
|
||||
lv_free(observer->user_data);
|
||||
}
|
||||
lv_free(observer);
|
||||
}
|
||||
|
||||
void lv_subject_remove_all_obj(lv_subject_t * subject, lv_obj_t * obj)
|
||||
{
|
||||
while(lv_obj_remove_event_cb(obj, unsubscribe_on_delete_cb));
|
||||
while(lv_obj_remove_event_cb(obj, btn_value_changed_event_cb));
|
||||
while(lv_obj_remove_event_cb(obj, arc_value_changed_event_cb));
|
||||
while(lv_obj_remove_event_cb(obj, roller_value_changed_event_cb));
|
||||
while(lv_obj_remove_event_cb(obj, dropdown_value_changed_event_cb));
|
||||
|
||||
lv_observer_t * observer = _lv_ll_get_head(&subject->subs_ll);
|
||||
while(observer) {
|
||||
lv_observer_t * observer_next = _lv_ll_get_next(&subject->subs_ll, observer);
|
||||
if(observer->target == obj) {
|
||||
lv_observer_remove(observer);
|
||||
}
|
||||
observer = observer_next;
|
||||
}
|
||||
}
|
||||
|
||||
void * lv_observer_get_target(lv_observer_t * observer)
|
||||
{
|
||||
LV_ASSERT_NULL(observer);
|
||||
|
||||
return observer->target;
|
||||
}
|
||||
|
||||
void lv_subject_notify(lv_subject_t * subject)
|
||||
{
|
||||
LV_ASSERT_NULL(subject);
|
||||
|
||||
lv_observer_t * observer;
|
||||
_LV_LL_READ(&(subject->subs_ll), observer) {
|
||||
observer->notified = 0;
|
||||
}
|
||||
|
||||
do {
|
||||
subject->notify_restart_query = 0;
|
||||
_LV_LL_READ(&(subject->subs_ll), observer) {
|
||||
if(observer->cb && observer->notified == 0) {
|
||||
observer->cb(subject, observer);
|
||||
if(subject->notify_restart_query) break;
|
||||
observer->notified = 1;
|
||||
}
|
||||
}
|
||||
} while(subject->notify_restart_query);
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
return observable;
|
||||
}
|
||||
|
||||
lv_observer_t * lv_obj_bind_flag_if_not_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, true);
|
||||
return observable;
|
||||
}
|
||||
|
||||
lv_observer_t * lv_obj_bind_state_if_eq(lv_obj_t * obj, lv_subject_t * subject, lv_state_t state, int32_t ref_value)
|
||||
{
|
||||
lv_observer_t * observable = bind_to_bitfield(subject, obj, obj_state_observer_cb, state, ref_value, false);
|
||||
return observable;
|
||||
}
|
||||
|
||||
lv_observer_t * lv_obj_bind_state_if_not_eq(lv_obj_t * obj, lv_subject_t * subject, lv_state_t state, int32_t ref_value)
|
||||
{
|
||||
lv_observer_t * observable = bind_to_bitfield(subject, obj, obj_state_observer_cb, state, ref_value, true);
|
||||
return observable;
|
||||
}
|
||||
|
||||
lv_observer_t * lv_button_bind_checked(lv_obj_t * obj, lv_subject_t * subject)
|
||||
{
|
||||
lv_observer_t * observable = bind_to_bitfield(subject, obj, obj_state_observer_cb, LV_STATE_CHECKED, 1, false);
|
||||
lv_obj_add_event(obj, btn_value_changed_event_cb, LV_EVENT_VALUE_CHANGED, subject);
|
||||
return observable;
|
||||
}
|
||||
lv_observer_t * lv_label_bind_text(lv_obj_t * obj, lv_subject_t * subject, const char * fmt)
|
||||
{
|
||||
if(fmt == NULL) {
|
||||
if(subject->type != LV_SUBJECT_TYPE_STRING && subject->type != LV_SUBJECT_TYPE_POINTER) {
|
||||
LV_LOG_WARN("Incompatible subject type: %d", subject->type);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(subject->type != LV_SUBJECT_TYPE_STRING && subject->type != LV_SUBJECT_TYPE_POINTER &&
|
||||
subject->type != LV_SUBJECT_TYPE_INT) {
|
||||
LV_LOG_WARN("Incompatible subject type: %d", subject->type);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
lv_observer_t * observer = lv_subject_add_observer_obj(subject, label_text_observer_cb, obj, (void *)fmt);
|
||||
return observer;
|
||||
}
|
||||
|
||||
lv_observer_t * lv_arc_bind_value(lv_obj_t * obj, lv_subject_t * subject)
|
||||
{
|
||||
if(subject->type != LV_SUBJECT_TYPE_INT) {
|
||||
LV_LOG_WARN("Incompatible subject type: %d", subject->type);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lv_obj_add_event(obj, arc_value_changed_event_cb, LV_EVENT_VALUE_CHANGED, subject);
|
||||
|
||||
lv_observer_t * observer = lv_subject_add_observer_obj(subject, arc_value_observer_cb, obj, NULL);
|
||||
return observer;
|
||||
}
|
||||
|
||||
|
||||
lv_observer_t * lv_slider_bind_value(lv_obj_t * obj, lv_subject_t * subject)
|
||||
{
|
||||
if(subject->type != LV_SUBJECT_TYPE_INT) {
|
||||
LV_LOG_WARN("Incompatible subject type: %d", subject->type);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lv_obj_add_event(obj, slider_value_changed_event_cb, LV_EVENT_VALUE_CHANGED, subject);
|
||||
|
||||
lv_observer_t * observer = lv_subject_add_observer_obj(subject, slider_value_observer_cb, obj, NULL);
|
||||
return observer;
|
||||
}
|
||||
|
||||
lv_observer_t * lv_roller_bind_value(lv_obj_t * obj, lv_subject_t * subject)
|
||||
{
|
||||
if(subject->type != LV_SUBJECT_TYPE_INT) {
|
||||
LV_LOG_WARN("Incompatible subject type: %d", subject->type);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lv_obj_add_event(obj, roller_value_changed_event_cb, LV_EVENT_VALUE_CHANGED, subject);
|
||||
|
||||
lv_observer_t * observer = lv_subject_add_observer_obj(subject, roller_value_observer_cb, obj, NULL);
|
||||
return observer;
|
||||
|
||||
}
|
||||
|
||||
lv_observer_t * lv_dropdown_bind_value(lv_obj_t * obj, lv_subject_t * subject)
|
||||
{
|
||||
if(subject->type != LV_SUBJECT_TYPE_INT) {
|
||||
LV_LOG_WARN("Incompatible subject type: %d", subject->type);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lv_obj_add_event(obj, dropdown_value_changed_event_cb, LV_EVENT_VALUE_CHANGED, subject);
|
||||
|
||||
lv_observer_t * observer = lv_subject_add_observer_obj(subject, dropdown_value_observer_cb, obj, NULL);
|
||||
return observer;
|
||||
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
static void group_notify_cb(lv_subject_t * subject, lv_observer_t * observer)
|
||||
{
|
||||
LV_UNUSED(subject);
|
||||
lv_subject_t * subject_group = observer->user_data;
|
||||
lv_subject_notify(subject_group);
|
||||
}
|
||||
|
||||
static void unsubscribe_on_delete_cb(lv_event_t * e)
|
||||
{
|
||||
lv_observer_t * observer = lv_event_get_user_data(e);
|
||||
lv_observer_remove(observer);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if(subject->type != LV_SUBJECT_TYPE_INT) {
|
||||
LV_LOG_WARN("Incompatible subject type: %d", subject->type);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
flag_and_cond_t * p = lv_malloc(sizeof(flag_and_cond_t));
|
||||
if(p == NULL) {
|
||||
LV_LOG_WARN("Out of memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
p->flag = flag;
|
||||
p->value.num = ref_value;
|
||||
p->inv = inv;
|
||||
|
||||
lv_observer_t * observable = lv_subject_add_observer_obj(subject, cb, obj, p);
|
||||
observable->auto_free_user_data = 1;
|
||||
return observable;
|
||||
}
|
||||
|
||||
static void obj_flag_observer_cb(lv_subject_t * subject, lv_observer_t * observer)
|
||||
{
|
||||
flag_and_cond_t * p = observer->user_data;
|
||||
|
||||
bool res = subject->value.num == p->value.num;
|
||||
if(p->inv) res = !res;
|
||||
|
||||
if(res) {
|
||||
lv_obj_add_flag(observer->target, p->flag);
|
||||
}
|
||||
else {
|
||||
lv_obj_clear_flag(observer->target, p->flag);
|
||||
}
|
||||
}
|
||||
|
||||
static void obj_state_observer_cb(lv_subject_t * subject, lv_observer_t * observer)
|
||||
{
|
||||
flag_and_cond_t * p = observer->user_data;
|
||||
|
||||
bool res = subject->value.num == p->value.num;
|
||||
if(p->inv) res = !res;
|
||||
|
||||
if(res) {
|
||||
lv_obj_add_state(observer->target, p->flag);
|
||||
}
|
||||
else {
|
||||
lv_obj_clear_state(observer->target, p->flag);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void btn_value_changed_event_cb(lv_event_t * e)
|
||||
{
|
||||
lv_obj_t * obj = lv_event_get_current_target(e);
|
||||
lv_subject_t * subject = lv_event_get_user_data(e);
|
||||
|
||||
lv_subject_set_int(subject, lv_obj_has_state(obj, LV_STATE_CHECKED));
|
||||
}
|
||||
|
||||
static void label_text_observer_cb(lv_subject_t * subject, lv_observer_t * observer)
|
||||
{
|
||||
const char * fmt = observer->user_data;
|
||||
|
||||
if(fmt == NULL) {
|
||||
lv_label_set_text(observer->target, subject->value.pointer);
|
||||
}
|
||||
else {
|
||||
switch(subject->type) {
|
||||
case LV_SUBJECT_TYPE_INT:
|
||||
lv_label_set_text_fmt(observer->target, fmt, subject->value.num);
|
||||
break;
|
||||
case LV_SUBJECT_TYPE_STRING:
|
||||
case LV_SUBJECT_TYPE_POINTER:
|
||||
lv_label_set_text_fmt(observer->target, fmt, subject->value.pointer);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void arc_value_changed_event_cb(lv_event_t * e)
|
||||
{
|
||||
lv_obj_t * arc = lv_event_get_current_target(e);
|
||||
lv_subject_t * subject = lv_event_get_user_data(e);
|
||||
|
||||
lv_subject_set_int(subject, lv_arc_get_value(arc));
|
||||
}
|
||||
|
||||
static void arc_value_observer_cb(lv_subject_t * subject, lv_observer_t * observer)
|
||||
{
|
||||
lv_arc_set_value(observer->target, subject->value.num);
|
||||
}
|
||||
|
||||
static void slider_value_changed_event_cb(lv_event_t * e)
|
||||
{
|
||||
lv_obj_t * slider = lv_event_get_current_target(e);
|
||||
lv_subject_t * subject = lv_event_get_user_data(e);
|
||||
|
||||
lv_subject_set_int(subject, lv_slider_get_value(slider));
|
||||
}
|
||||
|
||||
static void slider_value_observer_cb(lv_subject_t * subject, lv_observer_t * observer)
|
||||
{
|
||||
lv_slider_set_value(observer->target, subject->value.num, LV_ANIM_OFF);
|
||||
}
|
||||
|
||||
static void roller_value_changed_event_cb(lv_event_t * e)
|
||||
{
|
||||
lv_obj_t * roller = lv_event_get_current_target(e);
|
||||
lv_subject_t * subject = lv_event_get_user_data(e);
|
||||
|
||||
lv_subject_set_int(subject, lv_roller_get_selected(roller));
|
||||
}
|
||||
|
||||
static void roller_value_observer_cb(lv_subject_t * subject, lv_observer_t * observer)
|
||||
{
|
||||
if((int32_t)lv_roller_get_selected(observer->target) != subject->value.num) {
|
||||
lv_roller_set_selected(observer->target, subject->value.num, LV_ANIM_OFF);
|
||||
}
|
||||
}
|
||||
|
||||
static void dropdown_value_changed_event_cb(lv_event_t * e)
|
||||
{
|
||||
lv_obj_t * dropdown = lv_event_get_current_target(e);
|
||||
lv_subject_t * subject = lv_event_get_user_data(e);
|
||||
|
||||
lv_subject_set_int(subject, lv_dropdown_get_selected(dropdown));
|
||||
}
|
||||
|
||||
static void dropdown_value_observer_cb(lv_subject_t * subject, lv_observer_t * observer)
|
||||
{
|
||||
lv_dropdown_set_selected(observer->target, subject->value.num);
|
||||
}
|
||||
|
||||
#endif /*LV_USE_OBSERVER*/
|
||||
381
src/others/observer/lv_observer.h
Normal file
381
src/others/observer/lv_observer.h
Normal file
@@ -0,0 +1,381 @@
|
||||
/**
|
||||
* @file lv_observer.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_OBSERVER_H
|
||||
#define LV_OBSERVER_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "../../core/lv_obj.h"
|
||||
#if LV_USE_OBSERVER
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
struct _lv_observer_t;
|
||||
|
||||
typedef enum {
|
||||
LV_SUBJECT_TYPE_NONE = 0,
|
||||
LV_SUBJECT_TYPE_INT = 1, /**< an int32_t*/
|
||||
LV_SUBJECT_TYPE_POINTER = 2, /**< a void pointer*/
|
||||
LV_SUBJECT_TYPE_COLOR = 3, /**< an lv_color_t*/
|
||||
LV_SUBJECT_TYPE_GROUP = 4, /**< an array of subjects*/
|
||||
LV_SUBJECT_TYPE_STRING = 5, /**< a char pointer*/
|
||||
} lv_subject_type_t;
|
||||
|
||||
|
||||
/**
|
||||
* A common type to handle all the various observable types in the same way
|
||||
*/
|
||||
typedef union {
|
||||
int32_t num; /**< Integer number (opacity, enums, booleans or "normal" numbers)*/
|
||||
const void * pointer; /**< Constant pointer (string buffer, format string, font, cone text, etc)*/
|
||||
lv_color_t color; /**< Color */
|
||||
} lv_subject_value_t;
|
||||
|
||||
/**
|
||||
* The subject (an observable value)
|
||||
*/
|
||||
typedef struct {
|
||||
lv_ll_t subs_ll; /**< Subscribers*/
|
||||
uint32_t type : 4;
|
||||
uint32_t size : 28; /**< Might be used to store a size related to `type`*/
|
||||
lv_subject_value_t value; /**< Actual value*/
|
||||
lv_subject_value_t prev_value; /**< Previous value*/
|
||||
uint32_t notify_restart_query : 1; /**< If an observer deleted start notifying from the beginning. */
|
||||
} lv_subject_t;
|
||||
|
||||
/**
|
||||
* Callback called when the observed value changes
|
||||
* @param s the lv_observer_t object created when the object was subscribed to the value
|
||||
*/
|
||||
typedef void (*lv_observer_cb_t)(lv_subject_t * subject, struct _lv_observer_t * observer);
|
||||
|
||||
/**
|
||||
* The observer object: a descriptor returned when subscribing LVGL widgets to subjects
|
||||
*/
|
||||
typedef struct _lv_observer_t {
|
||||
lv_subject_t * subject; /**< The observed value */
|
||||
lv_observer_cb_t cb; /**< Callback that should be called when the value changes*/
|
||||
void * target; /**< A target for the observer, e.g. a widget or style*/
|
||||
void * user_data; /**< Additional parameter supplied when subscribing*/
|
||||
uint32_t auto_free_user_data : 1; /**< Automatically free user data when the observer is removed */
|
||||
uint32_t notified : 1; /**< Mark if this observer was already notified*/
|
||||
} lv_observer_t;
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* Initialize an integer type subject
|
||||
* @param subject pointer to the subject
|
||||
* @param value initial value
|
||||
*/
|
||||
void lv_subject_init_int(lv_subject_t * subject, int32_t value);
|
||||
|
||||
/**
|
||||
* Set the value of an integer subject. It will notify all the observers as well.
|
||||
* @param subject pointer to the subject
|
||||
* @param value the new value
|
||||
*/
|
||||
void lv_subject_set_int(lv_subject_t * subject, int32_t value);
|
||||
|
||||
/**
|
||||
* Get the current value of an integer subject
|
||||
* @param subject pointer to the subject
|
||||
* @return the current value
|
||||
*/
|
||||
int32_t lv_subject_get_int(lv_subject_t * subject);
|
||||
|
||||
/**
|
||||
* Get the previous value of an integer subject
|
||||
* @param subject pointer to the subject
|
||||
* @return the current value
|
||||
*/
|
||||
int32_t lv_subject_get_previous_int(lv_subject_t * subject);
|
||||
|
||||
/**
|
||||
* Initialize a string type subject
|
||||
* @param subject pointer to the subject
|
||||
* @param buf pointer to a buffer to store the string
|
||||
* @param prev_buf pointer to a buffer to store the previous string, can be NULL if not used
|
||||
* @param size size of the buffer
|
||||
* @param value initial value as a string, e.g. "hello"
|
||||
* @note the string subject stores the whole string, not only a pointer
|
||||
*/
|
||||
void lv_subject_init_string(lv_subject_t * subject, char * buf, char * prev_buf, size_t size, const char * value);
|
||||
|
||||
/**
|
||||
* Copy a string to a subject. It will notify all the observers as well.
|
||||
* @param subject pointer to the subject
|
||||
* @param buf the new string
|
||||
*/
|
||||
void lv_subject_copy_string(lv_subject_t * subject, const char * buf);
|
||||
|
||||
/**
|
||||
* Get the current value of an string subject
|
||||
* @param subject pointer to the subject
|
||||
* @return pointer to the buffer containing the current value
|
||||
*/
|
||||
const char * lv_subject_get_string(lv_subject_t * subject);
|
||||
|
||||
/**
|
||||
* Get the previous value of an string subject
|
||||
* @param subject pointer to the subject
|
||||
* @return pointer to the buffer containing the current value
|
||||
* @note NULL will be returned if NULL was passed in `lv_subject_init_string()`
|
||||
* as `prev_buf`
|
||||
*/
|
||||
const char * lv_subject_get_previous_string(lv_subject_t * subject);
|
||||
|
||||
/**
|
||||
* Initialize an pointer type subject
|
||||
* @param subject pointer to the subject
|
||||
* @param value initial value
|
||||
*/
|
||||
void lv_subject_init_pointer(lv_subject_t * subject, void * value);
|
||||
|
||||
/**
|
||||
* Set the value of a pointer subject. It will notify all the observers as well.
|
||||
* @param subject pointer to the subject
|
||||
* @param value the new value
|
||||
*/
|
||||
void lv_subject_set_pointer(lv_subject_t * subject, void * ptr);
|
||||
|
||||
/**
|
||||
* Get the current value of a pointer subject
|
||||
* @param subject pointer to the subject
|
||||
* @return the current value
|
||||
*/
|
||||
const void * lv_subject_get_pointer(lv_subject_t * subject);
|
||||
|
||||
/**
|
||||
* Get the previous value of a pointer subject
|
||||
* @param subject pointer to the subject
|
||||
* @return the current value
|
||||
*/
|
||||
const void * lv_subject_get_previous_pointer(lv_subject_t * subject);
|
||||
|
||||
/**
|
||||
* Initialize an color type subject
|
||||
* @param subject pointer to the subject
|
||||
* @param value initial value
|
||||
*/
|
||||
void lv_subject_init_color(lv_subject_t * subject, lv_color_t color);
|
||||
|
||||
/**
|
||||
* Set the value of a color subject. It will notify all the observers as well.
|
||||
* @param subject pointer to the subject
|
||||
* @param value the new value
|
||||
*/
|
||||
void lv_subject_set_color(lv_subject_t * subject, lv_color_t color);
|
||||
|
||||
/**
|
||||
* Get the current value of a color subject
|
||||
* @param subject pointer to the subject
|
||||
* @return the current value
|
||||
*/
|
||||
lv_color_t lv_subject_get_color(lv_subject_t * subject);
|
||||
|
||||
/**
|
||||
* Get the previous value of a color subject
|
||||
* @param subject pointer to the subject
|
||||
* @return the current value
|
||||
*/
|
||||
lv_color_t lv_subject_get_previous_color(lv_subject_t * subject);
|
||||
|
||||
/**
|
||||
* Initialize a subject group
|
||||
* @param subject pointer to the subject
|
||||
* @param list list of other subject addresses, any of these changes `subject` will be notified
|
||||
* @param list_len number of elements in `list`
|
||||
*/
|
||||
void lv_subject_init_group(lv_subject_t * subject, lv_subject_t * list[], uint32_t list_len);
|
||||
|
||||
/**
|
||||
* Get an element from the subject group's list
|
||||
* @param subject pointer to the subject
|
||||
* @param index index of the element to get
|
||||
* @return pointer a subject from the list, or NULL if the index is out of bounds
|
||||
*/
|
||||
lv_subject_t * lv_subject_get_group_element(lv_subject_t * subject, int32_t index);
|
||||
|
||||
/**
|
||||
* Add an observer to a subject. When the subject changes `observer_cb` will be called.
|
||||
* @param subject pointer to the subject
|
||||
* @param observer_cb the callback to call
|
||||
* @param user_data optional user data
|
||||
* @return pointer to the created observer
|
||||
*/
|
||||
lv_observer_t * lv_subject_add_observer(lv_subject_t * subject, lv_observer_cb_t observer_cb, void * user_data);
|
||||
|
||||
/**
|
||||
* Add an observer to a subject for an object.
|
||||
* When the object is deleted, it will be removed from the subject automatically.
|
||||
* @param subject pointer to the subject
|
||||
* @param observer_cb the callback to call
|
||||
* @param obj pointer to an object
|
||||
* @param user_data optional user data
|
||||
* @return pointer to the created observer
|
||||
*/
|
||||
lv_observer_t * lv_subject_add_observer_obj(lv_subject_t * subject, lv_observer_cb_t observer_cb, lv_obj_t * obj,
|
||||
void * user_data);
|
||||
|
||||
|
||||
/**
|
||||
* Add an observer to a subject and also save a target.
|
||||
* @param subject pointer to the subject
|
||||
* @param observer_cb the callback to call
|
||||
* @param target pointer to any data
|
||||
* @param user_data optional user data
|
||||
* @return pointer to the created observer
|
||||
*/
|
||||
lv_observer_t * lv_subject_add_observer_with_target(lv_subject_t * subject, lv_observer_cb_t cb, void * target,
|
||||
void * user_data);
|
||||
|
||||
/**
|
||||
* Remove an observer from its subject
|
||||
* @param observer pointer to an observer
|
||||
*/
|
||||
void lv_observer_remove(lv_observer_t * observer);
|
||||
|
||||
/**
|
||||
* Remove all observers from their subject related to an object
|
||||
* @param observer pointer to an observer
|
||||
* @param obj pointer to an object
|
||||
*/
|
||||
void lv_subject_remove_all_obj(lv_subject_t * subject, lv_obj_t * obj);
|
||||
|
||||
/**
|
||||
* Get the target of an observer
|
||||
* @param observer pointer to an observer
|
||||
* @return pointer to the saved target
|
||||
*/
|
||||
void * lv_observer_get_target(lv_observer_t * observer);
|
||||
|
||||
/**
|
||||
* Notify all observers of subject
|
||||
* @param subject pointer to a subject
|
||||
*/
|
||||
void lv_subject_notify(lv_subject_t * subject);
|
||||
|
||||
/**
|
||||
* Set an object flag if an integer subject's value is equal to a reference value, clear the flag otherwise
|
||||
* @param obj pointer to an object
|
||||
* @param subject pointer to a subject
|
||||
* @param flag a flag to set or clear (e.g. `LV_OBJ_FLAG_HIDDEN`)
|
||||
* @param ref_value a reference value to compare the subject's value with
|
||||
* @return pointer to the created observer
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* Set an object flag if an integer subject's value is not equal to a reference value, clear the flag otherwise
|
||||
* @param obj pointer to an object
|
||||
* @param subject pointer to a subject
|
||||
* @param flag a flag to set or clear (e.g. `LV_OBJ_FLAG_HIDDEN`)
|
||||
* @param ref_value a reference value to compare the subject's value with
|
||||
* @return pointer to the created observer
|
||||
*/
|
||||
lv_observer_t * lv_obj_bind_flag_if_not_eq(lv_obj_t * obj, lv_subject_t * subject, lv_obj_flag_t flag,
|
||||
int32_t ref_value);
|
||||
|
||||
/**
|
||||
* Set an object state if an integer subject's value is equal to a reference value, clear the flag otherwise
|
||||
* @param obj pointer to an object
|
||||
* @param subject pointer to a subject
|
||||
* @param flag a state to set or clear (e.g. `LV_STATE_CHECKED`)
|
||||
* @param ref_value a reference value to compare the subject's value with
|
||||
* @return pointer to the created observer
|
||||
*/
|
||||
lv_observer_t * lv_obj_bind_state_if_eq(lv_obj_t * obj, lv_subject_t * subject, lv_state_t state, int32_t ref_value);
|
||||
|
||||
/**
|
||||
* Set an object state if an integer subject's value is not equal to a reference value, clear the flag otherwise
|
||||
* @param obj pointer to an object
|
||||
* @param subject pointer to a subject
|
||||
* @param flag a state to set or clear (e.g. `LV_STATE_CHECKED`)
|
||||
* @param ref_value a reference value to compare the subject's value with
|
||||
* @return pointer to the created observer
|
||||
*/
|
||||
lv_observer_t * lv_obj_bind_state_if_not_eq(lv_obj_t * obj, lv_subject_t * subject, lv_state_t state,
|
||||
int32_t ref_value);
|
||||
|
||||
/**
|
||||
* Set an integer subject to 1 when a button is checked and set it 0 when unchecked.
|
||||
* @param obj pointer to a button
|
||||
* @param subject pointer to a subject
|
||||
* @return pointer to the created observer
|
||||
*/
|
||||
lv_observer_t * lv_button_bind_checked(lv_obj_t * obj, lv_subject_t * subject);
|
||||
|
||||
/**
|
||||
* Bind an integer, string, or pointer subject to a label.
|
||||
* @param obj pointer to a label
|
||||
* @param subject pointer to a subject
|
||||
* @param fmt an optional format string with 1 format specifier (e.g. "%d °C")
|
||||
* or NULL to bind the value directly.
|
||||
* @return pointer to the created observer
|
||||
* @note fmt == NULL can be used only with string and pointer subjects.
|
||||
* @note if the subject is a pointer must point to a `\0` terminated string.
|
||||
*/
|
||||
lv_observer_t * lv_label_bind_text(lv_obj_t * obj, lv_subject_t * subject, const char * fmt);
|
||||
|
||||
/**
|
||||
* Bind an integer subject to an arc's value
|
||||
* @param obj pointer to an arc
|
||||
* @param subject pointer to a subject
|
||||
* @return pointer to the created observer
|
||||
*/
|
||||
lv_observer_t * lv_arc_bind_value(lv_obj_t * obj, lv_subject_t * subject);
|
||||
|
||||
/**
|
||||
* Bind an integer subject to a slider's value
|
||||
* @param obj pointer to a slider
|
||||
* @param subject pointer to a subject
|
||||
* @return pointer to the created observer
|
||||
*/
|
||||
lv_observer_t * lv_slider_bind_value(lv_obj_t * obj, lv_subject_t * subject);
|
||||
|
||||
/**
|
||||
* Bind an integer subject to a roller's value
|
||||
* @param obj pointer to a roller
|
||||
* @param subject pointer to a subject
|
||||
* @return pointer to the created observer
|
||||
*/
|
||||
lv_observer_t * lv_roller_bind_value(lv_obj_t * obj, lv_subject_t * subject);
|
||||
|
||||
/**
|
||||
* Bind an integer subject to a dropdown's value
|
||||
* @param obj pointer to a drop down
|
||||
* @param subject pointer to a subject
|
||||
* @return pointer to the created observer
|
||||
*/
|
||||
lv_observer_t * lv_dropdown_bind_value(lv_obj_t * obj, lv_subject_t * subject);
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#endif /*LV_USE_OBSERVER*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_OBSERVER_H*/
|
||||
@@ -1005,6 +1005,7 @@ static void theme_apply(lv_theme_t * th, lv_obj_t * obj)
|
||||
lv_obj_add_style(obj, &theme->styles.outline_primary, LV_STATE_FOCUS_KEY);
|
||||
lv_obj_add_style(obj, &theme->styles.outline_secondary, LV_STATE_EDITED);
|
||||
lv_obj_add_style(obj, &theme->styles.transition_normal, LV_PART_INDICATOR);
|
||||
lv_obj_add_style(obj, &theme->styles.disabled, LV_STATE_DISABLED);
|
||||
}
|
||||
else if(lv_obj_check_type(obj, &lv_dropdownlist_class)) {
|
||||
lv_obj_add_style(obj, &theme->styles.card, 0);
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
#define LV_USE_IMGFONT 1
|
||||
#define LV_USE_IME_PINYIN 1
|
||||
#define LV_USE_MSG 1
|
||||
#define LV_USE_OBSERVER 1
|
||||
#define LV_USE_FILE_EXPLORER 1
|
||||
#define LV_USE_TINY_TTF 1
|
||||
#define LV_USE_SYSMON 1
|
||||
|
||||
499
tests/src/test_cases/test_observer.c
Normal file
499
tests/src/test_cases/test_observer.c
Normal file
@@ -0,0 +1,499 @@
|
||||
#if LV_BUILD_TEST == 1
|
||||
#include "../lvgl.h"
|
||||
|
||||
#include "unity/unity.h"
|
||||
#include "lv_test_indev.h"
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
/* Function run before every test */
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
/* Function run after every test */
|
||||
lv_obj_clean(lv_scr_act());
|
||||
}
|
||||
|
||||
static int32_t prev_v;
|
||||
static int32_t current_v;
|
||||
|
||||
static void observer_int(lv_subject_t * subject, lv_observer_t * observer)
|
||||
{
|
||||
LV_UNUSED(observer);
|
||||
prev_v = lv_subject_get_previous_int(subject);
|
||||
current_v = lv_subject_get_int(subject);
|
||||
}
|
||||
|
||||
|
||||
void test_observer_add_remove(void)
|
||||
{
|
||||
static lv_subject_t subject;
|
||||
lv_subject_init_int(&subject, 5);
|
||||
|
||||
lv_observer_t * observer = lv_subject_add_observer(&subject, observer_int, NULL);
|
||||
|
||||
current_v = 0;
|
||||
lv_subject_set_int(&subject, 10);
|
||||
TEST_ASSERT_EQUAL(10, lv_subject_get_int(&subject));
|
||||
TEST_ASSERT_EQUAL(10, current_v);
|
||||
|
||||
lv_observer_remove(observer);
|
||||
lv_subject_set_int(&subject, 15);
|
||||
TEST_ASSERT_EQUAL(15, lv_subject_get_int(&subject));
|
||||
TEST_ASSERT_EQUAL(10, current_v); /*The observer cb is not called*/
|
||||
}
|
||||
|
||||
void test_observer_int(void)
|
||||
{
|
||||
static lv_subject_t subject;
|
||||
lv_subject_init_int(&subject, 5);
|
||||
TEST_ASSERT_EQUAL(5, lv_subject_get_int(&subject));
|
||||
TEST_ASSERT_EQUAL(5, lv_subject_get_previous_int(&subject));
|
||||
|
||||
lv_subject_set_int(&subject, 10);
|
||||
TEST_ASSERT_EQUAL(10, lv_subject_get_int(&subject));
|
||||
TEST_ASSERT_EQUAL(5, lv_subject_get_previous_int(&subject));
|
||||
|
||||
lv_subject_set_int(&subject, 15);
|
||||
TEST_ASSERT_EQUAL(15, lv_subject_get_int(&subject));
|
||||
TEST_ASSERT_EQUAL(10, lv_subject_get_previous_int(&subject));
|
||||
|
||||
/*Ignore incorrect types*/
|
||||
lv_subject_set_pointer(&subject, NULL);
|
||||
TEST_ASSERT_EQUAL(15, lv_subject_get_int(&subject));
|
||||
TEST_ASSERT_EQUAL(10, lv_subject_get_previous_int(&subject));
|
||||
|
||||
lv_subject_set_color(&subject, lv_color_black());
|
||||
TEST_ASSERT_EQUAL(15, lv_subject_get_int(&subject));
|
||||
TEST_ASSERT_EQUAL(10, lv_subject_get_previous_int(&subject));
|
||||
|
||||
lv_subject_copy_string(&subject, "hello");
|
||||
TEST_ASSERT_EQUAL(15, lv_subject_get_int(&subject));
|
||||
TEST_ASSERT_EQUAL(10, lv_subject_get_previous_int(&subject));
|
||||
}
|
||||
|
||||
void test_observer_string(void)
|
||||
{
|
||||
char buf_current[32];
|
||||
char buf_previous[32];
|
||||
lv_subject_t subject;
|
||||
lv_subject_init_string(&subject, buf_current, buf_previous, sizeof(buf_current), "hello");
|
||||
TEST_ASSERT_EQUAL_STRING("hello", lv_subject_get_string(&subject));
|
||||
TEST_ASSERT_EQUAL_STRING("hello", lv_subject_get_previous_string(&subject));
|
||||
|
||||
lv_subject_copy_string(&subject, "my name is John");
|
||||
TEST_ASSERT_EQUAL_STRING("my name is John", lv_subject_get_string(&subject));
|
||||
TEST_ASSERT_EQUAL_STRING("hello", lv_subject_get_previous_string(&subject));
|
||||
|
||||
lv_subject_copy_string(&subject, "how are you?");
|
||||
TEST_ASSERT_EQUAL_STRING("how are you?", lv_subject_get_string(&subject));
|
||||
TEST_ASSERT_EQUAL_STRING("my name is John", lv_subject_get_previous_string(&subject));
|
||||
|
||||
/*Clip long text*/
|
||||
lv_subject_copy_string(&subject, "text to be clipped to 32 chars.this should be clipped");
|
||||
TEST_ASSERT_EQUAL_STRING("text to be clipped to 32 chars", lv_subject_get_string(&subject));
|
||||
TEST_ASSERT_EQUAL_STRING("how are you?", lv_subject_get_previous_string(&subject));
|
||||
|
||||
/*Check if the previous string is clipped correctly*/
|
||||
lv_subject_copy_string(&subject, "a");
|
||||
TEST_ASSERT_EQUAL_STRING("a", lv_subject_get_string(&subject));
|
||||
TEST_ASSERT_EQUAL_STRING("text to be clipped to 32 chars", lv_subject_get_previous_string(&subject));
|
||||
|
||||
/*Ignore incorrect types*/
|
||||
lv_subject_set_pointer(&subject, NULL);
|
||||
TEST_ASSERT_EQUAL_STRING("a", lv_subject_get_string(&subject));
|
||||
TEST_ASSERT_EQUAL_STRING("text to be clipped to 32 chars", lv_subject_get_previous_string(&subject));
|
||||
|
||||
lv_subject_set_color(&subject, lv_color_black());
|
||||
TEST_ASSERT_EQUAL_STRING("a", lv_subject_get_string(&subject));
|
||||
TEST_ASSERT_EQUAL_STRING("text to be clipped to 32 chars", lv_subject_get_previous_string(&subject));
|
||||
|
||||
lv_subject_set_int(&subject, 10);
|
||||
TEST_ASSERT_EQUAL_STRING("a", lv_subject_get_string(&subject));
|
||||
TEST_ASSERT_EQUAL_STRING("text to be clipped to 32 chars", lv_subject_get_previous_string(&subject));
|
||||
}
|
||||
|
||||
void test_observer_pointer(void)
|
||||
{
|
||||
static int32_t a[3] = {0};
|
||||
static lv_subject_t subject;
|
||||
|
||||
lv_subject_init_pointer(&subject, &a[0]);
|
||||
TEST_ASSERT_EQUAL_PTR(&a[0], lv_subject_get_pointer(&subject));
|
||||
TEST_ASSERT_EQUAL_PTR(&a[0], lv_subject_get_previous_pointer(&subject));
|
||||
|
||||
lv_subject_set_pointer(&subject, &a[1]);
|
||||
TEST_ASSERT_EQUAL_PTR(&a[1], lv_subject_get_pointer(&subject));
|
||||
TEST_ASSERT_EQUAL_PTR(&a[0], lv_subject_get_previous_pointer(&subject));
|
||||
|
||||
lv_subject_set_pointer(&subject, &a[2]);
|
||||
TEST_ASSERT_EQUAL_PTR(&a[2], lv_subject_get_pointer(&subject));
|
||||
TEST_ASSERT_EQUAL_PTR(&a[1], lv_subject_get_previous_pointer(&subject));
|
||||
|
||||
/*Ignore incorrect types*/
|
||||
lv_subject_set_int(&subject, 10);
|
||||
TEST_ASSERT_EQUAL_PTR(&a[2], lv_subject_get_pointer(&subject));
|
||||
TEST_ASSERT_EQUAL_PTR(&a[1], lv_subject_get_previous_pointer(&subject));
|
||||
|
||||
lv_subject_set_color(&subject, lv_color_black());
|
||||
TEST_ASSERT_EQUAL_PTR(&a[2], lv_subject_get_pointer(&subject));
|
||||
TEST_ASSERT_EQUAL_PTR(&a[1], lv_subject_get_previous_pointer(&subject));
|
||||
|
||||
lv_subject_copy_string(&subject, "hello");
|
||||
TEST_ASSERT_EQUAL_PTR(&a[2], lv_subject_get_pointer(&subject));
|
||||
TEST_ASSERT_EQUAL_PTR(&a[1], lv_subject_get_previous_pointer(&subject));
|
||||
}
|
||||
|
||||
void test_observer_color(void)
|
||||
{
|
||||
static lv_subject_t subject;
|
||||
|
||||
lv_subject_init_color(&subject, lv_color_hex3(0x123));
|
||||
TEST_ASSERT_EQUAL_COLOR(lv_color_hex3(0x123), lv_subject_get_color(&subject));
|
||||
TEST_ASSERT_EQUAL_COLOR(lv_color_hex3(0x123), lv_subject_get_previous_color(&subject));
|
||||
|
||||
lv_subject_set_color(&subject, lv_color_hex3(0x456));
|
||||
TEST_ASSERT_EQUAL_COLOR(lv_color_hex3(0x456), lv_subject_get_color(&subject));
|
||||
TEST_ASSERT_EQUAL_COLOR(lv_color_hex3(0x123), lv_subject_get_previous_color(&subject));
|
||||
|
||||
lv_subject_set_color(&subject, lv_color_hex3(0xabc));
|
||||
TEST_ASSERT_EQUAL_COLOR(lv_color_hex3(0xabc), lv_subject_get_color(&subject));
|
||||
TEST_ASSERT_EQUAL_COLOR(lv_color_hex3(0x456), lv_subject_get_previous_color(&subject));
|
||||
|
||||
/*Ignore incorrect types*/
|
||||
lv_subject_set_pointer(&subject, NULL);
|
||||
TEST_ASSERT_EQUAL_COLOR(lv_color_hex3(0xabc), lv_subject_get_color(&subject));
|
||||
TEST_ASSERT_EQUAL_COLOR(lv_color_hex3(0x456), lv_subject_get_previous_color(&subject));
|
||||
|
||||
lv_subject_set_int(&subject, 10);
|
||||
TEST_ASSERT_EQUAL_COLOR(lv_color_hex3(0xabc), lv_subject_get_color(&subject));
|
||||
TEST_ASSERT_EQUAL_COLOR(lv_color_hex3(0x456), lv_subject_get_previous_color(&subject));
|
||||
|
||||
lv_subject_copy_string(&subject, "hello");
|
||||
TEST_ASSERT_EQUAL_COLOR(lv_color_hex3(0xabc), lv_subject_get_color(&subject));
|
||||
TEST_ASSERT_EQUAL_COLOR(lv_color_hex3(0x456), lv_subject_get_previous_color(&subject));
|
||||
}
|
||||
|
||||
static int32_t group_observer_called;
|
||||
|
||||
static void group_observer_cb(lv_subject_t * subject, lv_observer_t * observer)
|
||||
{
|
||||
LV_UNUSED(observer);
|
||||
LV_UNUSED(subject);
|
||||
group_observer_called++;
|
||||
}
|
||||
|
||||
void test_observer_group(void)
|
||||
{
|
||||
static lv_subject_t subject_main;
|
||||
static lv_subject_t subject_sub1;
|
||||
static lv_subject_t subject_sub2;
|
||||
static lv_subject_t * subject_list[2] = {&subject_sub1, &subject_sub2};
|
||||
|
||||
lv_subject_init_int(&subject_sub1, 1);
|
||||
lv_subject_init_int(&subject_sub2, 2);
|
||||
|
||||
lv_subject_init_group(&subject_main, subject_list, 2);
|
||||
TEST_ASSERT_EQUAL_PTR(&subject_sub1, lv_subject_get_group_element(&subject_main, 0));
|
||||
TEST_ASSERT_EQUAL_PTR(&subject_sub2, lv_subject_get_group_element(&subject_main, 1));
|
||||
TEST_ASSERT_EQUAL_PTR(NULL, lv_subject_get_group_element(&subject_main, 2));
|
||||
TEST_ASSERT_EQUAL_PTR(NULL, lv_subject_get_group_element(&subject_main, 1000));
|
||||
|
||||
group_observer_called = 0;
|
||||
lv_subject_add_observer(&subject_main, group_observer_cb, NULL);
|
||||
TEST_ASSERT_EQUAL(1, group_observer_called);
|
||||
|
||||
lv_subject_set_int(&subject_sub1, 10);
|
||||
TEST_ASSERT_EQUAL(2, group_observer_called);
|
||||
|
||||
lv_subject_set_int(&subject_sub2, 20);
|
||||
TEST_ASSERT_EQUAL(3, group_observer_called);
|
||||
}
|
||||
|
||||
void test_observer_obj_flag(void)
|
||||
{
|
||||
lv_obj_t * obj = lv_obj_create(lv_scr_act());
|
||||
|
||||
/*Can bind only to int*/
|
||||
static lv_subject_t subject_wrong;
|
||||
lv_subject_init_pointer(&subject_wrong, NULL);
|
||||
lv_observer_t * observer = lv_obj_bind_state_if_eq(obj, &subject_wrong, LV_STATE_CHECKED, 5);
|
||||
TEST_ASSERT_EQUAL_PTR(NULL, observer);
|
||||
|
||||
static lv_subject_t subject;
|
||||
lv_subject_init_int(&subject, 1);
|
||||
|
||||
lv_obj_bind_flag_if_eq(obj, &subject, LV_OBJ_FLAG_HIDDEN, 5);
|
||||
/*Should be applied immediately*/
|
||||
TEST_ASSERT_EQUAL(false, lv_obj_has_flag(obj, LV_OBJ_FLAG_HIDDEN));
|
||||
|
||||
lv_obj_bind_flag_if_not_eq(obj, &subject, LV_OBJ_FLAG_CHECKABLE, 10);
|
||||
/*Should be applied immediately*/
|
||||
TEST_ASSERT_EQUAL(false, lv_obj_has_flag(obj, LV_OBJ_FLAG_HIDDEN));
|
||||
TEST_ASSERT_EQUAL(true, lv_obj_has_flag(obj, LV_OBJ_FLAG_CHECKABLE));
|
||||
|
||||
lv_subject_set_int(&subject, 5);
|
||||
TEST_ASSERT_EQUAL(true, lv_obj_has_flag(obj, LV_OBJ_FLAG_HIDDEN));
|
||||
TEST_ASSERT_EQUAL(true, lv_obj_has_flag(obj, LV_OBJ_FLAG_CHECKABLE));
|
||||
|
||||
lv_subject_set_int(&subject, 10);
|
||||
TEST_ASSERT_EQUAL(false, lv_obj_has_flag(obj, LV_OBJ_FLAG_HIDDEN));
|
||||
TEST_ASSERT_EQUAL(false, lv_obj_has_flag(obj, LV_OBJ_FLAG_CHECKABLE));
|
||||
}
|
||||
|
||||
void test_observer_obj_state(void)
|
||||
{
|
||||
lv_obj_t * obj = lv_obj_create(lv_scr_act());
|
||||
|
||||
/*Can bind only to int*/
|
||||
static lv_subject_t subject_wrong;
|
||||
lv_subject_init_pointer(&subject_wrong, NULL);
|
||||
lv_observer_t * observer = lv_obj_bind_state_if_eq(obj, &subject_wrong, LV_STATE_CHECKED, 5);
|
||||
TEST_ASSERT_EQUAL_PTR(NULL, observer);
|
||||
|
||||
static lv_subject_t subject;
|
||||
lv_subject_init_int(&subject, 1);
|
||||
|
||||
lv_obj_bind_state_if_eq(obj, &subject, LV_STATE_CHECKED, 5);
|
||||
/*Should be applied immediately*/
|
||||
TEST_ASSERT_EQUAL(false, lv_obj_has_flag(obj, LV_STATE_CHECKED));
|
||||
|
||||
lv_obj_bind_state_if_not_eq(obj, &subject, LV_STATE_DISABLED, 10);
|
||||
/*Should be applied immediately*/
|
||||
TEST_ASSERT_EQUAL(false, lv_obj_has_state(obj, LV_STATE_CHECKED));
|
||||
TEST_ASSERT_EQUAL(true, lv_obj_has_state(obj, LV_STATE_DISABLED));
|
||||
|
||||
lv_subject_set_int(&subject, 5);
|
||||
TEST_ASSERT_EQUAL(true, lv_obj_has_state(obj, LV_STATE_CHECKED));
|
||||
TEST_ASSERT_EQUAL(true, lv_obj_has_state(obj, LV_STATE_DISABLED));
|
||||
|
||||
lv_subject_set_int(&subject, 10);
|
||||
TEST_ASSERT_EQUAL(false, lv_obj_has_state(obj, LV_STATE_CHECKED));
|
||||
TEST_ASSERT_EQUAL(false, lv_obj_has_state(obj, LV_STATE_DISABLED));
|
||||
}
|
||||
|
||||
void test_observer_button_checked(void)
|
||||
{
|
||||
lv_obj_t * obj = lv_button_create(lv_scr_act());
|
||||
lv_obj_set_size(obj, 100, 100);
|
||||
lv_obj_add_flag(obj, LV_OBJ_FLAG_CHECKABLE);
|
||||
lv_obj_update_layout(obj);
|
||||
|
||||
/*Can bind only to int*/
|
||||
static lv_subject_t subject_wrong;
|
||||
lv_subject_init_pointer(&subject_wrong, NULL);
|
||||
lv_observer_t * observer = lv_button_bind_checked(obj, &subject_wrong);
|
||||
TEST_ASSERT_EQUAL_PTR(NULL, observer);
|
||||
|
||||
static lv_subject_t subject;
|
||||
lv_subject_init_int(&subject, 1);
|
||||
lv_button_bind_checked(obj, &subject);
|
||||
|
||||
TEST_ASSERT_EQUAL(true, lv_obj_has_state(obj, LV_STATE_CHECKED));
|
||||
|
||||
lv_subject_set_int(&subject, 0);
|
||||
TEST_ASSERT_EQUAL(false, lv_obj_has_state(obj, LV_STATE_CHECKED));
|
||||
|
||||
lv_test_mouse_click_at(10, 10);
|
||||
TEST_ASSERT_EQUAL(true, lv_obj_has_state(obj, LV_STATE_CHECKED));
|
||||
TEST_ASSERT_EQUAL(1, lv_subject_get_int(&subject));
|
||||
}
|
||||
|
||||
void test_observer_label_text_normal(void)
|
||||
{
|
||||
lv_obj_t * obj = lv_label_create(lv_scr_act());
|
||||
|
||||
lv_observer_t * observer;
|
||||
|
||||
/*Cannot bind color*/
|
||||
static lv_subject_t subject_color;
|
||||
lv_subject_init_color(&subject_color, lv_color_black());
|
||||
observer = lv_label_bind_text(obj, &subject_color, NULL);
|
||||
TEST_ASSERT_EQUAL_PTR(NULL, observer);
|
||||
|
||||
/*Cannot bind int*/
|
||||
static lv_subject_t subject_int;
|
||||
lv_subject_init_int(&subject_int, 0);
|
||||
observer = lv_label_bind_text(obj, &subject_int, NULL);
|
||||
TEST_ASSERT_EQUAL_PTR(NULL, observer);
|
||||
|
||||
/*Bind to string*/
|
||||
static char buf[32];
|
||||
static lv_subject_t subject_string;
|
||||
lv_subject_init_string(&subject_string, buf, NULL, 32, "hello");
|
||||
lv_label_bind_text(obj, &subject_string, NULL);
|
||||
TEST_ASSERT_EQUAL_STRING("hello", lv_label_get_text(obj));
|
||||
|
||||
lv_subject_copy_string(&subject_string, "world");
|
||||
TEST_ASSERT_EQUAL_STRING("world", lv_label_get_text(obj));
|
||||
|
||||
/*Remove the label from the subject*/
|
||||
lv_subject_remove_all_obj(&subject_string, obj);
|
||||
lv_subject_copy_string(&subject_string, "nothing");
|
||||
TEST_ASSERT_EQUAL_STRING("world", lv_label_get_text(obj));
|
||||
|
||||
/*Bind to pointer*/
|
||||
static lv_subject_t subject_pointer;
|
||||
lv_subject_init_pointer(&subject_pointer, "HELLO");
|
||||
lv_label_bind_text(obj, &subject_pointer, NULL);
|
||||
TEST_ASSERT_EQUAL_STRING("HELLO", lv_label_get_text(obj));
|
||||
|
||||
lv_subject_set_pointer(&subject_pointer, "WORLD");
|
||||
TEST_ASSERT_EQUAL_STRING("WORLD", lv_label_get_text(obj));
|
||||
|
||||
/*Remove the label from the subject*/
|
||||
lv_subject_remove_all_obj(&subject_pointer, obj);
|
||||
lv_subject_copy_string(&subject_pointer, "NOTHING");
|
||||
TEST_ASSERT_EQUAL_STRING("WORLD", lv_label_get_text(obj));
|
||||
}
|
||||
|
||||
void test_observer_label_text_formatted(void)
|
||||
{
|
||||
lv_obj_t * obj = lv_label_create(lv_scr_act());
|
||||
|
||||
lv_observer_t * observer;
|
||||
|
||||
/*Cannot bind color*/
|
||||
static lv_subject_t subject_color;
|
||||
lv_subject_init_color(&subject_color, lv_color_black());
|
||||
observer = lv_label_bind_text(obj, &subject_color, NULL);
|
||||
TEST_ASSERT_EQUAL_PTR(NULL, observer);
|
||||
|
||||
/*Bind to int*/
|
||||
static lv_subject_t subject_int;
|
||||
lv_subject_init_int(&subject_int, 10);
|
||||
lv_label_bind_text(obj, &subject_int, "value: %d");
|
||||
TEST_ASSERT_EQUAL_STRING("value: 10", lv_label_get_text(obj));
|
||||
|
||||
lv_subject_set_int(&subject_int, -20);
|
||||
TEST_ASSERT_EQUAL_STRING("value: -20", lv_label_get_text(obj));
|
||||
|
||||
/*Remove the label from the subject*/
|
||||
lv_subject_remove_all_obj(&subject_int, obj);
|
||||
lv_subject_set_int(&subject_int, 100);
|
||||
TEST_ASSERT_EQUAL_STRING("value: -20", lv_label_get_text(obj));
|
||||
|
||||
/*Bind to string*/
|
||||
static char buf[32];
|
||||
static lv_subject_t subject_string;
|
||||
lv_subject_init_string(&subject_string, buf, NULL, 32, "hello");
|
||||
lv_label_bind_text(obj, &subject_string, "text: %s");
|
||||
TEST_ASSERT_EQUAL_STRING("text: hello", lv_label_get_text(obj));
|
||||
|
||||
lv_subject_copy_string(&subject_string, "world");
|
||||
TEST_ASSERT_EQUAL_STRING("text: world", lv_label_get_text(obj));
|
||||
|
||||
/*Remove the label from the subject*/
|
||||
lv_subject_remove_all_obj(&subject_string, obj);
|
||||
lv_subject_copy_string(&subject_string, "nothing");
|
||||
TEST_ASSERT_EQUAL_STRING("text: world", lv_label_get_text(obj));
|
||||
|
||||
/*Bind to pointer*/
|
||||
static lv_subject_t subject_pointer;
|
||||
lv_subject_init_pointer(&subject_pointer, "HELLO");
|
||||
lv_label_bind_text(obj, &subject_pointer, "pointer: %s");
|
||||
TEST_ASSERT_EQUAL_STRING("pointer: HELLO", lv_label_get_text(obj));
|
||||
|
||||
lv_subject_set_pointer(&subject_pointer, "WORLD");
|
||||
TEST_ASSERT_EQUAL_STRING("pointer: WORLD", lv_label_get_text(obj));
|
||||
|
||||
/*Remove the label from the subject*/
|
||||
lv_subject_remove_all_obj(&subject_pointer, obj);
|
||||
lv_subject_copy_string(&subject_pointer, "NOTHING");
|
||||
TEST_ASSERT_EQUAL_STRING("pointer: WORLD", lv_label_get_text(obj));
|
||||
}
|
||||
|
||||
void test_observer_arc_value(void)
|
||||
{
|
||||
lv_obj_t * obj = lv_arc_create(lv_scr_act());
|
||||
|
||||
static lv_subject_t subject;
|
||||
lv_subject_init_int(&subject, 30);
|
||||
lv_arc_bind_value(obj, &subject);
|
||||
|
||||
TEST_ASSERT_EQUAL(30, lv_arc_get_value(obj));
|
||||
|
||||
lv_subject_set_int(&subject, 40);
|
||||
TEST_ASSERT_EQUAL(40, lv_arc_get_value(obj));
|
||||
|
||||
lv_obj_update_layout(obj);
|
||||
lv_test_mouse_release();
|
||||
lv_test_mouse_move_to(65, 10);
|
||||
lv_test_mouse_press();
|
||||
lv_test_indev_wait(100);
|
||||
lv_test_mouse_release();
|
||||
|
||||
TEST_ASSERT_EQUAL(50, lv_arc_get_value(obj));
|
||||
TEST_ASSERT_EQUAL(50, lv_subject_get_int(&subject));
|
||||
}
|
||||
|
||||
void test_observer_slider_value(void)
|
||||
{
|
||||
lv_obj_t * obj = lv_slider_create(lv_scr_act());
|
||||
|
||||
static lv_subject_t subject;
|
||||
lv_subject_init_int(&subject, 30);
|
||||
lv_slider_bind_value(obj, &subject);
|
||||
|
||||
TEST_ASSERT_EQUAL(30, lv_slider_get_value(obj));
|
||||
|
||||
lv_subject_set_int(&subject, 40);
|
||||
TEST_ASSERT_EQUAL(40, lv_slider_get_value(obj));
|
||||
|
||||
lv_obj_update_layout(obj);
|
||||
lv_test_mouse_release();
|
||||
lv_test_mouse_move_to(65, 10);
|
||||
lv_test_mouse_press();
|
||||
lv_test_indev_wait(100);
|
||||
lv_test_mouse_move_to(75, 10);
|
||||
lv_test_mouse_press();
|
||||
lv_test_indev_wait(100);
|
||||
lv_test_mouse_release();
|
||||
|
||||
TEST_ASSERT_EQUAL(29, lv_slider_get_value(obj));
|
||||
TEST_ASSERT_EQUAL(29, lv_subject_get_int(&subject));
|
||||
}
|
||||
|
||||
void test_observer_roller_value(void)
|
||||
{
|
||||
lv_obj_t * obj = lv_roller_create(lv_scr_act());
|
||||
|
||||
static lv_subject_t subject;
|
||||
lv_subject_init_int(&subject, 1);
|
||||
lv_roller_bind_value(obj, &subject);
|
||||
|
||||
TEST_ASSERT_EQUAL(1, lv_roller_get_selected(obj));
|
||||
|
||||
lv_subject_set_int(&subject, 2);
|
||||
TEST_ASSERT_EQUAL(2, lv_roller_get_selected(obj));
|
||||
|
||||
lv_obj_update_layout(obj);
|
||||
lv_test_mouse_click_at(30, 10);
|
||||
|
||||
TEST_ASSERT_EQUAL(1, lv_roller_get_selected(obj));
|
||||
TEST_ASSERT_EQUAL(1, lv_subject_get_int(&subject));
|
||||
}
|
||||
|
||||
void test_observer_dropdown_value(void)
|
||||
{
|
||||
lv_obj_t * obj = lv_dropdown_create(lv_scr_act());
|
||||
|
||||
static lv_subject_t subject;
|
||||
lv_subject_init_int(&subject, 1);
|
||||
lv_dropdown_bind_value(obj, &subject);
|
||||
|
||||
TEST_ASSERT_EQUAL(1, lv_dropdown_get_selected(obj));
|
||||
|
||||
lv_subject_set_int(&subject, 2);
|
||||
TEST_ASSERT_EQUAL(2, lv_dropdown_get_selected(obj));
|
||||
|
||||
lv_obj_update_layout(obj);
|
||||
lv_test_mouse_click_at(30, 10);
|
||||
lv_test_mouse_click_at(30, 60);
|
||||
|
||||
TEST_ASSERT_EQUAL(0, lv_dropdown_get_selected(obj));
|
||||
TEST_ASSERT_EQUAL(0, lv_subject_get_int(&subject));
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user