Ru/VGUI Documentation: Difference between revisions
KindDragon (talk | contribs) No edit summary |
(Grammar fixes, text styles update, synced with Eng ver.) |
||
(21 intermediate revisions by 13 users not shown) | |||
Line 1: | Line 1: | ||
[[ | {{LanguageBar|title=Документация VGUI}} | ||
[[ | {{toc-right}} | ||
'''{{LCategory|VGUI}}''' - это собственный [[Wikipedia:Graphical User Interface|графический интерфейс пользователя]] Valve. Все приложения на {{src|2}} и {{steamicon|2}} используют VGUI для рисования окон, диалогов и меню. Он также обрабатывает [[#Localization|локализацию]]: отображение текста на предпочитаемом пользователем языке. | |||
* Архитектура объекта является иерархической, и все реализованные элементы являются производными от базовых классов VGUI. | |||
* Система ввода с клавиатуры/мыши основана на событиях и очень похожа на другие системы с графическим интерфейсом. | |||
* Реализации для наиболее распространённых элементов графического интерфейса, таких как кнопки, текстовые поля или изображения, предоставляются библиотекой элементов управления VGUI {{Path|vgui_controls|lib|icon=file}}. | |||
* Заголовки базового интерфейса расположены в {{Path|\public\vgui\}}. | |||
* Элементы управления определены в {{Path|\public\vgui_controls\}}. | |||
Как автор мода вы, скорее всего, будете использовать VGUI в проекте client.dll для отображения меню, элементов HUD или для отображения в игре (на оружии или компьютерных терминалах и т.д.). | |||
== Панели и иерархия == | |||
Базовый класс, из которого происходят все элементы VGUI: <code>vgui::Panel</code>, который определяет область на вашем экране, который имеет определенный размер и положение, может рисовать себя и обрабатывать входные события. Диалоговые окна, текстовые поля и кнопки являются панелями VGUI в иерархических отношениях родитель-потомок. Самая первая панель - это корневая панель, предоставляемая движком Source. Корневая панель клиента покрывает весь экран, но ничего не показывает. Даже если вы можете использовать корневую панель клиента, большинство панелей клиента используют общую панель <code>BaseViewport</code> в качестве родительской ( <code>g_pClientMode->GetViewport()</code> ). | |||
На этой диаграмме показана иерархия панелей VGUI в Counter-Strike: | |||
[[ | [[File:Vgui hierarchy.png|center|link=|Иерархия клиентских панелей VGUI]] | ||
Вы можете проверить иерархию панелей вашего мода с помощью '''Инструмент иерархии VGUI'''. Откройте его, отправив в {{L|консоль разработчика}} команду <code>vgui_drawtree 1</code>. | |||
[[File:Vgui drawtree.png|right|Инструмент иерархии VGUI]] | |||
Названия панелей имеют цветовую кодировку: | |||
* <span style="padding:0 .4em;background:#23221D;color:white;">Видимое</span> | |||
* <span style="padding:0 .4em;background:#23221D;color:grey;">Скрытое</span> | |||
* <span style="padding:0 .4em;background:#23221D;color:yellow;">Всплывающая панель (Frame)</span> | |||
* <span style="padding:0 .4em;background:#23221D;color:limegreen;">Имеет фокус</span> | |||
И следующие опции доступны как флажки: | |||
; Показать видимое | |||
: Список всех видимых панелей | |||
; Показать скрытое | |||
: Список всех скрытых панелей | |||
; Только всплывающие | |||
: Список только всплывающих панелей (Frames) | |||
; Выделить мышью | |||
: При наведении курсора мыши на панели выделяется цветная рамка. Дерево панели будет развернуто, чтобы показать текущую панель. | |||
; Заморозить | |||
: Блокирует текущий вид дерева | |||
; Показать адреса | |||
: Показывает адрес памяти панели | |||
; Показать Альфа | |||
: Показывает Альфа значение панели, 0 = полупрозрачный, 255 = непрозрачный | |||
; В порядке отрисовки | |||
: Сортировка панелей в дереве по порядку отрисовки | |||
==Класс Panel== | |||
VGUI класс Panel является базовым классом для более чем 50 различных элементов управления, они определены в {{Path|\public\vgui_controls\}}. Заголовочные файлы обычно предоставляют хорошую документацию о возможностях и поведении элементов. Следующая диаграмма показывает иерархию классов для некоторых самых часто используемых элементов: | |||
{| | <center> | ||
{|class="standard-table" | |||
|- | |- | ||
| | !colspan=5|Panel | ||
|- | |- | ||
| | |EditablePanel | ||
|Label | |||
|TextEntry | |||
|rowspan=3|RichText | |||
|rowspan=3|ImagePanel | |||
|- | |- | ||
| | |Frame | ||
|Button | |||
|rowspan=2|ComboBox | |||
|- | |- | ||
| | |MessageBox | ||
|ToggleButton | |||
| | |||
|} | |} | ||
</center> | |||
< | |||
Поскольку все VGUI классы наследуемые от Panel, мы должны рассмотреть основные методы и свойства. | Поскольку все VGUI классы наследуемые от Panel, мы должны рассмотреть основные методы и свойства. | ||
Line 64: | Line 78: | ||
VGUI хранит дескриптор для каждой новой панели, который используется для глобальной адресации панели без использования указателей. Много функций оповещающих о событиях используют эти дескрипторы <code>VPANEL</code> для идентификации панелей источника и получателя. Для получения уникального дескриптора <code>VPANEL</code> панели используйте <code>GetVPanel()</code>. | VGUI хранит дескриптор для каждой новой панели, который используется для глобальной адресации панели без использования указателей. Много функций оповещающих о событиях используют эти дескрипторы <code>VPANEL</code> для идентификации панелей источника и получателя. Для получения уникального дескриптора <code>VPANEL</code> панели используйте <code>GetVPanel()</code>. | ||
Для установки размеров и положения панели | Для установки размеров и положения панели используйте методы <code>SetPos(int x,int y)</code> и <code>SetSize(int wide,int tall)</code>. Размер всегда указывается в пикселях и никогда не изменяется автоматически с изменением разрешения экрана. Положение всегда относительно данной родительской панели, где (0,0) - верхний левый угол. | ||
Для индексации в иерархии панелей используется определенная панель как точка отсчета, дескриптор <code>VPANEL</code> родительской панели извлекается с помощью <code>GetParent()</code> или <code>GetVParent()</code>. Естественно, панель может иметь только одну родительскую, но несколько дочерних | Для индексации в иерархии панелей используется определенная панель как точка отсчета, дескриптор <code>VPANEL</code> родительской панели извлекается с помощью <code>GetParent()</code> или <code>GetVParent()</code>. Естественно, панель может иметь только одну родительскую, но несколько дочерних панелей (подпанелей, кнопок, список элементов и т.д.). <code>GetChildCount()</code> возвращает общее число дочерних элементов, где каждый из детей может быть извлечен по порядковому индексу через <code>GetChild(int index)</code>. | ||
Если панель должна реагировать на события ввода например нажатие клавиш или щелчки мыши, существует виртуальные функции <code>OnEvent()</code> на подобии <code>OnMousePressed(MouseCode code)</code> или <code>OnKeyCodePressed(KeyCode code)</code>, в вашем случае они должны быть переопределены. Обработка событий мыши или клавиатуры может быть включена/отключена для панели используя <code>SetMouseInputEnabled(bool state)</code> или <code>SetKeyBoardInputEnabled(bool state)</code>. | Если панель должна реагировать на события ввода например нажатие клавиш или щелчки мыши, существует виртуальные функции <code>OnEvent()</code> на подобии <code>OnMousePressed(MouseCode code)</code> или <code>OnKeyCodePressed(KeyCode code)</code>, в вашем случае они должны быть переопределены. Обработка событий мыши или клавиатуры может быть включена/отключена для панели используя <code>SetMouseInputEnabled(bool state)</code> или <code>SetKeyBoardInputEnabled(bool state)</code>. | ||
Другая важная виртуальная функция которая | Другая важная виртуальная функция которая должна быть переопределена эта <code>OnThink()</code> которая вызывается каждый раз когда панель перерисовывается. Иногда это слишком много и вы хотите обновить или проверить состояние панели менее часто. Для этого можно использовать обработчик <code>OnTick()</code> который вызывается постоянно через регулируемый интервал времени. Но перед тем как его использовать нужно сообщить VGUI что будет вызываться <code>OnTick()</code> для данной панели и как часто, это делается с помощью <code>ivgui()->AddTickSignal(VPANEL panel, int intervalMilliseconds)</code>. | ||
==Окна и всплывающие окна== | ==Окна и всплывающие окна== | ||
Окно описывается как лежащее выше и занимающее 100% пространства родителя, расположение всегда относительно и фиксировано, смещение можно изменить только с помощью кода. Также они отображают себя только в области занимаемую родителем, если перемещать VGUI элемент вне родительской панели, он будет обрезаться. Данные | Окно описывается как лежащее выше и занимающее 100% пространства родителя, расположение всегда относительно и фиксировано, смещение можно изменить только с помощью кода. Также они отображают себя только в области занимаемую родителем, если перемещать VGUI элемент вне родительской панели, он будет обрезаться. Данные элементы хорошо применять для управляющих элементов, изображений или текстовых полей на панелях, не способных к независимому перемещению пользователем, изменению размеров и сворачиванию. Для таких случаев VGUI панели применяют функцию <code>MakePopup()</code> которая отделяет панель от отрисовки родителя и делает его новым, независимым окном. Но по прежнему оно принадлежит родительской панели и становится невидимой вместе с ней. | ||
Иногда может показаться неудобным применение <code>MakePopup()</code>, тогда можно использовать класс Frame. Данный класс инкапсулирует все присущие GUI окнам возможности такие строка заголовка, системное меню (закрыть, свернуть, развернуть), перетаскивание, изменение размеров, фокус и управление форматом. | Иногда может показаться неудобным применение <code>MakePopup()</code>, тогда можно использовать класс Frame. Данный класс инкапсулирует все присущие GUI окнам возможности такие строка заголовка, системное меню (закрыть, свернуть, развернуть), перетаскивание, изменение размеров, фокус и управление форматом. | ||
Ниже приводится небольшой пример как открывать кадр и активировать его. Заметьте, что кадр удаляет себя когда пользователь закрывает кадр или вызывает его функцию <code>Close()</code> (следует удостовериться что деструктор освобождает все ресурсы). | |||
< | <source lang=cpp> | ||
Frame *pFrame = new Frame( g_pClientMode->GetViewport(), "MyFrame" ); | Frame *pFrame = new Frame( g_pClientMode->GetViewport(), "MyFrame" ); | ||
pFrame->SetScheme("ClientScheme.res"); | pFrame->SetScheme("ClientScheme.res"); | ||
Line 86: | Line 100: | ||
pFrame->SetTitle("My First Frame", true ); | pFrame->SetTitle("My First Frame", true ); | ||
pFrame->Activate(); // установить видимость, переместить на передний план, запросить фокус | pFrame->Activate(); // установить видимость, переместить на передний план, запросить фокус | ||
</ | </source> | ||
Если изменяются размеры | Если изменяются размеры кадра, происходит вызов <code>PerformLayout()</code> так, что кадр может реорганизовать расположение элементов для более удобного размещения. Текущее положение и размер кадра также можно зафиксировать с помощью <code>SetMoveable(bool state)</code> и <code>SetSizeable(bool state)</code>. | ||
==Сообщения о событиях== | ==Сообщения о событиях== | ||
Line 94: | Line 108: | ||
VGUI панели сообщаются через систему сообщений для сигнализации состояния изменений или событий панелей и их дочерних элементов (или других панелей). Сообщения не посылаются напрямую (например вызовом listener-функции панели), вместо этого они управляются VGUI который доставляет их панели получателю. Таким образом помимо содержания сообщения, панели отправитель и получатель должны быть определены через дескрипторы VPANEL. VGUI шлет сообщения о событиях чтобы информировать панели о изменениях или событиях (движение мышью, изменение фокуса ввода и т.д.). | VGUI панели сообщаются через систему сообщений для сигнализации состояния изменений или событий панелей и их дочерних элементов (или других панелей). Сообщения не посылаются напрямую (например вызовом listener-функции панели), вместо этого они управляются VGUI который доставляет их панели получателю. Таким образом помимо содержания сообщения, панели отправитель и получатель должны быть определены через дескрипторы VPANEL. VGUI шлет сообщения о событиях чтобы информировать панели о изменениях или событиях (движение мышью, изменение фокуса ввода и т.д.). | ||
Имя и содержание сообщения указывается объект KeyValues | Имя и содержание сообщения указывается объект KeyValues {{Path|\public\KeyValues|h}}. Класс KeyValues имеет очень общую и гибкую структуру для хранения записей данных содержащих строки, целые и вещественные числа. Объект KeyValues содержит имя и набор записей с данными. Каждая запись имеет уникальное ключевое имя и соответствующее значение. С помощью KeyValues также создаются иерархические структуры, использующие вложенные ключи, но в данном случае, большинство VGUI сообщений являются двухмерными записями. KeyValues не имеет определений данных/типов или чего-то подобного, поэтому можно добавлять или удалять записи любого типа как вам нравиться. Таким образом, отправитель и получатель должны знать внутреннюю организацию (например имена ключей, типы и их значения) объекта сообщений KeyValues для успешной коммуникации. Ниже приводится общий пример использования KeyValues: | ||
< | <source lang=cpp> | ||
// создаем новый объект KeyValues называемый "MyName" | // создаем новый объект KeyValues называемый "MyName" | ||
KeyValues *data = new KeyValues("MyName"); | KeyValues *data = new KeyValues("MyName"); | ||
Line 108: | Line 122: | ||
Con_Printf( data->GetString("aString") ); | Con_Printf( data->GetString("aString") ); | ||
// | // удаляет повторно объект KeyValues, освобождает данные записей | ||
data->deleteThis(); | data->deleteThis(); | ||
</ | </source> | ||
Для отправки сообщения можно вызвать функцию PostMessage( | Для отправки сообщения можно вызвать функцию PostMessage(...) класса Panel или непосредственно ivgui()-> PostMessage(...). Имя объекта KeyValues является также именем сообщения для дальнейшей отправки. VGUI вызывает функцию <code>OnMessage(...)</code> панели-получателя, которая отправляет сообщение предыдущему определенному дескриптору сообщения. Панель может зарегистрировать новые дескрипторы сообщений с помощью одного из <code>MESSAGE_FUNC_*</code> макросов, который добавляет дескриптор функции в список сообщений. Нельзя переопределять функцию <code>OnMessage(...)</code> для обработки новых сообщений, для этого всегда используется макрос. Пожалуйста заметьте когда вы используете макрос MESSAGE_FUNC_* только указанные сообщения вызывают вашу функцию. | ||
Если вы хотите перехватить сообщение newLine (ENTER) из элемента txtEntry вы будете использовать MESSAGE_FUNC_PARAMS( NewLineMessage, "TextNewLine",data). NewLineMessage - это вызов | Если вы хотите перехватить сообщение newLine (ENTER) из элемента txtEntry вы будете использовать MESSAGE_FUNC_PARAMS( NewLineMessage, "TextNewLine",data). NewLineMessage - это вызов функции, "TextNewLine" означает все сообщения у которых имя "TextNewLine" будут перенаправлены и конечные данные это ввод будут переданы вашей функции. В этом случае NewLineMessage будет выглядеть как <code>void MyParentPanel::NewLineMessage (KeyValues *data)</code>. | ||
< | <source lang=cpp> | ||
class MyParentPanel | class MyParentPanel : public vgui::Panel | ||
{ | { | ||
... | ... | ||
Line 127: | Line 141: | ||
const char *text = data->GetString("text"); | const char *text = data->GetString("text"); | ||
} | } | ||
</ | </source> | ||
Панель-отправитель создает объект KeyValues, добавляет параметры сообщения и отправляет сообщение (в данном случае ее родителю). VGUI уничтожает объект KeyValues после обработки. | Панель-отправитель создает объект KeyValues, добавляет параметры сообщения и отправляет сообщение (в данном случае ее родителю). VGUI уничтожает объект KeyValues после обработки. | ||
<source lang=cpp> | |||
< | |||
void MyChildPanel::SomethingHappend() | void MyChildPanel::SomethingHappend() | ||
{ | { | ||
Line 141: | Line 154: | ||
} | } | ||
} | } | ||
</ | </source> | ||
Используя <code>PostMessage()</code> отправляющая панель должна адресовать одного определенного получателя, что означает что все другие панели заинтересованные в изменении состояния должны быть известны и адресованы отдельно. Для избежания изнурительного программирования этих зависимостей, панели имеют общедоступную систему сообщений называемую сигналы действий. Панель генерирует общие события с помощью <code>PostActionSignal(KeyValues *message)</code> и интересуемые панели могут регистрироваться как listener-ы этих сигналов с помощью <code>AddActionSignalTarget(Panel *target)</code>. Данные сигналы действий широко используются элементами управления VGUI, например сообщения типа <code>"TextChanged"</code> генерируются классом TextEntry или <code>"ItemSelected"</code> используется классом ListPanel. Все сигналы действий содержат запись с указателем "panel" который указывает на панель-отправитель. | Используя <code>PostMessage()</code> отправляющая панель должна адресовать одного определенного получателя, что означает что все другие панели заинтересованные в изменении состояния должны быть известны и адресованы отдельно. Для избежания изнурительного программирования этих зависимостей, панели имеют общедоступную систему сообщений называемую сигналы действий. Панель генерирует общие события с помощью <code>PostActionSignal(KeyValues *message)</code> и интересуемые панели могут регистрироваться как listener-ы этих сигналов с помощью <code>AddActionSignalTarget(Panel *target)</code>. Данные сигналы действий широко используются элементами управления VGUI, например сообщения типа <code>"TextChanged"</code> генерируются классом TextEntry или <code>"ItemSelected"</code> используется классом ListPanel. Все сигналы действий содержат запись с указателем "panel" который указывает на панель-отправитель. | ||
Line 147: | Line 160: | ||
Пример использования данных сигналов потребует панели-родителя для регистрации как listene-ра, желательно в конструкторе после создания дочерней панели. Дочерняя панель использует <code>PostActionSignal()</code> вместо <code>PostMessage()</code>: | Пример использования данных сигналов потребует панели-родителя для регистрации как listene-ра, желательно в конструкторе после создания дочерней панели. Дочерняя панель использует <code>PostActionSignal()</code> вместо <code>PostMessage()</code>: | ||
< | <source lang=cpp> | ||
MyParentPanel::MyParentPanel() | MyParentPanel::MyParentPanel() | ||
{ | { | ||
Line 166: | Line 179: | ||
PostActionSignal ( msg ); | PostActionSignal ( msg ); | ||
} | } | ||
</ | </source> | ||
Чтобы перехватить клавишу enter из элемента txtEntry используйте это. | Чтобы перехватить клавишу enter из элемента txtEntry используйте это. | ||
< | <source lang=cpp> | ||
class MyParentPanel : public vgui::Panel | class MyParentPanel : public vgui::Panel | ||
{ | { | ||
Line 186: | Line 199: | ||
{ | { | ||
// когда txtEntry box отправляет сигнал действия это только устанавливает имя | // когда txtEntry box отправляет сигнал действия это только устанавливает имя | ||
// | // поэтому лучше проверить сфокусировано ли указанная txtEntry | ||
// Text Entry сфокусирована? | // Text Entry сфокусирована? | ||
if (m_pChildPanel->HasFocus()) | if (m_pChildPanel->HasFocus()) | ||
Line 197: | Line 210: | ||
// Dawid Joubert 2006-02-11 | // Dawid Joubert 2006-02-11 | ||
</ | </source> | ||
Часто используемым сигналом является сообщение "Command", для которого не требуется установки дескриптора сообщения. Панели должны порождать виртуальные функции <code>OnCommand(const char *command)</code> и проверять правильную строку команды. Сообщение "Command" используется всеми классами Button и генерируется каждый раз во время нажатия клавиши. Ниже приводится пример использования сообщения Command: | Часто используемым сигналом является сообщение "Command", для которого не требуется установки дескриптора сообщения. Панели должны порождать виртуальные функции <code>OnCommand(const char *command)</code> и проверять правильную строку команды. Сообщение "Command" используется всеми классами Button и генерируется каждый раз во время нажатия клавиши. Ниже приводится пример использования сообщения Command: | ||
< | <source lang=cpp> | ||
class MyParentPanel : public vgui::Panel | class MyParentPanel : public vgui::Panel | ||
{ | { | ||
Line 234: | Line 247: | ||
PostActionSignal ( msg ); | PostActionSignal ( msg ); | ||
} | } | ||
</ | </source> | ||
==Схемы== | ==Схемы== | ||
VGUI схемы описывают внешний вид панелей указывая используемые цвета, шрифты и иконки элементов управления. Схемы описываются в ресурсных файлах, например | VGUI схемы описывают внешний вид панелей указывая используемые цвета, шрифты и иконки элементов управления. Схемы описываются в ресурсных файлах, например {{Path|hl2\resource\clientscheme|res|icon=file}}. Новые панели наследуют по умолчанию схему и установки их родителя. Менеджер схем VGUI может загружать новые схемы с помощью <code>LoadSchemeFromFile(char *fileName, char *tag)</code> которая возвращает дескриптор <code>HScheme</code>. Для использования загруженной схемы VGUI вызывает функцию панели <code>SetScheme(HScheme scheme)</code>. | ||
Схемы устанавливают внешний вид элементов панели, но не устанавливают определенных элементов управления вашей панели. Единственный путь добавлять | Схемы устанавливают внешний вид элементов панели, но не устанавливают определенных элементов управления вашей панели. Единственный путь добавлять элементы вашей панели в вашем коде. Вы можете создавать их (обычно в конструкторе родительской панели) и установить свойства такие как размер и позиция непосредственно используемые в функциях панелей. Все это дает полный комплекс и экономию времени для длинных диалогов с большим количеством управляющих элементов. | ||
Наиболее удобный способ описать формат элементов панелей - это описать все | Наиболее удобный способ описать формат элементов панелей - это описать все элементы в внешнем ресурсном файле на подобии {{Path|hl2\resource\UI\classmenu|res|icon=file}} (текстовый файл содержащий записи KeyValues). Когда создается родительская панель, такой файл загружается и выполняется с помощью <code>LoadControlSettings(char *dialogResourceName)</code>. Однако, эта функция только определена для EditablePanel (или для наследников EditablePanel таких как Frame). В этом ресурсном файле каждый управляющий элемент описан в отдельной секции. Типичное описание управления описано так: | ||
< | <source lang=cpp> | ||
"MyControlName" | "MyControlName" | ||
{ | { | ||
Line 258: | Line 271: | ||
"textAlignment" "west" // right text alignment | "textAlignment" "west" // right text alignment | ||
} | } | ||
</ | </source> | ||
Каждое управляющее свойство имеет ключевое имя и значение. Свойства описаны в базовом классе Panel доступны для всех элементов (например xpos, ypos, wide, tall и т.д.). Список все доступных имен ключей и значений панели возвращаются функцией <code>GetDescription()</code>. Порождаемые классы могут добавлять их специфические свойства. Обработка этих новых полей должна реализоваться переопределенной виртуальной функцией <code>ApplySettings(KeyValues *inResourceData)</code>. | Каждое управляющее свойство имеет ключевое имя и значение. Свойства описаны в базовом классе Panel доступны для всех элементов (например xpos, ypos, wide, tall и т.д.). Список все доступных имен ключей и значений панели возвращаются функцией <code>GetDescription()</code>. Порождаемые классы могут добавлять их специфические свойства. Обработка этих новых полей должна реализоваться переопределенной виртуальной функцией <code>ApplySettings(KeyValues *inResourceData)</code>. Здесь также можно определять как интерпретируется новые значения для существующего свойства. | ||
==Режим построения== | ==Режим построения== | ||
[[ | [[File:Vgui2_4.jpg|frame|left|Режим построения VGUI]] | ||
Довольно простым является редактирование панели и расположения элементов в VGUI "Режим построения". Это позволит вам | Довольно простым является редактирование панели и расположения элементов в VGUI "Режим построения". Это позволит вам изменять и сохранять положение панели в файле ресурсов пока программа запущена. Для редактирования панели, запустите игру, откройте эту панель, так чтоб она стала в фокусе ввода. Затем нажмите {{key|Shift+Ctrl+Alt+B}} для перехода в режим встроенного VGUI редактора. В встроенном режиме редактирования вы можете просто перегруппировать существующие элементы и изменить их управляющие свойства (для обновления изменений нажмите 'Apply'). Для добавления нового управляющего элемента, выберите требуемый класс из комбо-бокса в нижней правой стороне и пустой объект управления данного класса будет помещен на панель для дальнейшего редактирования. Для сохранения текущего форматирования в ассоциированном ресурсном файле нажмите кнопку 'Save' (удостоверьтесь что ресурсный файл не защищен от записи). | ||
{{clr}} | |||
==Рисование и поверхности== | ==Рисование и поверхности== | ||
Используя схемы и свойства элементов управления вы можете менять основное расположение и | Используя схемы и свойства элементов управления вы можете менять основное расположение и форматирование существующих средств управления, но это это не позволяет создавать полностью новые элементы. Для изменения места положения панели требуется переопределить две виртуальные функции <code>Panel::Paint()</code> и <code>Panel::PaintBackground()</code>. В этих функциях вы можете использовать рисующие функции предоставляющиеся интерфейсом ISurface для размещения линий, прямоугольников, текста, рисунков и т.д. Во время обновления экрана, VGUI вначале вызывает <code>PaintBackground()</code> затем <code>Paint()</code> для каждой панели и ее дочерних панелей рекурсивно. Координаты отрисовки относительны к той панели в которой они используются. Ниже приводится пример кода который рисует красный прямоугольник в верхнем левом углу: | ||
< | <source lang=cpp> | ||
void MyPanel::Paint(void) | void MyPanel::Paint(void) | ||
{ | { | ||
Line 281: | Line 294: | ||
surface()->DrawFilledRect( 0, 0, 20, 20 ); //x0,y0,x1,y1 | surface()->DrawFilledRect( 0, 0, 20, 20 ); //x0,y0,x1,y1 | ||
} | } | ||
</ | </source> | ||
Для отрисовки текста на поверхности панели сначала понадобится установить используемый шрифт. | Для отрисовки текста на поверхности панели сначала понадобится установить используемый шрифт. Шрифты имеют названия похожие на "DefaultSmall" и свойства на подобии True-Type-шрифтов, размер и толщина определяются в ресурсном файле схемы. Дескриптор шрифта может быть возвращен вызовом <code>GetFont("Name")</code> текущей схемы панели, затем поверхность может быть указана к использованию в качестве текущего шрифта. После установки цвета и положения для следующего текстового вывода, сам текст должен быть передан как строка в расширенной кодировке (Unicode) в <code>DrawPrintText(...)</code>. Этот текст не будет выведен так как есть и никак не локализируется, поэтому последовательности локализации должны производиться вручную. | ||
< | <source lang=cpp> | ||
void MyPanel::Paint(void) | void MyPanel::Paint(void) | ||
{ | { | ||
Line 299: | Line 312: | ||
surface()->DrawPrintText( pText, wcslen(pText) ); // печатаем текст | surface()->DrawPrintText( pText, wcslen(pText) ); // печатаем текст | ||
} | } | ||
</ | </source> | ||
Для отрисовки | Для отрисовки {{L|texture|текстуры}} или картинки, VGUI требуется предварительно загрузить текстуру с диска (в конструкторе панели) и сгенерировать соответствующий текстурный ID. Этот ID затем используется как ссылка, когда отрисовывается текстура. Ниже приводится пример загрузки и отрисовки текстуры {{Path|\materials\your_folder\mylogo|vmt|icon=file}}: | ||
< | <source lang=cpp> | ||
MyPanel::MyPanel(void) | MyPanel::MyPanel(void) | ||
{ | { | ||
Line 316: | Line 329: | ||
vgui::surface()->DrawTexturedRect( 0, 0, 100, 100 ); | vgui::surface()->DrawTexturedRect( 0, 0, 100, 100 ); | ||
} | } | ||
</ | </source> | ||
==Пропорциональность== | ==Пропорциональность== | ||
Значения | Значения координат которые вы указываете в конфигурационном файле относительны разрешения 640x480, если SetProportional(true) вызван до загрузки файла. Эти значения будут масштабироваться к правильному значению без разницы какое разрешение может использоваться. Для примера, значение "tall" 120 будет всегда означать что панель занимает 1/4 высоты экрана, и значение "x" 320 означает что панель будет всегда по центру. | ||
Тем | {{tip|Используйте <code>'''c'''-<wide/tall></code> в ваших значениях <code>xpos</code> и <code>ypos</code> чтобы от'''ц'''ентрировать элемент на экране для разных соотношений сторон.}} | ||
Тем не менее, иногда вам нужно высчитать координаты для рисования и к сожалению отказаться от конфигурационных файлов. Проблема в том что значения которые вы указываете в коде относительны родного пользовательского расширения, которые могут совпадать или нет с текущим игровым разрешением (поэтому <code>surface()->GetScreenSize()</code> бесполезно). Решение проблемы в использование <code>scheme()->GetProportionalScaledValue()</code>. Это позволяет вам указывать координаты в разрешение 640x480 и они будут пересчитаны к текущему разрешению. | |||
==Локализация== | ==Локализация== | ||
Текстовые элементы VGUI поддерживают автоматическую локализацию для предпочтительного для пользователя языка. Предположим, если текстовая метка содержит "Hello world!" значит мы можем вывести этот текст напрямую функцией <code>SetText( "Hello World.")</code>. Но если пользователь изменит на другой язык кроме английского этот текст | Текстовые элементы VGUI поддерживают автоматическую локализацию для предпочтительного для пользователя языка. Предположим, если текстовая метка содержит "Hello world!" значит мы можем вывести этот текст напрямую функцией | ||
<code>SetText( "Hello World.")</code>. Но если пользователь изменит на другой язык кроме английского этот текст все равно останется текстом английского языка. Поэтому всегда нужно использовать локализированные последовательности чтоб сообщить VGUI о переводе последовательности в родной язык пользователя, в этом примере <code>SetText( "#MyMod_HelloWorld") | |||
</code> | |||
Последовательность является строкой начинающейся с знака весовой единицы '#' как управляющего символа для того чтобы сообщить VGUI что это не просто обычный текст. | |||
VGUI хранит глобальную таблицу перевода для связывания последовательностей с текстовым представлением. Эти таблицы перевода загружаются из ресурсных файлов, где каждый из файлов содержит специальную копию для каждого поддерживаемого языка (например {{Path|\resource\hl2_english|txt|icon=file}}, {{Path|\resource\hl2_german|txt|icon=file}}). | |||
{{tip|Файлы локализации должны храниться в формате UCS2-Little Endian. Редактировать локализацию вы можете используя {{xblahmt|4}}, {{vguiloct|4}}, или же вам может понадобиться продвинутый текстовые редакторы, например {{npp|4}} или {{vscode|4}}}} | |||
Новое описание последовательности для вашей игры может выглядеть следующим образом: | |||
В mymod_english.txt: | В mymod_english.txt: | ||
Line 339: | Line 361: | ||
<pre> | <pre> | ||
"MyMod_HelloWorld" " | "MyMod_HelloWorld" "Здравствуй мир." | ||
"[english]MyMod_HelloWorld" "Hello world." | "[english]MyMod_HelloWorld" "Hello world." | ||
</pre> | </pre> | ||
Если папка вашего мода называется "mymod" движок Source автоматически загружает корректный файл перевода (<code>/resource/gamedir_language.txt</code>). Вы также загружать дополнительные файлы перевода используя функцию <code>AddFile(...) интерфейса ILocalize. | Если папка вашего мода называется "mymod" движок Source автоматически загружает корректный файл перевода (<code>/resource/gamedir_language.txt</code>). Вы также загружать дополнительные файлы перевода используя функцию <code>AddFile(...)</code> интерфейса ILocalize. | ||
Вы также можете использовать интерфейс <code>ILocalize</code> для ручного перевода последовательности в текущий язык пользователя, например. <code>vgui::localize()->Find("#MyMod_HelloWorld")</code>. Эта функция возвращает перевод в виде 16-битной расширенной строки (Unicode). | Вы также можете использовать интерфейс <code>ILocalize</code> для ручного перевода последовательности в текущий язык пользователя, например. <code>vgui::localize()->Find("#MyMod_HelloWorld")</code>. Эта функция возвращает перевод в виде 16-битной расширенной строки (Unicode). | ||
VGUI использует Unicode для всех текстовых представлений для поддержки языков которые используют больше чем 255 ASCII символов такие как Китайский или Русский. Языковые ресурсные файлы кодируются в Unicode. Для перевода строк между ANSI ASCII и Unicode во время выполнения программы можно использовать функции интерфейса ILocalize <code>ConvertANSIToUnicode( | VGUI использует Unicode для всех текстовых представлений для поддержки языков которые используют больше чем 255 ASCII символов такие как Китайский или Русский. Языковые ресурсные файлы кодируются в Unicode. Для перевода строк между ANSI ASCII и Unicode во время выполнения программы можно использовать функции интерфейса ILocalize <code>ConvertANSIToUnicode(...)</code> and <code>ConvertUnicodeToANSI(...)</code>. Также очень важной функцией является <code>ConstructString(...)</code> которая в основном похожа на sprintf(...) для Unicode строк. | ||
==Смотрите также== | ==Смотрите также== | ||
* | * {{L|HUD Elements|Элементы HUD}} | ||
==Уроки== | ==Уроки== | ||
* | * {{L|Making GameUI Panels}} | ||
* | * {{L|Adding Your Logo to the Menu}} | ||
* | * {{L|VGUI Screen Creation|Creating a VGUI Screen}} | ||
* | * {{L|VGUI2:_Creating_a_panel|Creating a Panel}} | ||
{{ | {{ACategory|Programming}} | ||
{{ | {{ACategory|VGUI}} |
Latest revision as of 23:33, 21 June 2025
Category:VGUI - это собственный графический интерфейс пользователя Valve. Все приложения на Source и
Steam используют VGUI для рисования окон, диалогов и меню. Он также обрабатывает локализацию: отображение текста на предпочитаемом пользователем языке.
- Архитектура объекта является иерархической, и все реализованные элементы являются производными от базовых классов VGUI.
- Система ввода с клавиатуры/мыши основана на событиях и очень похожа на другие системы с графическим интерфейсом.
- Реализации для наиболее распространённых элементов графического интерфейса, таких как кнопки, текстовые поля или изображения, предоставляются библиотекой элементов управления VGUI
vgui_controls.lib
. - Заголовки базового интерфейса расположены в
\public\vgui\
. - Элементы управления определены в
\public\vgui_controls\
.
Как автор мода вы, скорее всего, будете использовать VGUI в проекте client.dll для отображения меню, элементов HUD или для отображения в игре (на оружии или компьютерных терминалах и т.д.).
Панели и иерархия
Базовый класс, из которого происходят все элементы VGUI: vgui::Panel
, который определяет область на вашем экране, который имеет определенный размер и положение, может рисовать себя и обрабатывать входные события. Диалоговые окна, текстовые поля и кнопки являются панелями VGUI в иерархических отношениях родитель-потомок. Самая первая панель - это корневая панель, предоставляемая движком Source. Корневая панель клиента покрывает весь экран, но ничего не показывает. Даже если вы можете использовать корневую панель клиента, большинство панелей клиента используют общую панель BaseViewport
в качестве родительской ( g_pClientMode->GetViewport()
).
На этой диаграмме показана иерархия панелей VGUI в Counter-Strike:

Вы можете проверить иерархию панелей вашего мода с помощью Инструмент иерархии VGUI. Откройте его, отправив в консоль разработчика команду vgui_drawtree 1
.
Названия панелей имеют цветовую кодировку:
- Видимое
- Скрытое
- Всплывающая панель (Frame)
- Имеет фокус
И следующие опции доступны как флажки:
- Показать видимое
- Список всех видимых панелей
- Показать скрытое
- Список всех скрытых панелей
- Только всплывающие
- Список только всплывающих панелей (Frames)
- Выделить мышью
- При наведении курсора мыши на панели выделяется цветная рамка. Дерево панели будет развернуто, чтобы показать текущую панель.
- Заморозить
- Блокирует текущий вид дерева
- Показать адреса
- Показывает адрес памяти панели
- Показать Альфа
- Показывает Альфа значение панели, 0 = полупрозрачный, 255 = непрозрачный
- В порядке отрисовки
- Сортировка панелей в дереве по порядку отрисовки
Класс Panel
VGUI класс Panel является базовым классом для более чем 50 различных элементов управления, они определены в \public\vgui_controls\
. Заголовочные файлы обычно предоставляют хорошую документацию о возможностях и поведении элементов. Следующая диаграмма показывает иерархию классов для некоторых самых часто используемых элементов:
Panel | ||||
---|---|---|---|---|
EditablePanel | Label | TextEntry | RichText | ImagePanel |
Frame | Button | ComboBox | ||
MessageBox | ToggleButton |
Поскольку все VGUI классы наследуемые от Panel, мы должны рассмотреть основные методы и свойства.
Наиболее часто используемый конструктор для создания панели это Panel(Panel *parent, const char *panelName)
, так как каждая панель нуждается в родительской панели и уникальном имени в его уровне иерархии. После того как панель создана вы можете отображать/скрывать ее с помощью SetVisible(bool state)
.
VGUI хранит дескриптор для каждой новой панели, который используется для глобальной адресации панели без использования указателей. Много функций оповещающих о событиях используют эти дескрипторы VPANEL
для идентификации панелей источника и получателя. Для получения уникального дескриптора VPANEL
панели используйте GetVPanel()
.
Для установки размеров и положения панели используйте методы SetPos(int x,int y)
и SetSize(int wide,int tall)
. Размер всегда указывается в пикселях и никогда не изменяется автоматически с изменением разрешения экрана. Положение всегда относительно данной родительской панели, где (0,0) - верхний левый угол.
Для индексации в иерархии панелей используется определенная панель как точка отсчета, дескриптор VPANEL
родительской панели извлекается с помощью GetParent()
или GetVParent()
. Естественно, панель может иметь только одну родительскую, но несколько дочерних панелей (подпанелей, кнопок, список элементов и т.д.). GetChildCount()
возвращает общее число дочерних элементов, где каждый из детей может быть извлечен по порядковому индексу через GetChild(int index)
.
Если панель должна реагировать на события ввода например нажатие клавиш или щелчки мыши, существует виртуальные функции OnEvent()
на подобии OnMousePressed(MouseCode code)
или OnKeyCodePressed(KeyCode code)
, в вашем случае они должны быть переопределены. Обработка событий мыши или клавиатуры может быть включена/отключена для панели используя SetMouseInputEnabled(bool state)
или SetKeyBoardInputEnabled(bool state)
.
Другая важная виртуальная функция которая должна быть переопределена эта OnThink()
которая вызывается каждый раз когда панель перерисовывается. Иногда это слишком много и вы хотите обновить или проверить состояние панели менее часто. Для этого можно использовать обработчик OnTick()
который вызывается постоянно через регулируемый интервал времени. Но перед тем как его использовать нужно сообщить VGUI что будет вызываться OnTick()
для данной панели и как часто, это делается с помощью ivgui()->AddTickSignal(VPANEL panel, int intervalMilliseconds)
.
Окна и всплывающие окна
Окно описывается как лежащее выше и занимающее 100% пространства родителя, расположение всегда относительно и фиксировано, смещение можно изменить только с помощью кода. Также они отображают себя только в области занимаемую родителем, если перемещать VGUI элемент вне родительской панели, он будет обрезаться. Данные элементы хорошо применять для управляющих элементов, изображений или текстовых полей на панелях, не способных к независимому перемещению пользователем, изменению размеров и сворачиванию. Для таких случаев VGUI панели применяют функцию MakePopup()
которая отделяет панель от отрисовки родителя и делает его новым, независимым окном. Но по прежнему оно принадлежит родительской панели и становится невидимой вместе с ней.
Иногда может показаться неудобным применение MakePopup()
, тогда можно использовать класс Frame. Данный класс инкапсулирует все присущие GUI окнам возможности такие строка заголовка, системное меню (закрыть, свернуть, развернуть), перетаскивание, изменение размеров, фокус и управление форматом.
Ниже приводится небольшой пример как открывать кадр и активировать его. Заметьте, что кадр удаляет себя когда пользователь закрывает кадр или вызывает его функцию Close()
(следует удостовериться что деструктор освобождает все ресурсы).
Frame *pFrame = new Frame( g_pClientMode->GetViewport(), "MyFrame" );
pFrame->SetScheme("ClientScheme.res");
pFrame->SetSize( 100, 100 );
pFrame->SetTitle("My First Frame", true );
pFrame->Activate(); // установить видимость, переместить на передний план, запросить фокус
Если изменяются размеры кадра, происходит вызов PerformLayout()
так, что кадр может реорганизовать расположение элементов для более удобного размещения. Текущее положение и размер кадра также можно зафиксировать с помощью SetMoveable(bool state)
и SetSizeable(bool state)
.
Сообщения о событиях
VGUI панели сообщаются через систему сообщений для сигнализации состояния изменений или событий панелей и их дочерних элементов (или других панелей). Сообщения не посылаются напрямую (например вызовом listener-функции панели), вместо этого они управляются VGUI который доставляет их панели получателю. Таким образом помимо содержания сообщения, панели отправитель и получатель должны быть определены через дескрипторы VPANEL. VGUI шлет сообщения о событиях чтобы информировать панели о изменениях или событиях (движение мышью, изменение фокуса ввода и т.д.).
Имя и содержание сообщения указывается объект KeyValues \public\KeyValues.h
. Класс KeyValues имеет очень общую и гибкую структуру для хранения записей данных содержащих строки, целые и вещественные числа. Объект KeyValues содержит имя и набор записей с данными. Каждая запись имеет уникальное ключевое имя и соответствующее значение. С помощью KeyValues также создаются иерархические структуры, использующие вложенные ключи, но в данном случае, большинство VGUI сообщений являются двухмерными записями. KeyValues не имеет определений данных/типов или чего-то подобного, поэтому можно добавлять или удалять записи любого типа как вам нравиться. Таким образом, отправитель и получатель должны знать внутреннюю организацию (например имена ключей, типы и их значения) объекта сообщений KeyValues для успешной коммуникации. Ниже приводится общий пример использования KeyValues:
// создаем новый объект KeyValues называемый "MyName"
KeyValues *data = new KeyValues("MyName");
// добавляет записи данных
data->SetInt( "anInteger", 42 );
data->SetString( "aString", "Marvin" );
// читает записи данных
int x = data->GetInt("anInteger");
Con_Printf( data->GetString("aString") );
// удаляет повторно объект KeyValues, освобождает данные записей
data->deleteThis();
Для отправки сообщения можно вызвать функцию PostMessage(...) класса Panel или непосредственно ivgui()-> PostMessage(...). Имя объекта KeyValues является также именем сообщения для дальнейшей отправки. VGUI вызывает функцию OnMessage(...)
панели-получателя, которая отправляет сообщение предыдущему определенному дескриптору сообщения. Панель может зарегистрировать новые дескрипторы сообщений с помощью одного из MESSAGE_FUNC_*
макросов, который добавляет дескриптор функции в список сообщений. Нельзя переопределять функцию OnMessage(...)
для обработки новых сообщений, для этого всегда используется макрос. Пожалуйста заметьте когда вы используете макрос MESSAGE_FUNC_* только указанные сообщения вызывают вашу функцию.
Если вы хотите перехватить сообщение newLine (ENTER) из элемента txtEntry вы будете использовать MESSAGE_FUNC_PARAMS( NewLineMessage, "TextNewLine",data). NewLineMessage - это вызов функции, "TextNewLine" означает все сообщения у которых имя "TextNewLine" будут перенаправлены и конечные данные это ввод будут переданы вашей функции. В этом случае NewLineMessage будет выглядеть как void MyParentPanel::NewLineMessage (KeyValues *data)
.
class MyParentPanel : public vgui::Panel
{
...
private:
MESSAGE_FUNC_PARAMS( OnMyMessage, "MyMessage", data );
}
void MyParentPanel::OnMyMessage (KeyValues *data)
{
const char *text = data->GetString("text");
}
Панель-отправитель создает объект KeyValues, добавляет параметры сообщения и отправляет сообщение (в данном случае ее родителю). VGUI уничтожает объект KeyValues после обработки.
void MyChildPanel::SomethingHappend()
{
if ( GetVParent() )
{
KeyValues *msg = new KeyValues("MyMessage");
msg->SetString("text", "Something happend");
PostMessage(GetVParent(), msg);
}
}
Используя PostMessage()
отправляющая панель должна адресовать одного определенного получателя, что означает что все другие панели заинтересованные в изменении состояния должны быть известны и адресованы отдельно. Для избежания изнурительного программирования этих зависимостей, панели имеют общедоступную систему сообщений называемую сигналы действий. Панель генерирует общие события с помощью PostActionSignal(KeyValues *message)
и интересуемые панели могут регистрироваться как listener-ы этих сигналов с помощью AddActionSignalTarget(Panel *target)
. Данные сигналы действий широко используются элементами управления VGUI, например сообщения типа "TextChanged"
генерируются классом TextEntry или "ItemSelected"
используется классом ListPanel. Все сигналы действий содержат запись с указателем "panel" который указывает на панель-отправитель.
Пример использования данных сигналов потребует панели-родителя для регистрации как listene-ра, желательно в конструкторе после создания дочерней панели. Дочерняя панель использует PostActionSignal()
вместо PostMessage()
:
MyParentPanel::MyParentPanel()
{
...
m_pChildPanel->AddActionSignalTarget( this );
}
void MyParentPanel::OnMyMessage (KeyValues *data)
{
const char *text = data->GetString("text");
Panel *pSender = (Panel *) kv->GetPtr("panel", NULL);
}
void MyChildPanel::SomethingHappend()
{
KeyValues *msg = new KeyValues("MyMessage");
msg->SetString("text", "Something happend");
PostActionSignal ( msg );
}
Чтобы перехватить клавишу enter из элемента txtEntry используйте это.
class MyParentPanel : public vgui::Panel
{
...
private:
MESSAGE_FUNC_PARAMS( OnMyMessage, "NewLineMessage", data );
}
MyParentPanel::MyParentPanel()
{
...
m_pChildPanel = new vgui::TextEntry( this, "txtEntry" );
m_pChildPanel->AddActionSignalTarget( this );
m_pChildPanel->SendNewLine(true); // с типом txtEntry вы должны установить это чтобы передавать клавишу enter как сообщение
}
void MyParentPanel::NewLineMessage (KeyValues *data)
{
// когда txtEntry box отправляет сигнал действия это только устанавливает имя
// поэтому лучше проверить сфокусировано ли указанная txtEntry
// Text Entry сфокусирована?
if (m_pChildPanel->HasFocus())
{
// Мы перехватили сообщение, теперь мы можем его обработать. Мы просто переотправляем в функцию OnCommand
// Отправляем функцию нашему обработчику команды
this->OnCommand("Submit");
}// End HasFocus()
}
// Dawid Joubert 2006-02-11
Часто используемым сигналом является сообщение "Command", для которого не требуется установки дескриптора сообщения. Панели должны порождать виртуальные функции OnCommand(const char *command)
и проверять правильную строку команды. Сообщение "Command" используется всеми классами Button и генерируется каждый раз во время нажатия клавиши. Ниже приводится пример использования сообщения Command:
class MyParentPanel : public vgui::Panel
{
...
protected:
virtual void OnCommand(const char *command);
}
MyParentPanel::MyParentPanel()
{
...
m_pChildPanel->AddActionSignalTarget( this );
}
void MyParentPanel::OnCommand(const char *command)
{
if (!stricmp(command, "SomethingHappend "))
{
DoSomething ();
}
else
{
BaseClass::OnCommand(command);
}
}
void MyChildPanel::SomethingHappend()
{
KeyValues *msg = new KeyValues("Command");
msg->SetString("command", "SomethingHappend");
PostActionSignal ( msg );
}
Схемы
VGUI схемы описывают внешний вид панелей указывая используемые цвета, шрифты и иконки элементов управления. Схемы описываются в ресурсных файлах, например hl2\resource\clientscheme.res
. Новые панели наследуют по умолчанию схему и установки их родителя. Менеджер схем VGUI может загружать новые схемы с помощью LoadSchemeFromFile(char *fileName, char *tag)
которая возвращает дескриптор HScheme
. Для использования загруженной схемы VGUI вызывает функцию панели SetScheme(HScheme scheme)
.
Схемы устанавливают внешний вид элементов панели, но не устанавливают определенных элементов управления вашей панели. Единственный путь добавлять элементы вашей панели в вашем коде. Вы можете создавать их (обычно в конструкторе родительской панели) и установить свойства такие как размер и позиция непосредственно используемые в функциях панелей. Все это дает полный комплекс и экономию времени для длинных диалогов с большим количеством управляющих элементов.
Наиболее удобный способ описать формат элементов панелей - это описать все элементы в внешнем ресурсном файле на подобии hl2\resource\UI\classmenu.res
(текстовый файл содержащий записи KeyValues). Когда создается родительская панель, такой файл загружается и выполняется с помощью LoadControlSettings(char *dialogResourceName)
. Однако, эта функция только определена для EditablePanel (или для наследников EditablePanel таких как Frame). В этом ресурсном файле каждый управляющий элемент описан в отдельной секции. Типичное описание управления описано так:
"MyControlName"
{
"ControlName" "Label" // control class
"fieldName" "MyControlName" // name of the control element
"xpos" "8" // x position
"ypos" "72" // y position
"wide" "160" // width in pixels
"tall" "24" // height in pixels
"visible" "1" // it's visible
"enabled" "1" // and enabled
"labelText" "Hello world" // show this text
"textAlignment" "west" // right text alignment
}
Каждое управляющее свойство имеет ключевое имя и значение. Свойства описаны в базовом классе Panel доступны для всех элементов (например xpos, ypos, wide, tall и т.д.). Список все доступных имен ключей и значений панели возвращаются функцией GetDescription()
. Порождаемые классы могут добавлять их специфические свойства. Обработка этих новых полей должна реализоваться переопределенной виртуальной функцией ApplySettings(KeyValues *inResourceData)
. Здесь также можно определять как интерпретируется новые значения для существующего свойства.
Режим построения
Довольно простым является редактирование панели и расположения элементов в VGUI "Режим построения". Это позволит вам изменять и сохранять положение панели в файле ресурсов пока программа запущена. Для редактирования панели, запустите игру, откройте эту панель, так чтоб она стала в фокусе ввода. Затем нажмите Shift+Ctrl+Alt+B для перехода в режим встроенного VGUI редактора. В встроенном режиме редактирования вы можете просто перегруппировать существующие элементы и изменить их управляющие свойства (для обновления изменений нажмите 'Apply'). Для добавления нового управляющего элемента, выберите требуемый класс из комбо-бокса в нижней правой стороне и пустой объект управления данного класса будет помещен на панель для дальнейшего редактирования. Для сохранения текущего форматирования в ассоциированном ресурсном файле нажмите кнопку 'Save' (удостоверьтесь что ресурсный файл не защищен от записи).
Рисование и поверхности
Используя схемы и свойства элементов управления вы можете менять основное расположение и форматирование существующих средств управления, но это это не позволяет создавать полностью новые элементы. Для изменения места положения панели требуется переопределить две виртуальные функции Panel::Paint()
и Panel::PaintBackground()
. В этих функциях вы можете использовать рисующие функции предоставляющиеся интерфейсом ISurface для размещения линий, прямоугольников, текста, рисунков и т.д. Во время обновления экрана, VGUI вначале вызывает PaintBackground()
затем Paint()
для каждой панели и ее дочерних панелей рекурсивно. Координаты отрисовки относительны к той панели в которой они используются. Ниже приводится пример кода который рисует красный прямоугольник в верхнем левом углу:
void MyPanel::Paint(void)
{
BaseClass::Paint();
surface()->DrawSetColor( 255, 0, 0, 255 ); //RGBA
surface()->DrawFilledRect( 0, 0, 20, 20 ); //x0,y0,x1,y1
}
Для отрисовки текста на поверхности панели сначала понадобится установить используемый шрифт. Шрифты имеют названия похожие на "DefaultSmall" и свойства на подобии True-Type-шрифтов, размер и толщина определяются в ресурсном файле схемы. Дескриптор шрифта может быть возвращен вызовом GetFont("Name")
текущей схемы панели, затем поверхность может быть указана к использованию в качестве текущего шрифта. После установки цвета и положения для следующего текстового вывода, сам текст должен быть передан как строка в расширенной кодировке (Unicode) в DrawPrintText(...)
. Этот текст не будет выведен так как есть и никак не локализируется, поэтому последовательности локализации должны производиться вручную.
void MyPanel::Paint(void)
{
wchar_t *pText = L"Hello world!"; // wide char text
// получаем правильный дескриптор для этой схемы
vgui::IScheme *pScheme = vgui::scheme()->GetIScheme(GetScheme());
vgui::HFont hFont = pScheme->GetFont( "DefaultSmall" );
surface()->DrawSetTextFont( hFont ); // устанавливаем шрифт
surface()->DrawSetTextColor( 255, 0, 0, 255 ); // красный
surface()->DrawSetTextPos( 10, 10 ); // позиция x,y
surface()->DrawPrintText( pText, wcslen(pText) ); // печатаем текст
}
Для отрисовки текстуры или картинки, VGUI требуется предварительно загрузить текстуру с диска (в конструкторе панели) и сгенерировать соответствующий текстурный ID. Этот ID затем используется как ссылка, когда отрисовывается текстура. Ниже приводится пример загрузки и отрисовки текстуры \materials\your_folder\mylogo.vmt
:
MyPanel::MyPanel(void)
{
//Do _not_ forget CreateNewTextureID(), it gave me a headache for a week
m_nTextureID = vgui::surface()->CreateNewTextureID();
vgui::surface()->DrawSetTextureFile( m_nTextureID, "materials/your_folder/mylogo" , true, false);
}
void MyPanel::Paint(void)
{
vgui::surface()->DrawSetTexture( m_nTextureID );
vgui::surface()->DrawTexturedRect( 0, 0, 100, 100 );
}
Пропорциональность
Значения координат которые вы указываете в конфигурационном файле относительны разрешения 640x480, если SetProportional(true) вызван до загрузки файла. Эти значения будут масштабироваться к правильному значению без разницы какое разрешение может использоваться. Для примера, значение "tall" 120 будет всегда означать что панель занимает 1/4 высоты экрана, и значение "x" 320 означает что панель будет всегда по центру.

c-<wide/tall>
в ваших значениях xpos
и ypos
чтобы отцентрировать элемент на экране для разных соотношений сторон.Тем не менее, иногда вам нужно высчитать координаты для рисования и к сожалению отказаться от конфигурационных файлов. Проблема в том что значения которые вы указываете в коде относительны родного пользовательского расширения, которые могут совпадать или нет с текущим игровым разрешением (поэтому surface()->GetScreenSize()
бесполезно). Решение проблемы в использование scheme()->GetProportionalScaledValue()
. Это позволяет вам указывать координаты в разрешение 640x480 и они будут пересчитаны к текущему разрешению.
Локализация
Текстовые элементы VGUI поддерживают автоматическую локализацию для предпочтительного для пользователя языка. Предположим, если текстовая метка содержит "Hello world!" значит мы можем вывести этот текст напрямую функцией
SetText( "Hello World.")
. Но если пользователь изменит на другой язык кроме английского этот текст все равно останется текстом английского языка. Поэтому всегда нужно использовать локализированные последовательности чтоб сообщить VGUI о переводе последовательности в родной язык пользователя, в этом примере SetText( "#MyMod_HelloWorld")
Последовательность является строкой начинающейся с знака весовой единицы '#' как управляющего символа для того чтобы сообщить VGUI что это не просто обычный текст.
VGUI хранит глобальную таблицу перевода для связывания последовательностей с текстовым представлением. Эти таблицы перевода загружаются из ресурсных файлов, где каждый из файлов содержит специальную копию для каждого поддерживаемого языка (например \resource\hl2_english.txt
, \resource\hl2_german.txt
).





Новое описание последовательности для вашей игры может выглядеть следующим образом:
В mymod_english.txt:
"MyMod_HelloWorld" "Hello world."
В mymod_russian.txt:
"MyMod_HelloWorld" "Здравствуй мир." "[english]MyMod_HelloWorld" "Hello world."
Если папка вашего мода называется "mymod" движок Source автоматически загружает корректный файл перевода (/resource/gamedir_language.txt
). Вы также загружать дополнительные файлы перевода используя функцию AddFile(...)
интерфейса ILocalize.
Вы также можете использовать интерфейс ILocalize
для ручного перевода последовательности в текущий язык пользователя, например. vgui::localize()->Find("#MyMod_HelloWorld")
. Эта функция возвращает перевод в виде 16-битной расширенной строки (Unicode).
VGUI использует Unicode для всех текстовых представлений для поддержки языков которые используют больше чем 255 ASCII символов такие как Китайский или Русский. Языковые ресурсные файлы кодируются в Unicode. Для перевода строк между ANSI ASCII и Unicode во время выполнения программы можно использовать функции интерфейса ILocalize ConvertANSIToUnicode(...)
and ConvertUnicodeToANSI(...)
. Также очень важной функцией является ConstructString(...)
которая в основном похожа на sprintf(...) для Unicode строк.