Пользовательский интерфейс в Urho3D

Материал из Энциклопедия о программировании
Перейти к: навигация, поиск

Urho3D реализует простую иерархическую систему пользовательского интерфейса, основанную на прямоугольных элементах.

Элементы UI

Предоставляемые элементы:

  • BorderImage – изображение текстуры с опциональной рамкой
  • Button – кнопка.
  • CheckBox – чекбокс, т.е. переключатель, что можно включать/выключать.
  • Cursor – курсор мыши.
  • DropDownList – показывает вертикальный список элементов (с опциональной возможностью прокрутки) в виде всплывающего окна.
  • LineEdit – однострочный текстовый редактор.
  • ListView – показывает прокручиваемый вертикальный список элементов.
  • Menu – кнопка, которая может отображать всплывающий элемент.
  • ScrollBar – слайдер с кнопками назад и вперед.
  • ScrollView – прокручиваемый вид дочерних элементов.
  • Slider – горизонтальный или вертикальный ползунок.
  • Sprite – изображение текстуры, поддерживающее субпиксельное позиционирование, масштабирование и вращение.
  • Text – статический текст, который может быть многострочным.
  • ToolTip – всплывающее окно, которое автоматически отображается при наведении курсора на его родительский элемент.
  • UIElement – контейнер для других элементов, сам по себе ничего не отображает.
  • View3D – окно, которое отображает трехмерное окно просмотра.
  • Window – подвижное окно с изменяемым размером.

Корневой элемент пользовательского интерфейса можно запросить из подсистемы пользовательского интерфейса с помощью функции GetRoot. Это пустой холст (UIElement) размером с окно приложения, в который можно добавлять другие элементы.

Элементы добавляются друг в друга аналогично узлам сцены с помощью функций AddChild и удаляются с помощью RemoveChild. Каждый элемент пользовательского интерфейса также имеет пользовательские переменные VariantMap для хранения пользовательских данных и возможность добавления тегов для идентификации: см. функц. AddTag, RemoveTag, SetTags и GetChildrenWithTag.

Чтобы элементы могли реагировать на ввод с помощью мыши, элемент курсора мыши должен быть либо определен с помощью SetCursor, либо курсор мыши ОС должен быть установлен видимым из подсистемы ввода.

UI текстуры

BorderImage и производные от него элементы определяют текстуру и абсолютный пиксельный прямоугольник внутри него, чтобы использовать его для рендеринга; см. функц. SetTexture и SetImageRect. Текстура модулируется цветом элемента. Чтобы обеспечить более гибкое масштабирование, элемент можно разделить на 9 вложенных квадратов или заплаток, указав ширину каждой из его границ, см. функц. SetBorder. Установка нулевых границ (по-умолчанию) приводит к тому, что элемент отображается как один четырехугольник.

Абсолютные пиксельные прямоугольники плохо взаимодействуют с настройкой качества текстуры Renderer, что уменьшает размеры текстуры за счет пропуска самых верхних MIP-карт. Создание MIP-карт также часто не требуется для текстур пользовательского интерфейса, поскольку они обычно отображаются на экране с соотношением сторон 1:1. Поэтому рекомендуется использовать следующий прилагаемый XML-файл настроек для текстур пользовательского интерфейса, чтобы отключить снижение качества и MIP-карты:

<texture>
	<mipmap enable="false" />
	<quality low="0" />
</texture>

Файлы макета и определения стилей UI

Элементы пользовательского интерфейса являются производными от Serializable, поэтому их можно сериализовать в/из XML с помощью своих атрибутов. Существует два варианта использования файлов определения пользовательского интерфейса: либо определение только стиля элемента пользовательского интерфейса (напр., изображение, изменяющееся для каждого состояния кнопки, либо шрифт, используемый для текста), и оставление фактического положения и размеров для заполнения позже, или полное определение макета элемента пользовательского интерфейса. Определения стилей элементов по-умолчанию, используемые, напр., редактором и консолью отладки, находятся в файле "bin/Data/UI/DefaultStyle.xml".

Функция LoadLayout в пользовательском интерфейсе принимает XML-файл и создает экземпляры определенных в нём элементов. Чтобы быть действительным, в XML должен быть один элемент пользовательского интерфейса корневого уровня. Можно указать необязательный XML-файл стиля; идея состоит в том, чтобы сначала прочитать стиль элемента из этого файла, а затем заполнить остальное из фактического XML-файла макета. Т.о., файл макета может быть относительно простым, поскольку большая часть данных уже определена.

Обратите внимание, что стиль не может быть легко рекурсивно применен к загруженным элементам впоследствии. Поэтому не забудьте указать файл стиля уже при загрузке или, в качестве альтернативы, назначить файл стиля по-умолчанию корневому элементу пользовательского интерфейса, который затем будет выбран всеми загруженными макетами. Это работает, потому что подсистема пользовательского интерфейса ищет файл стиля, поднимаясь по родительской цепочке, начиная с целевого родительского элемента пользовательского интерфейса. Поиск прекращается сразу же после того, как будет найден файл стиля или когда он достигнет корневого элемента. Также обратите внимание, что Urho3D не ограничивает количество файлов стилей, одновременно используемых в приложении. У вас может быть другой файл стиля, установленный в родительской иерархии пользовательского интерфейса, если вашему приложению это необходимо.

