Шрифты и строки
Названия гарнитур системных шрифтов мы получаем с помощью метода static const StringArray Font::findAllTypefaceNames(), который возвращает массив строк (класс StringArray Juce), содержащий эти названия. Число элементов массива возвращается его методом int StringArray::size() const throw(). В цикле
for(int i = 0; i < sSystemFonts.size(); i++) { pFontsBox->addItem(sSystemFonts[i], i + 1); }
мы перебираем по одному элементы списка и добавляем их в качестве элементов выпадающего списка pFontsBox.
Элементы списка размера шрифтов(pFontSizeBox) добавляем в диапазоне от 8 до 20 пунктов.
Заметим, что в Juce групповой блок служит исключительно в качестве элемента оформления; радиокнопки, рисуемые на его поверхности, не являются его потомками, а представляют собой самостоятельные виджеты. Для того, чтобы объединить их в одну группу, необходимо воспользоваться методом void Button::setRadioGroupId(int newGroupId). Если метод у нескольких радиокнопок принимает в качестве параметра какое-либо одно, отличное от нуля, число, то эти кнопки начинают действовать как единая радиогруппа, т.е. возможно включение одной и только одной кнопки из набора. В качестве ID для нашей радиогруппы используется число 1234.
Деструктор и методы перерисовки и задания размеров нашего класса приведены в листинге 6.3 .
TCentralComponent::~TCentralComponent() { // Удаляем дочерние виджеты // и обнуляем их указатели deleteAllChildren(); } //------------------------------------------------- void TCentralComponent::paint(Graphics& Canvas) { Canvas.fillAll(Colours::azure); } //------------------------------------------------- void TCentralComponent::resized() { pFontViewer->setBounds(proportionOfWidth(0.5505f), 8, proportionOfWidth(0.4192f), proportionOfHeight(0.9507f)); pFontNameLabel->setBounds(8, 8, roundFloatToInt((proportionOfWidth(0.4192f)) * 1.2048f), 25); pFontsBox->setBounds(8, 38, roundFloatToInt((proportionOfWidth(0.4192f)) * 1.2048f), 25); pFontSizeLabel->setBounds (8, 68, roundFloatToInt((proportionOfWidth(0.4192f)) * 1.2048f), 25); pFontSizeBox->setBounds(8, 96, roundFloatToInt((proportionOfWidth(0.4192f)) * 1.2048f), 25); pFontStyleLabel->setBounds(8, 126, roundFloatToInt((proportionOfWidth(0.4192f)) * 1.2048f), 25); pFontGroup->setBounds(8, 156, roundFloatToInt((proportionOfWidth(0.4192f)) * 1.2048f), 196); pNormalButton->setBounds(32, 185, 150, 25); pBoldButton->setBounds(32, 216, 150, 25); pItalicButton->setBounds(32, 248, 150, 25); }Листинг 6.3. Деструктор и методы paint и resize класса TCentralComponent (файл TCentralComponent.cpp
Для задания относительных горизонтальных размеров виджетов мы использовали уже знакомую функцию int Component::proportionOfWidth(float proportion) const throw(). Для преобразования чисел с плавающей точкой, получаемых после расчёта координат компонентов, в целые использована функция из juce_MathsFunctions.h int roundFloatToInt(const float value) throw().
Как видно из рисунка 6.1 , гарнитура и размер шрифта текста, отображаемого в многострочном текстовом поле TextEditor* pFontViewer, выбираются пользователем из выпадающих списков. Поскольку сам компонент-контейнер TCentralComponent является слушателем изменений списков (ComboBoxListener), в его классе мы переопределяем наследуемую от класса linstener'а виртуальную функцию virtual void ComboBox::Listener::ComboBoxChanged(ComboBox* ComboNoxThatHasChanged), которая отвечает за реакцию программы на выбор пользователя. Её реализация приведена в листинге 6.4 .
void TCentralComponent::comboBoxChanged(ComboBox* pComboBox) { // Изменён выпадающий список гарнитуры шрифта if(pComboBox == pFontsBox) { // Устанавливаем в качестве шрифта текста поля ввода // шрифт, выбранный пользователем из списка CurrentFont.setTypefaceName(pFontsBox->getText()); } // Изменён выпадающий список размера шрифта else if(pComboBox == pFontSizeBox) { // Вычисляем размер шрифта в пикселях, // исходя из выбранного пользователем кегля в типографских пунктах float fFontHeight = pFontSizeBox->getText().getFloatValue() * 96 / 72; // Устанавливаем размер шрифта CurrentFont.setHeight(fFontHeight); } // Задаём шрифт для отображения текста pFontViewer->applyFontToAllText(CurrentFont); }Листинг 6.4. Реализация функции void ComboBoxChanged(ComboBox*) класса компонента содержимого TCentralComponent (файл TCentralComponent.cpp)
После того, как пользователь выбрал какой-либо пункт в выпадающем списке, отображаемый текст виджета изменяется соответственно надписи выбранного пункта. Получить этот текст можно с помощью функции const String ComboBox::getText() const. В случае первого списка (pFontsBox) это будет название выбранной пользователем гарнитуры шрифта.
Это название передаём в качестве параметра в функию void void Font::setTypefaceName(const String& faceName). Как понятно из названия, она устанавливает гарнитуру вызвавшего шрифта (объект класса Font) с названием, соответствующим строке-параметру. Если гарнитура с таким названием не будет найдена (шрифт не установлен на компьютере), то будет использоваться шрифт по умолчанию. В нашем случае такого происходить не должно, т.к. пользователь выбирает из ранее найденных названий шрифтов, установленных в системе.
Во втором выпадающем списке, pFontSizeBox, пользователь выбирает размер (кегль) шрифта в типографских пунктах. Как уже упоминалось, функция const String ComboBox::getText() const возвращает строку текущего текста списка, объект класса String. Для того, чтобы преобразовать строку в текст, достаточно вызвать метод float String::getFloatValue() const throw(), который преобразует строку в число с плавающей точкой. После этого можно рассчитать высоту шрифта в пикселях, исходя из упоминавшейся выше формулы и задать её для используемого шрифта с помощью функции void Font::setHeight(float newHeight) ( пример 6.4
Теперь можно использовать шрифт с заданными пользователем параметрами для отображения текста в многострочном поле ввода pFontViewer. У класса TextEditor есть для этого два метода:
- void TextEditor::setFont(const Font& newFont), который устанавливает шрифт для вновь добавляемого в поле текста;
- void TextEditor::applyFontToAllText(const Font& newFont), который применяет параметры шрифта ко всему тексту поля ввода.
Понятно, что в нашем случае подходит именно второй метод ( пример 6.4).
Задать стиль текста в нашей демонстрационной программе пользователь может посредством трёх радиокнопок, которые мы отнесли к одной группе с ID 1234. Это обеспечивает выбор одной и только одной радиокнопки.
В нашей программе мы даём пользователю возможность выбирать три из четырёх возможных стилей: нормальный, полужирный и курсив (подчёркнутый текст мы использовать не будем). Класс Font имеет следующие функции для задания стиля шрифта:
- void Font::setBold(bool shouldBeBold) — задаёт полужирное начертание шрифта;
- void Font::setItalic(bool shouldBeItalic) — задаёт курсивное начертание шрифта;
- void Font::setUnderline(bool shouldBeUnderline) — задаёт подчёркнутое начертание шрифта.
Значение принимаемого параметра, равное true, задаёт соответствующий стиль шрифта, а равное false — его отменяет. Специальной функции для задания нормального стиля шрифта в Juce нет; для того, чтобы шрифт отображался нормальным начертанием, необходимо отменить все остальные стили с помощью вышеприведённых функций.
Для того, чтобы узнать, какой стиль используется шрифтом, в классе Font библиотеки Juce есть следующие методы:
- bool Font::isBold() const throw();
- bool Font::isItalic() const throw();
- bool Font::isUnderlined() const throw().
В том случае, если функция возвращает истину, начертание шрифта является полужирным, курсивным или подчёркнутым, соответственно.
Попробуем применить эти сведения в нашей программе (см. первый вариант реализации функции void TCentralComponent::buttonClicked(Button* pButton) в пример 6.5).
void TCentralComponent::buttonClicked(Button* pButton) { if (pButton == pNormalButton) { if(CurrentFont.isBold()) { CurrentFont.setBold(false); } else if(CurrentFont.isItalic()) { CurrentFont.setItalic(false); } } else if (pButton == pBoldButton) { if(CurrentFont.isItalic()) { CurrentFont.setItalic(false); } CurrentFont.setBold(true); } else if (pButton == pItalicButton) { if(CurrentFont.isBold()) { CurrentFont.setBold(false); } CurrentFont.setItalic(true); } pFontViewer->applyFontToAllText(CurrentFont); }Листинг 6.5. Первый вариант реализации функции buttonClicked класса компонента содержимого TCentralComponent
Следует подчеркнуть, что вызов функций задания того или иного стиля, приведённых в листинге 6.5 , не отменяет предыдущий стиль шрифта. Например, при последовательном вызове
CurrentFont.setBold(true); CurrentFont.setItalic(true);
мы получим полужирный курсив. Поэтому в первом варианте реализации ( пример 6.5) нам приходится отменять предыдущий стиль перед заданием нового. Хотя программа работает в этом виде вполне корректно, код получается излишне громоздким.
Для того, чтобы задать один и только один стиль шрифта, нужно воспользоваться флагами стиля (упоминавшийся выше перечислимый тип FontStyleFlags. Они принимаются в качестве параметра функцией void Font::setStyleFlags(int newFlags), которая меняет стиль вызвавшего шрифта. Комбинация стилей в этой функции задаётся операцией побитового сложения (|).
Перепишем реализацию функции TCentralComponent::buttonClicked(Button* pButton) ( пример 6.6). Код стал значительно компактнее при сохранении функциональности программы.
void TCentralComponent::buttonClicked(Button* pButton) { if (pButton == pNormalButton) { // Задаём стиль шрифта - нормальный CurrentFont.setStyleFlags(Font::plain); } else if (pButton == pBoldButton) { // Задаём стиль шрифта - полужирный CurrentFont.setStyleFlags(Font::bold); } else if (pButton == pItalicButton) { // Задаём стиль шрифта - курсив CurrentFont.setStyleFlags(Font::italic); } pFontViewer->applyFontToAllText(CurrentFont); }Листинг 6.6. Второй вариант реализации функции buttonClicked класса компонента содержимого TCentralComponent (файл TCentralComponent.cpp
Для получения флага стиля того или иного шрифта используется функция int Font::getStyleFlags() const throw().
Завершая описание класса Font библиотеки Juce, следует отметить, что для него доступны операторы присваивания = (копирует один шрифт в другой) и сравнения (== и !=).