ループ

パネルは view(state) を定義し、必要に応じて initial()、on_open(state)、on_action(action, state) も定義します。Clink は初期状態のために initial() を一度、パネルが現れるたびに on_open を、そして view(state) を呼んで画面を組み立てます。コントロールは、書いている場所に文字を挿入するか、新しい状態を設定するか、on_action に渡すアクション名を持つかのいずれかで、そのたびに画面が組み立て直されます。

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
エディタの「新規パネル」ボタンがそのまま書く、出発点のパネルです。

電卓、スニペット集、ピッカー。あなたのパネルはキーボードのパネルボタンの奥に表示されます。

ビルダー

view(state) は、これら 19 個の関数が作るノードを返します。自由に入れ子にできます。スタックとグリッドは子要素のリストを受け取ります。

ビルダー描画するもの
vstack(children, spacing=6, align="leading")子要素を縦に積みます。
hstack(children, spacing=6, align="center")子要素を横に並べます。
grid(children, columns=4, spacing=6)子要素を均等な列に流し込みます。
wrap(children, spacing=6)子要素をパネルの幅いっぱいに並べ、あふれたら次の行へ折り返します。
scroll(children, spacing=6)独立したスクロール領域。パネルより長いリストに使います。
card(children, padding=10, color="")子要素を一枚の面にまとめ、まわりに余白をとります。
spacer(size=0)後に続くものを端まで押しやります。
divider()行と行のあいだの細い線。
text(s, size=17, weight="regular", color="", align="leading", lines=0, mono=False)一行のテキストです。weight は regular、medium、semibold、bold、heavy、light、thin を取ります。align は leading、center、trailing。lines は折り返す行数の上限、color は色名か #RRGGBB です。
icon(name, size=20, color="")SF Symbol を名前で指定します。
badge(s, color="")短い語を色付きのカプセルで表示します。
progress(value, total=1, label="")total のうち value まで満ちるバーです。
button(label, action="", value=None, insert="", set=None, style="plain", icon="", enabled=True)ボタンです。insert は書いている文章にその文字を打ち込み、set は state にキーを差し込み、action は on_action の受け口の名前になります。style は plain、primary、tinted、quiet、destructive を取ります。
row(title, subtitle="", detail="", icon="", action="", value=None, insert="")タップできる行です。タイトル、2 行目、アイコン、右端の補足からなります。
toggle(label, on=False, key="", action="")スイッチです。key を指定すると、その状態キーに True か False を書き込みます。
slider(value, min=0, max=100, step=1, label="", key="", action="")min から max までのスライダーです。key を指定すると位置をその状態キーに書き込みます。
stepper(value, min=0, max=100, step=1, label="", key="", action="")− と + を添えた数値です。min から max の範囲で動きます。
segmented(options, value=None, key="", action="")リストから 1 つを選ばせます。key を指定すると選んだ項目をその状態キーに書き込みます。
field(key, placeholder="", action="", submit="")state[key] に結びついたテキスト欄です。タップするとキーの入力先がそのキーになり、submit は Return が走らせる受け口の名前です。

ただの文字列も、ノードが使える場所ならそのまま使えます。既定のサイズのテキストとして描かれます。

タップに応える

何かを計算する必要があるコントロールは、決め打ちの set ではなく action を名前で指定します。すると Clink が on_action(action, state) を呼びます。コントロールが運ぶ値も使いたいときは on_action(action, value, state) にします。state はふつうの辞書なので、その場で書き換えて何も返さなくても、新しい辞書を返してもかまいません。

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
項目を足していき、タップして挿入するリストです。

on_open(state) はパネルが現れるたびに走ります。いま書いている文章から初期値を取るのはここです。

キーボードに頼む

on_action と on_open からは、戻り値では表せないことをキーボードに頼めます。呼び出しはいったん記録され、関数が終わってから実行されるので、パネルが自分のビューを組み立てている最中に入力欄を書き換えることはありません。

コマンド

  • 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
カーソル位置の語を、引用符で囲んだものに置き換えます。

context() はカーソルの周りを読みます。before、selected、after、word、clipboard、locale、language、full_access、time です。呼び出しが始まった時点のスナップショットなので、描画の途中で書き換わることはありません。

状態

state はふつうの辞書で、スクリプトが作れるものなら何でも入ります。数値、文字列、真偽値、None、そしてそれらのリストや辞書です。呼び出しのたびに返ってくるので、あるタップで追加したリストは次のタップでもそのままです。

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

def view(state):
    return vstack([
        field("name", placeholder="Who?"),
        button("Greet", insert="Hi " + state["name"] + "!", style="primary"),
    ])
フィールドは、指定した状態キーに直接書き込みます。

「状態を保持」を入れると、パネルの内容が開き直しても、キーボードを起動し直しても残ります。電卓なら切ったまま、リストなら入れておくとよいでしょう。

state はキーボードが画面に出ているあいだ生きています。パネルが記憶する設定なら、それより長く残ります。パネルは context() でカーソル周辺の文字を読めますが、あなたについてそれ以外は読めません。

制限

キーボードに与えられるメモリはアプリのごく一部です。だからパネルは信用ではなく上限で守られています。描画1回につきインタプリタのステップは 2,000,000 回までで、暴走したループは画面上のエラーで終わります。キーボードが応答しなくなることはありません。

ファイルやリポジトリから届いたパネルは、保存する前に検査されます。48,000 バイトと 1,200 行を超えないこと、def view( を定義していること、import はすべてサンドボックス内の名前空間のいずれかを指していること。そして次の断片はその場で拒否されます。

共有パネルでは拒否

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

共有パネルでインポートできるもの

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

アプリの中で自分が書くパネルは、この一覧の対象外です。その場合、暴走したループを止めるのはステップ予算だけなので、抜け道を用意しておいてください。

配置

各パネルに専用ボタンをピッカー内に用意し、1つのパネルボタンの奥にまとめないようにします。個々のパネルはエディタでこの設定を上書きできます。

「デフォルト」はその設定に従い、「単独」は常に自分のボタンを持ち、「グループ化」は常に中にまとまります。

書いてみる

  1. Clink でカスタムパネルを開き、「新規パネル」をタップします。
  2. スクリプトを書きます。隣では本物のパネルのプレビューが動き、エラーは入力しながら表示されます。
  3. 保存してパネルをオンにし、キーボードのパネルボタンを開きます。

「共有パネル」は .clinkpanel ファイル、つまりJSONを書き出します。Clink で開き直すと新しいIDで保存されるので、同じパネルを2回読み込んでも最初のものが上書きされることはありません。

クリエイター ›
App Storeでダウンロード