mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2025-12-12 12:42:30 +04:00
88 lines
2.9 KiB
C
88 lines
2.9 KiB
C
|
|
#include "../../js_modules.h" // IWYU pragma: keep
|
||
|
|
#include "js_gui.h"
|
||
|
|
#include "../js_event_loop/js_event_loop.h"
|
||
|
|
#include <gui/modules/submenu.h>
|
||
|
|
|
||
|
|
#define QUEUE_LEN 2
|
||
|
|
|
||
|
|
typedef struct {
|
||
|
|
FuriMessageQueue* queue;
|
||
|
|
JsEventLoopContract contract;
|
||
|
|
} JsSubmenuCtx;
|
||
|
|
|
||
|
|
static mjs_val_t choose_transformer(struct mjs* mjs, FuriMessageQueue* queue, void* context) {
|
||
|
|
UNUSED(context);
|
||
|
|
uint32_t index;
|
||
|
|
furi_check(furi_message_queue_get(queue, &index, 0) == FuriStatusOk);
|
||
|
|
return mjs_mk_number(mjs, (double)index);
|
||
|
|
}
|
||
|
|
|
||
|
|
void choose_callback(void* context, uint32_t index) {
|
||
|
|
JsSubmenuCtx* ctx = context;
|
||
|
|
furi_check(furi_message_queue_put(ctx->queue, &index, 0) == FuriStatusOk);
|
||
|
|
}
|
||
|
|
|
||
|
|
static bool
|
||
|
|
header_assign(struct mjs* mjs, Submenu* submenu, JsViewPropValue value, void* context) {
|
||
|
|
UNUSED(mjs);
|
||
|
|
UNUSED(context);
|
||
|
|
submenu_set_header(submenu, value.string);
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
static bool items_assign(struct mjs* mjs, Submenu* submenu, JsViewPropValue value, void* context) {
|
||
|
|
UNUSED(mjs);
|
||
|
|
submenu_reset(submenu);
|
||
|
|
size_t len = mjs_array_length(mjs, value.array);
|
||
|
|
for(size_t i = 0; i < len; i++) {
|
||
|
|
mjs_val_t item = mjs_array_get(mjs, value.array, i);
|
||
|
|
if(!mjs_is_string(item)) return false;
|
||
|
|
submenu_add_item(submenu, mjs_get_string(mjs, &item, NULL), i, choose_callback, context);
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
static JsSubmenuCtx* ctx_make(struct mjs* mjs, Submenu* input, mjs_val_t view_obj) {
|
||
|
|
UNUSED(input);
|
||
|
|
JsSubmenuCtx* context = malloc(sizeof(JsSubmenuCtx));
|
||
|
|
context->queue = furi_message_queue_alloc(QUEUE_LEN, sizeof(uint32_t));
|
||
|
|
context->contract = (JsEventLoopContract){
|
||
|
|
.magic = JsForeignMagic_JsEventLoopContract,
|
||
|
|
.object_type = JsEventLoopObjectTypeQueue,
|
||
|
|
.object = context->queue,
|
||
|
|
.non_timer =
|
||
|
|
{
|
||
|
|
.event = FuriEventLoopEventIn,
|
||
|
|
.transformer = (JsEventLoopTransformer)choose_transformer,
|
||
|
|
},
|
||
|
|
};
|
||
|
|
mjs_set(mjs, view_obj, "chosen", ~0, mjs_mk_foreign(mjs, &context->contract));
|
||
|
|
return context;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void ctx_destroy(Submenu* input, JsSubmenuCtx* context, FuriEventLoop* loop) {
|
||
|
|
UNUSED(input);
|
||
|
|
furi_event_loop_maybe_unsubscribe(loop, context->queue);
|
||
|
|
furi_message_queue_free(context->queue);
|
||
|
|
free(context);
|
||
|
|
}
|
||
|
|
|
||
|
|
static const JsViewDescriptor view_descriptor = {
|
||
|
|
.alloc = (JsViewAlloc)submenu_alloc,
|
||
|
|
.free = (JsViewFree)submenu_free,
|
||
|
|
.get_view = (JsViewGetView)submenu_get_view,
|
||
|
|
.custom_make = (JsViewCustomMake)ctx_make,
|
||
|
|
.custom_destroy = (JsViewCustomDestroy)ctx_destroy,
|
||
|
|
.prop_cnt = 2,
|
||
|
|
.props = {
|
||
|
|
(JsViewPropDescriptor){
|
||
|
|
.name = "header",
|
||
|
|
.type = JsViewPropTypeString,
|
||
|
|
.assign = (JsViewPropAssign)header_assign},
|
||
|
|
(JsViewPropDescriptor){
|
||
|
|
.name = "items",
|
||
|
|
.type = JsViewPropTypeArr,
|
||
|
|
.assign = (JsViewPropAssign)items_assign},
|
||
|
|
}};
|
||
|
|
JS_GUI_VIEW_DEF(submenu, &view_descriptor);
|