diff --git a/applications/system/js_app/examples/apps/Scripts/js_examples/gui.js b/applications/system/js_app/examples/apps/Scripts/js_examples/gui.js index bc63a7ef6..9b7042848 100644 --- a/applications/system/js_app/examples/apps/Scripts/js_examples/gui.js +++ b/applications/system/js_app/examples/apps/Scripts/js_examples/gui.js @@ -62,6 +62,12 @@ let views = { }), }; +// Enable illegal filename symbols since we're not choosing filenames, gives more flexibility +// Not available in all firmwares, good idea to check if it is supported +if (doesSdkSupport(["gui-textinput-illegalsymbols"])) { + views.keyboard.set("illegalSymbols", true); +} + // demo selector eventLoop.subscribe(views.demos.chosen, function (_sub, index, gui, eventLoop, views) { if (index === 0) { @@ -131,8 +137,8 @@ eventLoop.subscribe(gui.viewDispatcher.navigation, function (_sub, _, gui, views }, gui, views, eventLoop); // go to the demo chooser screen when the right key is pressed on the widget screen -eventLoop.subscribe(views.stopwatchWidget.button, function (_sub, buttonId, gui, views) { - if (buttonId === "right") +eventLoop.subscribe(views.stopwatchWidget.button, function (_sub, buttonEvent, gui, views) { + if (buttonEvent.key === "right" && buttonEvent.type === "short") gui.viewDispatcher.switchTo(views.demos); }, gui, views); diff --git a/applications/system/js_app/examples/apps/Scripts/uart_echo_8e1.js b/applications/system/js_app/examples/apps/Scripts/js_examples/uart_echo_8e1.js similarity index 100% rename from applications/system/js_app/examples/apps/Scripts/uart_echo_8e1.js rename to applications/system/js_app/examples/apps/Scripts/js_examples/uart_echo_8e1.js diff --git a/applications/system/js_app/modules/js_gui/widget.c b/applications/system/js_app/modules/js_gui/widget.c index bb2898030..47149b4ac 100644 --- a/applications/system/js_app/modules/js_gui/widget.c +++ b/applications/system/js_app/modules/js_gui/widget.c @@ -10,6 +10,11 @@ typedef struct { #define QUEUE_LEN 2 +typedef struct { + GuiButtonType key; + InputType type; +} JsWidgetButtonEvent; + /** * @brief Parses position (X and Y) from an element declaration object */ @@ -101,8 +106,11 @@ static bool element_get_text(struct mjs* mjs, mjs_val_t element, mjs_val_t* text * @brief Widget button element callback */ static void js_widget_button_callback(GuiButtonType result, InputType type, JsWidgetCtx* context) { - UNUSED(type); - furi_check(furi_message_queue_put(context->queue, &result, 0) == FuriStatusOk); + JsWidgetButtonEvent event = { + .key = result, + .type = type, + }; + furi_check(furi_message_queue_put(context->queue, &event, 0) == FuriStatusOk); } #define DESTRUCTURE_OR_RETURN(mjs, child_obj, part, ...) \ @@ -263,25 +271,44 @@ static mjs_val_t js_widget_button_event_transformer( FuriMessageQueue* queue, JsWidgetCtx* context) { UNUSED(context); - GuiButtonType btn_type; - furi_check(furi_message_queue_get(queue, &btn_type, 0) == FuriStatusOk); - const char* btn_name; - if(btn_type == GuiButtonTypeLeft) { - btn_name = "left"; - } else if(btn_type == GuiButtonTypeCenter) { - btn_name = "center"; - } else if(btn_type == GuiButtonTypeRight) { - btn_name = "right"; + JsWidgetButtonEvent event; + furi_check(furi_message_queue_get(queue, &event, 0) == FuriStatusOk); + const char* event_key; + if(event.key == GuiButtonTypeLeft) { + event_key = "left"; + } else if(event.key == GuiButtonTypeCenter) { + event_key = "center"; + } else if(event.key == GuiButtonTypeRight) { + event_key = "right"; } else { furi_crash(); } - return mjs_mk_string(mjs, btn_name, ~0, false); + const char* event_type; + if(event.type == InputTypePress) { + event_type = "press"; + } else if(event.type == InputTypeRelease) { + event_type = "release"; + } else if(event.type == InputTypeShort) { + event_type = "short"; + } else if(event.type == InputTypeLong) { + event_type = "long"; + } else if(event.type == InputTypeRepeat) { + event_type = "repeat"; + } else { + furi_crash(); + } + mjs_val_t obj = mjs_mk_object(mjs); + JS_ASSIGN_MULTI(mjs, obj) { + JS_FIELD("key", mjs_mk_string(mjs, event_key, ~0, true)); + JS_FIELD("type", mjs_mk_string(mjs, event_type, ~0, true)); + } + return obj; } static void* js_widget_custom_make(struct mjs* mjs, Widget* widget, mjs_val_t view_obj) { UNUSED(widget); JsWidgetCtx* context = malloc(sizeof(JsWidgetCtx)); - context->queue = furi_message_queue_alloc(QUEUE_LEN, sizeof(GuiButtonType)); + context->queue = furi_message_queue_alloc(QUEUE_LEN, sizeof(JsWidgetButtonEvent)); context->contract = (JsEventLoopContract){ .magic = JsForeignMagic_JsEventLoopContract, .object_type = JsEventLoopObjectTypeQueue, diff --git a/applications/system/js_app/packages/create-fz-app/template/package.json b/applications/system/js_app/packages/create-fz-app/template/package.json index 211411c73..a7f69cf22 100644 --- a/applications/system/js_app/packages/create-fz-app/template/package.json +++ b/applications/system/js_app/packages/create-fz-app/template/package.json @@ -6,7 +6,7 @@ "start": "npm run build && node node_modules/@darkflippers/fz-sdk-ul/sdk.js upload" }, "devDependencies": { - "@darkflippers/fz-sdk-ul": "^0.1", + "@next-flip/fz-sdk-mntm": "^0.3", "typescript": "^5.6.3" } } \ No newline at end of file diff --git a/applications/system/js_app/packages/fz-sdk/gui/widget.d.ts b/applications/system/js_app/packages/fz-sdk/gui/widget.d.ts index bf4aab22b..c524c53cb 100644 --- a/applications/system/js_app/packages/fz-sdk/gui/widget.d.ts +++ b/applications/system/js_app/packages/fz-sdk/gui/widget.d.ts @@ -58,12 +58,16 @@ type Element = StringMultilineElement type Props = {}; type Child = Element; +declare class ButtonEvent { + key: "left" | "center" | "right"; + type: "press" | "release" | "short" | "long" | "repeat"; +} declare class Widget extends View { /** * Event source for buttons. Only gets fired if there's a corresponding * button element. */ - button: Contract<"left" | "center" | "right">; + button: Contract; } declare class WidgetFactory extends ViewFactory { } declare const factory: WidgetFactory; diff --git a/documentation/js/js_gui__widget.md b/documentation/js/js_gui__widget.md index 9ea3e4dfa..4c922a67c 100644 --- a/documentation/js/js_gui__widget.md +++ b/documentation/js/js_gui__widget.md @@ -35,3 +35,20 @@ Elements are objects with properties to define them, in the form `{ element: "ty | `rect` | `x` (number), `y` (number)
`w` (number), `h` (number)
`radius` (number), `fill` (boolean) | Draw a rectangle, optionally rounded and filled. | | `circle` | `x` (number), `y` (number)
`radius` (number), `fill` (boolean) | Draw a circle, optionally filled. | | `line` | `x1` (number), `y1` (number)
`x2` (number), `y2` (number) | Draw a line between 2 points. | + +## Structures + +### ButtonEvent + +Button event information structure. + +**Fields** + +- key: The key that was pressed (`"left" | "center" | "right"`) +- type: The type of the event (`"press" | "release" | "short" | "long" | "repeat"`) + +## View events + +| Item | Type | Description | +|----------|--------|-----------------------------------------------------------------------------| +| `button` | `ButtonEvent`| Fires when the user presses on one of the three possible buttons if there's a corresponding button element. Refer to the `ButtonEvent` structure above for possible values. | diff --git a/documentation/js/js_serial.md b/documentation/js/js_serial.md index d06c799ac..c45ae5f74 100644 --- a/documentation/js/js_serial.md +++ b/documentation/js/js_serial.md @@ -7,6 +7,7 @@ let serial = require("serial"); ## setup() Configure serial port. Should be called before all other methods. +Automatically disables Expansion module service to prevent interference. **Parameters**