Die Schleife

Ein Panel definiert view(state) und optional initial(), on_open(state) und on_action(action, state). Clink ruft initial() einmal für den Startzustand auf, on_open bei jedem Erscheinen des Panels und dann view(state), um die Oberfläche zu bauen. Ein Bedienelement fügt entweder Text dort ein, wo du schreibst, setzt neuen Zustand oder benennt eine Aktion für on_action – und die Ansicht wird neu gebaut.

def initial():
    return {"count": 0, "step": 1}

def view(state):
    return vstack([
        text(f"{state['count']}", size=34, weight="bold", align="center"),
        segmented(["1", "5", "10"], value=str(state["step"]), action="step"),
        hstack([
            button("−", "bump", value=-1),
            button("+", "bump", value=1, style="primary"),
        ]),
        button("Type it", "type", icon="keyboard"),
    ])

def on_action(action, value, state):
    if action == "bump":
        state["count"] = state["count"] + value * state["step"]
    elif action == "step":
        state["step"] = int(value)
    elif action == "type":
        insert(str(state["count"]))
        close()
    return state
Das Start-Panel, direkt aus der Schaltfläche „Neues Panel“ im Editor.

Taschenrechner, Snippet-Boards, Picker: deine Panels erscheinen hinter der Panel-Taste der Tastatur.

Bausteine

view(state) gibt Knoten zurück, die diese 19 Funktionen erzeugen. Verschachtle sie frei: Stapel und Raster nehmen eine Liste von Kindern.

BausteinWas er zeichnet
vstack(children, spacing=6, align="leading")Stapelt seine Kinder nach unten.
hstack(children, spacing=6, align="center")Stapelt seine Kinder nebeneinander.
grid(children, columns=4, spacing=6)Verteilt seine Kinder auf gleiche Spalten.
wrap(children, spacing=6)Verteilt seine Elemente über das Panel und bricht in die nächste Zeile um.
scroll(children, spacing=6)Ein eigener scrollbarer Bereich, für eine Liste, die länger ist als das Panel.
card(children, padding=10, color="")Fasst seine Elemente auf einer Fläche zusammen, mit Abstand ringsum.
spacer(size=0)Schiebt alles Folgende ans Ende.
divider()Eine Haarlinie zwischen Zeilen.
text(s, size=17, weight="regular", color="", align="leading", lines=0, mono=False)Eine Textzeile. weight nimmt regular, medium, semibold, bold, heavy, light oder thin; align nimmt leading, center oder trailing; lines begrenzt die Zeilen beim Umbruch; color nimmt einen Farbnamen oder #RRGGBB.
icon(name, size=20, color="")Ein SF Symbol, über seinen Namen.
badge(s, color="")Ein kurzes Wort in einer eingefärbten Kapsel.
progress(value, total=1, label="")Ein Balken, gefüllt bis value von total.
button(label, action="", value=None, insert="", set=None, style="plain", icon="", enabled=True)Eine Schaltfläche. insert tippt ihren Text in das, was du gerade schreibst, set führt Schlüssel in den Zustand ein, und action benennt einen Handler für on_action. style nimmt plain, primary, tinted, quiet oder destructive.
row(title, subtitle="", detail="", icon="", action="", value=None, insert="")Eine antippbare Zeile: Titel, zweite Zeile, Symbol und ein Detail rechts.
toggle(label, on=False, key="", action="")Ein Schalter. key schreibt True oder False in diesen Zustandsschlüssel.
slider(value, min=0, max=100, step=1, label="", key="", action="")Ein Regler zwischen min und max. key schreibt die Position in diesen Zustandsschlüssel.
stepper(value, min=0, max=100, step=1, label="", key="", action="")Ein Wert mit − und + daneben, zwischen min und max.
segmented(options, value=None, key="", action="")Eine Auswahl aus einer Liste. key schreibt die gewählte Option in diesen Zustandsschlüssel.
field(key, placeholder="", action="", submit="")Ein Textfeld, gebunden an state[key]. Ein Tippen richtet die Tasten auf diesen Schlüssel; submit benennt den Handler, den die Eingabetaste auslöst.

Ein schlichter String funktioniert überall dort, wo ein Knoten funktioniert: er erscheint als Text in der Standardgröße.

Auf eine Berührung reagieren

Ein Bedienelement, das etwas berechnen muss, nennt ein action statt ein festes set zu tragen. Clink ruft dann on_action(action, state) auf – oder on_action(action, value, state), wenn du den Wert des Bedienelements brauchst. Der Zustand ist ein gewöhnliches Dictionary: Du kannst ihn direkt ändern und nichts zurückgeben, oder ein neues zurückgeben.

def initial():
    return {"items": [], "draft": ""}

