Tgui перевод. Создание, копирование и удаление виджетовСоздание виджетов
Скачать 1.02 Mb.
|
Создание, копирование и удаление виджетов Создание виджетов Создание виджета происходит в два этапа: его создание и добавление к родителю. Каждый виджет предоставляет статическую функцию создания, которую вы можете использовать. Для некоторых виджетов функция имеет необязательные параметры (например, имя файла для изображения или текст для кнопки), но вы всегда можете вызвать функцию создания без параметров и установить эти свойства позже. tgui::Button::Ptr button = tgui::Button::create(); tgui::EditBox::Ptr editBox = tgui::EditBox::create(); Второй шаг - добавить виджет к его родителю. Обычно это объект Gui, но это также может быть, например, дочернее окно. Функция добавления может принимать необязательное имя в качестве второго параметра, который может быть использован для последующего извлечения виджета из родительского элемента, если вы сами не сохраните виджет. gui.add(button); gui.add(editBox, "MyWidgetName" ); По умолчанию виджеты будут использовать встроенную «белую» тему. Для них не нужны никакие внешние ресурсы, и у вас сразу появляется что - то на экране. Хотя вы можете настроить виджеты, изменив свойство по свойству, вы, вероятно, захотите использовать темы, чтобы придать виджетам другой вид. TGUI уже поставляется с некоторыми файлами тем, которые вы можете загрузить и использовать "из коробки". Получение виджетов После добавления виджета в родительский виджет (или непосредственно в графический интерфейс) вам больше не нужно хранить его самостоятельно, вы можете просто позволить указателю выйти из области видимости. Когда вам это понадобится позже, вы всегда можете получить его обратно из родительского виджета. Имя, которое функция get принимает в качестве параметра, было необязательным параметром функции add. tgui::EditBox::Ptr editBox = gui.get<tgui::EditBox>( "MyWidgetName" ); tgui::Widget::Ptr widget = gui.get( "MyWidgetName" ); Копирование виджетов Когда вы знаете тип вашего виджета, вы можете просто использовать функцию копирования. Однако скопированный виджет не привязан к тому же родительскому виджету, вы должны добавить его снова. Это сделано для того, чтобы вы могли, например, сделать копию виджета внутри дочернего окна, а затем добавить копию в другое дочернее окно. tgui::Button::Ptr newButton = tgui::Button::copy(oldButton); gui.add(newButton); Конечно, возможно, что вы не знаете тип виджета. Если вы сохранили все виджеты в списке и хотите скопировать список, вы не сможете использовать функцию копирования. В этой ситуации вы должны использовать вместо этого функцию клона. std::vector<tgui::Widget::Ptr> newList; for (const auto& widget : oldList) newList.push_back(widget->clone()); Удаление виджетов Когда виджет больше не нужен, вы можете удалить его из родительского контейнера. gui.remove(button); Вы также можете удалить все виджеты внутри контейнера одновременно. gui.removeAllWidgets(); Основные функции виджета Этот урок о функциях, которые вы найдете в базовом классе Widget. Эти функции доступны для всех виджетов. Положение и размер Каждому виджету можно присвоить определенную позицию и размер. widget->setPosition( 50 , 50 ); sf::Vector2f position = widget->getPosition(); widget->setSize( 200 , 50 ); sf::Vector2f size = widget->getSize(); Положение относительно родителя. Когда виджет добавляется в графический интерфейс, это не имеет значения, но когда виджет добавляется в дочернее окно, тогда положение виджета относительно этого дочернего окна. Если вам действительно необходимо знать точное положение виджета, то вы можете использовать функцию getAbsolutePosition (). Некоторые виджеты больше, чем их размер. Давайте возьмем слайдер в качестве примера. Размер соответствует размеру его дорожки, но большой палец может быть за пределами этой области. Функция getWidgetOffset () возвращает отрицательное смещение, которое имеет позиция большого пальца. Функция getFullSize () вернет весь размер виджета (в случае с ползунком он будет включать область, где может быть большой палец). Вместо констант функциям setPosition и setSize также могут быть заданы проценты в качестве аргумента, чтобы вы могли задать своему виджету позицию или размер относительно размера его родителя. widget->setPosition( "10%" , "5%" ); widget->setSize( "30%" , "10%" ); Более сложные аргументы могут быть переданы функции SetPosition и SetSize, как описано в Макеты учебник . Видимо и включено Свойство enabled определяет, получает ли виджет события. Если виджет отключен, он все равно будет отрисован, но он не будет реагировать на щелчки мышью. Свойство видимости определяет, отображается ли виджет. Скрытый виджет также больше не получает события, его больше нет. Единственный способ получить невидимый, но все еще работающий виджет - это использовать ClickableWidget. Скрытие и отображение виджетов похоже на их добавление и удаление, просто и быстрее. Может быть очень удобно помещать разные экраны в разные виджеты Panel и просто скрывать все панели, кроме одной. Каждый виджет виден и включен по умолчанию. widget->hide(); widget->show(); widget->isVisible(); widget->enable(); widget->disable(); widget->isEnabled(); Перекрывающиеся виджеты Что происходит, когда виджеты перекрываются? Какой самый верхний виджет? По умолчанию ответ прост: последний виджет, который вы добавили к родителю. Вы можете манипулировать порядком отображения виджетов, используя функции moveToFront и moveToBack. widget->moveToFront(); widget->moveToBack(); Введение в сигналы Подключение к сигналам Когда что - то происходит, виджеты могут отправлять сигнал (например, кнопка имеет сигнал «нажата», а флажок имеет сигналы «проверено» и «не проверено»). Вы можете привязать обработчик сигнала к такому сигналу с помощью функции подключения. Первый параметр - это имя сигнала, это триггер, по которому должен быть отправлен обратный вызов. Это имя не чувствительно к регистру, поэтому не имеет значения, пишете ли вы «нажата» или «нажата». Второй параметр - это функция, которую вы хотите вызывать при отправке сигнала. Вот пример разрешения вызова функции при нажатии кнопки. void signalHandler () { std::cout << "Button pressed" << std::endl; } button->connect( "pressed" , signalHandler); Конечно, вы также можете использовать лямбда - функции, если ваша функция очень мала. Это весь код, который необходим для закрытия окна при нажатии кнопки выхода: quitButton->connect( "pressed" , [&](){ window.close(); }); Обработчик сигнала также может принимать два параметра: виджет, который излучал сигнал, и имя сигнала. void signalHandler(tgui::Widget::Ptr widget, const std::string& signalName); editBox->connect( "TextChanged" , signalHandler); Пользовательские параметры Пользовательские параметры также могут быть предоставлены. Они должны быть предоставлены при вызове функции соединения и будут переданы обработчику сигнала при возникновении события. Имейте в виду, что при передаче переменной будет сделана копия. Если обработчик сигнала должен иметь доступ к исходной переменной, используйте std :: ref. void signalHandler1( int i); void signalHandler2(tgui::Gui& gui, tgui::Widget::Ptr widget, const std::string& signalName); editBox->connect( "TextChanged" , signalHandler1, 5 ); editBox->connect( "TextChanged" , signalHandler2, std::ref(gui)); При подключении функций - членов класса вы также должны помнить, что первым параметром такой функции является скрытый указатель «this». Вы всегда должны указывать значение указателя this вручную. struct Class { void signalHandler1(); void signalHandler2(tgui::Widget::Ptr widget, const std::string& signalName); void signalHandler3(tgui::Gui& gui, tgui::Widget::Ptr widget, const std::string& signalName); }; Class instance; editBox->connect( "TextChanged" , &Class::signalHandler1, &instance); editBox->connect( "TextChanged" , &Class::signalHandler2, &instance); editBox->connect( "TextChanged" , &Class::signalHandler3, &instance, std::ref(gui)); Необязательные параметры Виджеты могут дополнительно предоставлять информацию о произошедшем событии. Например, «нажатый» сигнал от кнопок предоставляет текст кнопки в качестве необязательного параметра, чтобы обработчик сигнала мог различить, какая кнопка была нажата. Функция соединения во время выполнения проверит, что сигнал Pressed поддерживает параметр sf :: String, и позволит тексту кнопки передаваться в качестве аргумента при нажатии кнопки. void buttonPressedCallback1(); button->connect( "pressed" , buttonPressedCallback1); void buttonPressedCallback2(const sf::String& buttonText); button->connect( "pressed" , buttonPressedCallback2); void buttonPressedCallback3(tgui::Widget::Ptr widget, const std::string& signalName, const sf::String& buttonText); button->connect( "pressed" , buttonPressedCallback3); Отключение сигналов Функция connect возвращает уникальный идентификатор, который можно использовать для отсоединения обработчика сигнала. unsigned int id = button1->connect( "pressed" , signalHandler); button1->disconnect(id); Также возможно отключить все обработчики сигналов одновременно. button1->disconnectAll( "pressed" ); button2->disconnectAll(); Макеты По отношению к родителю В основных функциях виджетов я показал, что setPosition и setSize могут быть заданы константами или значением относительно размера родительского виджета. widget->setPosition( "10%" , "5%" ); widget->setSize( "30%" , "10%" ); Относительные позиции могут быть полезны, но их недостаточно, например, для центрирования виджета, так как «50%» поместит верхний левый угол виджета в середину. Система макетов позволяет ссылаться на другие виджеты, поэтому, используя размер родительского элемента и размер самого виджета, легко отцентрировать виджет на экране: widget->setPosition( "(parent.size - size) / 2" ); «Родитель» может быть сокращен до «&». Следующей строкой центр виджета расположен горизонтально на 25% ширины родительского элемента, а вертикальный - внизу его родительского элемента. widget->setPosition({ "&.width / 4 - width / 2" , "&.height - height" }); Система верстки будет следить за тем, чтобы при изменении размера родительского элемента положение и размер виджета также обновлялись. Привязка других виджетов Вы можете ссылаться не только на родителя, но и на другие виджеты. В приведенном ниже примере кнопка расположена на 50 пикселей справа от другой кнопки. Имя, используемое в строке макета, должно совпадать с именем, заданным для функции добавления, когда виджет был добавлен к общему родителю. group->add(button1, "ButtonName" ); group->add(button2); button2->setPosition({ "ButtonName.right + 50" , "ButtonName.top" }); Существуют также функции связывания для непосредственного обращения к виджетам без необходимости их имени или добавления в родительский элемент. button2->setSize(bindSize(button1)); button2->setPosition({bindRight(button1) + 50 , bindTop(button1)}); Renderers Доступ к свойствам рендерера У каждого виджета есть средство визуализации, которое содержит свойства того, как виджет должен выглядеть (например, цвет фона и текста). Все виджеты имеют метод getRenderer (), который будет возвращать указатель на средство визуализации. Средство рендеринга, возвращаемое этой функцией, будет иметь установщики и получатели для свойств, доступных для этого конкретного типа виджета (т. Е. Средство визуализации списка имеет функции, отличные от функции кнопки). auto slider = tgui::Slider::create(); slider->getRenderer()->setTrackColor(sf::Color::Green); sf::Color thumbColor = slider->getRenderer()->getThumbColor(); Обмен рендерерами Рендереры (которые обычно происходят из тем ) по умолчанию являются общими. Если тема (или средство визуализации, представляющее тему) изменено, все виджеты, использующие средство визуализации, будут обновлены. Когда widget->getRenderer() вызывается, виджет, однако, переключится на локальную копию средства визуализации. Если вы хотите получить доступ к средству визуализации, которое используется совместно для виджетов, вы должны использовать widget->getSharedRenderer() вместо этого. Theme theme{ "TGUI/themes/Black.txt" }; button1->setRenderer(theme.getRenderer( "Button" )); button2->setRenderer(theme.getRenderer( "Button" )); button1->getSharedRenderer()->setBackgroundColor(sf::Color::Red); // Changes button2 too button1->getRenderer()->setBackgroundColor(sf::Color::Green); // Does not affect button2 button1->getSharedRenderer()->setBackgroundColor(sf::Color::Blue); // Does not affect button2, relation was broken with getRenderer() call Использование тем / скинов Использование тем Класс Theme - это простой интерфейс для загрузки и хранения средств визуализации Рендереры будут тем, что фактически определяет, как будут выглядеть виджеты, тема просто отвечает за их загрузку и хранение. По умолчанию тема загружает средства визуализации из файла, который можно указать при создании экземпляра или позже, вызвав функцию загрузки. tgui::Theme blackTheme{ "TGUI/themes/Black.txt" }; tgui::Theme greyTheme; greyTheme.load( "TGUI/themes/TransparentGrey.txt" ); После анализа файла вы можете получить доступ к средствам визуализации, которые были загружены по их идентификатору, который является их именем в загруженном файле. В темах, которые поставляются с TGUI, имя средства визуализации соответствует имени виджета. Поэтому, если вы хотите использовать черный скин для поля редактирования вместо белого по умолчанию, просто добавьте следующую строку в код: editBox->setRenderer(blackTheme.getRenderer( "EditBox" )); Обратите внимание, что имена всегда чувствительны к регистру, это не будет иметь значения, если вы передадите «editbox», «EditBox» или «EDITBOX» в функцию getRenderer. Добавление и удаление визуализаторов вручную Функция загрузки добавит средства визуализации в тему, но вы также можете добавить средства визуализации в тему самостоятельно: theme.addRenderer( "RendererName" , widget->getRenderer()); Аналогичным образом вы можете удалить рендеры из темы: theme.removeRenderer( "RendererName" ); Тема по умолчанию Вместо вызова widget->setRenderer(...) каждого виджета можно установить тему по умолчанию. Тема по умолчанию использует указатель, поэтому вы должны сохранить экземпляр темы живым. Если переданная тема setDefault действительно разрушается, тема по умолчанию автоматически сбрасывается. { tgui::Theme theme{ "TGUI/themes/Black.txt" }; tgui::Theme::setDefault(&theme); auto w1 = tgui::CheckBox::create(); // Uses the "checkbox" section from the Black theme auto w2 = tgui::ListBox::create(); // Uses the "listbox" section from the Black theme } auto w3 = tgui::EditBox::create(); // Uses the "editbox" section of the built-in White theme Изменение свойств рендерера Все виджеты, загруженные с одной и той же темой, используют один и тот же модуль рендеринга (до тех пор, пока не widget->getRenderer() будет вызван, как показано в руководстве по рендереру ). Это позволяет изменить внешний вид всех этих виджетов одновременно. Код ниже изменит цвет фона всех кнопок, загруженных с темой: tgui::ButtonRenderer(theme.getRenderer( "button" )).setBackgroundColor(sf::Colo r::Blue); Перезагрузка темы Вы можете не только изменять свойство рендерера по свойству, но и одновременно заменять их все одной инструкцией. Например, вы загрузили все виджеты со скином Black и хотите, чтобы все эти виджеты использовали скин BabyBlue. Это так же просто, как снова вызвать функцию загрузки: Theme theme{ "TGUI/themes/Black.txt" }; button->setRenderer(theme.getRenderer( "Button" )); // Button changes from White to Black skin theme.load( "TGUI/themes/BabyBlue.txt" ); // Button changes from Black to BabyBlue skin Показать / Скрыть анимацию Вы хотите, чтобы ваш виджет летел сбоку или исчезал? TGUI поддерживает эти анимации из коробки! Все, что вам нужно сделать, это сообщить функции showWithEffect или hideWithEffect, какой тип анимации вы хотите и как долго должна длиться анимация. button1->showWithEffect(tgui::ShowAnimationType::Fade, sf::milliseconds( 800 )); button2->showWithEffect(tgui::ShowAnimationType::SlideFromLeft, sf::milliseconds( 500 )); button3->hideWi HorizontalLayout и VerticalLayout Прежде всего, обсуждаемые здесь классы макетов блоков не имеют никакого отношения к классу Layout Существуют классы HorizontalLayout и VerticalLayout, так что вы можете легко размещать виджеты под или рядом друг с другом, не указывая им точное положение или размеры. Существует также виджет HorizontalWrap для размещения виджетов рядом друг с другом, но он начинает новую строку, когда ширина становится слишком большой. Сначала вы создаете объект макета и задаете ему размер, который вы хотите заполнить виджетами. Здесь виджеты будут покрывать всю ширину, имея высоту 10% окна. auto layout = tgui::HorizontalLayout::create(); layout->setSize( "100%" , "10%" ); layout->setPosition( 0 , 100 ); gui.add(layout); Давайте добавим несколько виджетов в текущий пустой макет. auto button1 = tgui::Button::create(); button1->setText( "First" ); layout->add(button1); auto button2 = tgui::Button::copy(button1); button2->setText( "Second" ); layout->add(button2); На данный момент у вас есть две кнопки, каждая из которых занимает 50% ширины окна. Давайте поместим некоторое пространство между ними, но мы хотим, чтобы пространство было меньше, чем фактические кнопки. Каждый виджет и пространство имеют определенное соотношение, по умолчанию это соотношение равно 1. Чтобы вычислить размер виджета, соотношение сравнивается с суммой всех соотношений. Это легко сделать на примере, у нас уже есть две кнопки с коэффициентом 1, и теперь мы добавляем пробел с коэффициентом 0,5, в результате чего общая сумма равна 2,5. Это означает, что каждая из кнопок будет занимать 40% (1 / 2,5) ширины, а пространство занимает оставшиеся 20% (0,5 / 2,5). Первый параметр для insertSpace - это индекс, который равен 1, потому что мы хотим вставить его между двумя кнопками. layout->insertSpace( 1 , 0.5 f); Visual Studio CMake • Вам нужно будет использовать CMake для сборки TGUI. Вы можете скачать последнюю версию здесь • Также убедитесь, что у вас уже есть sfml на вашем компьютере. Было бы еще лучше убедиться, что у вас работает sfml, прежде чем пытаться использовать TGUI. Если после этого что - то пойдет не так, вы можете быть уверены, что проблема не в sfml. Задайте местоположение каталога TGUI, укажите каталог сборки (обычно это новая пустая папка) и нажмите «Настроить». Затем выберите ваш генератор, который является вашей версией Visual Studio в этом руководстве. Просто оставьте флажок «Использовать встроенные компиляторы по умолчанию» и игнорируйте остальные три параметра. Если SFML не может быть найден автоматически, вы получите сообщение об ошибке. Если SFML не был найден, вы должны установить SFML_DIR папку, содержащую SFMLConfig.cmake (SFML> = 2.5) или альтернативно установить SFML_ROOT переменную в корневую папку SFML (каталог, содержащий папки include и lib). Когда вы используете, SFML_ROOT когда сами компилируете sfml, вам придется следить за тремя вещами: • Вам, вероятно, нужно поместить туда папку lib вместе с библиотеками, которые вы собрали. • Эта папка должна содержать библиотеки напрямую (не внутри папки Debug или Release). • Если вы решите создать статические библиотеки ниже, тогда папка, конечно, должна содержать статические библиотеки sfml. Теперь вы можете настроить параметры. Установите CMAKE_BUILD_TYPE параметр «Отладка» или «Выпуск» в зависимости от типа библиотеки, которую вы хотите. Вы также должны посмотреть на TGUI_SHARED_LIBS параметр, отметьте его для создания динамических библиотек (файлов .dll), снимите флажок для создания статических библиотек. Эта опция должна соответствовать выбранной для SFML. После настройки параметров, которые вам нужны, вам нужно снова нажать «Настроить». Теперь вы должны увидеть сообщение «Настройка выполнена» внизу. Все, что вам нужно сделать сейчас, это нажать Generate. Здание библиотеки Вы еще не закончили. CMake создал проект Visual Studio в каталоге сборки, который должен быть собран первым. Откройте файл tgui.sln. Вам все равно придется вручную изменить конфигурацию решения, чтобы она соответствовала CMAKE_BUILD_TYPE опции CMake. Теперь нажмите «Построить решение», чтобы собрать библиотеки TGUI. Как только это будет сделано, вы найдете библиотеки внутри подкаталога lib папки build. Чтобы упростить использование tgui в вашем проекте, вам, вероятно, следует скопировать файлы .lib (и .dll) в новую папку «lib» в корневом каталоге TGUI. Использование TGUI Откройте Свойства проекта. В режиме отладки и выпуска добавьте каталоги include и library. Как в режиме отладки, так и в режиме выпуска (на этот раз отдельно), вы должны добавить библиотеку для связи. Когда вы собираетесь использовать только один режим, вам, очевидно, не нужно менять другой. При статическом соединении вам нужно будет ссылаться на tgui - s.lib и tgui- sd.lib вместо tgui.lib и tgui -d.lib. Порядок ссылок также важен: сначала sfml, а затем tgui. При динамическом линковании не забудьте скопировать необходимые библиотеки в каталог, где находится ваш скомпилированный исполняемый файл. Теперь вы должны иметь возможность использовать TGUI. Минимальный код Есть четыре вещи, которые вы должны изменить в своем коде, чтобы заставить TGUI работать. Первое, что нужно сделать, это включить его. TGUI будет включать в себя «SFML / Graphics.hpp» для вас. #include Следующее, что нужно сделать, это создать объект Gui. sf::RenderWindow window{{ 800 , 600 }, "Window" }; tgui::Gui gui{window}; Когда у вас будут виджеты, им нужно будет получать события. В противном случае они не будут знать, находится ли ваша мышь над ними или нет. Каждый раз, когда вы получаете событие, вам нужно будет рассказать об этом событии. Итак, в вашем цикле событий вы должны добавить следующую строку: gui.handleEvent(event); Последнее, что нужно сделать, это нарисовать виджеты на экране. Для этого вы должны вызвать функцию рисования графического интерфейса. gui.draw(); Таким образом, полный код будет выглядеть так: #include int main () { sf::RenderWindow window{{ 800 , 600 }, "Window" }; tgui::Gui gui{window}; // Create the gui and attach it to the window while (window.isOpen()) { sf::Event event; while (window.pollEvent(event)) { if (event.type == sf::Event::Closed) window.close(); gui.handleEvent(event); // Pass the event to the widgets } window.clear(); gui.draw(); // Draw all widgets window.display(); } } |