Bauen
Ihr erster Screen
Einen Screen in TSX schreiben, prüfen und umwandeln, ihn in QEMU und auf einem Raspberry Pi 5 sehen und ihn dann aus Rust steuern: seine Aktionen, Felder, Slots und Gruppen.
Schreiben Sie einen Screen, wie Sie es in React tun würden. Der Screen Compiler prüft ihn gegen seine Teilmenge und wandelt ihn einmal in Rust um, zur Build-Zeit, auf Ihrem Rechner. Auf das Gerät gelangt dieses Rust, eingebaut in das Systemabbild: kein JavaScript, kein Browser, kein Interpreter. Diese Anleitung führt einen Screen von einer TSX-Datei über QEMU bis auf einen Raspberry Pi 5 und zeigt dann, wie eine Anwendung einen Screen steuert. Screens aus React listet alles auf, was umgewandelt wird.
Einrichten
Klonen Sie NoWayOS, den Screen Compiler und den Showcase nebeneinander und bauen Sie einmal den Entwicklungscontainer. Jedes make-Ziel läuft darin, sodass auf Ihrem Rechner nichts weiter installiert wird; Erste Schritte sagt, was der Container braucht.
mkdir nowayos-work && cd nowayos-work
git clone https://github.com/NoWayOS/nowayos.git
git clone https://github.com/NoWayOS/screen-compiler.git
git clone https://github.com/NoWayOS/showcase.git
# the NoWayOS the Showcase builds on
git -C nowayos checkout $(cat showcase/nowayos.rev)
make -C nowayos container
Der Showcase ist die Demonstration, in die ein Pi bootet. Seine React gallery zeigt jede Komponente, die der Screen Compiler umwandelt, einen Screen nach dem anderen, und Ihr erster Screen kommt hinzu.
Einen Screen schreiben
Speichern Sie dies als showcase/showcase/showcase-app/tsx/gallery-hello.tsx:
import { Button, Column, HeaderBar, Screen, Spacer, Text } from "nowayos:kit";
type HelloProps = {
onHome: () => void;
onBack: () => void;
onNext: () => void;
onWave: () => void;
};
export function GalleryHelloScreen(props: HelloProps) {
return (
<Screen>
<Column gap={12} padding={16}>
<HeaderBar gap={8} padding={8}>
<Button variant="secondary" onPress={props.onHome}>Home</Button>
<Text role="heading">Hello</Text>
<Spacer />
<Button variant="secondary" onPress={props.onBack}>Back</Button>
<Button onPress={props.onNext}>Next</Button>
</HeaderBar>
<Text role="title">Hello from TSX</Text>
<Text>Written in React's syntax, compiled to Rust at build time.</Text>
<Button onClick={() => props.onWave()}>Wave</Button>
</Column>
</Screen>
);
}
Jede Callback-Prop wird zu einer Aktion, nummeriert ab 1 in der Reihenfolge, in der der Screen sie verwendet. Jeder Screen der Galerie beginnt mit derselben Leiste, Home, Back und Next, sodass der Showcase diese drei auf jedem Screen gleich behandelt; onWave gehört nur diesem Screen, und hier behandelt sie nichts.
Prüfen
cd screen-compiler
make run ARGS="check ../showcase/showcase/showcase-app/tsx/gallery-hello.tsx"
Ein Screen, der umgewandelt wird, erhält eine Zeile zurück:
../showcase/showcase/showcase-app/tsx/gallery-hello.tsx: supported (1 screen, 10 elements, 0 design tokens, 4 actions)
Alles außerhalb der Teilmenge wird dort abgelehnt, wo es steht, mit dem, was stattdessen zu schreiben ist, und nichts wird umgewandelt. Geben Sie der Spalte padding="1rem" und dem Button ein onClick, das etwas protokolliert, und die Prüfung meldet:
../showcase/showcase/showcase-app/tsx/gallery-hello.tsx: not supported
../showcase/showcase/showcase-app/tsx/gallery-hello.tsx:13:32: error[unsupported]: `padding` in `rem` is not supported: lengths are layout units, written in px
help: write padding="16px", padding={16} or a design token
../showcase/showcase/showcase-app/tsx/gallery-hello.tsx:23:26: error[unsupported]: a function here may only call a callback prop, with no arguments
help: pass a callback prop of the component: onClick={onContinue}, or onClick={() => props.onContinue()}
../showcase/showcase/showcase-app/tsx/gallery-hello.tsx: 2 errors
In die Galerie aufnehmen
cd ../showcase
make screens-bless
wandelt jeden Screen des Showcase mit dem Screen Compiler daneben um und schreibt das Rust nach showcase/showcase-app/src/generated/: gallery-hello.rs und eine Funktion, die ihn baut, generated::build_gallery_hello. Fügen Sie Ihren Screen dann in showcase/showcase-app/src/lib.rs am Ende der Galerie hinzu:
pub const GALLERY: [(&str, generated::Build); 12] = [
// ... the gallery's own eleven screens ...
("gallery-hello", generated::build_gallery_hello),
];
und in der Liste direkt darunter, deren Screens auf ihre Leiste geprüft werden: gallery_hello::gallery_hello_screen, mit gallery_hello im use dieser Liste. Die Prüfung läuft, während der Showcase kompiliert wird, sodass eine Leiste in falscher Reihenfolge den Build stoppt.
In QEMU ansehen
make qemu-test SYSTEM=showcase-gallery
spielt die vorgegebene Runde durch die Galerie ab und vergleicht jeden ihrer eigenen Screens Pixel für Pixel mit seinem abgenommenen Bild: Der Rest der Galerie wird weiterhin wie abgenommen gezeichnet. Um Ihren zu sehen:
make qemu SYSTEM=showcase-gallery VNC=1
Der Bildschirm von QEMU wird per VNC auf localhost:5900 Ihres Rechners bereitgestellt, mit einem Passwort, das nur für diesen Lauf ausgegeben wird; öffnen Sie unter macOS vnc://localhost:5900. Was Sie im Terminal tippen, erreicht den Showcase: Pfeil nach unten, Pfeil nach rechts und Enter öffnen die React gallery, dann drücken Shift-Tab und Enter Back, das vom ersten Screen zum letzten weiterläuft, Ihrem. Beenden Sie QEMU mit Ctrl-A, dann x.
Auf dem Pi ansehen
Richten Sie das Board einmal so ein, wie Betrieb auf einem Raspberry Pi 5 es zeigt: seine Karte, die Debug Probe, ein vor dem Einschalten angeschlossener Monitor und eine USB-Maus. Führen Sie dann aus showcase/ heraus aus, auf Ihrem Rechner statt im Container:
make pi-show PI_DEVICE=/dev/cu.usbmodemXXXX SYSTEM=showcase
Das baut den Showcase mit Ihrem Screen darin, sendet ihn über die Probe an das Board und lässt ihn laufen, bis das Board aus- und wieder eingeschaltet wird. Klicken Sie auf React gallery, dann auf Back: Ihr Screen, auf dem HDMI-Monitor des Pi.
Einen Screen aus Rust steuern
Ein Screen teilt der Anwendung mit, was geschehen ist, und die Anwendung teilt dem Screen mit, was er zeigen soll, auf vier Wegen: Aktionen, Felder, Slots und Gruppen. Die Fixture fixtures/visit-booking.tsx des Screen Compilers nutzt alle vier:
type VisitBookingProps = {
phoneError: string;
onBack: () => void;
onBook: () => void;
};
// ...
<TextInput id="name" placeholder="Your name" maxLength={NAME_LEN} />
{props.phoneError && <Text>{props.phoneError}</Text>}
<RadioGroup id="when" options={["Morning", "Afternoon", "Evening"]} />
<Checkbox id="remind" label="Remind me the day before" />
<Button id="book" onPress={props.onBook}>Book</Button>
Ihr Rust benennt jeden davon, nummeriert ab 1 in der Reihenfolge, in der der Screen sie schreibt:
| Im TSX | Im Rust | Die Anwendung |
|---|---|---|
Eine Callback-Prop, onBook |
Eine Aktion, actions::BOOK |
erfährt, wenn der Button gedrückt wird |
Ein Bedienelement mit einer id, id="name" |
Ein Feld, fields::NAME |
liest, was eingegeben wurde |
Eine als Text geschriebene Prop, {props.phoneError} |
Ein Slot, slots::PHONE_ERROR |
setzt den Text |
Eine Prop, die entscheidet, was erscheint, {props.phoneError && …} |
Eine Gruppe, groups::PHONE_ERROR |
zeigt oder verbirgt sie |
Die Anwendung baut den Screen in einen UI-Baum von NoWayOS, setzt seine Slots und Gruppen und reicht dem Baum jedes Eingabeereignis weiter. Ein Tippen auf Book kommt als Aktion von Book zurück, und dann liest die Anwendung die Felder:
let mut tree = Tree::<64>::new();
let screen = visit_booking::build(&mut tree)?;
tree.set_root(screen.root)?;
// A slot is text the application sets; a group is content it shows or hides.
tree.set_slot(slots::PHONE_ERROR, "Digits only, please")?;
tree.set_visible(groups::PHONE_ERROR, true)?;
// Every input event goes to the tree; a tap on Book comes back as its action.
match tree.handle(event) {
Some(UiEvent::Action(actions::BOOK)) => {
let name = tree.value(fields::NAME).unwrap_or("");
let when = tree.selected(fields::WHEN);
let remind = tree.checked(fields::REMIND).unwrap_or(false);
booked = Some((name.to_owned(), when, remind));
}
Some(UiEvent::Action(actions::BACK)) => back = true,
_ => {}
}
Dies ist der Code eines Tests im Repository des Screen Compilers, contract-check/tests/driving.rs, der den umgewandelten Screen auf dem UI-Kern von NoWayOS selbst baut und in seinem make ci auf Book tippt. Auf einem Gerät kommen die Ereignisse vom Input-Broker von NoWayOS, und der Baum wird von seinem Renderer gezeichnet. Ein Vergleich wie props.step === "review" wird auf dieselbe Weise zu einem Switch, switches::STEP, den die Anwendung mit set_case setzt.
Wie es weitergeht
Screens aus React enthält jede Komponente, jede Prop und jede Stileigenschaft, die der Screen Compiler annimmt. Um eine bestehende React-Anwendung herüberzuholen, beginnen Sie mit Von React portieren und seinem Abdeckungsbericht.