mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2026-05-26 11:56:12 +00:00
d0360625d6
* js: value destructuring and tests * js: temporary fix to see size impact * js_val: reduce code size 1 * i may be stupid. * test: js_value args * Revert "js: temporary fix to see size impact" This reverts commit f51d726dbafc4300d3552020de1c3b8f9ecd3ac1. * pvs: silence warnings * style: formatting * pvs: silence warnings? * pvs: silence warnings?? * js_value: redesign declaration types for less code * js: temporary fix to see size impact * style: formatting * pvs: fix helpful warnings * js_value: reduce .rodata size * pvs: fix helpful warning * js_value: reduce code size 1 * fix build error * style: format * Revert "js: temporary fix to see size impact" This reverts commit d6a46f01794132e882e03fd273dec24386a4f8ba. * style: format * js: move to new arg parser * style: format * feat: all js views done * js, toolbox: generalize string owning * toolbox: silence pvs warning --------- Co-authored-by: hedger <hedger@nanode.su> Co-authored-by: hedger <hedger@users.noreply.github.com>
275 lines
8.7 KiB
C
275 lines
8.7 KiB
C
#include "../../js_modules.h" // IWYU pragma: keep
|
|
#include "js_gui.h"
|
|
#include "../js_event_loop/js_event_loop.h"
|
|
#include <gui/modules/button_panel.h>
|
|
#include <toolbox/str_buffer.h>
|
|
|
|
typedef struct {
|
|
size_t matrix_x, matrix_y;
|
|
int32_t next_index;
|
|
StrBuffer str_buffer;
|
|
|
|
FuriMessageQueue* input_queue;
|
|
JsEventLoopContract contract;
|
|
} JsBtnPanelContext;
|
|
|
|
typedef struct {
|
|
int32_t index;
|
|
InputType input_type;
|
|
} JsBtnPanelEvent;
|
|
|
|
static const char* js_input_type_to_str(InputType type) {
|
|
switch(type) {
|
|
case InputTypePress:
|
|
return "press";
|
|
case InputTypeRelease:
|
|
return "release";
|
|
case InputTypeShort:
|
|
return "short";
|
|
case InputTypeLong:
|
|
return "long";
|
|
case InputTypeRepeat:
|
|
return "repeat";
|
|
default:
|
|
furi_crash();
|
|
}
|
|
}
|
|
|
|
static mjs_val_t
|
|
input_transformer(struct mjs* mjs, FuriMessageQueue* queue, JsBtnPanelContext* context) {
|
|
UNUSED(context);
|
|
JsBtnPanelEvent event;
|
|
furi_check(furi_message_queue_get(queue, &event, 0) == FuriStatusOk);
|
|
|
|
mjs_val_t event_obj = mjs_mk_object(mjs);
|
|
JS_ASSIGN_MULTI(mjs, event_obj) {
|
|
JS_FIELD("index", mjs_mk_number(mjs, event.index));
|
|
JS_FIELD("type", mjs_mk_string(mjs, js_input_type_to_str(event.input_type), ~0, false));
|
|
}
|
|
|
|
return event_obj;
|
|
}
|
|
|
|
static void input_callback(void* ctx, int32_t index, InputType type) {
|
|
JsBtnPanelContext* context = ctx;
|
|
JsBtnPanelEvent event = {
|
|
.index = index,
|
|
.input_type = type,
|
|
};
|
|
furi_check(furi_message_queue_put(context->input_queue, &event, 0) == FuriStatusOk);
|
|
}
|
|
|
|
static bool matrix_size_x_assign(
|
|
struct mjs* mjs,
|
|
ButtonPanel* panel,
|
|
JsViewPropValue value,
|
|
JsBtnPanelContext* context) {
|
|
UNUSED(mjs);
|
|
context->matrix_x = value.number;
|
|
button_panel_reserve(panel, context->matrix_x, context->matrix_y);
|
|
return true;
|
|
}
|
|
|
|
static bool matrix_size_y_assign(
|
|
struct mjs* mjs,
|
|
ButtonPanel* panel,
|
|
JsViewPropValue value,
|
|
JsBtnPanelContext* context) {
|
|
UNUSED(mjs);
|
|
context->matrix_y = value.number;
|
|
button_panel_reserve(panel, context->matrix_x, context->matrix_y);
|
|
return true;
|
|
}
|
|
|
|
static bool js_button_panel_add_child(
|
|
struct mjs* mjs,
|
|
ButtonPanel* panel,
|
|
JsBtnPanelContext* context,
|
|
mjs_val_t child_obj) {
|
|
typedef enum {
|
|
JsButtonPanelChildTypeButton,
|
|
JsButtonPanelChildTypeLabel,
|
|
JsButtonPanelChildTypeIcon,
|
|
} JsButtonPanelChildType;
|
|
static const JsValueEnumVariant js_button_panel_child_type_variants[] = {
|
|
{"button", JsButtonPanelChildTypeButton},
|
|
{"label", JsButtonPanelChildTypeLabel},
|
|
{"icon", JsButtonPanelChildTypeIcon},
|
|
};
|
|
static const JsValueDeclaration js_button_panel_child_type =
|
|
JS_VALUE_ENUM(JsButtonPanelChildType, js_button_panel_child_type_variants);
|
|
|
|
static const JsValueDeclaration js_button_panel_number = JS_VALUE_SIMPLE(JsValueTypeInt32);
|
|
static const JsValueObjectField js_button_panel_common_fields[] = {
|
|
{"type", &js_button_panel_child_type},
|
|
{"x", &js_button_panel_number},
|
|
{"y", &js_button_panel_number},
|
|
};
|
|
static const JsValueDeclaration js_button_panel_common =
|
|
JS_VALUE_OBJECT(js_button_panel_common_fields);
|
|
|
|
static const JsValueDeclaration js_button_panel_pointer =
|
|
JS_VALUE_SIMPLE(JsValueTypeRawPointer);
|
|
static const JsValueObjectField js_button_panel_button_fields[] = {
|
|
{"matrixX", &js_button_panel_number},
|
|
{"matrixY", &js_button_panel_number},
|
|
{"icon", &js_button_panel_pointer},
|
|
{"iconSelected", &js_button_panel_pointer},
|
|
};
|
|
static const JsValueDeclaration js_button_panel_button =
|
|
JS_VALUE_OBJECT(js_button_panel_button_fields);
|
|
|
|
static const JsValueDeclaration js_button_panel_string = JS_VALUE_SIMPLE(JsValueTypeString);
|
|
static const JsValueObjectField js_button_panel_label_fields[] = {
|
|
{"text", &js_button_panel_string},
|
|
{"font", &js_gui_font_declaration},
|
|
};
|
|
static const JsValueDeclaration js_button_panel_label =
|
|
JS_VALUE_OBJECT(js_button_panel_label_fields);
|
|
|
|
static const JsValueObjectField js_button_panel_icon_fields[] = {
|
|
{"icon", &js_button_panel_pointer},
|
|
};
|
|
static const JsValueDeclaration js_button_panel_icon =
|
|
JS_VALUE_OBJECT(js_button_panel_icon_fields);
|
|
|
|
JsButtonPanelChildType child_type;
|
|
int32_t x, y;
|
|
JsValueParseStatus status;
|
|
JS_VALUE_PARSE(
|
|
mjs,
|
|
JS_VALUE_PARSE_SOURCE_VALUE(&js_button_panel_common),
|
|
JsValueParseFlagReturnOnError,
|
|
&status,
|
|
&child_obj,
|
|
&child_type,
|
|
&x,
|
|
&y);
|
|
if(status != JsValueParseStatusOk) return false;
|
|
|
|
switch(child_type) {
|
|
case JsButtonPanelChildTypeButton: {
|
|
int32_t matrix_x, matrix_y;
|
|
const Icon *icon, *icon_selected;
|
|
JS_VALUE_PARSE(
|
|
mjs,
|
|
JS_VALUE_PARSE_SOURCE_VALUE(&js_button_panel_button),
|
|
JsValueParseFlagReturnOnError,
|
|
&status,
|
|
&child_obj,
|
|
&matrix_x,
|
|
&matrix_y,
|
|
&icon,
|
|
&icon_selected);
|
|
if(status != JsValueParseStatusOk) return false;
|
|
button_panel_add_item(
|
|
panel,
|
|
context->next_index++,
|
|
matrix_x,
|
|
matrix_y,
|
|
x,
|
|
y,
|
|
icon,
|
|
icon_selected,
|
|
(ButtonItemCallback)input_callback,
|
|
context);
|
|
break;
|
|
}
|
|
|
|
case JsButtonPanelChildTypeLabel: {
|
|
const char* text;
|
|
Font font;
|
|
JS_VALUE_PARSE(
|
|
mjs,
|
|
JS_VALUE_PARSE_SOURCE_VALUE(&js_button_panel_label),
|
|
JsValueParseFlagReturnOnError,
|
|
&status,
|
|
&child_obj,
|
|
&text,
|
|
&font);
|
|
if(status != JsValueParseStatusOk) return false;
|
|
button_panel_add_label(
|
|
panel, x, y, font, str_buffer_make_owned_clone(&context->str_buffer, text));
|
|
break;
|
|
}
|
|
|
|
case JsButtonPanelChildTypeIcon: {
|
|
const Icon* icon;
|
|
JS_VALUE_PARSE(
|
|
mjs,
|
|
JS_VALUE_PARSE_SOURCE_VALUE(&js_button_panel_icon),
|
|
JsValueParseFlagReturnOnError,
|
|
&status,
|
|
&child_obj,
|
|
&icon);
|
|
if(status != JsValueParseStatusOk) return false;
|
|
button_panel_add_icon(panel, x, y, icon);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void js_button_panel_reset_children(ButtonPanel* panel, JsBtnPanelContext* context) {
|
|
context->next_index = 0;
|
|
button_panel_reset(panel);
|
|
button_panel_reserve(panel, context->matrix_x, context->matrix_y);
|
|
str_buffer_clear_all_clones(&context->str_buffer);
|
|
}
|
|
|
|
static JsBtnPanelContext* ctx_make(struct mjs* mjs, ButtonPanel* panel, mjs_val_t view_obj) {
|
|
UNUSED(panel);
|
|
JsBtnPanelContext* context = malloc(sizeof(JsBtnPanelContext));
|
|
*context = (JsBtnPanelContext){
|
|
.matrix_x = 1,
|
|
.matrix_y = 1,
|
|
.next_index = 0,
|
|
.str_buffer = {0},
|
|
.input_queue = furi_message_queue_alloc(1, sizeof(JsBtnPanelEvent)),
|
|
};
|
|
context->contract = (JsEventLoopContract){
|
|
.magic = JsForeignMagic_JsEventLoopContract,
|
|
.object_type = JsEventLoopObjectTypeQueue,
|
|
.object = context->input_queue,
|
|
.non_timer =
|
|
{
|
|
.event = FuriEventLoopEventIn,
|
|
.transformer = (JsEventLoopTransformer)input_transformer,
|
|
.transformer_context = context,
|
|
},
|
|
};
|
|
mjs_set(mjs, view_obj, "input", ~0, mjs_mk_foreign(mjs, &context->contract));
|
|
return context;
|
|
}
|
|
|
|
static void ctx_destroy(ButtonPanel* input, JsBtnPanelContext* context, FuriEventLoop* loop) {
|
|
UNUSED(input);
|
|
furi_event_loop_maybe_unsubscribe(loop, context->input_queue);
|
|
furi_message_queue_free(context->input_queue);
|
|
str_buffer_clear_all_clones(&context->str_buffer);
|
|
free(context);
|
|
}
|
|
|
|
static const JsViewDescriptor view_descriptor = {
|
|
.alloc = (JsViewAlloc)button_panel_alloc,
|
|
.free = (JsViewFree)button_panel_free,
|
|
.get_view = (JsViewGetView)button_panel_get_view,
|
|
.custom_make = (JsViewCustomMake)ctx_make,
|
|
.custom_destroy = (JsViewCustomDestroy)ctx_destroy,
|
|
.add_child = (JsViewAddChild)js_button_panel_add_child,
|
|
.reset_children = (JsViewResetChildren)js_button_panel_reset_children,
|
|
.prop_cnt = 2,
|
|
.props = {
|
|
(JsViewPropDescriptor){
|
|
.name = "matrixSizeX",
|
|
.type = JsViewPropTypeNumber,
|
|
.assign = (JsViewPropAssign)matrix_size_x_assign},
|
|
(JsViewPropDescriptor){
|
|
.name = "matrixSizeY",
|
|
.type = JsViewPropTypeNumber,
|
|
.assign = (JsViewPropAssign)matrix_size_y_assign},
|
|
}};
|
|
|
|
JS_GUI_VIEW_DEF(button_panel, &view_descriptor);
|