diff --git a/docs/others/fragment.md b/docs/others/fragment.md new file mode 100644 index 000000000..5d2966773 --- /dev/null +++ b/docs/others/fragment.md @@ -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 fragment’s view hierarchy becomes part of, or attaches +to, the host’s 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 + +``` diff --git a/docs/others/index.md b/docs/others/index.md index 977363fcb..20628e95b 100644 --- a/docs/others/index.md +++ b/docs/others/index.md @@ -13,5 +13,6 @@ snapshot monkey gridnav + fragment ``` diff --git a/examples/others/fragment/index.rst b/examples/others/fragment/index.rst new file mode 100644 index 000000000..cc821eac0 --- /dev/null +++ b/examples/others/fragment/index.rst @@ -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 + + diff --git a/examples/others/fragment/lv_example_fragment.h b/examples/others/fragment/lv_example_fragment.h new file mode 100644 index 000000000..09b5b36bb --- /dev/null +++ b/examples/others/fragment/lv_example_fragment.h @@ -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*/ diff --git a/examples/others/fragment/lv_example_fragment_1.c b/examples/others/fragment/lv_example_fragment_1.c new file mode 100644 index 000000000..f773f7b6e --- /dev/null +++ b/examples/others/fragment/lv_example_fragment_1.c @@ -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 diff --git a/examples/others/fragment/lv_example_fragment_2.c b/examples/others/fragment/lv_example_fragment_2.c new file mode 100644 index 000000000..6b9cc7dca --- /dev/null +++ b/examples/others/fragment/lv_example_fragment_2.c @@ -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 diff --git a/examples/others/lv_example_others.h b/examples/others/lv_example_others.h index f054015fd..c7fff9c3d 100644 --- a/examples/others/lv_example_others.h +++ b/examples/others/lv_example_others.h @@ -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*/ diff --git a/lv_conf_template.h b/lv_conf_template.h index 916e8fe22..4a27c7643 100644 --- a/lv_conf_template.h +++ b/lv_conf_template.h @@ -657,6 +657,9 @@ /*1: Enable grid navigation*/ #define LV_USE_GRIDNAV 0 +/*1: Enable lv_obj fragment*/ +#define LV_USE_FRAGMENT 0 + /*================== * EXAMPLES *==================*/ diff --git a/src/core/lv_event.c b/src/core/lv_event.c index 143ff34b8..e3b2ac9cd 100644 --- a/src/core/lv_event.c +++ b/src/core/lv_event.c @@ -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++) { diff --git a/src/extra/others/fragment/README.md b/src/extra/others/fragment/README.md new file mode 100644 index 000000000..e69de29bb diff --git a/src/extra/others/fragment/lv_fragment.c b/src/extra/others/fragment/lv_fragment.c new file mode 100644 index 000000000..9431e5576 --- /dev/null +++ b/src/extra/others/fragment/lv_fragment.c @@ -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*/ diff --git a/src/extra/others/fragment/lv_fragment.h b/src/extra/others/fragment/lv_fragment.h new file mode 100644 index 000000000..da30b39a5 --- /dev/null +++ b/src/extra/others/fragment/lv_fragment.h @@ -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*/ diff --git a/src/extra/others/fragment/lv_fragment_manager.c b/src/extra/others/fragment/lv_fragment_manager.c new file mode 100644 index 000000000..4a301345f --- /dev/null +++ b/src/extra/others/fragment/lv_fragment_manager.c @@ -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*/ diff --git a/src/extra/others/lv_others.h b/src/extra/others/lv_others.h index a4338666a..48a3f5203 100644 --- a/src/extra/others/lv_others.h +++ b/src/extra/others/lv_others.h @@ -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 diff --git a/src/lv_conf_internal.h b/src/lv_conf_internal.h index ebc10579c..d7321ad26 100644 --- a/src/lv_conf_internal.h +++ b/src/lv_conf_internal.h @@ -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 *==================*/ diff --git a/src/misc/lv_ll.c b/src/misc/lv_ll.c index c84647ccf..e7582316b 100644 --- a/src/misc/lv_ll.c +++ b/src/misc/lv_ll.c @@ -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 diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 63e21b1d3..4acf87dbb 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -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