def view(state):
    return vstack([
        field("draft", placeholder="Add one…", submit="add"),
        scroll([row(item, action="use", value=item) for item in state["items"]]),
    ])

def on_action(action, value, state):
    if action == "add" and value:
        state["items"].append(value)
        state["draft"] = ""
    elif action == "use":
        insert(value)
        close()
    return state
Eine Liste, zu der du hinzufügst und aus der du per Tippen einfügst.

on_open(state) läuft jedes Mal, wenn das Panel erscheint – dort holt sich ein Panel seinen Anfangswert aus dem, was du gerade schreibst.

Die Tastatur bitten

Aus on_action und on_open heraus kann ein Panel die Tastatur um Dinge bitten, die ein Rückgabewert nicht ausdrücken kann. Jeder Aufruf wird notiert und erst nach dem Ende deiner Funktion ausgeführt – ein Panel bearbeitet das Feld also nie, während seine eigene Ansicht entsteht.

Befehle

  • insert()
  • backspace()
  • delete_word()
  • replace()
  • move_cursor()
  • copy()
  • close()
  • haptic()
  • toast()
def on_action(action, state):
    if action == "quote":
        word = context()["word"]
        replace(len(word), "“" + word + "”")
        haptic("light")
    return state
Das Wort am Cursor gegen ein in Anführungszeichen gesetztes tauschen.

context() liest, was um die Einfügemarke herum steht: before, selected, after, word, clipboard, locale, language, full_access und time. Es ist eine Momentaufnahme vom Beginn des Aufrufs und kann sich mitten im Rendern nicht unter dir ändern.

Zustand

Der Zustand ist ein gewöhnliches Dictionary und enthält alles, was ein Skript bauen kann: Zahlen, Zeichenketten, Wahrheitswerte, None sowie Listen und Dictionaries davon. Er wird dir bei jedem Aufruf zurückgereicht – eine Liste, an die du bei einer Berührung angehängt hast, ist bei der nächsten noch da.

def initial():
    return {"name": ""}

def view(state):
    return vstack([
        field("name", placeholder="Who?"),
        button("Greet", insert="Hi " + state["name"] + "!", style="primary"),
    ])
Ein Feld schreibt direkt in den Zustandsschlüssel, den du nennst.

Zustand merken behält den Inhalt des Panels zwischen zwei Aufrufen – und über Tastatursitzungen hinweg. Für einen Rechner aus lassen, für eine Liste einschalten.

Der Zustand lebt, solange die Tastatur auf dem Bildschirm ist – und länger, wenn sich das Panel erinnert. Ein Panel kann mit context() den Text rund um den Cursor lesen; sonst nichts über dich.

Grenzen

Eine Tastatur bekommt einen Bruchteil des Speichers einer App, deshalb sind Panels begrenzt statt vertraut. Jeder Aufbau bekommt 2.000.000 Interpreter-Schritte, und eine entlaufene Schleife endet mit einem Fehler auf dem Bildschirm statt mit einer Tastatur, die nicht mehr antwortet.

Ein Panel, das als Datei oder aus einem Repository kommt, wird vor dem Speichern geprüft. Es muss unter 48.000 Bytes und 1.200 Zeilen bleiben, es muss def view( definieren, jedes import muss einen der abgeschotteten Namensräume nennen – und diese Fragmente werden rundweg abgelehnt:

In einem geteilten Panel abgelehnt

  • __
  • exec(
  • eval(
  • open(
  • compile(

In einem geteilten Panel importierbar

  • json
  • math
  • random
  • re
  • sys
  • time

Panels, die du in der App schreibst, unterliegen dieser Liste nicht. Dann hält nur noch das Schrittbudget eine ausufernde Schleife auf – gib ihr also einen Ausweg.

Platzierung

Gib jedem Panel einen eigenen Button im Picker, statt sie alle hinter einem Panels-Button zu verschachteln. Einzelne Panels können das in ihrem Editor überschreiben.

Standard folgt diesem Schalter, Eigenständig bekommt immer eine eigene Taste, und Gruppiert verschachtelt immer.

Eines schreiben

  1. Öffne Eigene Panels in Clink und tippe auf Neues Panel.
  2. Schreib das Skript. Daneben läuft eine Vorschau des echten Panels, und Fehler erscheinen beim Tippen.
  3. Sichern, das Panel einschalten, dann auf der Tastatur die Panel-Taste öffnen.

Teilen-Panel schreibt eine .clinkpanel-Datei, also JSON. Öffnest du sie wieder in Clink, wird sie mit frischer ID gesichert: dasselbe Panel zweimal zu importieren überschreibt das erste nie.

Baukasten ›
Im App Store laden