Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
20294a8
js: value destructuring and tests
portasynthinca3 Mar 3, 2025
f51d726
js: temporary fix to see size impact
portasynthinca3 Mar 3, 2025
ca0fc5f
js_val: reduce code size 1
portasynthinca3 Mar 4, 2025
74700ce
i may be stupid.
portasynthinca3 Mar 4, 2025
cca650e
test: js_value args
portasynthinca3 Mar 4, 2025
11b82c0
Revert "js: temporary fix to see size impact"
portasynthinca3 Mar 4, 2025
4a69443
pvs: silence warnings
portasynthinca3 Mar 4, 2025
35d500d
style: formatting
portasynthinca3 Mar 4, 2025
4d9d1c6
pvs: silence warnings?
portasynthinca3 Mar 4, 2025
1c3f2e3
pvs: silence warnings??
portasynthinca3 Mar 4, 2025
e59eed4
js_value: redesign declaration types for less code
portasynthinca3 Mar 5, 2025
d6a46f0
js: temporary fix to see size impact
portasynthinca3 Mar 5, 2025
541ea2a
style: formatting
portasynthinca3 Mar 5, 2025
245800e
pvs: fix helpful warnings
portasynthinca3 Mar 5, 2025
e1f7a78
js_value: reduce .rodata size
portasynthinca3 Mar 5, 2025
7a9b0a9
pvs: fix helpful warning
portasynthinca3 Mar 5, 2025
bccc4f4
js_value: reduce code size 1
portasynthinca3 Mar 5, 2025
6b6ea5d
fix build error
portasynthinca3 Mar 5, 2025
b5a1f95
style: format
portasynthinca3 Mar 5, 2025
eb075f7
Revert "js: temporary fix to see size impact"
portasynthinca3 Mar 5, 2025
544193e
style: format
portasynthinca3 Mar 5, 2025
49e5649
js: move to new arg parser
portasynthinca3 Mar 5, 2025
db899d4
style: format
portasynthinca3 Mar 5, 2025
f7f6856
feat: all js views done
portasynthinca3 Mar 20, 2025
ff35eaf
js, toolbox: generalize string owning
portasynthinca3 Apr 29, 2025
a78f92e
Merge branch 'dev' into porta/3925-js-views-finale
portasynthinca3 Apr 29, 2025
9310660
toolbox: silence pvs warning
portasynthinca3 Apr 29, 2025
93800e6
Merge remote-tracking branch 'origin/dev' into porta/3925-js-views-fi…
hedger Sep 24, 2025
4366c8a
Merge branch 'dev' into porta/3925-js-views-finale
hedger Sep 24, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions applications/services/gui/modules/popup.c
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,12 @@ static bool popup_view_input_callback(InputEvent* event, void* context) {
void popup_start_timer(void* context) {
Popup* popup = context;
if(popup->timer_enabled) {
uint32_t timer_period =
popup->timer_period_in_ms / (1000.0f / furi_kernel_get_tick_frequency());
uint32_t timer_period = furi_ms_to_ticks(popup->timer_period_in_ms);
if(timer_period == 0) timer_period = 1;

if(furi_timer_start(popup->timer, timer_period) != FuriStatusOk) {
furi_crash();
};
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion applications/services/gui/modules/popup.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ void popup_free(Popup* popup);
*/
View* popup_get_view(Popup* popup);

/** Set popup header text
/** Set popup timeout callback
*
* @param popup Popup instance
* @param callback PopupCallback
Expand Down
48 changes: 48 additions & 0 deletions applications/system/js_app/application.fam
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,54 @@ App(
sources=["modules/js_gui/text_input.c"],
)

App(
appid="js_gui__number_input",
apptype=FlipperAppType.PLUGIN,
entry_point="js_view_number_input_ep",
requires=["js_app"],
sources=["modules/js_gui/number_input.c"],
)

App(
appid="js_gui__button_panel",
apptype=FlipperAppType.PLUGIN,
entry_point="js_view_button_panel_ep",
requires=["js_app"],
sources=["modules/js_gui/button_panel.c"],
)

App(
appid="js_gui__popup",
apptype=FlipperAppType.PLUGIN,
entry_point="js_view_popup_ep",
requires=["js_app"],
sources=["modules/js_gui/popup.c"],
)

App(
appid="js_gui__button_menu",
apptype=FlipperAppType.PLUGIN,
entry_point="js_view_button_menu_ep",
requires=["js_app"],
sources=["modules/js_gui/button_menu.c"],
)

App(
appid="js_gui__menu",
apptype=FlipperAppType.PLUGIN,
entry_point="js_view_menu_ep",
requires=["js_app"],
sources=["modules/js_gui/menu.c"],
)

App(
appid="js_gui__vi_list",
apptype=FlipperAppType.PLUGIN,
entry_point="js_view_vi_list_ep",
requires=["js_app"],
sources=["modules/js_gui/vi_list.c"],
)

App(
appid="js_gui__byte_input",
apptype=FlipperAppType.PLUGIN,
Expand Down
120 changes: 109 additions & 11 deletions applications/system/js_app/examples/apps/Scripts/gui.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ let byteInputView = require("gui/byte_input");
let textBoxView = require("gui/text_box");
let dialogView = require("gui/dialog");
let filePicker = require("gui/file_picker");
let buttonMenuView = require("gui/button_menu");
let buttonPanelView = require("gui/button_panel");
let menuView = require("gui/menu");
let numberInputView = require("gui/number_input");
let popupView = require("gui/popup");
let viListView = require("gui/vi_list");
let widget = require("gui/widget");
let icon = require("gui/icon");
let flipper = require("flipper");
Expand All @@ -27,6 +33,11 @@ let stopwatchWidgetElements = [
{ element: "button", button: "right", text: "Back" },
];

// icons for the button panel
let offIcons = [icon.getBuiltin("off_19x20"), icon.getBuiltin("off_hover_19x20")];
let powerIcons = [icon.getBuiltin("power_19x20"), icon.getBuiltin("power_hover_19x20")];
let settingsIcon = icon.getBuiltin("Settings_14");

// declare view instances
let views = {
loading: loadingView.make(),
Expand All @@ -48,19 +59,57 @@ let views = {
text: "This is a very long string that demonstrates the TextBox view. Use the D-Pad to scroll backwards and forwards.\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse rhoncus est malesuada quam egestas ultrices. Maecenas non eros a nulla eleifend vulputate et ut risus. Quisque in mauris mattis, venenatis risus eget, aliquam diam. Fusce pretium feugiat mauris, ut faucibus ex volutpat in. Phasellus volutpat ex sed gravida consectetur. Aliquam sed lectus feugiat, tristique lectus et, bibendum lacus. Ut sit amet augue eu sapien elementum aliquam quis vitae tortor. Vestibulum quis commodo odio. In elementum fermentum massa, eu pellentesque nibh cursus at. Integer eleifend lacus nec purus elementum sodales. Nulla elementum neque urna, non vulputate massa semper sed. Fusce ut nisi vitae dui blandit congue pretium vitae turpis.",
}),
stopwatchWidget: widget.makeWith({}, stopwatchWidgetElements),
buttonMenu: buttonMenuView.makeWith({
header: "Header"
}, [
{ type: "common", label: "Test" },
{ type: "control", label: "Test2" },
]),
buttonPanel: buttonPanelView.makeWith({
matrixSizeX: 2,
matrixSizeY: 2,
}, [
{ type: "button", x: 0, y: 0, matrixX: 0, matrixY: 0, icon: offIcons[0], iconSelected: offIcons[1] },
{ type: "button", x: 30, y: 30, matrixX: 1, matrixY: 1, icon: powerIcons[0], iconSelected: powerIcons[1] },
{ type: "label", x: 0, y: 50, text: "Label", font: "primary" },
]),
menu: menuView.makeWith({}, [
{ label: "One", icon: settingsIcon },
{ label: "Two", icon: settingsIcon },
{ label: "three", icon: settingsIcon },
]),
numberKbd: numberInputView.makeWith({
header: "Number input",
defaultValue: 100,
minValue: 0,
maxValue: 200,
}),
popup: popupView.makeWith({
header: "Hello",
text: "I'm going to be gone\nin 2 seconds",
}),
viList: viListView.makeWith({}, [
{ label: "One", variants: ["1", "1.0"] },
{ label: "Two", variants: ["2", "2.0"] },
]),
demos: submenuView.makeWith({
header: "Choose a demo",
items: [
"Hourglass screen",
"Empty screen",
"Text input & Dialog",
"Byte input",
"Text box",
"File picker",
"Widget",
"Exit app",
],
}),
}, [
"Hourglass screen",
"Empty screen",
"Text input & Dialog",
"Byte input",
"Text box",
"File picker",
"Widget",
"Button menu",
"Button panel",
"Menu",
"Number input",
"Popup",
"Var. item list",
"Exit app",
]),
};

// demo selector
Expand Down Expand Up @@ -92,6 +141,19 @@ eventLoop.subscribe(views.demos.chosen, function (_sub, index, gui, eventLoop, v
} else if (index === 6) {
gui.viewDispatcher.switchTo(views.stopwatchWidget);
} else if (index === 7) {
gui.viewDispatcher.switchTo(views.buttonMenu);
} else if (index === 8) {
gui.viewDispatcher.switchTo(views.buttonPanel);
} else if (index === 9) {
gui.viewDispatcher.switchTo(views.menu);
} else if (index === 10) {
gui.viewDispatcher.switchTo(views.numberKbd);
} else if (index === 11) {
views.popup.set("timeout", 2000);
gui.viewDispatcher.switchTo(views.popup);
} else if (index === 12) {
gui.viewDispatcher.switchTo(views.viList);
} else if (index === 13) {
eventLoop.stop();
}
}, gui, eventLoop, views);
Expand Down Expand Up @@ -156,6 +218,42 @@ eventLoop.subscribe(eventLoop.timer("periodic", 500), function (_sub, _item, vie
return [views, stopwatchWidgetElements, halfSeconds];
}, views, stopwatchWidgetElements, 0);

// go back after popup times out
eventLoop.subscribe(views.popup.timeout, function (_sub, _item, gui, views) {
gui.viewDispatcher.switchTo(views.demos);
}, gui, views);

// button menu callback
eventLoop.subscribe(views.buttonMenu.input, function (_sub, input, gui, views) {
views.helloDialog.set("text", "You selected #" + input.index.toString());
views.helloDialog.set("center", "Cool!");
gui.viewDispatcher.switchTo(views.helloDialog);
}, gui, views);

// button panel callback
eventLoop.subscribe(views.buttonPanel.input, function (_sub, input, gui, views) {
views.helloDialog.set("text", "You selected #" + input.index.toString());
views.helloDialog.set("center", "Cool!");
gui.viewDispatcher.switchTo(views.helloDialog);
}, gui, views);

// menu callback
eventLoop.subscribe(views.menu.chosen, function (_sub, index, gui, views) {
views.helloDialog.set("text", "You selected #" + index.toString());
views.helloDialog.set("center", "Cool!");
gui.viewDispatcher.switchTo(views.helloDialog);
}, gui, views);

// menu callback
eventLoop.subscribe(views.numberKbd.input, function (_sub, number, gui, views) {
views.helloDialog.set("text", "You typed " + number.toString());
views.helloDialog.set("center", "Cool!");
gui.viewDispatcher.switchTo(views.helloDialog);
}, gui, views);

// ignore VI list
eventLoop.subscribe(views.viList.valueUpdate, function (_sub, _item) {});

// run UI
gui.viewDispatcher.switchTo(views.demos);
eventLoop.run();
Loading
Loading