# Разметка и оформление выборки

Разметка определяет состав и расположение элементов выборки, способы представления данных и визуальное оформление интерфейса.

Разметка задаётся декларативно в XML-файле с расширением `.avm.xml`.

К основным элементам разметки относятся:

* отображения;
* компоновка формы;
* фреймы;
* атрибуты;
* операции;
* фильтры.

Фрейм определяет способ представления данных. Например, данные могут отображаться в виде списка, дерева или карточки.

## Разметка карточки

Для размещения атрибутов в карточке используются два режима:

1. Разметка на основе настроек атрибутов. Расположение определяется порядковым номером атрибута и значением свойства `isLastInLine`.
2. Разметка на основе контейнеров, указанных внутри тега `<card>`.

Для разметки атрибутов используются контейнеры:

* `hBox` — горизонтальный контейнер, элементы которого располагаются друг за другом по горизонтали;
* `vBox` — вертикальный контейнер, элементы которого располагаются друг за другом по вертикали;
* `vGroup` — вертикальная группа с рамкой и наименованием;
* `vSection` — сворачиваемая вертикальная секция с наименованием.

Для `vSection` можно задать исходное состояние секции: свёрнутое или развёрнутое.

Контейнеры можно вкладывать друг в друга.

Пример разметки карточки с помощью контейнеров:

```xml
<representation editMode="edit" name="Card" stdFilter.isAvailable="false">
  <layout>
    <simpleComposer>
      <frame filter.isVisible="false">
        <card>
          <layout>
            <vBox>
              <hBox>
                <vBox>
                  <attr name="idCustomerHL"/>
                  <attr name="dOutDate"/>
                </vBox>
                <vBox>
                  <attr name="gidSrcDoc" visible="true"/>
                  <attr name="nOrder"/>
                </vBox>
              </hBox>
              <vGroup caption="Приемка">
                <attr name="idCustomerHL"/>
                <attr name="dExecuteDate"/>
              </vGroup>
            </vBox>
            <vSection caption="Примечание" collapsed="false">
              <attr name="sComment" caption="Комментарий"/>
            </vSection>
          </layout>
        </card>
      </frame>
    </simpleComposer>
  </layout>
  <attributes/>
  <operations/>
</representation>
```

![](../img/repMarkupExample.jpg)

## Шаблоны разметки

