--:-- LUA API

画面遷移と保存

pop / push_scene / change_scene / launch_app / content_rect / args / store_load / store_save

pico.pop

pico.pop([result]) -> (なし)

自分を起動した画面(ランチャ、または push_scene の呼び出し元)へ戻ります。実際に戻るのはそのフレームの終わりです。

result(任意)を渡すと、push_scene の呼び出し元の画面の on_result(result) に渡されます(画面の間で値を受け渡す 参照)。result はJSONにできる値(テーブル・文字列・数値・真偽値)で、JSONにして1KiB未満である必要があります。大きすぎる・JSONにできない値はエラーです。ランチャから起動した画面など、戻り先がLuaの画面でなければ結果は捨てられます。

pico.push_scene

pico.push_scene(path: string [, args]) -> (なし)

path(SDの絶対パス)の Lua スクリプトを新しい画面として開きます。新しい画面で pico.pop() すれば今の画面へ戻れます。同じアプリとして、権限とアプリのフォルダはそのまま引き継がれます。

args(任意)は新しい画面の pico.args() で受け取れます。pico.pop の result と同じく、JSONにできる値で1KiB未満です。

pico.change_scene

pico.change_scene(path: string [, args]) -> (なし)

path の Lua スクリプトへ画面を置き換えます。スタックを消費しないため、今の画面へは戻れません(pico.pop() すると、今の画面を開いた側まで直接戻ります)。権限は push_scene と同じく引き継がれます。args も同じ。置き換えた先の pico.pop(result) は、元の画面を開いた側の on_result に届きます。

pico.args

pico.args() -> args | nil

push_scene / change_scene で渡された args を返します。何も渡されなければ nil。null は pico.json_null になります。

pico.launch_app

pico.launch_app(name: string) -> ok: boolean

ランチャにあるアプリ(OS標準のアプリを含む)を名前で探して開きます。見つからなければ false、見つかれば true です。開いたアプリは、そのアプリ自身の権限で動きます。

pico.content_rect

pico.content_rect() -> x: integer, y: integer, w: integer, h: integer

ステータスバーを除いた、アプリが使える範囲を返します。

画面の大きさは本体によって違います(既定は240x320。ビルド時に320x480等にできる)。ウィジェットや絵の位置は、240や320を直接書かずにこの値から決めてください。画面全体の大きさは定数 pico.SCREEN_WIDTH / pico.SCREEN_HEIGHT です。

local x, y, w, h = pico.content_rect()
local btn = pico.create("Button")
pico.set(btn, "x", x + w - 64)   -- 右端に寄せる(画面の幅によらない)

画面の間で値を受け渡す

push_scene で別の画面へ移って pop で戻ると、元の画面のスクリプトは最初から実行し直されます。値を受け渡すための仕組みが3つあります。

やりたいこと使うもの
子の画面に値を渡すpush_scene(path, args) → 子で pico.args()
子の画面から結果を受け取る子で pico.pop(result) → 親の on_result(result)
離れる前の状態を戻ったあとに復元するon_suspend() が返したテーブル → on_resume(state)
-- main.lua(親)
local selected = nil
function on_suspend()               -- 画面を離れる直前に呼ばれる
    return { selected = selected }  -- 返したテーブルが持ち越される(JSONで2KiB未満)
end
function on_resume(state)           -- 戻ってきて setup() の後に呼ばれる
    selected = state.selected
end
function on_result(r)               -- 子が pico.pop(result) したとき、on_resume の後に呼ばれる
    selected = r.color
    pico.set(label, "text", "選んだ色: " .. r.color)
end
pico.on(pick_btn, "press_start", function()
    pico.push_scene("/lua/apps/demo/picker.lua", { title = "色を選ぶ" })
end)

-- picker.lua(子)
local args = pico.args()
pico.on(red_btn, "press_start", function()
    pico.pop({ color = "red" })
end)
  • 呼ばれる順序は setup() → on_resume(state) → on_result(result) です。どれも定義は任意で、無ければ何も起きません。
  • on_suspend は画面を離れるたび(push_scene で別の画面へ移るとき、pop で戻るとき)に呼ばれます。返す値が無い(nil)ときは何も保存されません。ここでエラーになってもダイアログは出ず、ログに残るだけです。
  • on_result は、pico.pop(result) から約3秒以内に親が実際に戻ったときだけ呼ばれます。子の画面から pico.pop() した(結果を渡さなかった)ときは呼ばれません。
  • 持ち越した状態が残るのは、その画面がスタックに積まれている間(メモリ上)だけです。アプリを閉じた後も残したい値は store_save を使ってください。

pico.store_load / pico.store_save

pico.store_load() -> value | nil
pico.store_save(value) -> ok: boolean

アプリのフォルダの store.json に値を1つ保存・読み込みます(最高得点・設定・途中経過など)。sd_read/sd_write と json_encode/json_decode を組み合わせた便利関数です。

local save = pico.store_load() or { best = 0 }
if score > save.best then
    save.best = score
    pico.store_save(save)
end
  • 保存できるのは JSON にできる値で、16KiBまでです。JSONにできない値(関数など)を渡すとエラー、大きすぎる・書き込めないときは false を返します。
  • 一時ファイルに書いてから差し替えるので、書き込み中に電源が切れても前の内容は壊れません。
  • ファイルが無い・壊れている・SDが使えないときは nil を返します。
  • アプリのフォルダの外には書きません(sd_outside_app_dir の権限に関わらず固定です)。

暗号化して保存する

pico.store_save(value, opts?: { encrypt?: boolean, password?: string }) -> ok: boolean
pico.store_load(opts?: { password?: string }) -> value | nil [, reason: string]

opts.encrypt = true で、JSONを暗号化して store.json に書きます(password だけ渡しても暗号化されます)。読むときは自動で見分けます。パスワード付きで保存したものは、store_load({ password = ... }) が必要で、無い/違うと nil, 理由 を返します。暗号化したときの上限は12KiB(平文のJSONの大きさ)です。鍵の決め方と守れる範囲は 暗号化 を読んでください。

pico.store_save({ token = "abc" }, { password = "ながいあいことば" })
local data = pico.store_load({ password = "ながいあいことば" })

pico.on_back / pico.go_back

pico.on_back(fn: function | nil) -> (なし)
pico.go_back() -> (なし)

「戻る」の操作を横取りします。EscキーとコントローラーのHOMEで fn() が呼ばれます(画面に自前の「戻る」ボタンがあるなら、そこから pico.go_back() を呼びます)。fn が false を返すと「取らなかった」扱いで既定の動き(pico.pop())になります。それ以外(何も返さない場合を含む)は取った扱いです。nil で解除。

pico.on_back(function()
    if not dirty then return false end          -- そのまま戻る
    local d = pico.show_message("保存していません。戻りますか?", "いいえ", "はい")
    pico.on(d, "closed", function(_, ok) if ok then dirty = false; pico.pop() end end)
end)