feat(fragment): add fragment manager (a UI Controller concept) (#2940)

* adding lv_obj_controller

* adding examples for lv_obj_controller

* added some docs

* formatted code

* updated controller docs

* updated controller docs

* updated sample controller field

* changed lv_controller_manager_parent to lv_controller_manager_get_parent

* updated unmanaged controller creation/deletion

* renamed lv_controller_manager_t

* rename: controller -> fragment

* formatted code

* Update examples/others/fragment/lv_example_fragment.h

Co-authored-by: Gabor Kiss-Vamosi <kisvegabor@gmail.com>

* Update src/extra/others/fragment/lv_fragment.c

Co-authored-by: Gabor Kiss-Vamosi <kisvegabor@gmail.com>

* Update src/extra/others/fragment/lv_fragment.c

Co-authored-by: Gabor Kiss-Vamosi <kisvegabor@gmail.com>

* Update src/extra/others/fragment/lv_fragment.c

Co-authored-by: Gabor Kiss-Vamosi <kisvegabor@gmail.com>

* code cleanup

* fragment creation rework

* (wip) fragment manager

* (wip) fragment manager

* refactoring fragment

* lifecycle fixes

* updated fragment event callback

* exposed states of fragment

* added some docs

* updated lv_fragment_managed_states_t name

* updated docs

* updated docs

* updated lv_fragment_manager_dispatch_event docs

* removed msgbox fragment

* updated fragment docs

* updated fragment docs

* updated docs

* updating examples

* fixed example

* reformatted code

* fixed obj_created set timing

* simplified fragment

* improved fragment view del assertion

* fixed a typo

* fixed event_cb check in lv_obj_remove_event_cb_with_user_data

* fixing fragment obj assertion

* regenerated config

* fixed fragment examples

* fixed fragment examples

* added missing examples

* updated docs

* fragment api cleanup

* rename fragment struct names

* added missing param doc

* enabled test for 32bit build

* feat(porting): add a macro lv_run_timer_handler_in_period to simplify porting (#3063)

* feat(porting): add a macro lv_run_timer_handler_in_period to simplify porting

* feat: update helper function and doc

* doc(porting): update function names

* revise to the original os.md

* fix: fix typo

* fix: mitigate warnings

* chore: fix code formatting

* fix(fsdrv): replacing sprintf with lv_snprintf for safety (#3079)

* fix(Kconfig) remove duplicate LV_BUILD_EXAMPLES configuration

* feat(refr) add reset of FPS statistics

* fix(conf) mismatched macro judgment

* feat(fsdrv) replacing sprintf with lv_snprintf for safety

* feat(fsdrv) update stdio and win32

Co-authored-by: pengyiqiang <pengyiqiang@xiaomi.com>

* fix warnings

Co-authored-by: Gabor Kiss-Vamosi <kisvegabor@gmail.com>
Co-authored-by: Gabriel Wang <embedded_zhuoran@Hotmail.com>
Co-authored-by: _VIFEXTech <1290176185@qq.com>
Co-authored-by: pengyiqiang <pengyiqiang@xiaomi.com>
This commit is contained in:
Mariotaku 2022-02-11 20:43:08 +09:00 committed by GitHub
parent 9f90d82e91
commit e7736f2c32
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 1066 additions and 3 deletions

81
docs/others/fragment.md Normal file
View File

@ -0,0 +1,81 @@
```eval_rst
.. include:: /header.rst
:github_url: |github_link_base|/others/fragment.md
```
# Fragment
Fragment is a concept copied from [Android](https://developer.android.com/guide/fragments).
It represents a reusable portion of your app's UI. A fragment defines and manages its own layout, has its own lifecycle,
and can handle its own events. Like Android's Fragment that must be hosted by an activity or another fragment, Fragment
in LVGL needs to be hosted by an object, or another fragment. The fragments view hierarchy becomes part of, or attaches
to, the hosts view hierarchy.
Such concept also has some similarities
to [UiViewController on iOS](https://developer.apple.com/documentation/uikit/uiviewcontroller).
Fragment Manager is a manager holding references to fragments attached to it, and has an internal stack to achieve
navigation. You can use fragment manager to build navigation stack, or multi pane application easily.
## Usage
Enable `LV_USE_FRAGMENT` in `lv_conf.h`.
### Create Fragment Class
```c
struct sample_fragment_t {
/* IMPORTANT: don't miss this part */
lv_fragment_t base;
/* States, object references and data fields for this fragment */
const char *title;
};
const lv_fragment_class_t sample_cls = {
/* Initialize something needed */
.constructor_cb = sample_fragment_ctor,
/* Create view objects */
.create_obj_cb = sample_fragment_create_obj,
/* IMPORTANT: size of your fragment struct */
.instance_size = sizeof(struct sample_fragment_t)
};
```
### Use `lv_fragment_manager`
```c
/* Create fragment instance, and objects will be added to container */
lv_fragment_manager_t *manager = lv_fragment_manager_create(container, NULL);
/* Replace current fragment with instance of sample_cls, and init_argument is user defined pointer */
lv_fragment_manager_replace(manager, &sample_cls, init_argument);
```
### Fragment Based Navigation
```c
/* Add one instance into manager stack. View object of current fragment will be destroyed,
* but instances created in class constructor will be kept.
*/
lv_fragment_manager_push(manager, &sample_cls, NULL);
/* Remove the top most fragment from the stack, and bring back previous one. */
lv_fragment_manager_pop(manager);
```
## Example
```eval_rst
.. include:: ../../examples/others/fragment/index.rst
```
## API
```eval_rst
.. doxygenfile:: lv_fragment.h
:project: lvgl
```

View File

@ -13,5 +13,6 @@
snapshot
monkey
gridnav
fragment
```

View File

@ -0,0 +1,16 @@
Basic fragment usage
"""""""""""""""""""
.. lv_example:: others/fragment/lv_example_fragment_1
:language: c
Stack navigation example
"""""""""""""""""""
.. lv_example:: others/fragment/lv_example_fragment_2
:language: c

View File

@ -0,0 +1,38 @@
/**
* @file lv_example_fragment.h
*/
#ifndef LV_EXAMPLE_FRAGMENT_H
#define LV_EXAMPLE_FRAGMENT_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_example_fragment_1(void);
void lv_example_fragment_2(void);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_EXAMPLE_fragment_H*/

View File

@ -0,0 +1,47 @@
/**
* @file lv_example_fragment_1.c
* @brief Basic usage of obj fragment
*/
#include "../../lv_examples.h"
#if LV_USE_FRAGMENT && LV_BUILD_EXAMPLES
static void sample_fragment_ctor(lv_fragment_t *self, void *args);
static lv_obj_t *sample_fragment_create_obj(lv_fragment_t *self, lv_obj_t *parent);
static lv_obj_t * root = NULL;
struct sample_fragment_t {
lv_fragment_t base;
const char *name;
};
static const lv_fragment_class_t sample_cls = {
.constructor_cb = sample_fragment_ctor,
.create_obj_cb = sample_fragment_create_obj,
.instance_size = sizeof(struct sample_fragment_t)
};
void lv_example_fragment_1(void)
{
root = lv_obj_create(lv_scr_act());
lv_obj_set_size(root, LV_PCT(100), LV_PCT(100));
lv_fragment_manager_t *manager = lv_fragment_manager_create(NULL);
lv_fragment_t *fragment = lv_fragment_create(&sample_cls, "Fragment");
lv_fragment_manager_replace(manager, fragment, &root);
}
static void sample_fragment_ctor(lv_fragment_t *self, void *args) {
((struct sample_fragment_t *) self)->name = args;
}
static lv_obj_t *sample_fragment_create_obj(lv_fragment_t *self, lv_obj_t *parent) {
lv_obj_t *label = lv_label_create(parent);
lv_obj_set_style_bg_opa(label, LV_OPA_COVER, 0);;
lv_label_set_text_fmt(label, "Hello, %s!", ((struct sample_fragment_t *) self)->name);
return label;
}
#endif

View File

@ -0,0 +1,111 @@
/**
* @file lv_example_fragment_2.c
* @brief Navigation stack using obj fragment
*/
#include "../../lv_examples.h"
#if LV_USE_FRAGMENT && LV_USE_WIN && LV_BUILD_EXAMPLES
static void sample_fragment_ctor(lv_fragment_t *self, void *args);
static lv_obj_t *sample_fragment_create_obj(lv_fragment_t *self, lv_obj_t *parent);
static void sample_push_click(lv_event_t *e);
static void sample_pop_click(lv_event_t *e);
static void sample_fragment_inc_click(lv_event_t *e);
typedef struct sample_fragment_t {
lv_fragment_t base;
lv_obj_t *label;
int depth;
int counter;
} sample_fragment_t;
static const lv_fragment_class_t sample_cls = {
.constructor_cb = sample_fragment_ctor,
.create_obj_cb = sample_fragment_create_obj,
.instance_size = sizeof(sample_fragment_t)
};
static lv_obj_t *container = NULL;
void lv_example_fragment_2(void)
{
lv_obj_t *root = lv_obj_create(lv_scr_act());
lv_obj_set_size(root, LV_PCT(100), LV_PCT(100));
lv_obj_set_layout(root, LV_LAYOUT_GRID);
static const lv_coord_t col_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST};
static const lv_coord_t row_dsc[] = {LV_GRID_FR(1), LV_GRID_CONTENT, LV_GRID_TEMPLATE_LAST};
lv_obj_set_grid_dsc_array(root, col_dsc, row_dsc);
container = lv_obj_create(root);
lv_obj_remove_style_all(container);
lv_obj_set_grid_cell(container, LV_GRID_ALIGN_STRETCH, 0, 2, LV_GRID_ALIGN_STRETCH, 0, 1);
lv_obj_t *push_btn = lv_btn_create(root);
lv_obj_t *push_label = lv_label_create(push_btn);
lv_label_set_text(push_label, "Push");
lv_obj_t *pop_btn = lv_btn_create(root);
lv_obj_t *pop_label = lv_label_create(pop_btn);
lv_label_set_text(pop_label, "Pop");
lv_obj_set_grid_cell(push_btn, LV_GRID_ALIGN_START, 0, 1, LV_GRID_ALIGN_CENTER, 1, 1);
lv_obj_set_grid_cell(pop_btn, LV_GRID_ALIGN_END, 1, 1, LV_GRID_ALIGN_CENTER, 1, 1);
lv_fragment_manager_t *manager = lv_fragment_manager_create(NULL);
int depth = 0;
lv_fragment_t *fragment = lv_fragment_create(&sample_cls, &depth);
lv_fragment_manager_push(manager, fragment, &container);
lv_obj_add_event_cb(push_btn, sample_push_click, LV_EVENT_CLICKED, manager);
lv_obj_add_event_cb(pop_btn, sample_pop_click, LV_EVENT_CLICKED, manager);
}
static void sample_fragment_ctor(lv_fragment_t *self, void *args) {
LV_UNUSED(args);
((sample_fragment_t *) self)->depth = *((int *) args);
((sample_fragment_t *) self)->counter = 0;
}
static lv_obj_t *sample_fragment_create_obj(lv_fragment_t *self, lv_obj_t *parent) {
sample_fragment_t *fragment = (sample_fragment_t *) self;
lv_obj_t *content = lv_obj_create(parent);
lv_obj_remove_style_all(content);
lv_obj_set_style_bg_opa(content, LV_OPA_50, 0);
lv_obj_set_style_bg_color(content, lv_palette_main(LV_PALETTE_YELLOW), 0);
lv_obj_set_size(content, LV_PCT(100), LV_PCT(100));
lv_obj_set_flex_flow(content, LV_FLEX_FLOW_COLUMN);
lv_obj_t *depth = lv_label_create(content);
lv_label_set_text_fmt(depth, "Depth: %d", fragment->depth);
lv_obj_t *label = lv_label_create(content);
fragment->label = label;
lv_label_set_text_fmt(label, "The button has been pressed %d times", fragment->counter);
lv_obj_t *inc_btn = lv_btn_create(content);
lv_obj_t *inc_label = lv_label_create(inc_btn);
lv_label_set_text(inc_label, "+1");
lv_obj_add_event_cb(inc_btn, sample_fragment_inc_click, LV_EVENT_CLICKED, fragment);
return content;
}
static void sample_push_click(lv_event_t *e) {
lv_fragment_manager_t *manager = (lv_fragment_manager_t *) lv_event_get_user_data(e);
size_t stack_size = lv_fragment_manager_get_stack_size(manager);
lv_fragment_t *fragment = lv_fragment_create(&sample_cls, &stack_size);
lv_fragment_manager_push(manager, fragment, &container);
}
static void sample_pop_click(lv_event_t *e) {
lv_fragment_manager_t *manager = (lv_fragment_manager_t *) lv_event_get_user_data(e);
lv_fragment_manager_pop(manager);
}
static void sample_fragment_inc_click(lv_event_t *e) {
sample_fragment_t *fragment = (sample_fragment_t *) lv_event_get_user_data(e);
fragment->counter++;
lv_label_set_text_fmt(fragment->label, "The button has been pressed %d times", fragment->counter);
}
#endif

View File

@ -16,6 +16,7 @@ extern "C" {
#include "snapshot/lv_example_snapshot.h"
#include "monkey/lv_example_monkey.h"
#include "gridnav/lv_example_gridnav.h"
#include "fragment/lv_example_fragment.h"
/*********************
* DEFINES
@ -37,4 +38,4 @@ extern "C" {
} /*extern "C"*/
#endif
#endif /*LV_EX_OTHERS_H*/
#endif /*LV_EXAMPLE_OTHERS_H*/

View File

@ -657,6 +657,9 @@
/*1: Enable grid navigation*/
#define LV_USE_GRIDNAV 0
/*1: Enable lv_obj fragment*/
#define LV_USE_FRAGMENT 0
/*==================
* EXAMPLES
*==================*/

View File

@ -208,7 +208,7 @@ bool lv_obj_remove_event_cb_with_user_data(lv_obj_t * obj, lv_event_cb_t event_c
int32_t i = 0;
for(i = 0; i < obj->spec_attr->event_dsc_cnt; i++) {
if((event_cb == NULL || obj->spec_attr->event_dsc[i].cb) &&
if((event_cb == NULL || obj->spec_attr->event_dsc[i].cb == event_cb) &&
obj->spec_attr->event_dsc[i].user_data == user_data) {
/*Shift the remaining event handlers forward*/
for(; i < (obj->spec_attr->event_dsc_cnt - 1); i++) {

View File

View File

@ -0,0 +1,137 @@
/**
* @file lv_fragment.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_fragment.h"
#if LV_USE_FRAGMENT
/**********************
* STATIC PROTOTYPES
**********************/
static void cb_delete_assertion(lv_event_t * event);
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_fragment_t * lv_fragment_create(const lv_fragment_class_t * cls, void * args)
{
LV_ASSERT_NULL(cls);
LV_ASSERT_NULL(cls->create_obj_cb);
LV_ASSERT(cls->instance_size > 0);
lv_fragment_t * instance = lv_mem_alloc(cls->instance_size);
lv_memset_00(instance, cls->instance_size);
instance->cls = cls;
instance->child_manager = lv_fragment_manager_create(instance);
if(cls->constructor_cb) {
cls->constructor_cb(instance, args);
}
return instance;
}
void lv_fragment_del(lv_fragment_t * fragment)
{
LV_ASSERT_NULL(fragment);
if(fragment->managed) {
lv_fragment_manager_remove(fragment->managed->manager, fragment);
return;
}
if(fragment->obj) {
lv_fragment_del_obj(fragment);
}
/* Objects will leak if this function called before objects deleted */
const lv_fragment_class_t * cls = fragment->cls;
if(cls->destructor_cb) {
cls->destructor_cb(fragment);
}
lv_fragment_manager_del(fragment->child_manager);
lv_mem_free(fragment);
}
lv_obj_t * const * lv_fragment_get_container(lv_fragment_t * fragment)
{
LV_ASSERT_NULL(fragment);
LV_ASSERT_NULL(fragment->managed);
return fragment->managed->container;
}
lv_fragment_t * lv_fragment_get_parent(lv_fragment_t * fragment)
{
LV_ASSERT_NULL(fragment);
LV_ASSERT_NULL(fragment->managed);
return lv_fragment_manager_get_parent_fragment(fragment->managed->manager);
}
lv_obj_t * lv_fragment_create_obj(lv_fragment_t * fragment, lv_obj_t * container)
{
lv_fragment_managed_states_t * states = fragment->managed;
if(states) {
states->destroying_obj = false;
}
const lv_fragment_class_t * cls = fragment->cls;
lv_obj_t * obj = cls->create_obj_cb(fragment, container);
LV_ASSERT_NULL(obj);
fragment->obj = obj;
lv_fragment_manager_create_obj(fragment->child_manager);
if(states) {
states->obj_created = true;
lv_obj_add_event_cb(obj, cb_delete_assertion, LV_EVENT_DELETE, NULL);
}
if(cls->obj_created_cb) {
cls->obj_created_cb(fragment, obj);
}
return obj;
}
void lv_fragment_del_obj(lv_fragment_t * fragment)
{
LV_ASSERT_NULL(fragment);
lv_fragment_manager_del_obj(fragment->child_manager);
lv_fragment_managed_states_t * states = fragment->managed;
if(states) {
if(!states->obj_created) return;
states->destroying_obj = true;
bool cb_removed = lv_obj_remove_event_cb(fragment->obj, cb_delete_assertion);
LV_ASSERT(cb_removed);
}
LV_ASSERT_NULL(fragment->obj);
const lv_fragment_class_t * cls = fragment->cls;
if(cls->obj_will_delete_cb) {
cls->obj_will_delete_cb(fragment, fragment->obj);
}
lv_obj_del(fragment->obj);
if(cls->obj_deleted_cb) {
cls->obj_deleted_cb(fragment, fragment->obj);
}
if(states) {
states->obj_created = false;
}
fragment->obj = NULL;
}
void lv_fragment_recreate_obj(lv_fragment_t * fragment)
{
LV_ASSERT_NULL(fragment);
LV_ASSERT_NULL(fragment->managed);
lv_fragment_del_obj(fragment);
lv_fragment_create_obj(fragment, *fragment->managed->container);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void cb_delete_assertion(lv_event_t * event)
{
LV_UNUSED(event);
LV_ASSERT_MSG(0, "Please delete objects with lv_fragment_destroy_obj");
}
#endif /*LV_USE_FRAGMENT*/

View File

@ -0,0 +1,339 @@
/**
* Public header for Fragment
* @file lv_fragment.h
*/
#ifndef LV_FRAGMENT_H
#define LV_FRAGMENT_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lv_conf_internal.h"
#if LV_USE_FRAGMENT
#include "../../../core/lv_obj.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct _lv_fragment_manager_t lv_fragment_manager_t;
typedef struct _lv_fragment_t lv_fragment_t;
typedef struct _lv_fragment_class_t lv_fragment_class_t;
typedef struct _lv_fragment_managed_states_t lv_fragment_managed_states_t;
struct _lv_fragment_t {
/**
* Class of this fragment
*/
const lv_fragment_class_t * cls;
/**
* Managed fragment states. If not null, then this fragment is managed.
*
* @warning Don't modify values inside this struct!
*/
lv_fragment_managed_states_t * managed;
/**
* Child fragment manager
*/
lv_fragment_manager_t * child_manager;
/**
* lv_obj returned by create_obj_cb
*/
lv_obj_t * obj;
};
struct _lv_fragment_class_t {
/**
* Constructor function for fragment class
* @param self Fragment instance
* @param args Arguments assigned by fragment manager
*/
void (*constructor_cb)(lv_fragment_t * self, void * args);
/**
* Destructor function for fragment class
* @param self Fragment instance, will be freed after this call
*/
void (*destructor_cb)(lv_fragment_t * self);
/**
* Fragment attached to manager
* @param self Fragment instance
*/
void (*attached_cb)(lv_fragment_t * self);
/**
* Fragment detached from manager
* @param self Fragment instance
*/
void (*detached_cb)(lv_fragment_t * self);
/**
* Create objects
* @param self Fragment instance
* @param container Container of the objects should be created upon
* @return Created object, NULL if multiple objects has been created
*/
lv_obj_t * (*create_obj_cb)(lv_fragment_t * self, lv_obj_t * container);
/**
*
* @param self Fragment instance
* @param obj lv_obj returned by create_obj_cb
*/
void (*obj_created_cb)(lv_fragment_t * self, lv_obj_t * obj);
/**
* Called before objects in the fragment will be deleted.
*
* @param self Fragment instance
* @param obj object with this fragment
*/
void (*obj_will_delete_cb)(lv_fragment_t * self, lv_obj_t * obj);
/**
* Called when the object created by fragment received `LV_EVENT_DELETE` event
* @param self Fragment instance
* @param obj object with this fragment
*/
void (*obj_deleted_cb)(lv_fragment_t * self, lv_obj_t * obj);
/**
* Handle event
* @param self Fragment instance
* @param which User-defined ID of event
* @param data1 User-defined data
* @param data2 User-defined data
*/
bool (*event_cb)(lv_fragment_t * self, int code, void * userdata);
/**
* *REQUIRED*: Allocation size of fragment
*/
size_t instance_size;
};
/**
* Fragment states
*/
typedef struct _lv_fragment_managed_states_t {
/**
* Class of the fragment
*/
const lv_fragment_class_t * cls;
/**
* Manager the fragment attached to
*/
lv_fragment_manager_t * manager;
/**
* Container object the fragment adding view to
*/
lv_obj_t * const * container;
/**
* Fragment instance
*/
lv_fragment_t * instance;
/**
* true between `create_obj_cb` and `obj_deleted_cb`
*/
bool obj_created;
/**
* true before `lv_fragment_del_obj` is called. Don't touch any object if this is true
*/
bool destroying_obj;
/**
* true if this fragment is in navigation stack that can be popped
*/
bool in_stack;
} lv_fragment_managed_states_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create fragment manager instance
* @param parent Parent fragment if this manager is placed inside another fragment, can be null.
* @return Fragment manager instance
*/
lv_fragment_manager_t * lv_fragment_manager_create(lv_fragment_t * parent);
/**
* Destroy fragment manager instance
* @param manager Fragment manager instance
*/
void lv_fragment_manager_del(lv_fragment_manager_t * manager);
/**
* Create object of all fragments managed by this manager.
* @param manager Fragment manager instance
*/
void lv_fragment_manager_create_obj(lv_fragment_manager_t * manager);
/**
* Delete object created by all fragments managed by this manager. Instance of fragments will not be deleted.
* @param manager Fragment manager instance
*/
void lv_fragment_manager_del_obj(lv_fragment_manager_t * manager);
/**
* Attach fragment to manager, and add to container.
* @param manager Fragment manager instance
* @param fragment Fragment instance
* @param container Pointer to container object for manager to add objects to
*/
void lv_fragment_manager_add(lv_fragment_manager_t * manager, lv_fragment_t * fragment, lv_obj_t * const * container);
/**
* Detach and destroy fragment. If fragment is in navigation stack, remove from it.
* @param manager Fragment manager instance
* @param fragment Fragment instance
*/
void lv_fragment_manager_remove(lv_fragment_manager_t * manager, lv_fragment_t * fragment);
/**
* Attach fragment to manager and add to navigation stack.
* @param manager Fragment manager instance
* @param fragment Fragment instance
* @param container Pointer to container object for manager to add objects to
*/
void lv_fragment_manager_push(lv_fragment_manager_t * manager, lv_fragment_t * fragment, lv_obj_t * const * container);
/**
* Remove the top-most fragment for stack
* @param manager Fragment manager instance
* @return true if there is fragment to pop
*/
bool lv_fragment_manager_pop(lv_fragment_manager_t * manager);
/**
* Replace fragment. Old item in the stack will be removed.
* @param manager Fragment manager instance
* @param fragment Fragment instance
* @param container Pointer to container object for manager to add objects to
*/
void lv_fragment_manager_replace(lv_fragment_manager_t * manager, lv_fragment_t * fragment,
lv_obj_t * const * container);
/**
* Send event to top-most fragment
* @param manager Fragment manager instance
* @param code User-defined ID of event
* @param userdata User-defined data
* @return true if fragment returned true
*/
bool lv_fragment_manager_send_event(lv_fragment_manager_t * manager, int code, void * userdata);
/**
* Get stack size of this fragment manager
* @param manager Fragment manager instance
* @return Stack size of this fragment manager
*/
size_t lv_fragment_manager_get_stack_size(lv_fragment_manager_t * manager);
/**
* Get top most fragment instance
* @param manager Fragment manager instance
* @return Top most fragment instance
*/
lv_fragment_t * lv_fragment_manager_get_top(lv_fragment_manager_t * manager);
/**
* Find first fragment instance in the container
* @param manager Fragment manager instance
* @param container Container which target fragment added to
* @return First fragment instance in the container
*/
lv_fragment_t * lv_fragment_manager_find_by_container(lv_fragment_manager_t * manager, const lv_obj_t * container);
/**
* Get parent fragment
* @param manager Fragment manager instance
* @return Parent fragment instance
*/
lv_fragment_t * lv_fragment_manager_get_parent_fragment(lv_fragment_manager_t * manager);
/**
* Create a fragment instance.
*
* @param cls Fragment class. This fragment must return non null object.
* @param args Arguments assigned by fragment manager
* @return Fragment instance
*/
lv_fragment_t * lv_fragment_create(const lv_fragment_class_t * cls, void * args);
/**
* Destroy a fragment.
* @param fragment Fragment instance.
*/
void lv_fragment_del(lv_fragment_t * fragment);
/**
* Get associated manager of this fragment
* @param fragment Fragment instance
* @return Fragment manager instance
*/
lv_fragment_manager_t * lv_fragment_get_manager(lv_fragment_t * fragment);
/**
* Get container object of this fragment
* @param fragment Fragment instance
* @return Reference to container object
*/
lv_obj_t * const * lv_fragment_get_container(lv_fragment_t * fragment);
/**
* Get parent fragment of this fragment
* @param fragment Fragment instance
* @return Parent fragment
*/
lv_fragment_t * lv_fragment_get_parent(lv_fragment_t * fragment);
/**
* Create object by fragment.
*
* @param fragment Fragment instance.
* @param container Container of the objects should be created upon.
* @return Created object
*/
lv_obj_t * lv_fragment_create_obj(lv_fragment_t * fragment, lv_obj_t * container);
/**
* Delete created object of a fragment
*
* @param fragment Fragment instance.
*/
void lv_fragment_del_obj(lv_fragment_t * fragment);
/**
* Destroy obj in fragment, and recreate them.
* @param fragment Fragment instance
*/
void lv_fragment_recreate_obj(lv_fragment_t * fragment);
/**********************
* MACROS
**********************/
#endif /*LV_USE_FRAGMENT*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_FRAGMENT_H*/

View File

@ -0,0 +1,278 @@
/**
* @file lv_fragment_manager.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_fragment.h"
#if LV_USE_FRAGMENT
#include "../../../misc/lv_ll.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct _lv_fragment_stack_item_t {
lv_fragment_managed_states_t * states;
} lv_fragment_stack_item_t;
struct _lv_fragment_manager_t {
lv_fragment_t * parent;
/**
* Linked list to store attached fragments
*/
lv_ll_t attached;
/**
* Linked list to store fragments in stack
*/
lv_ll_t stack;
};
/**********************
* STATIC PROTOTYPES
**********************/
static void item_create_obj(lv_fragment_managed_states_t * item);
static void item_del_obj(lv_fragment_managed_states_t * item);
static void item_del_fragment(lv_fragment_managed_states_t * item);
static lv_fragment_managed_states_t * fragment_attach(lv_fragment_manager_t * manager, lv_fragment_t * fragment,
lv_obj_t * const * container);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_fragment_manager_t * lv_fragment_manager_create(lv_fragment_t * parent)
{
lv_fragment_manager_t * instance = lv_mem_alloc(sizeof(lv_fragment_manager_t));
lv_memset_00(instance, sizeof(lv_fragment_manager_t));
instance->parent = parent;
_lv_ll_init(&instance->attached, sizeof(lv_fragment_managed_states_t));
_lv_ll_init(&instance->stack, sizeof(lv_fragment_stack_item_t));
return instance;
}
void lv_fragment_manager_del(lv_fragment_manager_t * manager)
{
LV_ASSERT_NULL(manager);
lv_fragment_managed_states_t * states;
_LV_LL_READ_BACK(&manager->attached, states) {
item_del_obj(states);
item_del_fragment(states);
}
_lv_ll_clear(&manager->attached);
_lv_ll_clear(&manager->stack);
lv_mem_free(manager);
}
void lv_fragment_manager_create_obj(lv_fragment_manager_t * manager)
{
LV_ASSERT_NULL(manager);
lv_fragment_stack_item_t * top = _lv_ll_get_tail(&manager->stack);
lv_fragment_managed_states_t * states = NULL;
_LV_LL_READ(&manager->attached, states) {
if(states->in_stack && top->states != states) {
/*Only create obj for top item in stack*/
continue;
}
item_create_obj(states);
}
}
void lv_fragment_manager_del_obj(lv_fragment_manager_t * manager)
{
LV_ASSERT_NULL(manager);
lv_fragment_managed_states_t * states = NULL;
_LV_LL_READ_BACK(&manager->attached, states) {
item_del_obj(states);
}
}
void lv_fragment_manager_add(lv_fragment_manager_t * manager, lv_fragment_t * fragment, lv_obj_t * const * container)
{
lv_fragment_managed_states_t * states = fragment_attach(manager, fragment, container);
if(!manager->parent || manager->parent->managed->obj_created) {
item_create_obj(states);
}
}
void lv_fragment_manager_remove(lv_fragment_manager_t * manager, lv_fragment_t * fragment)
{
LV_ASSERT_NULL(manager);
LV_ASSERT_NULL(fragment);
LV_ASSERT_NULL(fragment->managed);
LV_ASSERT(fragment->managed->manager == manager);
lv_fragment_managed_states_t * states = fragment->managed;
lv_fragment_managed_states_t * prev = NULL;
bool was_top = false;
if(states->in_stack) {
void * stack_top = _lv_ll_get_tail(&manager->stack);
lv_fragment_stack_item_t * stack = NULL;
_LV_LL_READ_BACK(&manager->stack, stack) {
if(stack->states == states) {
was_top = stack_top == stack;
void * stack_prev = _lv_ll_get_prev(&manager->stack, stack);
if(!stack_prev) break;
prev = ((lv_fragment_stack_item_t *) stack_prev)->states;
break;
}
}
if(stack) {
_lv_ll_remove(&manager->stack, stack);
}
}
item_del_obj(states);
item_del_fragment(states);
_lv_ll_remove(&manager->attached, states);
if(prev && was_top) {
item_create_obj(prev);
}
}
void lv_fragment_manager_push(lv_fragment_manager_t * manager, lv_fragment_t * fragment, lv_obj_t * const * container)
{
lv_fragment_stack_item_t * top = _lv_ll_get_tail(&manager->stack);
if(top != NULL) {
item_del_obj(top->states);
}
lv_fragment_managed_states_t * states = fragment_attach(manager, fragment, container);
states->in_stack = true;
/*Add fragment to the top of the stack*/
lv_fragment_stack_item_t * item = _lv_ll_ins_tail(&manager->stack);
lv_memset_00(item, sizeof(lv_fragment_stack_item_t));
item->states = states;
item_create_obj(states);
}
bool lv_fragment_manager_pop(lv_fragment_manager_t * manager)
{
lv_fragment_t * top = lv_fragment_manager_get_top(manager);
if(top == NULL) return false;
lv_fragment_manager_remove(manager, top);
return true;
}
void lv_fragment_manager_replace(lv_fragment_manager_t * manager, lv_fragment_t * fragment,
lv_obj_t * const * container)
{
lv_fragment_t * top = lv_fragment_manager_find_by_container(manager, *container);
if(top != NULL) {
lv_fragment_manager_remove(manager, top);
}
lv_fragment_manager_add(manager, fragment, container);
}
bool lv_fragment_manager_send_event(lv_fragment_manager_t * manager, int code, void * userdata)
{
LV_ASSERT_NULL(manager);
lv_fragment_stack_item_t * top = _lv_ll_get_tail(&manager->stack);
if(!top) return false;
lv_fragment_managed_states_t * states = top->states;
lv_fragment_t * instance = states->instance;
if(!instance) return false;
if(lv_fragment_manager_send_event(instance->child_manager, code, userdata)) return true;
if(!states->cls->event_cb) return false;
return states->cls->event_cb(instance, code, userdata);
}
size_t lv_fragment_manager_get_stack_size(lv_fragment_manager_t * manager)
{
LV_ASSERT_NULL(manager);
return _lv_ll_get_len(&manager->stack);
}
lv_fragment_t * lv_fragment_manager_get_top(lv_fragment_manager_t * manager)
{
LV_ASSERT(manager);
lv_fragment_stack_item_t * top = _lv_ll_get_tail(&manager->stack);
if(!top)return NULL;
return top->states->instance;
}
lv_fragment_t * lv_fragment_manager_find_by_container(lv_fragment_manager_t * manager, const lv_obj_t * container)
{
LV_ASSERT(manager);
lv_fragment_managed_states_t * states;
_LV_LL_READ(&manager->attached, states) {
if(*states->container == container) return states->instance;
}
return NULL;
}
lv_fragment_t * lv_fragment_manager_get_parent_fragment(lv_fragment_manager_t * manager)
{
LV_ASSERT_NULL(manager);
return manager->parent;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void item_create_obj(lv_fragment_managed_states_t * item)
{
LV_ASSERT(item->instance);
lv_fragment_create_obj(item->instance, item->container ? *item->container : NULL);
}
static void item_del_obj(lv_fragment_managed_states_t * item)
{
lv_fragment_del_obj(item->instance);
}
/**
* Detach, then destroy fragment
* @param item fragment states
*/
static void item_del_fragment(lv_fragment_managed_states_t * item)
{
lv_fragment_t * instance = item->instance;
if(instance->cls->detached_cb) {
instance->cls->detached_cb(instance);
}
instance->managed = NULL;
lv_fragment_del(instance);
item->instance = NULL;
}
static lv_fragment_managed_states_t * fragment_attach(lv_fragment_manager_t * manager, lv_fragment_t * fragment,
lv_obj_t * const * container)
{
LV_ASSERT(manager);
LV_ASSERT(fragment);
LV_ASSERT(fragment->managed == NULL);
lv_fragment_managed_states_t * item = _lv_ll_ins_tail(&manager->attached);
lv_memset_00(item, sizeof(lv_fragment_managed_states_t));
item->cls = fragment->cls;
item->manager = manager;
item->container = container;
item->instance = fragment;
fragment->managed = item;
if(fragment->cls->attached_cb) {
fragment->cls->attached_cb(fragment);
}
return item;
}
#endif /*LV_USE_FRAGMENT*/

View File

@ -16,6 +16,7 @@ extern "C" {
#include "snapshot/lv_snapshot.h"
#include "monkey/lv_monkey.h"
#include "gridnav/lv_gridnav.h"
#include "fragment/lv_fragment.h"
/*********************
* DEFINES

View File

@ -2108,6 +2108,15 @@
#endif
#endif
/*1: Enable lv_obj fragment*/
#ifndef LV_USE_FRAGMENT
#ifdef CONFIG_LV_USE_FRAGMENT
#define LV_USE_FRAGMENT CONFIG_LV_USE_FRAGMENT
#else
#define LV_USE_FRAGMENT 0
#endif
#endif
/*==================
* EXAMPLES
*==================*/

View File

@ -286,7 +286,7 @@ void * _lv_ll_get_next(const lv_ll_t * ll_p, const void * n_act)
}
/**
* Return with the pointer of the previous node after 'n_act'
* Return with the pointer of the previous node before 'n_act'
* @param ll_p pointer to linked list
* @param n_act pointer a node
* @return pointer to the previous node

View File

@ -189,6 +189,7 @@ set(LVGL_TEST_OPTIONS_FULL_32BIT
-DLV_USE_SJPG=1
-DLV_USE_GIF=1
-DLV_USE_QRCODE=1
-DLV_USE_FRAGMENT=1
)
set(LVGL_TEST_OPTIONS_TEST_COMMON