1
mirror of https://github.com/DarkFlippers/unleashed-firmware.git synced 2025-12-12 12:42:30 +04:00

JS: Expose button event type in gui/widget button callback

by WillyJL
This commit is contained in:
MX
2025-07-30 05:04:02 +03:00
parent 610fd68b75
commit 7646902fdc
7 changed files with 72 additions and 17 deletions

View File

@@ -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 // demo selector
eventLoop.subscribe(views.demos.chosen, function (_sub, index, gui, eventLoop, views) { eventLoop.subscribe(views.demos.chosen, function (_sub, index, gui, eventLoop, views) {
if (index === 0) { if (index === 0) {
@@ -131,8 +137,8 @@ eventLoop.subscribe(gui.viewDispatcher.navigation, function (_sub, _, gui, views
}, gui, views, eventLoop); }, gui, views, eventLoop);
// go to the demo chooser screen when the right key is pressed on the widget screen // 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) { eventLoop.subscribe(views.stopwatchWidget.button, function (_sub, buttonEvent, gui, views) {
if (buttonId === "right") if (buttonEvent.key === "right" && buttonEvent.type === "short")
gui.viewDispatcher.switchTo(views.demos); gui.viewDispatcher.switchTo(views.demos);
}, gui, views); }, gui, views);

View File

@@ -10,6 +10,11 @@ typedef struct {
#define QUEUE_LEN 2 #define QUEUE_LEN 2
typedef struct {
GuiButtonType key;
InputType type;
} JsWidgetButtonEvent;
/** /**
* @brief Parses position (X and Y) from an element declaration object * @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 * @brief Widget button element callback
*/ */
static void js_widget_button_callback(GuiButtonType result, InputType type, JsWidgetCtx* context) { static void js_widget_button_callback(GuiButtonType result, InputType type, JsWidgetCtx* context) {
UNUSED(type); JsWidgetButtonEvent event = {
furi_check(furi_message_queue_put(context->queue, &result, 0) == FuriStatusOk); .key = result,
.type = type,
};
furi_check(furi_message_queue_put(context->queue, &event, 0) == FuriStatusOk);
} }
#define DESTRUCTURE_OR_RETURN(mjs, child_obj, part, ...) \ #define DESTRUCTURE_OR_RETURN(mjs, child_obj, part, ...) \
@@ -263,25 +271,44 @@ static mjs_val_t js_widget_button_event_transformer(
FuriMessageQueue* queue, FuriMessageQueue* queue,
JsWidgetCtx* context) { JsWidgetCtx* context) {
UNUSED(context); UNUSED(context);
GuiButtonType btn_type; JsWidgetButtonEvent event;
furi_check(furi_message_queue_get(queue, &btn_type, 0) == FuriStatusOk); furi_check(furi_message_queue_get(queue, &event, 0) == FuriStatusOk);
const char* btn_name; const char* event_key;
if(btn_type == GuiButtonTypeLeft) { if(event.key == GuiButtonTypeLeft) {
btn_name = "left"; event_key = "left";
} else if(btn_type == GuiButtonTypeCenter) { } else if(event.key == GuiButtonTypeCenter) {
btn_name = "center"; event_key = "center";
} else if(btn_type == GuiButtonTypeRight) { } else if(event.key == GuiButtonTypeRight) {
btn_name = "right"; event_key = "right";
} else { } else {
furi_crash(); 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) { static void* js_widget_custom_make(struct mjs* mjs, Widget* widget, mjs_val_t view_obj) {
UNUSED(widget); UNUSED(widget);
JsWidgetCtx* context = malloc(sizeof(JsWidgetCtx)); 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){ context->contract = (JsEventLoopContract){
.magic = JsForeignMagic_JsEventLoopContract, .magic = JsForeignMagic_JsEventLoopContract,
.object_type = JsEventLoopObjectTypeQueue, .object_type = JsEventLoopObjectTypeQueue,

View File

@@ -6,7 +6,7 @@
"start": "npm run build && node node_modules/@darkflippers/fz-sdk-ul/sdk.js upload" "start": "npm run build && node node_modules/@darkflippers/fz-sdk-ul/sdk.js upload"
}, },
"devDependencies": { "devDependencies": {
"@darkflippers/fz-sdk-ul": "^0.1", "@next-flip/fz-sdk-mntm": "^0.3",
"typescript": "^5.6.3" "typescript": "^5.6.3"
} }
} }

View File

@@ -58,12 +58,16 @@ type Element = StringMultilineElement
type Props = {}; type Props = {};
type Child = Element; type Child = Element;
declare class ButtonEvent {
key: "left" | "center" | "right";
type: "press" | "release" | "short" | "long" | "repeat";
}
declare class Widget extends View<Props, Child> { declare class Widget extends View<Props, Child> {
/** /**
* Event source for buttons. Only gets fired if there's a corresponding * Event source for buttons. Only gets fired if there's a corresponding
* button element. * button element.
*/ */
button: Contract<"left" | "center" | "right">; button: Contract<ButtonEvent>;
} }
declare class WidgetFactory extends ViewFactory<Props, Child, Widget> { } declare class WidgetFactory extends ViewFactory<Props, Child, Widget> { }
declare const factory: WidgetFactory; declare const factory: WidgetFactory;

View File

@@ -35,3 +35,20 @@ Elements are objects with properties to define them, in the form `{ element: "ty
| `rect` | `x` (number), `y` (number) <br> `w` (number), `h` (number) <br> `radius` (number), `fill` (boolean) | Draw a rectangle, optionally rounded and filled. | | `rect` | `x` (number), `y` (number) <br> `w` (number), `h` (number) <br> `radius` (number), `fill` (boolean) | Draw a rectangle, optionally rounded and filled. |
| `circle` | `x` (number), `y` (number) <br> `radius` (number), `fill` (boolean) | Draw a circle, optionally filled. | | `circle` | `x` (number), `y` (number) <br> `radius` (number), `fill` (boolean) | Draw a circle, optionally filled. |
| `line` | `x1` (number), `y1` (number) <br> `x2` (number), `y2` (number) | Draw a line between 2 points. | | `line` | `x1` (number), `y1` (number) <br> `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. |

View File

@@ -7,6 +7,7 @@ let serial = require("serial");
## setup() ## setup()
Configure serial port. Should be called before all other methods. Configure serial port. Should be called before all other methods.
Automatically disables Expansion module service to prevent interference.
**Parameters** **Parameters**