Файлы разметки поддерживают язык шаблонов [Thymeleaf](https://www.thymeleaf.org).

Шаблоны позволяют подключать к выборкам повторно используемые фрагменты разметки. Например, один общий фильтр можно использовать в нескольких отображениях.

Для работы с шаблонами в корневом теге `view` необходимо подключить пространство имён:

```xml
xmlns:th="http://www.global-system.ru/xsd/global3-view-template-1.0"
```

## Наследование разметки

Чтобы унаследовать разметку другой выборки:

1. Подключите пространство имён языка шаблонов.
2. В теге `view` укажите наследуемый файл в свойстве `th:extends`.

Пример:

```xml
<view xmlns="http://www.global-system.ru/xsd/global3-view-1.0"
      xmlns:th="http://www.global-system.ru/xsd/global3-view-template-1.0"
      name="RplTst_AllDbTypes"
      th:extends="RplTst_AllDbTypes.dvm.xml">
  <representation name="Default"/>
</view>
```

При обработке наследования выборки система:

1. Копирует в выборку отображения предка, отсутствующие в наследнике.
2. Объединяет отображения с одинаковыми именами, если они присутствуют и у предка, и у наследника.
3. Выполняет остальные команды языка разметки.

При наследовании отображения система:

1. Копирует отсутствующие элементы:

   * `levelGroups`;
   * `bandGroups`;
   * `layout`;
   * `filter`.
2. Добавляет отсутствующие атрибуты.
3. Добавляет отсутствующие операции.

## Мультиселект

Мультиселект позволяет выбрать несколько строк в списке или дереве.

Для включения мультиселекта в теге `grid` или `tree` укажите свойство:

```xml
isMultiSelectEnabled="true"
```

Пример включения мультиселекта для списка:

```xml
<representation name="List">
  <layout>
    <simpleComposer>
      <frame>
        <grid isMultiSelectEnabled="true"/>
      </frame>
    </simpleComposer>
  </layout>
</representation>
```

Количество выделенных строк возвращает метод:

```scala
selection.selectedRecordsCount()
```

Для получения значения атрибута выделенной строки используется метод:

```scala
selection.selectedValueByName(attributeName, recordIndex)
```

Параметры метода:

* `attributeName` — имя атрибута;
* `recordIndex` — индекс выделенной строки.

Пример обработки выделенных строк и получения значений атрибутов разных типов:

```scala
for (i <- 0 until selection.selectedRecordsCount()) {
  val id = NLong.fromAny(
    selection.selectedValueByName("id", i)
  )

  val nOrder = NNumber.fromAny(
    selection.selectedValueByName("nOrder", i)
  )

  val gid = NGid(
    selection.selectedValueByName("gid", i).asInstanceOf[String]
  )

  val sCaption = selection
    .selectedValueByName("sCaption", i)
    .asInstanceOf[String]
    .ns

  val dDate = NDate(
    selection.selectedValueByName("dDate", i).asInstanceOf[JDate]
  )
}
```

```{note}
Для формы, открытой в режиме выбора значения, мультиселект включается опцией `useMultiSelect` при создании формы. Подробнее см. на странице «Выборка».
```

## Настройка стилей

Стиль определяет визуальное оформление строки или отдельной колонки выборки.

Стиль может содержать следующие свойства:

* `Color` — цвет фона в формате `$00BBGGRR` или в виде Global-константы цвета;
* `FontColor` — цвет шрифта в формате `$00BBGGRR` или в виде Global-константы цвета;
* `FontSize` — размер шрифта, заданный натуральным числом;
* `FontItalic` — курсив: `0` — выключен, `1` — включён;
* `FontBold` — полужирное начертание: `0` — выключено, `1` — включено;
* `FontUnderLine` — подчёркивание: `0` — выключено, `1` — включено;
* `FontStrikeOut` — перечёркивание: `0` — выключено, `1` — включено.

Стиль задаётся строкой следующего формата:

```text
param1=value1;param2=value2;param3=value3;...;paramN=valueN
```

Пресеты стилей хранятся в классе `Btk_Registry` и могут использоваться для типовых вариантов оформления.

Стили можно применять к строкам и отдельным колонкам отображения.

### Применение стиля к строке

Для применения стиля к строке в свойстве `rowStyleAttr` тега `representation` указывается имя атрибута со стилем.

Атрибут может содержать:

* готовую строку стиля;
* системное имя пресета стиля.

Пример:

```xml
<representation name="List" editMode="notEdit" rowStyleAttr="sStyle1">
  <attributes>
    <attr name="sCode" caption="Код" editorType="edit"
          order="10" isRequired="true"/>
    <attr name="sCaption" caption="Наименование" editorType="edit"
          order="20" isRequired="true"/>
    <attr name="sStyle1" caption="Первый стиль" editorType="edit"
          order="40"/>
  </attributes>
</representation>
```

![](../img/StyleToRow.png)

### Применение стиля к колонке

Для применения стиля к колонке в теге `attr` используется вложенный тег `style`.

Стиль можно указать с помощью одного из свойств:

* `name` — содержит строку стиля или системное имя пресета;
* `attr` — содержит имя атрибута, в котором хранится строка стиля или системное имя пресета.

Пример:

```xml
<representation name="List" editMode="notEdit">
  <attributes>
    <attr name="sCode" caption="Код" editorType="edit"
          order="10" isRequired="true">
      <style name="Color=$00880000"/>
    </attr>
    <attr name="sCaption" caption="Наименование" editorType="edit"
          order="20" isRequired="true">
      <style name="BDG_URTotalRow"/>
    </attr>
    <attr name="sStyle1" caption="Первый стиль" editorType="edit"
          order="40">
      <style attr="sStyle1"/>
    </attr>
  </attributes>
</representation>
```

![](../img/StyleToColumn.png)

Имя атрибута со стилем также можно установить в прикладном коде:

```scala
selection
  .attrs("Имя атрибута, к которому применяется стиль")
  .styleAttributeName =
    "Имя атрибута со строкой стиля или системным именем пресета"
```

## Формирование стиля с помощью StyleBuilder

`StyleBuilder` — класс для формирования и изменения стиля как объекта с набором параметров.

Класс также содержит цветовые и шрифтовые константы.

Внутри `StyleBuilder` используется изменяемый словарь с парами:

```text
название параметра GS -> (значение параметра, название параметра CSS)
```

Для сопоставления параметров используется неизменяемый вспомогательный словарь с парами:

```text
название параметра GS -> название параметра CSS
```

Значения изменяются и читаются с помощью сеттеров и геттеров. Для формирования результирующей строки используются отдельные методы.

`StyleBuilder` позволяет формировать:

* строку стиля Global;
* строку стиля CSS.

### Создание экземпляра StyleBuilder

Экземпляр `StyleBuilder` можно создать без исходных параметров:

```scala
val builder = StyleBuilder()
builder.build()
```

В этом случае метод `build()` возвращает пустую строку.

Также можно передать существующую строку стиля:

```scala
val stringStyle = "color=$00FF4400;font=arial;fontsize=15".ns
val builder = StyleBuilder(stringStyle)

builder.build()
```

Результирующая строка будет содержать те же параметры и значения, которые переданы в `stringStyle`. Порядок параметров в строке может измениться.

### Работа с цветовыми параметрами

Для работы с цветами в `StyleBuilder` используется класс `Color`.

Методы работы с цветовыми параметрами принимают и возвращают экземпляры `Color`. В сеттер также можно передать значение типа `String`, которое будет неявно преобразовано в `Color`.

При создании экземпляра из строки `Color` преобразует цвет в формат HEX. Если переданное значение невозможно преобразовать, будет вызвано исключение.

Поддерживаются следующие форматы:

* `$00BBGGRR`;
* `$BBGGRR`;
* `#RRGGBB`;
* имя Global-константы цвета.

Примеры создания цвета:

```scala
val color1 = Color.Red
val color2 = Color("$000000FF")
val color3 = Color("$0000FF")
val color4 = Color("#FF0000")
val color5 = Color("clRed")
```

Для получения строкового представления используются методы:

```scala
color1.getGsHex
color1.getHex
```

Метод `getGsHex` возвращает значение в формате Global:

```text
$000000FF
```

Метод `getHex` возвращает значение в формате HEX:

```text
#FF0000
```

Если в `Color` передана пустая строка, цвет считается отсутствующим.

### Примеры формирования стиля

Пример формирования строки стиля Global:

```scala
val style = StyleBuilder()
  .setBackgroundColor(Color.Blue)
  .setFontColor("#FFAAFF")
  .setFont(Font.Impact)
  .setFontUnderLine(1.nn)
  .build()
```

Метод `build()` вернёт строку:

```text
color=$00FF0000;fontcolor=$00FFAAFF;fontunderline=1;fontname=Impact
```

Пример изменения существующего стиля и формирования CSS:

```scala
val style2 = StyleBuilder(style)
  .setBackgroundColor(Color(""))
  .setFontColor("$00FFFFFF")
  .setFont("")
  .setFontUnderLine(NNumber())
  .setFontBold(1.nn)
  .buildCSS
```

В примере:

* `setBackgroundColor(Color(""))` удаляет цвет фона;
* `setFontColor("$00FFFFFF")` изменяет цвет шрифта;
* `setFont("")` удаляет параметр шрифта;
* `setFontUnderLine(NNumber())` удаляет подчёркивание;
* `setFontBold(1.nn)` включает полужирное начертание.

Метод `buildCSS` вернёт строку:

```css
color: #FFFFFF;
font-weight: bold;
```

## Пример разметки выборки

В примере определены:

* отображение `Default`;
* фильтр с макросом;
* список;
* атрибуты выборки;
* отображения `List`, `Card` и `Lookup`.

```xml
<?xml version="1.0"?>
<view xmlns="http://www.global-system.ru/xsd/global3-view-1.0"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.global-system.ru/xsd/global3-view-1.0"
      name="Bs_GdsCostDeviationType">
  <representation name="Default"
                  doubleClickOperation.createFormMode="CardEdit"
                  doubleClickOperation.lookupMode="CloseFormOK"
                  caption="Виды отклонений в стоимости ТМЦ">
    <filter name="Bs_GdsCostDeviationTypeFilter">
      <macros name="DefFltReferenceMacro">
        <condition logicalOperator="and"
                   id="shownotused"
                   isExpression="true"
                   expression="(:filter$flt_bShowNotUsed = 1
                     or
                     (coalesce(t.bnotactive,0) = 0
                     and
                     (t.dexpirydate is null
                     or t.dexpirydate > current_date)))">
          <filterAttr name="flt_bShowNotUsed"
                      attribute-type="Long"
                      caption="Отображать неиспользуемые"
                      isLastInLine="false"
                      order="10"
                      defaultValue="0"
                      editorType="check"/>
        </condition>
      </macros>
    </filter>
    <layout>
      <simpleComposer>
        <frame filter.isVisible="true" toolBar.isVisible="true">
          <grid/>
        </frame>
      </simpleComposer>
    </layout>
    <attributes>
      <attr name="id" caption="Идентификатор" isVisible="false"
            editorType="edit" order="-1"/>
      <attr name="idClass" caption="idClass" isVisible="false"
            editorType="edit" order="-2"/>
      <attr name="gid" isVisible="false" editorType="edit"/>
      <attr name="sCode" caption="Код" editorType="edit"
            order="10" isRequired="true"/>
      <attr name="sCaption" caption="Наименование" editorType="edit"
            order="20" isRequired="true"/>
      <attr name="sDescription" caption="Описание"
            editorType="memo" order="30"/>
      <attr name="bNotActive" caption="Не используется"
            editorType="check"/>
      <attr name="dExpiryDate" caption="Дата окончания использования"
            editorType="datePick"/>
    </attributes>
    <operations>
    </operations>
  </representation>
  <representation name="List" editMode="notEdit">
  </representation>
  <representation name="Card" editMode="edit"
                  stdFilter.isAvailable="false">
    <layout>
      <simpleComposer>
        <frame filter.isVisible="false">
          <card/>
        </frame>
      </simpleComposer>
    </layout>
  </representation>
  <representation name="Lookup" editMode="notEdit">
  </representation>
</view>
```
