JS: Refactor widget with ViewHolder, fix crash

Based on textbox refactor by nminaylov
This commit is contained in:
Willy-JL 2024-06-26 02:53:39 +02:00
parent b65741d625
commit 681b1cd069

View file

@ -1,6 +1,5 @@
#include <assets_icons.h>
#include <gui/view_dispatcher.h>
#include <gui/view.h>
#include <gui/view_holder.h>
#include <m-array.h>
#include <m-list.h>
#include <string.h>
@ -113,8 +112,8 @@ typedef struct {
typedef struct {
View* view;
ViewDispatcher* view_dispatcher;
FuriThread* thread;
ViewHolder* view_holder;
bool is_shown;
} JsWidgetInst;
static JsWidgetInst* get_this_ctx(struct mjs* mjs) {
@ -754,60 +753,34 @@ static void js_widget_is_open(struct mjs* mjs) {
JsWidgetInst* widget = get_this_ctx(mjs);
if(!check_arg_count(mjs, 0)) return;
mjs_return(mjs, mjs_mk_boolean(mjs, !!widget->thread));
}
static void widget_deinit(void* context) {
JsWidgetInst* widget = context;
if(widget->thread) {
furi_thread_join(widget->thread);
furi_thread_free(widget->thread);
widget->thread = NULL;
furi_assert(widget->view_dispatcher);
view_dispatcher_remove_view(widget->view_dispatcher, 0);
view_dispatcher_free(widget->view_dispatcher);
widget->view_dispatcher = NULL;
furi_record_close(RECORD_GUI);
}
mjs_return(mjs, mjs_mk_boolean(mjs, widget->is_shown));
}
static void widget_callback(void* context, uint32_t arg) {
UNUSED(arg);
widget_deinit(context);
}
static bool widget_exit(void* context) {
JsWidgetInst* widget = context;
view_dispatcher_stop(widget->view_dispatcher);
furi_timer_pending_callback(widget_callback, widget, 0);
return true;
view_holder_stop(widget->view_holder);
widget->is_shown = false;
}
static int32_t widget_thread(void* context) {
ViewDispatcher* view_dispatcher = context;
view_dispatcher_run(view_dispatcher);
return 0;
static void widget_exit(void* context) {
JsWidgetInst* widget = context;
// Using timer to schedule view_holder stop, will not work under high CPU load
furi_timer_pending_callback(widget_callback, widget, 0);
}
static void js_widget_show(struct mjs* mjs) {
JsWidgetInst* widget = get_this_ctx(mjs);
if(!check_arg_count(mjs, 0)) return;
Gui* gui = furi_record_open(RECORD_GUI);
if(widget->is_shown) {
mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Widget is already shown");
mjs_return(mjs, MJS_UNDEFINED);
return;
}
widget->view_dispatcher = view_dispatcher_alloc();
view_dispatcher_enable_queue(widget->view_dispatcher);
view_dispatcher_add_view(widget->view_dispatcher, 0, widget->view);
view_dispatcher_set_event_callback_context(widget->view_dispatcher, widget);
view_dispatcher_set_navigation_event_callback(widget->view_dispatcher, widget_exit);
view_dispatcher_attach_to_gui(widget->view_dispatcher, gui, ViewDispatcherTypeFullscreen);
view_dispatcher_switch_to_view(widget->view_dispatcher, 0);
widget->thread =
furi_thread_alloc_ex("JsWidget", 1024, widget_thread, widget->view_dispatcher);
furi_thread_start(widget->thread);
view_holder_start(widget->view_holder);
widget->is_shown = true;
mjs_return(mjs, MJS_UNDEFINED);
}
@ -816,10 +789,8 @@ static void js_widget_close(struct mjs* mjs) {
JsWidgetInst* widget = get_this_ctx(mjs);
if(!check_arg_count(mjs, 0)) return;
if(widget->thread) {
view_dispatcher_stop(widget->view_dispatcher);
widget_deinit(widget);
}
view_holder_stop(widget->view_holder);
widget->is_shown = false;
mjs_return(mjs, MJS_UNDEFINED);
}
@ -839,57 +810,9 @@ static void widget_draw_callback(Canvas* canvas, void* model) {
}
}
static void widget_remove_view(void* context) {
JsWidgetInst* widget = context;
if(widget->view) {
with_view_model(
widget->view,
WidgetModel * model,
{
ComponentArray_it_t it;
ComponentArray_it(it, model->component);
while(!ComponentArray_end_p(it)) {
WidgetComponent* component = *ComponentArray_ref(it);
if(component && component->free) {
component->free(component);
}
ComponentArray_next(it);
}
ComponentArray_reset(model->component);
ComponentArray_clear(model->component);
},
false);
with_view_model(
widget->view, WidgetModel * model, { XbmImageList_clear(model->image); }, false);
view_free(widget->view);
widget->view = NULL;
}
}
static JsWidgetInst* widget_alloc(void) {
JsWidgetInst* widget = malloc(sizeof(JsWidgetInst));
widget->thread = NULL;
widget->view_dispatcher = NULL;
widget->view = view_alloc();
view_allocate_model(widget->view, ViewModelTypeLockFree, sizeof(WidgetModel));
view_set_draw_callback(widget->view, widget_draw_callback);
with_view_model(
widget->view,
WidgetModel * model,
{
ComponentArray_init(model->component);
XbmImageList_init(model->image);
model->max_assigned_id = 0;
},
true);
return widget;
}
static void* js_widget_create(struct mjs* mjs, mjs_val_t* object) {
JsWidgetInst* widget = widget_alloc();
JsWidgetInst* widget = malloc(sizeof(JsWidgetInst));
mjs_val_t widget_obj = mjs_mk_object(mjs);
mjs_set(mjs, widget_obj, INST_PROP_NAME, ~0, mjs_mk_foreign(mjs, widget));
// addBox(x: number, y: number, w: number, h: number): number (returns id of the added component)
@ -924,17 +847,60 @@ static void* js_widget_create(struct mjs* mjs, mjs_val_t* object) {
mjs_set(mjs, widget_obj, "show", ~0, MJS_MK_FN(js_widget_show));
// close(): void (closes the widget)
mjs_set(mjs, widget_obj, "close", ~0, MJS_MK_FN(js_widget_close));
widget->view = view_alloc();
view_allocate_model(widget->view, ViewModelTypeLockFree, sizeof(WidgetModel));
view_set_draw_callback(widget->view, widget_draw_callback);
with_view_model(
widget->view,
WidgetModel * model,
{
ComponentArray_init(model->component);
XbmImageList_init(model->image);
model->max_assigned_id = 0;
},
true);
Gui* gui = furi_record_open(RECORD_GUI);
widget->view_holder = view_holder_alloc();
view_holder_attach_to_gui(widget->view_holder, gui);
view_holder_set_back_callback(widget->view_holder, widget_exit, widget);
view_holder_set_view(widget->view_holder, widget->view);
*object = widget_obj;
return widget;
}
static void js_widget_destroy(void* inst) {
JsWidgetInst* widget = inst;
if(widget->thread) {
view_dispatcher_stop(widget->view_dispatcher);
widget_deinit(widget);
}
widget_remove_view(widget);
view_holder_stop(widget->view_holder);
view_holder_free(widget->view_holder);
widget->view_holder = NULL;
furi_record_close(RECORD_GUI);
with_view_model(
widget->view,
WidgetModel * model,
{
ComponentArray_it_t it;
ComponentArray_it(it, model->component);
while(!ComponentArray_end_p(it)) {
WidgetComponent* component = *ComponentArray_ref(it);
if(component && component->free) {
component->free(component);
}
ComponentArray_next(it);
}
ComponentArray_reset(model->component);
ComponentArray_clear(model->component);
XbmImageList_clear(model->image);
},
false);
view_free(widget->view);
widget->view = NULL;
free(widget);
}