[FL-3925, FL-3942, FL-3944] JS features & bugfixes (SDK 0.2) (#4075)

* feat: JS GPIO PWM, JS GUI Widget view; fix: JS EvtLoop stop on request, JS EvtLoop stop on error
* fix: f18 build
* docs: widget
* fix: js unit test
* change feature naming

Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
Anna Antonenko
2025-02-13 12:50:38 +04:00
committed by GitHub
parent ac1b723436
commit e27f82f041
33 changed files with 858 additions and 104 deletions

View File

@@ -33,9 +33,10 @@ type Props = {
length: number,
defaultData: Uint8Array | ArrayBuffer,
}
declare class ByteInput extends View<Props> {
type Child = never;
declare class ByteInput extends View<Props, Child> {
input: Contract<string>;
}
declare class ByteInputFactory extends ViewFactory<Props, ByteInput> { }
declare class ByteInputFactory extends ViewFactory<Props, Child, ByteInput> { }
declare const factory: ByteInputFactory;
export = factory;

View File

@@ -37,9 +37,10 @@ type Props = {
center: string,
right: string,
}
declare class Dialog extends View<Props> {
type Child = never;
declare class Dialog extends View<Props, Child> {
input: Contract<"left" | "center" | "right">;
}
declare class DialogFactory extends ViewFactory<Props, Dialog> { }
declare class DialogFactory extends ViewFactory<Props, Child, Dialog> { }
declare const factory: DialogFactory;
export = factory;

View File

@@ -26,7 +26,8 @@
import type { View, ViewFactory } from ".";
type Props = {};
declare class EmptyScreen extends View<Props> { }
declare class EmptyScreenFactory extends ViewFactory<Props, EmptyScreen> { }
type Child = never;
declare class EmptyScreen extends View<Props, Child> { }
declare class EmptyScreenFactory extends ViewFactory<Props, Child, EmptyScreen> { }
declare const factory: EmptyScreenFactory;
export = factory;

View File

@@ -0,0 +1,11 @@
export type BuiltinIcon = "DolphinWait_59x54" | "js_script_10px";
export type IconData = symbol & { "__tag__": "icon" };
// introducing a nominal type in a hacky way; the `__tag__` property doesn't really exist.
/**
* Gets a built-in firmware icon for use in GUI
* @param icon Name of the icon
* @version Added in JS SDK 0.2, extra feature `"gui-widget"`
*/
export declare function getBuiltin(icon: BuiltinIcon): IconData;

View File

@@ -26,23 +26,23 @@
* assumes control over the entire viewport and all input events. Different
* types of views are available (not all of which are unfortunately currently
* implemented in JS):
* | View | Has JS adapter? |
* |----------------------|------------------|
* | `button_menu` | ❌ |
* | `button_panel` | ❌ |
* | `byte_input` | ✅ |
* | `dialog_ex` | ✅ (as `dialog`) |
* | `empty_screen` | ✅ |
* | `file_browser` | |
* | `loading` | ✅ |
* | `menu` | ❌ |
* | `number_input` | ❌ |
* | `popup` | ❌ |
* | `submenu` | ✅ |
* | `text_box` | ✅ |
* | `text_input` | ✅ |
* | `variable_item_list` | ❌ |
* | `widget` | |
* | View | Has JS adapter? |
* |----------------------|-----------------------|
* | `button_menu` | ❌ |
* | `button_panel` | ❌ |
* | `byte_input` | ✅ |
* | `dialog_ex` | ✅ (as `dialog`) |
* | `empty_screen` | ✅ |
* | `file_browser` | ✅ (as `file_picker`) |
* | `loading` | ✅ |
* | `menu` | ❌ |
* | `number_input` | ❌ |
* | `popup` | ❌ |
* | `submenu` | ✅ |
* | `text_box` | ✅ |
* | `text_input` | ✅ |
* | `variable_item_list` | ❌ |
* | `widget` | |
*
* In JS, each view has its own set of properties (or just "props"). The
* programmer can manipulate these properties in two ways:
@@ -121,7 +121,7 @@ import type { Contract } from "../event_loop";
type Properties = { [K: string]: any };
export declare class View<Props extends Properties> {
export declare class View<Props extends Properties, Child> {
/**
* Assign value to property by name
* @param property Name of the property
@@ -129,9 +129,26 @@ export declare class View<Props extends Properties> {
* @version Added in JS SDK 0.1
*/
set<P extends keyof Props>(property: P, value: Props[P]): void;
/**
* Adds a child to the View
* @param child Child to add
* @version Added in JS SDK 0.2, extra feature `"gui-widget"`
*/
addChild<C extends Child>(child: C): void;
/**
* Removes all children from the View
* @version Added in JS SDK 0.2, extra feature `"gui-widget"`
*/
resetChildren(): void;
/**
* Removes all previous children from the View and assigns new children
* @param children The list of children to assign
* @version Added in JS SDK 0.2, extra feature `"gui-widget"`
*/
setChildren(children: Child[]): void;
}
export declare class ViewFactory<Props extends Properties, V extends View<Props>> {
export declare class ViewFactory<Props extends Properties, Child, V extends View<Props, Child>> {
/**
* Create view instance with default values, can be changed later with set()
* @version Added in JS SDK 0.1
@@ -140,9 +157,10 @@ export declare class ViewFactory<Props extends Properties, V extends View<Props>
/**
* Create view instance with custom values, can be changed later with set()
* @param initial Dictionary of property names to values
* @version Added in JS SDK 0.1
* @param children Optional list of children to add to the view
* @version Added in JS SDK 0.1; amended in JS SDK 0.2, extra feature `"gui-widget"`
*/
makeWith(initial: Partial<Props>): V;
makeWith(initial: Partial<Props>, children?: Child[]): V;
}
/**
@@ -163,7 +181,7 @@ declare class ViewDispatcher {
* View object currently shown
* @version Added in JS SDK 0.1
*/
currentView: View<any>;
currentView: View<any, any>;
/**
* Sends a number to the custom event handler
* @param event number to send
@@ -175,7 +193,7 @@ declare class ViewDispatcher {
* @param assoc View-ViewDispatcher association as returned by `add`
* @version Added in JS SDK 0.1
*/
switchTo(assoc: View<any>): void;
switchTo(assoc: View<any, any>): void;
/**
* Sends this ViewDispatcher to the front or back, above or below all other
* GUI viewports

View File

@@ -27,7 +27,8 @@
import type { View, ViewFactory } from ".";
type Props = {};
declare class Loading extends View<Props> { }
declare class LoadingFactory extends ViewFactory<Props, Loading> { }
type Child = never;
declare class Loading extends View<Props, Child> { }
declare class LoadingFactory extends ViewFactory<Props, Child, Loading> { }
declare const factory: LoadingFactory;
export = factory;

View File

@@ -31,9 +31,10 @@ type Props = {
header: string,
items: string[],
};
declare class Submenu extends View<Props> {
type Child = never;
declare class Submenu extends View<Props, Child> {
chosen: Contract<number>;
}
declare class SubmenuFactory extends ViewFactory<Props, Submenu> { }
declare class SubmenuFactory extends ViewFactory<Props, Child, Submenu> { }
declare const factory: SubmenuFactory;
export = factory;

View File

@@ -33,9 +33,10 @@ type Props = {
font: "text" | "hex",
focus: "start" | "end",
}
declare class TextBox extends View<Props> {
type Child = never;
declare class TextBox extends View<Props, Child> {
chosen: Contract<number>;
}
declare class TextBoxFactory extends ViewFactory<Props, TextBox> { }
declare class TextBoxFactory extends ViewFactory<Props, Child, TextBox> { }
declare const factory: TextBoxFactory;
export = factory;

View File

@@ -37,9 +37,10 @@ type Props = {
defaultText: string,
defaultTextClear: boolean,
}
declare class TextInput extends View<Props> {
type Child = never;
declare class TextInput extends View<Props, Child> {
input: Contract<string>;
}
declare class TextInputFactory extends ViewFactory<Props, TextInput> { }
declare class TextInputFactory extends ViewFactory<Props, Child, TextInput> { }
declare const factory: TextInputFactory;
export = factory;

View File

@@ -0,0 +1,66 @@
/**
* Displays a combination of custom elements on one screen.
*
* <img src="../images/widget.png" width="200" alt="Sample screenshot of the view" />
*
* ```js
* let eventLoop = require("event_loop");
* let gui = require("gui");
* let emptyView = require("gui/widget");
* ```
*
* This module depends on the `gui` module, which in turn depends on the
* `event_loop` module, so they _must_ be imported in this order. It is also
* recommended to conceptualize these modules first before using this one.
*
* # Example
* For an example refer to the GUI example.
*
* # View props
* This view does not have any props.
*
* # Children
* This view has the elements as its children.
*
* @version Added in JS SDK 0.2, extra feature `"gui-widget"`
* @module
*/
import type { View, ViewFactory } from ".";
import type { IconData } from "./icon";
import type { Contract } from "../event_loop";
type Position = { x: number, y: number };
type Size = { w: number, h: number };
type Alignment = { align: `${"t" | "c" | "b"}${"l" | "m" | "r"}` };
type Font = { font: "primary" | "secondary" | "keyboard" | "big_numbers" };
type Text = { text: string };
type StringMultilineElement = { element: "string_multiline" } & Position & Alignment & Font & Text;
type StringElement = { element: "string" } & Position & Alignment & Font & Text;
type TextBoxElement = { element: "text_box", stripToDots: boolean } & Position & Size & Alignment & Text;
type TextScrollElement = { element: "text_scroll" } & Position & Size & Text;
type ButtonElement = { element: "button", button: "left" | "center" | "right" } & Text;
type IconElement = { element: "icon", iconData: IconData } & Position;
type FrameElement = { element: "frame", radius: number } & Position & Size;
type Element = StringMultilineElement
| StringElement
| TextBoxElement
| TextScrollElement
| ButtonElement
| IconElement
| FrameElement;
type Props = {};
type Child = Element;
declare class Widget extends View<Props, Child> {
/**
* Event source for buttons. Only gets fired if there's a corresponding
* button element.
*/
button: Contract<"left" | "center" | "right">;
}
declare class WidgetFactory extends ViewFactory<Props, Child, Widget> { }
declare const factory: WidgetFactory;
export = factory;