> For the complete documentation index, see [llms.txt](https://docs.allout.game/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.allout.game/all-out-docs/ru/ui/uidoc-quick-start.md).

# Быстрый старт UIDoc

Создавайте игровой UI в стиле браузера с HTML- и CSS-ассетами UIDoc, привязками CSL, списками и событиями.

***

UIDoc позволяет описывать игровой интерфейс с помощью HTML и CSS, а затем передавать его изменяющиеся данные и обрабатывать его события в CSL. Это полезно для меню, инвентарей, деревьев навыков и других структурированных интерфейсов в экранном пространстве.

UIDoc похож на браузер, но это не встроенный веб-браузер. Используйте поддерживаемое подмножество, описанное в [Поддержка HTML и CSS в UIDoc](/all-out-docs/ru/ui/uidoc-html-css-support.md).

## Создайте ресурс UIDoc

Создайте папку в каталоге `res` вашей игры. Имя папки должно заканчиваться на `.uidoc` и содержать `index.html` и `index.css`:

```
res/
└── UI/
    └── settings.uidoc/
        ├── index.html
        └── index.css
```

Пути к ресурсам, используемые CSL, являются относительными к `res`, поэтому этот документ загружается как `UI/settings.uidoc`.

## Визуальное редактирование и генерация CSL

Выберите ресурс UIDoc в редакторе All Out, чтобы открыть его визуальный режим создания. Вы можете выделять и перетаскивать элементы на рабочем поле, изменять их размер, изменять порядок или родителя в Layers, а также редактировать общие свойства HTML и CSS в инспекторе. Предпросмотр отрисовывается самим runtime UIDoc, поэтому он использует то же поддерживаемое поведение компоновки и отрисовки, что и игра.

Инспектор интерфейса определяет динамические поля, повторяющиеся списки, поля ввода и действия по разметке документа. Сохранение такого ресурса, как `ui/shop.uidoc` также поддерживает `scripts/generated/uidoc/ui/shop.csl`. Его типизированная обёртка предоставляет `default_data`, `decode_event`, считыватели ввода и `draw`, поэтому игровому коду не нужно повторять низкоуровневые привязки, показанные далее в этом руководстве:

```csl
data := UiShop_UIDoc.default_data();
data.title = "Store";
UiShop_UIDoc.draw(data, player, shop_event);
```

Для модального окна используйте `UiShop_UIDoc.draw(data, player, shop_event, consume=true)` чтобы заблокировать ввод указателя для игровых виджетов под ним. Необязательный завершающий `consume` параметр по умолчанию равен `false`. Сохраните существующие ресурсы UIDoc в редакторе, чтобы сгенерировать обёртки с этим параметром.

Сгенерированный код использует те же публичные вызовы `UI.uidoc_*` описанные ниже; он не добавляет отдельную runtime-систему и не изменяет API runtime UIDoc.

При использовании инструментов автоматизации, `uidoc_create_asset` возвращает версию своего шаблона и точное сгенерированное `index.html`/`index.css` содержимое. Используйте `uidoc_diagnostics` для проверки создания и viewport в области редактора. Используйте `uidoc_runtime_inspect` для того, что рисуется прямо сейчас: размещённые узлы с вычисленным текстом и экранными прямоугольниками, обработчики кликов и ключи строк, отправленные привязки и проблемы runtime; по умолчанию используется активный клиент, пока выполняется Start Game, иначе требуется явное `scope: "editor"`. Каждый ответ runtime указывает свою область.

## Запишите документ

В `index.html`:

```html
<div class="screen">
  <div class="panel">
    <span class="title">Настройки</span>
    <span class="message">{{message}}</span>
    <button class="close" data-on-click="event:close">Закрыть</button>
  </div>
</div>
```

`{{message}}` является текстовой привязкой. `data-on-click` отправляет `UIDoc_Event` в CSL.

В `index.css`:

```css
.screen {
  position: fixed;
  inset: env(safe-area-inset-top) env(safe-area-inset-right)
         env(safe-area-inset-bottom) env(safe-area-inset-left);
  display: flex;
  align-items: center;
  justify-content: center;
}

.panel {
  display: flex;
  flex-direction: column;
  width: 420px;
  padding: 24px;
  gap: 16px;
  color: white;
  background: #172033;
  border: 2px solid #52627d;
  border-radius: 12px;
  box-shadow: 0 12px 28px 0 #00000066;
}

.title {
  font-size: 32px;
  text-align: center;
}

.close {
  height: 48px;
  background: #3559a8;
  border-radius: 8px;
}

.close:hover {
  background: #4770ca;
}

.close:pressed {
  background: #29447f;
}
```

Отступы safe-area сохраняют полноэкранный интерфейс свободным от вырезов устройства и области верхней панели игры.

## Привязка и отрисовка из CSL

Рисуйте интерфейс игрока из `ao_late_update` стека вызовов этого игрока. Очищайте и перестраивайте привязки UIDoc перед отрисовкой документа каждый кадр:

```go
settings_uidoc_event :: proc(event: UIDoc_Event, userdata: Object) {
    player := userdata.(Player);
    if player == null return;

    if event.handler == "event:close" {
        player.settings_open = false;
    }
}

draw_settings :: proc(player: Player) {
    UI.uidoc_clear_bindings();
    UI.uidoc_bind_text("message", "Изменения сохраняются автоматически.");

    document := get_asset(UIDoc_Asset, "UI/settings.uidoc");
    if document == null return;

    UI.uidoc(document, false, player, settings_uidoc_event);
}

Player :: class : Player_Base {
    settings_open: bool;
    pet_name: string;

    ao_late_update :: method(dt: float) {
        if this.is_local_or_server() && this.settings_open {
            draw_settings(this);
        }
    }
}
```

Обратный вызов получает точную строку обработчика из `data-on-click`. Неповторяющийся элемент управления может передать статический `data-key` для `event.key`; повторяющиеся элементы управления используют свой вычисленный `data-for-key` или ключ элемента списка.

`userdata` передаётся только этому обратному вызову; он не идентифицирует документ. Каждый ресурс UIDoc может быть отрисован один раз за одну симуляционную отправку. Когда документ перестаёт рисоваться, его текущая активация закрывается; при повторной отрисовке автоматически назначается новая генерация активации, чтобы устаревшие реплицированные раскладки не могли присоединиться к вновь открытому документу. Одновременно может быть активировано не более четырёх разных ресурсов UIDoc.

Доступные привязки верхнего уровня:

```go
bind_player_fields :: proc(player: Player) {
    UI.uidoc_bind_bool("visible", true);
    UI.uidoc_bind_text("name", player.get_username());
    UI.uidoc_bind_float("cameraSize", player.camera.size);
}
```

## Условия, привязки отрисовки и списки

Используйте `data-if` чтобы включать узел только пока привязка верхнего уровня типа boolean истинна. Необязательный ведущий `!` инвертирует его. `data-if` не вычисляет локальные для элемента выражения, такие как `item.visible`; повторяющееся визуальное состояние следует задавать вместо этого через локальную для списка привязку отрисовки, а повторяющуюся структурную или интерактивную видимость следует определять при построении списка CSL. Для другого динамического визуального состояния оставляйте классы статическими и привязывайте поддерживаемый цвет или прозрачность:

```html
<div
  class="notice"
  data-if="showNotice"
  data-style-color="noticeColor">
  {{noticeText}}
</div>
```

Привяжите `noticeColor` с помощью `UI.uidoc_bind_text` используя поддерживаемую строку цвета CSS. Атрибуты class компилируются как статические токены классов и не поддерживают `{{...}}` интерполяцию.

Используйте `data-for` для повторяющихся данных:

```html
<div class="inventory">
  <button
    class="item"
    data-for="item in items"
    data-for-key="item.id"
    data-key="inventory-item"
    data-style-color="item.rarityColor"
    data-on-click="event:item">
    {{item.name}}
  </button>
</div>
```

Постройте этот список в CSL перед вызовом `UI.uidoc`:

```go
Inventory_Row :: struct {
    id: string;
    name: string;
    rarity_color: string;
}

bind_inventory_rows :: proc(items: []Inventory_Row) {
    UI.uidoc_begin_list("items");
    for item: items {
        UI.uidoc_list_item(item.id);
        UI.uidoc_list_bind_text("id", item.id);
        UI.uidoc_list_bind_text("name", item.name);
        UI.uidoc_list_bind_text("rarityColor", item.rarity_color);
    }
    UI.uidoc_end_list();
}
```

Используйте стабильное, уникальное прямое выражение, например `data-for-key="item.id"` для каждого повторяющегося элемента. Оно возвращается как `event.key`. Дайте `UI.uidoc_list_item(...)` то же самое стабильное значение, чтобы взаимодействие, ввод и идентичность прокрутки сохранялись при изменении списка. Статический `data-key` обозначает роль элемента управления; UIDoc объединяет эту роль с привязанным ключом каждого вложенного элемента списка и текущим индексом для его зарегистрированной runtime-идентичности.

Для примера выше имя живого теста содержит суффикс вроде `inventory-item#potion-42:7/__widget`. `client_ui_tree` сокращает его до уникального селектора без необязательного `/__widget`, например `inventory-item#potion-42:7`; передайте этот результат напрямую в `Test.click_button`. Поиск только по роли, такой как `Test.click_button("inventory-item")` неоднозначен, когда видно несколько строк. `data-on-click` и видимый текст не являются селекторами тестов. Вложенные списки добавляют одну пару `#key:index` на каждый цикл.

## Поля ввода

Привяжите поле ввода с помощью `data-bind-value`:

```html
<input
  id="pet-name"
  class="name-input"
  placeholder="Имя питомца"
  data-bind-value="petName"
  data-on-click="event:name-input">
```

Прочитайте его текущее значение после отрисовки документа:

```go
draw_pet_name_input :: proc(player: Player, document: UIDoc_Asset) {
    UI.uidoc_bind_text("petName", player.pet_name);
    UI.uidoc(document, false, player, settings_uidoc_event);
    player.pet_name = UI.uidoc_text_value(document, "pet-name", player.pet_name);
}
```

`UI.uidoc_text_value` находит поле ввода по его `id` или `data-key`, а затем возвращает текущее значение его `data-bind-value` привязки.

## Прокрутка и масштабирование

Прокрутка включается через CSS overflow. Оба направления могут быть включены в одном и том же viewport:

```html
<div class="viewport" data-scroll-zoom="zoom">
  <div class="canvas">
    <button
      class="node"
      data-for="node in nodes"
      data-for-key="node.id"
      data-key="canvas-node"
      data-style-transform-x="node.x"
      data-style-transform-y="node.y"
      data-on-click="event:node">
      {{node.name}}
    </button>
  </div>
</div>
```

```css
.viewport {
  width: 100%;
  height: 100%;
  overflow-x: auto;
  overflow-y: auto;
}

.canvas {
  position: relative;
  width: 1600px;
  height: 1000px;
}

.node {
  position: absolute;
  width: 160px;
  height: 64px;
}
```

Привяжите `zoom` с помощью `UI.uidoc_bind_float`. `data-scroll-zoom` масштабирует позиции, размеры, текст, изображения, области попадания и пределы прокрутки содержимого относительно центра viewport. Держите фиксированные элементы управления масштабом вне масштабируемого viewport.

{% hint style="warning" %}
Атрибуты выражений привязки используют прямое выражение, например `data-for-key="node.id"`, `data-style-transform-x="node.x"`, или `data-scroll-zoom="zoom"`. Не помещайте эти выражения внутрь `{{...}}`. Интерполяция Mustache предназначена для текста и изображений `src`; классы и `data-key` значения являются статическими.
{% endhint %}

Для обычной панели прокрутки опустите `data-scroll-zoom`. Перетаскивание перемещает по всем включённым осям; колесо прокручивает по вертикали, либо по горизонтали, когда включена только горизонтальная прокрутка.

## Распространённые ошибки

* Загружайте документ, используя его путь относительно `res`, включая суффикс `.uidoc` .
* Рисуйте его из кода UI локального игрока под `is_local_or_server()`.
* Вызывайте `UI.uidoc_clear_bindings()` и передавайте текущие привязки перед каждой отрисовкой.
* Рисуйте каждый ресурс UIDoc только один раз за обновление. Используйте `userdata` только как данные обратного вызова.
* Используйте `data-if` только с верхнеуровневыми boolean. Привязывайте локальное для списка визуальное состояние через `data-style-opacity`/`data-style-color`, либо исключайте структурные/интерактивные строки из привязанного списка.
* Давайте повторяющимся узлам стабильное прямое `data-for-key` выражение; оставляйте `data-key` статическим при именовании роли элемента управления.
* Рассматривайте поддержку CSS как зависящую от конкретного свойства/значения. `auto` и `none` принимаются только там, где их перечисляет сгенерированная справка, боковые границы, такие как `border-bottom` не поддерживаются, а `calc(...)` ограничен документированными полями длины с простым сложением/вычитанием.
* Задавайте для прокручиваемого содержимого реальный размер. Не следует использовать только transforms как его единственный размер компоновки.
* Используйте собственную адаптивную компоновку и поведение масштабирования UIDoc вместо применения второго ручного масштаба UI в CSL.
