Цель лекции: Дать базовые представления о работе с текстом в Juce
Основным способом "общения" компьютерной программы с пользователем является вывод текстовых сообщений (диалоговые окна, надписи на ярлыках, панелях и т.п.). Внешний вид таких сообщений задаётся тем или иным шрифтом.
Масштабируемый шрифт — это рисунок, задающий внешний вид текста и позволяющий без искажений отображать его на экране компьютера и при печати. Функции отображения и печати шрифта берут на себя специальные функции
Для работы со шрифтами в Juce существует специальный класс, Font. Мы уже использовали его в предыдущих лекциях. Объект этого класса передаётся в качестве параметра в метод setFont() различных компонентов (Label, TextEditor, HyperlinkButton и др.). Класс Component метода setFont() не имеет.
Основные свойства компьютерных шрифтов заимствованы из типографского дела. Ими являются следующие:
Typeface — гарнитура или внешний вид шрифта, определяемый набором символов, имеющих стилистическое единство. Возвращается методом Typeface * Font::getTypeface() const. Если нужно получить не сам объект гарнитуры, а только её название для текущего шрифта (строку), то можно воспользоваться методом const String Font::getTypefaceName() const throw().float Font::getHeight() const throw().Font включает нумерованный список enum FontStyleFlags {plain = 0, bold = 1, italic = 2, underlined = 4}, элементы которого соответствуют вышеперечисленным стилям.Font для получения названий гарнитур шрифтов, использующихся по умолчанию в данной операционной системе:static const String Font::getDefaultMonospacedFontName() — возвращает имя гарнитуры моноширинного рубленного шрифта, используемого по умолчанию;static const String Font::getDefaultSerifFontName() — возвращает имя гарнитуры шрифта с засечками (Serif), используемого по умолчанию;static const String Font::getDefaultSansSerifFontName() — возвращает имя гарнитуры рубленного шрифта (Sans), используемого по умолчанию.По умолчанию для всех компонентов Juce задаётся нормальный (стиль plain) шрифт гарнитуры Sans размером 15 пикселей.
В большинстве текстовых редакторов задаётся кегль шрифта — размер высоты символов, измеряемый в void Font::setHeight(float newHeight), как упоминалось, измеряется в пикселях. Получить её, исходя из заданного пользователем кегля в
Высота (пиксели) = Кегль (пункты) ? DPI / 72,
где DPI (dots per inch) — число пикселей на дюйм, зависящее от характеристик монитора.
Рассмотрим работу со шрифтами на примере демонстрационного приложения (рис 6.1).
(рис 6.1) Демонстрационное приложение свойств шрифта
В приложении текст из поля ввода ().
#ifndef _TCentralComponent_h_
#define _TCentralComponent_h_
//-------------------------------------------------------
#include "../JuceLibraryCode/JuceHeader.h"
//-------------------------------------------------------
// Класс компонента содержимого.
// Наследует классы слушателей выпадающих списков и кнопок
class TCentralComponent : public Component,
public ComboBoxListener,
public ButtonListener
{
public:
TCentralComponent();
~TCentralComponent();
void paint(Graphics);
void resized();
// Функция, отслеживающая изменения в выпадающих списках
void comboBoxChanged(ComboBox*);
// Функция, отслеживающая щелчки по кнопке
void buttonClicked(Button*);
private:
// Текущий шрифт текста, отображаемого в TextEditor
Font CurrentFont;
// Многострочное поле ввода
TextEditor* pFontViewer;
Label* pFontNameLabel;
// Выбор гарнитуры шрифта
ComboBox* pFontsBox;
Label* pFontSizeLabel;
// Выбор размера (кегля) шрифта
ComboBox* pFontSizeBox;
Label* pFontStyleLabel;
GroupComponent* pFontGroup;
// Радиокнопка выбора стиля нормального шрифта
ToggleButton* pNormalButton;
// Радиокнопка выбора стиля полужирного шрифта
ToggleButton* pBoldButton;
// Радиокнопка выбора стиля курсива
ToggleButton* pItalicButton;
// Предотвращает создание копии конструктора и оператора =
TCentralComponent(const TCentralComponent);
const TCentralComponent operator= (const TCentralComponent);
};
//----------------------------------------------------
#endif
Реализация класса компонента содержимого довольно велика, поэтому рассмотрим её по частям. В конструкторе класса добавим и сделаем видимыми поле ввода, ярлыки, выпадающие списки, групповой блок ().
#include "TCentralComponent.h"
//--------------------------------------------------------
#define tr(s) String::fromUTF8(s)
//--------------------------------------------------------
TCentralComponent::TCentralComponent() : Component("Central Component"),
pFontViewer(0),
pFontNameLabel(0),
pFontsBox(0),
pFontSizeLabel(0),
pFontSizeBox(0),
pFontStyleLabel(0),
pFontGroup(0),
pNormalButton(0),
pBoldButton(0),
pItalicButton(0)
{
pFontViewer = new TextEditor("FontViewer");
// Поле ввода - многострочное
pFontViewer->setMultiLine(true);
// Новая строка при нажатии клавиши <ENTER>
pFontViewer->setReturnKeyStartsNewLine(true);
// Запрет на редактирование
pFontViewer->setReadOnly(true);
// Показать полосы прокрутки
pFontViewer->setScrollbarsShown(true);
pFontViewer->setText(tr("Съешь же ещё этих мягких французских булок да выпей чаю.\n1234567890\n`~@#$%^*()-_=+\\|{[]};:'\"/?"));
addAndMakeVisible(pFontViewer);
pFontNameLabel = new Label("FontNameLabel", tr("Гарнитура (вид) шрифта"));
pFontNameLabel->setFont(Font(15.0000f, Font::plain));
pFontNameLabel->setJustificationType(Justification::centredLeft);
pFontNameLabel->setEditable(false, false, false);
pFontNameLabel->setColour(Label::textColourId, Colours::black);
pFontNameLabel->setColour(Label::backgroundColourId,
Colour(Colours::azure));
addAndMakeVisible(pFontNameLabel);
pFontsBox = new ComboBox("FontsBox");
// Запрет на редактирование текста в выпадающем списке
pFontsBox->setEditableText(false);
// Выравнивание теста по левому краю и центру виджета
pFontsBox->setJustificationType(Justification::centredLeft);
pFontsBox->setTextWhenNothingSelected(tr("Стандартный шрифт"));
// Устанавливаем в качестве слушателя списка
// сам компонент-контейнер
pFontsBox->addListener (this);
addAndMakeVisible(pFontsBox);
// Получаем имена системных шрифтов, сохраняем их
// в строковом массиве...
StringArray sSystemFonts = Font::findAllTypefaceNames();
// и добавляем в выпадающий список
for(int i = 0; i < sSystemFonts.size(); i++)
{
pFontsBox->addItem(sSystemFonts[i], i + 1);
}
pFontSizeLabel = new Label("FontSizeLabel", tr("Размер (кегль) шрифта"));
pFontSizeLabel->setFont(Font(15.0000f, Font::plain));
pFontSizeLabel->setJustificationType(Justification::centredLeft);
pFontSizeLabel->setEditable(false, false, false);
pFontSizeLabel->setColour(Label::textColourId, Colours::black);
pFontSizeLabel->setColour(Label::backgroundColourId,
Colour(Colours::azure));
addAndMakeVisible(pFontSizeLabel);
pFontSizeBox = new ComboBox("FontSizeBox");
pFontSizeBox->setEditableText(false);
pFontSizeBox->setJustificationType(Justification::centredLeft);
pFontSizeBox->setTextWhenNothingSelected(tr("Размер по умолчанию"));
pFontSizeBox->addItem("8", 1);
pFontSizeBox->addItem("9", 2);
pFontSizeBox->addItem("10", 3);
pFontSizeBox->addItem("11", 4);
pFontSizeBox->addItem("12", 5);
pFontSizeBox->addItem("13", 6);
pFontSizeBox->addItem("14", 7);
pFontSizeBox->addItem("15", 8);
pFontSizeBox->addItem("16", 9);
pFontSizeBox->addItem("17", 10);
pFontSizeBox->addItem("18", 11);
pFontSizeBox->addItem("19", 12);
pFontSizeBox->addItem("20", 13);
// Устанавливаем в качестве слушателя списка
// сам компонент-контейнер
pFontSizeBox->addListener (this);
addAndMakeVisible(pFontSizeBox);
pFontStyleLabel = new Label("FontStyleLabel", tr("Стиль шрифта"));
pFontStyleLabel->setFont(Font(15.0000f, Font::plain));
pFontStyleLabel->setJustificationType(Justification::centredLeft);
pFontStyleLabel->setEditable(false, false, false);
pFontStyleLabel->setColour(Label::textColourId, Colours::black);
pFontStyleLabel->setColour(Label::backgroundColourId,
Colour(Colours::azure));
addAndMakeVisible(pFontStyleLabel);
pFontGroup = new GroupComponent("FontGroup", tr("Шрифт"));
pFontGroup->setTextLabelPosition(Justification::centredLeft);
addAndMakeVisible(pFontGroup);
pNormalButton = new ToggleButton("NormalButton");
pNormalButton->setButtonText(tr("Нормальный"));
// Устанавливаем в качестве слушателя кнопки
// сам компонент-контейнер
pNormalButton->addListener(this);
// Радиокнопка отмечена
pNormalButton->setToggleState(true, false);
pNormalButton->setRadioGroupId(1234);
addAndMakeVisible(pNormalButton);
pBoldButton = new ToggleButton("BoldButton");
pBoldButton->setButtonText(tr("Полужирный"));
pBoldButton->addListener(this);
// Радиокнопка не отмечена
pBoldButton->setToggleState(false, false);
pBoldButton->setRadioGroupId(1234);
addAndMakeVisible(pBoldButton);
pItalicButton = new ToggleButton("ItalicButton");
pItalicButton->setButtonText(tr("Курсив"));
pItalicButton->addListener(this);
pItalicButton->setToggleState(false, false);
pItalicButton->setRadioGroupId(1234);
addAndMakeVisible(pItalicButton);
setSize (600, 400);
}
//------------------------------------------------------
Названия гарнитур 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);
}
Для задания относительных горизонтальных размеров виджетов мы использовали уже знакомую функцию int Component::proportionOfWidth(float . Для преобразования чисел с плавающей точкой, получаемых после расчёта координат компонентов, в целые использована функция из 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);
}
После того, как пользователь выбрал какой-либо пункт в выпадающем списке, отображаемый текст виджета изменяется соответственно надписи выбранного пункта. Получить этот текст можно с помощью функции const String ComboBox::getText() const. В случае первого списка (pFontsBox) это будет название выбранной пользователем гарнитуры шрифта.
Это название передаём в качестве параметра в функию void void Font::setTypefaceName(const String faceName). Как понятно из названия, она устанавливает гарнитуру вызвавшего шрифта (объект класса Font) с названием, соответствующим строке-параметру. Если гарнитура с таким названием не будет найдена (шрифт не установлен на компьютере), то будет использоваться шрифт по умолчанию. В нашем случае такого происходить не должно, т.к. пользователь выбирает из ранее найденных названий шрифтов, установленных в системе.
Во втором выпадающем списке,
Теперь можно использовать шрифт с заданными пользователем параметрами для отображения текста в многострочном поле ввода pFontViewer. У класса TextEditor есть для этого два метода:
void TextEditor::setFont(const Font newFont), который устанавливает шрифт для вновь добавляемого в поле текста;void TextEditor::applyFontToAllText(const Font newFont), который применяет параметры шрифта ко всему тексту поля ввода.Понятно, что в нашем случае подходит именно второй метод ().
Задать стиль текста в нашей демонстрационной программе пользователь может посредством трёх радиокнопок, которые мы отнесли к одной группе с 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)
{
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 , не отменяет предыдущий стиль шрифта. Например, при последовательном вызове
CurrentFont.setBold(true); CurrentFont.setItalic(true);
мы получим полужирный курсив. Поэтому в первом варианте реализации () нам приходится отменять предыдущий стиль перед заданием нового. Хотя программа работает в этом виде вполне корректно, код получается излишне громоздким.
Для того, чтобы задать один и только один стиль шрифта, нужно воспользоваться флагами стиля (упоминавшийся выше FontStyleFlags. Они принимаются в качестве параметра функцией void Font::setStyleFlags(int newFlags), которая меняет стиль вызвавшего шрифта. Комбинация стилей в этой функции задаётся операцией побитового сложения (|).
Перепишем реализацию функции ). Код стал значительно компактнее при сохранении функциональности программы.
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);
}
Для получения флага стиля того или иного шрифта используется функция int Font::getStyleFlags() const throw().
Завершая описание класса Font библиотеки Juce, следует отметить, что для него доступны операторы присваивания = (копирует один шрифт в другой) и сравнения (== и !=).
Подобно многим другим GUI toolkit'ам в Juce представление строковых данных реализовано как класс (String). Мы уже встречались с этим классом в вышеприведённых программах.
Класс String имеет несколько конструкторов:
String() throw() — создаёт пустую строку;String(const String other) throw() — создаёт строку из другого объекта того же класса String;String(const char* text) — создаёт строку из массива символов, оканчивающегося нулём;String(const char* text, size_t maxChars) — создаёт строку из массива символов определённой длины;String(const juce_wchar* unicodeText) — создаёт строку из массива символов в формате Юникод, оканчивающегося нулём; typedef wchar_t juce_wchar — платформонезависимый тип символа Юникод;String(const juce_wchar* unicodeText, size_t maxChars) — создаёт строку из массива символов в формате Юникод определённой длины.Класс String предоставляет целую серию методов и операторов, позволяющих проводить со строками разного рода операции: соединять их, осуществлять поиск подстрок и т.п.
Для класса String имеется два =) и конкатенации (+=).
Оператор = замещает содержимое объекта класса String содержимым другой строки.
Оператор += осуществляет объединение или конкатенацию строк. При этом в конец исходной строки могут быть добавлены данные следующих типов:
char);juce_wchar);juce_wchar*);String);int);unsigned int).Объединение строк можно осуществить также с помощью функции-члена класса String void String::append(const juce_wchar* textToAppend, int maxCharsToTake), где textToAppend — строка, добавляемая в конец исходной, а maxCharsToTake — максимальное число передаваемых символов.
Строки можно сравнивать между собой с помощью набора методов класса:
bool String::equalsIgnoreCase(const String other) const throw() — сравнение с другой строкой без учёта регистра;bool String::equalsIgnoreCase(const juce_wchar* other) const throw() — сравнение с другой строкой (представленной массивом символов Юникод) без учёта регистра;bool String::equalsIgnoreCase(const char* other) const throw() — сравнение с другой строкой (представленной массивом символов) без учёта регистра;int String::compare(const String other) const throw() — сравнение с другой строкой с учётом регистра;int String::compare(const char* other) const throw() — сравнение с другой строкой (представленной массивом символов) с учётом регистра;int String::compare(const juce_wchar* other) const throw() — сравнение с другой строкой (представленной массивом символов Юникод) с учётом регистра;int String::compareIgnoreCase(const String other) const throw() — сравнение с другой строкой без учёта регистра;int String::compareLexicographically(const String other) const throw() — лексикографическое сравнение с другой строкой.bool String::isEmpty () const throw() — возвращает true, если строка не содержит ни одного символа;bool String::isNotEmpty() const throw() — возвращает true, если строка содержит хотя бы один символ.Того же результата можно добиться, проверив длину строки в символах методом int String::length() const throw().
Следует помнить, что строка, содержащая пробелы, пустой не является, поэтому функция , вызываемая ею, будет возвращать false. Как правило, пользователь воспринимает текстовое поле, в котором нет других символов, кроме пробелов, именно как пустое. Библиотека Juce включает удобную функцию bool String::containsNonWhitespaceChars() const throw(), которая возвращает true, если вызывающая строка содержит какие-либо символы, помимо пробелов.
Поиск строк и символов в тексте — одна из наиболее часто возникающих задач при разработке приложений. Класс String содержит довольно много методов для её решения.
Прежде всего, это методы поиска подстроки:
const String String::substring(int startIndex) const — возвращает часть вызывающей строки от символа startIndex и до её конца;const String String::substring(int startIndex, int endIndex) const — возвращает часть вызывающей строки между символами startIndex и endIndex.Для того, чтобы узнать, содержит ли строка подстроку или символы (в любой позиции), используются следующие функции:
bool String::contains(const String text) const throw() — проверяет, содержит ли строка подстроку text. Чувствительна к регистру. Если в качестве параметра была передана пустая строка, функция всё равно возвращает true.bool String::containsIgnoreCase(const String text) const throw() — работает аналогично предыдущей функции, но не учитывает регистр символов.bool String::containsChar(juce_wchar character) const throw() — проверяет, содержит ли строка символ, переданный в качестве параметра.bool String::containsAnyOf(const String charactersItMightContain) const throw() — возвращает true, если в вызывающей строке есть какие-либо символы из набора, принимаемого в качестве параметра.bool String::containsOnly(const String charactersItMightContain) const throw() — возвращает false, если в вызывающей строке отсутствует какой-либо из символов набора, принимаемого в качестве параметра.bool String::containsWholeWord(const String wordToLookFor) const throw() — возвращает true, если вызывающая метод строка содержит слово wordToLookFor, окружённое символами, не являющимися буквами и цифрами.Для замены определённой части строки другой класс String предоставляет метод const String String::replace(const String stringToReplace, const String stringToInsertInstead, bool ignoreCase = false) const, все вхождения подстроки stringToReplace строкой stringToInsertInstead с учётом или без учёта регистра в зависимости от принимаемого значения параметра ignoreCase.
Для замены одного символа в строке можно воспользоваться функцией const String String::replaceCharacter(juce_wchar characterToReplace,
juce_wchar characterToInsertInstead) const. Кроме того, класс String включает метод, который позволяет заменить один набор
символов другим: const String String::replaceCharacters(const String charactersToReplace, const String charactersToInsertInstead)
const. Он возвращает строку, в которой каждый символ из первого набора (charactersToReplace) замещается соответствующим символом
из второго набора (charactersToInsertInstead) в эквивалентной позиции. Понятно, что передаваемые в функцию две строки должны быть
одинакового размера. Например, при вызове replaceCharacters("abc", "def") в результирующей строке
"a" замещается на "d", "b"на "e" и т.п.
Близким к предыдущей функции является метод класса String const String String::retainCharacters(const String charactersToRetain) const, который возвращает строку, сохраняющую только тот набор символов, содержавшихся в вызвавшей строке, который был передан в качестве параметра. Например, если объект класса String, содержащий строку "1122334455", обратится к методу retainCharacters("432"), то будет возвращена строка "223344".
Для преобразования данных строки в верхний или нижний регистр используются методы const String String::toLowerCase() const и const String String::toUpperCase() const, которые возвращают, соответственно, вариант вызвавшей строки в нижнем и верхнем регистре.
Достаточно часто встречается ситуация, когда необходимо конвертировать числовые значения в строковые и наоборот. В первом случае необходимо создать объект класса String, в качестве параметра конструктора которого передать short int, unsigned short int, int, unsigned int, int64, uint64, float и double. Например:
String sNumber(1234); // Строка содержит текст "1234".
Преобразование из строкового в числовое значение производится методами, содержащими в своём имени название типа:
int String::getIntValue() const throw();int64 String::getLargeIntValue() const throw();float String::getFloatValue() const throw();double String::getDoubleValue() const throw();int String::getHexValue32() const throw(); int64 String::getHexValue64() const throw();Последние две функции возвращают шестнадцатеричное число соответствующего размера; прочие символы, если их содержит строка, игнорируются. Пример использования функции getFloatValue приведён в листинге 6.4 .
Кроме того, класс String включает довольно интересный метод int String::getTrailingIntValue() const throw(), который возвращает целое число, находящееся в конце строки. Например:
String sNumber("321 xyz654");
// Функция возвращает 654
int iNumber = sNumber.getTrailingIntValue()
Для хранения массива строковых объектов в Juce имеется класс StringArray, который описывает список строк с расширенными возможностями манипулирования ими.
Класс поддерживает следующие операции работы со строками:
Доступ к строке по её индексу возможен с помощью оператора [], как к элементу обычного массива. Если индекс выходит за пределы массива, будет возвращена пустая строка. Кроме того, получить ссылку на одну из строк массива по её индексу позволяет метод класса String StringArray::getReference(int index) throw().
Возвращение числа элементов списка (хранящихся строк) осуществляет метод int StringArray::size() const throw().
Добавление новых строк в конец списка. Производится функцией void StringArray::add(const String stringToAdd). Кроме того, в конец списка можно добавить целый массив строк, другой объект класса StringArray, с помощью метода void StringArray::addArray(const StringArray other, int startIndex = 0, int numElementsToAdd = -1), где other — первый элемент добавляемого строкового массива, с которого необходимо начинать вставку, а numElementsToAdd — максимальное число строк из добавляемого массива для вставки. Если значение параметра меньше нуля, то добавляются все элементы.
Вставка новых строк в произвольное место списка со смещением элементов массива. Функция void StringArray::insert(int index, const String stringToAdd) вставляет строку stringToAdd в массив на место, заданное индексом index, поднимая другие элементы. Если значение индекса меньше нуля или превышает размер массива, то строка добавляется в конец списка.
Вставка новых строк в произвольное место списка с заменой элементов массива. Функция void StringArray::set(int index, const String newString) вставляет строку newString в массив на место, заданное индексом index. При этом прежняя строка замещается новой. Если значение индекса меньше нуля функция не делает ничего, а если оно превышает размер массива, то строка добавляется в конец списка.
Поиск строк в массиве (по содержимому). Чаще бывает, что найти необходимую строку в массиве бывает нужно не по её индексу, а по тексту, в ней содержащемуся. В этом может помочь метод bool StringArray::contains(const String stringToLookFor, bool ignoreCase = false) const. Также, как и раньше, второй параметр — флаг, следует ли при поиске строки stringToLookFor учитывать регистр.
Удаление строк. Удалить одну строку, находящуюся на позиции index, из списка можно с помощью функции void StringArray::remove(int index). Если индекс выходит за пределы массива, то функция ничего не делает. Поиск строки stringToRemove в массиве с последующим её удалением выполняет функция void StringArray::removeString(const String stringToRemove, bool ignoreCase = false); второй её параметр — флаг, показывающий, следует ли игнорировать регистр.
Полезны также функции, позволяющие удалить все повторяющиеся строки, void StringArray::removeDuplicates(bool ignoreCase), и все пустые строки в массиве, void StringArray::removeEmptyStrings(bool removeWhiteSpaceStrings = true). Если последняя функция в качестве параметра принимает истину, то пустыми будут считаться также строки, заполненные только пробелами.
Кроме того, класс StringArray позволяет удалять сразу ряд строк. Для этого используется метод void StringArray::removeRange(int startIndex, int numberToRemove), где startIndex — индекс первой строки из ряда, подлежащего удалению, а numberToRemove — число удаляемых элементов.
Перемещение строк на новую позицию в массиве осуществляет метод void StringArray::move(int currentIndex, int newIndex) throw(). При вызове функции экземпляром класса StringArray происходит перемещение содержащейся в нём строки, находящейся на позиции currentIndex, на позицию newIndex.
Сортировка элементов списка осуществляется функцией void StringArray::sort(bool ignoreCase); параметр указывает, следует ли учитывать регистр. Строки сортируются в алфавитном порядке.
Очистка списка. Удаление всех строк из списка с обнулением его размера выполняет функция void StringArray::clear().
Нахождение индекса строки осуществляет функция int StringArray::indexOf(const String stringToLookFor, bool ignoreCase = false, int startIndex = 0) const, где stringToLookFor — текст (строка) для поиска, ignoreCase— флаг, показывающий, следует ли игнорировать регистр, а startIndex — индекс элемента, с которого начинается поиск. Функция возвращает индекс найденной строки, а в случае, если ничего не было найдено, возвращается -1.
Объединение строк массива в одну. Удобно для автоматической генерации текста, добавляемого, скажем, в многострочное поле ввода на основании собранных и сохранённых в массиве StringArray данных. Осуществляется функцией const String StringArray::joinIntoString(const String separatorString, int startIndex = 0, int numberOfElements = -1) const.
Мы можем с помощью параметра separatorString задать строку-разделитель, которая будет разделять элементы массива в результирующей строке. Например, если массив StringArray включал строки "a", "b" и "c", то вызов метода joinIntoString(",") вернёт строку "a,b,c".
startIndex, как нетрудно догадаться, индекс элемента массива, с которого следует начать объединение, а numberOfElements — число объединяемых строк. В том случае, если последний параметр принимает значение меньше нуля, то будут объединены все строки массива.
Для вывода неформатированного текста в Juce используется класс String, а форматированного — также и класс Font, хранящий информацию о параметрах шрифта текста.
Класс String, инкапсулирует действия со строками. Объект класса String — это строка, состоящая из произвольного числа символов. Литерные константы типа String представляют собой последовательности символов, заключённые в двойные кавычки.
Архив с исходными текстами примера Вы можете скачать здесь
Цель лекции: Дать базовые представления о работе с текстом в Juce
Основным способом "общения" компьютерной программы с пользователем является вывод текстовых сообщений (диалоговые окна, надписи на ярлыках, панелях и т.п.). Внешний вид таких сообщений задаётся тем или иным шрифтом.
Масштабируемый шрифт — это рисунок, задающий внешний вид текста и позволяющий без искажений отображать его на экране компьютера и при печати. Функции отображения и печати шрифта берут на себя специальные функции
Для работы со шрифтами в Juce существует специальный класс, Font. Мы уже использовали его в предыдущих лекциях. Объект этого класса передаётся в качестве параметра в метод setFont() различных компонентов (Label, TextEditor, HyperlinkButton и др.). Класс Component метода setFont() не имеет.
Основные свойства компьютерных шрифтов заимствованы из типографского дела. Ими являются следующие:
Typeface — гарнитура или внешний вид шрифта, определяемый набором символов, имеющих стилистическое единство. Возвращается методом Typeface * Font::getTypeface() const. Если нужно получить не сам объект гарнитуры, а только её название для текущего шрифта (строку), то можно воспользоваться методом const String Font::getTypefaceName() const throw().float Font::getHeight() const throw().Font включает нумерованный список enum FontStyleFlags {plain = 0, bold = 1, italic = 2, underlined = 4}, элементы которого соответствуют вышеперечисленным стилям.Font для получения названий гарнитур шрифтов, использующихся по умолчанию в данной операционной системе:static const String Font::getDefaultMonospacedFontName() — возвращает имя гарнитуры моноширинного рубленного шрифта, используемого по умолчанию;static const String Font::getDefaultSerifFontName() — возвращает имя гарнитуры шрифта с засечками (Serif), используемого по умолчанию;static const String Font::getDefaultSansSerifFontName() — возвращает имя гарнитуры рубленного шрифта (Sans), используемого по умолчанию.По умолчанию для всех компонентов Juce задаётся нормальный (стиль plain) шрифт гарнитуры Sans размером 15 пикселей.
В большинстве текстовых редакторов задаётся кегль шрифта — размер высоты символов, измеряемый в void Font::setHeight(float newHeight), как упоминалось, измеряется в пикселях. Получить её, исходя из заданного пользователем кегля в
Высота (пиксели) = Кегль (пункты) ? DPI / 72,
где DPI (dots per inch) — число пикселей на дюйм, зависящее от характеристик монитора.
Рассмотрим работу со шрифтами на примере демонстрационного приложения (рис 6.1).
(рис 6.1) Демонстрационное приложение свойств шрифта
В приложении текст из поля ввода ().
#ifndef _TCentralComponent_h_
#define _TCentralComponent_h_
//-------------------------------------------------------
#include "../JuceLibraryCode/JuceHeader.h"
//-------------------------------------------------------
// Класс компонента содержимого.
// Наследует классы слушателей выпадающих списков и кнопок
class TCentralComponent : public Component,
public ComboBoxListener,
public ButtonListener
{
public:
TCentralComponent();
~TCentralComponent();
void paint(Graphics);
void resized();
// Функция, отслеживающая изменения в выпадающих списках
void comboBoxChanged(ComboBox*);
// Функция, отслеживающая щелчки по кнопке
void buttonClicked(Button*);
private:
// Текущий шрифт текста, отображаемого в TextEditor
Font CurrentFont;
// Многострочное поле ввода
TextEditor* pFontViewer;
Label* pFontNameLabel;
// Выбор гарнитуры шрифта
ComboBox* pFontsBox;
Label* pFontSizeLabel;
// Выбор размера (кегля) шрифта
ComboBox* pFontSizeBox;
Label* pFontStyleLabel;
GroupComponent* pFontGroup;
// Радиокнопка выбора стиля нормального шрифта
ToggleButton* pNormalButton;
// Радиокнопка выбора стиля полужирного шрифта
ToggleButton* pBoldButton;
// Радиокнопка выбора стиля курсива
ToggleButton* pItalicButton;
// Предотвращает создание копии конструктора и оператора =
TCentralComponent(const TCentralComponent);
const TCentralComponent operator= (const TCentralComponent);
};
//----------------------------------------------------
#endif
Реализация класса компонента содержимого довольно велика, поэтому рассмотрим её по частям. В конструкторе класса добавим и сделаем видимыми поле ввода, ярлыки, выпадающие списки, групповой блок ().
#include "TCentralComponent.h"
//--------------------------------------------------------
#define tr(s) String::fromUTF8(s)
//--------------------------------------------------------
TCentralComponent::TCentralComponent() : Component("Central Component"),
pFontViewer(0),
pFontNameLabel(0),
pFontsBox(0),
pFontSizeLabel(0),
pFontSizeBox(0),
pFontStyleLabel(0),
pFontGroup(0),
pNormalButton(0),
pBoldButton(0),
pItalicButton(0)
{
pFontViewer = new TextEditor("FontViewer");
// Поле ввода - многострочное
pFontViewer->setMultiLine(true);
// Новая строка при нажатии клавиши <ENTER>
pFontViewer->setReturnKeyStartsNewLine(true);
// Запрет на редактирование
pFontViewer->setReadOnly(true);
// Показать полосы прокрутки
pFontViewer->setScrollbarsShown(true);
pFontViewer->setText(tr("Съешь же ещё этих мягких французских булок да выпей чаю.\n1234567890\n`~@#$%^*()-_=+\\|{[]};:'\"/?"));
addAndMakeVisible(pFontViewer);
pFontNameLabel = new Label("FontNameLabel", tr("Гарнитура (вид) шрифта"));
pFontNameLabel->setFont(Font(15.0000f, Font::plain));
pFontNameLabel->setJustificationType(Justification::centredLeft);
pFontNameLabel->setEditable(false, false, false);
pFontNameLabel->setColour(Label::textColourId, Colours::black);
pFontNameLabel->setColour(Label::backgroundColourId,
Colour(Colours::azure));
addAndMakeVisible(pFontNameLabel);
pFontsBox = new ComboBox("FontsBox");
// Запрет на редактирование текста в выпадающем списке
pFontsBox->setEditableText(false);
// Выравнивание теста по левому краю и центру виджета
pFontsBox->setJustificationType(Justification::centredLeft);
pFontsBox->setTextWhenNothingSelected(tr("Стандартный шрифт"));
// Устанавливаем в качестве слушателя списка
// сам компонент-контейнер
pFontsBox->addListener (this);
addAndMakeVisible(pFontsBox);
// Получаем имена системных шрифтов, сохраняем их
// в строковом массиве...
StringArray sSystemFonts = Font::findAllTypefaceNames();
// и добавляем в выпадающий список
for(int i = 0; i < sSystemFonts.size(); i++)
{
pFontsBox->addItem(sSystemFonts[i], i + 1);
}
pFontSizeLabel = new Label("FontSizeLabel", tr("Размер (кегль) шрифта"));
pFontSizeLabel->setFont(Font(15.0000f, Font::plain));
pFontSizeLabel->setJustificationType(Justification::centredLeft);
pFontSizeLabel->setEditable(false, false, false);
pFontSizeLabel->setColour(Label::textColourId, Colours::black);
pFontSizeLabel->setColour(Label::backgroundColourId,
Colour(Colours::azure));
addAndMakeVisible(pFontSizeLabel);
pFontSizeBox = new ComboBox("FontSizeBox");
pFontSizeBox->setEditableText(false);
pFontSizeBox->setJustificationType(Justification::centredLeft);
pFontSizeBox->setTextWhenNothingSelected(tr("Размер по умолчанию"));
pFontSizeBox->addItem("8", 1);
pFontSizeBox->addItem("9", 2);
pFontSizeBox->addItem("10", 3);
pFontSizeBox->addItem("11", 4);
pFontSizeBox->addItem("12", 5);
pFontSizeBox->addItem("13", 6);
pFontSizeBox->addItem("14", 7);
pFontSizeBox->addItem("15", 8);
pFontSizeBox->addItem("16", 9);
pFontSizeBox->addItem("17", 10);
pFontSizeBox->addItem("18", 11);
pFontSizeBox->addItem("19", 12);
pFontSizeBox->addItem("20", 13);
// Устанавливаем в качестве слушателя списка
// сам компонент-контейнер
pFontSizeBox->addListener (this);
addAndMakeVisible(pFontSizeBox);
pFontStyleLabel = new Label("FontStyleLabel", tr("Стиль шрифта"));
pFontStyleLabel->setFont(Font(15.0000f, Font::plain));
pFontStyleLabel->setJustificationType(Justification::centredLeft);
pFontStyleLabel->setEditable(false, false, false);
pFontStyleLabel->setColour(Label::textColourId, Colours::black);
pFontStyleLabel->setColour(Label::backgroundColourId,
Colour(Colours::azure));
addAndMakeVisible(pFontStyleLabel);
pFontGroup = new GroupComponent("FontGroup", tr("Шрифт"));
pFontGroup->setTextLabelPosition(Justification::centredLeft);
addAndMakeVisible(pFontGroup);
pNormalButton = new ToggleButton("NormalButton");
pNormalButton->setButtonText(tr("Нормальный"));
// Устанавливаем в качестве слушателя кнопки
// сам компонент-контейнер
pNormalButton->addListener(this);
// Радиокнопка отмечена
pNormalButton->setToggleState(true, false);
pNormalButton->setRadioGroupId(1234);
addAndMakeVisible(pNormalButton);
pBoldButton = new ToggleButton("BoldButton");
pBoldButton->setButtonText(tr("Полужирный"));
pBoldButton->addListener(this);
// Радиокнопка не отмечена
pBoldButton->setToggleState(false, false);
pBoldButton->setRadioGroupId(1234);
addAndMakeVisible(pBoldButton);
pItalicButton = new ToggleButton("ItalicButton");
pItalicButton->setButtonText(tr("Курсив"));
pItalicButton->addListener(this);
pItalicButton->setToggleState(false, false);
pItalicButton->setRadioGroupId(1234);
addAndMakeVisible(pItalicButton);
setSize (600, 400);
}
//------------------------------------------------------
Названия гарнитур 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);
}
Для задания относительных горизонтальных размеров виджетов мы использовали уже знакомую функцию int Component::proportionOfWidth(float . Для преобразования чисел с плавающей точкой, получаемых после расчёта координат компонентов, в целые использована функция из 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);
}
После того, как пользователь выбрал какой-либо пункт в выпадающем списке, отображаемый текст виджета изменяется соответственно надписи выбранного пункта. Получить этот текст можно с помощью функции const String ComboBox::getText() const. В случае первого списка (pFontsBox) это будет название выбранной пользователем гарнитуры шрифта.
Это название передаём в качестве параметра в функию void void Font::setTypefaceName(const String faceName). Как понятно из названия, она устанавливает гарнитуру вызвавшего шрифта (объект класса Font) с названием, соответствующим строке-параметру. Если гарнитура с таким названием не будет найдена (шрифт не установлен на компьютере), то будет использоваться шрифт по умолчанию. В нашем случае такого происходить не должно, т.к. пользователь выбирает из ранее найденных названий шрифтов, установленных в системе.
Во втором выпадающем списке,
Теперь можно использовать шрифт с заданными пользователем параметрами для отображения текста в многострочном поле ввода pFontViewer. У класса TextEditor есть для этого два метода:
void TextEditor::setFont(const Font newFont), который устанавливает шрифт для вновь добавляемого в поле текста;void TextEditor::applyFontToAllText(const Font newFont), который применяет параметры шрифта ко всему тексту поля ввода.Понятно, что в нашем случае подходит именно второй метод ().
Задать стиль текста в нашей демонстрационной программе пользователь может посредством трёх радиокнопок, которые мы отнесли к одной группе с 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)
{
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 , не отменяет предыдущий стиль шрифта. Например, при последовательном вызове
CurrentFont.setBold(true); CurrentFont.setItalic(true);
мы получим полужирный курсив. Поэтому в первом варианте реализации () нам приходится отменять предыдущий стиль перед заданием нового. Хотя программа работает в этом виде вполне корректно, код получается излишне громоздким.
Для того, чтобы задать один и только один стиль шрифта, нужно воспользоваться флагами стиля (упоминавшийся выше FontStyleFlags. Они принимаются в качестве параметра функцией void Font::setStyleFlags(int newFlags), которая меняет стиль вызвавшего шрифта. Комбинация стилей в этой функции задаётся операцией побитового сложения (|).
Перепишем реализацию функции ). Код стал значительно компактнее при сохранении функциональности программы.
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);
}
Для получения флага стиля того или иного шрифта используется функция int Font::getStyleFlags() const throw().
Завершая описание класса Font библиотеки Juce, следует отметить, что для него доступны операторы присваивания = (копирует один шрифт в другой) и сравнения (== и !=).
Подобно многим другим GUI toolkit'ам в Juce представление строковых данных реализовано как класс (String). Мы уже встречались с этим классом в вышеприведённых программах.
Класс String имеет несколько конструкторов:
String() throw() — создаёт пустую строку;String(const String other) throw() — создаёт строку из другого объекта того же класса String;String(const char* text) — создаёт строку из массива символов, оканчивающегося нулём;String(const char* text, size_t maxChars) — создаёт строку из массива символов определённой длины;String(const juce_wchar* unicodeText) — создаёт строку из массива символов в формате Юникод, оканчивающегося нулём; typedef wchar_t juce_wchar — платформонезависимый тип символа Юникод;String(const juce_wchar* unicodeText, size_t maxChars) — создаёт строку из массива символов в формате Юникод определённой длины.Класс String предоставляет целую серию методов и операторов, позволяющих проводить со строками разного рода операции: соединять их, осуществлять поиск подстрок и т.п.
Для класса String имеется два =) и конкатенации (+=).
Оператор = замещает содержимое объекта класса String содержимым другой строки.
Оператор += осуществляет объединение или конкатенацию строк. При этом в конец исходной строки могут быть добавлены данные следующих типов:
char);juce_wchar);juce_wchar*);String);int);unsigned int).Объединение строк можно осуществить также с помощью функции-члена класса String void String::append(const juce_wchar* textToAppend, int maxCharsToTake), где textToAppend — строка, добавляемая в конец исходной, а maxCharsToTake — максимальное число передаваемых символов.
Строки можно сравнивать между собой с помощью набора методов класса:
bool String::equalsIgnoreCase(const String other) const throw() — сравнение с другой строкой без учёта регистра;bool String::equalsIgnoreCase(const juce_wchar* other) const throw() — сравнение с другой строкой (представленной массивом символов Юникод) без учёта регистра;bool String::equalsIgnoreCase(const char* other) const throw() — сравнение с другой строкой (представленной массивом символов) без учёта регистра;int String::compare(const String other) const throw() — сравнение с другой строкой с учётом регистра;int String::compare(const char* other) const throw() — сравнение с другой строкой (представленной массивом символов) с учётом регистра;int String::compare(const juce_wchar* other) const throw() — сравнение с другой строкой (представленной массивом символов Юникод) с учётом регистра;int String::compareIgnoreCase(const String other) const throw() — сравнение с другой строкой без учёта регистра;int String::compareLexicographically(const String other) const throw() — лексикографическое сравнение с другой строкой.bool String::isEmpty () const throw() — возвращает true, если строка не содержит ни одного символа;bool String::isNotEmpty() const throw() — возвращает true, если строка содержит хотя бы один символ.Того же результата можно добиться, проверив длину строки в символах методом int String::length() const throw().
Следует помнить, что строка, содержащая пробелы, пустой не является, поэтому функция , вызываемая ею, будет возвращать false. Как правило, пользователь воспринимает текстовое поле, в котором нет других символов, кроме пробелов, именно как пустое. Библиотека Juce включает удобную функцию bool String::containsNonWhitespaceChars() const throw(), которая возвращает true, если вызывающая строка содержит какие-либо символы, помимо пробелов.
Поиск строк и символов в тексте — одна из наиболее часто возникающих задач при разработке приложений. Класс String содержит довольно много методов для её решения.
Прежде всего, это методы поиска подстроки:
const String String::substring(int startIndex) const — возвращает часть вызывающей строки от символа startIndex и до её конца;const String String::substring(int startIndex, int endIndex) const — возвращает часть вызывающей строки между символами startIndex и endIndex.Для того, чтобы узнать, содержит ли строка подстроку или символы (в любой позиции), используются следующие функции:
bool String::contains(const String text) const throw() — проверяет, содержит ли строка подстроку text. Чувствительна к регистру. Если в качестве параметра была передана пустая строка, функция всё равно возвращает true.bool String::containsIgnoreCase(const String text) const throw() — работает аналогично предыдущей функции, но не учитывает регистр символов.bool String::containsChar(juce_wchar character) const throw() — проверяет, содержит ли строка символ, переданный в качестве параметра.bool String::containsAnyOf(const String charactersItMightContain) const throw() — возвращает true, если в вызывающей строке есть какие-либо символы из набора, принимаемого в качестве параметра.bool String::containsOnly(const String charactersItMightContain) const throw() — возвращает false, если в вызывающей строке отсутствует какой-либо из символов набора, принимаемого в качестве параметра.bool String::containsWholeWord(const String wordToLookFor) const throw() — возвращает true, если вызывающая метод строка содержит слово wordToLookFor, окружённое символами, не являющимися буквами и цифрами.Для замены определённой части строки другой класс String предоставляет метод const String String::replace(const String stringToReplace, const String stringToInsertInstead, bool ignoreCase = false) const, все вхождения подстроки stringToReplace строкой stringToInsertInstead с учётом или без учёта регистра в зависимости от принимаемого значения параметра ignoreCase.
Для замены одного символа в строке можно воспользоваться функцией const String String::replaceCharacter(juce_wchar characterToReplace,
juce_wchar characterToInsertInstead) const. Кроме того, класс String включает метод, который позволяет заменить один набор
символов другим: const String String::replaceCharacters(const String charactersToReplace, const String charactersToInsertInstead)
const. Он возвращает строку, в которой каждый символ из первого набора (charactersToReplace) замещается соответствующим символом
из второго набора (charactersToInsertInstead) в эквивалентной позиции. Понятно, что передаваемые в функцию две строки должны быть
одинакового размера. Например, при вызове replaceCharacters("abc", "def") в результирующей строке
"a" замещается на "d", "b"на "e" и т.п.
Близким к предыдущей функции является метод класса String const String String::retainCharacters(const String charactersToRetain) const, который возвращает строку, сохраняющую только тот набор символов, содержавшихся в вызвавшей строке, который был передан в качестве параметра. Например, если объект класса String, содержащий строку "1122334455", обратится к методу retainCharacters("432"), то будет возвращена строка "223344".
Для преобразования данных строки в верхний или нижний регистр используются методы const String String::toLowerCase() const и const String String::toUpperCase() const, которые возвращают, соответственно, вариант вызвавшей строки в нижнем и верхнем регистре.
Достаточно часто встречается ситуация, когда необходимо конвертировать числовые значения в строковые и наоборот. В первом случае необходимо создать объект класса String, в качестве параметра конструктора которого передать short int, unsigned short int, int, unsigned int, int64, uint64, float и double. Например:
String sNumber(1234); // Строка содержит текст "1234".
Преобразование из строкового в числовое значение производится методами, содержащими в своём имени название типа:
int String::getIntValue() const throw();int64 String::getLargeIntValue() const throw();float String::getFloatValue() const throw();double String::getDoubleValue() const throw();int String::getHexValue32() const throw(); int64 String::getHexValue64() const throw();Последние две функции возвращают шестнадцатеричное число соответствующего размера; прочие символы, если их содержит строка, игнорируются. Пример использования функции getFloatValue приведён в листинге 6.4 .
Кроме того, класс String включает довольно интересный метод int String::getTrailingIntValue() const throw(), который возвращает целое число, находящееся в конце строки. Например:
String sNumber("321 xyz654");
// Функция возвращает 654
int iNumber = sNumber.getTrailingIntValue()
Для хранения массива строковых объектов в Juce имеется класс StringArray, который описывает список строк с расширенными возможностями манипулирования ими.
Класс поддерживает следующие операции работы со строками:
Доступ к строке по её индексу возможен с помощью оператора [], как к элементу обычного массива. Если индекс выходит за пределы массива, будет возвращена пустая строка. Кроме того, получить ссылку на одну из строк массива по её индексу позволяет метод класса String StringArray::getReference(int index) throw().
Возвращение числа элементов списка (хранящихся строк) осуществляет метод int StringArray::size() const throw().
Добавление новых строк в конец списка. Производится функцией void StringArray::add(const String stringToAdd). Кроме того, в конец списка можно добавить целый массив строк, другой объект класса StringArray, с помощью метода void StringArray::addArray(const StringArray other, int startIndex = 0, int numElementsToAdd = -1), где other — первый элемент добавляемого строкового массива, с которого необходимо начинать вставку, а numElementsToAdd — максимальное число строк из добавляемого массива для вставки. Если значение параметра меньше нуля, то добавляются все элементы.
Вставка новых строк в произвольное место списка со смещением элементов массива. Функция void StringArray::insert(int index, const String stringToAdd) вставляет строку stringToAdd в массив на место, заданное индексом index, поднимая другие элементы. Если значение индекса меньше нуля или превышает размер массива, то строка добавляется в конец списка.
Вставка новых строк в произвольное место списка с заменой элементов массива. Функция void StringArray::set(int index, const String newString) вставляет строку newString в массив на место, заданное индексом index. При этом прежняя строка замещается новой. Если значение индекса меньше нуля функция не делает ничего, а если оно превышает размер массива, то строка добавляется в конец списка.
Поиск строк в массиве (по содержимому). Чаще бывает, что найти необходимую строку в массиве бывает нужно не по её индексу, а по тексту, в ней содержащемуся. В этом может помочь метод bool StringArray::contains(const String stringToLookFor, bool ignoreCase = false) const. Также, как и раньше, второй параметр — флаг, следует ли при поиске строки stringToLookFor учитывать регистр.
Удаление строк. Удалить одну строку, находящуюся на позиции index, из списка можно с помощью функции void StringArray::remove(int index). Если индекс выходит за пределы массива, то функция ничего не делает. Поиск строки stringToRemove в массиве с последующим её удалением выполняет функция void StringArray::removeString(const String stringToRemove, bool ignoreCase = false); второй её параметр — флаг, показывающий, следует ли игнорировать регистр.
Полезны также функции, позволяющие удалить все повторяющиеся строки, void StringArray::removeDuplicates(bool ignoreCase), и все пустые строки в массиве, void StringArray::removeEmptyStrings(bool removeWhiteSpaceStrings = true). Если последняя функция в качестве параметра принимает истину, то пустыми будут считаться также строки, заполненные только пробелами.
Кроме того, класс StringArray позволяет удалять сразу ряд строк. Для этого используется метод void StringArray::removeRange(int startIndex, int numberToRemove), где startIndex — индекс первой строки из ряда, подлежащего удалению, а numberToRemove — число удаляемых элементов.
Перемещение строк на новую позицию в массиве осуществляет метод void StringArray::move(int currentIndex, int newIndex) throw(). При вызове функции экземпляром класса StringArray происходит перемещение содержащейся в нём строки, находящейся на позиции currentIndex, на позицию newIndex.
Сортировка элементов списка осуществляется функцией void StringArray::sort(bool ignoreCase); параметр указывает, следует ли учитывать регистр. Строки сортируются в алфавитном порядке.
Очистка списка. Удаление всех строк из списка с обнулением его размера выполняет функция void StringArray::clear().
Нахождение индекса строки осуществляет функция int StringArray::indexOf(const String stringToLookFor, bool ignoreCase = false, int startIndex = 0) const, где stringToLookFor — текст (строка) для поиска, ignoreCase— флаг, показывающий, следует ли игнорировать регистр, а startIndex — индекс элемента, с которого начинается поиск. Функция возвращает индекс найденной строки, а в случае, если ничего не было найдено, возвращается -1.
Объединение строк массива в одну. Удобно для автоматической генерации текста, добавляемого, скажем, в многострочное поле ввода на основании собранных и сохранённых в массиве StringArray данных. Осуществляется функцией const String StringArray::joinIntoString(const String separatorString, int startIndex = 0, int numberOfElements = -1) const.
Мы можем с помощью параметра separatorString задать строку-разделитель, которая будет разделять элементы массива в результирующей строке. Например, если массив StringArray включал строки "a", "b" и "c", то вызов метода joinIntoString(",") вернёт строку "a,b,c".
startIndex, как нетрудно догадаться, индекс элемента массива, с которого следует начать объединение, а numberOfElements — число объединяемых строк. В том случае, если последний параметр принимает значение меньше нуля, то будут объединены все строки массива.
Для вывода неформатированного текста в Juce используется класс String, а форматированного — также и класс Font, хранящий информацию о параметрах шрифта текста.
Класс String, инкапсулирует действия со строками. Объект класса String — это строка, состоящая из произвольного числа символов. Литерные константы типа String представляют собой последовательности символов, заключённые в двойные кавычки.
Архив с исходными текстами примера Вы можете скачать здесь
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.