См. код C++ элементов для всех поддерживаемых атрибутов и примеры макетов пользовательского интерфейса редактора в каталоге "bin/Data/UI". Вы также можете использовать приложение Editor для создания макетов пользовательского интерфейса. Формат сериализации аналогичен XML-сериализации сцены с тремя важными отличиями:

  1. Тип элемента для создания экземпляра и стиль, который будет использоваться для него, можно установить отдельно. Напр., следующее определение элемента
    <element type="Button" style="CloseButton" />

    сообщает о необходимости создания экземпляра элемента Button и о том, что он должен использовать стиль «CloseButton», определенный в XML-файле стиля.

  2. Внутренние дочерние элементы, напр., полосы прокрутки ScrollView, должны быть отмечены как таковые, чтобы избежать их создания как дубликатов. Это делается путем добавления атрибута internal="true" к элементу XML и требуется как в файлах макета, так и в файлах стиля XML. Кроме того, элементы должны быть перечислены в том порядке, в котором они были добавлены в качестве дочерних по отношению к родительскому элементу (в случае сомнений см. код конструктора C++ элемента. Допускается пропуск элементов в середине). Напр.:
    <element type="ScrollView" />
    	<element type="ScrollBar" internal="true" />
    		...customize the horizontal scroll bar attributes here...
    	</element>
    	<element type="ScrollBar" internal="true" />
    		...customize the vertical scroll bar attributes here...
    	</element>
    </element>
    
  3. Всплывающий элемент, отображаемый Menu и DropDownList, не является фактическим дочерним элементом. При сериализации XML он, тем не менее, сохраняется как дочерний элемент, но помечается атрибутом popup="true".

Программное определение макетов UI

Вместо загрузки иерархии элементов пользовательского интерфейса из файла определения так же допустимо (хотя и громоздко для более крупных иерархий) создавать элементы в коде, что демонстрируется несколькими примерами Urho3D.

В этом режиме работы стили не применяются автоматически при добавлении или создании элемента в иерархию, даже если родительскому элементу (или корню пользовательского интерфейса) назначен файл стилей по-умолчанию. Это потому, что применение стиля к элементу означает просто установку ряда атрибутов и потенциально может быть «деструктивным», т.е. перезаписать уже установленные значения. Для каждого созданного элемента вам необходимо вручную вызвать либо SetStyle, чтобы указать имя стиля, который следует применить, либо SetStyleAuto, чтобы использовать имя типа элемента в качестве имени стиля, напр. стиль «Button» для элемента Button.

Макет дочернего элемента

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

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

Для макета также можно указать ширину левой, верхней, правой и нижней границ и расстояние между элементами. Макет сетки напрямую не поддерживается, но его можно создать вручную с помощью горизонтального макета внутри вертикального макета или наоборот.

Используйте функции SetLayout или SetLayoutMode для управления компоновкой.

Привязка дочернего элемента

Отдельным от макета механизмом, который позволяет автоматически настраивать иерархию пользовательского интерфейса, является использование привязки. Сначала включите привязку в дочернем элементе с помощью SetEnableAnchor, после чего верхний левый и нижний правый углы по отношению к размеру родительского элемента (диапазон 0-1) можно установить с помощью SetMinAnchor и SetMaxAnchor. Углы можно дополнительно смещать в пикселях, вызывая SetMinOffset и SetMaxOffset. Наконец, обратите внимание, что вместо того, чтобы просто устанавливать горизонтальное/вертикальное выравнивание, поворот дочернего элемента также может быть выражен в диапазоне от 0 до 1 относительно его размера путем вызова SetPivot.

Шрифты

