Цикл

Панель определяет 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="")Полоса, заполненная до value из total.
button(label, action="", value=None, insert="", set=None, style="plain", icon="", enabled=True)Кнопка. insert печатает свой текст туда, где вы пишете, set вносит ключи в состояние, а action называет обработчик для on_action. style принимает plain, primary, tinted, quiet или destructive.
row(title, subtitle="", detail="", icon="", action="", value=None, insert="")Строка, по которой можно нажать: заголовок, вторая строка, значок и деталь справа.
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="")Один вариант из списка. key записывает выбранный вариант в этот ключ состояния.
field(key, placeholder="", action="", submit="")Текстовое поле, привязанное к state[key]. Касание направляет клавиши в этот ключ; submit называет обработчик, который запускает Return.

Обычная строка работает везде, где работает узел: она рисуется как текст обычного размера.

Обработка нажатия

Элемент, которому нужно что-то вычислить, называет action вместо готового set. Тогда Clink вызывает on_action(action, state), или on_action(action, value, 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. Это снимок, сделанный в начале вызова, поэтому он не изменится у вас под руками посреди отрисовки.

Состояние

Состояние это обычный словарь, и в нём лежит всё, что скрипт умеет построить: числа, строки, логические значения, None, а также их списки и словари. Оно возвращается вам при каждом вызове, так что список, в который вы добавили по одному нажатию, никуда не денется к следующему.

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

def view(state):
    return vstack([
        field("name", placeholder="Who?"),
        button("Greet", insert="Hi " + state["name"] + "!", style="primary"),
    ])
Поле пишет прямо в тот ключ состояния, который вы указали.

«Запоминать состояние» сохраняет содержимое панели между открытиями и между запусками клавиатуры. Для калькулятора оставьте выключенным, для списка включите.

Состояние живёт, пока клавиатура на экране, и дольше, если панель запоминает. Панель может прочитать текст вокруг курсора через context(), и больше ничего о вас.

Ограничения

Клавиатуре достаётся доля той памяти, что есть у приложения, поэтому панели ограничены, а не просто приняты на веру. На каждую отрисовку даётся 2 000 000 шагов интерпретатора, и сорвавшийся цикл заканчивается ошибкой на экране, а не клавиатурой, которая перестала отвечать.

Панель, пришедшая файлом или из репозитория, проверяется до сохранения. Она должна укладываться в 48 000 байт и 1 200 строк, определять def view(, каждый import должен называть одно из изолированных пространств имён, а эти фрагменты отклоняются сразу:

Отклоняется в присланной панели

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

Что можно импортировать в общей панели

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

На панели, которые вы пишете в приложении, этот список не распространяется. Тогда убежавший цикл остановит только бюджет шагов, поэтому оставьте ему выход.

Размещение

Дать каждой панели свою кнопку в выборе вместо того, чтобы прятать их за одной кнопкой «Панели». Отдельная панель может переопределить это в своём редакторе.

«По умолчанию» следует этому переключателю, «Отдельная» всегда получает свою кнопку, а «Сгруппировано» всегда вкладывается.

Как написать свою

  1. Откройте «Свои панели» в Clink и нажмите «Новая панель».
  2. Напишите скрипт. Рядом работает предпросмотр настоящей панели, а ошибки появляются прямо по ходу набора.
  3. Сохраните, включите панель и откройте кнопку панелей на клавиатуре.

«Поделиться панелью» создаёт файл .clinkpanel, то есть JSON. При открытии его обратно в Clink он сохраняется с новым идентификатором, поэтому повторный импорт той же панели никогда не затирает первую.

Конструктор ›
Загрузить в App Store