Urho3D поддерживает как шрифты FreeType (.ttf, .otf), так и "растровые" шрифты (http://www.angelcode.com/products/bmfont/).

Для шрифтов FreeType можно настроить расположение глифов шрифта. См. SetAbsoluteGlyphOffset, чтобы установить фиксированное смещение в пикселях для всех размеров точки, или SetScaledGlyphOffset, чтобы установить смещение с плавающей запятой, которое будет умножено на размер точки перед применением. Информация о смещении также может быть сохранена в сопроводительном XML-файле рядом с файлом шрифта, который отформатирован следующим образом: (допустимо указывать одно или оба из абсолютных и масштабированных смещений, а также одну или обе координаты X и Y)

<font>
	<absoluteoffset x="xInt" y="yInt" />
	<scaledoffset x="xFloat" y="yFloat" />
</font>

В классе UI имеются различные глобальные параметры конфигурации для рендеринга шрифтов. Настройки по-умолчанию аналогичны рендерингу в стиле Windows, с четкими символами, но неравномерным интервалом. Для рендеринга в стиле macOS с точным интервалом, но немного более размытыми контурами вызовите UI::SetFontHintLevel(FONT_HINT_LEVEL_NONE). Используйте типографический пример, чтобы изучить эти параметры и найти лучшую конфигурацию для вашей игры.

По-умолчанию контурные шрифты будут иметь хинтинг (выровнены по границам пикселей), если возможно, используя встроенные в шрифт хинты. Вызовите SetForceAutoHint, чтобы использовать стандартный авто-хинтер FreeType, а не встроенные хинты каждого шрифта.

Чтобы настроить хинты, вызовите SetFontHintLevel. Если установлен уровень FONT_HINT_LEVEL_LIGHT, шрифты будут выровнены по пиксельной сетке по вертикали, но не по горизонтали. На FONT_HINT_LEVEL_NONE хинтинг полностью отключен.

Уровни хинтов LIGHT и NONE позволяют размещать глифы субпикселей, что значительно улучшает интервалы, особенно при малых размерах шрифта. По-умолчанию субпиксельное позиционирование используется только при размерах до 12 точек; при больших размерах каждый глиф выравнивается по пикселям. Вызовите SetFontSubpixelThreshold, чтобы изменить этот порог.

Когда текстуры не выровнены по пикселям, билинейная фильтрация текстур делает их размытыми. Поэтому текстуры субпиксельных шрифтов подвергаются передискретизации (растягиваются по горизонтали), чтобы уменьшить эту размытость за счет использования дополнительной памяти. По-умолчанию субпиксельные шрифты используют двукратную передискретизацию. Вызовите SetFontOversampling, чтобы изменить это.

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

Спрайты

Спрайты - особый вид элементов пользовательского интерфейса, которые позволяют субпиксельное (плавающее) позиционирование и масштабирование, а также вращение, в то время как другие элементы используют целочисленное позиционирование для идеального отображения пикселей. Спрайты могут использоваться для реализации вращающихся элементов HUD, таких как миникарты или стрелки спидометра.

Из-за возможности свободного преобразования спрайты нельзя надежно запрашивать с помощью GetElementAt. Кроме того, только другие спрайты должны быть родительскими для спрайтов, поскольку другие элементы не поддерживают масштабирование и вращение.

Фигуры курсора

Urho3D поддерживает настраиваемые формы курсора, определенные на основе изображения.

Форма может быть значением ОС по-умолчанию из перечисления CursorShape, которое автоматически переключается на и обратно с помощью подсистемы UI, но её можно переключить вручную с помощью Cursor::SetShape(CursorShape).

В качестве альтернативы они могут быть определены с использованием имени в формате String для его идентификации, которое можно только вручную переключить с помощью Cursor::SetShape(const String&).

Существует ряд зарезервированных имен, которые используются ОС для значений курсора по-умолчанию:

  • Normal
  • IBeam
  • Cross
  • ResizeVertical
  • ResizeDiagonalTopRight
  • ResizeHorizontal
  • ResizeDiagonalTopLeft
  • ResizeAll
  • AcceptDrop
  • RejectDrop
  • Busy
  • BusyArrow

Формы курсора (Cursor) можно определить несколькими способами:
XML:

<element type="Cursor">
	<attribute name="Shapes">
		<variant type="VariantVector" >
			<variant type="String" value="Normal" />
			<variant type="ResourceRef" value="Image;Textures/UI.png" />
			<variant type="IntRect" value="0 0 12 24" />
			<variant type="IntVector2" value="0 0" />
		</variant>
		<variant type="VariantVector" >
			<variant type="String" value="Custom" />
			<variant type="ResourceRef" value="Image;Textures/UI.png" />
			<variant type="IntRect" value="12 0 12 36" />
			<variant type="IntVector2" value="0 0" />
		</variant>
	</atrribute>
</element>

C++:

UI* ui = GetSubsystem<UI>();
ResourceCache* rc = GetSubsystem<ResourceCache>();
Cursor* cursor = new Cursor(context_);
Image* image = rc->GetResource<Image>("Textures/UI.png");
if (image) {
	cursor->DefineShape(CS_NORMAL, image, IntRect(0, 0, 12, 24), IntVector2(0, 0));
	cursor->DefineShape("Custom", image, IntRect(12, 0, 12, 36), IntVector2(0, 0));
}
ui->SetCursor(cursor);

Angelcode:

Cursor@ cursor = new Cursor();
Image@ image = cache.GetResource("Image", "Textures/UI.png");
if (image !is null) {
	cursor.DefineShape(CS_NORMAL, image, IntRect(0, 0, 12, 24), IntVector2(0, 0));
	cursor.DefineShape("Custom", image, IntRect(12, 0, 12, 36), IntVector2(0, 0));
}
ui.SetCursor(cursor);

Масштабирование

По-умолчанию пользовательский интерфейс идеален до пикселя: размер корневого элемента равен размеру окна приложения.

Масштабирование пикселей можно изменить с помощью функций SetScale, SetWidth и SetHeight.